Filter的连接

  • Post author:
  • Post category:其他


1. PIN连接

filters通过IPin接口连接pin。output pin连接到input pin。每个pin连接有一个media sample,通过am_media_type描述。

一个应用程序通过调用在filter graph manager上的方法来连接filter,而不是通过filters或者pins上的方法。应用程序可以直接指定哪些filters连接,通过IFilterGraph::ConnectDirct或者IGraphBuilder::Connect。或者使用如IGraphBuilder::RenderFile的graph-building方法间接连接filters。

如果连接成功,两个 filter必须在filter graph中,应用程序可以通过IFilterGraph::AddFilter方法增加一个filter到graph中,filter graph manager可以增加filter到graph。当一个filter被增加到graph中,filter graph manager 调用filter的IBaseFilter::JoinFilterGraph来通知这个filter。

一般连接过程的概述如下:

1) Filter graph manager调用在output pin上Ipin::Connect,传递一个指针到input pin。

2) 如果output pin接受这个连接,它调用在input pin上的Ipin::ReceiveConnection。

3) 如果output pin也接受这个连接,连接成功并且pins连接。

一些pin可以在filter激活时断开和重新连接。这种连接类型叫dynamic reconnection。大多数filter不支持动态重连

filter一般用downstream顺序连接,换句话,filter的input pin等于output pin连接,filter应该一直支持这种连接顺序。也有filter连接顺序相反—output pin先连接,再连接input pins。如,可能先连接一个MUX filter的output pin到file-writer filter,再连接mux filter的input pin。

当pin的 connect或者ReceiveConnection方法被调用时,这个pin必须验证是否支持这个连接,细节依赖于具体的filter,大多数步骤如下:

1) 查检media type是否可接受

2) 协商一个allocator

3) 查询被请求接口的其它pin

2. 协商media type

Filter graph manager调用Ipin::connect方法时,有几个选择来指定media type

1) complete type : 如果media type全指定,pins试图用这种类型连接。如果不能连接,连接失败。

2) Partial media type :如果主类型、子类型、格式类型是GUID_NULL,media type是partial,GUID_NULL如同通配符,表示可以接受任何值。这个PIN协商于兼容于partial类型的类型。

3) No media type : 如果filter graph manager传递一个NULL指针,两个PINS允许接受任何media type。

如果pin连接了,连接会一直有一个完整的media type,它的目标是让filter graph manager限制可能的连接类型

在协商过程中,output pin通过调用input pin的Ipin::ReceiveConnection来倾向一个media type,这个input pin可能接受或拒绝这个类型,这个过程重复直至有一个input pin接受这个连接,或者output pin尝试所有所有连接然后连接失败。

一个output pin如何选择media type依赖于它的实现。在directshow基类中,output pin调用在input pin上Ipin::EnumMediaTypes,它返回一个枚举器枚举input pin倾向的media type,如果失败,output pin枚举自己倾向的类型

用media type工作

任何函数接受AM_MEDIA_TYPE参数,在引用pbFormat成员前要一直验证cbFormat和formattype值。如下代码是错误的:

if(pmt->formattype==FORMAT_VideoInfo)

{


VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;

//wrong

}

正确的代码是:

if((pmt->formattype==FORMAT_VideoInfo) &&

(pmt->cbFormat>sizeof(VIDEOINFOHEADER)&&

(pbFormat!=NULL)

{


VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;

//now you can dereference pVIH

}

3. 协商allocator

当两个pin连接,需要一个交换media数据的机制,它叫做transport,一般,directshow体系关于transport是不确定的。两个filter可以允许使用任何两个都支持transport来连接。

大多数transport是local memory transport,即 media数据保留在主内存中。存在两个flavor本地内存transport,如push model / pull model,在push模型中,source filter push数据到downstream filter,使用在downstream filter的input pin上的IMemInputPin接口 ,在pull模型中,downstream filter从source filter请求数据,使用在source filter output pin上的IAsyncReader接口

在local memory transport,对象响应分配memory buffer被叫做allocator,一个allocator支持IMemAllocator接口,两个PINs共享一个单独的allocator,任何一个pin可以提供一个allocator,但output pin选择哪个allocator来使用

Output pin也设置allocator的属性,属性决定有allocator建立多少个buffers,每个buffer的尺寸,和内存对齐,output pin服从于input pin 的buffer请求。

在ImemInputput连接中,allocator协商工作如下:

1) 可选的,output pin调用IMemInputPin::GetAllocatorRequirements,它返回input pin的buffer请求,如内存对齐。一般的,output pin应该尊重于 input pin的请求,除非有好的原因不去这么做。

2) 可选的,output pin调用ImemInputPin::GetAllocator,它从input pin请求一个allocator,input pin提供一个或者返回错误代码。

3) 输出pin选择一个allocator,它能使用input pin提供的一个,或者自己创建。

4) Output pin调用IMemAllocator::SetProperties设置allocator properties属性。可是,allocator可能不理会被请求的属性(如,如果input pin提供allocator时这种情况可能发生),在SetProperties方法中allocator返回实际的属性作为output参数。

5) outpin调用ImemInputPin::NotifyAllocator通知input pin的选择

6) Input pin应该调用ImemAllocator::GetProperties验证是否allocator属性可接受的。

7) Output pin响应提供或反提交allocator,这个发生在流开始或停止时。

在一个IAsyncReader连接中,allocator协商工作如下:

1) input pin调用在output pin上的IAsyncReader::RequestAllocaor,这个input pin指定它的buffer请求,可选的,提供一个allcator。

2) Output pin选择一个allocator,它可能使用input pin提供的一个,或者自己建立。

3) Output pin返回allocator作为在RequestAllocator中的outgoing参数,input pin应该查检allocator属性。

4) Input pin响应于提交或反提交allocator

5) 在allocator协商过程中任何时间,任何pin可能连接失败。

6) 如果output pin使用input pin的allocator,它可能仅使用这个allocator传递sample到输入pin,所属的filter必须不使用allocator来传递sample到其它的pins。

4. 提供一个自定义的allcator

本节描述如何为一个filter提供一个自定义allocator。只描述了IMemInputPin连接,但IAsyncReader连接步骤是类似的。

首先,为allocator定义一个C++类。自定义的allocator继承自一个标准的allocator类,CBaseAllocator或者CMemAllocator,或者你可以建立全新的allocator类,如果这样,必须暴露ImemAllocator接口。

保留的步骤依赖于在自定义filter上的allocator是否属于一个input pin或者一个output pin,在allocator协商分析期间input pin扮演一个不同于output pin的角色,因为output pin无条件选择这个allocator。

1) 为input pin提供一个自定义allocator

为了为一个input pin提供一个allocator,重载input pin上的CBaseInputPin::GetAllocator。在这个方法中,查检m_pAllocator成员变量,如果它为非空,意味着allocator已经被选为这个连接,所以GetAllocator必须返回一个指向allocator的指针。如果为NULL,意味着allocator没被选择,所以GetAllocator返回一个指针指向input pin倾向的allocator,在这个情况下,建立一个自定义的Allocator的实例并且返回它的IMemAllocator指针。下列代码显示如何实现GetAllocator方法:

STDMETHODIMP CMyInputPin::GetAllocator(IMemAllocator **ppAllocator)

{


CheckPointer(ppAllocator, E_POINTER);

if(m_pAllocator)

{


//we already have an allocator, so return that one

*ppAllocator = m_pAllocator;

(*ppAllocator)->AddRef();

return S_OK;

}

// no allocator yet, so propose our custom allocator, the exact coe here will depend on

// your custom allocator class defininition

HRESULT hr = S_OK;

CMyAllocator * pAlloc = new CMyAllocator(&hr);

if(!pAlloc)

{


return E_OUTOFMEMORY;

}

if(FAILED(hr))

{


delete pAlloc;

return hr;

}

//return the IMemAllocator interface to the caller

return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAllocator);

}

当upstream filter选择一个allocator,它调用input pin的IMemInputPin::NotifyAllocator方法。重载CBaseInputPin::notifyAllocator方法来查检allocator属性,在一些情况下,input pin可能拒绝这个allocator如果不是你自定义的allocator,虽然可能引起所有pin连接失败。

2) 为output pin提供一个自定义的allocator

为了为output pin提供一个allocator,重载CBaseOutputPin::InitAllocator建立自定义的allocator的一个实例:

HRESULT MyOutputPin::InitAllocator(IMemAllocator **pAlloc)

{


HRESULT hr = S_OK;

CMyAllocator * pAlloc = new CMyAllocator(&hr);

if(!pAlloc)

{


return E_OUTOFMEMORY;

}

if(FAILED(hr))

{


delete pAlloc;

return hr;

}

//return the IMemAllocator interface

return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAllocator);

}

默认的,CBaseOutpin类首先从input pin请求一个allocator,如果allocator不合适,output 建立它自己的allocator,为了强制连接使用你自定义的allocator,重载CBaseOutputPin::DecideAllocator,可是,要意识到它可能阻止你的output pin连接到确定的filter,因为其它fitlers也许请求自己所属的自定义allocator。

5. 重新连接pin

在一个pin连接期间,一个filter可能断开连接和再连接一个或多个pin,如下

1) filter调用在另一个filter pin的Ipin::QueryAccept,并且指定新的媒体类型。

2) 如果queryAccept返回S_OK,FILTER调用IFilterGraph2::ReconnectEx重新连接此PIN。

下列例子需要可能重新连接pins:

1) Tee filter:tee filter分离input stream为多个输出流而不改变流中数据。tee fitler可能接受一定范围的media type,但这些type必须匹配贯穿所有pin连接。因此,当input pin连接时,filter可能需要重新协商在output pin上的任何存在的连接。如InfTee Filter sample。

2) Trans-in-place filter : Trans-in-place filter修改在原始buffer的input数据代替拷贝数据到一个单独的out buffer,对于upstream / downstream 连接,它必须使用同样的allcaotor,第一个pin连接(input / output ) 用一般方式中协商一个allcator,当其它的pin连接,可是,第一个allcoator可能不被接受,在这个情况下,第二个pin选择一个不同的allocator,第一个pin重新连接使用新的allocator。例如,在CTransInPlaceFilter类

在ReconnectEx中,filter graph manager异步断开连接和重新连接pin,filter必须不会试图重新连接除非QueryAccept返回S_OK,另外,PIN将被断开连接,引起graph错误,filter也从Ipin::Connect内部请求重新连接,在同样的线程中,如果连接返回一个线程,当另一个线程有重新连接的请求,filter graph manager可能在它重新连接之前run,引起graph错误。