媒体基础中的音频/视频捕获

Microsoft Media Foundation 支持音频和视频捕获。 通过 UVC 类驱动程序支持视频捕获设备,并且必须与 UVC 1.1 兼容。 通过 Windows 音频会话 API (WASAPI) 支持音频捕获设备。

捕获设备由媒体源对象在媒体基础中表示,该对象公开 IMFMediaSource 接口。 在大多数情况下,应用程序不会直接使用此接口,而是使用更高级别的 API(如 源读取器 )来控制捕获设备。

枚举捕获设备

若要枚举系统上的捕获设备,请执行以下步骤:

  1. 调用 MFCreateAttributes 函数以创建属性存储。

  2. MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE 属性设置为以下值之一:

    说明
    MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID 枚举音频捕获设备。
    MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID 枚举视频捕获设备。

     

  3. 调用 MFEnumDeviceSources 函数。 此函数分配 IMFActivate 指针数组。 每个指针表示系统上一台设备的激活对象。

  4. 调用 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;
}

可以查询各种属性的激活对象,包括:

以下示例采用 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);
    }
}

如果你已经知道视频设备的符号链接,还有另一种方法可以创建设备的媒体源:

  1. 调用 MFCreateAttributes 以创建属性存储。
  2. MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE 属性设置为 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
  3. MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK 属性设置为符号链接。
  4. 调用 MFCreateDeviceSourceMFCreateDeviceSourceActivate 函数。 前者返回 IMFMediaSource 指针。 后者返回指向激活对象的 IMFActivate 指针。 可以使用激活对象创建源。 (激活对象可以封送到另一个进程,因此,如果要在另一个进程中创建源,则它很有用。有关详细信息,请参阅 Activation Objects.)

以下示例采用视频设备的符号链接并创建媒体源。

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 创建音频设备:

  1. 调用 MFCreateAttributes 以创建属性存储。
  2. MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE 属性设置为 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID
  3. MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID 属性设置为终结点 ID。
  4. 调用 MFCreateDeviceSourceMFCreateDeviceSourceActivate 函数。

以下示例采用音频终结点 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;    
}

使用捕获设备

为捕获设备创建媒体源后,使用 源读取器 从设备获取数据。 源读取器提供包含捕获音频数据或视频帧的媒体示例。 下一步取决于应用程序方案:

  • 视频预览:使用 Microsoft Direct3D 或 Direct2D 显示视频。
  • 文件捕获:使用 接收器编写器 对文件进行编码。
  • 音频预览:使用 WASAPI

如果要将音频捕获与视频捕获相结合,请使用 聚合媒体源。 聚合媒体源包含媒体源的集合,并将其所有流合并到单个媒体源对象中。 若要创建聚合媒体源的实例,请调用 MFCreateAggregateSource 函数。

关闭捕获设备

不再需要捕获设备时,必须对通过调用 MFCreateDeviceSourceIMFActivate::ActivateObject 获取的 IMFMediaSource 对象调用 Shutdown 来关闭设备。 调用 Shutdown 失败可能会导致内存链接,因为系统可能会在调用 Shutdown 之前保留对 IMFMediaSource 资源的引用。

if (g_pSource)
{
    g_pSource->Shutdown();
    g_pSource->Release();
    g_pSource = NULL;
}

如果分配了包含捕获设备的符号链接的字符串,则还应释放此对象。

    CoTaskMemFree(g_pwszSymbolicLink);
    g_pwszSymbolicLink = NULL;

    g_cchSymbolicLink = 0;

音频/视频捕获