【Gstreamer 06】基础教程之 媒体格式和Pad功能

官网地址

Basic tutorial 6: Media formats and Pad Capabilities

一、目标

Pad Capabilities(Pad 能力)是 GStreamer 中的一个基本元素,尽管大多数情况下它们是隐形的,因为框架会自动处理它们。这个稍显理论化的教程展示了以下内容:

  • 什么是 Pad Capabilities。
  • 如何获取它们。
  • 何时获取它们。
  • 为什么你需要了解它们。

二、介绍

2.1、Pads

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

      Pads 可以支持多种 Capabilities(例如,一个视频接收器可以支持不同类型的 RGB 或 YUV 格式的视频),并且 Capabilities 可以指定为范围(例如,一个音频接收器可以支持从 1 到 48000 样本每秒的采样率)。然而,实际在 Pad 之间传输的信息必须具有一种明确指定的类型。通过一个称为“协商”的过程,两个链接的 Pad 会达成一种共同的类型,因此 Pad 的 Capabilities 会被固定下来(它们只有一种类型,并且不包含范围)。下面示例代码的逐步解析应该会让这一切更加清晰。

      为了让两个元素能够链接在一起,它们必须共享一个共同的 Capabilities 子集(否则它们无法理解彼此)。这是 Capabilities 的主要目标。

     作为应用程序开发者,你通常通过将元素链接在一起来构建管道(如果你使用像 playbin 这样的全能元素,则较少需要这样做)。在这种情况下,你需要了解元素的 Pad Caps(这是它们的俗称),或者至少当 GStreamer 因协商错误而拒绝链接两个元素时,知道它们是什么。

2.2、Pad templates(模板)

     Pads 是从 Pad 模板创建的,模板指示了一个 Pad 可能具有的所有 Capabilities。模板对于创建多个相似的 Pad 非常有用,并且还允许在元素之间早期拒绝连接:如果它们的 Pad 模板的 Capabilities 没有共同的子集(它们的交集为空),则无需进一步协商。

     Pad 模板可以看作是协商过程的第一步。随着过程的推进,实际的 Pad 会被实例化,并且它们的 Capabilities 会被细化,直到它们被固定(或协商失败)。

2.3、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 是一个接收端(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 表示这个源 Pad 输出原始视频。它支持广泛的尺寸和帧率,以及一组 YUV 格式(花括号表示列表)。所有这些格式表示图像平面的不同打包和子采样方式。

2.4、最后说明

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

        需要注意的是,某些元素会查询底层硬件以获取支持的格式,并相应地提供它们的 Pad Caps(它们通常在进入 READY 状态或更高状态时执行此操作)。因此,显示的 Caps 可能会因平台而异,甚至在不同的运行之间也会有所不同(尽管这种情况很少见)。

      本教程实例化了两个元素(这次是通过它们的工厂),展示了它们的 Pad 模板,将它们链接起来并将管道设置为播放状态。在每次状态变化时,都会显示接收端元素 Pad 的 Capabilities,以便你可以观察协商过程,直到 Pad Caps 被固定。

三、一个简单的Pad功能示例

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

basic-tutorial-6.c

#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 安装库(Install GStreamer on Ubuntu or Debian)

apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

其他的系统看官网教程。

Installing on Mac OS X

执行编译

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

必需的库:gstreamer-1.0

四、代码解析

print_field、print_caps 和 print_pad_templates 函数只是以人类友好的格式显示能力结构。如果你想了解 GstCaps 结构的内部组织,请阅读 GStreamer 文档中关于 Pad Caps 的部分。

/* 显示给定元素中指定 Pad 的当前能力 */
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
  GstPad *pad = NULL;
  GstCaps *caps = NULL;

  /* 获取 Pad */
  pad = gst_element_get_static_pad (element, pad_name);
  if (!pad) {
    g_printerr ("无法获取 Pad '%s'\n", pad_name);
    return;
  }

  /* 获取已协商的 Caps(如果协商未完成,则获取可接受的 Caps) */
  caps = gst_pad_get_current_caps (pad);
  if (!caps)
    caps = gst_pad_query_caps (pad, NULL);

  /* 打印并释放 */
  g_print ("%s Pad 的 Caps:\n", pad_name);
  print_caps (caps, "      ");
  gst_caps_unref (caps);
  gst_object_unref (pad);
}

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

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

       然后我们打印这些能力。

/* 创建元素工厂 */
source_factory = gst_element_factory_find ("audiotestsrc");
sink_factory = gst_element_factory_find ("autoaudiosink");
if (!source_factory || !sink_factory) {
  g_printerr ("无法创建所有元素工厂。\n");
  return -1;
}

/* 打印这些工厂的 Pad 模板信息 */
print_pad_templates_information (source_factory);
print_pad_templates_information (sink_factory);

/* 让工厂实例化实际元素 */
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 模板已经可以通过工厂访问,因此在创建工厂后立即打印它们。

     我们跳过管道的创建和启动,直接进入状态更改消息的处理:

case GST_MESSAGE_STATE_CHANGED:
  /* 我们只对来自管道的状态更改消息感兴趣 */
  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 ("\n管道状态从 %s 更改为 %s:\n",
        gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
    /* 打印接收端元素的当前能力 */
    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() 检索它们。
  • 它们的含义取决于管道的状态(最初它们表示所有可能的能力,后来它们表示 Pad 当前协商的能力)。
  • Pad Caps 对于预先知道两个元素是否可以链接在一起很重要。
  • 可以使用 基础教程10:GStreamer工具 中描述的 gst-inspect-1.0 工具找到 Pad Caps。

下一个教程展示了如何手动将数据注入和提取到 GStreamer 管道中。

请记住,在本页面附件中,你可以找到教程的完整源代码以及构建它所需的任何辅助文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值