gstreamer教程(8)——构建应用之Pad和Pad的能力集
Pad和能力集:
正如我们在 Elements 中看到的那样,pad 是 Element 与外部世界的接口。数据从一个 Element 的 source pad 流向另一个 Element 的 sink pad。元素可以处理的媒体类型都是通过pad的能力集公布的。我们将在本章后面详细讨论功能(参见 pad 的功能)。
Pad:
pad 类型有两个属性:方向和可用性。正如我们之前提到的,GStreamer 定义了两个 pad 方向:source pads 和 sink pads。此术语是从 元素内部的视图定义的:元素在其 sink pad 上接收数据,并在其 source pad 上生成数据。从原理图上讲,sink pads 绘制在 element 的左侧,而 source pads 绘制在 element 的右侧。在此类图形中,数据从左向右流动。
pad 的方向相比 pad 的可用性 要简单的多。pad 可以一下三种可用性类型:always(总是)、sometimes(有时)和on-request(请求时)。这三种类型的含义正如它所说:always意思是始终存在 pad,sometimes意思是 pad 仅在某些情况下存在(并且可以随机消失),on-request意思是 pad 只有在明确的请求时才会存在。
动态Pad:
在创建元素时,某些元素可能没有pad。例如,这可能发生在 Ogg 解复用器元素中。当该元素在 Ogg 流中检测到此类流时,该元素将读取 Ogg 流并为每个包含的基本流 (vorbis:音频压缩技术, theora:视频压缩技术) 动态创建 pad。同样,当流结束时,它将删除 pad。这个原则对于 demuxer 元素非常有用。
运行 gst-inspect-1.0 oggdemux
将显示该元素只有一个 pad:一个名为 'sink' 的 sink pad。其他 pad 处于 “休眠” 状态。你可以在 pad 模板中看到这一点,因为有一个 “Availability: Sometimes ”属性。根据您播放的 Ogg 文件类型创建 pad。我们将看到,这在当你要创建动态管道时非常重要。你可以将 signal 处理程序附加到 element 上,以便在 element 从其 “sometimes” pad 模板之一创建新 pad 时通知你。以下代码是有关如何执行此操作的示例:
basic-example-11.c
#include <stdio.h> #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); [..] }
该示例并不是完整代码,不能运行,主要用于理解监听pad-added事件和处理该事件的回调函数。
从 “pad-added” 回调中将元素添加到 pipeline 的情况并不少见。如果添加到了pipeline,请不要忘记使用 gst_element_set_state()
或 gst_element_sync_state_with_parent()
把新添加元素的状态设置为 pipeline 管道的目标状态。
请求Pad:
元素当然也可以有请求 pad。这些 pad 不是自动创建的,而只是按需创建。这对于多路复用器、聚合器和 tee 元素是非常有用。聚合器是将多个输入流的内容合并到一个输出流中的元素。Tee 元素则相反:它们是具有一个输入流的元素,并将此流复制到其每个输出的 pad 上,这些输出 pad 就是根据请求创建的。每当应用程序需要流的另一个副本时,它只需要从 tee 元素请求新的输出 pad 即可。
以下代码显示了如何从 “tee” 元素请求新的输出 pad:
basic-example-12.c
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 模板的名称从element 元素中获取 pad。也可以请求与另一个 pad 模板兼容的 pad。这在你要请求一个兼容的 pad 用于将 element 元素链接到多路转换器 element 元素时会非常有用。该方法 gst_element_get_compatible_pad()
可用于请求兼容的 pad,如下例所示。它将从任何输入源的 Ogg 多路复用器中请求兼容的 pad。
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); }
Pad的能力集:
由于 Pad 在外界如何理解元素方面起着非常重要的作用,因此实现了通过能力集来描述数据可以流经或当下流经的pad的一种机制。在这里,我们将简要描述能力集是什么以及如何使用它们,让我们足以理解能力集这个概念。要深入了解能力集以及 GStreamer 中定义的所有能力集列表,请参阅插件编写者指南。
能力集是附加到 Pad 模板和 Pad的。对于 pad 模板,它将描述可能在从此模板创建的 pad 上流经的媒体类型。对于 pad,对于没有协商之前,它是可能的 caps 列表(通常是 pad 模板能力集的副本),对于已经协商好的pad,它是当前流经此 pad 的媒体类型。
剖析能力集:
Pad 的能力集在 GstCaps
对象中描述。在内部, GstCaps
将包含一个或多个 GstStructure
,用于描述一种媒体类型。协商的 pad 将具有仅包含一个结构的功能集。此外,此结构将仅包含固定值。这些约束不适用于未协商的 pad 或 pad 模板。
例如,下面是 “vorbisdec” 元素功能集,您将通过运行 gst-inspect-1.0 vorbisdec 来获得。您将看到两个 Pad:一个 Source Pad 和一个 Sink Pad。这两个 pad 始终可用,并且都具有附加的功能。sink pad 将接受 vorbis 编码的音频数据,媒体类型为 “audio/x-vorbis”。source pad 将用于将原始 (解码) 音频样本发送到下一个元素,其中包含原始音频媒体类型 (在本例中为“audio/x-raw”) 。Source Pad 还将包含音频采样率和声道数量的属性,以及一些您暂时无需担心的属性。
/opt/gstreamer# gst-inspect-1.0 vorbisdec Factory Details: Rank primary (256) Long-name Vorbis audio decoder Klass Codec/Decoder/Audio Description decode raw vorbis streams to float audio Author Benjamin Otte <otte@gnome.org>, Chris Lord <chris@openedhand.com> Plugin Details: Name vorbis Description Vorbis plugin library Filename /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstvorbis.so Version 1.14.5 License LGPL Source module gst-plugins-base Source release date 2019-05-29 Binary package GStreamer Base Plugins (Ubuntu) Origin URL https://launchpad.net/distros/ubuntu/+source/gst-plugins-base1.0 GObject +----GInitiallyUnowned +----GstObject +----GstElement +----GstAudioDecoder +----GstVorbisDec 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 Element has no clocking capabilities. Element has no URI handling capabilities. Pads: SINK: 'sink' Pad Template: 'sink' SRC: 'src' Pad Template: 'src' Element Properties: name : The name of the object flags: 可读, 可写 String. Default: "vorbisdec0" parent : The parent of the object flags: 可读, 可写 Object of type "GstObject" min-latency : Aggregate output data to a minimum of latency time (ns) flags: 可读, 可写 Integer64. Range: 0 - 9223372036854775807 Default: 0 tolerance : Perfect ts while timestamp jitter/imperfection within tolerance (ns) flags: 可读, 可写 Integer64. Range: 0 - 9223372036854775807 Default: 0 plc : Perform packet loss concealment (if supported) flags: 可读, 可写 Boolean. Default: false
属性和值:
属性用于描述能力集的额外信息。属性由键 (字符串) 和值组成。这里是可以使用一些不同的可能的值类型:
- 基础类型,这几乎可以是任何在 Glib 中注册的
GType
。这些属性指示此属性的特定非动态值。示例包括:
- 整数值 (
G_TYPE_INT
):属性具有此确切值。 - 布尔值 (
G_TYPE_BOOLEAN
):属性为TRUE
或FALSE
。 - 浮点值 (
G_TYPE_FLOAT
):属性具有此确切的浮点值。 - 字符串值 (
G_TYPE_STRING
):该属性包含 UTF-8 字符串。 - 分数值 (
GST_TYPE_FRACTION
):包含由整数分子和分母表示的分数。
- 范围类型是由 GStreamer 注册的
GType
,用于指示可能的值范围。它们用于指示允许的音频采样率值或支持的视频大小。GStreamer 中定义的两种类型是:
- 整数范围值 (
GST_TYPE_INT_RANGE
):该属性表示可能的整数范围,具有下限和上限。例如,“vorbisdec” 元素的 rate 属性可以介于 8000 和 50000 之间。 - 浮点范围值 (
GST_TYPE_FLOAT_RANGE
):该属性表示可能的浮点值范围,具有下限和上限。 - 分数范围值 (
GST_TYPE_FRACTION_RANGE
):该属性表示可能的分数值范围,具有下限和上限。
- 列表值 (
GST_TYPE_LIST
):该属性可以从此列表中给出的基本值列表中获取任何值。
- 示例:表示支持 44100 Hz 采样率和 48000 Hz 采样率的上限将使用整数值列表,其中一个值为 44100,一个值为 48000。
- 数组值 (
GST_TYPE_ARRAY
):属性是值的数组。数组中的每个值本身也是一个完整值。数组中的所有值都应该是相同的 elementary 类型。这意味着数组可以包含整数、整数列表、整数范围的任意组合,浮点数或字符串也是如此,但不能同时包含 float 和 int。
- 示例:对于涉及两个以上声道的音频,需要指定声道布局(对于单声道和双声道音频,除非大写字母中另有说明,否则声道布局是隐式的)。因此,声道布局将是一个整数枚举值数组,其中每个枚举值代表一个扬声器位置。与
GST_TYPE_LIST
不同,数组中的值将作为一个整体进行解释。
能力集能干什么:
能力集 (简称:caps) 描述在两个 Pad 之间流式传输的数据类型,或者一个Pad (模板) 支持的数据类型。这使得它们对于各种目的非常有用:
- Autoplugging 自适应插拔器:根据其能力集自动链接元素之间的 pad。所有 Autoplugging 自适应插拔器都使用此方法。
- 能力集检测:当两个 pad 链接时,GStreamer 可以验证两个 pad 是否使用的是相同的媒体类型。链接两个 pad 并校验它们是否兼容的过程称为 “能力集协商”。
- 元数据:通过从pad读取能力集,应用程序可以提供有关流经pad的媒体类型的信息,即有关当前正在播放的流的信息。
- 筛选:应用程序可以使用能力集将可在两个pad之间流式传输的流媒体类型限制为其支持的流媒体类型的特定子集。例如,应用程序可以使用 “filtered caps” 来限定两个 pad 之间流经的流的属性为特定的 (固定或非固定) 视频大小。您将在本手册后面的 Manually adding or removing data from / to a pipeline 中看到一个过滤的示例。你可以通过向pipeline管道插入一个 capsfilter 元素,并设置其 “caps” 属性来执行 caps 过滤。Caps 过滤器通常放置在 audioconvert、audioresample、videoconvert 或 videoscale 等转换器元素之后,以强制这些转换器在流中的某位置将数据转换为特定的输出格式。
使用元数据能力集:
一个 pad 可以附加一组 (即一个或多个) 能力集。能力集 (GstCaps
) 表示为一个或多个 GstStructure
的数组,每个 GstStructure
都是一个字段数组,其中每个字段由字段名称字符串(例如“width”)和类型化值(例如 G_TYPE_INT
或 GST_TYPE_INT_RANGE
)组成。
-请注意,pad的可能功能(即通常是 gst-inspect 中显示的焊盘模板的 caps ),焊盘允许的电容(可以与 pad 的模板电容或它们的子集相同,具体取决于对等焊盘的可能电容)和最后协商之间存在明显差异caps (这些描述流或缓冲区的确切格式,并且只包含一个结构,并且没有像 ranges 或 lists 这样的可变位,即它们是固定的 caps)。
您可以通过查询一个结构体的单个属性来获取一组功能中的属性值。您可以使用 gst_caps_get_structure ()
从 Cap 中获取结构,使用 gst_caps_get_size ()
从 GstCaps
中获取结构数。
当大写字母仅包含一个结构时称为简单大写字母,当它们仅包含一个结构且没有可变字段类型(如范围或可能值列表)时称为固定大写字母。另外两种特殊类型的 Cap 是 ANY Cap 和 Empty Cap。
以下是如何从一组固定视频大写字母中提取宽度和高度的示例:
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); }
创建筛选功能:
虽然功能主要在插件内部用于描述打击垫的媒体类型,但应用程序程序员通常还必须对功能有基本的了解,以便与插件连接,尤其是在使用过滤电容时。当您使用过滤的 Cap 或 Fixation 时,您将允许在两个打击垫之间流式传输的媒体类型限制为其支持的媒体类型的子集。您可以在管道中使用 capsfilter
元素来执行此操作。为此,您还需要创建自己的 GstCap
。最简单的方法是使用便捷函数 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 的连接)。
在某些情况下,您将需要创建一组更复杂的功能来过滤两个 pad 之间的链接。那么,这个函数太简单了,你需要使用 () 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; }
有关 GstStructure
和 GstCaps
的完整 API,请参阅 API 参考。
Ghost Pad:
从没有重影焊盘的 GstBin 元素的可视化中可以看到,bin 如何没有自己的焊盘。这就是 “幽灵垫 ”发挥作用的地方。
鬼垫是来自 bin 中某个元素的 pad,也可以直接从 bin 访问。将其与 UNIX 文件系统中的符号链接进行比较。在 bin 上使用 ghost pad,bin 也有一个 pad,并且可以透明地用作代码其他部分的元素。
带有 ghost pad 的 GstBin 元素的可视化是 ghost pad 的表示形式。元素 1 的 sink pad 现在也是 bin 的 pad。因为 ghost pad 的外观和工作方式与任何其他 pads 一样,所以它们可以添加到任何类型的元素中,而不仅仅是 GstBin
,就像普通 pad 一样。
使用函数 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:给定元素的名为 “sink” 的 pad。从这里开始,bin 可以用作 sink 元素的替代品。例如,您可以将另一个元素链接到 bin。