前文讲了一些开发DirectShow的基本配置方法以及一些基本的开发过程,如如何创造一个filter并加入filter graph中。这里继续上文的步骤讨论如何得到filter的pin,以及如何连接两个filter。
1、如何获取filter的pin
获取filter上的pin是连接filter之前必须的一步。主要思路是枚举filter上所有的pin,并通过QueryDirection检查pin的方向,以及通过connectedTo检查pin是否已经被链接。方法如下:
HRESULT GetUnconectedPin( IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin ) { *ppPin = 0; IEnumPins *pEnum = 0; IPin *pPin = 0; HRESULT hr = pFilter->EnumPins(&pEnum); if (FAILED(hr)) { return hr; } hr = pEnum->Reset(); while (pEnum->Next(1,&pPin,NULL) == S_OK) { PIN_DIRECTION ThisPinDirection; pPin->QueryDirection(&ThisPinDirection); if (ThisPinDirection == PinDir) { IPin *pTemp = 0; hr = pPin->ConnectedTo(&pTemp); if (SUCCEEDED(hr)) { //当前pin已经连接,无效; pTemp->Release(); } else { pEnum->Release(); *ppPin = pPin; return S_OK; } } pPin->Release(); } pEnum->Release(); return E_FAIL; }
需要注意的是,有些filter想要检索出其中的pin是有条件的,比如上文中提到的lav splitter source filter,只有在加载了视频文件之后,才可以检索出其pin。
2、连接两个filter
两个filter之间由上一级的输出pin连接到下一级的输入pin,实现的方法有IFilterGraph::ConnectDirect和IGraphBuilder::Connect实现,方法如下:
HRESULT ConnectFilters( IGraphBuilder *pGraph, IPin *pOut, IBaseFilter *pDest ) { if ((pGraph == NULL)||(pOut == NULL)||(pDest == NULL)) return E_POINTER; #ifdef _DEBUG PIN_DIRECTION PinDir; pOut->QueryDirection(&PinDir); _ASSERT(PinDir == PINDIR_OUTPUT); #endif // _DEBUG //得到下级filter的输入pin IPin *pIn = 0; HRESULT hr = GetUnconectedPin(pDest,PINDIR_INPUT,&pIn); if (FAILED(hr)) return hr; hr = pGraph->Connect(pOut,pIn); pIn->Release(); return hr; } HRESULT ConnectFilters( IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest) { if ((pGraph == NULL)||(pOut == NULL)||(pDest == NULL)) return E_POINTER; IPin *pOut = 0; HRESULT hr = GetUnconectedPin(pSrc,PINDIR_OUTPUT,&pOut); if (FAILED(hr)) return hr; hr = ConnectFilters(pGraph,pOut,pDest); pOut->Release(); return hr; }
下面我们依样画葫芦,讲lavvideo.ax和lavaudio.ax这两个组件进行注册,并利用相同的方法添加到filter graph中,并与lav splitter source进行连接,代码如下:
int _tmain(int argc, _TCHAR* argv[]) { //...... hr = AddFilterByCLSID(pGraph,CLSID_LavSplitter_Source,L"Lav Splitter Source",&pLavSplitterSource); hr = pLavSplitterSource->QueryInterface(IID_IFileSourceFilter,(void **)&pFileSourceFilter); hr = pFileSourceFilter->Load(fileName,NULL);//必须loadfile后才能检索pin hr = AddFilterByCLSID(pGraph,CLSID_LavVideoDecoder,L"Lav Video Decoder",&pLavVideoDecoder); hr = ConnectFilters(pGraph,pLavSplitterSource,pLavVideoDecoder); hr = AddFilterByCLSID(pGraph,CLSID_LavAudioDecoder,L"Lav Audio Decoder",&pLavAudioDecoder); hr = ConnectFilters(pGraph,pLavSplitterSource,pLavAudioDecoder); //...... }
上述代码段中CLSID_LavVideoDecoder和CLSID_LavAudioDecoder都是在GraphStudioNext中查找CLSID后定义在头文件中的,分别表示lavfilter的视频和音频解码器组件。用GraphStudioNext查看该进程的Filter Graph如下图所示:
由上图可见,视频和音频解码器分别已经链接在了source filter的视频和音频输出接口上,不过至于能不能正常使用现在还不得而知。我们的下一个目标就是通过用代码调用lav filter组件的方式手工播放这部电影。