设计自定义的Filter.docx
《设计自定义的Filter.docx》由会员分享,可在线阅读,更多相关《设计自定义的Filter.docx(15页珍藏版)》请在冰豆网上搜索。
设计自定义的Filter
如何设计自定义的transformfilter(转)
对于DIrectShow的初学者而言,最大的困难莫过于尝试设计自定义的filter。
设计自定义的transformfilter是困难的
因为首先filter是一种dll(后缀名为.ax)而编写dll工程需要一定的VC基础所以建议先补充一点dll的知识
其次dll的注册,GUID的生成和工程的配置都很麻烦。
再次网上缺乏现成的transformfilter的例子。
DirectShow给的源码比如NULLINPLACE和CONTRAST都太复杂,都带有对话框和属性页,不适合初学者,而且这些例子没有一个涉及到图像格式的转换,而transformfilter最大的公用就是媒体类型的转换,因此这些例子不适用
作为一个初学者,我深深受到这些问题的困扰,经过刻苦钻研终于走出了这个泥潭,豁然开朗。
于是把它记录下来,希望可以对其他人有帮助,也作为对08年的一个小结。
我的例子是设计一个transformfilter把YUY216bit的媒体转化为RGB2424bit的类型。
原因是我的摄像头只支持YUY216bit这种格式,我想得到位图。
。
顺便学习一下Filter的设计
以下为具体步骤:
一配置开发环境
1.VC中在Tools->Options->Directories设置好DirectXSDK的头文件和库文件路径
2.编译了基类源码,生成strmbasd.lib(debug版),strmbase.lib(release版)
3.VC向导新建一个win32DLL(empty)工程
4.Setting->Link->Outputfilename:
YUV2RGBfilter.ax
5.Setting->Link加入strmbasd.libwinmm.libquartz.libvfw32.lib (注意路径)
6.定义一个同名.def文件,加入到工程,内容如下:
LIBRARYYUV2RGBfilter.ax
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServerPRIVATE
7.建立一个类YUV2RGBfilter建立他的cpp文件和h文件
8.在YUV2RGBfilter.cpp中定义DLL的入口函数及注册 放在cpp文件的最后
//
//DllEntryPoint
//
extern"C"BOOLWINAPIDllEntryPoint(HINSTANCE,ULONG,LPVOID);
BOOLAPIENTRYDllMain(HANDLEhModule,
DWORDdwReason,
LPVOIDlpReserved)
{
returnDllEntryPoint((HINSTANCE)(hModule),dwReason,lpReserved);
}
////////////////////////////////////////////////////////////////////////
//
//Exportedentrypointsforregistrationandunregistration
//(inthiscasetheyonlycallthroughtodefaultimplementations).
//
////////////////////////////////////////////////////////////////////////
STDAPIDllRegisterServer()
{
returnAMovieDllRegisterServer2(TRUE);
}
STDAPIDllUnregisterServer()
{
returnAMovieDllRegisterServer2(FALSE);
}
9.cpp文件中要包含的头文件
#include
#include
#include
#include
#if(1100>_MSC_VER)
#include
#endif
#include"Y2Ruids.h" //ourownpublicguids
#include"YUV2RGBfilter.h"
二开发Filter
1.生成GUID(命令行模式下运行guidgen工具)为他建立一个文件Y2Ruids.h单独引用
#include
//YUV2toRGB24FilterObject
//{F91FC8FD-B1A6-49b0-A308-D6EDEAF405DA}
DEFINE_GUID(CLSID_YUV2toRGB24,
0xf91fc8fd,0xb1a6,0x49b0,0xa3,0x8,0xd6,0xed,0xea,0xf4,0x5,0xda);
2.构造CYUV2RGBfilter类继承自CTransformFilter 写在TransformFilter.h中
//----------------------------------------------------------------------------
//ClassdefinitionsofCYUV2RGBfilter
//----------------------------------------------------------------------------
//
//
classCYUV2RGBfilter:
publicCTransformFilter
{
public:
staticCUnknown*WINAPICreateInstance(LPUNKNOWNpunk,HRESULT*phr);
STDMETHODIMPNonDelegatingQueryInterface(REFIIDriid,void**ppv);
DECLARE_IUNKNOWN;
//overridepurevirtualfunction
HRESULTCheckInputType(constCMediaType*mtIn);
HRESULTCheckTransform(constCMediaType*mtIn,constCMediaType*mtOut);
HRESULTDecideBufferSize(IMemAllocator*pAlloc,ALLOCATOR_PROPERTIES*pProp);
HRESULTGetMediaType(intiPosition,CMediaType*pMediaType);
HRESULTTransform(IMediaSample*pIn,IMediaSample*pOut);
private:
//Constructor
CYUV2RGBfilter(TCHAR*tszName,LPUNKNOWNpunk,HRESULT*phr);
//memberfunction
VOIDChangeFormat(AM_MEDIA_TYPE*pAdjustedType);
DWORDConvertYUV2toRGB(BYTE*yuv,BYTE*rgb,DWORDdsize);
//membervariable
constlongm_lBufferRequest;
CCritSecm_Y2RLock; //Toserialiseaccess.
};
3.按格式改写构造函数
//
//CNullInPlace:
:
Constructor
//
CYUV2RGBfilter:
:
CYUV2RGBfilter(TCHAR*tszName,LPUNKNOWNpunk,HRESULT*phr):
CTransformFilter(tszName,punk,CLSID_YUV2toRGB24),
m_lBufferRequest
(1)
{
ASSERT(tszName);
ASSERT(phr);
}//CYUV2RGBfilter
4.改写CTransformFilter五个纯虚函数(最重要的地方)
HRESULTCheckInputType(constCMediaType*mtIn);
HRESULTCheckTransform(constCMediaType*mtIn,constCMediaType*mtOut);
HRESULTDecideBufferSize(IMemAllocator*pAlloc,ALLOCATOR_PROPERTIES*pProp);
HRESULTGetMediaType(intiPosition,CMediaType*pMediaType);
HRESULTTransform(IMediaSample*pIn,IMediaSample*pOut);
5.设计自己的私有函数完成一定的功能
6.注册Filter信息
//注册信息
//setupdata
constAMOVIESETUP_MEDIATYPE
sudPinTypes= {&MEDIATYPE_Video //clsMajorType
,&MEDIASUBTYPE_NULL}; //clsMinorType
constAMOVIESETUP_PIN
psudPins[]={{L"Input" //strName
,FALSE //bRendered
,FALSE //bOutput
,FALSE //bZero
,FALSE //bMany
,&CLSID_NULL //clsConnectsToFilter
,L"Output" //strConnectsToPin
,1 //nTypes
,&sudPinTypes} //lpTypes
,{L"Output" //strName
,FALSE //bRendered
,TRUE //bOutput
,FALSE //bZero
,FALSE //bMany
,&CLSID_NULL //clsConnectsToFilter
,L"Input" //strConnectsToPin
,1 //nTypes
,&sudPinTypes}}; //lpTypes
constAMOVIESETUP_FILTER
sudYUV2RGB={&CLSID_YUV2toRGB24 //clsID
,L"YUV2RGB" //strName
,MERIT_DO_NOT_USE //dwMerit
,2 //nPins
,psudPins}; //lpPin
//
//NeededfortheCreateInstancemechanism
//
CFactoryTemplateg_Templates[1]=
{ {L"YUV2RGB"
,&CLSID_YUV2toRGB24
,CYUV2RGBfilter:
:
CreateInstance
,NULL
,&sudYUV2RGB}
};
intg_cTemplates=sizeof(g_Templates)/sizeof(g_Templates[0]);
编译成功后生成GrayFilter.ax
命令行运行regsvr32GrayFilter.ax注册即可不用反复注册,只用注册一次,如若修改只需将重新编译的.ax覆盖原来的就行了
调试最好在graphEdit中经行比较方便。
以上就是设计一个filter的总体步骤。
三下面就关键点五个重载的纯虚函数做详细介绍。
这才是最关键的地方。
HRESULTCheckInputType(constCMediaType*mtIn);
HRESULTCheckTransform(constCMediaType*mtIn,constCMediaType*mtOut);
HRESULTDecideBufferSize(IMemAllocator*pAlloc,ALLOCATOR_PROPERTIES*pProp);
HRESULTGetMediaType(intiPosition,CMediaType*pMediaType);
HRESULTTransform(IMediaSample*pIn,IMediaSample*pOut);
这五个函数全部是都纯虚函数,是CTransformFilter为我们提供的接口,必须重载他们才能实例化。
初学者最大的困扰莫过于,是谁调用了这些函数。
这些函数调用的时候实参是从哪来的。
我一开始就被这些问题困扰。
其实DX的帮助文档里就讲的很清楚了只是我一开始没认真看;
CheckInputType是由tranformfiltr的输入pin调用的用来检查本Filter的输入媒体是否合法;
CheckTransform是由tranformfiltr的输出pin调用的用来检查本filter的输出是否和合法;
GetMediaType是有由tranformfiltr的输出pin调用的用来获取该输出端口支持的媒体格式供下游filter的枚举
DecideBufferSize是由tranformfiltr的输出pin调用的来确定buffer的数量和大小
上游filter通过调用filter上输入pin上的IMemInputPin:
:
Receive方法,将sample传递到filter,filter调用CTransformFilter:
:
Transform方法来处理数据
整个过程就是
输入pin调用CheckInputType来筛选上游过来的媒体类型,如果可以接受就有输出pin通GetMediaType来枚举输出媒体类型,进一步通过输出pin的CheckTransform来找到与输入媒体类型相融合的输出媒体类型并选中。
在通过DecideBufferSize确定输出buffer的属性,所有的检查和筛选通过以后就可以连接了,并通过tranform将输入pin上的sample传个输出pin输出媒体的类型是由GetMediaType来确定的,只要媒体类型对应了就可以成功连接但是数据的传送还是要通过transform来实现。
理论上对于没有压缩的视频,一个sample就是一帧的数据,可以精确的量化处理。
要实现输出pin上媒体格式的转化就必须在在GetMediaType函数中修改新的媒体格式,然后在checkTransform中确认输出的媒体格式是不是期望的输出。
例如要将YUY216bit的媒体格式改为RGB88bit的媒体格式就要做如下修改:
在GetMediaType中
CheckPointer(pMediaType,E_POINTER);
VIDEOINFO vih;
memset(&vih,0,sizeof(vih));
vih.bmiHeader.biCompression = 0;
vih.bmiHeader.biBitCount = 8;
vih.bmiHeader.biSize = 40;
vih.bmiHeader.biWidth = 640;
vih.bmiHeader.biHeight = 480;
vih.bmiHeader.biPlanes = 1;
vih.bmiHeader.biSizeImage = 307200;
vih.bmiHeader.biClrImportant= 0;
vih.bmiHeader.biClrUsed = 256;
//alterthepallete
for(UINTi=0;i<256;i++)
{
vih.bmiColors[i].rgbBlue=(BYTE)i;
vih.bmiColors[i].rgbRed=(BYTE)i;
vih.bmiColors[i].rgbGreen=(BYTE)i;
vih.bmiColors[i].rgbReserved=(BYTE)0;
}
pMediaType->SetType(&MEDIATYPE_Video);
pMediaType->SetFormatType(&FORMAT_VideoInfo);
pMediaType->SetFormat((BYTE*)&vih,sizeof(vih));
pMediaType->SetSubtype(&MEDIASUBTYPE_RGB8);
pMediaType->SetSampleSize(307200);
returnNOERROR;
然后在checkTransform中确认是否是期望的输出
BITMAPINFOHEADER*pNewType=HEADER(mtOut->Format());
if((pNewType->biPlanes==1)
&&(pNewType->biBitCount==8)
&&(pNewType->biWidth==640)
&&(pNewType->biHeight==480)
&&(pNewType->biClrUsed==256)
&&(pNewType->biSizeImage==307200))
{
returnS_OK;
}
我的实现过程如下
//GetMediaType
//
//Isupportonetype,namelythetypeoftheinputpin
//Wemustbeconnectedtosupportthesingleoutputtype
//
HRESULTCYUV2RGBfilter:
:
GetMediaType(intiPosition,CMediaType*pMediaType)
{
//Istheinputpinconnected
if(m_pInput->IsConnected()==FALSE)
{
returnE_UNEXPECTED;
}
//Thisshouldneverhappen
if(iPosition<0)
{
returnE_INVALIDARG;
}
//Dowehavemoreitemstooffer
if(iPosition>0)
{
returnVFW_S_NO_MORE_ITEMS;
}
CheckPointer(pMediaType,E_POINTER);
if(iPosition==0)
{
HRESULThr=m_pInput->ConnectionMediaType(pMediaType);
if(FAILED(hr))
{
returnhr;
}
}
//makesomeappropriatechange
ASSERT(pMediaType->formattype==FORMAT_VideoInf