gstreamer教程(7)——構建應用之Bus的使用

风吹大风车發表於2024-08-29

Bus 匯流排:

  bus 匯流排是一個簡單的系統,它負責將訊息從流執行緒轉發到其自己的執行緒上下文中的應用程式。匯流排的優點是,即使 GStreamer 本身是大量執行緒的,應用程式也不需要執行緒感知即可使用 GStreamer。

  預設情況下,每個 pipeline 管道都包含一條 bus 匯流排,因此應用程式不需要建立匯流排或任何東西。應用程式唯一應該做的是在匯流排上設定一個訊息處理程式,這類似於物件的訊號處理程式。當主迴圈執行時,將定期檢查匯流排是否有新訊息,當有任意訊息可用時,將呼叫回撥。

如何使用Bus匯流排:

  有兩種方式使用 bus 匯流排:

  • 執行一個 GLib/Gtk+ 主迴圈(或者自己定期迭代預設的 GLib 主上下文)並將某種監聽附加到匯流排上。這樣, GLib 主迴圈將檢查匯流排是否有新訊息,有訊息時就會通知你。

   要使用bus匯流排,請使用 gst_bus_add_watch() 將訊息處理程式附加到 pipeline 管道的匯流排上。每當 pipeline 向 bus 發出訊息時,都會呼叫此處理函式。在此處理函式中,檢查訊號型別(請參閱下一章節) 並相應地執行一些操作。處理程式的返回值應為 TRUE 以保持處理程式附加到匯流排,返回 FALSE 以將其刪除。

  • 自己檢查 bus 資訊。這可以使用 gst_bus_peek() 和/或 gst_bus_poll() 來完成。

  basic-example-10.c

#include <stdio.h>
#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;
}

  重要的是要知道處理程式將在 mainloop 的執行緒上下文中呼叫。這意味著管道和應用程式之間透過匯流排的互動是非同步的,因此不適合某些實時處理,例如音軌之間的交叉淡化、執行(理論上)無縫播放或影片效果。所有這些事情都應該在pipeline管道上下文中完成,最簡單的方法是編寫 GStreamer 外掛。不過,它對於其主要目的非常有用:將訊息從管道傳遞到應用程式。這種方法的優點是 GStreamer 在內部執行的所有執行緒操作都對應用程式不可見的,應用程式開發人員根本不需要擔心執行緒問題。

  請注意,如果您使用的是預設的 GLib mainloop 整合,則可以連線到匯流排上的 “message” 訊號,而不是新增一個watch監聽。這樣你就不必對所有可能的訊息型別進行 switch() ;只需以 message::<type> 的形式連線感興趣的訊號,其中 <type> 是特定的訊息型別(有關訊息型別的解釋,請參閱下一節)。

  上面的程式碼段也可以寫成:

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 mainloop,則預設情況下非同步訊息訊號將不可用。但是,您可以安裝一個自定義同步處理程式,該處理程式喚醒自定義 mainloop 並使用 gst_bus_async_signal_func() 發出訊號。

message type 訊息型別:

  GStreamer 有一些可以透過匯流排傳遞的預定義訊息型別。但是,這些訊息是可擴充套件的。外掛可以定義其他訊息,應用程式可以決定為這些訊息提供特定程式碼或忽略它們。強烈建議所有應用程式至少透過向使用者提供視覺反饋來處理錯誤訊息。

  所有訊息都有訊息來源、型別和時間戳。訊息源可用於檢視哪個元素髮出了訊息。例如,對於某些訊息,只有頂級管道發出的訊息才會對大多數應用程式感興趣(例如,對於狀態更改通知)。以下是所有訊息的列表,以及它們的作用以及如何解析特定於訊息的內容的簡短說明。

  • Error, warning and info(錯誤、警告和資訊):如果需要向使用者展示有關管道狀態的訊息,element 元素會使用這些訊息。錯誤訊息是致命的,會終止資料傳遞。應修復此錯誤以恢復管道活動。警告不是致命的,但仍然意味著存在問題。資訊訊息用於非問題通知。所有這些訊息都包含一個 GError,其中包含主要錯誤型別和訊息,以及可選的除錯字串。所有的這些都可以使用 gst_message_parse_error()、gst_message_parse_warning() 和 gst_message_parse_info() 進行提取。使用後,錯誤字串和除錯字串都應釋放掉。
  • EOS:當流結束時發出此通知。管道的狀態不會改變,但進一步的媒體處理將停止。應用程式可以使用它來跳到播放列表中的下一首歌曲。在EOS之後,也可以跳回當前的流的起始位置。然後,會自動繼續播放。此訊息沒有特定的引數。
  • -Tags:在流中找到後設資料時發出。對於管道,可以多次發出此訊息(例如,一次用於描述性後設資料,如藝術家姓名或歌曲名稱,另一次用於流資訊,如取樣率和位元率)。應用程式應在內部快取後設資料。gst_message_parse_tag() 應該用於解析標籤列表,當不再需要時,應該gst_tag_list_unref () 來。
  • state-changes:在成功更改狀態後發出。 gst_message_parse_state_changed () 可用於解析此轉換的舊狀態和新狀態。
  • Buffering:在快取網路流期間發出。可以透過從 gst_message_get_structure() 返回的結構中提取 “buffer-percent” 屬性,從訊息中手動提取進度(以百分比表示)。另請參閱緩衝
  • 元素訊息:這些是某些元素獨有的特殊訊息,通常表示附加功能。元素的文件應詳細提及特定元素可以傳送哪些元素訊息。例如,如果流包含重定向指令,則 'qtdemux' QuickTime 解複用器元素可能會在某些情況下傳送 'redirect' 元素訊息。
  • 特定於應用程式的訊息:可以透過獲取訊息結構(見上文)並讀取其欄位來提取有關這些訊息的任何資訊。通常,可以安全地忽略這些訊息。

  應用程式訊息主要用於應用程式內部使用,以防應用程式需要將資訊從某個執行緒封送到主執行緒中。當應用程式使用 element 訊號時,這特別有用(因為這些訊號將在 streaming thread 的上下文中發出)。

相關文章