跳转至

媒体格式和Pad Capabilities

目标

Pad Capabilities 是 GStreamer 的一个基本元素,尽管大多数时候它们是不可见的,因为框架会自动处理它们。这个有点理论性的教程展示了:

  • 什么是 Pad 功能。
  • 如何检索它们。
  • 何时检索它们。
  • 为什么你需要了解它们。

介绍

Pads

如前所述,Pads 允许信息进入和离开元素。然后,Pad 的 Capabilities(或简称 Caps)指定什么样的信息可以通过 Pad。例如,“分辨率为 320x200 像素和每秒 30 帧的 RGB 视频”,或“每个样本音频 16 位,每秒 5.1 个样本的 5.1 个通道”,甚至是 mp3 或 h264 等压缩格式。

Pads可以支持多个 Capabilities(例如,视频接收器可以支持不同类型的 RGB 或 YUV 格式的视频),并且可以将功能指定为范围 (例如,音频接收器可以支持每秒 1 到 48000 个样本的采样率)。但是,从 Pad 到 Pad 传输的实际信息必须只有一个明确指定的类型。通过一个称为协商的过程,两个链接的 Pad 就一个公共类型达成一致,因此 Pad 的功能变得固定(它们只有一个类型,不包含范围)。下面示例代码的演练应该清楚地说明了这一切。

为了将两个元素链接在一起,它们必须共享一个公共的 Capabilities 子集(否则它们不可能相互理解)。这是 Capabilities 的主要目标。

作为应用程序开发人员,您通常会通过将元素链接在一起来构建管道(如果您使用 playbin 等多合一元素,则程度会较小)。在这种情况下,您需要知道元素的 Pad Caps(正如人们所熟悉的称呼),或者至少,当 GStreamer 拒绝链接两个具有协商错误的元素时,它们是什么。

Pad 模版

Pads是从Pad模板创建的,它指示Pad可能具有的所有可能功能。模板对于创建多个类似的 Pads 很有用,并且还允许元素之间的连接提前被拒绝: 如果它们的 Pad Templates 的 Capabilities 没有公共子集(它们的交集为空),则无需进一步协商。

Pad Templates 可以被视为协商过程的第一步。随着流程的发展,实际的 Pads 会被实例化并优化它们的 Capability,直到它们被修复(或协商失败)。

功能示例

SINK template: 'sink'
  Availability: Always
  Capabilities:
    audio/x-raw
               format: S16LE
                 rate: [ 1, 2147483647 ]
             channels: [ 1, 2 ]
    audio/x-raw
               format: U8
                 rate: [ 1, 2147483647 ]
             channels: [ 1, 2 ]

这个pad是一个在元件上始终可用的水槽(我们现在不讨论可用性)。它支持两种媒体,都是整数格式 (audio/x-raw) 的原始音频:有符号的 16 位小端和无符号的 8 位。方括号表示一个范围:例如,通道数从 1 到 2 不等。

SRC template: 'src'
  Availability: Always
  Capabilities:
    video/x-raw
                width: [ 1, 2147483647 ]
               height: [ 1, 2147483647 ]
            framerate: [ 0/1, 2147483647/1 ]
               format: { I420, NV12, NV21, YV12, YUY2, Y42B, Y444, YUV9, YVU9, Y41B, Y800, Y8, GREY, Y16 , UYVY, YVYU, IYU1, v308, AYUV, A420 }

video/x-raw 表示此 Source Pad 输出 Raw 视频。它支持各种尺寸和帧速率,以及一组 YUV 格式(大括号表示列表)。所有这些格式都表示图像平面的不同打包和子采样。

最后的评论

您可以使用基本教程 10:GStreamer 工具中描述的 gst-inspect-1.0 工具来了解任何 GStreamer 元素的 Cap。

请记住,某些元素会查询底层硬件以获取支持的格式,并相应地提供其 Pad Caps(它们通常在进入 READY 或更高状态时执行此作)。因此,显示的上限可能因平台而异,甚至因执行而异(即使这种情况很少见)。

本教程实例化两个元素(这次通过它们的工厂),显示它们的 Pad Templates,链接它们并设置要播放的管道。每次状态更改时,都会显示 sink 元素的 Pad 的 Capabilities ,因此您可以观察协商如何进行,直到 Pad Cap 得到修复。

一个简单的 Pad 示例

将此代码复制到名为 basic-tutorial-6.c 的文本文件中(或在 GStreamer 安装中找到它)。

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

需要帮助?

如果您在编译此代码时需要帮助,请参阅针对您的平台构建教程部分:Linux、Mac OS X 或 Windows,或在 Linux 上使用以下特定命令:

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

如果您需要运行此代码的帮助,请参阅适用于您的平台的运行教程部分:Linux、Mac OS X 或 Windows 的

本教程仅显示不同时间时刻的 Pad 功能相关信息。

请记住,没有延迟管理(缓冲),因此在连接速度较慢时,电影可能会在几秒钟后停止。了解基本教程 12:流式处理如何解决此问题。

必需安装的库:gstreamer-1.0

代码走查

print_field、print_caps 和 print_pad_templates 以用户友好的格式简单地显示功能结构。如果您想了解 GstCaps 结构的内部组织,请阅读有关 Pad Cap 的 GStreamer 文档。

gst_element_get_static_pad() 从给定的元素中检索命名的 Pad。这个 Pad 是静态的,因为它始终存在于元素中。要了解有关 Pad 可用性的更多信息,请阅读有关 Pads 的 GStreamer 文档。

然后我们调用 gst_pad_get_current_caps() 来检索 Pad 的当前 Capabilities,该 Capabilities 可以是固定的,也可以是不固定的,具体取决于协商过程的状态。它们甚至可能不存在,在这种情况下,我们调用 gst_pad_query_caps() 来检索当前可接受的 Pad Capabilities。当前可接受的 Caps 将是处于 NULL 状态的 Pad 模板的 Caps,但在以后的状态中可能会发生变化,因为可能会查询实际的硬件 Capabilities。

然后,我们打印这些功能。

/* 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() 创建了元素,跳过了工厂的讨论,但现在我们将这样做。GstElementFactory 负责实例化由其工厂名称标识的特定类型的元素。

您可以使用 gst_element_factory_find() 创建“videotestsrc”类型的工厂,然后使用它使用 gst_element_factory_create() 实例化多个“videotestsrc”元素。gst_element_factory_make() 实际上是 gst_element_factory_find()+ gst_element_factory_create() 的快捷方式。

Pad Templates 已经可以通过工厂访问,因此一旦工厂创建,它们就会被打印出来。

我们跳过管道创建和启动,转到 State-Changed 消息处理:

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;

这只是在每次管道状态更改时打印当前的 Pad Caps。你应该看到,在输出中,初始 Caps(Pad 模板的 Caps)是如何逐渐细化的,直到它们完全固定(它们包含没有范围的单个类型)。

总结

本教程展示了:

  • 什么是 Pad Capabilities 和 Pad Template Capabilities。
  • 如何使用 gst_pad_get_current_caps() 或 gst_pad_query_caps() 检索它们。
  • 根据 pipeline 的状态,它们具有不同的含义(最初它们表示所有可能的 Capability,后来它们表示当前协商的 Pad 的 Caps)。
  • 如果两个元素可以链接在一起,事先了解 Pad Caps 很重要。
  • 该焊盘帽可以使用基本教程 10:GStreamer 工具中描述的 gst-inspect-1.0 工具找到。

下一个教程将介绍如何手动将数据注入 GStreamer 管道并从中提取数据。

请记住,本页附带了本教程,您应该可以找到本教程的完整源代码以及构建本教程所需的任何附件。很高兴您来到这里,很快再见!

评论