openwrt gstreamer实例学习笔记(六. gstreamer Pads及其功能)
一:概述
如我们在Elements一章中看到的那样,Pads是element对外的接口。数据流从一个element的source pad到另一个element的sink pad。pads的功能(capabilities)决定了一个element所能处理的媒体类型。
一个pad的类型由2个特性决定:它的数据导向(direction)以及它的时效性(availability)。正如我们先前提到 的,Gstreamer定义了2种pads的数据导向:source pad以及sink pad。pads的数据导向这个术语是从element内部的角度给予定义的:
element通过它们的sink pad接收数据,通过它们的source pad输出数据。pads的时效性比pads的数据导向复杂得多。一个pads可以拥有三种类型的时效性: 永久型(always)、随机型(sometimes)、请求型(on request)。三种时效性的意义顾名思义: 永久型的pads一直会存在,随机型的pads只在某种特定的条件下才存在(会随机消失的pads也属于随机型),请求型的pads只在应用程序明确发出请求时才出现。
1).动态(随机)pads
一些element在其被创建时不会立刻产生所有它将用到的pads。例如在一个Ogg demuxer的element中可能发生这种情况。这个element将会读取Ogg流,每当它在Ogg流中检测到一些元数据流时(例如vorbis,theora ),它会为每个元数据流创建动态pads。同样,它也会在流终止时删除该pads。动态pads在demuxer这种element中可以起到很大的作用。
运行gst-inspect oggdemux只会显示出一个pads在元件中: 一个名字叫作‘sink‘的sink pad,其它的pads都处于‘休眠‘中,你可以从pad模板(pad template)中的"Exists: Sometimes"的属性看到这些信息。pad会根据你所播放的Ogg文件的类型而产生,认识到这点 对于你创建一个动态管道特别重要。当pads通过它的随机型(sometimes)pads模板创建了一个随机型(sometimes)的pads的时侯,你可以通过对该element绑定一个信号处理器(signal handler),通过它来得知pads被创建。下面一段代码演示了如何这样做:
名叫‘sink‘的sink pads,其它的pads都处于‘休眠‘中,显而易见这是pads”有时存在”的特性。pads会根据你所播放的Ogg文件的类型而产生,这点在你准备创建一个动态管道时显得特别重要,当element创建了一个”有时存在”的pad时,你可以通过对该element触发一个信号处理器(signal handler) 来得知pads被创建。
#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);
[..]
}
2).请求pads
element同样可以拥有request pads。这种pads不是自动被创建,而是根据请求被创建的。这在多路复用(multiplexers)类型的element中有很大的用处。例如 aggregators以及tee element。
Aggregators element可以把多个输入流合并成一个输出流; tee element件正好相反,它只有一个输入流,然后根据请求把数据流发送到不同的输出 pads。只要应用程序需要另一份数据流,它可以简单的从tee element请求到一个 输出pads。
下面一段代码演示了怎样在一个”tee”元件请求一个新的输出衬垫:
static void some_function (GstElement *tee)
{
GstPad * pad;
gchar *name;
pad = gst_element_get_request_pad (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_get_request_pad()方法可以从一个element中得到一个pads,这个pads基于pads模板的名字(pad template)。同样可以请求一个同其它pads模板兼容的pads,这点在某些情况下非常重要。
比如当你想要将一个element连接到一个多路复用型的element时,你就需要请求一个带兼容性的pad。gst_element_get_compatible_pad()方法可以得到一个带兼容性的pad。
下面一段代码将从一个基于Ogg的带多输入pads的element中请求一个带兼容性的pads。
static void ink_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);
gst_pad_link (tolinkpad, 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", srcname, sinkname);
g_free (sinkname);
g_free (srcname);
}
二:(Pads)的性能
由于pads对于一个element起了非常重要的作用,因此就有了一个术语来描述能够通过pads或当前通过pads的数据流。这个术语就是功能 (capabilities)。
pads的功能(capabilities)是与pads模板(pad templates)以及pads实例相关联的。对于pads模板,pads的功能(capabilities)描述的是:当通过该pads模板创建了一个pads后,该pads允许通过的媒体类型。
对于pads实例,功能可以描述所有可能通过该pads的媒体类型(通常是该pads实例所属的pads模板的功能的一份拷贝),或者是当前已经通过该pads的流媒体类型。前一种情况,该pads实例还未有任何数据流在其中通过。
1). 分解功能
pads的功能通过GstCaps 对象来进行描述。一个GstCaps对象会包含一个或多个 GstStructure。一个 GstStructure描述一种媒体类型。一个被数据流通过的pad(negotiated pad)存在功能集(capabilities set),每种功能只包含一个GstStructure结构。结构中只包含固定的值。但上述约束并不对尚未有数据流通过的pads(unnegotiated pads)或pads模板有效。
下面给出了一个例子,你可以通过运行 gst-inspect vorbisdec 看到"vorbisdec" 元件的一些功能。你可能会看到2个pads: source pad, sink pad, 2衬垫的时效性都是永久型,并且每个pads都有相应的功能描述。sink pads会接收vorbis编码的音频数据,其 mime-type显示为"audio/x-vorbis"。source pads可以将解码后的音频数据采样(raw audio samples)发送给下一个element,其 mime-type显示为为"audio/x-raw-int"。source pad功能描述中还包含了一些其它的特性: 音频采样率(audio samplerate)、声道数、以及一些你可能并不太关心的信息。
Pad Templates:
SRC template: ‘src‘
Availability: Always
Capabilities:
audio/x-raw-float
rate: [ 8000, 50000 ]
channels: [ 1, 2 ]
endianness: 1234
width: 32
buffer-frames: 0
SINK template: ‘sink‘
Availability: Always
Capabilities:
audio/x-vorbis
2).特性与值
特性(Properties)用来描述功能中的额外信息(注:除数据流类型之外的信息)。一条特性由一个关键字和一个值组 成。下面是一些值的类型:
基本类型,几乎涵盖了Glib 中的所有GType类型。这些类型为每条特性(Properties)指明了一个明确,非动态的值。例子如下所示:
整型(G_TYPE_INT): 明确的数值(相对范围值)。
布尔类型:(G_TYPE_BOOLEAN): TRUE 或FALSE。
浮点类型:(G_TYPE_FLOAT): 明确的浮点数。
字符串类型:(G_TYPE_STRING): UTF-8 编码的字符串。
分数类型:(GST_TYPE_FRACTION): 由整数做分子分母的分数。
范围类型(Range types):由GStreamer注册的且属于GTypes的一种数据类型。它指明了一个范围值。范围类型通常被用来指示支持的音频采样率范围或者支持 的视频文件大小范围。GStreamer 中又有2种不同类型的范围值。
整型范围值(GST_TYPE_INT_RANGE): 用最大和最小边界值指明了一个整型数值范围。举例来说: "vorbisdec"元件的采样率范围为8000-50000。
浮点范围值(GST_TYPE_FLOAT_RANGE): 用最大和最小边界值指明了一个浮点数值范围。
分数范围值(GST_TYPE_FRACTION_RANGE): 用最大和最小边界值指明了一个分数数值范围。
列表类型(GST_TYPE_LIST):可以在给定的列表中取任何一个值。示例:某个衬垫的功能如果想要表示其支持采样率为44100Hz以及48000Hz的数据,它可以用一个包含44100和48000的列表 数据类型。
数组类型(GST_TYPE_ARRAY): 一组数据。数组中的每个元素都是特性的全值(full value)。数组中的元素必须是同样的数据类型。这意味着一个数组可以包含任意的整数,整型的列表,整型范围的组合。对于浮点数与字符串类型也是如此, 但一个数组不能同时包含整数与浮点数。示例: 对于一个多于两个声道的音频文件,其声道布局(channel layout)需要被特别指明。(对于单声道和双声道的音频文件除非明确指明在特性中指明其声道数,否则按默认处理)。因此声道布局应该用一个枚举数组类 型来存储。每个枚举值代表了一个喇叭位置。与 GST_TYPE_LIST类型不一样的是,数组类型是作为一个整体来看待的。
3).pads性能的用途
pads的功能(Capabilities)(简称 caps)描述了两个pads之间的数据流类型,或者它们所支持的数据流类型。功能主要用于以下用途:
自动填充(Autoplugging): 根据element的功能自动找到可连接的element。所有的自动充填器(autopluggers)都采用的这种方法。
兼容性检测(Compatibility detection): 当两个pads连接时,GStreamer 会验证它们是否采用的同样的数据流格式进行交互。连接并验证两个pads是否兼容的过程叫”功能谈判”(caps negotiation)。
元数据(Metadata): 通过读取pads的功能(capabilities),应用程序能够提供有关当前流经pads的正在播放的媒体类型信息。而这个信息我们叫做元数据(Metadata)。
过滤(Filtering): 应用程序可以通过pads的功能(capabilities)来给两个交互的pads之间的媒体类型加以限制,这些被限制的媒体类型的集合应该是两个交互的pads共同支持的格式集的子集。举例来说:应用程序可以使用"filtered caps"指明两个交互的pads所支持的视频大小(固定或不固定)。你可以往你的管道中插入一个 capsfilter element,并设置其pads的功能(capabilities)属性,从而实现pads的功能(capabilities)的过滤。功能过滤器(caps filters)一般放在一些转换element后面,将数据在特定的位置强制转换成特定的输出格式。这些转换element有: audioconvert、audioresample、ffmpegcolorspace 和 videoscale。
I)使用pads的功能(capabilities)来操作元数据
一个pads能够有多个功能。功能(GstCaps)可以用一个包含一个或多个 GstStructures 的数组来表示。每个GstStructures由一个名字字符串(比如说 "width")和相应的值(类型可能为G_TYPE_INT或GST_TYPE_INT_RANGE)构成。
值得注意的是,这里有三种不同的pads的功能(capabilities)需要区分:pads的可能功能(possible capabilities)(通常是通过对pads模板使用gst-inspect 得到),pads的允许功能(allowed caps)(它是pads模板的功能的子集,具体取决于每对交互pads的可能功能), pads的最后协商功能(lastly negotiated caps)(准确的流或缓存格式,只包含一个结构,以及没有像范围值或列表值这种不定变量)。
你可以通过查询每个功能的结构得到一个pads功能集的所有功能。你可以通过gst_caps_get_structure()得到一个功能的 GstStructure,通过gst_caps_get_size()得到一个GstCaps对象中的GstStructure数量。
简易pads的功能(capabilities)(simple caps )是指仅有一个GstStructure,固定衬垫的功能(capabilities)(fixed caps)指其仅有一个GstStructure,且没有可变的数据类型(像范围或列表等)。另外还有两种特殊的功能 - 任意pads的功能(capabilities)(ANY caps)和空pads的功能(capabilities)(empty caps)。
下面的例子演示了如何从一个固定的视频功能提取出宽度和高度信息:
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);
}
II). 功能(capabilities)应用于过滤器
由于pads的功能(capabilities)常被包含于插件(plugin)中,且用来描述pads支持的媒体类型,所以程序员在为了在插件(plugin)间 进行交互时,尤其是使用过滤功能(filtered caps)时,通常需要对pads功能有着基本的理解。当你使用过滤功能(filtered caps)或固定功能(fixation)时,你就对交互的pads间所允许的媒体类型做了限制,限制其为交互的pads所支持的媒体类型的一个子集。你可以通过 在管道中使用capsfilter element实现上述功能,而为了做这些,你需要创建你自己的GstCaps。这里我们给出最容易的方法是,你可以通过gst_caps_new_simple()函数来创建你自己的GstCaps。
static gboolean
link_elements_with_filter (GstElement *element1, GstElement *element2)
{
gboolean link_ok;
GstCaps *caps;
caps = gst_caps_new_simple ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC (‘I‘, ‘4‘, ‘2‘, ‘0‘),
"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;
}
上述代码会将两个elment间交互的数据限制为特定的视频格式、宽度、高度以及帧率(如果没达到这些限制条件,两个 elemment会连接失败)。请记住:当你使用 gst_element_link_filtered()时,Gstreamer会自动创建一个capsfilter element, 将其加入Bins或管道中, 并插入到你想要交互的两个element间。(当你想要断开两个element的连接时,你需要注意到这一点)。
在某些情况下,当你想要在两个pads间创建一个更精确的带过滤连接的功能集时,你可以用到一个更精简的函数- gst_caps_new_full ():
static gboolean ink_elements_with_filter (GstElement *element1, GstElement *element2)
{
gboolean link_ok;
GstCaps *caps;
caps = gst_caps_new_full (
gst_structure_new ("video/x-raw-yuv",
"width", G_TYPE_INT, 384,
"height", G_TYPE_INT, 288,
"framerate", GST_TYPE_FRACTION, 25, 1,
NULL),
gst_structure_new ("video/x-raw-rgb",
"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;
}
4)精灵 Pads (Ghost pads)
Bins有属于它自己的Pads,这就是”精灵Pads”的由来。
如下图。 没有使用精灵Pads的GstBin element
精灵pads来自于bins中某些element,它同样可以在该Bins中被直接访问。精灵Pads与UNIX文件系统中的符号链接很类似。使用Bins,你可以在你的代码中将Bins当作一个普通element来使用。
如下图. 使用了精灵Pads的GstBin element
最左边element的sink pad同样也是整个Bins的精灵pads。由于精灵pads看起来与其它pads没什么区别,而且与其它pads有着类似的功能。所以它们可以加到任何一种element上,而不仅仅是GstBin。
通过函数gst_ghost_pad_new ()可以创建一个ghost pad :
#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_pad (sink, "sink");
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (GST_OBJECT (pad));
[..]
}
上面的例子中,pads不仅有精灵pads,而且还存在一个带名叫”sink”的sink pad element。 因此这个Bins可以作为那个element的替代者。你可以将其它的element与这个Bins进行连接。