Gstreamer插件教程3.1—高级概念(Advanced Concepts):需求pads和间或pads(Request and Sometimes pads)

英文原文:https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/request.html

到目前为止,你应该能够创建可以接收和发送数据的filter element。这是Gstreamer所支持的简单模型。但Gstreamer还可以做更多的事!在这接下来的章节,我们将讨论多个高级主题,如时间安排、特殊的pad类型、时钟、事件、接口和标签等更多。这些主题使得应用中使用Gstreamer更简单。

直到目前为止,我们只处理了那些一直存在的pads,然而,还有许多pads是只有在某些时候或只有应用程序要求时才被创建的。第一个被称为sometimes pad,第二个被称为request pad。pad的可用性(即always, sometimes或request)是可以在pad的template中看出来的。这章将讨论这两种pad在何时有用,如何创建它们及它们什么时候被销毁。

Sometimes pads

一个 “sometimes" pad是一个在某些特定情况下,而不是任何时候被创建的pad。这主要依赖于流的内容:分流器一般会解析流的头信息,决定什么样的基础(视频、音频及标题等)流会被嵌入到系统流中,并且将会为这些基础流分别创建一个sometimes pad。正如它能够自己选择,对于每一个element实例,它也可以创建不止一个实例。唯一的限制是,每一个创建的pad都必须有其独一无二的名称。sometimes pad在流数据被销毁时,它也会被销毁(如PAUSED状态转变到READY状态时)。你不应该在流末尾销毁pad,因为可能有人重新激活pipeline,并返回至流末尾之前的某点。当到达流末尾时,流至少在流数据被销毁之前保持有效。任何情况下,element都是这个pad的拥有者。

下面的示例代码将解析一个文本文件,其中文件中的第一行是一个数字(n),后面的所有行都将以数字(0到n-1)开头,此数字表示source pad的数量,通过source pad就能够发送数据。

3
0: foo
1: bar
0: boo
2: bye
用于解析此文件及创建动态的"sometimes" pads的代码是类似下面这样的:

typedef struct _GstMyFilter {
[..]
  gboolean firstrun;
  GList *srcpadlist;
} GstMyFilter;

static GstStaticPadTemplate src_factory =
GST_STATIC_PAD_TEMPLATE (
  "src_%u",
  GST_PAD_SRC,
  GST_PAD_SOMETIMES,
  GST_STATIC_CAPS ("ANY")
);

static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]
  gst_element_class_add_pad_template (element_class,
    gst_static_pad_template_get (&src_factory));
[..]
}

static void
gst_my_filter_init (GstMyFilter *filter)
{
[..]
  filter->firstrun = TRUE;
  filter->srcpadlist = NULL;
}

/*
 * Get one line of data - without newline.
 */

static GstBuffer *
gst_my_filter_getline (GstMyFilter *filter)
{
  guint8 *data;
  gint n, num;

  /* max. line length is 512 characters - for safety */
  for (n = 0; n < 512; n++) {
    num = gst_bytestream_peek_bytes (filter->bs, &data, n + 1);
    if (num != n + 1)
      return NULL;

    /* newline? */
    if (data[n] == '\n') {
      GstBuffer *buf = gst_buffer_new_allocate (NULL, n + 1, NULL);

      gst_bytestream_peek_bytes (filter->bs, &data, n);
      gst_buffer_fill (buf, 0, data, n);
      gst_buffer_memset (buf, n, '\0', 1);
      gst_bytestream_flush_fast (filter->bs, n + 1);

      return buf;
    }
  }
}

static void
gst_my_filter_loopfunc (GstElement *element)
{
  GstMyFilter *filter = GST_MY_FILTER (element);
  GstBuffer *buf;
  GstPad *pad;
  GstMapInfo map;
  gint num, n;

  /* parse header */
  if (filter->firstrun) {
    gchar *padname;
    guint8 id;

    if (!(buf = gst_my_filter_getline (filter))) {
      gst_element_error (element, STREAM, READ, (NULL),
             ("Stream contains no header"));
      return;
    }
    gst_buffer_extract (buf, 0, &id, 1);
    num = atoi (id);
    gst_buffer_unref (buf);

    /* for each of the streams, create a pad */
    for (n = 0; n < num; n++) {
      padname = g_strdup_printf ("src_%u", n);
      pad = gst_pad_new_from_static_template (src_factory, padname);
      g_free (padname);

      /* here, you would set _event () and _query () functions */

      /* need to activate the pad before adding */
      gst_pad_set_active (pad, TRUE);

      gst_element_add_pad (element, pad);
      filter->srcpadlist = g_list_append (filter->srcpadlist, pad);
    }
  }

  /* and now, simply parse each line and push over */
  if (!(buf = gst_my_filter_getline (filter))) {
    GstEvent *event = gst_event_new (GST_EVENT_EOS);
    GList *padlist;

    for (padlist = srcpadlist;
         padlist != NULL; padlist = g_list_next (padlist)) {
      pad = GST_PAD (padlist->data);
      gst_pad_push_event (pad, gst_event_ref (event));
    }
    gst_event_unref (event);
    /* pause the task here */
    return;
  }

  /* parse stream number and go beyond the ':' in the data */
  gst_buffer_map (buf, &map, GST_MAP_READ);
  num = atoi (map.data[0]);
  if (num >= 0 && num < g_list_length (filter->srcpadlist)) {
    pad = GST_PAD (g_list_nth_data (filter->srcpadlist, num);

    /* magic buffer parsing foo */
    for (n = 0; map.data[n] != ':' &&
                map.data[n] != '\0'; n++) ;
    if (map.data[n] != '\0') {
      GstBuffer *sub;

      /* create region copy that starts right past the space. The reason
       * that we don't just forward the data pointer is because the
       * pointer is no longer the start of an allocated block of memory,
       * but just a pointer to a position somewhere in the middle of it.
       * That cannot be freed upon disposal, so we'd either crash or have
       * a memleak. Creating a region copy is a simple way to solve that. */
      sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
          n + 1, map.size - n - 1);
      gst_pad_push (pad, sub);
    }
  }
  gst_buffer_unmap (buf, &map);
  gst_buffer_unref (buf);
}
注意我们到处都使用了许多检查以确保文件中的内容是有效的。这有两个目的:第一,文件可能是错误的,若是如此,会导致程序崩溃。第二且最重要的原因,在一些极端的情况下,此文件可能被恶意地使用,以导致插件中存在未知行为,而这可能导致安全问题。我们应总是假设此文件可能导致一些坏的事情的发生。

request pads

"Request" pads和sometimes pads类似,除了request pad是应element之外的(而不是在element内)需求时被创建的。此概念经常被用在整流器上,此时,整流器会应需求生成一个sink pad。整流器会将每一个基础流(视频、音频及标题等)放至要输出的系统流中。request pads也可以被用在拥有多个输入或输出pad的element上,例如tee(多输出)和input-selector(多输入)element。

为了实现request pads,你需要提供一个包含GST_PAD_REQUEST的pad template并在GstElement中实现request_new_pad虚函数。最后为了清理,你还需要实现release_pad虚函数。

static GstPad * gst_my_filter_request_new_pad   (GstElement     *element,
                         GstPadTemplate *templ,
                                                 const gchar    *name,
                                                 const GstCaps  *caps);

static void gst_my_filter_release_pad (GstElement *element,
                                       GstPad *pad);

static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
  "sink_%u",
  GST_PAD_SINK,
  GST_PAD_REQUEST,
  GST_STATIC_CAPS ("ANY")
);

static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]
  gst_element_class_add_pad_template (klass,
    gst_static_pad_template_get (&sink_factory));
[..]
  element_class->request_new_pad = gst_my_filter_request_new_pad;
  element_class->release_pad = gst_my_filter_release_pad;
}

static GstPad *
gst_my_filter_request_new_pad (GstElement     *element,
                   GstPadTemplate *templ,
                   const gchar    *name,
                   const GstCaps  *caps)
{
  GstPad *pad;
  GstMyFilterInputContext *context;

  context = g_new0 (GstMyFilterInputContext, 1);
  pad = gst_pad_new_from_template (templ, name);
  gst_pad_set_element_private (pad, context);

  /* normally, you would set _chain () and _event () functions here */

  gst_element_add_pad (element, pad);

  return pad;
}

static void
gst_my_filter_release_pad (GstElement *element,
                           GstPad *pad)
{
  GstMyFilterInputContext *context;

  context = gst_pad_get_element_private (pad);
  g_free (context);

  gst_element_remove_pad (element, pad);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值