Deepstream中使用的是gstreamer作为多媒体数据的处理库,其中出现的几个基本概念
-
GstElement:
GStreamer中的最重要的对象,其定义了pipeline的结构。Element包含了可以是以下:- source
- filters
- sinks
- containers(Bin)
上面的元素可以是GStreamer本身就有的,亦可能从plugin中重载。通常一个GstElement都是通过GstElementFactory产生的。GstElement命名尽管可以重名,但最好是是唯一的,其对应一个唯一的pipeline。
-
GstPad
GstPad是给定的GstElement的成员,其可以让图中的抽象结构具有连接的能力。每一个GstElement(除了source和sink)都至少拥有2个GstPad,并会以GList的形式储存在其对应的GstElement中。Pad使得element有了让信息传入和传出elements的能力GstPad通过_add_pad()函数添加到Element中,通过_get_static_pad()来取值对应名字的pad,这也就意味着同一个Element的每一个Pad命名是唯一的。如果想存储获取所有的Pad的GList,可以通过_iterate_pads()获取其头指针。
可以通过gst_element_add_pad(element,pads)函数将某个GstPad设置为某个GstElemt的pad。成功操作会发送带此pad作为参数的new_pad信号。如果pad、element为NULL或者这个pad已经有主了,那么就会操作失败
pad = gst_element_get_pad(element, “padname”)可以搜索element中特定名字的pad
我们打开一个文件作为容器(理解为),demuxer这一element会负责打开。 如果一个容器要分到多个数据流时,demuxer会将其分离并通过端口暴露给接下来的elements,这样就可以建立不同的数据分支。而element之间的沟通port其实就是指的是GstPad,因此有对于某个element来说,作为输入的sink pad和作为输出的src pad(对,不要看错,输入时sink pad,输出时src pad),而source element只有src pad,sink element只有sink pad。
一个demuxer有一个sink pad,多个src pad。
GStreamer中的基本流程
Hello World例程:
#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
("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/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;
}
-
初始化
gst_init (&argc, &argv);
每一个GStreamer应用第一个都必须执行这个语句,其作用是
- 初始化所有内部结构
- 检查哪些plugin可用
- 从传入的命令参数中执行关于GStreamer的相关操作
-
构建pipeline
pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
由于GStreamer是一个为多个媒体流的框架,媒体从source element(源)到sink element(槽),其中会经过多个element进行不同的操作,这些element串联起来就是一个完整的pipeline。
一般这些elements之间的连接都是由使用者自己定义的,但是如果pipeline非常简单的时候,可以用上述的gst_parse_launch进行操作。gst_parse_launch输入一个文字表述,然后自动生成pipeline。
其中在这里,这一文字表述为(“playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm”, NULL).playbin
是一种特殊的element,其可以同时作为source和sink,其本身就是完整的pipeline了。而其中url则作为了媒体流的来源 -
播放状态
/* Start playing */ gst_element_set_state (pipeline, GST_STATE_PLAYING);
这一句指出了一个概念:状态(the state)。每一个GStreamer element都有一个相关状态,可以想象为播放器的播放/暂停按键。当执行这一操作时,pipeline就开始从媒体源中取流并执行对应操作。注意,这里可以直接传入pipeline是因为这个是唯一的element
-
等待结束或出错
/* 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);
这句语句作用是等待错误发生或者发现了一个EOS(end of stream)。 gst_element_get_bus()用于获取pipeline的bus,而gst_bus_timed_pop_filtered()则是当通过bus获取到一个ERROR或者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);
一个更有用的Hello World例程:
#include <gst/gst.h>
int main(int argc, char *argv[]) {
GstElement *pipeline, *source, *sink;
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret;
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");
if (!pipeline || !source || !sink) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
/* 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;
}
/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);
/* Start playing */
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;
}
/* 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);
/* Parse message */
if (msg != NULL) {
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
gst_message_parse_error (msg, &err, &debug_info);
g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error (&err);
g_free (debug_info);
break;
case GST_MESSAGE_EOS:
g_print ("End-Of-Stream reached.\n");
break;
default:
/* We should not reach here because we only asked for ERRORs and EOS */
g_printerr ("Unexpected message received.\n");
break;
}
gst_message_unref (msg);
}
/* Free resources */
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
return 0;
}
先说大概的流程,整个pipeline是这样构成的:
source---->filter---->sink
这里我们跳过GStreamer的初始化,直接上前面一个Hello World没有的内容:
https://gstreamer.freedesktop.org/documentation/tutorials/basic/concepts.html?gi-language=c
-
Element的创建
/* Create the elements */ source = gst_element_factory_make ("videotestsrc", "source"); sink = gst_element_factory_make ("autovideosink", "sink");
最开始介绍中提到,要通过gst_element_factory_make()创建GstElement,其中第一个参数是声明element的类型,第二个参数是创造的element的名字,后续可以通过此名字来获取element(也可以保存其指针进行获取),如果你传NULL,那么GStreamer会帮你取一个唯一的名字。
对于第一个参数中,element的类型,一下有几个常用的element类型:- Bin类:
- playbin
- uridecodebin
- decodebin
- 文件输入输出类:
- filesrc
- filesink
- 网络类:
- souphttpsrc
- 媒体生成测试类(在不需要媒体情况下测试pipeline是否正常工作):
- videotestsrc
- audiotestsrc
- 视频转换类:
- videoconvert
- videorate
- videoscale
- 音频转化类:
- audioconvert
- audioresample
- audiorate
- 多线程类:
- queue
- queue2
- multiqueue
- tee
- 测试类:
- fakesink
- identity
- Bin类:
-
pipeline的创建
/* Create the empty pipeline */ pipeline = gst_pipeline_new ("test-pipeline");
element必须要在pipeline里面才能被使用,通过函数gst_pipeline_new()创建一个空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; }
pipeline本身是一种特殊的Bin,是一种用于容纳其他element的element,所以理论上所有适用于bin的方法都可以在pipeline中使用。在这里gst_bin_add_many()就是这样一个函数,用于给pipeline添加多个elements。该函数接受一个list的elements,并以NULL结尾。如果想只将单个element加入到pipeline,使用gst_bin_add()。
注意:在这里加入到pipeline的element彼此之间没有联系
我们通过gst_element_link()来进行elements之间的连接,第一个参数为源,第二个为目,需要注意根据数据流向传入顺序。
注意:在同一个pipeline内的elements才能进行彼此连接操作,所以记得在连接element之前将其添加到同一个pipeline中 -
修改element属性
/* Modify the source's properties */ g_object_set (source, "pattern", 0, NULL);
多数的element都有可以修改的属性,可以通过修改来达到element进行不同的操作。
element的属性读取g_object_get(),属性设定g_object_set(),其中g_object_set()可以一次性接受多个element名字,来达到一次修改多个。(所有的element的父类为GObject,所以为什么会有g_的前缀。)
至此整个pipeline构建完成,接下来将会进行实际流操作。
-
检查报错
/* Start playing */ 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; }
使用gst_element_set_state()设定开始播放,并使用其返回值检查是否正确操作开始。
/* 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); /* Parse message */ if (msg != NULL) { GError *err; gchar *debug_info; switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ERROR: gst_message_parse_error (msg, &err, &debug_info); g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message); g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none"); g_clear_error (&err); g_free (debug_info); break; case GST_MESSAGE_EOS: g_print ("End-Of-Stream reached.\n"); break; default: /* We should not reach here because we only asked for ERRORs and EOS */ g_printerr ("Unexpected message received.\n"); break; } gst_message_unref (msg); }
此处前两步操作类似前一个例程,这里仔细说明GstBus *bus:
GStreamer中的bus负责按顺序传递应用中的elements生成的GstMessage,传递到应用线程(这一点很重要,因为实际的媒体串流是会在其他线程中被执行)。GstMessage可以通过gst_bus_timed_pop_filtered()同步地从pipeline执行线程中被提取,可以通过bus来监控整个pipeline的执行情况。
至此,基本介绍完。