GStreamer---Basic tutorial 7: Multithreading and Pad Availability

GStreamer自动处理多线程,但是在某些情况下,用户可能需要手动解耦线程。这篇教程将展示如何解耦线程以及完善关于Pad Availability的描述。更准确来说,这篇文档解释了:

  1. 如何为pipeline的某些部分创建新的线程。
  2. 什么是Pad Availability。
  3. 如何复制流。

Multithreading

GStreamer是一个多线程的框架,这意味着在内部,它根据需要创建和销毁线程,例如,将流的处理从应用程序线程解耦。此外,插件也可以自由创建线程来处理它们的任务,例如视频解码器可以创建四个线程以充分利用CPU的四个核。

除此以外,应用程序在创建pipeline的时候可以明确的指定它的一个分支(pipeline的一部分)运行在不同的线程上(例如同时进行音频和视频的解码)。

这使用queue插件完成,它的sink pad只负责将数据入队,并且在另一个线程中src pad将数据出队并传递给其余插件。这个插件同样可以用来做缓冲机制,这点在后面讲述流的教程中可以看到,queue内部队列的长度可以通过属性来设置。

The example pipeline

在这里插入图片描述
程序的源是合成音频信号(连续的音调),它被tee分离(tee将从sink pad中接收到的所有东西通过src pad发送出去)。一个分支将信号传递给声卡,并外一个分支将波形渲染成视频并发送给显示屏。

如上图所示,queue创建了一个新的线程,所以整条pipeline有三个线程。含有多个sink element的pipeline通常是多线程的,因为为了同步多个sink元素通常会互相阻塞直到所有的sink准备好,假如是单线程运行那么它们将被第一个sink阻塞住。

Request pads

我们了解到uridecodebin这个插件在最开始是没有src pad的,直到数据开始传递并且uridecodebin知道媒体类型才出现,这类pad被称为Sometimes Pads,而通常一直可用的pad被称作Always Pads。

还有一类pad是Request Pad,这类pad是按需创建的。最典型的例子就是tee,它只有sink pad而没有初始化的src pads:它们需要被申请然后tee才会添加它们。在这种情况下,一个输入的流可以被复制任意次数。缺点是Request Pad和其他element的连接和sometimes pads一样,需要手动完成。

Simple multithreaded example

#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 */
    //实例化 pipeline 中的 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 */
    //修改 audio_source 和 visual 的属性
    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 */
    //把所有的 elements 加入到 pipeline 中,并且把所有可以自动连接的 element 都连接上
    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 */
    //手动连接具有 Request pads 的元件
    //如果 pad 是使用 gst_element_request_pad 创建的,则 gst_element_release_request_pad 需要后跟 gst_object_unref 以释放 pad。
    tee_audio_pad = gst_element_request_pad_simple(tee, "src_%u");
    g_print("Obtained request pad %s for audio branch.\n", gst_pad_get_name(tee_audio_pad));

    //sink 为普通的always pad ,所以使用 gst_element_get_static_pad
    queue_audio_pad = gst_element_get_static_pad(audio_queue, "sink");
    tee_video_pad = gst_element_request_pad_simple(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 */
    //Request Pad是在我们不需要的时候释放,也就是在程序的最后。

    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;
}

总结

这篇教程展示了:

  1. 如何使用queue在不同线程上运行pipeline的一部分。
  2. 什么是Request Pad以及如何使用gst_element_request_pad_simple(),gst_pad_link()和gst_element_release_request_pad()将 elements和Request Pads连接。
  3. 如何使用tee复制stream。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ap21ril

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值