Directshow中Filter开发基础

摘要:
关于开发自己的Filter,我以前写过一篇文章,《利用Directshow开发自己的filter》,里面详细介绍了开发filter一些步骤,这里我想介绍一些filter的基础知识,可以让你更好的理解filter。本篇文档主要包括下面一些内容
  • 1filter的连接
    2filter间的数据流动
    3pin连接时数据格式的动态改变
    4Threads and Critical Sections
    5质量控制管理
    6Directshow和com
1filter的连接
Pin的连接
应用程序通过调用filter 图表管理器的方法来连接filter,并不是来调用filter或者pin本身的函数。应用程序可以调用IFilterGraph::ConnectDirect or IGraphBuilder::Connect来指定不同的filter直接连接,也可以通过IGraphBuilder::RenderFile间接连接。
只有两个filter都在graph里,连接才能成功。应用程序可以通过IFilterGraph::AddFilter将filter 添加graph中,当一个filter被添加到graph中时,filter图表管理器通过IBaseFilter::JoinFilterGraph来通知filter。
Pin连接的大致过程如下:
1图表管理器首先调用输出pin上的IPin::Connect,然后传递一个指针给输入pin。
2如果输出pin接受连接的邀请,它就调用输入pin上的IPin::ReceiveConnection。
3如果输入pin也接受连接邀请,那么连接成功,pin之间的连接ok。
当filter处于活动状态的时候,许多pin可以断开连接和重新连接。这种类型的连接称为动态连接。当然,大多数的filter并不支持动态连接。
Filter通常采用从上游到下游的连接顺序。也就是说filter上的输入pin总是比输出pin先连接。Filter应该支持这种连接顺序。然而有许多filter支持相反的连接顺序,输出pin先连接,输入pin后连接。例如:在连接MUX filter的输入pin之前一定要将MUX filter的输出pin和writer filter连接起来。
当pin的Connect or ReceiveConnection方法被调用的时候,pin必须检查一下自己是否支持这个连接。通常要进行下列检查:
1 检查媒体类型是否匹配。
2 就内存的分配达成一致。
3请求其他pin的其他接口。
媒体类型匹配
当一个filter 图表管理器调用IPin::Connect方法时,可能有下面的几种媒体类型。
1 完整类型
如果媒体类型每一个部分都定义的很完成,那么pin就严格按照定义的类型类型进行连接。如果不匹配,连接失败。
2 部分媒体类型
如果媒体类型的机构中,major type, subtype, or format type的值为GUID_NULL,这个值是一个通配符号。任何类型都可以匹配。
3没有媒体类型
如果filter图表管理器传递过来一个NULL的指针,这个pin就可以和任意的类型的媒体类型匹配。
一般在连接过程中,都有一个完整的媒体类型。图表管理器传递媒体类型的目的是为了限制连接类型。
一般来说,都是输出pin通过调用输入pin IPin::ReceiveConnection提供一个媒体类型。输入pin可以拒绝也可以接受这个媒体类型。这个过程一直重复,直到输入pin接受了一个类型,或者输出pin枚举完了它支持的所有的媒体类型,连接失败。
输出pin通过调用输入pin上的IPin::EnumMediaTypes枚举输入pin所支持的媒体类型。
看看如何匹配媒体类型的吧。
if ((pmt->formattype == FORMAT_VideoInfo) &&
(pmt->cbFormat > sizeof(VIDEOINFOHEADER) &&
(pbFormat != NULL))
{
VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;
// Now you can dereference pVIH.
}
Pin连接中的内存分配
当两个pin连接起来后,他们需要一种机制来交换媒体数据。大多数数据交换采用的局部内存交换机制。所有的媒体数据都在主内存中。DirectShow为局部存储器传输定义了两种机制:推模式(push model)和拉模式(pull model)。在推模式中,源过滤器生成数据并提交给下一级过滤器。下一级过滤器被动的接收数据,完成处理后再传送给再下一级过滤器。在拉模式中,源过滤器与一个分析过滤器相连。分析过滤器向源过滤器请求数据后,源过滤器才传送数据以响应请求。推模式使用的是IMemInputPin接口,拉模式使用IAsyncReader接口,推模式比拉模式要更常用。
在局部存储器传输中,负责分配内存的对象称为allocator。每个allocator都支持一个IMemAllocator接口。所有的pin都共享一个allocator。
每个pin都提供一个allocator,但是输出pin选择使用哪个allocator。
输出pin可以设置allocator的属性。比如,分配内存的大小,
在IMemInputPin连接中,allocator工作过程如下
1 首先,输出pin调用IMemInputPin::GetAllocatorRequirements,这个方法检查输入pin对内存的要求,比如内存的队列,一般来说,输出pin要满足输入pin对内存的要求。
2 输出pin然后调用IMemInputPin::GetAllocator.,这个方法从输入pin请求一个allocator,
3 输出pin选择一个allocator,可以是输入pin提供,也可以是自己生产的。
4输出pin调用IMemAllocator::SetProperties来设置allocator的属性。
5然后输出pin通过IMemInputPin::NotifyAllocator来通知输入pin,选择的allocator。
6输入pin通过IMemAllocator::GetProperties来检查是否能够接受allocator的属性。
7当数据流开始和停止的时候,输出pin负责提交allocator。
在IAsyncReader连接过程如下:
1 输入pin调用输出pin上的IAsyncReader::RequestAllocator,输入pin确定内存的属性,并提供一个allocator。
2 输出pin选择一个allocator,
3 输入pin检查

如何提供一个自定义的allocator
这里只讲一下IMemInputPin连接,IAsyncReader类似。
首先,定义一个C++类,你的allocator应该从一个标准的allocator类中派生,比如CBaseAllocator or CMemAllocator,你也可以自己创建一个新的allocator类,如果你是新建的类,你必须支持IMemAllocator接口。
下面看看在输入pin和输出pin中如何使用你定义的allocator。
在输入pin中提供allocator
在输入pin中提供allcator,必须重载CBaseInputPin::GetAllocator方法。在这个方法里,首先检查m_pAllocator是否可用,如果为非空,就表明allocator已经被选中,所以直接返回这个allocator指针即可,如果m_pAllocator为空,表明allocator还没有被选中,所以,就要返回输入pin的allocator,因此,创建一个allcaotor的实例,返回IMemAllocator接口。
看下面的代码把
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 code
// here will depend on your custom allocator class definition.
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);
}
当输出pin选择一个allocator,它就调用输入pin的IMemInputPin::NotifyAllocator,因此,要重载CBaseInputPin::NotifyAllocator方法来检查allocator的属性。
在输出pin中如何提供一个定制的Allocator
在输出pin中提供一个allcator,要重载CBaseOutputPin::InitAllocator
HRESULT MyOutputPin::InitAllocator(IMemAllocator **ppAlloc)
{
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);}
}
缺省情况下CBaseOutputPin首先从输入pin中申请一个allocator,
2filter间的数据流动
1 传递Samples
本文讲述了如何传递一个sample,包括两种模式下,推模式下采用IMemInputPin的方法,在拉模式下调用IAsyncReader的方法。
推模式
输出pin通过调用IMemInputPin::Receive或者IMemInputPin::ReceiveMultiple方法来传递一个sample。在Receive和ReceiveMultiple方法里,输入pin可以阻塞数据流。如果输入pin阻塞,那么IMemInputPin::ReceiveCanBlock必须返回S_OK。如果pin保证不会阻塞,那么ReceiveCanBlock方法要返回S_FALSE,返回S_OK并不表明Receive方法阻塞,只是表明可能阻塞。
尽管Receive可以阻塞一直等待某种资源变的可用,但是它不能通过阻塞来等待数据流的到来。因为如果上游的filter正在等待下游的filter正在等待下游的filter释放资源,就会造成死锁。如果一个filter拥有多个输入pin,那么其中的一个pin可以等待另外的一个pin接收数据。例如AVI Mux filter就是通过这种方法来同步音频和视频流的。
如果有以下原因,pin可能拒绝sample。
1pin正在flushing
2pin没有连接
3filter停止
4发生了其他错误
如果输入pin拒绝了sample,那么Receive方法就要返回S_FALSE,或者其它的错误码,如果Receive没有返回S_OK,那么上游的fitler就会停止发送sample。
前面三种错误都是可以预见的错误,第四种是不可预见的错误,即使pin正处于接收数据流德状态,当这种错误发生时,接受pin就拒绝接受sample,发送pin就给下游的连接pin发送一个结束发送数据流的通知,并且给Filter 图表管理器发送一个EC_ERRORABORT事件通知。
在directshow的基类中CBaseInputPin::CheckStreaming方法用来检查通常的数据流错误,比如flushing, stopped, and so forth。派生类要检查所发生的错误。在发生错误的时候,CBaseInputPin::Receive方法将发送一个结束数据流的通知和一个EC_ERRORABORT事件通知。
在拉模式下,IAsyncReader接口中,输入pin将通过以下方法从输出pin中请求samples。
? IAsyncReader::Request
? IAsyncReader::SyncRead
? IAsyncReader::SyncReadAligned
Reques方法是异步的,输入pin调用IAsyncReader::WaitForNext来等待请求数据传递结束。另外两个方法是同步。
2数据处理
//略
3数据流结束的通知
当一个源filter结束发送数据流时,它调用和它连接的filter的输入pin的IPin::EndOfStream,然后下游的filter再依次通知与之相连的filter。当EndOfStream方法一直调用到renderer filter的时候,最后的一个filter就给filter图表管理器发送一个EC_COMPLETE事件通知。如果renderer有多个输入pin,当所有的输入pin都接收到end of stream通知的时候,它才会给filter图表管理器发送一个EC_COMPLETE事件通知。

Filter必须在其他函数调用之后调用EndOfStream函数,比如IMemInputPin::Receive.。
在一些情况下,下游的filter可能比源filter更早的发现数据流的结束。在这种情况下,下游filter发送 结束stream的通知,同时, IMemInputPin::Receive函数返回S_FALSE直到图表管理器停止。这个返回值提示源filter停止发送数据。
对EC_COMPLETE事件的缺省处理
缺省的情况下,filter图表管理器并不将EC_COMPLETE事件通知发送给应用程序,当所有的数据流都发送了EC_COMPLETE事件通知后,它才给应用程序发送一个EC_COMPLETE事件通知。所以,应用程序只有在所有的数据流停止的时候才能接收到这个通知。

filter图表管理器通过计算支持seeking接口的filter,并且具有一个renderer pin,没有相应的输出pin,就可以确定数据流的数目。Filter图表管理器通过下面的方法来决定一个pin是否是个renderer 。
1 pin的IPin::QueryInternalConnections方法通过nPin参数返回0;
2 filter保露一个IAMFilterMiscFlags接口,并且返回一个AM_FILTER_MISC_FLAGS_IS_RENDERER标志。
在拉模式下的数据流结束通知
在IAsyncReader连接中,源filter并不发送数据流结束的通知,相应的发送数据流结束的通知是有renderer filter发出的。
4New Segments(

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值