GStreamer快速上手

Deepstream中使用的是gstreamer作为多媒体数据的处理库,其中出现的几个基本概念

  1. GstElement:
    GStreamer中的最重要的对象,其定义了pipeline的结构。Element包含了可以是以下:

    • source
    • filters
    • sinks
    • containers(Bin)

    上面的元素可以是GStreamer本身就有的,亦可能从plugin中重载。通常一个GstElement都是通过GstElementFactory产生的。GstElement命名尽管可以重名,但最好是是唯一的,其对应一个唯一的pipeline。

  2. 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;
}
  1. 初始化

     gst_init (&argc, &argv);
    

    每一个GStreamer应用第一个都必须执行这个语句,其作用是

    • 初始化所有内部结构
    • 检查哪些plugin可用
    • 从传入的命令参数中执行关于GStreamer的相关操作
  2. 构建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则作为了媒体流的来源

  3. 播放状态

     /* Start playing */
     gst_element_set_state (pipeline, GST_STATE_PLAYING);
    

    这一句指出了一个概念:状态(the state)。每一个GStreamer element都有一个相关状态,可以想象为播放器的播放/暂停按键。当执行这一操作时,pipeline就开始从媒体源中取流并执行对应操作。注意,这里可以直接传入pipeline是因为这个是唯一的element

  4. 等待结束或出错

     /* 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的时候,阻塞并返回信息。

  5. 清理
    在退出应用前,通过以下方式释放各个对象

     /* 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

  1. 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
  2. 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中

  3. 修改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构建完成,接下来将会进行实际流操作。

  1. 检查报错

     /* 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的执行情况。

至此,基本介绍完。

猜你喜欢

转载自blog.csdn.net/Johnson_star/article/details/107712244