| 修's profile玻璃玻璃小酒杯PhotosBlogLists | Help |
|
|
玻璃玻璃小酒杯没有啤酒没有咖啡,漂亮女孩打开窗,弹钢琴,叮叮当 January 08 directshow抓取解码后的音频数据最近在研究directshow,想从directshow中得到解码后的声音数据,存放成wav文件。directshow播放音频文件大概是这样一个流程
分别对应mp3文件,wma文件,wav文件 一个source filter,一个render filter,中间是些transform filter。 要获得解码后的声音数据,就应该在render filter和transform filter中间插一个filter来抓取声音数据存放到自己定义的缓存或者文件中 。如下图所示
大致步骤就是: 1,开发一个grabber filter 2,把这个filter加到directshow的播放链路中,在directsound device之前,如果不需要回放,可以把render filter可以删除。 3,存储数据 第一步,directshow sdk的sample里有个Grabber filter。我直接拿它来用,编译完成后,然后regsvr32 grabber.ax。filter中就多了 一个samplegrabbersample,我在graphedit里调试的时候发现还有一个samplegrabber filter。以后我用的是哪个我也糊里糊涂的。
第二步,addfilter,直接上代码吧 包含的头文件 #include <streams.h> #include <dshow.h> #include <qedit.h> #include "CapAudioCB.h" 定义的接口和对象 IGraphBuilder *m_pGB; IMediaControl *m_pMC; //MediaEvent 用来通知播放完毕 IMediaEvent *m_pEvent; IBaseFilter *m_pGrabberF;
ISampleGrabber *m_pGrabber; //为了回调函数 CCapAudioCB m_CB; 方法 //set to null void SetDefaults(); //init dxshow HRESULT InitializeCapture(); //get interfaces HRESULT GetInterfaces(); void ReleaseCapture(void); void Error( TCHAR * pText ); HRESULT ConnectFilters(IGraphBuilder *pGraph, IPin *pOut, IBaseFilter *pDest); HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pOut,IBaseFilter *pDest); HRESULT GetUnconnectedPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin) 方法的实现
HRESULT InitializeCapture() { HRESULT hr; SetDefaults();
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr = GetInterfaces())) return hr; return hr;
} HRESULT GetInterfaces()
{ HRESULT hr; // Create the filter graph. hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGB); if (FAILED(hr) || !m_pGB) return E_NOINTERFACE; //Create the media ctrol hr=m_pGB->QueryInterface(IID_IMediaControl,(void**)&m_pMC); if (FAILED(hr) || !m_pGB) return E_NOINTERFACE; //Create the media event hr=m_pGB->QueryInterface(IID_IMediaEvent,(void**)&m_pEvent); if (FAILED(hr) || !m_pEvent) return E_NOINTERFACE; // Create the capture graph builder. hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**) &m_pGrabberF);
if (FAILED(hr) || !m_pGrabberF)
return E_NOINTERFACE; //获得SampleGrabber接口 hr = m_pGrabberF->QueryInterface(IID_ISampleGrabber,(void**)&m_pGrabber); if (FAILED(hr) || !m_pGrabber) { return E_NOINTERFACE; } //If TRUE, the filter copies sample data into an internal buffer. m_pGrabber->SetBufferSamples(TRUE); //设置sampleGrabber的回调函数,每次sample来的时候会调用一次 //设置为1对应的是sampleGrabberCB的BufferCB函数 hr = m_pGrabber->SetCallback( &m_CB, 1 ); if (FAILED(hr)) { return E_NOINTERFACE; } return hr; //设置filter的媒体类型,才能进行connect。 CMediaType audioType; audioType.SetType(&MEDIATYPE_Audio); hr = m_pGrabber->SetMediaType( &audioType ); // shouldn't fail if( FAILED( hr ) ) { Error( TEXT("Could not set audio type")); return hr; } }
//这个函数中的宏SAFE_RELEASE可以自己写就是个释放语句 void ReleaseCapture(void) { SAFE_RELEASE(m_pGB); SAFE_RELEASE(m_pMC); SAFE_RELEASE(m_pGrabberF); SAFE_RELEASE(m_pGrabber); } //一下三个函数是从SDK的帮助里copy过来的 HRESULT ConnectFilters( IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest) { if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL)) { return E_POINTER; } // Find an output pin on the first filter.
IPin *pOut = 0; HRESULT hr = GetUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut); if (FAILED(hr)) { return hr; } hr = ConnectFilters(pGraph, pOut, pDest); pOut->Release(); return hr; } HRESULT CTestSampleGrabberDlg::GetUnconnectedPin( IBaseFilter *pFilter, // Pointer to the filter. PIN_DIRECTION PinDir, // Direction of the pin to find. IPin **ppPin) // Receives a pointer to the pin. { *ppPin = 0; IEnumPins *pEnum = 0; IPin *pPin = 0; HRESULT hr = pFilter->EnumPins(&pEnum); if (FAILED(hr)) { return hr; } while (pEnum->Next(1, &pPin, NULL) == S_OK) { PIN_DIRECTION ThisPinDir; pPin->QueryDirection(&ThisPinDir); if (ThisPinDir == PinDir) { IPin *pTmp = 0; hr = pPin->ConnectedTo(&pTmp); if (SUCCEEDED(hr)) // Already connected, not the pin we want. { pTmp->Release(); } else // Unconnected, this is the pin we want. { pEnum->Release(); *ppPin = pPin; return S_OK; } } pPin->Release(); } pEnum->Release(); // Did not find a matching pin. return E_FAIL; } HRESULT CTestSampleGrabberDlg::ConnectFilters(
IGraphBuilder *pGraph, // Filter Graph Manager. IPin *pOut, // Output pin on the upstream filter. IBaseFilter *pDest) // Downstream filter. { if ((pGraph == NULL) || (pOut == NULL) || (pDest == NULL)) { return E_POINTER; } #ifdef debug PIN_DIRECTION PinDir; pOut->QueryDirection(&PinDir); _ASSERTE(PinDir == PINDIR_OUTPUT); #endif // Find an input pin on the downstream filter.
IPin *pIn = 0; HRESULT hr = GetUnconnectedPin(pDest, PINDIR_INPUT, &pIn); if (FAILED(hr)) { return hr; } // Try to connect them. hr = pGraph->Connect(pOut, pIn); pIn->Release(); return hr; } //消息显示函数 void Error( TCHAR * pText ) { ::MessageBox( NULL, pText, TEXT("Error!"), MB_OK | MB_TASKMODAL | MB_SETFOREGROUND ); } 现在基本上前期工作已经准备完毕,接下来具体怎么来把sg filter加到m_pGB的合适位置中去。我用graphedit先做了一个模拟,发现 如果系统智能连接的话,会把这个filter加到decoder之前,很恼火,只能自己手动来加个个filter然后connect他们。这样做又太烦,先
让m_pGB自己renderfile一次自动建立一个链路,然后枚举这个m_pGB的filter把render filter disconncet掉然后把sg filter加进去。
其中用到的FindFilterFromName,FindPinFromFilter,AddGraphFiltersToList等函数在sdk的samples的common里的dshowutil 和mfcutil中,一下代码仅仅针对MP3,如果播放wma和wav,自己修改一下。
IBaseFilter *pSrc; IPin *pPin; HRESULT hr; long samplebufsize=0; m_pGB->RenderFile(L"e:\\3.mp3",NULL); hr = m_pGB->AddFilter(m_pGrabberF, L""); if (FAILED(hr)) { return; } IEnumFilters *pEnum = NULL; IBaseFilter *pFilter = NULL; //把input pin连接到上层filter的output pin pFilter=FindFilterFromName(m_pGB,L"MPEG Layer-3 Decoder"); pPin=FindPinFromFilter(pFilter,PINDIR_OUTPUT); hr=m_pGB->Disconnect(pPin); if (FAILED(hr)) { return ; } hr=ConnectFilters(m_pGB, pFilter, m_pGrabberF); if (FAILED(hr)) { return ; } //把output pin连接到下层filter的input pin pFilter=FindFilterFromName(m_pGB,L"Default DirectSound Device"); pPin=FindPinFromFilter(pFilter,PINDIR_INPUT); hr=m_pGB->Disconnect(pPin); if (FAILED(hr)) { return ; } hr=ConnectFilters(m_pGB, m_pGrabberF,pFilter); if (FAILED(hr)) { return ; } pFilter->Release(); pPin->Release(); pSrc->Release(); AddGraphFiltersToList(m_pGB,m_CList1); m_pMC->Run();
long evCode = 0; m_pEvent->WaitForCompletion(INFINITE, &evCode); Error(L"complete"); 有关回调函数,看sdk的帮助好像只能回调ISampleGrabberCB的函数,于是自己继承了一个,很简单的继承,只是写文件。
class CCapAudioCB : public ISampleGrabberCB { public: CFile pfl; CCapAudioCB( ) { pfl.Open(L"E:\\3w.wav",CFile::modeCreate|CFile::modeWrite); }
~CCapAudioCB( ) { pfl.Close(); }
// fake out any COM ref counting
// STDMETHODIMP_(ULONG) AddRef() { return 2; } STDMETHODIMP_(ULONG) Release() { return 1; } // fake out any COM QI'ing
// STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) { if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown ) { *ppv = (void *) static_cast<ISampleGrabberCB*> ( this ); return NOERROR; } return E_NOINTERFACE; } // we don't implement this interface for this example
// STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample ) { return 0; } STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize )
{ if (!pBuffer) return E_POINTER; pfl.Write((void*)pBuffer,lBufferSize); return 0; } }; 这样编译完,运行后,生成的wav文件并没有头,需要自己加上。忙了一天,搞定这么个简单的事情,还是有点成就感的。 June 01 真实的低调的不疯狂的
April 24 会疯掉的
April 14 小人闲居为不善
February 11 回家的路(下)
February 10 回家的路(上)
January 09 很惊恐以下是一位网友在BBS上发的帖子:
"一个朋友的同事新年猝死"
"31岁,应用程序架构经理,曾经是最佳员工。家人打电话给公司说失去联系很多天,
结果HR和JC一起到他的家里一看,人死在床上,据说可能是心脏问题猝死" 很惊恐,保佑亲人朋友和自己身体健康,平平安安。。。 January 04 真的着急了
December 27 你们两个都是仙女儿
|
|||||||||||
|
|