科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件利用DirectShow开发自己的Filter

利用DirectShow开发自己的Filter

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

无论开发Filter还是开发Dshow的应用程序都要配置一下开发环境的,其实就是包含一下dshow用到的头文件和动态库。

作者:智慧的鱼 来源:天极网 2007年10月19日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
四 如何实现自己的Filter

  在这里就要讲如何创建自己的Filter了,下面我们以写一个CTransformFilter为例

  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没有Pin接口,但在我的demo里的Filter,却是有个out pin和一个input pin。
我的Filter类的定义如下:

class CMyFilter : public CCritSec, public CBaseFilter
{
 public:
  CMyFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *hr);
  virtual ~CMyFilter();
  static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
  CBasePin *GetPin(int n);
  int GetPinCount();
}

  注:因为基类是一个纯虚的基类,所以在你的filter一定要派生一个其中的纯虚函数,否则编译器会提示你的派生类也是一个纯虚类,
你在创建这个com组件对象的时候,纯虚类是没法创建对象的。

  2、给自己的Filter生成一个CLSID

  你可以用Guidgen or Uuidgen给自己的Filter生成一个128位的ID号,然后利用DEFINE_GUID宏在Filter的头文件声明该Filter的CLSID;
[myFilter.h]

// {1915C5C7-02AA-415f-890F-76D94C85AAF1}
DEFINE_GUID(CLSID_MYFilter,
0x1915c5c7, 0x2aa, 0x415f, 0x89, 0xf, 0x76, 0xd9, 0x4c, 0x85, 0xaa, 0xf1);

  这个CLSID_MYFilter在类厂数组用到,在注册Filter时也要用到。

  3 CMyFilter类的简单实现

  这个类纯粹为了演示用,所以特别简单,你可以参考我的demo,那个filter写的功能比较全。

CMyFilter::CMyFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *hr)
:CBaseFilter(NAME("my filter"), pUnk, this, CLSID_MYFilter)
{ }
CMyFilter::~CMyFilter()
{}

// Public method that returns a new instance.
CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
 CMyFilter *pFilter = new CMyFilter(NAME("my Filter"), pUnk, pHr);
 if (pFilter== NULL)
 {
  *pHr = E_OUTOFMEMORY;
 }
 return pFilter;
}

CBasePin * CMyFilter::GetPin(int n)
{
 return NULL;
}
int CMyFilter::GetPinCount()
{
 return 0;
}

  这样基本上就实现了一个filter,但是这个filter没有与之相联系的PIN,但是实现Filter的基本过程就时这样了,至于逻辑上的东西,比如Filter和pin如何连接,数据流是如何流动的,你都要去看看sdk了,按照上面的步骤你就可以写一个Filter的框架出来。

  下面我们总结一下写一个Filter至少需要那些东西。

  1、Filter的实现类

  在这里就是CMyFilter类,在这个类里你可以实现自己的逻辑上的功能,包括定义你的filter的特性,给你的filter配备pin接口等。

  2、com组件的引出函数

  五个全局函数:

  DllMain //dll的入口函数
  DllGetClassObject //获得com组件的类厂对象
  DllCanUnloadNow //com组件是否可以卸载
  DllRegisterServer //注册com组件
  DllUnregisterServer //卸载com组件

  其中DllGetClassObject 已经由基类完成你自己只要完成三个函数即可DllMain,DllRegisterServer,DllUnregisterServer。

  3、com组件的类厂对象

  类厂对象是用来生成Filter对象的,用的模板类定义了一个全局的模板类对象数组,一般格式如下

CFactoryTemplate g_Templates[1] =
{
 {
  L"my filter", // Name
  &CLSID_MYFilter, // CLSID
  CMyFilter::CreateInstance, // Method to create an instance of MyComponent
  NULL, // Initialization function
  &sudInfTee // Set-up information (for filters)
 }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

  4、关于你自己定义的Filter以及Pin的信息
 
  这些是一个全局的结构变量,用于描述你的Filter和你定义的pin,在注册Filter的时候会用到,如下

  AMOVIESETUP_FILTER 描述一个Filter
  AMOVIESETUP_PIN 描述pin
  AMOVIESETUP_MEDIATYPE 描述数据类型

  下面的代码描述了一个Filter带有一个output PIN

static const WCHAR g_wszName[] = L"Some Filter";
AMOVIESETUP_MEDIATYPE sudMediaTypes[] = {
 { &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24 },
 { &MEDIATYPE_Video, &MEDIASUBTYPE_RGB32 },
};
AMOVIESETUP_PIN sudOutputPin = {
 L"", // Obsolete, not used.
 FALSE, // Is this pin rendered?
 TRUE, // Is it an output pin?
 FALSE, // Can the filter create zero instances?
 FALSE, // Does the filter create multiple instances?
 &GUID_NULL, // Obsolete.
 NULL, // Obsolete.
 2, // Number of media types.
 sudMediaTypes // Pointer to media types.
};

AMOVIESETUP_FILTER sudFilterReg = {
 &CLSID_SomeFilter, // Filter CLSID.
 g_wszName, // Filter name.
 MERIT_NORMAL, // Merit.
 1, // Number of pin types.
 &sudOutputPin // Pointer to pin information.
};

  最后如果你还是调试通不过,看看你是否包含了下面的头文件

#include streams.h
#include initguid.h
#include tchar.h
#include stdio.h

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章