zoukankan      html  css  js  c++  java
  • 【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就没有这个问题。

          闲话少说,上代码。


    简单地多线程例子

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <gst/gst.h>  
    2.     
    3. int main(int argc, charchar *argv[]) {  
    4.   GstElement *pipeline, *audio_source, *tee, *audio_queue, *audio_convert, *audio_resample, *audio_sink;  
    5.   GstElement *video_queue, *visual, *video_convert, *video_sink;  
    6.   GstBus *bus;  
    7.   GstMessage *msg;  
    8.   GstPadTemplate *tee_src_pad_template;  
    9.   GstPad *tee_audio_pad, *tee_video_pad;  
    10.   GstPad *queue_audio_pad, *queue_video_pad;  
    11.     
    12.   /* Initialize GStreamer */  
    13.   gst_init (&argc, &argv);  
    14.     
    15.   /* Create the elements */  
    16.   audio_source = gst_element_factory_make ("audiotestsrc""audio_source");  
    17.   tee = gst_element_factory_make ("tee""tee");  
    18.   audio_queue = gst_element_factory_make ("queue""audio_queue");  
    19.   audio_convert = gst_element_factory_make ("audioconvert""audio_convert");  
    20.   audio_resample = gst_element_factory_make ("audioresample""audio_resample");  
    21.   audio_sink = gst_element_factory_make ("autoaudiosink""audio_sink");  
    22.   video_queue = gst_element_factory_make ("queue""video_queue");  
    23.   visual = gst_element_factory_make ("wavescope""visual");  
    24.   video_convert = gst_element_factory_make ("ffmpegcolorspace""csp");  
    25.   video_sink = gst_element_factory_make ("autovideosink""video_sink");  
    26.     
    27.   /* Create the empty pipeline */  
    28.   pipeline = gst_pipeline_new ("test-pipeline");  
    29.     
    30.   if (!pipeline || !audio_source || !tee || !audio_queue || !audio_convert || !audio_resample || !audio_sink ||  
    31.       !video_queue || !visual || !video_convert || !video_sink) {  
    32.     g_printerr ("Not all elements could be created. ");  
    33.     return -1;  
    34.   }  
    35.     
    36.   /* Configure elements */  
    37.   g_object_set (audio_source, "freq"215.0fNULL);  
    38.   g_object_set (visual, "shader"0"style"3NULL);  
    39.     
    40.   /* Link all elements that can be automatically linked because they have "Always" pads */  
    41.   gst_bin_add_many (GST_BIN (pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,  
    42.       video_queue, visual, video_convert, video_sink, NULL);  
    43.   if (gst_element_link_many (audio_source, tee, NULL) != TRUE ||  
    44.       gst_element_link_many (audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||  
    45.       gst_element_link_many (video_queue, visual, video_convert, video_sink, NULL) != TRUE) {  
    46.     g_printerr ("Elements could not be linked. ");  
    47.     gst_object_unref (pipeline);  
    48.     return -1;  
    49.   }  
    50.     
    51.   /* Manually link the Tee, which has "Request" pads */  
    52.   tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");  
    53.   tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULLNULL);  
    54.   g_print ("Obtained request pad %s for audio branch. ", gst_pad_get_name (tee_audio_pad));  
    55.   queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");  
    56.   tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULLNULL);  
    57.   g_print ("Obtained request pad %s for video branch. ", gst_pad_get_name (tee_video_pad));  
    58.   queue_video_pad = gst_element_get_static_pad (video_queue, "sink");  
    59.   if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||  
    60.       gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {  
    61.     g_printerr ("Tee could not be linked. ");  
    62.     gst_object_unref (pipeline);  
    63.     return -1;  
    64.   }  
    65.   gst_object_unref (queue_audio_pad);  
    66.   gst_object_unref (queue_video_pad);  
    67.     
    68.   /* Start playing the pipeline */  
    69.   gst_element_set_state (pipeline, GST_STATE_PLAYING);  
    70.     
    71.   /* Wait until error or EOS */  
    72.   bus = gst_element_get_bus (pipeline);  
    73.   msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);  
    74.     
    75.   /* Release the request pads from the Tee, and unref them */  
    76.   gst_element_release_request_pad (tee, tee_audio_pad);  
    77.   gst_element_release_request_pad (tee, tee_video_pad);  
    78.   gst_object_unref (tee_audio_pad);  
    79.   gst_object_unref (tee_video_pad);  
    80.     
    81.   /* Free resources */  
    82.   if (msg != NULL)  
    83.     gst_message_unref (msg);  
    84.   gst_object_unref (bus);  
    85.   gst_element_set_state (pipeline, GST_STATE_NULL);  
    86.     
    87.   gst_object_unref (pipeline);  
    88.   return 0;  
    89. }  

    工作流程

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Create the elements */  
    2. audio_source = gst_element_factory_make ("audiotestsrc""audio_source");  
    3. tee = gst_element_factory_make ("tee""tee");  
    4. audio_queue = gst_element_factory_make ("queue""audio_queue");  
    5. audio_convert = gst_element_factory_make ("audioconvert""audio_convert");  
    6. audio_resample = gst_element_factory_make ("audioresample""audio_resample");  
    7. audio_sink = gst_element_factory_make ("autoaudiosink""audio_sink");  
    8. video_queue = gst_element_factory_make ("queue""video_queue");  
    9. visual = gst_element_factory_make ("wavescope""visual");  
    10. video_convert = gst_element_factory_make ("ffmpegcolorspace""csp");  
    11. 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的行为就类似于直通——对信号不做任何修改,这对于效率的影响基本可以忽略不计。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Configure elements */  
    2. g_object_set (audio_source, "freq"215.0fNULL);  
    3. g_object_set (visual, "shader"0"style"3NULL);  
          为了更好的演示做了小小的调整:audiotestsrc的“freq”属性设置成215Hz,wavescope设置“shader”和“style”,让波形连续。用gst-inspect可以更好的了解这几个element的属性。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Link all elements that can be automatically linked because they have "Always" pads */  
    2. gst_bin_add_many (GST_BIN (pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,  
    3.     video_queue, visual, video_convert, video_sink, NULL);  
    4. if (gst_element_link_many (audio_source, tee, NULL) != TRUE ||  
    5.     gst_element_link_many (audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||  
    6.     gst_element_link_many (video_queue, visual, video_convert, video_sink, NULL) != TRUE) {  
    7.   g_printerr ("Elements could not be linked. ");  
    8.   gst_object_unref (pipeline);  
    9.   return -1;  
    10. }  
          这块代码在pipeline里加入了所有的element并且把可以自动连接的element都连接了起来(就是Always Pad)。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Manually link the Tee, which has "Request" pads */  
    2. tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");  
    3. tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULLNULL);  
    4. g_print ("Obtained request pad %s for audio branch. ", gst_pad_get_name (tee_audio_pad));  
    5. queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");  
    6. tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULLNULL);  
    7. g_print ("Obtained request pad %s for video branch. ", gst_pad_get_name (tee_video_pad));  
    8. queue_video_pad = gst_element_get_static_pad (video_queue, "sink");  
    9. if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||  
    10.     gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {  
    11.   g_printerr ("Tee could not be linked. ");  
    12.   gst_object_unref (pipeline);  
    13.   return -1;  
    14. }  
    15. gst_object_unref (queue_audio_pad);  
    16. 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。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Release the request pads from the Tee, and unref them */  
    2. gst_element_release_request_pad (tee, tee_audio_pad);  
    3. gst_element_release_request_pad (tee, tee_video_pad);  
    4. gst_object_unref (tee_audio_pad);  
    5. gst_object_unref (tee_video_pad);  
          gst_element_release_request_pad()可以释放tee的pad,但还需要调用gst_object_unref()才行。
  • 相关阅读:
    CentOS 7 安装 Nginx (LNMP环境搭建第一步)
    JS去除字符串最后的逗号
    mkdir如何创建多级目录
    php实现获取数组中相同/不相同的元素
    git 查看远程仓库地址
    windows git pull或者push或者clone代码时弹出安全框解决办法
    【laravel】api路由无法访问
    PHP开发api接口安全验证
    PHP根据身份证号码,获取性别、获取生日、计算年龄等多个信息
    PHP按一定比例压缩图片
  • 原文地址:https://www.cnblogs.com/huty/p/8517318.html
Copyright © 2011-2022 走看看