DiectShow工程的开发需要提前编译相关的两个静态库,具体方法在前文【视频处理工程】1、DirectShow基本概念中已有叙述。这里假设我们的开发环境是Windows 7+Visual Studio 2010 Ultimate,来看如何利用DirectShow开发视频处理的应用程序。
新建工程:
作为demo,我们新建一个简单的控制台工程,命名为MyDirectShowProject1.sln。随后,我们需要对工程进行一些设置,将directShow等相关的头文件目录加入到包含目录中。
1、包含头文件:
除了工程默认的库目录之外,还需要找到directShow所在的sdk目录,将DirectShow的头文件目录添加到工程目录中。具体方法是在Project Properties->Configuration Properties->VC++ Directories->Include Directories中添加DirectShow的common和baseclasses目录。我的电脑上的目录位置是在:C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses和C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\common。
2、添加库目录:
分别对Debug和Release模式在Project Properties->Configuration Properties->VC++ Directories->Library Directories中加入DirectShow的lib文件的目录。如C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Release。
3、在工程中链接lib库文件
在Project Properties->Configuration Properties->Linker->Input->Additional Dependencies中加入strmbase.lib(Release模式)或者strmbasd.lib(Debug模式)。
开发过程:
1、首先包含directshow头文件:
#include <DShow.h>
Filter Graph Manager也是一类典型的COM对象,因此创建FGM也需要首先初始化COM库,然后调用CoCreateInstance()根据CLSID建立对象。
IGraphBuilder *pGraph = NULL; //FGM的接口之一,IGraphBuilder继承自IFilterGraph。IFilterGraph提供了将filter加入graph,连接/断开filters,删除filter等其他基本操作。 //IGraphBuilder则可以依据部分信息建立整个Filter Graph IMediaControl *pControl = NULL; //FGM的接口之一。IMediaControl控制Filter Graph中的数据流,包含Run,Stop,Paused方法。 IMediaEvent *pEvent = NULL; //FGM的接口之一。包含检索事件通知的方法,和重写FGM的默认事件处理函数。 DWORD dwGraphRegister; // Initialize the COM library. HRESULT hr = CoInitialize(NULL);//在当前线程中初始化COM库。 if (FAILED(hr)) { printf("ERROR - Could not initialize COM library"); return; } // Create the filter graph manager and query for interfaces. // 建立一个未经初始化的类实例,与某一CLSID相联系 // 建立Filter Graph Manager对象,对象指针为pGraph。 hr = CoCreateInstance(CLSID_FilterGraph, //class identifier is CLSID_FilterGraph NULL, //object is not being created as part of an aggregate CLSCTX_INPROC_SERVER, //The Filter Graph Manager is provided by an in-process DLL, so the execution context is CLSCTX_INPROC_SERVER. IID_IGraphBuilder, //interface ID,类名的识别码——A reference to the identifier of the interface . (void **)&pGraph); //输出指针,指向由上一个参数规定的对象。 if (FAILED(hr)) { printf("ERROR - Could not create the Filter Graph Manager."); return; }为了使我们建立的filter graph manager可以在GraphEdt中显示出来,我们需要将这个对象加入活动对象表ROT中,而在程序结束该对象被析构之前需要将其从ROT中移除。实现这个功能需要分别调用下面两个函数。
HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) { IMoniker * pMoniker = NULL; IRunningObjectTable *pROT = NULL; if (FAILED(GetRunningObjectTable(0, &pROT))) { return E_FAIL; } const size_t STRING_LENGTH = 256; WCHAR wsz[STRING_LENGTH]; StringCchPrintfW( wsz, STRING_LENGTH, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId() ); HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker); if (SUCCEEDED(hr)) { hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph, pMoniker, pdwRegister); pMoniker->Release(); } pROT->Release(); return hr; } void RemoveFromRot(DWORD pdwRegister) { IRunningObjectTable *pROT; if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) { pROT->Revoke(pdwRegister); pROT->Release(); } }
3、在Filter Graph Manager中添加Filter
给定了Filter的CLSID之后,将该filter添加到Filter Graph Manager中可以通过CoCreateInstance创建后通过IGraphBuilder::AddFilter添加。以下函数AddFilterByCLSID封装了这两个函数实现了该功能:
HRESULT AddFilterByCLSID( IGraphBuilder *pGraph, const GUID& clsid, LPCWCHAR wszName, IBaseFilter **ppF ) { if (!pGraph || !ppF) return E_POINTER; *ppF = 0; IBaseFilter *pF = 0; HRESULT hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pF)); if (SUCCEEDED(hr)) { hr = pGraph->AddFilter(pF, wszName); if (SUCCEEDED(hr)) *ppF = pF; else pF->Release(); } return hr; }
通常,每一个filter的文件都已一个后缀名为ax的动态链接库实现。而且在调用之前需要将这个filter在注册表中注册,注册方式为regsvr32.exe filter.ax。注册之后在graphedt中便可以查看这个filter的CLSID等信息。但是包括我自己在内的很多人都遇到一个问题,就是在graphedt中试图添加一个DirectShow Filter是会出现程序崩溃的情况,因此在这里https://code.google.com/p/graph-studio-next/source/checkout下载了另一工具graphstudionext替代,界面如下图所示。在软件冲选择graph->Insert Filter,并在DirectShow Filter中选择了lav splitter source这个filter所显示的信息如下。
从该工具中,我们可以得到这个filter的CLSID并用上文实现的方法将一个LAV Splitter Source filter加入到filter graph manager中。
现在,我们可以尝试着将一个lav splitter source用上述方法添加到filter graph中。代码如下:
#include "stdafx.h" #include "DirectShowAPI.h" #include "global.h" int _tmain(int argc, _TCHAR* argv[]) { IGraphBuilder *pGraph = NULL; //FGM的接口之一,IGraphBuilder继承自IFilterGraph。IFilterGraph提供了将filter加入graph,连接/断开filters,删除filter等其他基本操作。 //IGraphBuilder则可以依据部分信息建立整个Filter Graph IMediaControl *pControl = NULL; //FGM的接口之一。IMediaControl控制Filter Graph中的数据流,包含Run,Stop,Paused方法。 IMediaEvent *pEvent = NULL; //FGM的接口之一。包含检索事件通知的方法,和重写FGM的默认事件处理函数。 DWORD dwGraphRegister; // Initialize the COM library. HRESULT hr = CoInitialize(NULL);//在当前线程中初始化COM库。 if (FAILED(hr)) { printf("ERROR - Could not initialize COM library"); return 0; } // Create the filter graph manager and query for interfaces. // 建立一个未经初始化的类实例,与某一CLSID相联系 // 建立Filter Graph Manager对象,对象指针为pGraph。 hr = CoCreateInstance(CLSID_FilterGraph, //class identifier is CLSID_FilterGraph NULL, //object is not being created as part of an aggregate CLSCTX_INPROC_SERVER, //The Filter Graph Manager is provided by an in-process DLL, so the execution context is CLSCTX_INPROC_SERVER. IID_IGraphBuilder, //interface ID,类名的识别码——A reference to the identifier of the interface . (void **)&pGraph); //输出指针,指向由上一个参数规定的对象。 hr = AddToRot(pGraph,&dwGraphRegister); // //向Filter Graph中添加filter IBaseFilter *pLavSplitterSource, *pFileSourceFilter; hr = AddFilterByCLSID(pGraph,CLSID_LavSplitter_Source,L"Lav Splitter Source",&pLavSplitterSource); while (1) { } RemoveFromRot(dwGraphRegister); pGraph->Release(); return 1; }
从图中可以看出,这个filter没有任何pin。这是因为没有加载任何文件。在这个filter上右键,选择choose source file,并选择一个视频文件加载后,filter上就出现了相应的输出pin,如下图所示:
此时filter上出现了三个输出pin,分别用于处理视频、音频和字幕数据。由此可以看出,lav splitter source属于那种必须加载文件然后才能枚举pin的那类,可以参考一下这里http://bbs.csdn.net/topics/290018228。