相见恨晚之gstreamer衬垫的激活

  1.衬垫的激活 

       当一个bin(箱柜)的状态发生变化时,它里面的元件的状态是按照sink到source的顺序变化的,即先设置sink元件的新状态,再设置source元件的新状态。当状态变化是从ready变到pause时,元件的衬垫开始激活,为数据在元件之间的流动做准备。有些pad,还会启动一个任务(task),来驱动数据流。

      对某个元件来说,它通常先激活它的source 衬垫,再激活它的sink衬垫。为什么要按照这样的顺序呢?这是为了保证这一点:当元件的sink pad被激活了并且准备好接受数据了,元件的source pads已经激活并可以传递数据到下游元件了。反之,如果先激活在sink衬垫,就会出现这样的情况:sink衬垫已经在接收数据了,而source 衬垫还没有准备好,因此在一段时间内,就无法传递数据到下游元件。也就是说,传递数据流带来了不必要的延时。 

         +-------------------------------+    
      | bin                            |
      |    +--------+   +-------+    |
      |    | 元件1 |    | 元件2|    |
      |  /sink   src- sink    |   |
    sink+--------+   +--------+   |
     +---------------------------------+

包含2个元件的箱柜-管道示意图

     pad的激活有2个模式:推送(PUSH)模式和拉拽(PULL)模式.我们通过图解来说明。如上图所示,该bin包含2个元件,元件之间包含有一个src-sink的连接(link)。push模式是比较常见也比较好理解的模式。在push模式下,link上的src pad通过调用gst_pad_push()函数来传递数据给sink pad.与之相反,在pull模式下,sink pads通过 gst_pad_pull_range()从srcpad那里请求数据。src pad一般会实现一个getrange函数。

   gstreamer core 通过调用gst_pad_set_active()来激活一个pad.如果pad已经激活了,无论是push还是pull模式,该函数都不做任何事情,立刻返回;如果还没有被激活,它将调用相应的pad的激活函数。这些激活函数通常都是在每个pad所属的具体的元件的构造函数里以函数指针的形式设置的,如:

          gst_pad_set_activate_function (avi->sinkpad, gst_avi_demux_sink_activate);

     gstreamer core并不知道需要把pad激活在push还是pull模式。于是它把这个任务交给了pad的activate函数,即上段提到的那个函数指针指向的函数,如 gst_xx_demux_sink_activate。对于两种模式都可能支持的sink pad,它会在gst_xx_demux_sink_activate函数里调用gst_pad_check_pull_range()函数决定是激活在pull模式还是push模式。如果check_get_range函数返回为true,选择pull模式,调用xx_activate_pull函数;否则,激活在push模式,调用xxx_activate_push函数。activate函数默认支持的模式是push,即调用xxx_activate_push函数。 一旦模式选定了,gstreamer core将调用每个模式特定的activate函数,一般为XX_activate_push函数或xx_activate_pull函数。

     我们分析上面的箱柜示意图。假设元件2配置为pull模式。箱柜-管道中的状态变化将首先由元件2开始,因为元件2是这个管道中最下游的元件。gstreamer core将会在元件2的sink pad上调用activate函数。元件2的sink pad要想工作在pull模式,它实现的 activate函数将会根据gst_pad_check_pull_range函数的返回值(为true)调用gst_pad_activate_pull函数(因为gstreamer core默认是工作在push模式的,即调用gst_pad_active_push函数)。

      gst_pad_activate_pull函数会进一步调用GST_PAD_ACTIVATEPULLFUNC (pad),它是一个宏,也是在元件2的对象实例的构造函数里以函数指针的形式设置的,形式为xx_xx_activate_pull函数。最后,xx_xx_activate_pull函数会起一个任务,通常为一个线程,负责从元件1的src pad那里“拉拽”数据.当然,元件2的sink 要从元件1的src那里pull数据之前,必须要通知元件1。怎么通知呢?同样也是在gst_pad_activate_pull函数里通知的。读者可参考如下从该函数里摘取的示例代码:

  相见恨晚之gstreamer衬垫的激活_第1张图片

      也就是说,元件2的sink pad在调用gst_pad_activate_pull的过程中,会去通知它的peer pad(这里即为元件1的src pad).通知方式为调用peer pad上的gst_pad_activate_pull.在该pull函数中会根据gst_pad_get_direction判断当前是src pad 还是sink pad。如果是src pad,会去调用getrange函数。在这个步骤完成之后,就可调用自己的xx_xx_activate_pull函数来pull数据了。具体流程,读者可详细阅读gst_pad_activate_pull函数的实现源码。

      简而言之,在下游组件调用gst_pad_activate_pull时,它会去通知上游组件,上游组件的src pad于是知会并去做好准备生产数据的准备。通知方式为对上游元件的src pad再次调用gst_pad_activate_pull函数。判断上游组件是否已经准备好生产数据,是通过调用getrange函数来实现的。

 

 

  

   

 

你可能感兴趣的:(相见恨晚之gstreamer衬垫的激活)