音频采集 via DirectShow

本文介绍DirectShow框架下音频采集的方法,DirectShow是基于COM的流媒体处理开发包,支持多种媒体格式。文章详细讲解了DirectShow采集音频的流程,包括使用enumAudioInputFilters函数枚举音频输入设备,AddFilterByCLSID函数添加滤镜,ConnectFilters函数连接滤镜,以及GraphEdit工具的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DirectShow 简介

DirectShow(有时缩写为 DS 或 DShow),开发代号 Quartz,是微软在 ActiveMovie 和 Video for Windows 的基础上推出的新一代基于 COM 的流媒体处理的开发包,与 DirectX 开发包一起发布。DShow 使用一种叫 Filter Graph 的模型来管理整个数据流的处理过程,有了 DShow,我们可以很方便地从支持 WDM 驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。这样使在多媒体数据库管理系统(MDBMS)中多媒体数据的存取变得更加方便。它广泛地支持各种媒体格式,包括 asf、mpeg、avi、dv、mp3、wav 等,为多媒体流的捕捉和回放提供了强有力的支持。

DirectShow 采集音频

采集流程图

DShow cap audio

采集代码

以下是整个 DirectShow 采集过程的概要代码,略去各个函数的具体实现和资源释放。

CoInitialize(NULL);  
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);   

// enumerate the audio input device and pick the first one.   
hr = enumAudioInputFilters((void**)&pAudioInputFilter);   
hr = pGraph->AddFilter(pAudioInputFilter, _T("Capture"));   
hr = AddFilterByCLSID(pGraph, CLSID_AviDest, _T("AVI Mux"), &pAVIMux);   
hr = ConnectFilters(pGraph, pAudioInputFilter, pAVIMux);   
hr = AddFilterByCLSID(pGraph, CLSID_FileWriter, _T("File Writer"), &pFileWriter);   
hr = pFileWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);   
hr = pSink->SetFileName(argv[1], NULL);   
hr = ConnectFilters(pGraph, pAVIMux, pFileWriter); 
  
hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
hr = pControl->Run();   

char ch = getchar(); // Wait keyboard input and then stop the recording
hr = pControl->Stop();
CoUninitialize();

enumAudioInputFilters 函数

枚举音频输入设备,通常是麦克风。

HRESULT enumAudioInputFilters(void** gottaFilter)   
{   
    HRESULT hr = E_FAIL;

    // Create the System Device Enumerator.   
    CComPtr<ICreateDevEnum> pDevEnum = NULL;   
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum);   
    RETURN_IF_FAILED(hr);

    // Obtain a class enumerator for the audio input category.   
    CComPtr<IEnumMoniker> pEnumCat = NULL;   
    hr = pDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &pEnumCat, 0);   
    RETURN_IF_FAILED(hr);

    // Enumerate the monikers.   
    CComPtr<IMoniker> pMoniker = NULL;   
    ULONG cFetched = 0;   
    // Bind the first moniker to an object   
    hr = pEnumCat->Next(1, &pMoniker, &cFetched);
    RETURN_IF_FAILED(hr);

    CComPtr<IPropertyBag> pPropBag = NULL;   
    hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);   
    if (SUCCEEDED(hr)) {
        // To retrieve the filter's friendly name, do the following:   
        VARIANT varName;   
        VariantInit(&varName);   
        hr = pPropBag->Read(_T("FriendlyName"), &varName, 0);   
        if (SUCCEEDED(hr))   
            DL_T1("Audio Input Device: %s", varName.bstrVal);   
        VariantClear(&varName);   
    }

    // To create an instance of the filter, do the following:   
    // Remember to release pFilter later   
    hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, gottaFilter);   
    RETURN_IF_FAILED(hr);

    return S_OK;   
}   

AddFilterByCLSID 函数

AddFilterByCLSID 是 Windows SDK sample 提供的函数,在系统中注册过的 filter(包括解码器、编码器、muxer 等等)可以很方便的加到 filter graph 中。

HRESULT AddFilterByCLSID(
    IGraphBuilder *pGraph,  // Pointer to the Filter Graph Manager.   
    const GUID& clsid,      // CLSID of the filter to create.   
    LPCWSTR wszName,        // A name for the filter.   
    IBaseFilter **ppF)      // Receives a pointer to the filter.   
{   
    *ppF = NULL;   
    HRESULT hr = E_FAIL;
    IBaseFilter *pF = NULL;   

    hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pF));   
    RETURN_IF_FAILED(hr);

    hr = pGraph->AddFilter(pF, wszName);   
    if (SUCCEEDED(hr))   
        *ppF = pF;   
    else   
        pF->Release();   
    
    return hr;   
}   

ConnectFilters 函数

ConnectFilters 也是 Windows SDK sample 提供的函数,Filter 的连接是通过 Pin 完成的,一个 Pin 只能有一个连接,非常专一。下方有图例。

// ConnectFilters: Filter to filter
HRESULT ConnectFilters(
    IGraphBuilder *pGraph, 
    IBaseFilter *pSrc, 
    IBaseFilter *pDest)
{
    if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL))
        return E_POINTER;

    HRESULT hr = S_OK;
    IPin *pOut = NULL;

    // Find an output pin on the first filter.
    CHECK_HR(hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut));
    CHECK_HR(hr = ConnectFilters(pGraph, pOut, pDest));

done:
    SAFE_RELEASE(pOut);
    return hr;
}

// ConnectFilters: Filter to pin
HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IPin *pIn)
{
    if ((pGraph == NULL) || (pSrc == NULL) || (pIn == NULL))
        return E_POINTER;

    HRESULT hr = S_OK;
    IPin *pOut = NULL;
        
    // Find an output pin on the upstream filter.
    CHECK_HR(hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut));

    // Try to connect them.
    CHECK_HR(hr = pGraph->Connect(pOut, pIn));

done:
	SAFE_RELEASE(pOut);
    return hr;
}

GraphEdit

GraphEdit 是一个用于建立和测试 Filter Graph 的可视化工具。包含在 Windows SDK 7.x 中(Bin/graphedt.exe)。通过 GraphEdit,你可以在写程序代码前验证 Filter Graph。你也可以加载一个由程序创建的 Filter Graph,来核实是否创建了正确的 Graph。如果你开发一个定制的 Filter,GraphEdit 提供了一种快速的方式测试它:简单的加载一个带有你定制的 Filter 的 Graph,并且试着运行它。

下图就是上面音频采集程序的 Filter Graph,包含一个 Source Filter,AVI Mux 和 File Writer,通过 Pin 连接。
audio cap graph
GraphEdit 中包含了丰富的内置或第三方(需注册)的 Filter,可以通过 Graph 菜单下的 Insert Filters… 子菜单打开,如下图所示:
filters

其他框架下的采集

请参考对应的文章。

Blueware
EOF

/---------------------------------------------------------------------\ * 书 名:《DirectShow开发指南》 * 作 者: 陆其明(著) 金邦飞(审校) * 内容提要: 本书以DirectX SDK 9.0版为蓝本,涉及的内容几乎涵盖了在Windows平台上使 用DirectShow进行C++编码的方方面面。全书共分4个部分。第1部分详细介绍了 DirectShow的基础知识。第2部分重点讨论了Filter的开发,以及DirectShow 应用程序的开发,包括目前非常流行的如音视频采集、数码摄像机的支持、非线性 编辑等应用。第3部深入分析了DirectShow SDK提供的部分典型源代码例子。第 4部分结合作者个人的一些开发实践,通过案例和开放源码分析,进一步介绍 DirectShow的实务应用。 本书完全忠实于DirectX SDK 9.0的帮助文档以及基类源代码,并结合作者多年 的实践,经过提炼而成。内容丰富,条理清晰,实用性强。适合广大的流媒体应用 开发人员,以及对Windows平台上多媒体处理感兴趣的编程爱好者、学生学习和参 考。 * 下载内容说明: readme.txt:本说明文件。 Chapter04:第4章用到的代码,其中AppIPTransform为MFC Filter的例子, DsDemo是一些演示代码,FilterTitleOverlay是字符叠加Filter的源代码。 Chapter05:第5章用到的代码,其中GraphBuilding为Filter Graph构建技术 的一些代码,SimplePlayer为一个简单的播放器例子。 Chapter07:第7章用到的代码,其中DsDemo是一些演示代码。 Chapter09:第9章用到的代码,其中DESCallback演示了控制DirectShow智能 连接的方法。 Chapter18:第18章用到的代码,其中MpegNetwork为“MPEG流的网络客户端播放” 实现的所有源代码(请打开Daisy.dsw浏览各项目)。 Chapter19:第19章用到的代码,其中DVD2AVI_1.77.3_SRC.zip为开放源码, FilterMpeg2VD为此开放源码基础上开发的MPEG-2 Video Decoder Filter例子。 除上述代码外,本书各章用到的其他代码均在DirectX SDK安装目录的Samples 子目录下可以找到。 * 备注: DirectX SDK 9.0以及DirectX 9.0运行时库可以到微软的网站上下载。 请访问微软的网站首页http://www.microsoft.com,然后输入DirectX进行搜 索;或者直接访问http://www.microsoft.com/directx下载运行时库,或者 http://www.microsoft.com/downloads/details.aspx?FamilyId=9216652F-51E0-402E-B7B5-FEB68D00F298&displaylang=en * 技术支持网站:http://hqtech.nease.net \---------------------------------------------------------------------/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值