GStreamer学习笔记(七)——基础教程6:媒体格式和pad能力

原文:Basic tutorial 6: Media formats and Pad Capabilities (gstreamer.freedesktop.org)

目标

Pad功能是GStreamer的基本元素,尽管大多数时候它们是不可见的,因为框架会自动处理它们。本教材有点偏理论,它展示了:

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

概述

Pads

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

Pads可以支持多种caps(例如,视频接收器可以支持不同类型的RGB或YUV格式的视频),caps可以指定为范围(例如,音频接收器可以支持每秒1到48000个采样率)。但是,最终在Pad到Pad之间传递的信息必须只有一种明确指定的类型。通过一个称为协商的过程,两个链接的pad就一个公共类型达成一致,因此pad的Capabilities就固定了(它们只有一种类型,不包含范围)。下面示例代码的演练应该可以清晰说明。

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

作为应用程序开发人员,您通常会通过将element链接在一起来构建pipeline(如果使用诸如playbin之类的all-in-all元素,则使用较少)。在这种情况下,您需要知道element的Pad Caps(就像我们所熟悉的那样),或者,至少,当GStreamer以协商错误拒绝链接两个element时,您需要知道它们是什么。

Pads模板

Pad是从Pad模板创建的,Pad模板表示Pad可能拥有的所有功能。模板对于创建几个类似的Pad非常有用,并且还允许在元素之间早期拒绝连接:如果它们的Pad模板的功能没有公共子集(它们的交集是空的),则不需要进一步协商。

Pad模板可以看作是协商过程中的第一步。随着流程的发展,实际的pad将被实例化,它们的功能将被改进,直到它们被固定(或协商失败)。

Capabilities示例

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是element始终可用的sink(我们现在不讨论可用性)。它支持两种类型的媒体,都是整数格式的原始音频(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输出原始视频。它支持广泛的尺寸和帧率,以及一组YUV格式(花括号表示一个列表)。所有这些格式都表示图像平面的不同打包和子采样。

最后说明

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

请记住,有些element查询底层硬件以获得支持的格式,并相应地提供它们的Pad Caps(它们通常在进入READY或更高状态时执行此操作)。因此,所显示的上限可能因平台而异,甚至可能因一次执行而异(尽管这种情况很少见)。

本教程实例化了两个element(这一次,通过它们的工厂),展示了它们的Pad模板,链接它们并设置要播放的管道。在每次状态更改时,都会显示接收元素的Pad的Capabilities,因此您可以观察协商是如何进行的,直到Pad Caps固定为止。

一个简单的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;
}

代码走读

print_fieldprint_capsprint_pad_templates以一种人性化的格式显示Caps结构。如果您想了解GstCaps结构的内部组织,请阅读有关Pad Caps的GStreamer文档。

/* 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);
}

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

然后我们打印这些能力。

/* 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模板已经可以通过工厂访问,因此一旦工厂创建,它们就会被打印出来。

我们跳过pipeline的创建并开始,然后转到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;

这只是在每次pipeline状态发生变化时打印当前的Pad Caps。您应该在输出中看到,初始的Caps(Pad Template的Caps)是如何逐步精确,直到它们完全固定的(它们包含一个没有范围的单一类型)。

运行效果

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我会尽力回答你的问题。关于通过UDP传输音视频,我了解一些相关的知识,下面是一些学习笔记: 1. gstreamer是一个流媒体框架,用于创建、处理和播放多媒体流。它支持多种音视频格式,可以通过插件扩展功能。 2. 通过gstreamer可以使用UDP协议传输音视频数据。UDP协议是一种无连接的协议,不保证数据传输的可靠性和顺序性,但是传输效率高。 3. 首先需要创建一个gstreamer的pipeline,包括音视频源、编码器、UDP发送端等组件。例如: ``` gst-launch-1.0 -v filesrc location=test.mp4 ! decodebin ! x264enc ! rtph264pay ! udpsink host=192.168.1.100 port=5000 ``` 这个pipeline的作用是从test.mp4文件读取音视频流,解码后使用x264编码器进行压缩,然后使用rtph264pay将数据打包成RTP数据包,最后通过udpsink发送到指定的IP地址和端口。 4. 接收端需要创建一个gstreamer的pipeline,包括UDP接收端、解包器、解码器等组件。例如: ``` gst-launch-1.0 -v udpsrc port=5000 ! application/x-rtp, payload=96 ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! autovideosink ``` 这个pipeline的作用是从UDP端口5000接收音视频数据,使用rtpjitterbuffer解决网络抖动问题,使用rtph264depay将RTP数据包解包成原始的H.264数据流,然后使用avdec_h264解码器进行解码,最后使用autovideosink播放视频。 5. 在实际使用过程,还需要考虑数据的带宽限制、网络延迟等问题,以保证音视频传输的效果。 希望这些笔记能对你有帮助。如果你还有其他问题,可以继续问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最佳打工人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值