gstreamer不同的调度模式

本文详细介绍了GStreamer中数据流的调度模式,包括推模式和拉模式。推模式中,元素通过gst_pad_push()在srcpad上推送buffer,而拉模式下,元素启动任务并通过gst_pad_pull_range()从sinkpad获取数据。GStreamer允许元素的不同pads以不同模式运行,提供了更大的灵活性。解复用器、解析器和某些类型的解码器通常更倾向于拉模式,因为它允许字节精确的随机访问。文章还展示了如何在元素中实现拉模式的任务函数和_get_range()函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

pad的调度模式定义了数据如何从source pads获取数据或给数据给sinkpads。GStreamer可以在两种调度模式下运行,称为推模式和拉模式。GStreamer支持在任何一种调度模式下的element pads,而不是所有的pads都需要在同一模式下运行。

到目前为止,我们只讨论了_chain()操作的element,即在他们的sink pad上有一个chain函数设置,在他们的source pads上有push buffer。我们称之为推模式,因为对等元素会在srcpad上使用gst_pad_push(),这将导致我们的_chain()函数被调用,这反过来又导致我们的元素在srcpad上推送出一个buffer。启动数据流发生在上游的某个地方,当它推送出一个buffer时,所有下游的元素在它们的_chain()函数被依次调用时都会被调度。

在解释拉动模式的调度之前,让我们先了解一下不同的调度模式是如何在pad上被选择和激活的。

pads激活阶段

READY->PAUSED的元件状态变化期间,GStreamer调用一个pads的_activate()函数,元件的pads将被激活。这首先发生在元素的srcpads上,然后在元素的sinkpads上。默认情况下,这个函数将通过调用gst_pad_activate_mode ()GST_PAD_MODE_PUSH调度模式激活pads。可以重载一个pads的_activate()函数决定一个不同的调度模式。你可以通过重载_activate_mode ()-函数来知道一个垫子在什么调度模式下被激活。

GStreamer允许一个元素的不同pads以不同的调度模式运行。这允许许多不同的可能使用情况,下面是一些典型使用情况的概述。

  • 如果一个元素的所有pads在push模式的调度中被激活,那么这个元素作为一个整体是在push模式下运行。对于source element来说,这意味着他们将不得不启动一个任务,将srcpad上的buffer推送给下游元素。下游元素将由上游元素使用sinkpads _chain ()函数推送数据给他们,这将推送srcpads上的buffer。这种调度模式的前提条件是,使用gst_pad_set_chain_function()为每个sinkpad设置一个chain函数,并且所有下游元素都以相同的模式运行。

  • 另外,sinkpad可以成为管道背后的驱动力,在拉动模式下运行,而元素的sourcepads仍然在推动模式下运行。为了成为驱动力,这些pads在被激活时要启动一个GstTask。这个任务是一个线程,它将调用一个由元素指定的函数。当被调用时,这个函数将在所有sinkpads上有随机的数据访问(通过gst_pad_pull_range()),并可以在sourcepads上推送数据,这实际上意味着这个元素控制着管道中的数据流。这种模式的前提条件是,所有下游元素可以在推模式下行动,并且所有上游元素在拉模式下操作(见下文)。

    当下游元素从GST_QUERY_SCHEDULING查询中返回GST_PAD_MODE_PULL时,srcpads可以以PULL模式激活。这种调度模式的前提条件是,使用gst_pad_set_getrange_function ()srcpads设置了一个getrange-function

  • 最后,一个元素中的所有pads可以在PULL模式下被激活。然而,与上述情况相反,这并不意味着它们自己开始一个任务。相反,这意味着它们是下游元素的拉动从属,并且必须从它们的_get_range()函数中提供随机数据访问。要求是使用函数gst_pad_set_getrange_function ()在这个pads上设置一个_get_range ()函数。另外,如果该元素有任何sinkpads,所有这些sinkpads(以及它们的peers pads)也需要在PULL访问模式下运行。

    当一个sink elementPULL模式下被激活时,它应该启动一个任务,在其sinkpad上调用gst_pad_pull_range()。只有当上游的SCHEDULING查询返回对GST_PAD_MODE_PULL调度模式的支持时,它才能这样做。

pads驱动管道

在拉模式下运行的sinkpads,在推模式下运行的sourcepads(或者当它是一个sink时没有sourcepads),可以启动一个任务,驱动管道数据流。在这个任务功能中,你可以随机访问所有的sinkpads,并通过源排推送数据。这对几种不同类型的元素都很有用。

解复用器、解析器和某些类型的解码器,其中数据是未经解析的(如MPEG音频或视频流),因为这些元素更喜欢从它们的输入中进行字节精确(随机)访问。然而,如果可能的话,这些元素也应该准备在推模式下运行。

某些音频输出,需要控制它们的输入数据流,如Jack sound server

首先,你需要执行一个SCHEDULING查询,检查上游元素是否支持拉动模式的调度。如果可以的话,你可以在拉动模式下激活sinkpad。在activation_mode函数里面,你就可以启动任务了。

#include "filter.h"
#include <string.h>

static gboolean gst_my_filter_activate      (GstPad      * pad,
                                             GstObject   * parent);
static gboolean gst_my_filter_activate_mode (GstPad      * pad,
                                             GstObject   * parent,
                                             GstPadMode    mode,
                         gboolean      active);
static void gst_my_filter_loop      (GstMyFilter * filter);

G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE(my_filter, "my-filter", GST_RANK_NONE, GST_TYPE_MY_FILTER);

static void
gst_my_filter_init (GstMyFilter * filter)
{

[..]

  gst_pad_set_activate_function (filter->sinkpad, gst_my_filter_activate);
  gst_pad_set_activatemode_function (filter->sinkpad,
      gst_my_filter_activate_mode);


[..]
}

[..]

static gboolean
gst_my_filter_activate (GstPad * pad, GstObject * parent)
{
  GstQuery *query;
  gboolean pull_mode;

  /* first check what upstream scheduling is supported */
  query = gst_query_new_scheduling ();

  if (!gst_pad_peer_query (pad, query)) {
    gst_query_unref (query);
    goto activate_push;
  }

  /* see if pull-mode is supported */
  pull_mode = gst_query_has_scheduling_mode_with_flags (query,
      GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
  gst_query_unref (query);

  if (!pull_mode)
    goto activate_push;

  /* now we can activate in pull-mode. GStreamer will also
   * activate the upstream peer in pull-mode */
  return gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE);

activate_push:
  {
    /* something not right, we fallback to push-mode */
    return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
  }
}

static gboolean
gst_my_filter_activate_pull (GstPad    * pad,
                 GstObject * parent,
                 GstPadMode  mode,
                 gboolean    active)
{
  gboolean res;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (mode) {
    case GST_PAD_MODE_PUSH:
      res = TRUE;
      break;
    case GST_PAD_MODE_PULL:
      if (active) {
        filter->offset = 0;
        res = gst_pad_start_task (pad,
            (GstTaskFunction) gst_my_filter_loop, filter, NULL);
      } else {
        res = gst_pad_stop_task (pad);
      }
      break;
    default:
      /* unknown scheduling mode */
      res = FALSE;
      break;
  }
  return res;
}

一旦启动,你的任务就可以完全控制输入和输出。任务函数的最简单的情况是读取输入并将其推送到其srcpads上。它不是那么有用,但比我们到目前为止所看到的旧的推送模式的情况提供了一些更多的灵活性。

 #define BLOCKSIZE 2048

    static void
    gst_my_filter_loop (GstMyFilter * filter)
    {
      GstFlowReturn ret;
      guint64 len;
      GstBuffer *buf = NULL;

      if (!gst_pad_query_duration (filter->sinkpad, GST_FORMAT_BYTES, &len)) {
        GST_DEBUG_OBJECT (filter, "failed to query duration, pausing");
        goto stop;
      }

       if (filter->offset >= len) {
        GST_DEBUG_OBJECT (filter, "at end of input, sending EOS, pausing");
        gst_pad_push_event (filter->srcpad, gst_event_new_eos ());
        goto stop;
      }

      /* now, read BLOCKSIZE bytes from byte offset filter->offset */
      ret = gst_pad_pull_range (filter->sinkpad, filter->offset,
          BLOCKSIZE, &buf);

      if (ret != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (filter, "pull_range failed: %s", gst_flow_get_name (ret));
        goto stop;
      }

      /* now push buffer downstream */
      ret = gst_pad_push (filter->srcpad, buf);

      buf = NULL; /* gst_pad_push() took ownership of buffer */

      if (ret != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (filter, "pad_push failed: %s", gst_flow_get_name (ret));
        goto stop;
      }

      /* everything is fine, increase offset and wait for us to be called again */
      filter->offset += BLOCKSIZE;
      return;

    stop:
      GST_DEBUG_OBJECT (filter, "pausing task");
      gst_pad_pause_task (filter->sinkpad);
    }

提供随机访问

在上一节中,我们已经谈到了被激活的元素(或pads)如何使用它们自己的任务来驱动流水线,必须在它们的sinkpads上使用拉模式调度。这意味着所有与这些pads相连的pads都需要以拉模式激活。以拉模式激活的srcpads必须实现一个_get_range ()函数,通过使用gst_pad_set_getrange_function ()设置,当对等pads用gst_pad_pull_range ()请求一些数据时,该函数将被调用。然后,该元素负责寻找到正确的偏移量并提供所要求的数据。

有几个元素可以实现随机访问:

  • data source,如file source,可以从任何偏移量以合理的低延迟提供数据。
  • filters,在整个管道上提供拉动模式调度的过滤器。
  • parsers,解析器可以通过跳过其输入的一小部分来轻松地提供这种调度,因此基本上是 "转发 "range请求,而不涉及任何自己的处理。这方面的例子包括标签阅读器(如ID3)或单一输出解析器,如WAVE解析器。

下面的例子将展示如何在一个source element中实现_get_range()-函数:

#include "filter.h"
static GstFlowReturn
        gst_my_filter_get_range (GstPad     * pad,
                     GstObject  * parent,
                     guint64      offset,
                     guint        length,
                     GstBuffer ** buf);

G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE(my_filter, "my-filter", GST_RANK_NONE, GST_TYPE_MY_FILTER);


static void
gst_my_filter_init (GstMyFilter * filter)
{

[..]

  gst_pad_set_getrange_function (filter->srcpad,
      gst_my_filter_get_range);

[..]
}

static GstFlowReturn
gst_my_filter_get_range (GstPad     * pad,
             GstObject  * parent,
             guint64      offset,
             guint        length,
             GstBuffer ** buf)
{

  GstMyFilter *filter = GST_MY_FILTER (parent);

  [.. here, you would fill *buf ..]

  return GST_FLOW_OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值