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
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(&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。