gstreamer教程(三)-总线(Bus)_衬垫(Pads)_缓冲区(Buffers)_事件(Events)
1、总线(Bus)总线是一个简单的系统,它采用自己的线程机制将一个管道线程的消息分发到一个应用程序当中。总线的优势是:当使用 GStreamer 的时候,应用程序不需要线程识别,即便 GStreamer 已经被加载了多个线程。每一个管道默认包含一个总线,所以应用程序不需要再创建总线。应用程序只需要在总线上设置一个类似于对象的信号处理器的消息处理器。当主循环运行的时候,总线将会轮询这个消息处理器是否
Gstreamer基础知识:
1、总线(Bus)
总线是一个简单的系统,它采用自己的线程机制将一个管道线程的消息分发到一个应用程序当中。总线的优势是:当使用 GStreamer 的时候,应用程序不需要线程识别,即便 GStreamer 已经被加载了多个线程。
每一个管道默认包含一个总线,所以应用程序不需要再创建总线。应用程序只需要在总线上设置一个类似于对象的信号处理器的消息处理器。当主循环运行的时候,总线将会轮询这个消息处理器是否有新的消息,当消息被采集到后,总线将呼叫相应的回调函数来完成任务。
1.1、如何使用一个总线(Bus)
使用总线有两种方法,如下:
• 运行 GLib/Gtk+ 主循环 (你也可以自己运行默认的 GLib 的主循环),然后使用侦听器对总线进行侦听。使用这种方法, GLib 的主循环将轮询总线上是否存在新的消息,当存在新的消息的时候,总线会马上通知你。
在这种情况下,你会用到gst_bus_add_watch () 、gst_bus_add_signal_watch ()两个函数。
当使用总线时,设置消息处理器到管道的总线上可以使用gst_bus_add_watch ()。 来创建一个消息处理器来侦听管道。每当管道发出一个消息到总线,这个消息处理器就会被触发,消息处理器则开始检测消息信号类型(见下章)从而决定哪些事件将被处理。当处理器从总线删除某个消息的时候,其返回值应为TRUE。
• 自己侦听总线消息,使用gst_bus_peek () 和/或 gst_bus_poll () 就可以实现。
#include <gst/gst.h>
static GMainLoop *loop;
static gboolean my_bus_callback (GstBus *bus,
GstMessage *message,
gpointer data)
{
g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));
switch (GST_MESSAGE_TYPE (message))
{
case GST_MESSAGE_ERROR:
{
GError *err;
gchar *debug;
gst_message_parse_error (message, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
g_free (debug);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:
/* end-of-stream */
g_main_loop_quit (loop);
break;
default:
/* unhandled message */
break;
}
/* we want to be notified again the next time there is a message
* on the bus, so returning TRUE (FALSE means we want to stop watching
* for messages on the bus and our callback should not be called again)
*/
return TRUE;
}
gint main (gint argc, gchar *argv[])
{
GstElement *pipeline;
GstBus *bus;
/* init */
gst_init (&argc, &argv);
/* create pipeline, add handler */
pipeline = gst_pipeline_new ("my_pipeline");
/* adds a watch for new message on our pipeline's message bus to
* the default GLib main context, which is the main context that our
* GLib main loop is attached to below
*/
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_watch (bus, my_bus_callback, NULL);
gst_object_unref (bus);
//[..]
/* create a mainloop that runs/iterates the default GLib main context
* (context NULL), in other words: makes the context check if anything
* it watches for has happened. When a message has been posted on the
* bus, the default main context will automatically call our
* my_bus_callback() function to notify us of that message.
* The main loop will be run until someone calls g_main_loop_quit()
*/
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
/* clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_element_unref (pipeline);
gst_main_loop_unref (loop);
return 0;
}
理解消息处理器在主循环的线程 context 被调用是相当重要的,因为在总线上管道和应用程序之间的交互是异步,所以上述方法无法适用于实时情况,比如音频轨道、无间隔播放(理论上的)、视频效果之间的交叉混合。如果需要满足实时要求,实现上述功能,你就需要编写一个 GStreamer 插件来实现在管道中直接触发回调。而对于一些初级的应用来说,使用从管道传递消息给应用程序的方法来实现应用程序与管道的交互,还是非常有用的。这种方法的好处是 GStreamer 内部所有的线程将被应用程序隐藏,而开发人员也不必去担心线程问题。
注意:如果你使用了默认的GLib主循环来实现管道与应用程序的交互,建议你可以将“消息" 信号链接到总线上,而不必在管道上使用侦听器,这样对于所有可能的消息类型,你就不需用switch(), 只要连接到所需要的信号 格式为"message::<type>",其中<Type>是一种消息类型(见下一节对消息类型的详细解释)
上面的代码段也可以这样写:
GstBus *bus;
[..]
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline);
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message::error", G_CALLBACK (cb_message_error),NULL);
g_signal_connect (bus, "message::eos", G_CALLBACK (cb_message_eos),NULL);
[..]
如果你没有使用GLib主循环,默认的消息信号将会无效,然而,你可以导出一个小助手给集成提供你使用的主循环,启动产生总线信号。 (详细见 documentation )https://gstreamer.freedesktop.org/documentation/gstreamer/gstbus.html?gi-language=c
1.2、消息类型(Message types)
GStreamer有几种由总线传递的预定义消息类型,这些消息都是可扩展的。插件可以定义另外的一些消息,应用程序可以有这些消息的绝对代码或者忽略它们。强烈推荐应用程序至少要处理错误消息并直接的反馈给用户。
所有的消息都有一个消息源、类型和时间戳。这个消息源能被用来判断由哪个组件发出消息。例如,在众多的消息中,应用程序只对上层的管道发出的消息感兴趣(如状态变换的提示)。下面列出所有的消息种类、代表的意义,以及如何解析具体消息的内容。
• 错误、警告和消息提示:它们被各个组件用来在必要的时候告知用户现在管道的状态。错误信息表明有致命的错误并且终止数据传送。错误应该被修复,这样才能继续管道的工作。警告并不是致命的,但是暗示有问题存在。消息提示用来告知非错误的信息。这些消息含有一个带有主要的错误类型和消息的 GError,和一个任选的调试字符串。这两项都可以用gst_message_parse_error (), _parse_warning () 以及 _parse_info ()三个函数来提取其信息。当使用完毕后,错误和修正字符串都将被释放。
• 数据流结束(End-of-stream)提示:当数据流结束的时候,该消息被发送。管道的状态不会改变,但是之后的媒体操作将会停止。应用程序可以通过收到这一消息来跳到播放列表的下一首歌。在数据流结束提示出现之后,仍然可以通过向后搜索来回到以前数据流前面的位置。之后的播放工作将会自动的继续执行。这个消息没有特殊的参数。
• 标签(Tags):当元数据在数据流中被找到的时候,此消息被发送。一个管道可以发出多个 Tag(如元数据的描述里有艺术家、歌曲名,另外的例子如流的信息采样率和比特率)。应用程序应该将元数据存储在缓存里。函数gst_message_parse_tag () 被用来解析 tag 的列表,当该列表不再使用的时候,函数 gst_tag_list_free () 释放其相应的 tag。
• 状态转换(State-changes):当状态成功的转换时发送该消息。函数gst_message_parse_state_changed ()可以用来解析转换中的新旧状态。
• 缓冲(Buffering):当缓冲网络数据流时此消息被发送。你可以通过函数gst_message_get_structure ()的返回值,来解析"buffer-percent" 属性,从而手动的得到缓冲进度(该缓冲进度以百分比的形式表示)。
• 组件消息( Element messages):它是一组特殊的消息,用以标识一个特定组件。这样一组特殊的消息通常表述了一些额外的信息。组件的信息应该被详细的描述,因为这样一些组件信息将被作为消息而发送给其它组件。
例如: 'qtdemux' QuickTime 整流器( demuxer)应该把'redirect'信息保存于该组件信息当中,以便在某种特殊情况下将'redirect'组件信息发送出去。
• Application-specific 消息:我们可以将取得的消息结构解析出来,从而得到有关 Application-specific 消息的任何信息。通常这些信息是能够被安全地忽略。
应用程序消息主要用于内部,以备从一些线程排列信息到主线程应用的需求。这些在使用组件信号的应用中非常实用(这些信号在数据流线程的上下文被发射)。
2、衬垫(Pads)及其功能
如我们在Elements一章中看到的那样,衬垫(Pads)是组件对外的接口。数据流从一个组件的源衬垫(source pad)到另一个组件的接收衬垫(sink pad)。衬垫的功能(capabilities)决定了一个组件所能处理的媒体类型。在这章的后续讲解中,我们将对衬垫的功能做更详细的说明。
2.1、衬垫(Pads)
一个衬垫的类型由 2 个特性决定:它的数据导向(direction)以及它的时效性(availability)。正如我们先前提到的,Gstreamer定义了 2 种衬垫的数据导向:源衬垫以及接收衬垫。衬垫的数据导向这个术语是从组件内部的角度给予定义的: 组件通过它们的接收衬垫接收数据,通过它们的源衬垫输出数据。如果通过一张图来形象地表述,接收衬垫画在组件的左侧,而源衬垫画在组件的右侧,数据从左向右流动。
衬垫的时效性比衬垫的数据导向复杂得多。一个衬垫可以拥有三种类型的时效性:永久型(always)、随机型(sometimes)、请求型(on request)。三种时效性的意义顾名思义: 永久型的衬垫一直会存在,随机型的衬垫只在某种特定的条件下才存在(会随机消失的衬垫也属于随机型),请求型的衬垫只在应用程序明确发出请求时才出现。
2.1.1、动态(随机)衬垫
一些组件在其被创建时不会立刻产生所有它将用到的衬垫。例如在一个 Oggdemuxer 的组件中可能发生这种情况。这个组件将会读取 Ogg 流,每当它在 Ogg流中检测到一些元数据流时(例如 vorbis, theora ),它会为每个元数据流创建动态衬垫。同样,它也会在流终止时删除该衬垫。动态衬垫在 demuxer 这种组件中可以起到很大的作用。
运行 gst-inspect oggdemux 只会显示出一个衬垫在组件中: 一个名字叫作'sink'的接收衬垫,其它的衬垫都处于'休眠'中,你可以从衬垫模板(pad template)中的"Exists:Sometimes"的属性看到这些信息。衬垫会根据你所播放的 Ogg 档的类型而产生,认识到这点对于你创建一个动态管道特别重要。当组件通过它的随机型(sometimes)衬垫范本创建了一个随机型(sometimes)的衬垫的时侯,你可以通过对该组件绑定一个信号处理器(signal handler),通过它来得知衬垫被创建。下面一段代码演示了如何这样做:
名叫'sink'的接收衬垫, 其它的衬垫都处于'休眠'中, 显而易见这是衬垫”有时存在”的特性。衬垫会根据你所播放的 Ogg 档的类型而产生,这点在你准备创建一个动态管道时显得特别重要,当组件创建了一个”有时存在”的衬垫时,你可以通过对该组件触发一个信号处理器(signal handler) 来得知衬垫被创建。下面一段代码演示了如何这样做:
#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.1.2、请求衬垫
组件同样可以拥有请求衬垫(request pads)。这种衬垫不是自动被创建,而是根据请求被创建的。这在多任务(multiplexers)类型的组件中有很大的用处。例如aggregators 以及 tee 组件。 Aggregators 组件可以把多个输入流合并成一个输出流;tee 组件正好相反,它只有一个输入流,然后根据请求把数据流发送到不同的输出衬垫。只要应用程序需要另一份数据流,它可以简单的从 tee 组件请求到一个输出衬垫。
下面一段代码演示了怎样在一个"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()方法可以从一个组件中得到一个衬垫,这个衬垫基于衬垫范本的名字(pad template)。同样可以请求一个同其它衬垫模板兼容的衬垫,这点在某些情况下非常重要。比如当你想要将一个组件连接到一个多任务型的组件时,你就需要请求一个带兼容性的衬垫。 gst_element_get_compatible_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);
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);
}
由于衬垫对于一个组件起了非常重要的作用,因此就有了一个术语来描述能够通过衬垫或当前通过衬垫的数据流。这个术语就是功能 (capabilities)。在这里,我们将简要介绍什么是衬垫的功能以及怎么使用它们。这些足以使我们对这个概念有个大致的了解。如果想要对衬垫的功能有更深入的了解,并知道在GStreamer 中定义的所有的衬垫的功能,请参考插件开发手册 Plugin Writers Guide。
2.2、衬垫的功能
衬垫的功能(capabilities)是与衬垫模板(pad templates)以及衬垫实例相关联的。对于衬垫模板,衬垫的功能(capabilities)描述的是:当通过该衬垫范本创建了一个衬垫后,该衬垫允许通过的媒体类型。对于衬垫实例,功能可以描述所有可能通过该衬垫的媒体类型(通常是该衬垫实例所属的衬垫模板的功能的一份拷贝),或者是当前已经通过该衬垫的流媒体类型。前一种情况,该衬垫实例还未有任何数据流在其中通过。
2.2.1、分解功能
衬垫的功能通过GstCaps 对象来进行描述。一个GstCaps对象会包含一个或多个GstStructure。一个 GstStructure描述一种媒体类型。一个被数据流通过的衬垫(negotiated pad)存在功能集(capabilities set),每种功能只包含一个GstStructure结构。结构中只包含固定的值。但上述约束并不对尚未有数据流通过的衬垫(unnegotiated pads)或衬垫范本有效。
下面给出了一个例子,你可以通过运行 gst-inspect vorbisdec 看到"vorbisdec" 组件的一些功能。你可能会看到 2 个衬垫: 源衬垫和接收衬垫, 2 个衬垫的时效性都是永久型,并且每个衬垫都有相应的功能描述。接收衬垫将会接收 vorbis 编码的音频数据,其 mime-type 显示为"audio/x-vorbis"。源衬垫可以将译码后的音频数据采样(raw audio samples)发送给下一个组件,其 mime-type 显示为为"audio/x-raw-int"。源衬垫的功能描述中还包含了一些其它的特性: 音频采样率(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.2.2、特性与值
特性(Properties)用来描述功能中的额外信息(注:除数据流类型之外的信息)。一条特性由一个关键词和一个值组成。下面是一些值的类型:
• 基本类型,几乎涵盖了 Glib 中的所有 GType 类型。这些类型为每条特性(Properties)指明了一个明确,非动态的值。例子如下所示:
o 整型(G_TYPE_INT): 明确的数值(相对范围值)。
o 布尔类型:(G_TYPE_BOOLEAN): TRUE 或 FALSE。
o 浮点类型:(G_TYPE_FLOAT): 明确的浮点数。
o 字符串类型:(G_TYPE_STRING): UTF-8 编码的字符串。
o 分数类型:(GST_TYPE_FRACTION): 由整数做分子分母的分数。
• 范围类型(Range types):由 GStreamer 注册的且属于 GTypes 的一种数据类型。
它指明了一个范围值。范围类型通常被用来指示支持的音频采样率范围或者支持的视频档大小范围。 GStreamer 中又有 2 种不同类型的范围值。
o 整型范围值(GST_TYPE_INT_RANGE): 用最大和最小边界值指明了一个整型数值范围。举例来说: "vorbisdec"组件的采样率范围为8000-50000。
o 浮点范围值(GST_TYPE_FLOAT_RANGE): 用最大和最小边界值指明了一个浮点数值范围。
o 分数范围值(GST_TYPE_FRACTION_RANGE): 用最大和最小边界值指明了一个分数数值范围。
• 列表类型(GST_TYPE_LIST):可以在给定的列表中取任何一个值。
示例:某个衬垫的功能如果想要表示其支持采样率为 44100Hz 以及 48000Hz的数据,它可以用一个包含 44100 和 48000 的列表数据类型。
• 数组类型(GST_TYPE_ARRAY): 一组数据。数组中的每个元素都是特性的全值(full value)。数组中的元素必须是同样的数据类型。这意味着一个数组可以包含任意的整数,整型的列表,整型范围的组合。对于浮点数与字符串类型也是如此,但一个数组不能同时包含整数与浮点数。
示例: 对于一个多于两个声道的音频档,其声道布局(channel layout)需要被特别指明。 (对于单声道和双声道的音频档除非明确指明在特性中指明其声道数,否则按默认处理)。因此声道布局应该用一个枚举数组类型来存储。每个枚举值代表了一个喇叭位置。与 GST_TYPE_LIST 类型不一样的是,数组类型是作为一个整体来看待的。
2.3、衬垫(Pads)性能的用途
衬垫的功能(Capabilities)(简称 caps)描述了两个衬垫之间的数据流类型,或者它们所支持的数据流类型。功能主要用于以下用途:
• 自动填充(Autoplugging): 根据组件的功能自动找到可连接的组件。所有的自动充填器(autopluggers)都采用的这种方法。
• 兼容性检测(Compatibility detection): 当两个个衬垫连接时, GStreamer 会验证它们是否采用的同样的数据流格式进行交互。连接并验证两个衬垫是否兼容的过程叫" 功能谈判"(caps negotiation)。
• 元数据(Metadata): 通过读取衬垫的功能(capabilities),应用程序能够提供有关当前流经衬垫的正在播放的媒体类型信息。而这个信息我们叫做元数据(Metadata)。
• 过滤(Filtering): 应用程序可以通过衬垫的功能(capabilities)来给两个交互的衬垫之间的媒体类型加以限制,这些被限制的媒体类型的集合应该是两个交互的衬垫共同支持的格式集的子集。举例来说:应用程序可以使用"filtered caps"指明两个交互的衬垫所支持的视频大小(固定或不固定)。你可以往你的管道中插入一个 capsfilter组件,并设置其衬垫的功能(capabilities)属性,从而实现衬垫的功能(capabilities)的过滤。功能过滤器(caps filters)一般放在一些转换组件后面,将数据在特定的位置强制转换成特定的输出格式。这些转换组件有: audioconvert、 audioresample、ffmpegcolorspace 和 videoscale。
2.3.1、使用衬垫的功能(capabilities)来操作元数据
一个衬垫能够有多个功能。功能(GstCaps)可以用一个包含一个或多个GstStructures 的数组来表示。每个 GstStructures 由一个名字字符串(比如说 "width")和相应的值(类型可能为 G_TYPE_INT 或 GST_TYPE_INT_RANGE)构成。
值得注意的是,这里有三种不同的衬垫的功能(capabilities)需要区分:衬垫的可能功能(possible capabilities)(通常是通过对衬垫模板使用 gst-inspect 得到),衬垫的允许功能(allowed caps)(它是衬垫模板的功能的子集,具体取决于每对交互衬垫的可能功能),衬垫的最后协商功能(lastly negotiated caps)(准确的流或缓存格式,只包含一个结构,以及没有像范围值或列表值这种不定变量)。
你可以通过查询每个功能的结构得到一个衬垫功能集的所有功能。你可以通过gst_caps_get_structure()得到一个功能的 GstStructure,通过 gst_caps_get_size()得到一个 GstCaps 对象中的 GstStructure 数量。
简易衬垫的功能(capabilities)(simple caps )是指仅有一个 GstStructure,固定衬垫的功能(capabilities)(fixed caps)指其仅有一个 GstStructure,且没有可变的数据类型(像范围或列表等)。另外还有两种特殊的功能 - 任意衬垫的功能(capabilities)(ANYcaps)和空衬垫的功能(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);
}
2.3.2、功能(capabilities)应用于过滤器
由于衬垫的功能(capabilities)常被包含于插件(plugin)中, 且用来描述衬垫支持的媒体类型,所以程序员在为了在插件(plugin)间进行交互时,尤其是使用过滤功能(filtered caps)时,通常需要对衬垫功能有着基本的理解。当你使用过滤功能(filteredcaps)或固定功能(fixation)时,你就对交互的衬垫间所允许的媒体类型做了限制,限制其为交互的衬垫所支持的媒体类型的一个子集。你可以通过在管道中使用capsfilter 组件实现上述功能,而为了做这些,你需要创建你自己的 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;
}
上述代码会将两个组件间交互的数据限制为特定的视频格式、宽度、高度以及帧率(如果没达到这些限制条件,两个组件就会连接失败)。请记住:当你使用gst_element_link_filtered()时, Gstreamer 会自动创建一个 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-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;
}
2.4、精灵衬垫(Ghost pads)
你可以从图 2-1看到,箱柜没有一个属于它自己的衬垫,这就是" 精灵衬垫" 的由来。

图 2-1. 没有使用精灵衬垫的GstBin组件
精灵衬垫来自于箱柜中某些组件,它同样可以在该箱柜中被直接访问。精灵衬垫与 UNIX 文件系统中的符号链接很类似。使用箱柜,你可以在你的代码中将箱柜当作一个普通组件来使用。

图2-2. 使用了精灵衬垫的GstBin组件
图2-2 显示了一个精灵衬垫。最左边组件的接收衬垫同样也是整个箱柜的精灵衬垫。由于精灵衬垫看起来与其它衬垫没什么区别,而且与其它衬垫有着类似的功能。所以它们可以加到任何一种组件上,而不仅仅是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_pad (sink, "sink");
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (GST_OBJECT (pad));
[..]
}
面的例子中,箱柜不仅有精灵衬垫,而且还存在一个带名叫"sink" 的接收衬垫的组件。因此这个箱柜可以作为那个组件的替代者。你可以将其它的组件与这个箱柜进行连接。
3、缓冲区(Buffers)和事件(Events)
管道的数据流由一组缓冲区和事件组成,缓冲区包括实际的管道数据,事件包括控制信息,如寻找信息和流的终止信号。所有这些数据流在运行的时候自动的流过管道。这一章将主要为你阐述这些概念。
3.1、缓冲区(Buffers)
缓冲区包含了你创建的管道里的数据流。通常一个源组件会创建一个新的缓冲区,同时组件还将会把缓冲区的数据传递给下一个组件。当使用 GStreamer 底层构造来创建一个媒体管道的时候,你不需要自己来处理缓冲区,组件将会为你处理这些缓冲区。
一个缓冲区主要由以下一个组成:
• 指向某块内存的指针
• 内存的大小
• 缓冲区的时间戳
• 一个引用计数,指出了缓冲区所使用的组件数。没有组件可引用的时候,这个引用将用于销毁缓冲区。
这里有一个简单的例子,我们先创建了一个缓冲区,然后为这个缓冲区分配内存,然后将数据存放在缓冲区中,并传递至下一个组件。该组件读取数据,处理某些事件(像创建一个新的缓冲区并进行译码),对该缓冲区解引用,这将造成数据空闲,导致缓冲区被销毁。典型的音频和视频译码器就是这样工作的。
尽管如此,还有一些更为复杂的设定,组件会适当的修改缓冲区,也就是说,不会分配一个新的缓冲区。组件也可以写入硬件内存(如视频捕获源)或是使用XShm 从 X-server 分配内存。缓冲区只能读,等等。
3.2、事件(Events)
事件是一系列控制粒子,随着缓冲区被发送到管道的上游和下游。下游事件通知流状态相同的组件,可能的事件包括中断, flush,流的终止信号等等。在应用程序与组件之间的交互以及事件与事件之间的交互中,上游事件被用于改变管道中数据流的状态,如查找。对于应用程序来说,上游事件非常重要,下游事件则是为了说明获取更加完善的数据概念上的图像。由于大多数应用程序以时间为单位查找,下面的例子实现了同样的功能:
static void seek_to_time (GstElement *element, guint64 time_ns)
{
GstEvent *event;
event = gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_TIME, time_ns);
gst_element_send_event (element, event);
}
以上代码主要是说明其具体的工作原理,快捷算法是一个函数gst_element_seek ()。
更多推荐


所有评论(0)