编写DirectShow Filters—Filters如何连接

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错误。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值