习了好长一段时间的directshow,关于视频那一块的一些部分简单地过了一遍,如采集、解压缩、传输 等。现在回过头来学习音频的采集。现在将这几天的学习心得写下来。 我们知道directshow对我们的硬件的支持是通过特定的包装Filter来实现的,如我们的声卡是使用的Audio Captue Filter,他在内部有使用了以waveIn开头的一些API函数来实现(学过windows编程就知道,如waveInOpen).这些我们可以通过GraphEdit来检查。 下面我们在建一个完整的Filter Graph链路。如下: 图(1) 注意:这里用到了一个Filter,WAV 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; // EnumFiltersWithMonikerToList是mfcutil.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; // EnumPinsOnFilter是mfcutil.cpp中提供工具函数,可以为我们枚举声卡的输入Pin并 //将他添加到inListBox中来显示,inListBox是我们在对话框上的用于显示声卡的 //各个输入Pin的CListBox控件的变量。 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里去掉这个属性,设置Sort为false。不然我们在选择输入pin时会出错,因为CListBox控件里显示的可能与我们同枚举得到的序号并不一致。 |
|
来自: vcand3d > 《directshow》