创建一个filter实例(transform filter)【转载http://blog.csdn.net/captain_x/archive/2007/08/10/1737619.aspx】

1、选择所要创建的filter的用途,据此来选择基类。基类可以从CTransformFilter、CTransInPlaceFilter、CVideoTransformFilter和CBaseFilter中来选取。

(1) CTransInPlaceFilter提供了本地处理Sample的机制(Sample可以认为是存储一个视频帧的结构),当一个trans-in-place filter收到一个sample时,你可以通过重载它的Transform()函数来修改其中的数据,trans-in-place filter会在Transform()函数执行完后直接把这个sample传递给下一个filter。

(2) CTransformFilter完成的功能与CTransInPlaceFilter一样,它们的区别就是CTransformFilter总是把上游filter传递过来的sample复制一份,并把复制后的sample传递给下一个filter。当然,你可以通过重载Transform()函数来控制这个过程,包括修改其中的数据(这也是自己写filter的原因)。

(3)CVideoTransformFilter与CTransformFilter一样,只是多加了质量控制功能。

(4)以上三个filter都继承于CBaseFilter,所以如果想对filter进行更多的控制,就要直接从CBaseFilter来继承,但是所要做的工作也最多。在这个例子中,我选择CTransformFilter,因为CTransInPlaceFilter太简单了,dx9sdk中的例子NullNull就是一个完整的CTransInPlaceFilter的框架,并且只是一个框架,什么工作也没有做,如果要用的话直接修改就可以用了。

2、在vc中选择win32 dll,创建一个dll,名字随便取,这里我取SplitFilter,选择空dll。

3、然后,创建一个类CSplitFilter,继承自CTransformFilter,当然要选择public方式。

4、为filter生成一个CLSID,可以使用Guidgen,它的用法是在命令行中打Guidgen,然后回车,Guidgen就执行了,单击New GUID就会生成一个新的GUID;单击Copy就可以把新生成的GUID复制到剪贴板上。然后在SplitFilter.h文件上粘贴进来,最后是这个样子:

GUID{3DCD790F-B7A0-429a-B9E1-3CE3255D8D1C}

DEFINE_GUID(<<name>>, 0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c);

然后把其中的<<name>>换成自己设置的名字,如下:

// {3DCD790F-B7A0-429a-B9E1-3CE3255D8D1C}

DEFINE_GUID(CLSID_SplitFilter, 0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c);

然后在SplitFilter.cpp中加入#include <initguid.h>再来修改构造函数,形式如下:

CSplitFilter::CSplitFilter() : CTransformFilter(NAME("SplitFilter"), 0, CLSID_SplitFilter){}

5、处理媒体类型

    首先要明白一点,两个filter连接,也就是两个filter的输出pin和输入pin在进行连接,这个工作是由输出pin发起的,由输入pin来检查媒体类型是否匹配,并决定是否接受这个连接。在CTransformFilter中协商媒体类型的工作是由CTransformFilter来完成的,这个工作本来是应该由pin来完成的,但是CTransformFilter中的pin只是简单的调用CTransformFilter中相应的函数而已,所以我们所有的工作只是重载CTransformFilter中的三个虚函数而已:

(1)实现CheckInputType(const CMediaType *mtIn)(不要问我如何添加这个函数,如果这个都不会的话趁早别看dshow了,赶紧去看vc的书去)。这个函数是由输入pin来调用,当上游输出pin要来进行连接的时候,我们的filter的输入pin就会调用这个函数来检查是否支持上游输出pin的媒体类型。其中的CMediaType类是对AM_MEDIA_TYPE结构的封装,AM_MEDIA_TYPE包含了有关的媒体类型,具体可查阅dxsdk文档。因为我这里只是写一个框架来为大家演示,功能只是传递sample,并不对数据有任何的修改,所以任何的格式都可以,所以不管什么格式我们都应该返回ok,实际函数如下:

HRESULT CSplitFilter::CheckInputType(const CMediaType *mtIn){ // Everything is good.    return S_OK;}

(2)实现GetMediaType(int iPosition, CMediaType *pMediaType)前面我们已经讲了输入pin接受连接的时候用的函数,那么这个函数呢就是输入pin进行连接的时候用的,输出pin进行连接的时候首先要有一个支持的媒体类型列表,这个函数就是来生成这个列表的。实际上我们也可以直接返回一个ok了事,但是这样做有点太不负责任,我们虽然什么工作都不做,但是还是要把例行的检查做完了,这样老板(filter graph)看到了也会很满意,你们是不是也很赞同?首先,我们要确定输入pin是否已经连接了,如果输入pin没有连接,那我们和下面的filter连接了也没有意义,因为上游没有数据传过来,我们拿什么给后面的filter?画饼是不能充饥的:)

ASSERT(m_pInput->IsConnected());

然后,看看pin的位置是否正确if (iPosition < 0)    {        return E_INVALIDARG;}    这个函数最后的结果是这样的:

HRESULT CSplitFilter::GetMediaType(int iPosition, CMediaType *pMediaType){   

// Is the input pin connected    

if(m_pInput->IsConnected() == FALSE)    {        return E_UNEXPECTED;    }    

// This should never happen   

if(iPosition < 0)    {        return E_INVALIDARG;    }    

// Do we have more items to offer    

if(iPosition > 0)    {        return VFW_S_NO_MORE_ITEMS;    }     CheckPointer(pMediaType,E_POINTER);     *pMediaType = m_pInput->CurrentMediaType();    return NOERROR;}

(3)实现CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)虽然输入pin和输出pin的媒体类型都已经协商过了,但是我们的filter是否能够完成两中媒体类型的转换还是个未知数,那么就要调用CheckTransform来判断我们的filter是否能够支持这两个媒体类型的转换。我在这里的选择是直接返回ok。呵呵:)

6、设置分配器虽然我们已经完成了媒体类型的匹配,但filter的连接还没有完成,我们的pin还必须为连接选择allocator,设置allocator的属性,比如缓冲区的大小和数量等。输入pin我们可以不用管它,因为默认的情况下它只需要同意输出pin提供的allocator就可以了,但是输出pin的allocator就要我们自己来搞定了。如果后面的filter提供了一个allocator,输出pin可以使用这个allocator,否则就要建立一个新的allocator。这就要用到CTransformFilter的DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp),pAlloc是一个allocator的指针,pProp是一个ALLOCATOR_PROPERTIES结构体,代表后面的filter所需要的allocator的属性。我们可以在这个函数中根据我们自己的filter的需要和后面的filter的需要来设置allocator的各项属性,设置allocator的属性可以用IMemAllocator::SetProperties函数。这里面最重要的就是设置buffer的大小,一般是从输入pin的媒体类型的大小来决定,如果从这里没有得到固定的大小的话,我们就要猜buffer的大小了。最后的函数是这样的:

HRESULT CSplitFilter::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties){   

CheckPointer(pAlloc,E_POINTER);   

CheckPointer(pProperties,E_POINTER);     // Is the input pin connected    

if(m_pInput->IsConnected() == FALSE)    {       

return E_UNEXPECTED;    }    

HRESULT hr = NOERROR;   

pProperties->cBuffers = 1;   

pProperties->cbBuffer = m_pInput->CurrentMediaType().GetSampleSize();   

ASSERT(pProperties->cbBuffer);     // If we don't have fixed sized samples we must guess some size    

if(!m_pInput->CurrentMediaType().bFixedSizeSamples)    {

       if(pProperties->cbBuffer < 100000)        {

           // nothing more than a guess!!   

         pProperties->cbBuffer = 100000;    

    }

}    

// Ask the allocator to reserve us some sample memory, NOTE the function   

// can succeed (that is return NOERROR) but still not have allocated the   

// memory that we requested, so we must check we got whatever we wanted

     ALLOCATOR_PROPERTIES Actual;   

hr = pAlloc->SetProperties(pProperties,&Actual);

   if(FAILED(hr))    {

       return hr;

   }

     ASSERT(Actual.cBuffers == 1);  

   if(pProperties->cBuffers > Actual.cBuffers ||        pProperties->cbBuffer > Actual.cbBuffer)    {

       return E_FAIL;    }

    return NOERROR;}

7、处理数据终于可以开始处理数据了,我们重载Transform(IMediaSample *pSource, IMediaSample *pDest)来实现对数据的处理,从字面上都可以看出这两个sample的意义,因为这里我们什么工作都不做,所以我们只需要把源sample复制到目的sample就可以了。这个函数如下:

HRESULT CSplitFilter::Transform(IMediaSample *pIn, IMediaSample *pOut){

       HRESULT hr = Copy(pIn, pOut);

       return hr;}

其中用到了功能函数copy,其代码如下:

HRESULT CSplitFilter::Copy(IMediaSample *pSource, IMediaSample *pDest) const{

        CheckPointer(pSource,E_POINTER);

   CheckPointer(pDest,E_POINTER);  

   // Copy the sample data

   BYTE *pSourceBuffer, *pDestBuffer;    long lSourceSize = pSource->GetActualDataLength();

#ifdef DEBUG

    long lDestSize = pDest->GetSize();  

ASSERT(lDestSize >= lSourceSize);

#endif

    pSource->GetPointer(&pSourceBuffer);  

pDest->GetPointer(&pDestBuffer);  

   CopyMemory((PVOID) pDestBuffer,(PVOID) pSourceBuffer,lSourceSize);   

// Copy the sample times

    REFERENCE_TIME TimeStart, TimeEnd;

   if(NOERROR == pSource->GetTime(&TimeStart, &TimeEnd))    {

       pDest->SetTime(&TimeStart, &TimeEnd);

   }

    LONGLONG MediaStart, MediaEnd;

    if(pSource->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR)    {  

      pDest->SetMediaTime(&MediaStart,&MediaEnd);  

}

     // Copy the Sync point property    

HRESULT hr = pSource->IsSyncPoint();

   if(hr == S_OK)    {

       pDest->SetSyncPoint(TRUE);

    }    else if(hr == S_FALSE)    {

       pDest->SetSyncPoint(FALSE);  

}    else    {

// an unexpected error has occured...       

return E_UNEXPECTED;

   }    

// Copy the media type    

AM_MEDIA_TYPE *pMediaType;

   pSource->GetMediaType(&pMediaType);

    pDest->SetMediaType(pMediaType);

   DeleteMediaType(pMediaType);  

   // Copy the preroll property  

   hr = pSource->IsPreroll();  

if(hr == S_OK)    {   

     pDest->SetPreroll(TRUE);

    }    else if(hr == S_FALSE)    {  

      pDest->SetPreroll(FALSE);   

}    else    {

// an unexpected error has occured...   

     return E_UNEXPECTED;

    }  

   // Copy the discontinuity property    

hr = pSource->IsDiscontinuity();

    if(hr == S_OK)    {    

    pDest->SetDiscontinuity(TRUE);  

}    else if(hr == S_FALSE)    {   

     pDest->SetDiscontinuity(FALSE);

   }    else    {

// an unexpected error has occured...      

return E_UNEXPECTED;

   }  

   // Copy the actual data length   

long lDataLength = pSource->GetActualDataLength();  

pDest->SetActualDataLength(lDataLength);  

   return NOERROR;}

8、增加对com的支持因为filter是一个com组件,所以要符合com规范。又因为CTransformFilter是从CUnknown继承而来的,所以不用再自己实现AddRef 和 Release,如果我们要是有自己定义的接口的话,就要自己实现QueryInterface,幸好我们这个filter不实现自己定义的接口,就不用那么麻烦了,呵呵。好了,闲话少续,开始了:首先,创建一个静态的方法来返回一个我们的filter的实例,这个方法的名字可以任意指定,但是一般都叫做CreateInstance,那我们也就取这个名字吧,这个函数的参数如下(内含一般代码):

CUnknown * WINAPI CSplitFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr){        CSplitFilter *pFilter = new CSplitFilter();    if (pFilter== NULL)     {  

      *pHr = E_OUTOFMEMORY;

   }  

return pFilter;

}

然后,声明一个全局的CFactoryTemplate类数组,取名g_Templates,每一个CFactoryTemplate实例包含一个filter或者我们定义的接口的注册信息,因为我们这里就只有一个filter,也没有自定义接口,所以数组中就只有一项内容。CFactoryTemplate g_Templates[] = { {     L"SplitFilter",    &CLSID_SplitFilter,    CSplitFilter::CreateInstance,    NULL,    NULL }};int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

最后,实现dll的注册函数

STDAPI DllRegisterServer(){

   return AMovieDllRegisterServer2( TRUE );

}

STDAPI DllUnregisterServer(){

   return AMovieDllRegisterServer2( FALSE );

}

再加上#include <Streams.h>引入静态库Strmbasd.lib Msvcrtd.lib Winmm.lib最后再建立其导出文件SplitFilter.def,内容如下:

LIBRARY     SplitFilter.ax

EXPORTS

           DllMain                 PRIVATE

            DllGetClassObject       PRIVATE

          DllCanUnloadNow         PRIVATE

          DllRegisterServer       PRIVATE  

         DllUnregisterServer     PRIVATE

这样我们就可以建立一个最基本的filter的框架了。 当然,如果你要播放高清视频,如果在分配器上分配的内存不够的话你可以加大内存,也就是刚开始猜内存需要多大的时候尽量大一点,我反正在测试2732×768的时候报错,但是我把分配的内存加了个0就ok了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值