媒体基础中的音频/视频捕获
Microsoft Media Foundation 支持音频和视频捕获。 视频捕获设备通过 UVC 类驱动程序支持,并且必须与 UVC 1.1 兼容。 音频捕获设备通过 Windows 音频会话 API (WASAPI) 支持。
在 Media Foundation 中,捕获设备由可公开 IMFMediaSource 接口的媒体源对象表示。 在大多数情况下,应用程序不会直接使用此接口,而是使用更高级别的 API(如源读取器)来控制捕获设备。
枚举捕获设备
要枚举系统上的捕获设备,请执行以下步骤:
调用 MFCreateAttributes 函数以创建属性存储。
将 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE 属性设置为下列值之一:
值 说明 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID 枚举音频捕获设备。 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID 枚举视频捕获设备。 调用 MFEnumDeviceSources 函数。 此函数分配一个 IMFActivate 指针数组。 每个指针代表系统中一个设备的激活对象。
调用 IMFActivate::ActivateObject 方法,从一个激活对象创建一个媒体源实例。
以下示例为枚举列表中的第一个视频捕获设备创建了一个媒体源:
HRESULT CreateVideoCaptureDevice(IMFMediaSource **ppSource)
{
*ppSource = NULL;
UINT32 count = 0;
IMFAttributes *pConfig = NULL;
IMFActivate **ppDevices = NULL;
// Create an attribute store to hold the search criteria.
HRESULT hr = MFCreateAttributes(&pConfig, 1);
// Request video capture devices.
if (SUCCEEDED(hr))
{
hr = pConfig->SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
);
}
// Enumerate the devices,
if (SUCCEEDED(hr))
{
hr = MFEnumDeviceSources(pConfig, &ppDevices, &count);
}
// Create a media source for the first device in the list.
if (SUCCEEDED(hr))
{
if (count > 0)
{
hr = ppDevices[0]->ActivateObject(IID_PPV_ARGS(ppSource));
}
else
{
hr = MF_E_NOT_FOUND;
}
}
for (DWORD i = 0; i < count; i++)
{
ppDevices[i]->Release();
}
CoTaskMemFree(ppDevices);
return hr;
}
可以查询激活对象的各种属性,具体如下:
- MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME 属性包含设备的显示名称。 显示名称适合向用户显示,但可能并非唯一。
- 对于视频设备,MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK 属性包含设备的符号链接。 符号链接可以唯一标识系统中的设备,但并非可读字符串。
- 对于音频设备,MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID 属性包含设备的音频终结点 ID。 音频终结点 ID 类似于符号链接。 它可以唯一标识系统上的设备,但并非可读字符串。
以下示例使用 IMFActivate 指针数组,并会将每个设备的显示名称打印到调试窗口:
void DebugShowDeviceNames(IMFActivate **ppDevices, UINT count)
{
for (DWORD i = 0; i < count; i++)
{
HRESULT hr = S_OK;
WCHAR *szFriendlyName = NULL;
// Try to get the display name.
UINT32 cchName;
hr = ppDevices[i]->GetAllocatedString(
MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
&szFriendlyName, &cchName);
if (SUCCEEDED(hr))
{
OutputDebugString(szFriendlyName);
OutputDebugString(L"\n");
}
CoTaskMemFree(szFriendlyName);
}
}
如果已经知道视频设备的符号链接,还有另一种方法可以为设备创建媒体源:
- 调用 MFCreateAttributes 以创建属性存储。
- 将 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE 属性设置为 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID。
- 将 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK 属性设置为符号链接。
- 调用 MFCreateDeviceSource 或 MFCreateDeviceSourceActivate 函数。 前者会返回一个 IMFMediaSource 指针。 后者会返回一个指向激活对象的 IMFActivate 指针。 可以使用激活对象来创建源。 (激活对象可以被封送到到另一个进程,因此如果要在另一个进程中创建源,它就很有用。有关详细信息,请参阅激活对象。)
以下示例使用视频设备的符号链接创建了一个媒体源。
HRESULT CreateVideoCaptureDevice(PCWSTR *pszSymbolicLink, IMFMediaSource **ppSource)
{
*ppSource = NULL;
IMFAttributes *pAttributes = NULL;
IMFMediaSource *pSource = NULL;
HRESULT hr = MFCreateAttributes(&pAttributes, 2);
// Set the device type to video.
if (SUCCEEDED(hr))
{
hr = pAttributes->SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
);
}
// Set the symbolic link.
if (SUCCEEDED(hr))
{
hr = pAttributes->SetString(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
(LPCWSTR)pszSymbolicLink
);
}
if (SUCCEEDED(hr))
{
hr = MFCreateDeviceSource(pAttributes, ppSource);
}
SafeRelease(&pAttributes);
return hr;
}
有一种等效的方法可以通过音频终结点 ID 创建音频设备:
- 调用 MFCreateAttributes 以创建属性存储。
- 将 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE 属性设置为 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID。
- 将 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID 属性设置为终结点 ID。
- 调用 MFCreateDeviceSource 或 MFCreateDeviceSourceActivate 函数。
以下示例使用音频终结点 ID 并创建了一个媒体源。
HRESULT CreateAudioCaptureDevice(PCWSTR *pszEndPointID, IMFMediaSource **ppSource)
{
*ppSource = NULL;
IMFAttributes *pAttributes = NULL;
IMFMediaSource *pSource = NULL;
HRESULT hr = MFCreateAttributes(&pAttributes, 2);
// Set the device type to audio.
if (SUCCEEDED(hr))
{
hr = pAttributes->SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID
);
}
// Set the endpoint ID.
if (SUCCEEDED(hr))
{
hr = pAttributes->SetString(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID,
(LPCWSTR)pszEndPointID
);
}
if (SUCCEEDED(hr))
{
hr = MFCreateDeviceSource(pAttributes, ppSource);
}
SafeRelease(&pAttributes);
return hr;
}
使用捕获设备
在为捕获设备创建媒体源之后,使用源读取器从设备获取数据。 源读取器提供包含捕获音频数据或视频帧的媒体示例。 下一步取决于应用场景:
如果要将音频捕获与视频捕获结合起来,请使用聚合媒体源。 聚合媒体源包含媒体源集合,并会将其所有的流合并到一个媒体源对象中。 要创建聚合媒体源实例,请调用 MFCreateAggregateSource 函数。
关闭捕获设备
当不再需要捕获设备时,必须在通过调用 MFCreateDeviceSource 或 IMFActivate::ActivateObject 获得的 IMFMediaSource 对象上调用 Shutdown 来关闭设备。 如果不调用 Shutdown,可能会导致内存泄漏,因为系统可能会保留对 IMFMediaSource,直到调用 Shutdown。
if (g_pSource)
{
g_pSource->Shutdown();
g_pSource->Release();
g_pSource = NULL;
}
如果分配了包含捕获设备符号链接的字符串,则也应释放此对象。
CoTaskMemFree(g_pwszSymbolicLink);
g_pwszSymbolicLink = NULL;
g_cchSymbolicLink = 0;
相关主题