DirectShow框架简介
DirectShow框架是多媒体播放框架上一个非常经典的框架,现在已经十多年了,在Windows平台上依然无法替代,非常值得去学习研究。个人觉得从设计模式的角度上看,directshow框架的灵活性、复用性、可维护性、可拓展性这些方面做得非常不错,也是它经久不衰历久弥新的一个原因,现在的很多第三方的decoder和filter都基于directshow框架开发,可以很灵活的移植到directshow视频框架中,例如视骏开发的HEVC/H.265
解码器,都可以直接挂载在directshow框架中进行视频解码。
图形化理解DirectShow
推荐一款工具GraphStudio,了解DirectShow框架必备工具,软件截图如下:
我们点击Graph
可以插入我们在电脑系统中注册的Filter
Render
,默认情况下,我们将播放的视频加到GraphStudio中,会自动生成directshow
的整个播放流程,然后就可以播放视频了。一般的播放效果流程如下:
GraphStudio
会自动采用系统默认的一套Filter
和Render
,如果安装了K-Lite Codec Pack,就可以修改系统默认的这一套,如下图:
我们想测试我们自己的Filter
和Render
,都可以自定义插入,下面就以DirectShow中植入视骏的HEVC
解码器为例子,了解DirecShow的整个播放流程,如下图所示:
DirectShow播放HEVC视频
可以参考雷老师关于DirectShow
的介绍,地址:http://blog.csdn.net/leixiaohua1020/article/details/42372419
播放的流程如下:
整个播放我们可以抽象出三个步骤:
- 注册:播放HEVC视频需要的
Strongene Mpeg-4 Demultiplexor
,Lentoid HEVC Decoder
,Video Renderer
- 链接:相当于上面
GraphStudio
图中的链接箭头 - 播放:将视频导入播放链路中,开始播放
注册Filter和Render
解码器属性
首先获取到这些Filter的Object name
、CLSID
、Filename
和FilePath
如果已经将filter注册进Windows的系统中,就只需要用到Object Name
,为了避免重新注册导致冲突;
声明DirectShow播放需要的类和变量
|
|
注册Filter:
- 加载解码器并获取DllGetClassObject指针,loadFilter()1234567891011121314151617bool loadFilter(LPCSTR chAx){//加载Filter所在的dll文件m_hInst = ::LoadLibrary(chAx);if (NULL == m_hInst){return false;}//获取DllGetClassObject函数指针m_pDllGetClassObject = ( DLL_GET_CLASS_OBJECT )::GetProcAddress(m_hInst, "DllGetClassObject");if (NULL == m_pDllGetClassObject){return false;}return true;}
- 动态创建解码器对象,createFilter()12345678910111213141516171819202122232425bool createFilter(GUID clId, IBaseFilter** pBaseFilter){if (NULL == m_pDllGetClassObject){return false;}//获取类工厂接口IClassFactory *p_IClassFactory = NULL;if (FAILED( m_pDllGetClassObject(clId, IID_IClassFactory, (void **)&p_IClassFactory) )){return false;}//创建与类厂相关联的COM对象(Filter),并获取其IBaseFilter接口p_IClassFactory->CreateInstance(NULL, IID_IBaseFilter, (void **)pBaseFilter);p_IClassFactory->Release();p_IClassFactory = NULL;if ((NULL == pBaseFilter) || (NULL == *pBaseFilter)){return false;}return true;}
- 申明一个方法函数InitRegister(),1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495bool InitRegister(){mGraph=NULL;mMediaControl=NULL;mEvent=NULL;mBasicVideo=NULL;mBasicAudio=NULL;mVideoWindow=NULL;mSeeking=NULL;if(!mGraph){//建立 filter graph managerHRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&mGraph);if(mGraph != NULL){LPCSTR splitterDll, HEVCDecoderDll;LPCWSTR splitterName, HEVCDecoderName;GUID splitterGuid, HEVCDecoderGuid;//Source Filterhr = CoCreateInstance(CLSID_AsyncReader, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void **)&m_pSourceFilter);if(hr == S_OK){mGraph->AddFilter(m_pSourceFilter, L"File Source Filter");}else{cout << "load source filter failed" << endl;return false;}//SplittersplitterDll = ".\\Codec\\mp4demux.dll";splitterName = L"Strongene Mpeg-4 Demultiplexor";splitterGuid = CLSID_SMp4Demultiplexor;//HEVCDecoderHEVCDecoderDll = ".\\Codec\\hevcdecfltr.dll";HEVCDecoderName = L"Lentoid HEVC Decoder";HEVCDecoderGuid = CLSID_HEVCDecoder;//Splitterif (loadFilter(splitterDll)){if (createFilter(splitterGuid, &m_pSplitter)){mGraph->AddFilter(m_pSplitter, splitterName);}else{cout << "add spliter failed" << endl;return false;}}else{cout << "load splitter faild" << endl;return false;}//HEVC Decoderif (loadFilter(HEVCDecoderDll)){if (createFilter(HEVCDecoderGuid, &m_pVideoHEVCDecoder)){mGraph->AddFilter(m_pVideoHEVCDecoder, HEVCDecoderName);}else{cout << "add decoder failed" << endlreturn false;}}else{cout << "load decoder failed!" << endl;return false;}//Video Rendererhr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void **)&m_pVideoRender);if(hr == S_OK){mGraph->AddFilter(m_pVideoRender, L"Video Mixing Renderer 9");}else{cout << "load video renderer failed" << endl;return false;}}}}
链接Filter和Renderer
查找空闲的filter的接口
12345678910111213141516171819202122232425262728293031323334353637383940414243HRESULT GetUnconnectedPin( IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin, int iNum ){*ppPin = NULL;IEnumPins* pEnum = NULL;IPin* pPin = NULL;int nIndex = 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) //如果out/in类型符合{nIndex ++;IPin *pTmp = NULL;hr = pPin->ConnectedTo(&pTmp); //如果已经被连接if (SUCCEEDED(hr)){pTmp->Release();}else{if(nIndex == iNum) //若端口号与传入端口号同{pEnum->Release();*ppPin = pPin;pPin->Release();return S_OK;}}}pPin->Release();}pEnum->Release();cout << "GetUnconnectedPin ----> Next is null" << endl;return false;}查找空闲的splitter的空闲指针接口
|
|
- DirectShow提供自动搜寻Filter,我们只需要将他们链接起来
|
|
播放HEVC视频
|
|