MFC Filter
Filter并不总是在一个Win32 DLL中实现的。根据实际的需要,我们可能要在Filter
的实现中用到MFC,更或者我们将Filter实现在一个EXE应用程序中。下面介绍这样一种在
MFC应用程序中开发Filter的方式。
在应用程序中定义的Filter,严格来说,已经不能算是标准的COM组件了,它没有独
立的实现模块,不需要注册,当然也不能通过标准的COM类工厂来创建。创建这种Filter
的正确方法是,直接在应用程序中new出一个Filter类对象实例来。代码如下:
m_pFilterObject=new CFilterClass();
//Make the initial refcount 1 to match COM creation
m_pFilterObject->AddRef();
接下去,这个Filter就跟其他标准的通过CoCreateInstance函数创建的Filter一样使用
了。举例来说,假设我们在播放媒体文件的时候,想要知道一些数据流的信息(比如视频流
图像的宽、高、帧率等),另外完成一些简单功能(比如抓帧等)。但是,我们又不想使用
另外的DLL来开发Filter。于是,我们就可以在应用程序中定义一个Transform filter,并
插入到媒体文件播放的Filter链路中。代码如下:
class CAppTransform:public CTransInPlaceFilter
{
public:
CAppTransform(LPUNKNOWN pUnkOuter,HRESULT *phr);
HRESULT CheckInputType(const CMediaType *mtIn);
HRESULT Transform(IMediaSample *pSample);
};
然后,如下实现:
CAppTransform::CAppTransform(LPUNKNOWN pUnkOuter,HRESULT *phr):
CTransInPlaceFilter(NAME("App Trans-IP"),pUnkOuter,GUID_NULL,phr)
{
}
HRESULT CAppTransform::Transform(IMediaSample *pSample)
{
//Override to do someting inside the application
//Such as grabbing a poster frame...
return S_OK;
}
//Check if we can support this specific proposed type and format
HRESULT CAppTransform::CheckInputType(const CMediaType *pmt)
{
//We accept a series of raw media types
if(pmt->majortype==MEDIATYPE_Video &&
(pmt->subtype==MEDIASUBTYPE_RGB32||
pmt->subtype==MEDIASUBTYPE_RGB24||
pmt->subtype==MEDIASUBTYPE_RGB565||
pmt->subtype==MEDIASUBTYPE_RGB555||
pmt->subtype==MEDIASUBTYPE_UYVY||
pmt->subtype==MEDIASUBTYPE_YUY2))
{
return NOERROR;
}
return E_FAIL;
}
为了播放媒体文件,我们另外设计了一个类CAppGraphBuilder,用于Filter Graph的
构建。我们的思路是,首先使用IGraphBuilder::RenderFile构建一个完整的媒体文件回放
链路,然后找到Video Renderer(或者Overlay Mixer),在它之前插入我们自己的Transform
Filter。代码如下:
//给定一个媒体文件,创建一个回放链路,随后插入我们的Transform Filter
HRESULT CAppGraphBuilder::BuildFromFile(LPCWSTR pszFile)
{
DestroyGraph();
//创建一个Filter Graph
HRESULT hr=CoCreateInstance(
CLSID_FilterGraph,
NULL,
CLSCTX_INPROC,
IID_IGraphBuilder,
(void**)&m_pGraph);
if(FAILED(hr))
{
return hr;
}
AddToObjectTable();
//首先构建一个源文件的回放Filter Graph
hr=m_pGraph->RenderFile(pszFile,NULL);
if(FAILED(hr))
{
return hr;
}
//通过IVideoWindow接口找到Video Renderer
IBaseFilter *pVR;
hr=FindFilterByInterface(IID_IVideoWindow,&pVR);
if(FAILED(hr))
{
return hr;
}
//注意,如果Video Renderer的上一级Filter是Overlay Mixer,则将没有实际的
//数据传送到Video Renderer上,这种情况下,我们的Filter应该插在Overlay
//Mixer之前
IPin *pPin=InputPinOf(pVR); //获取Video Renderer的输入Pin
AM_MEDIA_TYPE mt;
pPin->ConnectionMediaType(&mt); //取得连接用的媒体类型
pPin->Release();
CMediaType mtIn=mt;
FreeMediaType(mt);
if(mtIn.subtype==MEDIASUBTYPE_Overlay)
{
//This connection may be a overlay mixer
//need to move upstream one place
IBaseFilter *pOvMix=NULL;
hr=NextUpstream(pVR,&pOvMix);
pVR->Release();
if(FAILED(hr))
{
return hr;
}
pVR=pOvMix;
}
//创建我们的Transform Filter
CreateAppFilter();
//将我们的Filter插入到已有的Filter链路中
hr=ConnectUpstreamof(pVR,m_pFilter);
pVR->Release();
return hr;
}
//创建我们的Transform Filter,注意不是用CoCreateInstance函数
void CAppGraphBuilder::CreateAppFilter(void)
{
if(m_pFilter)
{
m_pFilter->Release();
m_pFilter=NULL;
}
HRESULT hr=S_OK;
//创建Filter对象实例
m_pFilter=new CAppTransform(NULL,&hr);
//增加一个引用计数,防止COM的自动销毁
m_pFilter->AddRef();
//获取IBaseFilter接口
IBaseFilter *pFilter=NULL;
hr=m_pFilter->QueryInterface(IID_IBaseFilter,(void **)&pFilter);
if(SUCCEEDED(hr))
{
hr=m_pGraph->AddFilter(pFilter,L"App Transform");
pFilter->Release();
}
}
//将pTransform Filter(假设为一进一出)插入到pFilter的前面
HRESULT CAppGraphBuilder::ConnectUpstreamof(IBaseFilter *pFilter,
IBaseFilter *pTransform)
{
IPin *pPinIn=InputPinOf(pFilter);
if(!pPinIn)
{
return E_FAIL;
}
//Get the peer output pin
IPin *pPinOut=NULL;
HRESULT hr=pPinIn->ConnectedTo(&pPinOut);
if(FAILED(hr))
{
pPinIn->Release();
return hr;
}
//Disconnect the current connection
hr=m_pGraph->Disconnect(pPinOut);
if(SUCCEEDED(hr))
{
hr=m_pGraph->Disconnect(pPinIn);
}
//Insert pTransform filter by connecting its input pin and output pin
if(SUCCEEDED(hr))
{
IPin *pPinInXfm=InputPinOf(pTransform);
hr=m_pGraph->Connect(pPinOut,pPinInXfm);
pPinInXfm->Release();
}
if(SUCCEEDED(hr))
{
IPin *pPinOutXfm=OutputPinOf(pTransform);
hr=m_pGraph->Connect(pPinOutXfm,pPinIn);
pPinOutXfm->Release();
}
pPinIn->Release();
pPinOut->Release();
return hr;
}