GStreamer基礎教程03 - 媒體類型與Pad

摘要

  在上一篇文章中,咱們介紹瞭如何將多個element鏈接起來構造一個pipline,進行數據傳輸。那麼GStreamer是經過何種方式保證element之間能正常的進行數據傳輸?今天就將介紹GStreamer是如何利用Pad來控制數據的傳輸。html

 

Pad

  咱們知道,pad是element之間的數據的接口,一個src pad只能與一個sink pad相連。每一個element能夠經過pad過濾數據,接收本身支持的數據類型。Pad經過Pad Capabilities(簡稱爲Pad Caps)來描述支持的數據類型。例如:ios

  • 表示分辨率爲300x200,幀率爲30fps的RGB視頻的Caps: 

   「video/x-raw,format=RGB,width=300,height=200,framerate=30/1」數據結構

  • 表示採樣位寬爲16位,採樣率44.1kHz,雙通道PCM音頻的Caps:

   「audio/x-raw,format=S16LE,rate=44100,channels=2」app

  • 或者直接描述編碼數據格式Voribis,VP8:

   「audio/x-vorbis」 "video/x-vp8"ide

  一個Pad能夠支持多種類型的Caps(好比一個video sink能夠同時支持RGB或YUV格式的數據),同時能夠指定Caps支持的數據範圍(好比一個audio sink能夠支持1~48k的採樣率)。可是,在一個Pipeline中,Pad之間所傳輸的數據類型必須是惟一的。GStreamer在進行element鏈接時,會經過協商(negotiation)的方式選擇一個雙方都支持的類型。工具

  所以,爲了能使兩個Element可以正確的鏈接,雙方的Pad Caps之間必須有交集,從而在協商階段選擇相同的數據類型,這就是Pad Caps的主要做用。在實際使用中,咱們能夠經過gst-inspect工具查看Element所支持的Pad Caps,從而才能知道在鏈接出錯時如何處理。源碼分析

Pad Templates(模板)

  咱們曾使用gst_element_factory_make()接口建立Element,這個接口內部也會先建立一個Element 工廠,再經過工廠方法建立一個Element。因爲大部分Element都須要建立相似的Pad,因而GStreame定義了Pad Template,Pad Template被包含中Element工廠中,在建立Element時,用於快速建立Pad。
  Pad Template包含了一個Pad所能支持的全部Caps。經過Pad Template,咱們能夠快速的判斷兩個pad是否可以鏈接(好比兩個elements都只提供了sink template,這樣的element之間是沒法鏈接的,這樣就不必進一步判斷Pad Caps)。ui

  因爲Pad Template屬於Element工廠,因此咱們能夠直接使用gst-inspect查看其屬性,但Element實際的Pad會根據Element所處的不一樣狀態來進行實例化,具體的Pad Caps會在協商後纔會被肯定。編碼

Pad Templates Capabilities例子

咱們看一個 「gst-inspect-1.0 alsasink」的例子(不一樣平臺會有差別):spa

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      audio/x-raw
                 format: S16LE
                 layout: interleaved
                   rate: [ 1, 48000 ]
               channels: [ 1, 2 ]
      audio/x-ac3
                 framed: true

 alsasink只提供了一個sink template,能夠建立sink pad,而且是一直存在的。支持兩種類型的音頻數據:16位的PCM(audio/x-raw),採樣率1~48k,1-2通道和AC3(audio/x-ac3)的幀數據。

 

再看一個 「gst-inspect-1.0 videotestsrc」的例子:

Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-raw
                 format: { I420, YV12, YUY2, UYVY, AYUV, RGBx, BGRx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, Y41B, Y42B, YVYU, Y444, v210, v216, NV12, NV21, NV16, NV24, GRAY8, GRAY16_BE, GRAY16_LE, v308, RGB16, BGR16, RGB15, BGR15, UYVP, A420, RGB8P, YUV9, YVU9, IYU1, ARGB64, AYUV64, r210, I420_10LE, I420_10BE, I422_10LE, I422_10BE, Y444_10LE, Y444_10BE, GBR, GBR_10LE, GBR_10BE }
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
              framerate: [ 0/1, 2147483647/1 ]
      video/x-bayer
                 format: { bggr, rggb, grbg, gbrg }
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
              framerate: [ 0/1, 2147483647/1 ]

videotestsrc只提供了一個src template用於建立src pad,pad支持多種格式,能夠經過參數指定輸出的數據類型或Caps Filter指定。

 

Pad Availability(有效性)

  上面的例子中顯示的Pad Template都是一直存在的(Availability: Always),建立的Pad也是一直有效的。但有些Element會根據輸入數據以及後續的Element動態增長或刪除Pad,所以GStreamer提供了3種Pad有效性的狀態:Always,Sometimes,On request。

Always Pad

  在element被初始化後就存在的pad,被稱爲always pad或static pad。


Sometimes Pad

  根據輸入數據的不一樣而產生的pad,被稱爲sometimes pad,常見於各類文件格式解析器。例如用於解析mp4文件的qtdemux:"gst-inspect-1.0 qtdemux"

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/quicktime
      video/mj2
      audio/x-m4a
      application/x-3gp

  SRC template: 'video_%u'
    Availability: Sometimes
    Capabilities:
      ANY

  SRC template: 'audio_%u'
    Availability: Sometimes
    Capabilities:
      ANY

  SRC template: 'subtitle_%u'
    Availability: Sometimes
    Capabilities:
      ANY

  只有咱們從mp4文件中讀取數據時,咱們才能知道這個文件中包含多少音頻,視頻,字幕,因此這些src pad都是sometimes pad。


Request Pad

  按需建立的pad被稱爲request pad,常見於合併或生成多路數據。例如,用於1到N轉換的tee:"gst-inspect-1.0 tee"

Pad Templates:
  ...
  SRC template: 'src_%u'
    Availability: On request
      Has request_new_pad() function: gst_tee_request_new_pad
    Capabilities:
      ANY

  當咱們須要將同一路視頻流同時進行顯示和存儲,這時候咱們就須要用到tee,在建立tee element的時候,咱們不知道pipeline須要多少個src pad,須要後續element來請求一個src pad。

 

示例代碼

  GStreamer提供了gst-inspect工具來查看element所提供的Pad Templates,但沒法查看element在不一樣狀態時其Pad所支持的數據類型,經過下面的代碼,咱們能夠看到Pad Caps在不一樣狀態下的變化。

#include <gst/gst.h>

/* Functions below print the Capabilities in a human-friendly format */
static gboolean print_field (GQuark field, const GValue * value, gpointer pfx) {
  gchar *str = gst_value_serialize (value);

  g_print ("%s  %15s: %s\n", (gchar *) pfx, g_quark_to_string (field), str);
  g_free (str);
  return TRUE;
}

static void print_caps (const GstCaps * caps, const gchar * pfx) {
  guint i;

  g_return_if_fail (caps != NULL);

  if (gst_caps_is_any (caps)) {
    g_print ("%sANY\n", pfx);
    return;
  }
  if (gst_caps_is_empty (caps)) {
    g_print ("%sEMPTY\n", pfx);
    return;
  }

  for (i = 0; i < gst_caps_get_size (caps); i++) {
    GstStructure *structure = gst_caps_get_structure (caps, i);

    g_print ("%s%s\n", pfx, gst_structure_get_name (structure));
    gst_structure_foreach (structure, print_field, (gpointer) pfx);
  }
}

/* Prints information about a Pad Template, including its Capabilities */
static void print_pad_templates_information (GstElementFactory * factory) {
  const GList *pads;
  GstStaticPadTemplate *padtemplate;

  g_print ("Pad Templates for %s:\n", gst_element_factory_get_longname (factory));
  if (!gst_element_factory_get_num_pad_templates (factory)) {
    g_print ("  none\n");
    return;
  }

  pads = gst_element_factory_get_static_pad_templates (factory);
  while (pads) {
    padtemplate = pads->data;
    pads = g_list_next (pads);

    if (padtemplate->direction == GST_PAD_SRC)
      g_print ("  SRC template: '%s'\n", padtemplate->name_template);
    else if (padtemplate->direction == GST_PAD_SINK)
      g_print ("  SINK template: '%s'\n", padtemplate->name_template);
    else
      g_print ("  UNKNOWN!!! template: '%s'\n", padtemplate->name_template);

    if (padtemplate->presence == GST_PAD_ALWAYS)
      g_print ("    Availability: Always\n");
    else if (padtemplate->presence == GST_PAD_SOMETIMES)
      g_print ("    Availability: Sometimes\n");
    else if (padtemplate->presence == GST_PAD_REQUEST)
      g_print ("    Availability: On request\n");
    else
      g_print ("    Availability: UNKNOWN!!!\n");

    if (padtemplate->static_caps.string) {
      GstCaps *caps;
      g_print ("    Capabilities:\n");
      caps = gst_static_caps_get (&padtemplate->static_caps);
      print_caps (caps, "      ");
      gst_caps_unref (caps);

    }

    g_print ("\n");
  }
}

/* Shows the CURRENT capabilities of the requested pad in the given element */
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
  GstPad *pad = NULL;
  GstCaps *caps = NULL;

  /* Retrieve pad */
  pad = gst_element_get_static_pad (element, pad_name);
  if (!pad) {
    g_printerr ("Could not retrieve pad '%s'\n", pad_name);
    return;
  }

  /* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */
  caps = gst_pad_get_current_caps (pad);
  if (!caps)
    caps = gst_pad_query_caps (pad, NULL);

  /* Print and free */
  g_print ("Caps for the %s pad:\n", pad_name);
  print_caps (caps, "      ");
  gst_caps_unref (caps);
  gst_object_unref (pad);
}

int main(int argc, char *argv[]) {
  GstElement *pipeline, *source, *sink;
  GstElementFactory *source_factory, *sink_factory;
  GstBus *bus;
  GstMessage *msg;
  GstStateChangeReturn ret;
  gboolean terminate = FALSE;

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

  /* Create the element factories */
  source_factory = gst_element_factory_find ("audiotestsrc");
  sink_factory = gst_element_factory_find ("autoaudiosink");
  if (!source_factory || !sink_factory) {
    g_printerr ("Not all element factories could be created.\n");
    return -1;
  }

  /* Print information about the pad templates of these factories */
  print_pad_templates_information (source_factory);
  print_pad_templates_information (sink_factory);

  /* Ask the factories to instantiate actual elements */
  source = gst_element_factory_create (source_factory, "source");
  sink = gst_element_factory_create (sink_factory, "sink");

  /* Create the empty pipeline */
  pipeline = gst_pipeline_new ("test-pipeline");

  if (!pipeline || !source || !sink) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }

  /* Build the pipeline */
  gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
  if (gst_element_link (source, sink) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }

  /* Print initial negotiated caps (in NULL state) */
  g_print ("In NULL state:\n");
  print_pad_capabilities (sink, "sink");

  /* Start playing */
  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state (check the bus for error messages).\n");
  }

  /* Wait until error, EOS or State Change */
  bus = gst_element_get_bus (pipeline);
  do {
    msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS |
        GST_MESSAGE_STATE_CHANGED);

    /* Parse message */
    if (msg != NULL) {
      GError *err;
      gchar *debug_info;

      switch (GST_MESSAGE_TYPE (msg)) {
        case GST_MESSAGE_ERROR:
          gst_message_parse_error (msg, &err, &debug_info);
          g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
          g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
          g_clear_error (&err);
          g_free (debug_info);
          terminate = TRUE;
          break;
        case GST_MESSAGE_EOS:
          g_print ("End-Of-Stream reached.\n");
          terminate = TRUE;
          break;
        case GST_MESSAGE_STATE_CHANGED:
          /* We are only interested in state-changed messages from the pipeline */
          if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {
            GstState old_state, new_state, pending_state;
            gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
            g_print ("\nPipeline state changed from %s to %s:\n",
                gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
            /* Print the current capabilities of the sink element */
            print_pad_capabilities (sink, "sink");
          }
          break;
        default:
          /* We should not reach here because we only asked for ERRORs, EOS and STATE_CHANGED */
          g_printerr ("Unexpected message received.\n");
          break;
      }
      gst_message_unref (msg);
    }
  } while (!terminate);

  /* Free resources */
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  gst_object_unref (source_factory);
  gst_object_unref (sink_factory);
  return 0;
}

  將源碼保存爲basic-tutorial-3.c,執行下列命令可獲得編譯結果:

$ gcc basic-tutorial-3.c -o basic-tutorial-3 `pkg-config --cflags --libs gstreamer-1.0`

 

源碼分析

輸出可讀信息

  print_field, print_caps and print_pad_templates_information實現相似功能,打印GStreamer的數據結構,能夠查看相應GStreamer GstCaps 接口瞭解更多信息。

/* Shows the CURRENT capabilities of the requested pad in the given element */
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
  GstPad *pad = NULL;
  GstCaps *caps = NULL;

  /* Retrieve pad */
  pad = gst_element_get_static_pad (element, pad_name);
  if (!pad) {
    g_printerr ("Could not retrieve pad '%s'\n", pad_name);
    return;
  }

  /* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */
  caps = gst_pad_get_current_caps (pad);
  if (!caps)
    caps = gst_pad_query_caps (pad, NULL);

  /* Print and free */
  g_print ("Caps for the %s pad:\n", pad_name);
  print_caps (caps, "      ");
  gst_caps_unref (caps);
  gst_object_unref (pad);
}

  由於咱們使用的source和sink都具備static(always)pad,因此這裏使用gst_element_get_static_pad()獲取Pad, 其餘狀況可使用gst_element_foreach_pad()或gst_element_iterate_pads()獲取動態建立的Pad。
  接着使用gst_pad_get_current_caps()獲取pad當前的caps,根據不一樣的element狀態會有不一樣的結果,甚至可能不存在caps。若是沒有,咱們經過gst_pad_query_caps()獲取當前能夠支持的caps,當element處於NULL狀態時,這個caps爲Pad Template所支持的caps,其值可隨狀態變化而變化。

獲取Element工廠

/* Create the element factories */
source_factory = gst_element_factory_find ("audiotestsrc");
sink_factory = gst_element_factory_find ("autoaudiosink");
if (!source_factory || !sink_factory) {
  g_printerr ("Not all element factories could be created.\n");
  return -1;
}

/* Print information about the pad templates of these factories */
print_pad_templates_information (source_factory);
print_pad_templates_information (sink_factory);

/* Ask the factories to instantiate actual elements */
source = gst_element_factory_create (source_factory, "source");
sink = gst_element_factory_create (sink_factory, "sink");

  在使用gst_element_factory_make()接口建立element時,應用不須要關心element工廠。在這裏,因爲Pad Template數據Element工程,所以咱們首先根據工廠名建立了相應工廠實例(GstElementFactory ),再由其獲取Pad Template以及建立element。
  此處使用gst_element_factory_find()查找"audiotestsrc"工廠,再經過gst_element_factory_create()建立source element。之前使用的gst_element_factory_make()是gst_element_factory_find() + gst_element_factory_create()的簡化版。

處理State-Changed消息

  Pipeline的建立過程與其餘示例相同,此例新增了狀態變化的處理。

case GST_MESSAGE_STATE_CHANGED:
  /* We are only interested in state-changed messages from the pipeline */
  if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {
    GstState old_state, new_state, pending_state;
    gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
    g_print ("\nPipeline state changed from %s to %s:\n",
        gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
    /* Print the current capabilities of the sink element */
    print_pad_capabilities (sink, "sink");
  }
  break;

  由於咱們在gst_bus_timed_pop_filtered()中加入了GST_MESSAGE_STATE_CHANGED,因此咱們會收到狀態變化的消息。在狀態變化時,輸出sink element的pad caps中當前狀態的信息。

 

輸出分析

Pad Templates for Audio test source:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      audio/x-raw
                 format: { S16LE, S32LE, F32LE, F64LE }
                 layout: interleaved
                   rate: [ 1, 2147483647 ]
               channels: [ 1, 2 ]

Pad Templates for Auto audio sink:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      ANY

  首先是「audiotestsrc」和「autoaudiosink」的pad templates信息,這個與gst-inspect的輸出相同。

 

In NULL state:
Caps for the sink pad:
      ANY

  NULL狀態爲Element的初始化狀態,此時,「autoaudiosink」的sink pad caps與Pad Template相同,支持全部的格式。

 

Pipeline state changed from NULL to READY:
Caps for the sink pad:
      audio/x-raw
                 format: { S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, S24LE, S24BE, S24_32LE, S24_32BE, U8 }
                 layout: interleaved
                   rate: [ 1, 2147483647 ]
               channels: [ 1, 32 ]
      audio/x-alaw
                   rate: [ 1, 2147483647 ]
               channels: [ 1, 32 ]
      audio/x-mulaw
                   rate: [ 1, 2147483647 ]
               channels: [ 1, 32 ]

  狀態從NULL轉到READY時,GStreamer會獲取音頻輸出設備所支持的全部類型,這裏能夠看到sink pad caps列出了輸出設備所能支持的類型。

 

Pipeline state changed from READY to PAUSED:
Caps for the sink pad:
      audio/x-raw
                 format: S16LE
                 layout: interleaved
                   rate: 44100
               channels: 1

Pipeline state changed from PAUSED to PLAYING:
Caps for the sink pad:
      audio/x-raw
                 format: S16LE
                 layout: interleaved
                   rate: 44100
               channels: 1

  狀態從READY轉到PAUSED時,GStreamer會協商一個全部element都支持的類型。當進入PLAYING狀態時,sink會採用協商後的類型進行數據傳輸。

 

總結

在本教程中,咱們掌握了:

  • 什麼是Pad Capabilities 和 Pad Template Capabilities。
  • Pad有效性的類別。
  • 如何經過gst_pad_get_current_caps() 和 gst_pad_query_caps()獲取當前的caps。
  • Pad Capabilities在element不一樣狀態下的變化。
  • 如何使用gst-inspect工具查看element的Pad Caps。


引用

https://gstreamer.freedesktop.org/documentation/tutorials/basic/media-formats-and-pad-capabilities.html?gi-language=c
https://gstreamer.freedesktop.org/documentation/tutorials/basic/multithreading-and-pad-availability.html
https://gstreamer.freedesktop.org/documentation/gstreamer/gstcaps.html?gi-language=c

 

做者: John.Leng
本文版權歸做者全部,歡迎轉載。商業轉載請聯繫做者得到受權,非商業轉載請在文章頁面明顯位置給出原文鏈接.
相關文章
相關標籤/搜索