65.9K
CodeProject 正在变化。 阅读更多。
Home

在 DirectShow 中读取音频、视频设备和编解码器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.93/5 (9投票s)

2008年12月3日

CPOL

4分钟阅读

viewsIcon

75008

downloadIcon

2483

如何通过 DirectShow 读取 PC 上安装的音频、视频设备和编解码器。

引言

要获取系统上可用的音频设备、视频设备和编解码器的列表,您需要 DirectX 或更准确地说 DirectShow 的起点。这个极小的程序将为您提供一个起点。但是,要播放音频和视频,我们需要修改我们的程序,特别是如果我们想编写一个需要即时播放和文件保存等的程序。然而,这个小程序将为有兴趣学习 DirectShow 编程并访问输入/输出设备和音视频编解码器的初学者提供主要的起点。

背景

DirectX 使用“过滤器”一词来指定不同的硬件和软件编解码器。而在程序中,我使用了“设备”一词。当我开始编程时,我从不喜欢使用“过滤器”这个词,而是总是使用“设备”这个词,所以我觉得你们中的一些人也会有同感!异常处理有限,正如我之前的教程一样。我故意这样做是为了让代码更用户友好,而不是一堆专业代码。

如您所知,DirectX 是基于 COM 的,所以一点 COM 方面的理解会有所帮助。

代码

除了 main() 函数外,还有两个简单的函数。

void HR_Failed(HRESULT);
void Device_Reader(GUID,ICreateDeviceEnum);

首先,让我们看看 main() 中有什么。程序的 main() 函数包含 COM 的初始化代码,并声明了几个变量,“hr”和“ICreateDevEnum”。ICreateDevEnum 是一个接口,我们在指针变量 *pDeviceEnum 中维护其引用。这个接口的作用将在解释 HR_Failed(hr) 函数后进行说明。

HR_Failed() 函数接收一个 HRESULT 类型;每当发生错误时都会调用该函数。例如:

hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);// Initialise COM
if (SUCCEEDED(hr))
    {
        cout<<"COM Initialisation successful"<<endl;
    }
    else HR_Failed(hr)

在上面的代码行中,在调用初始化 COM 后返回 hr。如果初始化失败,则调用 HR_Failed(hr)。函数内的代码会将“hr”的值转换为更易于人类理解的代码(老实说,我在 MSDN 上找到了这段代码!)。

void HR_Failed(HRESULT hr)
{
    TCHAR szErr[MAX_ERROR_TEXT_LEN];

    DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
    if (res == 0)
    {
        StringCchPrintf(szErr, MAX_ERROR_TEXT_LEN, 
                        L"Unknown Error: 0x%2x", hr);
    } 
    MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
    return;
}

Device_Reader(GUID,ICreateDevEnum) 函数负责读取或提取系统中安装的“过滤器”,即音频、视频和编解码器过滤器。不要将“设备”与编解码器混淆。DirectX 称之为“过滤器”,我称之为“设备”。如果您觉得方便,可以将该函数重命名为 Filter_Reader。在我之前的一篇文章中,我曾解释过如何读取系统上的设备。您可以在 这里 找到该主题,但我仍将在此处写几行,因为 Device_Reader 函数中的代码几乎相同。

首先,我们通过调用 CoCreateInstance 来初始化设备枚举。

hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 
   IID_ICreateDevEnum, (void **)&pDeviceEnum);//Initialise Device enumerator

然后,我们需要创建一个设备枚举,该枚举允许我们获取或“读取”系统中可用的音频设备。在这里,我们需要指定我们正在尝试读取或获取的设备的类别。我们通过传递 MSDN 文档定义的每个类别的 GUID 来进行此枚举。在这种情况下,我使用了几个:CLSID_AudioInputDeviceCategoryCLSID_AudioCompressorCategory。每个 GUID 的名称都指代一个特定的类别。

通过类 ID 使用设备枚举的另一个好处是,假设我们有两个声卡,它们都支持相同的过滤器,使用此枚举我们可以将它们作为单独的实例区别对待。枚举后,我们无法简单地使用 AudioInputDeviceCategory 中的设备的引脚属性或访问任何其他方法。要做到这一点,我们必须使用 Monikers。Moniker 是指向特定设备的接口,然后我们使用 IMonikerIPropertyBag 将设备绑定到 Moniker 并分别读取设备的属性。IPropertyBag 接口保存着设备属性的键。

void Device_Reader(GUID DEVICE_CLSID,ICreateDevEnum *pDeviceEnum )
{
    HRESULT hr;
    IMoniker *pDeviceMonik = NULL;// Device moniker
    IEnumMoniker *pEnumCat = NULL;// Device enumeration moniker
    VARIANT varName;

    // Enumerate the specified device, distinguished by DEVICE_CLSID
    hr = pDeviceEnum->CreateClassEnumerator(DEVICE_CLSID, &pEnumCat, 0);

    if (hr == S_OK) 
    {
        ULONG cFetched;
        while (pEnumCat->Next(1, &pDeviceMonik, &cFetched) == S_OK)
        //Pickup as moniker
        {
            IPropertyBag *pPropBag = NULL;
            //bind the properties of the moniker  
            hr = pDeviceMonik->BindToStorage(0, 0, 
                    IID_IPropertyBag,(void **)&pPropBag);
            if (SUCCEEDED(hr))
            {
                // Initialise the variant data type
                VariantInit(&varName);
                //extract the FriendlyName
                // - This is the name we see in GraphEdit
                hr = pPropBag->Read(L"FriendlyName", &varName, 0);
                if (SUCCEEDED(hr))
                {
                    wcout<<varName.bstrVal<<endl;
                    // show the name
                }
                else HR_Failed(hr);
                //clear the variant data type
                VariantClear(&varName);
                pPropBag->Release();//release the properties
            }
            else HR_Failed(hr);
            pDeviceMonik->Release();//release Device moniker
        }
        pEnumCat->Release();//release category enumerator
    }
    else HR_Failed(hr);    
}

该函数接受 GUID 和指向 ICreateDevEnum 接口的指针。使用不同的 GUID,每次调用都会访问系统中可用的音频、视频和编解码器过滤器。下面是我系统上可用内容的屏幕截图。

list.jpg

当找不到过滤器时会发生什么?

会调用 HR_Failed,然后出现以下窗口,显示错误消息“函数不正确”。每当找不到一整类过滤器时,就会调用 HR_Failed,它会显示窗口。

error.jpg

关注点

GraphEdit 始终是开发人员摆脱困境的工具。虽然它看起来非常原始,但它绝对是一个杰作。只有在使用 GraphEdit 后,我才意识到它显示的“FriendlyName”与控制台输出中看到的完全相同。所以,一定要试试。如果您在系统上找不到 GraphEdit,请确保您已安装 Windows SDK,并在“Bin”目录中查找 GrapEdit.exe

该代码使用以下内容构建:

  • Windows Server 2008
  • MS Visual Studio 2008
  • DirectX 10.1
  • Microsoft Windows SDK 6.1

历史

第一篇帖子。如有需要将更新。

参考文献

© . All rights reserved.