自学内容网 自学内容网

GStreamer 简明教程(六):利用 Tee 复制流数据,巧用 Queue 实现多线程

系列文章目录



前言

本章基于官方教程 Basic tutorial 7: Multithreading and Pad Availability 进行说明和补充。本章内容并不复杂,引入了两个新的 Element:Tee 和 Queue。Tee 用于复制数据, Queue 创建线程让 Pipeline 的一部分在另外线程中运行。

由于官方教程在本人机器上总是无法运行,我对它做了一些修改,具体代码在 basic-tutorial-7.c

Pipeline

在这里插入图片描述

本次教程中使用的 Pipeline 如上图所示,简单做个解释说明:

  1. audiotestsrc: 生成测试音频数据。
  2. tee:接收一个输入流,并可以创建多个完全相同的输出流。这些输出流可以被发送到不同的处理管道中。
  3. queue:创建一个 FIFO(先进先出)缓冲区。它接收输入数据,暂时存储,然后在单独线程中按照接收顺序输出数据。
  4. wavescope: 可视化音频数据的波形
  5. spectrascope:可视化音频数据的频谱
  6. videoconvert: 视频格式转换
  7. autovideosink: 显示视频数据

Request Pads

GStreamer 简明教程(三):动态调整 Pipeline 中我们学习了 uridecodebinuridecodebin 是一个典型的例子,展示了如何使用动态创建的 Pad。这种 Pad 类型被称为 Sometime Pad。当 uridecodebin 切换状态后,它会从输入源读取媒体信息,根据识别出的流动态添加相应数量的 Pad。随后,它通过发送 “pad-added” 信号来通知应用程序这些新创建的 Pad。

相比之下,Always Pad 是在元素创建时即存在,并在元素的整个生命周期内保持可用。这类 Pad 通常用于固定格式的数据流处理。

第三种类型是 Request Pad,它们根据需要通过显式请求创建。这类 Pad 被用于动态连接或多路输出的场景。例如,在解码器中,可以根据请求生成相应的 Pad,以处理特定类型的子流。

tee 元素就支持 Request Pad,首先通过 gst-inspect-1.0 来看下 tee 的详细信息,其中有 Pad Templates 的描述

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      ANY
  
  SRC template: 'src_%u'
    Availability: On request
    Capabilities:
      ANY

src_%u 表示 Request Pad 的名称格式。其中 %u 是一个占位符,代表一个唯一的无符号整数。这意味着每次请求一个新的源 Pad 时,tee 元素将创建一个新的 Pad,并用依次递增的整数替换 %u。例如,第一次请求可能会创建 src_0,第二次创建 src_1,依此类推。这种命名方式帮助管理和区分多个动态创建的 Pad。

tee 元素创建 pad 的代码示例:

// 请求创建新的 src pad
GstPad *pad1 = gst_element_request_pad_simple(tee, "src_%u");
// 这可能创建名为 "src_0" 的 pad

GstPad *pad2 = gst_element_request_pad_simple(tee, "src_%u");
// 这可能创建名为 "src_1" 的 pad

// 使用这些 pad
// ...

// 在不需要时释放
gst_element_release_request_pad(tee, pad1);
gst_element_release_request_pad(tee, pad2);

Show me the code

接下来对具体代码进行说明,完整代码在 basic-tutorial-7.c

代码会生成两个窗口,一个窗口展示音频的频谱图,另一个显示音频的波形图
在这里插入图片描述

#include <gst/gst.h>

#ifdef __APPLE__
#include <TargetConditionals.h>
#endif

int tutorial_main(int argc, char *argv[]) {
  GstElement *pipeline;
  GstElement *audio_source;
  GstElement *queue;
  GstElement *visual;
  GstElement *video_convert;
  GstElement *video_sink;
  GstElement *tee;
  GstElement *queue2;
  GstElement *visual2;
  GstElement *video_convert2;
  GstElement *video_sink2;

  /* Initialize GStreamer */
  gst_init(&argc, &argv);

  // create elements
  audio_source = gst_element_factory_make("audiotestsrc", "audio_source");
  tee = gst_element_factory_make("tee", "tee");

  queue = gst_element_factory_make("queue", "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");

  queue2 = gst_element_factory_make("queue", "queue2");
  visual2 = gst_element_factory_make("spectrascope", "visual2");
  video_convert2 = gst_element_factory_make("videoconvert", "video_convert2");
  video_sink2 = gst_element_factory_make("autovideosink", "video_sink2");

  // create pipeline
  pipeline = gst_pipeline_new("test-pipeline");

  if (!pipeline || !audio_source || !tee || !queue || !visual ||
      !video_convert || !video_sink || !queue2 || !visual2 || !video_convert2 ||
      !video_sink2) {
    g_printerr("Not all elements could be created.\n");
    return -1;
  }

  // configure elements
  g_object_set(audio_source, "freq", 215.0f, "wave", 2, NULL);
  g_object_set(visual, "shader", 0, "style", 1, NULL);
//  g_object_set(visual2, "shader", 0, "style", 3, NULL);

  // link elements
  gst_bin_add_many(GST_BIN(pipeline), audio_source, tee, queue, visual,
                   video_convert, video_sink, queue2, visual2, video_convert2,
                   video_sink2, NULL);

  if (gst_element_link_many(audio_source, tee, NULL) != TRUE) {
    g_printerr("Elements could not be linked.\n");
    gst_object_unref(pipeline);
    return -1;
  }

  // link first branch
  if (gst_element_link_many(queue, visual, video_convert, video_sink, NULL) !=
      TRUE) {
    g_printerr("Elements could not be linked.\n");
    gst_object_unref(pipeline);
    return -1;
  }

  // link second branch
  if (gst_element_link_many(queue2, visual2, video_convert2, video_sink2,
                            NULL) != TRUE) {
    g_printerr("Elements could not be linked.\n");
    gst_object_unref(pipeline);
    return -1;
  }

  /* Manually link the Tee, which has "Request" pads */
  GstPad *tee_audio_pad = gst_element_request_pad_simple(tee, "src_%u");
  g_print("Obtained request pad %s for first audio branch.\n",
          gst_pad_get_name(tee_audio_pad));
  GstPad *queue_audio_pad = gst_element_get_static_pad(queue, "sink");

  GstPad *tee_audio_pad2 = gst_element_request_pad_simple(tee, "src_%u");
  g_print("Obtained request pad %s for second audio branch.\n",
          gst_pad_get_name(tee_audio_pad));
  GstPad *queue_audio_pad2 = gst_element_get_static_pad(queue2, "sink");
  if (gst_pad_link(tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
      gst_pad_link(tee_audio_pad2, queue_audio_pad2) != 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_audio_pad2);

  // start playing
  gst_element_set_state(pipeline, GST_STATE_PLAYING);

  // wait until error or EOS
  GstBus *bus = gst_element_get_bus(pipeline);
  GstMessage *msg = gst_bus_timed_pop_filtered(
      bus, GST_CLOCK_TIME_NONE,
      (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));

  gst_element_release_request_pad(tee, tee_audio_pad);
  gst_element_release_request_pad(tee, tee_audio_pad2);
  gst_object_unref(tee_audio_pad);
  gst_object_unref(tee_audio_pad2);

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

int main(int argc, char *argv[]) {
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
  return gst_macos_main((GstMainFunc)tutorial_main, argc, argv, NULL);
#else
  return tutorial_main(argc, argv);
#endif
}
  1. 首先,创建各个元素,并将可以连接的部分先进行连接:
    • audio_source -> tee
    • queue -> visual -> video_convert -> video_sink
    • queue2 -> visual2 -> video_convert2 -> video_sink2
  2. 向 tee 元素请求两个 src pad,连接 tee 与 queue、queue2
  3. 其他就是启动 pipeline,监听 bus 数据等常规操作,不再赘述
  4. 最后就是清理工作

总之非常简单,非常清晰。


参考


原文地址:https://blog.csdn.net/weiwei9363/article/details/141611426

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!