Qt在Mac上实现USB热插拔检测

Mac 上的 USB 热插拔检测,可以通过 IOKit framework 来实现,网上也有对应的代码,下面是我的一个测试 Demo:

#include <stdlib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/usb/IOUSBLib.h>

// IOServiceAddMatchingNotification 回调函数
// refCon 是注册时传递的参数,比如对象指针等
void deviceAdded(void *refCon, io_iterator_t iterator) {
    io_service_t usb_device;
    while (usb_device = IOIteratorNext(iterator)) {
        // 设备名称
        CFStringRef device_name = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(usb_device, CFSTR(kUSBProductString), kCFAllocatorDefault, 0));
        if (!device_name) {
            device_name = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(usb_device, CFSTR(kUSBVendorString), kCFAllocatorDefault, 0));
        }
        if (!device_name) {
            device_name = CFStringCreateWithCString(kCFAllocatorDefault, "<Unknown>", kCFStringEncodingUTF8);
        }
        ssize_t len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(device_name), kCFStringEncodingUTF8);
        char *buf = new char[len];
        CFStringGetCString(device_name, buf, len, kCFStringEncodingUTF8);
        fprintf(stderr, "Device added: %s.\n", buf);
        delete buf;
        CFRelease(device_name);

        // 获取 vip 和 pid
        CFTypeRef cf_vendor, cf_product;
        cf_vendor = (CFTypeRef)IORegistryEntrySearchCFProperty(usb_device, kIOServicePlane, CFSTR("idVendor"), kCFAllocatorDefault,
                                                               kIORegistryIterateRecursively | kIORegistryIterateParents);

        cf_product = (CFTypeRef)IORegistryEntrySearchCFProperty(usb_device, kIOServicePlane, CFSTR("idProduct"), kCFAllocatorDefault,
                                                                kIORegistryIterateRecursively | kIORegistryIterateParents);
        SInt32 vendor_id = 0, product_id = 0;
        if (cf_vendor && cf_product && CFNumberGetValue((CFNumberRef)cf_vendor, kCFNumberSInt32Type, &vendor_id) &&
                CFNumberGetValue((CFNumberRef)cf_product, kCFNumberSInt32Type, &product_id)) {
            fprintf(stderr, "Device vid 0x%04x, pid 0x%04x.\n", vendor_id, product_id);
        }
        if (cf_vendor)
            CFRelease(cf_vendor);
        if (cf_product)
            CFRelease(cf_product);

        IOObjectRelease(usb_device);
    }
}

int main() {
    // 创建 I/O Kit 通知端口
    IONotificationPortRef notify_prot = IONotificationPortCreate(kIOMasterPortDefault);

    // 创建匹配字典,可以指定要监听的设备的厂商 ID 和产品 ID
    CFMutableDictionaryRef match_dict = IOServiceMatching(kIOUSBDeviceClassName);
    // SInt32 vendor_id = 0x2717;
    // SInt32 product_id = 0xFF08;
    // CFDictionarySetValue(match_dict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor_id));
    // CFDictionarySetValue(match_dict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product_id));

    // 创建异步通知端口
    io_iterator_t add_iterator;
    kern_return_t result;
    // kIOMatchedNotification 添加,kIOTerminatedNotification 移除
    result = IOServiceAddMatchingNotification(notify_prot, kIOMatchedNotification, match_dict, deviceAdded, nullptr, &add_iterator);
    if (result != KERN_SUCCESS) {
        fprintf(stderr, "IOServiceAddMatchingNotification error: %d. \n", result);
        return -1;
    }
    // 初始化的时候要调用一次,不然没回调,也可以在这时枚举当前已有设备
    // 可以使用空的处理来跳过
    deviceAdded(nullptr, add_iterator);

    // 将通知端口连接到一个运行循环结构中
    // 运行循环属于 Core Foundation 变成模型,实现了消息循环,当消息到达通知端口时,用户提供的回调函数会被调用
    CFRunLoopSourceRef run_loop = IONotificationPortGetRunLoopSource(notify_prot);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop, kCFRunLoopDefaultMode);

    // 进入主运行循环,等待设备插入或拔出事件
    // 如果是放到 Qt 事件循环里就不需要这个
    CFRunLoopRun();

    // 释放
    IONotificationPortDestroy(notify_prot);

    return 0;
}

Qt qmake 在使用对应的 framework 时 pro 需要添加 LIBS:

macos{
    LIBS += -framework IOKit -framework CoreFoundation
}

但是,USB 插拔消息来的时候,QCamera 还没法枚举到对应的摄像头,这个和 Windows 是一样的,需要注册相机的插拔回调才行。

Qt5 的 QCamera 后端实现在 Mac 上使用的 AVFoundation framework,我又找了一个相机插拔的代码(pro 加上 -framework AVFoundation):

#include <AVFoundation/AVFoundation.h>
#include <objc/objc.h>
#include <QCameraInfo>
#include <QDebug>

id attachHandle = nullptr;
id detachHandle = nullptr;

void init()
{
    // 注册之前必须先调用AVCaptureDevice
    [AVCaptureDevice devices];
    // 摄像头可用事件
    attachHandle = [[NSNotificationCenter defaultCenter]
            addObserverForName:AVCaptureDeviceWasConnectedNotification
                               object:nil
                               queue:nil
                               usingBlock:^(NSNotification *){
        qDebug()<<"add"<<QCameraInfo::availableCameras().size();
    }];
    // 摄像头不可用
    detachHandle = [[NSNotificationCenter defaultCenter]
            addObserverForName:AVCaptureDeviceWasDisconnectedNotification
                               object:nil
                               queue:nil
                               usingBlock:^(NSNotification *){
        qDebug()<<"remove"<<QCameraInfo::availableCameras().size();
    }];
}

void free()
{
    if (attachHandle) {
        [[NSNotificationCenter defaultCenter] removeObserver:attachHandle];
    }
    if (detachHandle) {
        [[NSNotificationCenter defaultCenter] removeObserver:detachHandle];
    }
}

代码链接:

https://github.com/gongjianbo/MyTestCode/tree/master/Qt/DeviceHotplug_Mac

要在Qt实现USB热插拔检测,可以使用QDeviceWatcher类。QDeviceWatcher是一个用于监测设备插入和移除事件的类。 首先,确保在项目文件(.pro)中添加以下内容以启用Qt的udev库: ```cpp LIBS += -ludev ``` 然后,可以按照以下步骤在Qt实现USB热插拔检测: 1. 在头文件中包含必要的头文件: ```cpp #include <QObject> #include <QDeviceWatcher> ``` 2. 创建一个类并继承自QObject: ```cpp class USBWatcher : public QObject { Q_OBJECT public: explicit USBWatcher(QObject *parent = nullptr); private slots: void deviceAdded(const QString &devPath); void deviceRemoved(const QString &devPath); private: QDeviceWatcher *m_deviceWatcher; }; ``` 3. 在实现文件中定义构造函数,并在构造函数中初始化QDeviceWatcher对象,并连接相应的槽函数: ```cpp USBWatcher::USBWatcher(QObject *parent) : QObject(parent) { m_deviceWatcher = new QDeviceWatcher(this); connect(m_deviceWatcher, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString))); connect(m_deviceWatcher, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString))); m_deviceWatcher->start(); } ``` 4. 实现设备插入和移除的槽函数: ```cpp void USBWatcher::deviceAdded(const QString &devPath) { // 处理设备插入事件 qDebug() << "Device added: " << devPath; } void USBWatcher::deviceRemoved(const QString &devPath) { // 处理设备移除事件 qDebug() << "Device removed: " << devPath; } ``` 5. 在你的应用程序中实例化USBWatcher对象,并将其保持活动状态: ```cpp int main(int argc, char *argv[]) { QApplication app(argc, argv); USBWatcher usbWatcher; return app.exec(); } ``` 通过以上步骤,你现在可以在`deviceAdded()`和`deviceRemoved()`槽函数中处理设备插入和移除事件。你可以根据需求来执行一些自定义的操作,比如更新UI或者执行特定的任务。 请注意,USB热插拔检测需要在具有相应权限的操作系统上运行,如Linux。在不同的操作系统和平台上,可能需要使用不同的方法来实现设备监测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值