转载请标明是引用于 http://blog.csdn.net/chenyujing1234
例子代码:
http://www.rayfile.com/zh-cn/files/ee306f61-71a1-11e1-8468-0015c55db73d/
参考书本:
《DirectShow开发指南》
1、添加入口函数.
Filter是个基于DLL的进程内的com组件,所以一般的Filter都要实现下面几个入口函数
在cim_capture.def中添加
; cim_capture.def : Filter exports
LIBRARY "cim_capture.dll"
EXPORTS DllMain PRIVATE DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE
2、然后要定义这些函数的实现
其实这些工作dshow的基类里都已经替我们做好了,我们所要做的就拿来用就是了,最重要的三个函数的实现一般如下
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2(true);
}
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2(false);
}
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}
显然
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
dshow的基类里都已经替我们做好了
可以看到AMovieDllRegisterServer2是在dllsetup.h中定义的。
而在PC机的DX9上是在
C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\dllsetup.cpp这个文件定义的
DllEntryPoint在PC的DX9是在C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\dllentry.cpp定义;
3、添加头文件和lib
首先包含
#include
如果是Release版本则包含
strmbase.lib strmiids.lib Msvcrt.lib Winmm.lib
如果是Debug版本则包含
strmbasd.lib strmiids.lib Msvcrtd.lib Winmm.lib
在PC的DX9环境下是
c:\DX90SDK\Samples\C++\DirectShow\BaseClasses\debug\strmbasd.lib msvcrtd.lib quartz.lib vfw32.lib winmm.lib kernel32.lib advapi32.lib version.lib largeint.lib user32.lib gdi32.lib comctl32.lib ole32.lib olepro32.lib oleaut32.lib uuid.lib
4、实现Filter的com接口
Filter是如何实现com接口的,它和其他的com实现方式的不同。
我们知道一个Filter是一个com组件,所以它com特性的实现其实在其基类中实现的,比如IUnknown接口,我们直接从基类派生出我们的Filter后,它就支持com接口了,它就是一个com组件了。
所有的com组件为了实现二进制的封装,所以连创建的接口都封装了,因此每个com对象都有个类对象(也叫类厂对象,本身也是com对象,用来创建com组件)来创建com组件。
下面温习一下com组件的创建过程,其中涉及到几个函数
1 当客户端要创建一个com组件时,它通过底层的COM API函数 CoGetClassObject()使用SCM的服务,这个函数请SCM把一个指针绑定到客户端请求的com组件的类对象上,
其实在CoGetClassObject()里它装载了该DLL的库,通过该dll的导出函数DllGetClassObject();DllGetClassObject根据客户端提供的com组件CLASSID,返回该com组件类对象的指针。下面com组件的创建就是SCM无关了。
2 客户端利用组件的类对象(类厂对象)的IClassFactory::CreateInstance方法创建com组件。
Filter在这里使用了一个类厂模板类来当作Filter的类厂对象。
下面看看类厂在DShow是怎么工作的。
类厂对象也是一个com组件。本来DllGetClassObject是我们自己写的一个函数,在directshow里已经完成了,我们不用自己来完成它了。它的功能就是来寻找这个DLL中的类厂对象,看是否有符合客户端请求的类厂对象。
DLL里声明了一个全局的类厂模板数组,当DllGetClassObject请求类厂对象的时候,它就搜索这个数组,看是否有和CLSID匹配的类厂对象。当它找到一个匹配的CLSID,它就创建一个类厂对象,然后讲类厂指针返回给CoGetClassObject,然后客户端可以根据返回去的类厂指针,调用IClassFactory::CreateInstance方法创建组件,类厂就根据数组里定义的方法创建com组件。
factory template包含下列变量:
const WCHAR * m_Name; // Name
const CLSID * m_ClsID; // CLSID
LPFNNewCOMObject m_lpfnNew; // Function to //create an instance of the component
LPFNInitRoutine m_lpfnInit; // Initialization function (optional)
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; // Set-up information (for filters)
其中的两个函数指针m_lpfnNew and m_lpfnInit使用下面的定义
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
你可以参照如下的方式定义你的类厂对象
以下是我们com组件创建函数
CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
CMyFilter *pFilter = new CMyFilter( pUnk, pHr);
if (pFilter== NULL)
{
*pHr = E_OUTOFMEMORY;
}
return pFilter;
}
你可以声明自己的类厂数组如下:如果在这个com组件中你要支持多个filter,你可以在这个数组中继续添加就是了。
CFactoryTemplate g_Templates[] =
{
{
L"my filter", // 名字
CMyFilter::m_sudFilter.clsID, // CLSID
CMyFilter::CreateInstance, // 创建一个实例的方法
NULL, // 初始化函数
&CMyFilter::m_sudFilter // 设置数据、信息 (for filters)
}
};
int g_cTemplates = NUMELMS(g_Templates);
5、如何实现自己的Filter
1 选择一个基类,声明自己的类
创建filter很简单,你只要根据自己的需要选择不同的基类Filter派生出自己的Filter,它就已经支持com特性了。
从逻辑上考虑,在写Filter之前,选择一个合适的Filter基类是至关重要的。为此,你必须对几个Filter的基类有相当的了解。在实际应用中,Filter的基类并不总是选择CBaseFilter的。相反,因为我们绝大部分写的都是中间的传输Filter(Transform Filter),所以基类选择CTransformFilter和CTransInPlaceFilter的居多。如果我们写的是源Filter,我们可以选择CSource作为基类;如果是Renderer Filter,可以选择CBaseRenderer或CBaseVideoRenderer等。
总之,选择好Filter的基类是很重要的。当然,选择Filter的基类也是很灵活的,没有绝对的标准。能够通过CTransformFilter实现的Filter当然也能从CBaseFilter一步一步实现。下面,笔者就从本人的实际经验出发,对Filter基类的选择提出几点建议供大家参考。
首先,你必须明确这个Filter要完成什么样的功能,即要对Filter项目进行需求分析。请尽量保持Filter实现的功能的单一性。如果必要的话,你可以将需求分解,由两个(或者更多的)功能单一的Filter去实现总的功能需求。
其次,你应该明确这个Filter大致在整个Filter Graph的位置,这个Filter的输入是什么数据,输出是什么数据,有几个输入Pin、几个输出Pin等等。你可以画出这个Filter的草图。弄清这一点十分重要,这将直接决定你使用哪种“模型”的Filter。比如,如果Filter仅有一个输入Pin和一个输出Pin,而且一进一处的媒体类型相同,则一般采用CTransInPlaceFilter作为Filter的基类;如果媒体类型不一样,则一般选择CTransformFilter作为基类。
再者,考虑一些数据传输、处理的特殊性要求。比如Filter的输入和输出的Sample并不是一一对应的,这就一般要在输入Pin上进行数据的缓存,而在输出Pin上使用专门的线程进行数据处理。这种情况下,Filter的基类选择CSource为宜(虽然这个Filter并不是源Filter)。
当Filter的基类选定了之后,Pin的基类也就相应选定了。接下去,就是Filter和Pin上的代码实现了。有一点需要注意的是,从软件设计的角度上来说,应该将你的逻辑类代码同Filter的代码分开。
下面,我们一起来看一下输入Pin的实现。你需要实现基类所有的纯虚函数,比如CheckMediaType等。在CheckMediaType内,你可以对媒体类型进行检验,看是否是你期望的那种。因为大部分Filter采用的是推模式传输数据,所以在输入Pin上一般都实现了Receive方法。有的基类里面已经实现了Receive,而在Filter类上留一个纯虚函数供用户重载进行数据处理。这种情况下一般是无需重载Receive方法的,除非基类的实现不符合你的实际要求。而如果你重载了Receive方法,一般会同时重载以下三个函数EndOfStream、BeginFlush和EndFlush。我们再来看一下输出Pin的实现。一般情况下,你要实现基类所有的纯虚函数,除了CheckMediaType进行媒体类型检查外,一般还有DecideBufferSize以决定Sample使用内存的大小,GetMediaType提供支持的媒体类型。
最后,我们看一下Filter类的实现。首先当然也要实现基类的所有纯虚函数。除此之外,Filter还要实现CreateInstance以提供COM的入口,实现NonDelegatingQueryInterface以暴露支持的接口。如果我们创建了自定义的输入、输出Pin,一般我们还要重载GetPinCount和GetPin两个函数。
我的Filter类的定义如下:
class DECLSPEC_UUID("55D36CED-7FC1-47ea-8AAE-C4C82F4EB656")
CMyFilter
: public CBaseFilter
{
public:
static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);
// filter registration table
static const AMOVIESETUP_MEDIATYPE m_sudType;
static const AMOVIESETUP_PIN m_sudPin;
static const AMOVIESETUP_FILTER m_sudFilter;
// CBaseFilter methods
int GetPinCount();
CBasePin *GetPin(int n);
private:
CMyFilter(LPUNKNOWN lpunk, HRESULT *phr);
~CMyFilter();
private:
CCritSec m_csFilter;
};
注:因为基类是一个纯虚的基类,所以在你的filter一定要派生一个其中的纯虚函数,否则编译器会提示你的派生类也是一个纯虚类,你在创建这个com组件对象的时候,纯虚类是没法创建对象的。
这样基本上就实现了一个filter,但是这个filter没有与之相联系的PIN。
2、 CMyFilter类的简单实现
CMyFilter::CMyFilter(LPUNKNOWN lpunk, HRESULT *phr)
: CBaseFilter(NAME("CMyFilter"), lpunk, &m_csFilter, *m_sudFilter.clsID)
{
}
CMyFilter::~CMyFilter()
{
}
CBasePin * CMyFilter::GetPin(int n)
{
return NULL;
}
int CMyFilter::GetPinCount()
{
return 0;
}
=================================================================
终于完成了设计,大家编译发现报错了
解决方法:
工程属性->C/C++->语言->将wchar_t 视为内置类型 设为 否
当布置到设备时选择注册,那么马上会进入到
CMyFilter::CMyFilter(LPUNKNOWN lpunk, HRESULT *phr)
: CBaseFilter(NAME("CMyFilter"), lpunk, &m_csFilter, *m_sudFilter.clsID)
{
}
关于strmbase.lib的生成可以参考我博客中转载别人的文章
关于Windows Mobile手机视听电脑视音频的程序实现(一)
如何使用请参考我的第二篇文章
<
http://blog.csdn.net/chenyujing1234/article/details/7369763