2015年11月18日 星期三

Gstreamer簡介


Reference:



  • http://gstreamer.freedesktop.org/

  • http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/manual.pdf

  • gstreamer SDK Tutorials

  • Android MediaCodec stuff





  • About GStreamer
    GStreamer 是一個建立streaming media applications的 framework 
    GStreamer提供了
    • an API for multimedia applications
    • a plugin architecture
    • a pipeline architecture
    • a mechanism for media type handling/negotiation
    • a mechanism for synchronization
    • over 250 plug-ins providing more than 1000 elements
    • a set of tools

    Foundations
    • Elements:每一個原件都有特定的功能,像是讀取檔案資料、decoding of this data or outputting this data to your sound card,將幾個element串聯起來就可以建立一個pipeline來執行特定任務
    • Pads:當element連接各個element時,pad為elements的輸入和輸出,常被用於協商各elements之間的連結和資料串流在GStreamer中,Pads具有特定資料處理能力:
    • 限制通過Pads的資料型態
    • 資料型態的協商呼叫 caps negotiation 這個process,資料型態的描述為 GstCaps
    • 打個比方。Pads類似於物理設備上的插頭或插孔。例如,一個家庭影院系統,包括一個放大器,一個DVD播放機的,和一個(無聲)視頻投影機。連結DVD播放機到放大器是允許的,因為這兩種設備有音頻插孔,以及投影機與DVD播放機連接是允許的,因為這兩種設備都兼容的視頻插孔。可能不會在投影機和放大器之間的聯繫,因為投影機和放大器具有不同類型的插孔。 GStreamer中的服務類似於在家庭影院系統的接口。
    • Bins:蒐集所有elements的容器,可以改變bins的狀態來改變所有elements的狀態
    • pipeline:為一個high-level的bin,它提供bus讓application與其子集合同步,當設定為PAUSED 或 PLAYING 狀態,資料流將會進行處理
    • Communication:GStreamer提供一些機制讓pipeline和application來做溝通和資料交換
    • buffers:各個elements 在pipeline 中傳輸資料流,總是從source到sink(downstream)
    • events:各個elements 之間傳送 或是 application 傳給 pipeline。為downstream 和 upstream,downstream可以同步資料流
    •  messages :pipeline與application溝通之用。也常用於傳輸 errors、狀態交換等等資訊給application。
    • queries:允許application 從pipeline中請求 duaration和 current playback position 等訊息。elemant也常使用queries 與對等element中來請求資訊。

    Initializing GStreamer

    • Simple initialization
    • #include  <gst/gst.h> 
    • int
    • main (int argc, char *argv[])
    • {
    •  const gchar *nano_str;
    •  guint major, minor, micro, nano;
    •  gst_init (&argc, &argv);  //初始化gstreamer
    •  gst_version (&major, &minor, &micro, &nano); //利用 gst_version 來獲得當前版本訊息
    •  if (nano == 1)
    •  nano_str = "(CVS)";
    •  else if (nano == 2)
    •  nano_str = "(Prerelease)";
    •  else
    •  nano_str = "";
    •  printf ("This program is linked against GStreamer %d.%d.%d %s\n",
    •  major, minor, micro, nano_str);
    •  return 0;
    • } 

    •  Initialisation using the GOption interface
    • #include <gst/gst.h>
    • int
    • main (int argc,
    •  char *argv[])
    • {
    •  gboolean silent = FALSE;
    •  gchar *savefile = NULL;
    •  GOptionContext *ctx;
    •  GError *err = NULL;
    •  GOptionEntry entries[] = {
    •  { "silent", 's', 0, G_OPTION_ARG_NONE, &silent,
    •  "do not output status information", NULL },
    •  { "output", 'o', 0, G_OPTION_ARG_STRING, &savefile,
    •  "save xml representation of pipeline to FILE and exit", "FILE" },
    •  { NULL }
    •  };
    •  ctx = g_option_context_new ("- Your application");
    •  g_option_context_add_main_entries (ctx, entries, NULL);
    •  g_option_context_add_group (ctx, gst_init_get_option_group ());
    •  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
    •  g_print ("Failed to initialize: %s\n", err->message);
    •  g_error_free (err); 
    •  return 1;
    •  }
    •  printf ("Run me with --help to see the Application options appended.\n");
    •  return 0;
    •  }

    Element
    所有(high-level)都源自 GstElement 物件。任何一个解碼器编码器、分離器、視頻/音頻输出元件都是一個 GstElement對象。Element 就像黑盒子,從Element 一端輸入數據,經過處理後,另外一端就會進行輸出。

    1. Source Element
    • 用於為pipeline產生數據,如從音效卡讀取資料。
    • 僅產生數據,不接收數據
    1. Filters(過濾器)、convertors(轉換器)、demuxers(分流器)、muxers(整流器)以及codecs(編解碼器)

    3. Sink elements(接收元件)

    • Creating a GstElement
    • 建立elements:呼叫函數  gst_element_factory_make (),這個函数使用一個已存在的factory name和一個新的element name来建立element。建立完之後,你可以用新的element name在(bin)中查询得到這個element。
    • 解除引用elements:呼叫 gst_object_unref () ,會使得一個element的引用數減少 1,任何element 在建立時其引用數為 1,當引用數為0時,element會銷毀。
    • #include <gst/gst.h>
    • int main (int   argc,  char *argv[])
      {
    •   GstElement *element;
    •   /* init GStreamer */
    •   gst_init (&argc, &argv);
    •   /* create element */
    •   element = gst_element_factory_make ("fakesrc", "source");
    •   if (!element) {
    •     g_print ("Failed to create element of type 'fakesrc'\n");
    •     return -1;
    •   }
    •   gst_object_unref (GST_OBJECT (element));
    •   return 0;
    • }
    gst_element_factory_find () 使用一個唯一的 factory name 来訪問一個 GstElementFactory 對象。
    • #include <gst/gst.h>
    • int main (int   argc,  char *argv[])
    • {
    •   GstElementFactory *factory;
    •   GstElement * element;
    •   /* init GStreamer */
    •   gst_init (&argc, &argv);
    •   /* create element, method #2 */
    •   factory = gst_element_factory_find ("fakesrc");
    •   if (!factory) {
    •     g_print ("Failed to find factory of type 'fakesrc'\n");
    •     return -1;
    •   }
    •   element = gst_element_factory_create (factory, "source");
    •   if (!element) {
    •     g_print ("Failed to create element, even though its factory exists!\n");
    •     return -1;
    •   }
    •   gst_object_unref (GST_OBJECT (element));
    •   return 0;
    • }

    • Using an element as a GObject
    使用 GObject 的方法可以對 GstElement 進行查詢、設置、獲取屬性的值。同樣的 GParamSpecs 也有支持。
    每個 GstElement 都從其 GstObject 繼承了至少一個“名字"屬性
    • gst_object_set_name:設定該屬性
    • gst_object_get_name:取得一個對象名字屬性
    • #include <gst/gst.h>
    • int main (int   argc,   char *argv[])
      {
        GstElement *element;
    •   gchar *name;
    •   /* init GStreamer */
    •   gst_init (&argc, &argv);
    •   /* create element */
    •   element = gst_element_factory_make ("fakesrc", "source");
    •   /* get name */
    •   g_object_get (G_OBJECT (element), "name", &name, NULL);
    •   g_print ("The name of the element is '%s'.\n", name);
    •   g_free (name);
    •   gst_object_unref (GST_OBJECT (element));
    •   return 0;
    • }

    gst-inspect 是一個用來查詢特定element 特性(properties)的實用工具。它也提供了諸如函數簡短介紹、參數的類型及其支持的範圍等信息。
    factory element 最有用處的功能為包含對element 所產生的pads 一個詳細描述及pads的功能(pads所支援的媒體類型)

    • Linking elements
    舉例來說,第一個為source,第二個為 ogg/vorbis 音頻解碼器,最終為音效卡。
    • #include <gst/gst.h>
    • int main (int   argc,  char *argv[])
      {
        GstElement *pipeline;
    •   GstElement *source, *filter, *sink;
    •   /* init */
    •   gst_init (&argc, &argv);
    •   /* create pipeline */
    •   pipeline = gst_pipeline_new ("my-pipeline");
    •   /* create elements */
    •   source = gst_element_factory_make ("fakesrc", "source");
    •   filter = gst_element_factory_make ("identity", "filter");
    •   sink = gst_element_factory_make ("fakesink", "sink");
    •   /* must add elements to pipeline before linking them */
    •   gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL);
    •   /* link */
    •   if (!gst_element_link_many (source, filter, sink, NULL)) {
    •     g_warning ("Failed to link elements!");
    •   }
    • [..]
    • }
    • note:連結不同element前,需確保這些element都被加在同一個bin之中。

    • Element States
    element被建立之後不會執行任何操作,需要改變其狀態才能做事情
    • GST_STATE_NULL:預設狀態。會回收所有被該element 佔用的資源
    • GST_STATE_READY:預備狀態。會得到所需的global resource,這些global resource 將被通過該element 的資料流使用
    • GST_STATE_PAUSED:暫停狀態。
    • GST_STATE_PLAYING:播放狀態。

    gst_element_set_state():改變element 的狀態

    Bins
    將一組有連接的element 組合成一個大的邏輯組件,不用對單個element 進行操作,只需要操作 bins
    Bins可以對包含在其中的element 進行管理,會計算數據怎樣流入Bins ,並對其制定一個最佳計畫(Gstreamer最難的步驟之一)
    pipeline:允許包含的element 進行 scheduling的普通容器

    • Creating a bin
    可用 gst_bin_new()和 gst_pipeline_new () 建立 bins
    利用 gst_bin_add() 在bins中增加element 和 gst_bin_remove() 移除bins中的element
    • #include <gst/gst.h>
    • int main (int   argc,   char *argv[])
      {
    •   GstElement *bin, *pipeline, *source, *sink;
    •   /* init */
    •   gst_init (&argc, &argv);
    •   /* create */
    •   pipeline = gst_pipeline_new ("my_pipeline");
    •   bin = gst_bin_new ("my_bin");
    •   source = gst_element_factory_make ("fakesrc", "source");
    •   sink = gst_element_factory_make ("fakesink", "sink");
    •   /* First add the elements to the bin */
    •   gst_bin_add_many (GST_BIN (bin), source, sink, NULL);
    •   /* add the bin to the pipeline */
    •   gst_bin_add (GST_BIN (pipeline), bin);
    •   /* link the elements */
    •   gst_element_link (source, sink);
    • [..]
    • }

    可以透過函數 gst_bin_get_list() 得到一個bins中所有element 的列表

    • Custom bins
    • int main (int   argc,   char *argv[])
      {
        GstElement *player;
    •   /* init */
    •   gst_init (&argc, &argv);
    •   /* create player */
    •   player = gst_element_factory_make ("oggvorbisplayer", "player");
    •   /* set the source audio file */
    •   g_object_set (player, "location", "helloworld.ogg", NULL);
    •   /* start playback */
    •   gst_element_set_state (GST_ELEMENT (player), GST_STATE_PLAYING);
    • [..]
    • }

    BUS
    採用自己的thread 機制將一個 pipeline thread 送到 application 之中,優點為使用 Gstreamer 時,application 不需要 thread-aware
    每個pipeline都預設一個 Bus,所以application 不需要在另外建立。

    使用Bus的兩種方法:
    • 執行 GLib/Gtk+ loop,然後對Bus進行偵測,當有新Message 出現 Bus會通知你
    • gst_bus_add_watch () :建立Messgae Hanlder 來偵測 pipeline,當pipeline 發消息到Bus 訊息處理器就會被觸發,檢測訊息信號類型決定哪些事件將被處理,當處理器刪除某個消息時,其返回值為TRUE
    •  gst_bus_add_signal_watch ()
    • 使用 gst_bus_peek () 或 gst_bus_poll () 自己偵測 Bus 消息

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

    瞭解 message handler 在mainloop 中thread context是很重要的。因為在Bus上 pipeline和 application之前的交互是非同步的,所以上述方法無法用於real-time的情況。如果需要real-time 的要求,就需要Gstreamer plug-in。

    note:如果使用預設的GLib主循環實現pipeline與application之間的交互,可以將"message"信號連結到Bus上。

    • Message Type
    Gstreamer 有幾種由Bus 傳遞的預定義 Message type。所有的 message 都有一個 message source、type和 timestamp
    • Error, warning and information notifications:被各個element 用來在必要的時後告知用戶現在pipeline的狀態
    • gst_message_parse_error ()、 _parse_warning () 和 _parse_info () 來提取訊息
    • End-of-stream notification:
    • Tags:
    • State-changes:
    • Buffering:
    • Element messages:
    • Application-specific messages:

    沒有留言:

    張貼留言