GStreamer基础教程02 - 基本概念

摘要

Gstreamer基础教程01 - Hello World中,咱们介绍了如何快速的经过一个字符串建立一个简单的pipeline。为了可以更好的控制pipline中的element,咱们须要单首创建element,而后再构造pipeline,下面将介绍GStreamer的一些基本概念并展现pipeline的另外一种构造方式。html

 

基本概念

Element

咱们知道element是构建GStreamer pipeline的基础,element在框架中的类型为GstElement,全部GStreamer提供的解码器(decoder),编码器(encoder), 分离器(demuxer), 音视频输出设备都是派生自GstElement。网络

那么element究竟是什么呢?
从应用的角度,咱们能够将一个element认为是一个功能块,他实现一个特定的功能,好比:数据读取,音频解码,声音输出等。各个功能块之间能够经过某种特定的数据接口(这种接口称为pad,将在后续章节讲述)进行数据传输。每一个element有惟一的类型,还有相应的属性,用于控制element的行为。多线程

 

为了更直观的展示element,咱们一般用一个框来表示一个element,在element内部使用小框表示pad。app


这些功能块有些能够生成数据,有些只接收数据,有些先接收数据,再生成数据。为了便于区分这些element,咱们把他们分为三类:
1. source element
只能生成数据,不能接收数据的element称为source element。例如用于文件读取的filesrc等。
对于source element,咱们一般用src pad表示element能产生数据,并将其放在element的右边。source element只有src pad,经过设备、文件、网络等方式读取数据后,经过src pad向pipeline发送数据,开始pipeline的处理流程。以下图:框架

2. sink element
只能接收数据,不能产生数据的element称为sink element。例如播放声音的alsasink等。
对于sink element,咱们一般用sink pad表示element能接收处理数据,并将其放在element的左边。sink element只有sink pad,从sink pad读取数据后,将数据发送到指定设备或位置,结束pipeline的处理流程。以下图:ide

3. filter-like element
既能接收数据,又能生成数据的element称为filter-like element。例如分离器,解码器,音量控制器等。
对于filter-like element,既包含位于element左边的sink pad,又包含位于element右边的src pad。Element首先从sink pad读取数据,而后对数据进行处理,最后在src pad产生新的数据。以下图:函数

对于这些的element,可能包含多个src pad,也可能包含多个sink pad,例如mp4的demuxer(qtdemux)会将mp4文件中的音频和视频的分离到audio src pad和video src pad。而mp4的muxer(mp4mux)则相反,会将audio sink pad和video sink pad的数据合并到一个src pad,再经其余element将数据写入文件或发送到网络。demuxer以下图:源码分析

 

链接element
当咱们有不少element时,咱们须要将他们按数据的传输路径将其串联起来,src pad只能联接到sink pad,这样才可以实现相应的功能。测试

 

 

 

Bin和Pipeline

咱们将element串联起来后就能实现相应的功能,为何咱们还须要bin和pipline呢?咱们首先来看看在GStreamer框架中element,bin,pipeline对象之间的继承关系:ui

GObject
    ╰──GInitiallyUnowned
        ╰──GstObject
            ╰──GstElement
                ╰──GstBin
                    ╰──GstPipeline

 

这里bin和pipeline都是一个element,那么bin和pipeline都在element的基础上实现了什么功能,解决了什么问题呢?
咱们在建立了element多个element后,咱们须要对element进行状态/资源管理,若是每次状态改变时,都须要依次去操做每一个element,这样每次编写一个应用都会有大量的重复工做,这时就有了bin。
Bin继承自element后,实现了容器的功能,能够将多个element添加到bin,当操做bin时,bin会将相应的操做转发到内部全部的element中,咱们能够将bin认为认为是一个新的逻辑element,由bin来管理其内部element的状态及资源,同事转发其产生的消息。常见的bin有decodebin,autovideoconvert等。

 

Bin实现了容器的功能,那pipeline又有什么功能呢?
在多媒体应用中,音视频同步是一个基本的功能,须要支持这样的功能,全部的element必需要有一个相同的时钟,这样才能保证各个音频和视频在同一时间输出。pipeline就会为其内部全部的element选择一个相同的时钟,同时还为应用提供了bus系统,用于消息的接收。

 

Bus

刚才咱们提到pipeline会提供一个bus,这个pipeline上全部的element均可以使用这个bus向应用程序发送消息。Bus主要是为了解决多线程之间消息处理的问题。因为GStreamer内部可能会建立多个线程,若是没有bus,应用程序可能同时收到从多个线程的消息,若是应用程序在发送线程中经过回调去处理消息,应用程序有可能阻塞播放线程,形成播放卡顿,死锁等其余问题。为了解决这类问题,GStreamer一般是将多个线程的消息发送到bus系统,由应用程序从bus中取出消息,而后进行处理。Bus在这里扮演了消息队列的角色,经过bus解耦了GStreamer框架和应用程序对消息的处理,下降了应用程序的复杂度。

 

Element Hello World

在有上面的知识后,咱们经过一个示例来看看element是如何建立及使用的。

#include <gst/gst.h>

int main (int argc, char *argv[]) { GstElement *pipeline, *source, *filter, *sink; GstBus *bus; GstMessage *msg; GstStateChangeReturn ret; /* Initialize GStreamer */ gst_init (&argc, &argv); /* Create the elements */ source = gst_element_factory_make ("videotestsrc", "source"); filter = gst_element_factory_make ("timeoverlay", "filter"); sink = gst_element_factory_make ("autovideosink", "sink"); /* Create the empty pipeline */ pipeline = gst_pipeline_new ("test-pipeline"); if (!pipeline || !source || !filter || !sink) { g_printerr ("Not all elements could be created.\n"); return -1; } /* Build the pipeline */ gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL); if (gst_element_link_many (source, filter, sink, NULL) != 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; }

编译并运行示例,能够看到弹出的窗口中播放着测试视频,而且还显示着播放时间。

$ gcc basic-tutorial-2.c -o basic-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0`
$ ./basic-tutorial-2

 

源码分析

建立Element

/* Create the elements */ source = gst_element_factory_make ("videotestsrc", "source"); filter = gst_element_factory_make ("timeoverlay", "filter"); sink = gst_element_factory_make ("autovideosink", "sink");

在对GStreamer进行初始化后,咱们能够经过gst_element_factory_make建立element。第一个参数是element的类型,能够经过这个字符串,找到对应的类型,从而建立element对象。第二个参数指定了建立element的名字,当咱们没有保存建立element的对象指针时,咱们能够经过gst_bin_get_by_name从pipeline中取得该element的对象指针。若是第二个参数为NULL,则GStreamer内部会为该element自动生成一个惟一的名字。
咱们在当前示例中建立了3个element:videotestsrc,timeoverlay,autovideosink,其做用分别为:

  • videotestsrc是一个source element,用于产生视频数据,一般用于调试。
  • timeoverlay是一个filter-like element,能够在视频数据中叠加一个时间字符串。
  • autovideosink上一个sink element,用于自动选择视频输出设备,建立视频显示窗口,并显示其收到的数据。

 

建立Pipeline

 /* Create the empty pipeline */ pipeline = gst_pipeline_new ("test-pipeline");

Pipeline经过gst_pipeline_new建立,参数为pipeline的名字。


咱们知道pipeline会提供播放所必须的时钟以及对消息的处理,因此咱们须要把咱们建立的element添加到pipeline中。

/* Build the pipeline */ gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL); if (gst_element_link_many (source, filter, sink, NULL) != TRUE) { g_printerr ("Elements could not be linked.\n"); gst_object_unref (pipeline); return -1; }

从上面的讲解咱们知道pipeline是继承自bin,因此全部bin的方法均可以应用于pipeline,须要注意的是,咱们须要经过相应的宏(这里是GST_BIN)来将子类转换为父类,宏内部会对其作类型检查。在这里咱们使用gst_bin_add_many将多个element加入到pipeline中,这个函数接受任意多个参数,最后以NULL表示参数列表的结束。若是一次只须要加入一个,可使用gst_bin_add函数。


在将element加入bin后,咱们须要将其链接起来才能完成相应的功能,因为有多个element,因此咱们这里使用gst_element_link_many,element会根据参数的顺序依次将element链接起来。

须要注意的是,只有被加入到同一个bin的element才可以被链接在一块儿,因此咱们须要在链接前,将所须要的element加入到pipeline/bin中。

 

至此,咱们已经完成了pipeline的建立,test-pipeline能够表示为:

 

 设置element属性

/* Modify the source's properties */ g_object_set (source, "pattern", 0, NULL);

大部分的element都有本身的属性。有的属性只能被读取,这种属性经常使用于查询element的状态。有的属性同时支持修改,这种属性经常使用于控制element的行为。


因为GstElement继承于GObject,同时GObject对象系统提供了 g_object_get()用于读取属性,g_object_set()用于修改属性,g_object_set()支持以NULL结束的属性-值的键值对,因此能够一次修改element的多个属性。

咱们这里经过g_object_set()来修改videotestsrc的pattern属性。pattern属性能够控制测试图像的类型,能够尝试将0修改成1,查看输出结果有何不一样。

咱们能够经过gst-inspect-1.0 videotestsrc命令来查看pattern所支持的全部值。

 

设置播放状态

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

在完成pipeline的建立以及属性的修改后,咱们将pipeline的状态设置为PLAYING,这里与上一文章中的示例相同,只增长来错误处理,其余的返回值处理讲在后续章节讲述。

 

等待播放结束与释放资源

这部份内容与上一文章中的示例相同,这里只增长了消息类型的判断。更多关于消息的内容将在后续章节讲述。
因为videotestsrc会持续输出视频数据,因此咱们在这个例子中不会受到EOS消息,只有当咱们关闭视频窗口时会收到error消息,发送消息的element名和具体的消息内容会经过终端输出。

 

总结

在本教程中,咱们掌握了:

  • GStreamer element,bin,pipeline,bus的基本概念。
  • 如何使用gst_element_factory_make ()建立element。
  • 如何使用gst_pipeline_new ()建立pipeline。
  • 如何使用gst_bin_add_many ()将多个element加入pipeline。
  • 如何使用gst_element_link_many ()将多个element链接起来。

在这两篇文章中,咱们介绍了pipeline的两种建立方式,下一篇文章咱们将介绍GStreamer是如何保证element能够正确的链接在一块儿。


引用

https://gstreamer.freedesktop.org/documentation/tutorials/basic/concepts.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/application-development/basics/elements.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/application-development/basics/bins.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/gstreamer/gstpipeline.html?gi-language=c

 

做者: John.Leng
本文版权归做者全部,欢迎转载。商业转载请联系做者得到受权,非商业转载请在文章页面明显位置给出原文链接.
相关文章
相关标签/搜索