DirectShow应用:用程序连接Filters实现媒体播放
Windows平台下的音视频应用,多以DirectShow为开发工具,通过Graphedit选择合适的Filters并连接成实际的链路,就可以实现简单播放。但在程序中如何实现呢,笔者带着这个问题进行了探索,现将初步成果呈现出来,以期收抛砖引玉之效,共同提高。
一. 枚举Filters
Windows系统中的Filters都经过了注册,并按照Filter目录进行组织,故为方便应用程序设计,宜将系统中所有的Filter依照其目录结构枚举出来,统一存放和管理,为此,设计了一个类CFilterManager来进行统一的管理,其定义如下:
#pragma once
#include "strmif.h"
#include "CurrentDevice.h"
#include "Category.h"
#include "ReadyFilter.h"
#pragma comment(lib,"strmiids.lib")
#pragma comment(lib,"quartz.lib")
class CFilterManager
{
public:
~CFilterManager(void);
HRESULT init(void);
IBaseFilter *GetFilterByFriendlyName(CString friendlyName );
IBaseFilter *GetFilterByClsid( CLSIDclsid );
IBaseFilter *GetFilterByDisplayName(CString displayName );
staticCFilterManager *GetInstance( void );
voidPlayFiles( void );
public:
HRESULT EnumFilters(IEnumMoniker*pEnumCat, CLSID category);
CList<CCategory *, CCategory *>mCategoryList;
CList<CReadyFilter *, CReadyFilter*> mReadyFilterList;
private:
HRESULT ConnectFilters( IGraphBuilder*pGraph, IBaseFilter *pSource, IBaseFilter *pTarget );
CFilterManager(void);
voidCreateCategory( void );
voidCreateFilters( void );
CComPtr<ICreateDevEnum>pCreateDevEnum;
staticCFilterManager *pManager;
};
成员变量pCreateDevEnum用于存放系统枚举指针,列表mCategoryList存放所有Filter目录项,列表mReadyFilterList用于存放所有枚举到的Filter。
系统中只应有一个Filter管理者实例,故此类设计成单件Singlton模式,通过静态函数GetInstance获取其唯一实例,其实现代码如下:
CFilterManager *CFilterManager::GetInstance( void )
{
if(NULL == pManager )
{
pManager = new CFilterManager();
if(NULL != pManager )
{
HRESULT hr =pManager->init();
if( FAILED(hr) )
{
pManager =NULL;
return NULL;
}
pManager->CreateCategory();
pManager->CreateFilters();
}
}
returnpManager;
}
如上所示,在第一次创建实例成功后,即调用init函数进行COM组件库的初始化,如初始化失败则撤销新建的实例,返回NULL以阻止后续程序操作。初始化成功后,再调用函数CreateCategory枚举系统中所有的Filter目录项,并存于列表mCategoryList中,调用CreateFilters枚举所有的Filters存于列表mReadFilterList中,这样所有的Filter都在掌握之中,方便查取。由于这几个函数只需调用一次,故放在这里与创建唯一实例的代码连成一体。
HRESULTCFilterManager::init(void)
{
// Initializethe COM library
HRESULT hr = CoInitializeEx( NULL,COINIT_APARTMENTTHREADED );
if(FAILED(hr) )
{
AfxMessageBox( _T("COM initializing failed!") );
returnhr;
}
// Create thesystem device enumerator.
hr =CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
returnhr;
}
以上代码首先调用CoInitializeEx初始化COM库,然后取得系统设备枚举器指针pCreateDevEnum供后面的代码使用。
void CFilterManager::CreateCategory( void )
{
ASSERT( NULL != pCreateDevEnum );
HRESULT hr;
CComPtr<IEnumMoniker> pEmCat;
CComPtr<IPropertyBag> pPropBag;
// Use themeta-category that contains a list of all categories.
hr =pCreateDevEnum->CreateClassEnumerator( CLSID_ActiveMovieCategories,&pEmCat, NULL );
// Enumerateover every category and get the category CLSID and description.
if(FAILED(hr) )
{
return;
}
CComPtr<IMoniker> pMoniker;
ULONG wished = 1, fetched = 1;
while( S_OK== pEmCat->Next( wished, &pMoniker, &fetched ) )
{
VARIANT varCatName;
VariantInit(&varCatName);
VARIANT varCatClsid;
VariantInit(&varCatClsid);
CLSID clsidCat;
// Associatemoniker
hr = pMoniker->BindToStorage(0, 0,IID_IPropertyBag, (void **)&pPropBag);
//Read CLSID string from property bag
if(SUCCEEDED(hr))
{