Building an Application
Fundamental knowledge of GStreamer.
这一章节,我们将讨论GStreamer的基础概念和最常用的objects,例如elements,pads和buffers。我们将使用这些objects的可视化表示,以便能够可视化您稍后将学习构建的更复杂的pipeline。你将对GStreamer的API有一个初步印象,足以构建基本的应用。之后你将学着构建一个基础的命令行应用。
Initializing GStreamer
在开发GStreamer应用的过程中,可以简单的只包含gst/gst.h
即可访问大部分库函数,除了某些特殊plugin的定制化API需要单独包含头文件。
此外,为了能够调用GStreamer库,在主程序中必须调用gst_init(&argc, &argv)
函数完成必须的初始化工作以及命令行参数的分析。
Element
GStreamer应用中最重要的对象是GstElement对象,element是多媒体pipeline基本的构建组件,所有的高级组件都集成自GstElement。
Gstreamer中主要有三种elements:sink element,src element,filter-like element,element的类型由其具备哪些pads决定(pad相关内容将在后续小节展开介绍)。



Creating a GstElement
gst_init (&argc, &argv);
GstElement* element;
element = gst_element_factory_make("factory name", "unique element name");
// or
GstElementFactory* factory;
GstElement* element;
factory = gst_element_factory_find ("factory name");
element = gst_element_factory_create (factory, "unique element name");
Using an element as a GObject
一个GstElement能够拥有一些属性,这些属性是使用标准GObject的属性实现的。每个GstElement至少继承了GObject的“name”属性,也即创建GstElement时所传递的“unique element name”。GObject为common属性提供了setter和getter,但更一般的做法是使用g_object_set/get
。
g_object_set(G_OBJECT(element), "propeerty name", &value, NULL);
g_object_get(G_OBJECT(element), "propeerty name", &value, NULL);
在属性的查询上,GStreamer提供了gst-inspect-1.0
工具用于查询指定element的属性和简单描述。
除了属性,通常一个GstElement还提供一些GObject信号以实现灵活的回调机制用于pipeline和application交互。
More about element factories
Element factories是GStreamer注册表中检索的基本单位,他们描述了所有GStreamer能够创建的插件和elements。
从上面创建GstElement对象的过程来看,可以理解为我们从一个GstElementFactory中创建了一个GstElemnt对象,但是我对于GstElementFactory的理解很粗糙,官方文档也没有详细的设计说明。这个GstElementFactory和工厂模式不太一样,在GStreamer中一个plugin就是一个工厂,或许是因为一个plugin可能由多个elements实现。
GstElementFactory最重要的特性就是它拥有其所属于的plugin所支持的pads的完整描述,而不需要将element真正加载进内存。
Link Elements
两个elements的连接,实际是src-pad和sink-pad之间的negotiation,因此连接需要两个pad的caps具备交集以及elements处于同一个GstBin。
gst_element_link();
gst_elemenet_link_many();
gst_element_link_pads();
Element States
GStreamer的elements仅有四种状态,四种状态从NULL<->READY<->PAUSE<->PLAYING必须依次切换,即使越级切换状态成功也是接口内部完成了相关的操作。
GST_STATE_NULL
:默认状态,在这个状态不会申请任何资源,当element的引用计数变为0时必须处于NULL状态。其他状态切换到这个状态会释放掉所有已申请的资源。
GST_STATE_READY
:在这个状态下element会申请相关的全局资源,但不涉及stream数据。简单来说就是NULL->READY仅是打开相关的硬件设备,申请buffer;PLAYING->READY就是把停止读取stream数据。
GST_STATE_PAUSE
:这个状态实际是GStreamer中最常见的一个状态,在这个阶段pipeline打开了stream但并未处理它,例如sink element已经读取到了视频文件的第一帧并准备播放。
GST_STATE_PLAYING
:PLAYING状态和PAUSE状态实际并没有区别,只是PLAYING允许clock 润run。
通常只需要设置bin或者pipeline元素的状态即可自动完成其内含的所有elements的状态切换,但假如动态的向一条处于PLAYING状态的pipeline添加element,则需要手动完成这个element的状态切换。
Bin
GstBin可以将一系列elements组合形成一个逻辑上的element,以便从整体上操控和管理elements。
最外层的bin即使pipeline。
GstBin管理它内部elements的状态。
Bus
GstBus是将stream线程消息转发给应用程序线程的系统。
GstBus本身运行在应用程序的上下文中,但能够自动监听GStreamer内的线程。
每条pipeline都自带一条GstBus,开发人员仅需为其设定handler以便在接收到消息是能或者正确的处理。
#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;
guint bus_watch_id;
/* 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));
bus_watch_id = 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_object_unref (pipeline);
g_source_remove (bus_watch_id);
g_main_loop_unref (loop);
return 0;
}
loop线程(主线程)会定期检查它所监听的消息是否发生。
GstBus的消息监听是异步的,无法处理同步需求。
gst_bus_add_watch()
会处理所有类型的GstBus消息,可以在handler中使用switch语句进行细化,也可以用gst_bus_add_signal_watch(bus)
和g_signal_connect(bus, "message::eos", G_CALLBACK(cb_message_eos), NULL)
来为特定类型的消息创建一个handler。
Pads and Capabilities
Pad是一个element与外部交互的接口,数据从一个element的src-pad传递给另一个element的sink-pad。Pad的Capabilities表明element能处理的数据。
Dynamic(or sometimes) pads
某些elements在创建时并不会带有pads,例如demuxer,demuxer在创建时并不带有sink-pad,直到pipeline处于PAUSE状态读取到了stream的足够信息。demuxer将解析读取的stream中的所有基本流(Video,Audio)数据并为它们分别创建一个能处理对应流数据类型的cap的pad。
pad-added
:对于具有sometimes pad的element,它将在创建一个新的pad的时候发出一个signal,用户需要对这个信号做一定的处理以便能正常的连接pipeline中的elemnet。
static void cb_new_pad(GstElement* element, GstPad* pad, gpointer data);
g_signal_connect(demux, "pad-added", G_CALLBACK(cb_new_pad), NULL);
Request pads
request pads是根据请求才创建的pad,广泛应用于muxer,aggregator,tee中,大多数情况下用户不需要处理request pads。
gst_element_request_pad_simple(tee, "src_%d");
gst_element_get_compatiable_pad(mux, tolink_pad, NULL);
Capabilities of a pad
Capabilities是用于描述一个pad能够处理或正在处理的数据类型的机制。GStreamer使用GstCaps描述pads的capabilities,一个GstCaps将含有一个或多个GStructure来描述媒体类型,但对于已经完成negotiation的pad,其GstCaps的GStructure是唯一的,并且属性值是固定的。
What capabilities are used for
Autoplugging:基于pad的的caps自动查找能与其link的element;
Compatibility detection:为pad negotiation提供支持;
Metadata:读取pad的caps,可以或缺当前正在播放的流 的信息;
Filtering:用于限制两个pad之间支持的流类型,常被置于convert elements之后,用于指定上一个与其连接的elements的输出数据格式。
Using capabilities for metadata
const GStructure* structure;
structure = gst_caps_get_structure(caps, 0);
gst_structure_get_int(structure, "width", &width);
Creating capabilities of filtering
GstCaps* caps;
gboolean link_ok;
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);
// or
GstElement* capfilter;
capfilter = gst_element_factory_make("capfilter", "capfilter0");
g_object_set(G_OBJECT(capfilter), "caps", caps, NULL);
gst_bin_add_many(GST_BIN(pipeline), capfilter, NULL);
gst_element_link_many(elem1, capfilter, elem2, NULL);
gst_element_link_filtered()
内部会自动根据caps创建一个capfilter element并将其插在两个待链接的元素之间。
Last updated
Was this helpful?