GStreamer基础教程之多进程与pad的有效性

概况

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 那样自动。

此外,为了请求(或释放)处于PLAYINGPAUSED状态的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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值