【视频处理工程】3、DirectShow基本开发过程(一)

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>

2、创建Filter Graph Manager对象

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所显示的信息如下。

【视频处理工程】3、DirectShow基本开发过程(一)_第1张图片

【视频处理工程】3、DirectShow基本开发过程(一)_第2张图片

从该工具中,我们可以得到这个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;
}

上述代码中存在一个死循环导致进程不会退出。在运行之后打开GraphStudioNext,并选择connect to remote graph并选择其中的选项后,界面上显示了filter graph中存在一个 lav splitter source,如下图所示。

【视频处理工程】3、DirectShow基本开发过程(一)_第3张图片

从图中可以看出,这个filter没有任何pin。这是因为没有加载任何文件。在这个filter上右键,选择choose source file,并选择一个视频文件加载后,filter上就出现了相应的输出pin,如下图所示:

【视频处理工程】3、DirectShow基本开发过程(一)_第4张图片

此时filter上出现了三个输出pin,分别用于处理视频、音频和字幕数据。由此可以看出,lav splitter source属于那种必须加载文件然后才能枚举pin的那类,可以参考一下这里http://bbs.csdn.net/topics/290018228。




你可能感兴趣的:(视频,filter,directshow)