GStreamer官方教程系列
Basic tutorial 3: Dynamic pipelines
原文:https://gstreamer.freedesktop.org/documentation/tutorials/basic/dynamic-pipelines.html?gi-language=c
目标
本教程会展示前两个教程余下的使用GStreamer需要的基本概念,“动态”构建管道,当信息变得可用时,而不是在一开始就定义一个完整的管道。
在学习本教程之后,你就有了足够的知识储备来开始学习Playback tutorials的内容。本教程重点:
- 如何在链接元件的时候获得更细致的控制。
- 如何接受到你感兴趣的事件来及时响应。
- 元件的状态。
引言
就如你将要看到的那样,本教程中的管道并不是在管道设置为播放状态之前就完全构建完成的。这是OK的。如果我们没有采取后续的操作,数据会到达管道的末端,然后管道会产生一个错误消息并且停止。但我们会采取一些后续的操作。
在本例中,我们将会打开一个文件,这个文件是一个多路复用(multiplexed或者muxed)的,即视频和音频一起存储在同一个容器文件中。负责打开这样的容器的元件叫做解复用元件(demuxer),容器有一些格式,例如Matroska(MKV),Quick Time(QT,MOV),OGG,或者一些高级系统格式(ASF,WMV,WMA)。
如果一个容器中嵌入了多个流(例如一个视频以及两个音频轨道),解复用元件会将它们分开并且通过不同的输出端口来输出。这样,管道中可以构建不同的分支,处理不同类型的数据。
元件之间交流的端口叫做pad(GstPad)。有槽pad,数据通过它进入一个元件;源pad,数据通过它离开元件。很显然,源元件只有源pad,槽元件只有槽pad,过滤元件两者都有。
Figure 1. GStreamer elements with their pads.
一个解复用元件有一个槽pad,通过它复用数据进入元件,有多个源pad,容器中的每一条流对应一个源pad:
Figure 2. A demuxer with two source pads.
完整地看,你可以看到有个简化的管道包含了一个解复用元件以及两条分支,一条是视频,另一条是音频。但这个不是本例中要构造的管道:
Figure 3. Example pipeline with two branches.
当引入解复用元件时主要的复杂点在于它们在接收到数据并有机会看到容器中有什么东西之前不能产生任何信息。也就是说,解复用元件开始的时候并没有能够与其他元件链接的源pad,因此管道必须在解复用元件处断开。
解决方法就是从源元件到解复用元件构造其管道,并将其设置为播放,当解复用元件接收到足够的信息来知道容器中流的种类和数量后,它会创建源pad。这时就可以继续构建管道来链接解复用元件创建的源pad。
本例中,为了简化,我们仅仅链接音频pad而忽略视频pad。
Dynamic Hello World
basic-tutorial-3.c代码见下。
#include <gst/gst.h>
/* Structure to contain all our information, so we can pass it to callbacks */
typedef struct _CustomData {
GstElement *pipeline;
GstElement *source;
GstElement *convert;
GstElement *resample;
GstElement *sink;
} CustomData;
/* Handler for the pad-added signal */
static void pad_added_handler (GstElement *src, GstPad *pad, CustomData *data);
int main(int argc, char *argv[]) {
CustomData data;
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret;
gboolean terminate = FALSE;
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Create the elements */
data.source = gst_element_factory_make ("uridecodebin", "source");
data.convert = gst_element_factory_make ("audioconvert", "convert");
data.resample = gst_element_factory_make ("audioresample", "resample");
data.sink = gst_element_factory_make ("autoaudiosink", "sink");
/* Create the empty pipeline */
data.pipeline = gst_pipeline_new ("test-pipeline");
if (!data.pipeline || !data.source || !data.convert || !data.resample || !data.sink) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
/* Build the pipeline. Note that we are NOT linking the source at this
* point. We will do it later. */
gst_bin_add_many (GST_BIN (data.pi