GStreamer框架会自动处理多线程的逻辑,但在某些状况下,咱们仍然须要根据实际的状况本身将部分Pipeline在单独的线程中执行,本文将介绍如何处理这种状况。html
GStreamer框架是一个支持多线程的框架,线程会根据Pipeline的须要自动建立和销毁,例如,将媒体流与应用线程解耦,应用线程不会被GStreamer的处理阻塞。并且,GStreamer的插件还能够建立本身所需的线程用于媒体的处理,例如:在一个4核的CPU上,视频解码插件能够建立4个线程来最大化利用CPU资源。
此外,在建立Pipeline时,咱们还能够指定某个Pipeline的分支在不一样的线程中执行(例如,使audio、video同时在不一样的线程中进行解码)。这是经过queue Element来实现的,queue的sink pad仅仅将数据放入队列,另一个线程从队列中取出数据,并传递到下一个Element。queue一般也被用于做为数据缓冲,缓冲区大小能够经过queue的属性进行配置。ios
在上面的示例Pipeline中,souce是audiotestsrc,会产生一个相应的audio信号,而后使用tee Element将数据分为两路,一路被用于播放,经过声卡输出,另外一路被用于转换为视频波形,用于输出到屏幕。
示例图中的红色阴影部分表示位于同一个线程中,queue会建立单独的线程,因此上面的Pipeline使用了3个线程完成相应的功能。拥有多个sink的Pipeline一般须要多个线程,由于在多个sync间进行同步的时候,sink会阻塞当前所在线程直到所等待的事件发生。多线程
示例代码将建立上图所示的Pipeline。框架
#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 */ 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 */ 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_get_request_pad (tee, "src_%u"); 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_get_request_pad (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 */ 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; }
保存以上代码,执行下列编译命令便可获得可执行程序:ide
gcc basic-tutorial-8.c -o basic-tutorial-8 `pkg-config --cflags --libs gstreamer-1.0`
/* Create the 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", "video_convert"); video_sink = gst_element_factory_make ("autovideosink", "video_sink");
首先建立所需的Element:audiotestsrc会产生测试的音频波形数据。wavescope 会将输入的音频数据转换为波形图像。audioconvert,audioresample,videoconvert保证了Pipeline中各个Element之间的数据能够互相兼容,使得Pipeline可以被正确的link起来,若是不须要对数据进行转换,这些Element会直接将数据发送到下一个Element,这种状况下的性能影响能够忽略不计。函数
/* Configure elements */ g_object_set (audio_source, "freq", 215.0f, NULL); g_object_set (visual, "shader", 0, "style", 1, NULL);
这里修改相应Element的参数,使得输出结果更直观。“freq”会设置audiotestsrc输出波形的频率为215Hz,设置“shader”和“style”使得波形更加连续。其余的参数能够经过gst-inspect查看。源码分析
/* 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_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_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; }
这里咱们使用gst_element_link_many 将多个Element链接起来,须要注意的是,这里咱们只链接了拥有Always Pad的Eelement。虽然gst_element_link_many() 可以在内部处理Request Pad的状况,但咱们仍然须要单独释放Request Pad,若是直接使用此函数链接全部的Element,这样容易忘记释放Request Pad。所以咱们使用下面的代码单独处理Request Pad。性能
/* Manually link the Tee, which has "Request" pads */ tee_audio_pad = gst_element_get_request_pad (tee, "src_%u"); 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_get_request_pad (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);
为了可以链接到Request Pad,咱们须要主动的向Element取得相应的Pad。因为一个Element能够提供不一样的Request Pad,因此咱们须要指定所需的“Pad Template”,Element提供的Pad Template能够经过gst-inspect查看。从下面的结果能够发现,tee提供了2种类型的模板, ”sink“ 和“src_%u"。测试
$ gst-inspect-1.0 tee ... Pad Templates: SRC template: 'src_%u' Availability: On request Has request_new_pad() function: gst_tee_request_new_pad Capabilities: ANY SINK template: 'sink' Availability: Always Capabilities: ANY ...
因为咱们这里须要的是2个Source Pad,因此咱们经过gst_element_get_request_pad (tee, "src_%u")获取两个Request Pad分别用于audio和video。queue的Sink Pad是Alwasy Pad,因此咱们直接使用gst_element_get_static_pad 获取其Sink Pad。最后再经过gst_pad_link()将其链接起来,在gst_element_link()和gst_element_link_many()内部也是使用此函数链接两个Element的Pad。spa
须要注意的是,咱们经过Element获取到的Pad的引用计数会自动增长,所以咱们须要调用gst_object_unref()释放相关的引用,对于Request Pad,咱们须要在Pipeline执行完成后进行释放。
/* Release the request pads from the Tee, and unref them */ 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);
除了播放完成后正常的资源释放外,咱们还要对Request进行释放,须要首先调用gst_element_release_request_pad(),最后再释放相应的对象。
咱们在本文中了解了: