分享

DirectShow音频采集

 vcand3d 2013-05-03

习了好长一段时间的directshow,关于视频那一块的一些部分简单地过了一遍,如采集、解压缩、传输 等。现在回过头来学习音频的采集。现在将这几天的学习心得写下来。

 

我们知道directshow对我们的硬件的支持是通过特定的包装Filter来实现的,如我们的声卡是使用的Audio Captue Filter,他在内部有使用了以waveIn开头的一些API函数来实现(学过windows编程就知道,如waveInOpen).这些我们可以通过GraphEdit来检查。

 

下面我们在建一个完整的Filter Graph链路。如下:

                                     图(1

注意:这里用到了一个FilterWAV Dest。他是我们的DirectShow SDK中的一个例子,位于SDK安装目录的DirectShow\Samples\C++\DirectShow\Filters\WavDest下,主要的功能是在采集时,即PCM数据前部写入文件之后,再在文件头位置插入一个Wave格式的数据块。我们如果要使用到这里,我们必须首先需要编译这个例子并且注册成功后,才能从GraphEdit中添加到这里使用。

由于我们在写入文件的同时,我们需要实时监听,故我们可以使用Smart Tee,当然我们也可以使用Microsoft为我们在DirectShow SDK中提供的Infinite Pin Tee,位于SDK安装目录的DirectShow\Samples\C++\DirectShow\Filters\InfTee下,作用是将输入Pin上输入的数据复制多份(可以多于2),然后通过输出Pin发送出去。他的使用同WAV Dest

                                      图(2

    好了,现在开始编写我们的代码了,当然首先要创建一个Audio Capture Filter,由于不同的机器有不同的声卡,还有的机子可能不止一块声卡,所以我们要像采集视频是创建Video Capture Filter一样去枚举系统设备,找到了后再创建。在这里这个过程不在重复了。

并且Microsoft为我们提供了一个好的例子,位于SDK安装目录的DirectShow\Sample\C++

\DirectShow\Capture下的AudioCap。我们可以来参考它。

我们在GraphEdit中加入我们的声卡后会发现它只有一个Capture Output Pin,却有好几个Input Pin。需要说明的是,在GraphEdit中,这些Input Pin 并没有真正的数据流入,他们只是声卡的各个输入端子的象征性表示,所以这些Input Pin永远也不用连接。但是我们在选择了输入端子后,我们必须保证我们的物理连接正确。

AudioCap例子中使用了一个列表控件,同时他还给我们提供了很多很好的工具函数(mfcutil.cpp文件中, 当建立基于对话框的MFC应用程序时,它可以给我们带来方便),在这里我不赘述,大家可以自己看。

 

我们建立一个基于对话框的MFC应用程序AudioCap。下面是步骤:

①枚举并显示系统设备

HRESULT AuGraph::EnumAudioDevices(CListBox &inListBox)

{

    HRESULT hr = NOERROR;

    // EnumFiltersWithMonikerToListmfcutil.cpp中提供工具函数,可以为我们枚举系

    //统设备并添加到inListBox显示,inListBox为我们在对话框上的用于显示系统设备

    //CListBox控件的变量。

      hr= ::EnumFiltersWithMonikerToList(NULL,&CLSID_AudioInputDeviceCategory,

inListBox);

    if(FAILED(hr))

        return hr;

    return hr;

}

 

枚举并显示声卡的给个输入pin

HRESULT AuGraph::EnumAudioInputPin(CListBox &inListBox)

{

    HRESULT hr = NOERROR;

    // EnumPinsOnFiltermfcutil.cpp中提供工具函数,可以为我们枚举声卡的输入Pin

    //将他添加到inListBox中来显示,inListBox是我们在对话框上的用于显示声卡的

    //各个输入PinCListBox控件的变量。

    hr = ::EnumPinsOnFilter(m_pInputDev,PINDIR_INPUT,inListBox);

    if(FAILED(hr))

        return hr;

    return hr;

}

 

构建Filter Graph

HRESULT AuGraph::GetInterface()

{

    HRESULT hr = NOERROR;

    hr = CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,

                          IID_IGraphBuilder,(void **)&m_pGB);

    if(FAILED(hr))

        return hr;

    hr |= m_pGB->QueryInterface(IID_IMediaControl,(void **)&m_pMC);

    hr |= m_pGB->QueryInterface(IID_IMediaEventEx,  (void **)&m_pME);

    if(FAILED(hr))

        return hr;

    return hr;

}

 

现在创建声卡并加到Filter Graph

HRESULT AuGraph::CreateDevices(IMoniker * inMoniker)

{

    HRESULT hr = NOERROR;

    if(inMoniker)

    {

        hr = inMoniker->BindToObject(0,0,IID_IBaseFilter,(void **)&m_pInputDev);

        if(FAILED(hr))

            return hr;

        hr = m_pGB->AddFilter(m_pInputDev,L"Audio Capture");

        if(FAILED(hr))

            return hr;

    }

    return hr;

}

按图(1)或图(2)建立整个Filter Graph,在这里我就不写代码了,可以自己添加。

设置输入端子。这是采集音频与采集视频不同的地方,也是最重要的必不可少的一步。

在声卡Filter的给个输入Pin上我们都可以得到IAMAudioInputMixer接口,通过这个接口我们可以设置各个输入端子的音频属性,包括进行音频的合成时是否允许某个输入端子的音频参与混合、音频输入的音量、高音、低音等参数,当然这个接口还可以设置那个输入Pin为我们的输入端子。

HRESULT AuGraph::ActivateSelectInputPin(int inNum,int inIndex)// inNum是我们

//的声卡上输入pin的个数,inIndex是我们选择作为输入端子的pin的序号(注①)

{

    HRESULT hr = NOERROR;

    CComPtr<IPin>              pInputPin = NULL;

    CComPtr<IAMAudioInputMixer> pInputMixer = NULL;

    for(int i=0; i<inNum; i++)

    {

        hr = ::GetPin(m_pInputDev,PINDIR_INPUT,i,&pInputPin);

        if(SUCCEEDED(hr))

        {

            hr = pInputPin->QueryInterface(IID_IAMAudioInputMixer,

(void **)&pInputMixer);

            if(SUCCEEDED(hr))

            {

                if(i == inIndex)

                    hr = pInputMixer->put_Enable(TRUE);//设置该输入Pin为输入端子

                else

                    hr = pInputMixer->put_Enable(FALSE);

 

                if(pInputMixer)

                    pInputMixer = NULL;

            }

 

            if(pInputPin)

                pInputPin = NULL;

        }

    }

    return hr;

}

运行程序

   当然如果我们的Filter Graph构建成功并且各个Filter连接也成功,就可以运行了

HRESULT AuGraph::AudioRun()

{

    HRESULT hr = NOERROR;

    if(m_pMC)

    {

        hr = m_pMC->Run();

        if(S_FALSE == hr)

        {

            ::AfxMessageBox(L"The filter is not runing!");

            return hr;

        }

        m_bIsCapture = TRUE;

    }

    return hr;

}

    好了,这就是我们采集音频的整个过程,实际上他是很简单的,但也有一些问题需要我们注意。如:我们在选择了输入端子后,要保证它的物理连接。比如我们选择了麦克风作为输入端子,就要保证机子上的麦克风连接正确而且是可用的

注①:我在利用CListBox控件时,要注意它在显示时是对字符串自动排序显示的,所以我们要在它的属性Sort里去掉这个属性,设置Sortfalse。不然我们在选择输入pin时会出错,因为CListBox控件里显示的可能与我们同枚举得到的序号并不一致。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多