Win7下视频采集设备调用死锁、显式选设备及帧数据内存存储问题
问题1:capDriverConnect 死锁解决
死锁核心原因是VFW(Video for Windows)的捕获窗口依赖Windows消息循环,你当前代码如果是运行在无消息泵的控制台线程中,底层驱动发送的窗口消息无法被处理,就会陷入无限等待。
修复方案:
- 不要将捕获窗口设为
WS_CHILD挂到桌面窗口,改为创建隐藏的重叠窗口:HWND hCam = capCreateCaptureWindow(L"hoven", WS_OVERLAPPED | WS_HIDDEN, 0, 0, 0, 0, NULL, 0); - 在调用cap系列函数前后主动排空消息队列,添加如下消息处理逻辑:
MSG msg; while (PeekMessage(&msg, hCam, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } - 调用
capDriverDisconnect前先停止所有帧捕获操作,断开后同样排空一次消息队列再销毁窗口。
问题2:帧数据直接存内存优化(你现有剪贴板方案可用,补充更高效方案)
可以直接注册帧捕获回调,不需要走剪贴板中转,性能更高:
- 用
capSetCallbackOnFrame(hCam, FrameCallbackProc)注册回调函数 - 回调函数中可以直接拿到
VIDEOHDR结构,里面的lpData就是当前帧的RGB原始数据,直接拷贝到你申请的内存块即可。
问题3:显式指定采集设备
capDriverConnect的设备索引从0开始,有效范围是0~9(VFW的硬限制),你之前直接传1/2无效是因为没有先枚举系统中注册的VFW兼容设备,很多USB采集卡、新摄像头只会把第一个设备注册到VFW的设备列表,或者索引不是你预期的顺序。
- 枚举设备的代码示例:
WCHAR szDeviceName[80] = {0}; WCHAR szDeviceVersion[80] = {0}; for (int i = 0; i < 10; i++) { if (capGetDriverDescriptionW(i, szDeviceName, sizeof(szDeviceName)/sizeof(WCHAR), szDeviceVersion, sizeof(szDeviceVersion)/sizeof(WCHAR))) { wprintf(L"设备索引%d:%s %s\n", i, szDeviceName, szDeviceVersion); } } - 枚举完成后,找到你要使用的设备对应的索引,再传入
capDriverConnect即可,不要硬写0。如果你的第二个设备枚举不出来,说明该设备驱动不兼容VFW接口,建议换用DirectShow或者Media Foundation框架做采集,兼容性更好。 - 关于
capDlgVideoSource弹框不稳定:该接口是驱动层实现的,部分驱动在没有连接设备、或者已经设过默认参数时会跳过弹框,要稳定触发设备选择,应该在枚举设备后让用户自己选索引,不要依赖驱动的弹框。
修复后的参考代码
#include <windows.h> #include <vfw.h> #pragma comment(lib, "vfw32.lib") int main() { // 先枚举所有可用VFW设备 WCHAR szDeviceName[80] = {0}; WCHAR szDeviceVersion[80] = {0}; int targetDeviceIndex = 0; // 枚举到目标设备后修改为对应索引 for (int i = 0; i < 10; i++) { if (capGetDriverDescriptionW(i, szDeviceName, sizeof(szDeviceName)/sizeof(WCHAR), szDeviceVersion, sizeof(szDeviceVersion)/sizeof(WCHAR))) { wprintf(L"设备索引%d:%s %s\n", i, szDeviceName, szDeviceVersion); // 这里可以加判断,匹配到你要的设备名就记录索引 // if (wcsstr(szDeviceName, L"你的采集卡名字") != NULL) targetDeviceIndex = i; } } // 创建隐藏的捕获窗口 HWND hCam = capCreateCaptureWindow(L"hoven", WS_OVERLAPPED | WS_HIDDEN, 0, 0, 0, 0, NULL, 0); if (hCam == NULL) { printf("capCreateCaptureWindow Error !\n"); return -1; } // 排空消息队列 MSG msg; while (PeekMessage(&msg, hCam, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 连接指定索引的设备 if (!capDriverConnect(hCam, targetDeviceIndex)) { printf("capDriverConnect Error !\n"); DestroyWindow(hCam); return -1; } // 再次排空消息 while (PeekMessage(&msg, hCam, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } capGrabFrame(hCam); capFileSaveDIB(hCam, L"shot.bmp"); // 断开前处理消息 while (PeekMessage(&msg, hCam, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } capDriverDisconnect(hCam); // 断开后处理消息 while (PeekMessage(&msg, hCam, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } DestroyWindow(hCam); return 0; }
内容的提问来源于stack exchange,提问作者ted777
相关产品推荐
相关产品推荐

