DirectShow编程(3.7) - 关于DirectShow - Graph动态重建(Dynamic Graph Building)

http://blog.csdn.net/believefym/article/details/1779746

3.7. Graph动态重建(Dynamic Graph Building)
    如果你需要修改一个已经存在的filter graph,你可以停止,修改后再重新启动它。这通常是一种最佳的解决方法。但是,在某此情况下,你可能需要在一个graph处于运行状态时来修改它,比如:
    *应用程序在进行视频回放时需要插入一个(视频滤镜filter)Video effect filter;
    *source filter在播放的过程中改变了媒体格式,此时可能需要接入新的解码filter;
    *应用程序在graph中加入一个新的视频流。
    上面的这些都是graph动态重建的例子。所有在graph继续处于运行状态而做的graph修改都被叫做graph动态重建。动态重建可以由应用程序发起,也可以由一个在graph中的filter发起。动态重建有三种可能:
    *媒体格式动态变化:一个filter可以在运行的中途改变媒体格式,而不需要重新被替换为另一个;
    *动态重连:在graph中添加或删除filter
    *Filter Chain操作:添加,删除,控制filter chain,(Filter Chain是相互连接着的一条Filter链路,并且链路中的每个Filter至多有一个Input pin,至多有一个Output pin)
    
3.7.1. 动态重连
    在绝大多数的directshow filter中,当graph处于运行状态时pin是不能被重新连接的,应用程序必须在重连前停止graph。但是,某些filter却支持动态重连,这既可以由应用程序来执行,也可以由graph中的一个filter来执行。
    如下图:
    

Dynamic graph-building diagram

 
    假设我们要将filter 2从graph中移除掉,替换成另一个filter,而此时graph还处于运行状态,那么必须具备以下几个条件:
    *filter 3的输入pin(pin D)必须支持IPinConnection接口,这个接口可以重新连接pin而不需要停止它。
    *filter 1的输出pin(pin A)必须能够在重连时阻塞媒体数据,数据不再在pin A和pin D之间传递。也就是说,输出Pin必须支持IPinFlowControl接口。但是,如果filter 1是发起重连的那个filter,那么它有可能已经在其内部实现了阻塞;
    动态重连包括下列步骤:
    1. 从Pin A那里阻塞数据流
    2. 重新连接Pin A和Pin D,或者在中间加入新的filter
    3. 取消Pin A上的阻塞

    步骤1. 阻塞数据流
    通过调用Pin A上的IPinFlowControl::Block方 法来阻塞数据流。这个方法既可以被同步调用,也可以被异步调用。要异步调用这个方法,需要创建一个win32事件对象,并将事件句柄传给Block,方法 会立即返回,然后使用WaitForSingleObject或其它函数来等待事件的触发。当阻塞工作完成时,pin会触发这个事件。如:

// Create an event
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent != NULL)
{
    // Block the data flow.
    hr = pFlowControl->Block(AM_PIN_FLOW_CONTROL_BLOCK, hEvent); 
    if (SUCCEEDED(hr))
    {
        // Wait for the pin to finish.
        DWORD dwRes = WaitForSingleObject(hEvent, dwMilliseconds);
    }
}

    如果是同步调用Block,那么只需将传入的hEvent参数设为NULL,此时这个方法会一直阻塞到阻塞工作完成为止。如果pin还没有准备好 deliver一个新的sample,那么就会一直阻塞。而如果filter处于就绪状态,这可能会花费任意长的时间,因此,不要在应用程序的主线程中使 用同步调用,以免发生死锁,开一个工作线程来使用同步调用,或者干脆就使用异步调用。

    步骤2. 重连pin
    要重新连接pin,查询graph的IGraphConfig接口并调用IGraphConfig::ReconnectIGraphConfig::Reconfigure。Reconnect方法使用比较简单:
    *停止中间filter(比如filter 2),并移除它
    *如果需要的话,加入新的中间filter
    *连接所有的pin
    *pause或run所有新的filter,使它的状态与graph相同
    Reconnect方法有参数可以用来指定pin连接的媒体类型和中间filter。如:

pGraph->AddFilter(pNewFilter, L"New Filter for the Graph");
pConfig->Reconnect(
    pPinA,      // Reconnect this output pin...
    pPinD,      // ... to this input pin.
    pMediaType, // Use this media type.
    pNewFilter, // Connect them through this filter.
    NULL, 
    0);

    如果Reconnect还不够用来应付我们的要求,那么你可以使用Reconfigure方法,它调用一个由应用程序定义的回调函数来重连这些pin。要调用这个方法,需要在你的应用程序中实现IGraphConfigCallback接口。
    在调用Reconfigure之前,如前面所述地那样阻塞输出pin的数据流。然后如下所示,将处于待处理状态的数据push下去:
    1. 在重连链路中处于下游的最远的那个输入pin(例子中为Pin D)上调用IPinConnection::NotifyEndOfStream方法,方法的参数是一个Win32事件句柄;
    2. 在与要阻塞数据的那个输出pin直接相连的那个输入pin上调用IPin::EndOfStream方法。(在例子中,要阻塞的那个输出pin是pin A,那么直接与之相连的那个输入pin为Pin B);
    3. 等待事件触发。输入pin(pin D)在它接收到end-of-stream事件通告时触发事件。这表示再没有数据需要传输,此时就可以安全地进行重连了。
    注意:IGraphConfig::Reconnect方法会自动处理上述步骤,你仅在调用Reconfigure方法时才需要自己来处理。
    当数据完成push后,调用Reconfigure,传入IGraphConfigCallback回调函数的指针。Filter Graph Manager会调用IGraphConfigCallback::Reconfigure方法。

    步骤3. 取消数据流的阻塞
    当你完成重连后,通过调用IPinFlowControl::Block,第一个参数为0来取消阻塞。

    注意:如果动态重连是由一个filter来执行的,那么你需要知道一点线程方面的问题。如果filter graph manager尝试去停止filter,它可能会死锁,因为graph等待filter停止,而与此同时,filter有可能在等待数据在graph中完 成push。要防止这个可能存在的死锁问题,如前所述可以用事件机制来处理。

3.7.2. filter链(filter chains)
    一个filter chain是一系列具备下述条件的相互连接的filter:
    *每一个在链中的filter最多只有一个已连接的输入pin和一个已连接的输出pin;
    *Filter链路中的数据流不依赖于链路外的其他Filter
    举个例子,在下图中,filter A-B,C-D和F-G-H是一个filter chains。每个F-G-H中的子链(F-G和G-H)也是一个filter chain。一个filter chain同样可以是由单个filter组成的,因此A、B、C、D、F、G和H同样也是filter chain。filter E由于有两个输入连接,所以任何含有E的一系列filter都不是filter chain。
 

Filter chain (Example 1)

 
    IFilterChain接口提供下述方法来控制filter chain:

    IFilterChain::StartChain  开启一个链
    IFilterChain::StopChain   停止一个链
    IFilterChain::PauseChain  暂停一个链
    IFilterChain::RemoveChain  从graph中移除一个链

    没有特殊的方法来添加一个链,要添加链,通过调用IFilterGraph::AddFilter方法来插入新的filter,然后调用IGraphBuilder::ConnectIGraphBuilder::Render或类似的方法来连接它们。
    当graph运行时,一个filter chain可以在运行和停止状态间切换。当graph处理就绪状态时,它可以在就绪和停止状态间切换。这是两种仅有的filter chain状态切换可能。
    Filter链指南
    当你使用IFilterChain方法时,确认在graph中的filter是否能支持filter链操作是十分必要的,否则,可能会发生死锁或graph错误。filter连接到链上必须发生在链状态改变后。
    使用IFilterChain的最佳情况是与一系统为链而设计的filter一起使用。使用下面的指南来确保你的filter是链操作安全的。参考下图:
   

Filter chain (Example 2)

 
       在filter链状态变化前,所在在filter链分界线上调用的数据处理都必须已完成。这个规则应用于 IMemInputPin::ReceiveIPin::NewSeqmentIPin::EndOfStream方法。链中的filter必须从由链外filter实现的这些方法调用中返回;而链外的filter也必须从这些由链内filter实现的这些方法调用中返回。
    举个例子,在上图中,filter B必须完成在filter A上的所有数据处理调用,而filter E也必须完成从filter D上的调用。如果pin暴露了 IPinFlowControlIPinConnection接口,那么如在动态重连那一节中所讲的,你可以通过调用 IPinFlowControl::BlockIGraphConfig::PushThroughData方法来推数据。filter也可能通过自己的方法来推数据。
     上游filter必须与链的状态一起发生变化。比如,在上图中,假如链已停止,但filter A调用 IMemInputPin::Receive方法,那么调用将失败,作为回应,filter A停止流。当应用程序重新开启链时,不会产生什么影响,因为filter A不再向使数据流动了。
     下 游filter必须同样与链的状态一起发生变化,否则,下游filter在等待取得sample时会发生死锁,因为sample不会再到来了。比如,多路 复用(MUX)filter总是在它所有的input pin上需要数据,如果挂起其中的一个input pin,在其它input pin上的流处理也会被阻塞。这会导致graph死锁
     每个与链内部filter相连的外部 filter的pin必须拥有自己的分配器(allocator),它不能被其它pin连接共享。当链的状态发生变化或从graph移除掉时,分配器便不 可用了,此时如果还有其它的连接使用这个分配器的话,它们将不能再处理sample了。
     除非与链相连的filter支持动态断开,否则不要移除链。典型的,已连接的filter会支持IPinConnection或IPinFlowControl接口,或者用它自己定义的接口代替。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值