处理视频设备丢失
这个主题描述了在使用视频捕获设备时如何检测设备丢失。它包括以下几个部分:
- 注册设备通知
- 获取设备的符号链接
- 处理WM_DEVICECHANGE消息
- 取消注册通知
注册设备通知
在开始从设备捕获数据之前,请调用RegisterDeviceNotification
函数来注册设备通知。请按下面的代码示例注册KSCATEGORY_CAPTURE
设备类:
#pragma once
#include <dbt.h>
#include <ks.h>
#include <ksmedia.h>
HDEVNOTIFY g_hdevnotify = NULL;
// HWND hwnd: 窗口句柄
// HWND 的作用是:当设备丢失时,系统会向这个窗口发送WM_DEVICECHANGE消息
// 返回值:如果函数调用成功,则返回值为非零值。如果函数调用失败,则返回值为零。
BOOL RegisterForDeviceNotification(HWND hwnd)
{
DEV_BROADCAST_DEVICEINTERFACE di = {
0 };
di.dbcc_size = sizeof(di);
di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
di.dbcc_classguid = KSCATEGORY_CAPTURE;
g_hdevnotify = RegisterDeviceNotification(
hwnd,
&di,
DEVICE_NOTIFY_WINDOW_HANDLE
);
if (g_hdevnotify == NULL)
{
return FALSE;
}
return TRUE;
}
获取设备的符号链接
枚举系统上的视频设备,如在《枚举视频捕获设备》中所述。从列表中选择一个设备,然后查询激活对象以获取MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK
属性,如下所示的代码所示。
WCHAR *g_pwszSymbolicLink = NULL;
UINT32 g_cchSymbolicLink = 0;
HRESULT GetSymbolicLink(IMFActivate *pActivate)
{
return pActivate->GetAllocatedString(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
&g_pwszSymbolicLink,
&g_cchSymbolicLink
);
}
处理 WM_DEVICECHANGE
在你的消息循环中,监听 WM_DEVICECHANGE 消息。lParam 消息参数是一个指向 DEV_BROADCAST_HDR 结构的指针。
Note: Windows 消息循环
简要概括 windows 消息循环
- 消息
Windows 使用消息进行通讯。每个Windows 消息共有两个参数,wParam
和lParam
。 在win32中,这两个参数都是32位的。
wParam
包含两个值HIWORD(wParam)
是通知消息(如果可用)LOWORD(wParam)
是发送消息的空间或菜单ID。
IParam
是发送消息的控件的HWND
(窗口句柄), 如果为NULL,表示消息不是由控件发送的。
case WM_DEVICECHANGE:
if (lParam != 0)
{
HRESULT hr = S_OK;
BOOL bDeviceLost = FALSE;
hr = CheckDeviceLost((PDEV_BROADCAST_HDR)lParam, &bDeviceLost);
if (FAILED(hr) || bDeviceLost)
{
CloseDevice();
MessageBox(hwnd, L"Lost the capture device.", NULL, MB_OK);
}
}
return TRUE;
wchar_t* ExtractDeviceId(const wchar_t* devicePath)
{
const wchar_t* start = wcschr(devicePath, L'#');
if (start == NULL)
{
return NULL;
}
start += 1;
const wchar_t* end = wcschr(start, L'#');
const wchar_t* next = end;
while (next != NULL)
{
end = next;
next += 1;
next = wcschr(next, L'#');
}
if (end == NULL)
{
return NULL;
}
size_t length = end - start;
wchar_t* deviceId = (wchar_t*)malloc((length + 1) * sizeof(wchar_t));
wcsncpy_s(deviceId, length + 1, start, length);
deviceId[length] = L'\0';
return deviceId;
}
//-------------------------------------------------------------------
// CheckDeviceLost
// Checks whether the video capture device was removed.
//
// The application calls this method when is receives a
// WM_DEVICECHANGE message.
//-------------------------------------------------------------------
HRESULT CPreview::CheckDeviceLost(DEV_BROADCAST_HDR *pHdr, BOOL *pbDeviceLost)
{
DEV_BROADCAST_DEVICEINTERFACE *pDi = NULL;
if (pbDeviceLost == NULL)
{
return E_POINTER;
}
*pbDeviceLost = FALSE;
if (m_pSource == NULL)
{
return S_OK;
}
if (pHdr == NULL)
{
return S_OK;
}
if (pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
{
return S_OK;
}
// Compare the device name with the symbolic link.
pDi = (DEV_BROADCAST_DEVICEINTERFACE*)pHdr;
if (m_pwszSymbolicLink)
{
_locale_t locale = _create_locale(LC_ALL, "en-US");
if (_wcsicmp_l(ExtractDeviceId(m_pwszSymbolicLink), ExtractDeviceId(pDi->dbcc_name), locale) == 0)
{
*pbDeviceLost = TRUE;
}
}
return S_OK;
}
完整Demo 查看官方github 代码