GStreamer自动处理多线程,但是在某些情况下,用户可能需要手动解耦线程。这篇教程将展示如何解耦线程以及完善关于Pad Availability的描述。更准确来说,这篇文档解释了:
- 如何为pipeline的某些部分创建新的线程。
- 什么是Pad Availability。
- 如何复制流。
Multithreading
GStreamer是一个多线程的框架,这意味着在内部,它根据需要创建和销毁线程,例如,将流的处理从应用程序线程解耦。此外,插件也可以自由创建线程来处理它们的任务,例如视频解码器可以创建四个线程以充分利用CPU的四个核。
除此以外,应用程序在创建pipeline的时候可以明确的指定它的一个分支(pipeline的一部分)运行在不同的线程上(例如同时进行音频和视频的解码)。
这使用queue插件完成,它的sink pad只负责将数据入队,并且在另一个线程中src pad将数据出队并传递给其余插件。这个插件同样可以用来做缓冲机制,这点在后面讲述流的教程中可以看到,queue内部队列的长度可以通过属性来设置。
The example pipeline
程序的源是合成音频信号(连续的音调),它被tee分离(tee将从sink pad中接收到的所有东西通过src pad发送出去)。一个分支将信号传递给声卡,并外一个分支将波形渲染成视频并发送给显示屏。
如上图所示,queue创建了一个新的线程,所以整条pipeline有三个线程。含有多个sink element的pipeline通常是多线程的,因为为了同步多个sink元素通常会互相阻塞直到所有的sink准备好,假如是单线程运行那么它们将被第一个sink阻塞住。
Request pads
我们了解到uridecodebin这个插件在最开始是没有src pad的,直到数据开始传递并且uridecodebin知道媒体类型才出现,这类pad被称为Sometimes Pads,而通常一直可用的pad被称作Always Pads。
还有一类pad是Request Pad,这类pad是按需创建的。最典型的例子就是tee,它只有sink pad而没有初始化的src pads:它们需要被申请然后tee才会添加它们。在这种情况下,一个输入的流可以被复制任意次数。缺点是Request Pad和其他element的连接和sometimes pads一样,需要手动完成。
Simple multithreaded example
#include <gst/gst.h>
int main(int argc, char* argv[]) {
GstElement* pipeline, * audio_source, * tee, * audio_queue, * audio_convert, * audio_resample, * audio_sink;
GstElement* video_queue, * visual, * video_convert, * video_sink;
GstBus* bus;
GstMessage* msg;
GstPad* tee_audio_pad, * tee_video_pad;
GstPad* queue_audio_pad, * queue_video_pad;
/* Initialize GStreamer */
gst_init(&argc, &argv);
/* Create the elements */
//实例化 pipeline 中的 elements
audio_source = gst_element_factory_make("audiotestsrc", "audio_source");
tee = gst_element_factory_make("tee", "tee");
audio_queue = gst_element_factory_make("queue", "audio_queue");
audio_convert = gst_element_factory_make("audioconvert", "audio_convert");
audio_resample = gst_element_factory_make("audioresample", "audio_resample");
audio_sink = gst_element_factory_make("autoaudiosink", "audio_sink");
video_queue = gst_element_factory_make("queue", "video_queue");
visual = gst_element_factory_make("wavescope", "visual");
video_convert = gst_element_factory_make("videoconvert", "csp");
video_sink = gst_element_factory_make("autovideosink", "video_sink");
/* Create the empty pipeline */
pipeline = gst_pipeline_new("test-pipeline");
if (!pipeline || !audio_source || !tee || !audio_queue || !audio_convert || !audio_resample || !audio_sink ||
!video_queue || !visual || !video_convert || !video_sink) {
g_printerr("Not all elements could be created.\n");
return -1;
}
/* Configure elements */
//修改 audio_source 和 visual 的属性
g_object_set(audio_source, "freq", 215.0f, NULL);
g_object_set(visual, "shader", 0, "style", 1, NULL);
/* Link all elements that can be automatically linked because they have "Always" pads */
//把所有的 elements 加入到 pipeline 中,并且把所有可以自动连接的 element 都连接上
gst_bin_add_many(GST_BIN(pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,
video_queue, visual, video_convert, video_sink, NULL);
if (gst_element_link_many(audio_source, tee, NULL) != TRUE ||
gst_element_link_many(audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||
gst_element_link_many(video_queue, visual, video_convert, video_sink, NULL) != TRUE) {
g_printerr("Elements could not be linked.\n");
gst_object_unref(pipeline);
return -1;
}
/* Manually link the Tee, which has "Request" pads */
//手动连接具有 Request pads 的元件
//如果 pad 是使用 gst_element_request_pad 创建的,则 gst_element_release_request_pad 需要后跟 gst_object_unref 以释放 pad。
tee_audio_pad = gst_element_request_pad_simple(tee, "src_%u");
g_print("Obtained request pad %s for audio branch.\n", gst_pad_get_name(tee_audio_pad));
//sink 为普通的always pad ,所以使用 gst_element_get_static_pad
queue_audio_pad = gst_element_get_static_pad(audio_queue, "sink");
tee_video_pad = gst_element_request_pad_simple(tee, "src_%u");
g_print("Obtained request pad %s for video branch.\n", gst_pad_get_name(tee_video_pad));
queue_video_pad = gst_element_get_static_pad(video_queue, "sink");
if (gst_pad_link(tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
gst_pad_link(tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {
g_printerr("Tee could not be linked.\n");
gst_object_unref(pipeline);
return -1;
}
gst_object_unref(queue_audio_pad);
gst_object_unref(queue_video_pad);
/* Start playing the pipeline */
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);
/* Release the request pads from the Tee, and unref them */
//Request Pad是在我们不需要的时候释放,也就是在程序的最后。
gst_element_release_request_pad(tee, tee_audio_pad);
gst_element_release_request_pad(tee, tee_video_pad);
gst_object_unref(tee_audio_pad);
gst_object_unref(tee_video_pad);
/* 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;
}
总结
这篇教程展示了:
- 如何使用queue在不同线程上运行pipeline的一部分。
- 什么是Request Pad以及如何使用gst_element_request_pad_simple(),gst_pad_link()和gst_element_release_request_pad()将 elements和Request Pads连接。
- 如何使用tee复制stream。