1、基本概念:元件GstElement、箱柜GstBin(管道GstPipeline)、衬垫GstPad
2、消息包括:错误消息(error messages),标签消息(tag messages),EOS
消息(EOS messages)
3、管道(pipeline)是高级的箱柜(Bins)
4、一旦开始,管道将在一个
单独的线程
中运行,直到被停止或者数据流播
放完毕。
5、
对于大部分情况,所有的数据流都是在链接好的元素之间流动。数据在这里代表的是缓冲区(GstBuffer)和事件
(GstEvent)。
6、
当你准备写一个GStreamer
应用程序时,你仅需要通过包含头文件
gst/gst.h
来访问库函数。除此之外,不要忘记初始化
GStreamer
库
。
7、
gst_init (&argc, &argv); gstreamer初始化,
GOption来初始化你的参数。
8、
gst_element_factory_make
创建元件,
gst_object_unref
释放元件 ,其中除了创建,
GstElementFactory查询
插件
和衬垫
详细信息,
gst_registry_pool_feature_list
得到所有在GStreamer中注册过的工厂元件。
gst_element_factory_make ("fakesrc", "source");
等价于
GstElementFactory *factory = gst_element_factory_find ("fakesrc"); //fakesrc名称查找元件工厂
gst_element_factory_create (factory, "source");//创建元件并命名source
gst_object_unref (GST_OBJECT (element));
//插件和衬垫详细信息,查询衬垫所支持的媒体类型从而将合适插件加载
g_print ("The '%s' element is a member of the category %s.\n Description: %s\n",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
gst_element_factory_get_klass (factory),
gst_element_factory_get_description (factory));
//获取所有注册元件
GstElementFactoryList *list = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
9、
gst_object_set
和
gst_object_get
元件属性 ----
gst-inspect工具查询
g_object_get (G_OBJECT (element), "name", &name, NULL);
10、
GstElement 对象同样提供了许多的
GObject
信号方法来实现一个灵活的回调机制。你同样可以使用gst-inspect 来检查一个特定元件所支持的信号。总之,
信号和属性是元件与应用程序交互的最基本的方式。
11、
在链接不同的元件之前,你需要确保这些元件都被加在
同一个箱柜
中,因为将一个元件
加载
到一个箱柜中会破坏该元件已存在的一些链接关系
。同时,你
不能直接链接
不在同一箱柜
或管道中的
元件
。如果你想要连接处于
不同层次中的元件或衬垫
,你将使用到
精灵衬垫
。
12、
元件状态
gst_element_set_state
GST_STATE_NULL: 默认状态。该状态将会回收所有被该元件占用的资源。
GST_STATE_READY: 准备状态。元件会得到所有所需的全局资源,这些全局资源将被通过该元件的数据流所使用。例如打开设备、分配缓存等。但在这种状态下,数据流仍未开始被处理,所以数据流的位置信息应该自动置0。如果数据流先前被打开过,它应该被关闭,并且其位置信息、特性信息应该被重新置为初始状态。
GST_STATE_PAUSED: 在这种状态下,元件已经对流开始了处理,但此刻暂停了处理。因此该状态下元件可以修改流的位置信息,读取或者处理流数据,以及一旦状态变为PLAYING,流可以重放数据流。这种情况下,时钟是禁止运行的。总之, PAUSED 状态除了不能运行时钟外,其它与PLAYING 状态一模一样。处于PAUSED 状态的元件会很快变换到PLAYING 状态。举例来说,视频或音频输出元件会等待数据的到来并将它们压入队列。一旦状态改变,元件就会处理接收到的数据。同样,视频接收元件能够播放数据的第一帧。(因为这并不会影响时钟)。自动加载器(Autopluggers)可以对已经加载进管道的插件进行这种状态转换其它更多的像codecs或者filters 这种元件不需要在这个状态上做任何事情。
GST_STATE_PLAYING: PLAYING 状态除了当前运行时钟外,其它与PAUSED 状态一模一样。你可以通过函数gst_element_set_state()来改变一个元件的状态。你如果显式地改变一个元件的状态,GStreamer 可能会使它在内部经过一些中间状态。例如你将一个元件从NULL 状态设置为PLAYING 状态,GStreamer 在其内部会使得元件经历过READY 以及PAUSED 状态。当处于GST_STATE_PLAYING 状态,管道会自动处理数据。它们不需要任何形式的迭代。
13、GStreamer 同样可以使用
GstBus 在管道线程和应用程序现成间交互信息。
14、
箱柜是一种容器元件。
当你在构建一个复杂的管道时,你会发现箱柜的巨大优势,因为它允许你将复杂的管
道分解成一些小块。
它会
计算数据怎样流入箱柜
,并对流入的数据流制定一
个最佳的计划(
generate an optimal plan
)。
计划制定
(
Plan generation
)是
GStreamer
中最复杂的步骤之
一。
顶层(
toplevel
)箱柜必须
为一个管道。
因此每个
GStreamer
应用程序都至少需要一个管道
。当应用程序启动后,管道会自动运
行在后台线程中。
15、
gst_bin_new和
gst_pipeline_new 创建箱柜和管道 ,
gst_bin_add_many 和
gst_bin_add 添加多个元件和单个元件,
gst_element_link
链接元件,
gst_bin_get_list
得到一个箱柜中所有元件
的一个列表
pipeline = gst_pipeline_new("my_pipeline");
bin = gst_bin_new("my_bin");
source = gst_element_factory_make ("fakesrc", "source");
sink = gst_element_factory_make ("fakesink", "sink");
gst_bin_add_many (GST_BIN (bin), source, sink, NULL);
gst_bin_add (GST_BIN (pipeline), bin);
gst_element_link (source, sink);
16、
oggvorbisplayer
,
playbin
与
decodebin
元件都是自定义箱柜
player = gst_element_factory_make ("oggvorbisplayer", "player");
g_object_set (player, "location", "helloworld.ogg", NULL);
gst_element_set_state (GST_ELEMENT (player), GST_STATE_PLAYING);
17、
每一个管道默认包含一个总线,所以应用程序不需要再创建总线。应用程序只需要在总线上设置一个类似于对象的信号处理器的消息处理器。
1、运行GLib/Gtk+ 主循环gst_bus_add_watch 和 gst_bus_add_signal_watch
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_watch (bus, my_bus_callback, NULL);
gst_object_unref (bus);
...
g_main_loop_run (loop);
2、自己侦听总线消息,使用gst_bus_peek 或 gst_bus_poll就可以实现。
18、
理解消息处理器在主循环的线程
被调用
是相当重要的
,因为在总线上管道和应用程序之间
的交互是异步,所以上述方法
无法适用于实时情况
,比如音频轨道、无间隔播放(理论上的)、视频效
果之间的交叉混合。如果需要满足实时要求,实现上述功能,你就需要编写一个
GStreamer
插件来实
现在管道中直接
触发回调
。而对于一些初级的应用来说,使用从管道传递消息给应用程序的方法来实
现应用程序与管道的交互,还是非常有用的。这种方法的好处是
GStreamer
内部所有的线程将被应用
程序隐藏,而开发人员也不必去担心线程问题。
注意:如果你使用了默认的
GLib
主循环来实现管道与应用程序的交互,建议你可以将
“
消息
”
信号
链接到总线上,而不必在管道上使用侦听器,这样对于所有可能的消息类型,你就不需用
switch()
,只
要连接到所需要的信号格式为
"message::<type>"
,其中
<Type>
是一种消息类型。
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);
19、
所有的消息
GstMessage
都有一个消息源(
判断由哪个元件发出消息
)、
GstMessageType
类型和时间戳。
在众多的消息中,应用程序只对上层的管道发出的消息感兴趣。
1、消息类
//public
GstMessageType type;
guint64 timestamp;
GstObject *src;
guint32 seqnum;
2、常用的消息类型
错误、警告和消息提示(GST_MESSAGE_ERROR,GST_MESSAGE_WARNING,GST_MESSAGE_INFO):它们被各个元件用来在必要的时候告知用户现在管道的状态。错误信息表明有致命的错误并且终止数据传送。错误应该被修复,这样才能继续管道的工作。警告并不是致命的,但是暗示有问题存在。消息提示用来告知非错误的信息。这些消息含有一个带有主要的错误类型, 消息的GError和一个任选的调试字符串。这两项都可以用 gst_message_parse_error , gst_message_parse_warning 以及 gst_message_parse_info 三个函数来提取其信息。当使用完毕后,错误和修正字符串都将被释放。
数据流结束(End-of-stream)提示(GST_MESSAGE_EOS):当数据流结束的时候,该消息被发送。管道的状态不会改变,但是之后的媒体操作将会停止。应用程序可以通过收到这一消息来跳到播放列表的下一首歌。在数据流结束提示出现之后,仍然可以通过向后搜索来回到以前数据流前面的位置。之后的播放工作将会自动的继续执行。这个消息没有特殊的参数。
标签(Tags)提示(GST_MESSAGE_TAG):当元数据在数据流中被找到的时候,此消息被发送。一个管道可以发出多个Tag(如元数据的描述里有艺术家、歌曲名,另外的例子如流的信息采样率和比特率)。应用程序应该将元数据存储在缓存里。函数 gst_message_parse_tag 被用来解析tag 的列表,当该列表不再使用的时候,函数 gst_tag_list_free 释放其相应的tag。
状态转换(State-changes)提示(GST_MESSAGE_STATE_CHANGED):当状态成功的转换时发送该消息。函数gst_message_parse_state_changed 可以用来解析转换中的新旧状态。
缓冲(Buffering)提示(GST_MESSAGE_BUFFERING,GST_MESSAGE_STRUCTURE_CHANGE):当缓冲网络数据流时此消息被发送。你可以通过函数gst_message_get_structure 的返回值,来解析"buffer-percent" 属性,从而手动的得到缓冲进度(该缓冲进度以百分比的形式表示)。
元件消息(Element messages)提示(GST_MESSAGE_ELEMENT):它是一组特殊的消息,用以标识一个特定元件。这样一组特殊的消息通常表述了一些额外的信息。元件的信息应该被详细的描述,因为这样一些元件信息将被作为消息而发送给其他元件。例如:'qtdemux' QuickTime 整流器(demuxer)应该把'redirect'信息保存于该元件信息当中,以便在某种特殊情况下将'redirect'元件信息发送出去。
Application-specific 消息(GST_MESSAGE_APPLICATION):我们可以将取得的消息结构解析出来,从而得到有关Application-specific 消息的任何信息。通常这些信息是能够被安全地忽略。
20、
衬垫
(GstPad)
是元件对外的接口。
数据流从一个元件的源衬
垫
(source pad)
到另一个元件的接收衬垫
(sink pad)
。
衬垫的功能
(
GstCaps
)
决定了一个元件所能处理的
媒体类型。
1、数据导向 (direction): 源衬垫(Source) 和 接收衬垫(Sink)
2、时效性(Availability)
永久型(always): 一直存在
随机型(sometimes):数据决定,常用于解复用器
请求型(on request):消息决定,常用于复用器(GstAggregator/GstTee),通过gst_element_get_request_pad获取,请求一个兼容性的衬垫gst_element_get_compatible_pad
//创建衬垫
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);
21、
可以通过对该元
件绑定一个信号处理器
g_signal_connect
,通过它来得知衬垫被创建。
//绑定
demux = gst_element_factory_make ("oggdemux", "demuxer");
g_signal_connect (demux, "pad-added", G_CALLBACK (cb_new_pad), NULL);
//回调
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);
}
22、gst_bus_timed_pop_filtered
()会阻塞直到你遇到一个错误或者流播放结束。
#include <gst/gst.h>
int main(int argc, char *argv[]) {
GstElement *pipeline;
GstBus *bus;
GstMessage *msg;
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Build the pipeline */
pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer- 480p.webm", NULL);
/* Start playing */
gst_element_set_state (pipeline, GST_STATE_PLAYING);
/* Wait until error or EOS 会阻塞直到你遇到一个错误或者流播放结束*/
bus = gst_element_get_bus (pipeline);
msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
/* Free resources */
if (msg != NULL)
gst_message_unref (msg);
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
return 0;
}
23、
使用GStreamer时你常常需要使用独立的elements来手动搭建一个pipeline,但是,在比较简单的情况下,我们也可以使用gst_parse_launch()。这个函数原本是描述一个pipeline的,但也可以很方便的用来建立一个pipeline。
24、修改GstPipeline是否成功
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (pipeline);
return -1;
}
25、
记住只有在同一个bin里面的element才能连接起来,所以一定要把
element在连接之前加入到pipeline
中。
/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
if (gst_element_link (source, sink) != TRUE) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
26、
g_object_set()方法接受一个用NULL结束的属性名称/属性值的组成的对,所以可以一次同时修改多项属性。
g_object_set (source, "pattern", 0, NULL);
g_object_set (visual, "shader", 0, "style", 3, NULL);
27、GstBus分类消息回调
bus = gst_element_get_bus (data.playbin2);
gst_bus_add_signal_watch (bus);
g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, &data);
g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback)eos_cb, &data);
g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, &data);
g_signal_connect (G_OBJECT (bus), "message::application", (GCallback)application_cb, &data);
gst_object_unref (bus);
28、
在PLAYING或PAUSED状态下去获得pad需要注意(Pad阻塞,本教程没有讲到这点),在NULL和READY状态去获得pad就没有这个问题。
29、GstPad三种状态处理: Always , Sometime和 Request
Always :
GstElement内部创建
,代码类似Request
Sometime :
pad-added
回调创建
g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);
...
static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data)
{
GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink");
GstPadLinkReturn ret;
GstCaps *new_pad_caps = NULL;
GstStructure *new_pad_struct = NULL;
const gchar *new_pad_type = NULL;
g_print("Received new pad '%s' from '%s':\n",GST_PAD_NAME(new_pad),GST_ELEMENT_NAME(src));
/*sinkpad是否已经连接*/
if (gst_pad_is_linked (sink_pad)) {
g_print (" We are already linked. Ignoring.\n");
goto exit;
}
/*检查创建sometime pad的类型,检查是否为音频*/
new_pad_caps = gst_pad_get_caps (new_pad);
new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
new_pad_type = gst_structure_get_name (new_pad_struct);
if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
g_print (" It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
goto exit;
}
/*尝试连接*/
ret = gst_pad_link (new_pad, sink_pad);
if (GST_PAD_LINK_FAILED (ret)) {
g_print (" Type is '%s' but link failed.\n", new_pad_type);
} else {
g_print (" Link succeeded (type '%s').\n", new_pad_type);
}
exit:
/* Unreference the new pad's caps, if we got them */
if (new_pad_caps != NULL)
gst_caps_unref (new_pad_caps);
/* Unreference the sink pad */
gst_object_unref (sink_pad);
}
Request:
手动创建及连接
/*手动连接Request Gstpad*/
GstPadTemplate *tee_src_pad_template=gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS (tee), "src%d");
tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
g_print ("Obtained request pad %s for audio branch.\n", gst_pad_get_name (tee_audio_pad));
queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink”);
tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
g_print ("Obtained request pad %s for video branch.\n", gst_pad_get_name (tee_video_pad));
queue_video_pad = gst_element_get_static_pad (video_queue, "sink");
if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {
g_printerr ("Tee could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
gst_object_unref (queue_audio_pad);
gst_object_unref (queue_video_pad);
注:以上代码实现的功能gst-launch-1.0 audiotestsrc ! tee name=t ! queue ! audioconvert ! audioresample ! autoaudiosink t. ! queue ! wavescope ! ffmpegcolorspace ! autovideosink
30、
实时流是不能暂停的,所以在PAUSED状态的行为和PLAYING状态是一样的。在设置实时流到PAUSED成功后,会返回
GST_STATE_CHANGE_NO_PREROLL
,而不是平常的GST_STATE_CHANGE_SUCCESS。
因为状态的变化是渐变的
(从NULL到READY,从PAUSED到PLAYING),所以我们把pipeline设置到PLAYING状态,也会收到NO_PROROLL这个返回值。
|
GStreamer学习四(《GStreamer应用手册》笔记 )
最新推荐文章于 2024-07-31 18:30:05 发布