昨天努了一天,终于搞完了一个全手动连接的filter graph 程序,虽然很简单,但是还是费了老大的劲。不扯了,上菜……
开发环境:Vista+VS2008+DX9.0,控制台应用程序(便于调试)
第一步:在GraphEdit中模拟视频播放过程。
我的graph图如下,如果不能正常播放,先要注册需要的filter,我就注册了MPEG Layer-3 Decoder:
第二步:动手编程吧。
1、包含文件:
#include "stdafx.h"
#include <dshow.h>
#pragma comment(lib, "strmiids.lib")
2、定义自己注册的filter,在main()函数前进行定义:
static const GUID CLSID_AudioDecoder=
{ 0x38BE3000,0xDBF4,0x11D0,{0x86,0x0e,0x00,0xa0,0x24,0xcf,0xef,0x6d}};
3、定义需要用到的COM组件:
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
IBaseFilter *pInputFileFilter = NULL;
IBaseFilter *pAviSplitter= NULL;
IBaseFilter *pVideoDecoder= NULL;
IBaseFilter *pVideoColour= NULL;
IBaseFilter *pAudioDecoder= NULL;
IBaseFilter *pDVideoRenderer=NULL;
IBaseFilter *pDSoundRenderer = NULL;
IPin *pFileOut = NULL, *pSplitterIn=NULL,
*pSplitterOut1=NULL,*pSplitterOut2=NULL,
*pVideoDecIn = NULL,*pVideoDecOut = NULL,*pVideoColIn = NULL,*pVideoColOut = NULL,
*pVideoRenIn = NULL,*pAudioDecIn = NULL,*pAudioDecOut = NULL,*pAudioRenIn = NULL;
4、COM库初始化:
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return hr;
}
5、创建滤波器链表管理器(Filter Graph Manager),获得控制、事件的接口
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void **)&pGraph);
if (FAILED(hr))
{
printf("ERROR - Could not create the Filter Graph Manager.");
CoUninitialize();
return hr;
}
// Now get the media control interface...
hr = pGraph->QueryInterface(IID_IMediaControl,(void **)&pControl);
if (FAILED(hr))
{
pGraph->Release();
CoUninitialize();
return hr;
}
// And the media event interface.
hr = pGraph->QueryInterface(IID_IMediaEvent,(void **)&pEvent);
if (FAILED(hr))
{
pControl->Release();
pGraph->Release();
CoUninitialize();
return hr;
}
6、添加源滤波器,我指定了我的D盘上的start.avi文件:
hr = pGraph->AddSourceFilter(L"d://start.avi",L"source",&pInputFileFilter);
7、创建连接AVI分流器
hr = CoCreateInstance(CLSID_AviSplitter, NULL,CLSCTX_INPROC_SERVER,
IID_IBaseFilter,(void **)&pAviSplitter);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pAviSplitter, L"Avi Splitter");
//连接Source和splitter
if (SUCCEEDED(hr))
{
//获得空闲(未连接)的输出引脚
pFileOut =
GetPin(pInputFileFilter, PINDIR_OUTPUT);//GetPin函数很重要,在最后面讲
if (pFileOut!= NULL)
{
// Is the pin unconnected? Obtain the input pin of the Avi Splitter.
pSplitterIn= GetPin(pAviSplitter,PINDIR_INPUT);
if (pSplitterIn!= NULL)
{
// Is the pin good? Connect the pins together:
hr = pGraph->ConnectDirect(pFileOut, pSplitterIn,NULL);
}
}
}
}
8、创建连接视频解码器
hr = CoCreateInstance(CLSID_AVIDec, NULL,CLSCTX_INPROC_SERVER,
IID_IBaseFilter,(void **)&pVideoDecoder);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pVideoDecoder, L"Video Decoder");
//连接splitter和Video Decoder
if (SUCCEEDED(hr))
{
pSplitterOut1= GetPin(pAviSplitter,PINDIR_OUTPUT);//获取分流器输出引脚1
if (pSplitterOut1!= NULL)
{
// Is the pin good? Obtain the input pin of the Avi Decoder.
pVideoDecIn=GetPin(pVideoDecoder,PINDIR_INPUT);//获取AVI解码器输入引脚
if (pVideoDecIn!= NULL)
{
hr = pGraph->ConnectDirect(pSplitterOut1,pVideoDecIn,NULL);
}
}
}
}
9、创建连接视频色彩转换器
hr = CoCreateInstance(CLSID_Colour, NULL,CLSCTX_INPROC_SERVER,
IID_IBaseFilter,(void **)&pVideoColour);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pVideoColour, L"Video Colour");
//连接Video Decoder和Video Colour
if (SUCCEEDED(hr))
{
pVideoDecOut= GetPin(pVideoDecoder,PINDIR_OUTPUT);//获取解码器输出引脚
if (pVideoDecOut!= NULL)
{
pVideoColIn=GetPin(pVideoColour,PINDIR_INPUT);//获取色彩转换器输入引脚
if (pVideoColIn!= NULL)
{
hr = pGraph->ConnectDirect(pVideoDecOut,pVideoColIn,NULL);
}
}
}
}
10、创建连接视频渲染器
hr = CoCreateInstance(CLSID_VideoRendererDefault, NULL,CLSCTX_INPROC_SERVER,
IID_IBaseFilter,(void **)&pDVideoRenderer);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pDVideoRenderer, L"Video Renderer");
//连接Video Decoder和Video Renderer
if (SUCCEEDED(hr))
{
pVideoColOut= GetPin(pVideoColour,PINDIR_OUTPUT);//获取AVI解码器输出引脚
if (pVideoColOut!= NULL)
{
pVideoRenIn=GetPin(pDVideoRenderer,PINDIR_INPUT);//获取AVI渲染器输入引脚
if (pVideoRenIn!= NULL)
{
hr = pGraph->ConnectDirect(pVideoColOut,pVideoRenIn,NULL);
}
}
}
}
11、视频流连接完毕,音频流类似,所以在此一步到位。
// 创建连接音频解码器
hr = CoCreateInstance(CLSID_AudioDecoder, NULL,CLSCTX_INPROC_SERVER,
IID_IBaseFilter,(void **)&pAudioDecoder);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pAudioDecoder, L"Audio Decoder");
//连接splitter和Video Decoder
if (SUCCEEDED(hr))
{
pSplitterOut2= GetPin(pAviSplitter,PINDIR_OUTPUT);//获取分流器输出引脚2,两个输出引脚啊开始不知道怎么搞
if (pSplitterOut2!= NULL)
{
pAudioDecIn=GetPin(pAudioDecoder,PINDIR_INPUT);//获取AVI解码器输入引脚
if (pAudioDecIn!= NULL)
{
// Is the pin good? Connect the pins together:
hr = pGraph->ConnectDirect(pSplitterOut2,pAudioDecIn,NULL);
}
}
}
}
//// 创建连接音频渲染器
hr = CoCreateInstance(CLSID_DSoundRender, NULL,CLSCTX_INPROC_SERVER,
IID_IBaseFilter,(void **)&pDSoundRenderer);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pDSoundRenderer, L"Audio Renderer");
if (SUCCEEDED(hr))
{
pAudioDecOut= GetPin(pAudioDecoder, PINDIR_OUTPUT);//获取音频解码器输出引脚
if (pAudioDecOut!= NULL)
{
pAudioRenIn= GetPin(pDSoundRenderer,PINDIR_INPUT);//获取音频渲染器输入引脚
if (pAudioRenIn!= NULL)
{
hr = pGraph->ConnectDirect(pAudioDecOut,pAudioRenIn,NULL);
}
}
}
}
//音频渲染器创建连接完毕
12、所有的都连接好了,下面的控制就根据自己需要定,我就不写了大同小异,还有释放部分,我还不太懂就不误导大家了。下面看GetPin()函数,特别注意这个函数适合处理多个同方向的引脚查询。
IPin* GetPin(IBaseFilter *pFilter, PIN_DIRECTION dir)//查找 dir方向上的空闲PIN
{
IEnumPins* pEnumPins;
IPin* pOutpin;
PIN_DIRECTION pDir;
pFilter->EnumPins(&pEnumPins);
while (pEnumPins->Next(1,&pOutpin,NULL)==S_OK)
{
pOutpin->QueryDirection(&pDir);
if (pDir==dir)//方向对了
{
IPin *pTmp=0;
HRESULT hr=pOutpin->ConnectedTo(&pTmp);
if (SUCCEEDED(hr))// 已经连接了,不是我们想要的
;
else //第一个未连接的,就是它了
{
pEnumPins->Release();
return pOutpin;
}
}
pOutpin->Release();
}
pEnumPins->Release();
return 0;
}
结束语:
一切OK,调试一下没问题就可以收工了,我的反正没问题,呵呵
参考资料:
1、Visual C++音频/视频处理技术及工程实践。路锦正,周冬梅。电子工业出版社,
2、 http://hi.baidu.com/fenjianren/blog/item/7e27bbc46515f4ce38db497a.html