Gstreamer应用开发实战指南

Posted Geek.Fan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gstreamer应用开发实战指南相关的知识,希望对你有一定的参考价值。

1、垫和功能:

        正如我们在 Elements 中看到的,焊盘是元素与外界的接口。 数据从一个元素的源焊盘流向另一个元素的接收焊盘。 元素可以处理的特定媒体类型将通过 pad 的功能公开。

2、垫:

        焊盘类型由两个属性定义:方向和可用性。 正如我们之前提到的,GStreamer 定义了两个焊盘方向:源焊盘和接收焊盘。 这个术语是从元素内部的角度定义的:元素在它们的接收垫上接收数据并在它们的源垫上生成数据。 示意性地,sink pads 绘制在元素的左侧,而 source pads 绘制在元素的右侧。 在这样的图中,数据从左向右流动。

        与焊盘可用性相比,焊盘方向非常简单。 垫可以具有以下三种可用性中的任何一种:始终、有时和应要求。 这三种类型的含义正如它所说的:总是 pad 总是存在,有时 pad 仅在某些情况下存在(并且可以随机消失),并且仅在应用程序明确请求时才会出现 on-request pad。

3、动态焊盘

        创建元素时,某些元素可能没有所有的焊盘。 例如,使用 Ogg demuxer 元素时可能会发生这种情况。 当它在 Ogg 流中检测到这样的流时,该元素将读取 Ogg 流并为每个包含的基本流(vorbis、theora)创建动态填充。 同样,它会在流结束时删除 pad。 例如,这个原则对于解复用器元素非常有用。

#include <gst/gst.h>

static void cb_new_pad (GstElement *element, GstPad *pad, gpointer  data)

  gchar *name;
  name = gst_pad_get_name (pad);
  g_print ("A new pad %s was created\\n", name);
  g_free (name);

  /* here, you would setup a new pad link for the newly created pad */
[..]



int main (int   argc, char *argv[])

  GstElement *pipeline, *source, *demux;
  GMainLoop *loop;

  /* init */
  gst_init (&argc, &argv);

  /* create elements */
  pipeline = gst_pipeline_new ("my_pipeline");
  source = gst_element_factory_make ("filesrc", "source");
  g_object_set (source, "location", argv[1], NULL);
  demux = gst_element_factory_make ("oggdemux", "demuxer");

  /* you would normally check that the elements were created properly */

  /* put together a pipeline */
  gst_bin_add_many (GST_BIN (pipeline), source, demux, NULL);
  gst_element_link_pads (source, "src", demux, "sink");

  /* listen for newly created pads */
  g_signal_connect (demux, "pad-added", G_CALLBACK (cb_new_pad), NULL);

  /* start the pipeline */
  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

[..]

         仅从“填充添加”回调中将元素添加到管道的情况并不少见。 如果这样做,不要忘记使用 gst_element_set_state () 或 gst_element_sync_state_with_parent () 将新添加元素的状态设置为管道的目标状态。

4、请求垫:

        一个元素也可以有请求垫。 这些焊盘不是自动创建的,而是按需创建的。 这对于多路复用器、聚合器和 tee 元素非常有用。 聚合器是将多个输入流的内容合并到一个输出流中的元素。 Tee 元素是相反的:它们是具有一个输入流并将该流复制到其每个输出焊盘的元素,这些输出焊盘是根据请求创建的。 每当应用程序需要另一个流副本时,它可以简单地从 tee 元素请求一个新的输出焊盘。

        以下代码显示了如何从“tee”元素请求新的输出焊盘:

static void some_function (GstElement * tee)

  GstPad *pad;
  gchar *name;

  pad = gst_element_request_pad_simple (tee, "src%d");
  name = gst_pad_get_name (pad);
  g_print ("A new pad %s was created\\n", name);
  g_free (name);

  /* here, you would link the pad */

  /* [..] */

  /* and, after doing that, free our reference */
  gst_object_unref (GST_OBJECT (pad));

gst_element_request_pad_simple() 方法可用于根据pad模板的名称从元素中获取一个pad。 也可以请求与另一个垫模板兼容的垫。 如果您想将一个元素链接到一个多路复用器元素并且您需要一个兼容的焊盘,这将非常有用。 方法 gst_element_get_compatible_pad () 可用于请求兼容的 pad,如下例所示。 它将从任何输入请求来自 Ogg 多路复用器的兼容焊盘。
static void link_to_multiplexer (GstPad * tolink_pad, GstElement * mux)

  GstPad *pad;
  gchar *srcname, *sinkname;

  srcname = gst_pad_get_name (tolink_pad);
  pad = gst_element_get_compatible_pad (mux, tolink_pad, NULL);
  gst_pad_link (tolink_pad, pad);
  sinkname = gst_pad_get_name (pad);
  gst_object_unref (GST_OBJECT (pad));

  g_print ("A new pad %s was created and linked to %s\\n", sinkname, srcname);
  g_free (sinkname);
  g_free (srcname);

5、垫子的功能:

        由于 pad 在外部世界如何查看元素方面起着非常重要的作用,因此实现了一种机制来使用能力来描述可以流过或当前流过 pad 的数据。 在这里,我们将简要描述什么是能力以及如何使用它们,足以理解这个概念。 有关 GStreamer 中定义的功能和所有功能的深入见解,请参阅插件编写器指南

        功能附加到垫模板和垫。 对于 pad 模板,它将描述可以在从该模板创建的 pad 上流式传输的媒体类型。 对于 pad,它可以是可能的上限列表(通常是 pad 模板功能的副本),在这种情况下 pad 尚未协商,或者它是当前通过此 pad 流式传输的媒体类型,在这种情况下 垫已经协商好了。

6、剖析能力

        pad 的功能在 GstCaps 对象中描述。 在内部,一个 GstCaps 将包含一个或多个描述一种媒体类型的 GstStructure。 协商的垫将具有仅包含一个结构的功能集。 此外,此结构将仅包含固定值。 这些约束不适用于未协商的焊盘或焊盘模板。

        例如,下面是“vorbisdec”元素功能的转储,您将通过运行 gst-inspect vorbisdec 获得。 您将看到两个垫:一个源垫和一个汇垫。 这两个垫子始终可用,并且都具有附加功能。 sink pad 将接受 vorbis 编码的音频数据,媒体类型为“audio/x-vorbis”。 源垫将用于将原始(解码)音频样本发送到下一个元素,具有原始音频媒体类型(在本例中为“audio/x-raw”)。 源垫还将包含音频采样率和通道数量的属性,以及我们现在不需要担心的更多属性。 

Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      audio/x-raw
                 format: F32LE
                   rate: [ 1, 2147483647 ]
               channels: [ 1, 256 ]

  SINK template: 'sink'
    Availability: Always
    Capabilities:
      audio/x-vorbis

属性和值:

        属性用于描述能力的额外信息。 一个属性由一个键(一个字符串)和一个值组成。 可以使用不同的可能值类型:

        基本类型,这几乎可以是在 Glib 中注册的任何 GType。 这些属性指示此属性的特定非动态值。 示例包括:

  • 整数值 (G_TYPE_INT):属性具有此精确值。
  • 布尔值 (G_TYPE_BOOLEAN):属性为 TRUE 或 FALSE。
  • 浮点值 (G_TYPE_FLOAT):该属性具有这个精确的浮点值。
  • 字符串值 (G_TYPE_STRING):该属性包含一个 UTF-8 字符串。
  • 分数值(GST_TYPE_FRACTION):包含由整数分子和分母表示的分数。

        z范围类型是由 GStreamer 注册的 GType,用于指示可能值的范围。 它们用于指示允许的音频采样率值或支持的视频大小。 GStreamer 中定义的两种类型是:

  • 整数范围值 (GST_TYPE_INT_RANGE):该属性表示可能的整数范围,具有下限和上限。 例如,“vorbisdec”元素的速率属性可以在 8000 到 50000 之间。
  • 浮点范围值 (GST_TYPE_FLOAT_RANGE):该属性表示可能的浮点值范围,具有下限和上限。
  • 分数范围值 (GST_TYPE_FRACTION_RANGE):该属性表示可能的分数值范围,具有下限和上限。

列表值 (GST_TYPE_LIST):该属性可以从该列表中给出的基本值列表中获取任何值。

        示例:表示支持 44100 Hz 和 48000 Hz 采样率的上限将使用整数值列表,其中一个值为 44100,一个值为 48000。

        数组值 (GST_TYPE_ARRAY):该属性是一个值数组。数组中的每个值本身也是一个完整值。数组中的所有值都应该是相同的基本类型。这意味着数组可以包含整数、整数列表、整数范围的任意组合,对于浮点数或字符串也是如此,但它不能同时包含浮点数和整数。

        示例:对于涉及两个以上通道的音频,需要指定通道布局(对于一个和两个通道音频,除非在大写中另有说明,否则通道布局是隐式的)。因此,通道布局将是一个整数枚举值数组,其中每个枚举值代表一个扬声器位置。与 GST_TYPE_LIST 不同,数组中的值将被解释为一个整体。

使用元数据功能:

        一个垫可以有一组(即一个或多个)附加到它的功能。 Capabilities (GstCaps) 表示为一个或多个 GstStructures 的数组,每个 GstStructure 是一个字段数组,其中每个字段由字段名称字符串(例如“宽度”)和类型化值(例如 G_TYPE_INT 或 GS​​T_TYPE_INT_RANGE)组成。

        请注意,pad 的可能功能(即通常您在 gst-inspect 中显示的 pad 模板的上限)与 pad 的允许上限(可以与 pad 的相同)之间存在明显差异模板上限或它们的子集,取决于对等垫的可能上限)和最后协商的上限(这些描述了流或缓冲区的确切格式,并且只包含一个结构,并且没有像范围或列表这样的可变位,即。它们是固定的上限)。

        我们可以通过查询一个结构的各个属性来获取一组功能中的属性值,也可以使用 gst_caps_get_structure () 从 caps 中获取结构,并使用 gst_caps_get_size () 从 GstCaps 中获取结构的数量。

        大写仅包含一种结构时称为简单大写,而仅包含一种结构且没有可变字段类型(如范围或可能值列表)时称为固定大写。另外两种特殊类型的瓶盖是 ANY 瓶盖和空瓶盖。

        以下是如何从一组固定视频帧中提取宽度和高度的示例:

static void read_video_props (GstCaps *caps)

  gint width, height;
  const GstStructure *str;

  g_return_if_fail (gst_caps_is_fixed (caps));

  str = gst_caps_get_structure (caps, 0);
  if (!gst_structure_get_int (str, "width", &width) ||
      !gst_structure_get_int (str, "height", &height)) 
    g_print ("No width/height available\\n");
    return;
  

  g_print ("The video size of this set of capabilities is %dx%d\\n",
       width, height);

创建过滤功能:

        虽然功能主要在插件中用于描述垫的媒体类型,但应用程序程序员通常还必须对功能有基本的了解才能与插件交互,尤其是在使用过滤帽时。 当您使用过滤上限或固定时,我们将可以在两个垫之间流式传输的允许媒体类型限制为它们支持的媒体类型的子集。 我们可以在管道中使用 capsfilter 元素来执行此操作。 为此,我们还需要创建自己的 GstCaps。 最简单的方法是使用便捷函数 gst_caps_new_simple ():

static gboolean link_elements_with_filter (GstElement *element1, GstElement *element2)

  gboolean link_ok;
  GstCaps *caps;

  caps = gst_caps_new_simple ("video/x-raw",
          "format", G_TYPE_STRING, "I420",
          "width", G_TYPE_INT, 384,
          "height", G_TYPE_INT, 288,
          "framerate", GST_TYPE_FRACTION, 25, 1,
          NULL);

  link_ok = gst_element_link_filtered (element1, element2, caps);
  gst_caps_unref (caps);

  if (!link_ok) 
    g_warning ("Failed to link element1 and element2!");
  

  return link_ok;

        这将强制这两个元素之间的数据流为特定的视频格式、宽度、高度和帧率(或者如果在所涉及的元素的上下文中无法实现,则链接将失败)。 请记住,当我们使用 gst_element_link_filtered () 时,它会自动为我们创建一个 capsfilter 元素并将其插入到我们要连接的两个元素之间的 bin 或管道中(如果我们想断开这些元素,这很重要,因为那时我们将不得不从 capsfilter 断开两个元素)。

        在某些情况下,我们需要创建一组更精细的功能来过滤两个打击垫之间的链接。 然后,这个函数太简单了,你会想使用方法 gst_caps_new_full():

static gboolean link_elements_with_filter (GstElement *element1, GstElement *element2)

  gboolean link_ok;
  GstCaps *caps;

  caps = gst_caps_new_full (
      gst_structure_new ("video/x-raw",
             "width", G_TYPE_INT, 384,
             "height", G_TYPE_INT, 288,
             "framerate", GST_TYPE_FRACTION, 25, 1,
             NULL),
      gst_structure_new ("video/x-bayer",
             "width", G_TYPE_INT, 384,
             "height", G_TYPE_INT, 288,
             "framerate", GST_TYPE_FRACTION, 25, 1,
             NULL),
      NULL);

  link_ok = gst_element_link_filtered (element1, element2, caps);
  gst_caps_unref (caps);

  if (!link_ok) 
    g_warning ("Failed to link element1 and element2!");
  

  return link_ok;

幽灵垫:

        我们可以从没有幻影垫的 GstBin 元素的可视化中看到 bin 是如何没有自己的垫子的。 这就是“幽灵垫”发挥作用的地方。

        ghost pad 是 bin 中某个元素的 pad,也可以直接从 bin 访问。 将其与 UNIX 文件系统中的符号链接进行比较。 在 bin 上使用 ghost pads,bin 也有一个 pad,可以透明地用作代码其他部分的元素。

         带有幽灵垫的 GstBin 元素的可视化是幽灵垫的表示。 元素一的水槽垫现在也是垃圾箱的垫。 因为幽灵焊盘的外观和工作方式与任何其他焊盘一样,它们可以添加到任何类型的元素中,而不仅仅是 GstBin,就像普通焊盘一样。

        使用函数 gst_ghost_pad_new () 创建 ghostpad:

#include <gst/gst.h>

int main (int   argc,  char *argv[])

  GstElement *bin, *sink;
  GstPad *pad;

  /* init */
  gst_init (&argc, &argv);

  /* create element, add to bin */
  sink = gst_element_factory_make ("fakesink", "sink");
  bin = gst_bin_new ("mybin");
  gst_bin_add (GST_BIN (bin), sink);

  /* add ghostpad */
  pad = gst_element_get_static_pad (sink, "sink");
  gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
  gst_object_unref (GST_OBJECT (pad));

[..]

        在上面的示例中,bin 现在还有一个 pad:给定元素的 pad 称为“sink”。 从这里开始,料仓可以用作水槽元件的替代品。 例如,您可以将另一个元素链接到 bin。

        实际上,不反对数据从源焊盘流向上游元件的接收焊盘(在图中该元件的左侧)。 然而,数据总是从一个元素的源焊盘流向另一个元素的汇焊盘。

缓冲区和事件

        流经管道的数据由缓冲区和事件的组合组成。 缓冲区包含实际的媒体数据。 事件包含控制信息,例如寻找信息和流结束通知。 所有这些都将在运行时自动流过管道。 本章主要是为了向你解释这个概念; 你不需要为此做任何事情。

缓冲区

        缓冲区包含将流经您创建的管道的数据。 源元素通常会创建一个新缓冲区,并通过填充将其传递给链中的下一个元素。 使用 GStreamer 基础架构创建媒体管道时,您不必自己处理缓冲区; 这些元素会为你做到这一点。

除其他外,缓冲区包括:

  • 指向内存对象的指针。 内存对象封装了内存中的一个区域。
  • 缓冲区的时间戳
  • 指示有多少元素正在使用此缓冲区的 refcount。 当没有元素引用缓冲区时,此引用计数将用于销毁缓冲区。
  • 缓冲区标志。

        简单的情况是创建一个缓冲区,分配内存,将数据放入其中,然后传递给下一个元素。该元素读取数据,执行某些操作(如创建新缓冲区并解码到其中),然后取消引用缓冲区。这将导致释放数据并销毁缓冲区。典型的视频或音频解码器的工作原理如下。

        但是,还有更复杂的场景。元素可以就地修改缓冲区,即无需分配新缓冲区。元素还可以写入硬件内存(例如来自视频捕获源)或从 X 服务器分配的内存(使用 XShm)。缓冲区可以是只读的,依此类推。

事件:

        事件是与缓冲区一起在管道中向上和下游发送的控制粒子。 下游事件通知流状态的其他元素。 可能的事件包括搜索、刷新、流结束通知等。 上游事件用于应用程序元素交互以及元素元素交互以请求流状态的更改,例如搜索。 对于应用程序,只有上游事件是重要的。 只是为了更全面地了解数据概念而对下游事件进行了解释。

        由于大多数应用程序都以时间单位进行搜索,因此我们下面的示例也是这样做的:

static void seek_to_time (GstElement *element, guint64 time_ns)

  GstEvent *event;

  event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
                  GST_SEEK_FLAG_NONE,
                  GST_SEEK_METHOD_SET, time_ns,
                  GST_SEEK_TYPE_NONE, G_GUINT64_CONSTANT (0));
  gst_element_send_event (element, event);

        函数 gst_element_seek () 是一个快捷方式。 这主要是为了展示它是如何工作的。

以上是关于Gstreamer应用开发实战指南的主要内容,如果未能解决你的问题,请参考以下文章

Gstreamer应用开发实战指南

全面解析C语言多媒体开源框架GStreamer

如何使用 GStreamer 以编程方式创建视频(来自 RGB 和 PCM)?

使用自动工具链接 gstreamer 插件中的外部库

Django企业开发实战 高效Python Web框架指南

hbase实战 hbase权威指南哪本好书