【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性
目标
GStreamer会自动处理多线程这部分,但在有些情况下,你需要手动对线程做解耦。本教程会教你怎样才能做到这一点,另外也展示了Pad的有效性。主要内容包括:
如何针对部分的pipeline建立一个新的线程
什么是Pad的有效性
如何复制流
介绍
多线程
GStreamer是一个支持多线程的框架。这就说明,如果有必要它会在内部自动创建/销毁线程。比如:在应用线程中把流解出来。而且,plugin在自身也可以任意的创建线程,比如一个视频解码器为了充分利用4核CPU的能力,可以创建4个线程。
这里最重要的是,当应用在建立pipeline时可以明确一个branch(一部分pipeline)在另一个线程中运行(比如,让音频解码和视频解码同时运行)。
这个可以用queue element来达到这个目的,运行的时候sink pad仅仅负责把数据放到queue里面,同时在另一个线程里把数据从queue里面取出并向下发送。这个element同样可以用来做缓冲,这点在后面讲述流的教程时可以看到。Queue的大小事可以用设置属性的方法来设置的。
一个pipeline例子
这个例子建立的pipeline如下图所示。
这里的源是一个合成的音频信号(一个连续的tone),这个音频信号会被tee element分成两路。一路发送信号到声卡,另一个就在屏幕上渲染一个波形。
从图上看,queue会创建一个新的线程,所以整个pipeline有3个线程在运行。通常来说,有多于一个的sink element时就需要多个线程。这是因为在同步时,sink通常是阻塞起来等待所有其他的sink都准备好,如果仅仅只有一个线程是无法做到这一点的。
Request pads
在《GStreamer基础教程03——动态pipeline》里面,我们看见了一个初始时没有pad的element(uridecodebin),pad会在数据流到element时才会出现。这种pad被成为Sometimes
Pad,平时的那种一直存在的pad被称为Always Pad。
第三种pad称为Request Pad,这是根据需要来建立的。经典的例子就是tee element——有1个输入pad而没有输出pad,需要有申请,tee element才会生成。通过这种方法,输入流可以被复制成多份。和Always Pad比起来,Request Pad因为并非一直存在,所以是不能自动连接element的,这点在下面的例子中可以看到。
另外,在PLAYING或PAUSED状态下去获得pad需要注意(Pad阻塞,本教程没有讲到这点),在NULL和READY状态去获得pad就没有这个问题。
闲话少说,上代码。
简单地多线程例子
- #include <gst/gst.h>
- int main(int argc, charchar *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;
- GstPadTemplate *tee_src_pad_template;
- 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 ("ffmpegcolorspace", "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");
- ;
- }
- /* Configure elements */
- 15.0f, NULL);
- , "style", 3, 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);
- ;
- }
- /* Manually link the Tee, which has "Request" pads */
- tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");
- 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 (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 ||
- 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);
- ;
- }
- 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);
- ;
- }
工作流程
- /* 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 ("ffmpegcolorspace", "csp");
- video_sink = gst_element_factory_make ("autovideosink", "video_sink");
上面图中所有的element都在这里创建了。
audiotestsrc会创建一个合成的tone,wavescope会像示波器一样用一个音频信号来渲染出一个波形。我们前面已经用过autoaudiosink和autovideosink这两个element了。
转换element(audio convert,audioresample和ffmpegcolorspace)也是必须的,它们可以保证pipeline可以正确地连接。事实上,音频和视频的sink的Caps是由硬件确定的,所以你在设计时是不知道audiotestsrc和wavescope是否可以匹配上。如果Caps能够匹配,这些element的行为就类似于直通——对信号不做任何修改,这对于效率的影响基本可以忽略不计。
- /* Configure elements */
- 15.0f, NULL);
- , "style", 3, NULL);
为了更好的演示做了小小的调整:audiotestsrc的“freq”属性设置成215Hz,wavescope设置“shader”和“style”,让波形连续。用gst-inspect可以更好的了解这几个element的属性。
- /* 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);
- ;
- }
这块代码在pipeline里加入了所有的element并且把可以自动连接的element都连接了起来(就是Always Pad)。
- /* Manually link the Tee, which has "Request" pads */
- tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");
- 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 (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 ||
- 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);
- ;
- }
- gst_object_unref (queue_audio_pad);
- gst_object_unref (queue_video_pad);
为了连接Request Pad,需要获得对element的“requesting”。一个element可能可以创建不同种类的Request Pad,所以,当请求Pad生成时,必须提供想要的Pad模板。Pad模板可以用gst_element_class_get_pad_template()方法来获得,而且用它们的名字来区分开。在tee
element的文档里面我们可以看到两个pad模板,分别被称为“sink”(sink pad)和“src%d”(Request Pad)。
一旦我们有了Pad模板,我们就用gst_element_request_pad()方法向tee请求两个Pad——分别给音频分支和视频分支。
然后我们去获得下游的element需要连接Request Pad的Pad,这些通常都是Always Pad,所以我们用get_element_get_static_pad()方法去获得。
最后,我们用gst_pad_link()方法把pad连接起来。在gst_element_link()和gst_element_link_many()方法里面也是调用这个函数来连接的。
我们获得的这个sink pad需要通过gst_object_unref()来释放。Request Pad是在我们不需要的时候释放,也就是在程序的最后。
就像平常一样,我们设置pipeline到PLAYING状态,等待一个错误消息或者EOS消息到达。剩下的所有事情就是清楚request pad。
- /* 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);
gst_element_release_request_pad()可以释放tee的pad,但还需要调用gst_object_unref()才行。
【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性的更多相关文章
- Java基础教程:多线程基础——线程池
Java基础教程:多线程基础——线程池 线程池 在正常负载的情况瞎,通过为每一个请求创建一个新的线程来提供服务,从而实现更高的响应性. new Thread(runnable).start() 在生产 ...
- Android程序开发0基础教程(一)
程序猿学英语就上视觉英语网 Android程序开发0基础教程(一) 平台简单介绍 令人激动的Google手机操作系统平台-Android在2007年11月13日正式公布了,这是一个开放源码的操 ...
- Java基础教程:多线程基础(1)——基础操作
Java:多线程基础(1) 实现多线程的两种方式 1.继承Thread类 public class myThread extends Thread { /** * 继承Thread类,重写RUN方法. ...
- Java基础教程:多线程基础(4)——Lock的使用
Java基础教程:多线程基础(4)——Lock的使用 快速开始 Java 5中Lock对象的也能实现同步的效果,而且在使用上更加方便. 本节重点的2个知识点是:ReentrantLock类的使用和Re ...
- Java基础教程:多线程基础(2)——线程间的通信
Java基础教程:多线程基础(2)——线程间的通信 使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督. 线程间的通信 ...
- Java基础教程:多线程杂谈——双重检查锁与Volatile
Java基础教程:多线程杂谈——双重检查锁与Volatile 双重检查锁 有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化.此时程序员可能会采用延迟初始化.但要正确实 ...
- Java基础教程:多线程基础(6)——信号量(Semaphore)
Java基础教程:多线程基础(6)——信号量(Semaphore) 信号量 信号量(Semaphore)由一个值和一个指针组成,指针指向等待该信号量的进程.信号量的值表示相应资源的使用情况.信号量S≥ ...
- Java基础教程:多线程基础(5)——倒计时器(CountDownLatch)
Java基础教程:多线程基础(5)——倒计时器(CountDownLatch) 引入倒计时器 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种 ...
- 【GStreamer开发】GStreamer基础教程06——媒体格式和pad的Capabilities
目标 Pad的Capabilities是一个GStreamer element的基础,因为framework大部分时间是自动处理的,所以我们几乎感觉不到它的存在.本教程比较偏向原理,介绍了 ...
随机推荐
- RookeyFrame 加载 自定义JS
注意JS存放的位置:是在model文件夹下的某某文件夹!!! 线上添加的模块: 1.JS文件名:和表名一致 2.JS目录:Rookey.BusSys.Web\Scripts\model\TempMod ...
- RS译码的描述
在描述Reed-Solomon码的译码时,需要确定错误多项式的系数,然后进行搜索.通常,错误多项式的最高次数与错误的符号数相同. 如: λ(X)= λ0+ λ1X+ ...+ λνXν. 设该RS码最 ...
- 实现一个简易版webpack
现实 webpack 的打包产物 大概长这样(只把核心代码留下来): 实现一个简版的webpack 依葫芦画瓢,实现思路分2步: 1. 分析入口文件,把所有的依赖找出来(包括所有后代的依赖) 2. 拼 ...
- Struts2.5入门之环境设置
1. 下载Struts2.5,下载地址 2. 新建项目struts2 3. 解压后将struts\lib下的所有jar包复制到WEB-INF/lib文件夹下 4. 修改web.xml配置文件,添加如下 ...
- 区块链阶段1-Linux基础- 1 Linux简介
对于一般的办公和娱乐我们接触更多的是Windows.但是对于IT从业人员,特别是从事服务器管理以及软件开发的人员来讲,我们需要熟练掌握 Linux/UNIX操作系统的知识.所以,Linux 是众多操作 ...
- Java 多线程之生产者消费者(多个生成者多个消费者)synchronized 和lock多线程通讯和同步实现
public class ProducterConsumerSample { public static void main(String[] args) { Resourse res = new R ...
- 浅谈python闭包及装饰器
1. 什么是闭包: 闭包 是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变 ...
- tecplot三维模型绘制二维切片流线
原视频下载地址链接: https://pan.baidu.com/s/1csugHK 密码: xrni
- 根据数据文件自定义边界条件timeVaryingUniformFixedValue【转载】
转载自:http://blog.sina.com.cn/s/blog_e256415d0101nf9j.html 在OpenFOAM中,可以创建数据文件,自定义边界条件. 下面的例子读取outletP ...
- 做动画animation--matplotlib--python2和3通用代码
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/weixin_42053726/artic ...
