概况
GStreamer可自动处理多线程操作,但在有些情况下,你需要手动的解耦线程。此教程就是展示如何处理此类问题。另外,完成了关于pad availability的说明,更多的为阐述以下三个问题:
- 如何创建一个新的进程,这个进程是为了执行管道中部分功能。
- 什么是pad availability。
- 如何复制流。
介绍
多线程
GStreamer 是一个多线程框架。这意味着,在内部,它会根据需要来创建和销毁线程,例如,将流与应用程序线程分离。此外,插件也可以自由地为自己的处理创建线程,例如,视频解码器可以创建 4 个线程以充分利用具有 4 个内核的 CPU。
最重要的是,在构建管道时,应用程序可以明确指定分支(管道的一部分)在不同的线程上运行(例如,让音频和视频解码器同时执行)。
这是使用queue
元素完成的,其工作方式如下。sink pad 只是将数据放入到队列并返回控制权。在不同的线程上,数据出队列并推送到下游。此元素也用于缓冲,如稍后在流式教程中所见。队列的大小可以通过属性来控制。
示例管道
此示例构建以下管道:
源是一个合成音频信号(连续音),它使用一个tee
元素进行分割(tee使用src_%u发送其从sink接收到的所有内容)。然后一个分支将信号发送到声卡,另一个分支渲染波形视频并将其发送到屏幕。
如图所示,队列内部在src pad上创建了一个新线程,用来解耦Queue的sink pad和src pad。所以这个管道在 3 个线程中运行。具有多个接收器的管道通常需要多线程,因为要同步,接收器通常会阻塞执行,直到所有其他接收器都准备就绪,如果只有一个线程,这个线程会被第一个接收器阻塞,那么它们就无法准备好。
Request pads
回顾基础概念:pads分为sometimes pads, always pads, request pads
sometimes pads和always pads请自行回顾基础概念。这里只讲request pads.
Request Pad,它是按需创建的,典型的例子是tee element,它有一个sink pad但没有初始的src pad,这些src pad 需要被请求然后tee去添加它们。通过这种方式,输入流可以被复制任意次。缺点是使用 Request Pads 链接元素不像链接 Always Pads 那样自动。
此外,为了请求(或释放)处于PLAYING
或PAUSED
状态的pad,您需要注意本教程中未描述的其他注意事项(pad阻塞)。但是,在NULL
或 READY
状态下请求(或释放)pad是安全的。
示例代码
#include "mainwindow.h"
#include <QApplication>
//int main(int argc, char *argv[])
//{
// QApplication a(argc, argv);
// MainWindow w;
// w.show();
// return a.exec();
//}
#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 */
//audiotestsrc产生合成音。wavescope消耗音频信号并呈现波形,就好像它是(公认便宜的)示波器一样。我们已经使用了autoaudiosink和 autovideosink。
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 */
//“freq”属性 audiotestsrc控制波的频率(215Hz 使波在窗口中看起来几乎是静止的),这种样式和着色 wavescope器使波连续
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 */
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 */
// tee_audio_pad = gst_element_request_pad_simple(tee, "src_%u"); //gstreamer-1.20版本才可用
GstPadTemplate * tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src_%u");
if(!tee_src_pad_template)
{
g_print("Retrieves a padtemplate from element_class with the given name failed.\n");
return -1;
}
tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
g_print ("Obtained request pad %s for audio branch.\n", gst_pad_get_name (tee_audio_pad));
queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");
// tee_video_pad = gst_element_request_pad_simple (tee, "src_%u"); //gstreamer-1.20版本才可用
tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
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 || //连接tee_audio_pad与queue_audio_pad
gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) { //连接tee_video_pad和 queue_video_pad
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, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
/* Release the request pads from the Tee, and unref them */
gst_element_release_request_pad (tee, tee_audio_pad); //释放tee 中的tee_audio_pad
gst_element_release_request_pad (tee, tee_video_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;
}