GStreamer应用程序——Bus

1,Bus

总线是一个简单的系统,负责将来自流线程的消息转发到应用程序自己的线程上下文中。总线的优势在于,应用程序不需要了解多线程就可以使用GStreamer,尽管GStreamer本身是高度多线程的。

每个管道默认都包含一个总线,因此应用程序不需要创建总线或任何其他内容。应用程序唯一需要做的是在总线上设置一个消息处理程序,这类似于对象的信号处理程序。当主循环运行时,总线将定期检查新消息,并在有任何消息可用时调用回调函数。

1.1,如何使用 bus

使用总线有两种方式:

  • 要运行GLib/Gtk+主循环(或定期迭代默认的GLib主上下文),并将某种监视器附加到总线上。这样,GLib主循环将检查总线上的新消息,并在有消息时通知您。

    在这种情况下,您通常会使用gst_bus_add_watch()gst_bus_add_signal_watch()

    要使用总线,请使用gst_bus_add_watch()将消息处理程序附加到管道的总线。此处理程序将在管道向总线发出消息时被调用。在此处理程序中,检查信号类型(参见下一节),并据此采取相应的操作。处理程序的返回值应为TRUE以保持处理程序附加到总线,返回FALSE以将其删除。

  • 可以使用gst_bus_peek()和/或gst_bus_poll()自行检查总线上的消息。

#include <gst/gst.h>

static GMainLoop *loop;

static gboolean
my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
  g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:{
      GError *err;
      gchar *debug;

      gst_message_parse_error (message, &err, &debug);
      g_print ("Error: %s\n", err->message);
      g_error_free (err);
      g_free (debug);

      g_main_loop_quit (loop);
      break;
    }
    case GST_MESSAGE_EOS:
      /* end-of-stream */
      g_main_loop_quit (loop);
      break;
    default:
      /* unhandled message */
      break;
  }

  /* we want to be notified again the next time there is a message
   * on the bus, so returning TRUE (FALSE means we want to stop watching
   * for messages on the bus and our callback should not be called again)
   */
  return TRUE;
}

gint
main (gint argc, gchar * argv[])
{
  GstElement *pipeline;
  GstBus *bus;
  guint bus_watch_id;

  /* init */
  gst_init (&argc, &argv);

  /* create pipeline, add handler */
  pipeline = gst_pipeline_new ("my_pipeline");

  /* adds a watch for new message on our pipeline's message bus to
   * the default GLib main context, which is the main context that our
   * GLib main loop is attached to below
   */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL);
  gst_object_unref (bus);

  /* [...] */

  /* create a mainloop that runs/iterates the default GLib main context
   * (context NULL), in other words: makes the context check if anything
   * it watches for has happened. When a message has been posted on the
   * bus, the default main context will automatically call our
   * my_bus_callback() function to notify us of that message.
   * The main loop will be run until someone calls g_main_loop_quit()
   */
  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

  /* clean up */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  g_source_remove (bus_watch_id);
  g_main_loop_unref (loop);

  return 0;
}

了解处理程序将在主循环的线程上下文中被调用是很重要的。这意味着通过总线进行的管道与应用程序之间的交互是异步的,因此不适合一些实时用途,如音频轨道之间的交叉淡出、(理论上)无间隙播放或视频效果。所有这些都应该在管道上下文中完成,通过编写GStreamer插件来实现最为简单。然而,它对于其主要目的非常有用:将消息从管道传递给应用程序。这种方法的优势在于,GStreamer内部进行的所有线程操作对应用程序都是隐藏的,应用程序开发者根本不需要担心线程问题。

请注意,如果您使用的是默认的GLib主循环集成,您可以连接总线上的“message”信号,而不是附加监视器。这样您就不需要使用switch()函数处理所有可能的消息类型;只需以message::的形式连接到感兴趣的信号,其中是特定的消息类型(下一节将解释消息类型)。

上述代码片段也可以写成:

GstBus *bus;

[..]

bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message::error", G_CALLBACK (cb_message_error), NULL);
g_signal_connect (bus, "message::eos", G_CALLBACK (cb_message_eos), NULL);

[..]

如果您没有使用GLib主循环,则默认情况下异步消息信号将不可用。然而,您可以安装一个自定义的同步处理器,它唤醒自定义主循环并使用gst_bus_async_signal_func()来发射信号。(有关详细信息,请参见文档)

1.2,消息类型

GStreamer预定义了一些可以通过总线传递的消息类型。然而,这些消息是可扩展的。插件可以定义额外的消息,应用程序可以决定为这些消息提供特定代码或忽略它们。强烈建议所有应用程序至少处理错误消息,通过向用户提供视觉反馈。

所有消息都有一个消息源、类型和时间戳。消息源可以用来查看哪个元素发出了消息。对于某些消息,例如,只有顶级管道发出的消息对大多数应用程序来说可能很有趣(例如,用于状态更改通知)。下面是一个所有消息的列表以及它们的简短说明,以及如何解析特定于消息的内容。

  • 错误、警告和信息通知:当需要向用户显示有关管道状态的消息时,元素会使用这些通知。错误消息是致命的,会终止数据传输。应该修复错误以恢复管道活动。警告虽然不致命,但仍然意味着存在问题。信息消息用于非问题通知。所有这些消息都包含一个GError,其中包含主要的错误类型和消息,以及可选的调试字符串。可以使用gst_message_parse_error()、_parse_warning() 和 _parse_info() 来提取它们。使用后应释放错误和调试字符串。

  • 流结束通知:当流结束时发出此通知。管道的状态不会改变,但进一步的媒体处理将会停止。应用程序可以使用这个通知来跳到播放列表中的下一首歌曲。在流结束后,还可以在流中向后寻找。然后播放将自动继续。这个消息没有特定的参数。

  • 标签:当在流中发现元数据时发出。对于管道来说,这可能会多次发出(例如,一次用于描述性元数据,如艺术家名称或歌曲标题,另一次用于流信息,如采样率和比特率)。应用程序应该在内部缓存元数据。应该使用gst_message_parse_tag()来解析标签列表,当不再需要时应该使用gst_tag_list_unref()来释放它。

  • 状态更改:在成功的状态更改后发出。可以使用gst_message_parse_state_changed()来解析这次转换的旧状态和新状态。

  • 缓冲:在网络流的缓存期间发出。可以通过从gst_message_get_structure()返回的结构中提取“buffer-percent”属性来手动提取进度(以百分比表示)。参见Buffering

  • 元素消息:这些是特定于某些元素的特殊消息,通常代表附加功能。元素的文档应该详细说明特定元素可能发送哪些元素消息。例如,'qtdemux' QuickTime解复用器在某些情况下可能会在流包含重定向指令时发送'redirect'元素消息。

  • 应用程序特定消息:通过获取消息结构(见上文)并阅读其字段,可以提取任何有关这些消息的信息。通常,这些消息可以安全地被忽略。

    应用程序消息主要是为了在应用程序内部使用,以防应用程序需要将某些线程的信息传送到主线程。当应用程序使用元素信号时,这特别有用(因为这些信号将在流线程的上下文中发出)。

### GStreamer 中的 Bin 和 Pipeline 在 GStreamer 的架构中,`Bin` 是一种特殊的容器对象,用于管理多个 `Element` 并简化它们之间的连接和控制[^3]。顶层的 `Bin` 被称为 `Pipeline`,它是所有 GStreamer 应用程序的核心组件之一。通过使用 `Pipeline`,程序员可以方便地管理和协调复杂的多媒体处理流程。 #### Boxed (箱柜) 数据结构的应用场景 虽然 GStreamer 自身并没有直接提供名为“Boxed”的数据结构或功能模块,但在实际开发过程中,“boxed”通常指的是某种封装机制或者特定的数据容器形式。这种概念可能体现在以下几个方面: 1. **自定义 Element 封装** 开发者可以通过创建自定义的 `GstElement` 来实现类似于“box”(箱子/盒)的功能,即将一组逻辑上紧密关联的操作打包在一起。这种方式允许开发者隐藏内部复杂性并暴露统一接口给外部调用方[^1]。 2. **Pipeline 组织模式** 使用 `Pipeline` 对象来构建整个媒体流水线时,实际上也是将各个独立的功能单元(即 Elements)组合成更大的整体——这本身就可以看作是一种高层次上的“boxing”。例如,在音频播放器中可能会有源解析、解码、音量调节以及最终渲染等多个阶段;这些都可以被放入同一个 pipeline 当中形成完整的解决方案。 下面给出一个简单示例展示如何利用 Python 构建基于 PyGST(GStreamer bindings for python) 的 basic audio player application: ```python import gi gi.require_version('Gst', '1.0') from gi.repository import Gst, GObject def on_message(bus: Gst.Bus, message: Gst.Message, loop: GObject.MainLoop): mtype = message.type if mtype == Gst.MessageType.EOS: print("End of stream") loop.quit() elif mtype == Gst.MessageType.ERROR: err, debug = message.parse_error() print(f"Error: {err}, Debug: {debug}") loop.quit() # Initialize GStreamer Gst.init(None) # Create the elements pipeline = Gst.parse_launch( "playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm" ) loop = GObject.MainLoop() bus = pipeline.get_bus() bus.add_signal_watch() bus.connect("message", on_message, loop) # Start playing pipeline.set_state(Gst.State.PLAYING) try: loop.run() except KeyboardInterrupt: pass # Clean up pipeline.set_state(Gst.State.NULL) ``` 此脚本展示了怎样快速搭建起能够播放网络资源的基础框架,并且包含了错误处理部分以增强健壮性。 #### 相关扩展方向探讨 对于更高级别的需求来说,则需要深入研究具体领域内的最佳实践和技术细节。比如针对实时通信(RTC),则需考虑 WebRTC 协议栈集成等问题;如果是图像识别类任务,则要引入机器学习模型推理环节等等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值