深入浅出gstreamer的调度模式

        gstreamer的官方文档里,调度的英文是schedule. 什么是schedule? 它的英文解释为" to plan that something will happen at a particular time “.在gstreamer里,调度的意义跟它基本差不多。在gstreamer里,调度的目的主要有以下几个:

      (1)在适当的时候通知每个元件去完成相应的任务,确保这些职责的执行。这个任务,就是数据处理。

      (2)每个元件都从它的上一个源元件获取数据,并为下一个元件准备好数据。即获取输入数据处理数据,最后输出数据给下一个元件。   

       GStreamer中,衬垫(pad)是用来在元件间协商连接和数据流的。衬垫可以看作元件间互相连接的接口,数据流通过这些接口流入流出元件。Pad具有特殊的数据处理能力:衬垫可以限制通过它的数据类型。只有当两个衬垫允许通过的数据类型兼容时才可以将它们连接起来。

    第一个source元件,只有src衬垫,即只输出数据;最后一个sink(输出)元件,只有sink衬垫,即只接收数据;管道中其他元件,一般都有一个sink衬垫和source衬垫。

    Gstreamer的调度,也可以理解为不同元件的pad连接和通信,即处理pad之间的数据流的启动,暂停,继续和终止。

      调度模式有两种:PUSH模式和PULL模式。在此,我将其翻译为推送模式和拉拽模式。

1.什么是推送模式和拉拽模式?

      Downstream(下游) 和 Upstream(上游)模式是管道中用于描述数据流动方向的术语。从source元件到sink元件的数据流动称作“下游”,从sink元件到source元件的数据流动模式称为“上游”模式。

       在推送模式下,上游元件通过调用gst_pad_push,实现把数据“推送”给下游元件。

       在拉拽模式下,下游元件通过请求调用gst_pad_pull_range(),从上游组件那里把数据“拉”过来。

     上面提到的数据,一般是一个缓存(buffer).

      用得最多的数据流模式是推送模式。拉拽模式可以用在某些特殊情况下,如demuxer元件。

2.推送模式是怎么工作的?

       
  .----------.             .----------.           .----------.
  | element1 |      | element2 |      | element3 |
 ...        src -> sink       src -> sink       ...
  '----------'               '----------'            '----------'      

      如上图所示,假设元件1的src pad工作在push模式。当元件1打算把数据推送给下一个与之相连接的元件2时,元件1的src pad通常产生数据,元件2的相应sink pad(相对于元件1,被称为peer pad)接受数据。通常,元件1会实现一个loop函数,该函数会被循环调用直到它返回值为假。该函数可以根据需要进行阻塞循环调用,但当元件1的pad解除激活时,该函数就解除阻塞(即不再调用)。

       元件2的pad通过实现一个链条(chain)函数来接受元件1的pad生产的数据。

       push模式的实现是由元件1通过gst_pad_push()来实现的. 产生数据的元件pad, 注意:这个方法根据传入的pad参数,具体调用相应的链接函数xx_chain。该方法的函数原型是GstFlowReturngst_pad_push (GstPad * pad, GstBuffer * buffer)。在该函数的实现里,通过调用 GST_PAD_CHAINFUNC (peer) (peer, buffer)来把数据推送给下一个元件,同时下一个元件根据他来接受数据。

      总之,推送模式下,源元件发起数据传输,是管道中的驱动力量;下游元件在chain函数中接收buffer.

这样,就完成了从上游元件到下游元件的buffer传递。

 

3.拉拽模式是怎么工作的?

      工作在PULL模式的pad, 通常只能从实现了pull_range函数的pad那里“拉”数据。

   .----------.             .----------.          .----------.         .----------.
  | element1 |      | element2 |      | demuxer|    |decoder|
 ...        src -> sink            src -> sink          src -> sink     ...
   '----------'             '----------'           '----------'            ----------'     

        如上图所示,我们假设demuxer元件的sink pad工作在pull模式,并是以plugin的形式加载到工作管道中。在demuxer对象实例初始化时候,我们一般首先会新建和初始化一个sink pad,并通过调用gst_pad_set_activate_function (avi->sinkpad, gst_xx_demux_sink_activate)来设置pad激活时的函数指针。于是,当pipeline状态变化时,尤其是demuxer元件从READY切换到PAUSE状态时,会进行如下图所示的函数调用:

 

在上图的激活函数里,会去激活demuxer 的sink pad, 它是通过遍历所有的sink pads,调用activate_pads来激活pad的。如下图所示:

在gst_pad_set_actie函数里,最后会调用(GST_PAD_ACTIVATEFUNC(pad)) (pad); 被调用这个宏函数由于在demuxer元件里设置了函数指针gst_pad_set_activate_function (avi->sinkpad, gst_xx_demux_sink_activate),于是其实对于demuxer组件来说,执行的是gst_xx_demux_sink_activate函数。

     常,这个函数会调用gst_pad_check_pull_range,并根据前面图中的element2的pad(称之为peer pad)是否实现了XX_pull_range函数,来判断demuxer 元件的sink pad应该激活在pull模式还是push模式我们这里讨论激活在pull模式的情况。

         当激活在pull模式下,我们会给demux起一个task, task函数为gst_xx_demux_loop函数。gst_avi_demux_loop函数会从peer pad 拉拽数据。拉拽完数据后,通常会把数据push 到下一个元件的sink pad.

 

4.调度模式是如何动态决定的

      这个问题的答案可以在上面第3节找到找到。这里总结下:当一个pad被激活时,gst_element_pad_activate()函数被调用. 这个pad随后可以根据上游的能力(upstream capabilities,实际上为是否实现了XX_pull_range)来决定激活在push模式还是pull模式。如果一个pad没有activate函数,gstreamer core将默认为它激活在push模式。

 

5.getrange函数

     当gst_pad_check_pull_range调用时,peer pad的getrange函数被调用。事实上,前面说过,一个上游pad只有实现了getrange函数,才能被下游的pad以pull的模式调用它从而拉拽数据。下游pad对getrange函数的调用,是通过以宏的形式---GST_PAD_GETRANGEFUNC (pad)来传递的。而该宏的赋值,是上游的可以支持pull模式的元件在初始化的时候,调用gst_pad_set_getrange_function 设置的。

6.chain函数(链条函数)

    当上游元件的pad调用gst_pad_push函数时,下游元件pad的chain函数被调用。



 

         

 

 

你可能感兴趣的:(多媒体)