基本教程6:Media formats and Pad Capabilities(媒体格式和Pad功能)
目标
Pad的Capabilities时GStreamer的一个基本元素,由于大部分时间都由框架自动处理它们,所以用户很少感觉到它们的存在。这篇略微理论化的教程将展示:
-
什么是Pad Capabilities。
-
如何检索它们。
-
何时检索它们。
-
为什么用户需要了解他们。
介绍
Pads
正如已经展示的那样,Pads允许信息进入和离开一个元素。因此,一个Pad的Capabilities(或简称Caps)指定了可以通过该Pad传输的信息类型。例如,“分辨率为320x200像素、每秒30帧的RGB视频”,或者“每样本16位、5.1声道、每秒44100样本的音频”,甚至包括像mp3或h264这样的压缩格式。
Pads可以支持多种Capabilities(例如,一个视频接收器可以支持不同类型的RGB或YUV格式的视频),并且Capabilities可以指定为范围(例如,一个音频接收器可以支持从1到48000样本每秒的采样率)。然而,从Pad到Pad实际传输的信息必须只有一种明确指定的类型。通过一个称为协商的过程,两个链接的Pads达成一致的共同类型,因此Pads的Capabilities变得固定(它们只有一种类型,不包含范围)。下面示例代码的讲解应该能使这一切变得清晰。
为了两个元素能够链接在一起,它们必须共享一个共同的Capabilities子集(否则它们根本无法相互理解)。这是Capabilities的主要目标。
作为一名应用开发者,你通常会通过链接元素来构建管道(如果你使用像playbin这样的全能元素,这种情况会少一些)。在这种情况下,你需要了解你的元素的Pad Caps(正如它们被亲切地称呼的),或者至少当GStreamer拒绝将两个元素通过协商错误链接时,知道它们是什么。
Pad 模板
Pads是根据Pad模板创建的,这些模板指示了一个Pad可能拥有的所有可能的Capabilities。模板对于创建几个相似的Pad很有用,也允许在元素之间提前拒绝连接:如果它们的Pad模板的Capabilities没有共同的子集(它们的交集为空),则无需进一步协商。
Pad模板可以被视为协商过程的第一步。随着过程的进行,实际的Pad被实例化,并且它们的Capabilities被细化,直到它们被固定(或协商失败)。
Capabilities 示例
SINK template: 'sink'Availability: AlwaysCapabilities:audio/x-rawformat: S16LErate: [ 1, 2147483647 ]channels: [ 1, 2 ]audio/x-rawformat: U8rate: [ 1, 2147483647 ]channels: [ 1, 2 ]
这是一个element的永久sink pad
(暂时不讨论Availablility
)。它支持2种媒体格式,都是音频的原始数据audio/x-raw-int
,16位的小端序符号数和8位的无符号数。方括号表示一个范围,例如,频道channels
的范围是1到2.
SRC template: 'src'Availability: AlwaysCapabilities:video/x-rawwidth: [ 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:GStreamer工具中描述gst-inspect-1.0
工具
来了解任何GStreamer元素的Caps。
请记住,有些元素会查询底层硬件以获取支持的格式,并据此提供它们的Pad Caps(它们通常在进入READY状态或更高状态时这样做)。因此,显示的caps可能会因平台而异,甚至可能因一次执行而异(尽管这种情况很少见)。
本教程实例化了两个元素(这次是通过它们的工厂),显示了它们的Pad模板,链接了它们并设置了管道播放。在每个状态变化时,都会显示接收元素Pad的Capabilities,因此你可以观察到协商如何进行,直到Pad Caps被固定。
一个简单的Pad Capabilities示例
将此代码复制到名为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);elseg_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");elseg_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功能在不同时间瞬间的信息。
所需库:
gstreamer-1.0
工作流
print_pad_capabilities
, print_caps
,print_pad_templates
以一种友好的形式简单展示了capabilities的结构体。假如你想了解GstCaps的内部结构,请阅读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);
}
gst_element_get_static_pad()
用于根据Pad name检索给定element的pad结构体,这个pad是静态的,因为它会一直存在。关于Pad的的更多内容请阅读GstPad。
获取pad之后我们调用gst_pad_get_current_caps()
来获取这个pad当前的capabilities,可能是固定的也可能不是,这取决于当前“协商”过程的状态。pad甚至可能还未生成capabilities,在这种情况下,我们调用gst_pad_query_caps()
来获取一个当前可接受的Pad Capabilities。这个当前可接受的Caps是Pad Template在NULL
状态下的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()
来创建GStreamer element并且跳过了factories的讨论,可以明确的是一个GstElementFactory
管理着一个特定类型的GStreamer element的实例化,以factory name区分(可以理解为一个GstElementFactory
代表一个插件,一个插件可以实例化多个GStreamer element对象)。
gst_element_factory_make()
是gst_element_factory_create()
和gst_element_factory_create()
的简洁形式。
通过工程,Pad模板实际上已经可以访问了,所以factories一建立我们立刻打印这些信息。
我们跳过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;
上述代码将在每次pipeline状态变化时打印autoaudiosink
的sink pad
。在输出中你能看到一个最初的caps (Pad Template的caps)是如何逐步完善的知道它们完全固定(Caps只包含一个无范围的类型)。
结论
本教程显示:
-
什么是Pad Capabilities和Pad Template Capabilities。
-
如何使用
gst_pad_get_current_caps()
和get_pad_query_caps()
检索它们。 -
它们根据pipeline的不同状态有不同的含义(在初始化时它们表示所有可能的Capabilities,在这之后表示当前Pad的特定Caps)。
-
事先知道elements支持的Caps类型对于elements的连接至关重要。
-
可以使用所描述
gst-inspect-1.0
工具找到Pad Caps 在基本教程10:GStreamer工具。