目标
Pad的Capabilities是一个GStreamer element的基础,因为framework大部分时间是自动处理的,所以我们几乎感觉不到它的存在。本教程比较偏向原理,介绍了:
什么是Pad Capabilities
如何获得这个东西
什么时候应该去获得这个东西
为什么你需要了解他们
介绍
Pads
Pads允许信息进入或者离开一个element——就像曾经展示过得一样。这个Capabilities(或者简单地叫做Caps)就是指定哪些信息可以通过Pad来传输。例如:“”RGB视频,尺寸为320x200并且每秒30帧“或者”16位的音频采样,5.1声道,每秒采样44.1k“甚至可以是类似于mp3/h264之类的压缩格式。
Pads支持多重Capabilities(比如,一个视频的sink可以支持RGB输出或者YUV输出),Capabilities可以指定一个范围而不必须是一个特定值(比如,一个音频sink可以支持从1~48000的采样率)。然而,数据从一个pad流向另一个pad的时候,必须是一个双方都能支持的格式。某一种数据形式是两个pad都能支持的,这样Pads的Capabilities就固定下来(只有一个钟个数,并且不再是一个数据区间了),这个过程被称为协商。下面的例子会更清楚的说明这一点。
为了两个element可以连接,他们必须有一个共同的Capabilities子集(否则它们肯定不能互相连接)。这就是Capabilities存在的主要目的。
作为一个应用开发者,我们通常都是用连接一个个element的方法来建立pipeline的。在这个例子中,你需要了解你使用的element的Pad的Caps。
Pad模板
Pad是由Pad模板创建的,模板里面会列出一个Pad所有可能的Capabilities。模板对于创建几个相似的Pad是很有帮助的,但也会比较早就判断出两个element是否可以相连:如果连两个Pad的模板都不具备共同的子集的化,就没必要进行更深层的协商了。
Pad模板检查是协商流程的第一步。随着流程的逐步深入,Pad会正式初始化,也会确定他们的Capability(除非协商失败了)。
Capabilities例子
-
SINK template: 'sink'
-
Availability: Always
-
Capabilities:
-
audio/x-raw-int
-
signed: true
-
16
-
depth: 16
-
rate: [ 1, 2147483647 ]
-
channels: [ 1, 2 ]
-
audio/x-raw-int
-
signed: false
-
8
-
depth: 8
-
rate: [ 1, 2147483647 ]
-
channels: [ 1, 2 ]
这是一个element的永久sink pad。它支持2种媒体格式,都是音频的原始数据(audio/x-raw-int),16位的符号数和8位的无符号数。方括号表示一个范围,例如,频道(channels)的范围是1到2.
-
SRC template: 'src'
-
Availability: Always
-
Capabilities:
-
video/x-raw-yuv
-
[ 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-yur表示这个source pad用YUV格式输出视频。它支持一个很广的维数和帧率,一系列的YUV格式(用花括号列出了)。所有这些格式都显示不同的图像打包和子采样程度。
最后的总结
你可以使用gst-inspect-0.10这个工具(在后面教程中会介绍)来查看一下element的Caps。
记住有些element会查看底层的硬件来确定支持的格式和能提供的Pad的Caps(通常在进入READY或更后面的状态)。因此,这个显示的Caps由于平台的不同是不一样的,甚至可能在每一次运行都不相同(这种情况很少)。
本教程会创建2个element,显示它们各自的Pad模板,连接起来并把pipeline设置成PLAY状态。在每个状态切换的过程中,sink element的Pad的Capabilities会显示出来,这样你就可以观察到协商过程是如何进行的,最后Pad的Caps是怎么定下来的。
一个简单地Pad Capabilities例子
-
#include <gst/gst.h>
-
-
-
static gboolean print_field (GQuark field, const GValue * value, gpointer pfx) {
-
gchar *str = gst_value_serialize (value);
-
-
g_print ("%s %15s: %s
", (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
", pfx);
-
return;
-
}
-
if (gst_caps_is_empty (caps)) {
-
g_print ("%sEMPTY
", pfx);
-
return;
-
}
-
-
for (i = 0; i < gst_caps_get_size (caps); i++) {
-
GstStructure *structure = gst_caps_get_structure (caps, i);
-
-
g_print ("%s%s
", pfx, gst_structure_get_name (structure));
-
gst_structure_foreach (structure, print_field, (gpointer) pfx);
-
}
-
}
-
-
-
static void print_pad_templates_information (GstElementFactory * factory) {
-
const GList *pads;
-
GstStaticPadTemplate *padtemplate;
-
-
g_print ("Pad Templates for %s:
", gst_element_factory_get_longname (factory));
-
if (!factory->numpadtemplates) {
-
g_print (" none
");
-
return;
-
}
-
-
pads = factory->staticpadtemplates;
-
while (pads) {
-
padtemplate = (GstStaticPadTemplate *) (pads->data);
-
pads = g_list_next (pads);
-
-
if (padtemplate->direction == GST_PAD_SRC)
-
g_print (" SRC template: '%s'
", padtemplate->name_template);
-
else if (padtemplate->direction == GST_PAD_SINK)
-
g_print (" SINK template: '%s'
", padtemplate->name_template);
-
else
-
g_print (" UNKNOWN!!! template: '%s'
", padtemplate->name_template);
-
-
if (padtemplate->presence == GST_PAD_ALWAYS)
-
g_print (" Availability: Always
");
-
else if (padtemplate->presence == GST_PAD_SOMETIMES)
-
g_print (" Availability: Sometimes
");
-
else if (padtemplate->presence == GST_PAD_REQUEST) {
-
g_print (" Availability: On request
");
-
} else
-
g_print (" Availability: UNKNOWN!!!
");
-
-
if (padtemplate->static_caps.string) {
-
g_print (" Capabilities:
");
-
print_caps (gst_static_caps_get (&padtemplate->static_caps), " ");
-
}
-
-
g_print ("
");
-
}
-
}
-
-
-
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
-
GstPad *pad = NULL;
-
GstCaps *caps = NULL;
-
-
-
pad = gst_element_get_static_pad (element, pad_name);
-
if (!pad) {
-
g_printerr ("Could not retrieve pad '%s'
", pad_name);
-
return;
-
}
-
-
-
caps = gst_pad_get_negotiated_caps (pad);
-
if (!caps)
-
caps = gst_pad_get_caps_reffed (pad);
-
-
-
g_print ("Caps for the %s pad:
", pad_name);
-
print_caps (caps, " ");
-
gst_caps_unref (caps);
-
gst_object_unref (pad);
-
}
-
-
int main(int argc, charchar *argv[]) {
-
GstElement *pipeline, *source, *sink;
-
GstElementFactory *source_factory, *sink_factory;
-
GstBus *bus;
-
GstMessage *msg;
-
GstStateChangeReturn ret;
-
gboolean terminate = FALSE;
-
-
-
gst_init (&argc, &argv);
-
-
-
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.
");
-
return -1;
-
}
-
-
-
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");
-
-
-
pipeline = gst_pipeline_new ("test-pipeline");
-
-
if (!pipeline || !source || !sink) {
-
g_printerr ("Not all elements could be created.
");
-
return -1;
-
}
-
-
-
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
-
if (gst_element_link (source, sink) != TRUE) {
-
g_printerr ("Elements could not be linked.
");
-
gst_object_unref (pipeline);
-
return -1;
-
}
-
-
-
g_print ("In NULL state:
");
-
print_pad_capabilities (sink, "sink");
-
-
-
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).
");
-
}
-
-
-
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);
-
-
-
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
", GST_OBJECT_NAME (msg->src), err->message);
-
g_printerr ("Debugging information: %s
", 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.
");
-
terminate = TRUE;
-
break;
-
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 ("
Pipeline state changed from %s to %s:
",
-
gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
-
-
print_pad_capabilities (sink, "sink");
-
}
-
break;
-
default:
-
-
g_printerr ("Unexpected message received.
");
-
break;
-
}
-
gst_message_unref (msg);
-
}
-
} while (!terminate);
-
-
-
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_field,print_caps和print_pad_templates仅仅简单的用方便阅读的方式显示Capabilities结构。如果你想学到GstCaps数据结构的内在组织,那么请度以下Gstreamer文档关于Pad
Caps的部分。
-
-
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
-
GstPad *pad = NULL;
-
GstCaps *caps = NULL;
-
-
-
pad = gst_element_get_static_pad (element, pad_name);
-
if (!pad) {
-
g_printerr ("Could not retrieve pad '%s'
", pad_name);
-
return;
-
}
-
-
-
caps = gst_pad_get_negotiated_caps (pad);
-
if (!caps)
-
caps = gst_pad_get_caps_reffed (pad);
-
-
-
g_print ("Caps for the %s pad:
", pad_name);
-
print_caps (caps, " ");
-
gst_caps_unref (caps);
-
gst_object_unref (pad);
-
}
gst_element_get_static_pad()方法会从给定的element里面取得Pad的名字。这个Pad是静态的,因为它会一直存在。要进一步了解Pad请阅读GStreamer文档关于Pad的部分。
然后我们调用gst_pad_get_negotiated_caps()方法来获得Pad当前的Capabilities,无论是否已经固定下来,都会依赖协商过程的状态。这时Capabilities可能是不存在的,如果遇到这种情况,我们就调用gst_pad_get_caps_reffed()方法来建立一个当前可以接受的Pad
Capabilities。这个所谓的当前可接受的Caps时Pad Template的在NULL状态下的Caps,但后面这个可能会改变,因为还会查询实际的硬件。
gst_pad_get_caps_reffed()通常来说比gst_pad_get_caps()快一点,而且如果我们不需要改变获得的Caps的话也足够用了。然后我们就打印出这些Capabilities。
-
-
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.
");
-
return -1;
-
}
-
-
-
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()方法来创建element,而忽略了工厂本身,我们现在补上这一部分。一个GstElementFactory是负责通过一个工厂名来实例化一个element的。
你可以使用gst_element_factory_find()来创建一个“videotestsrc”工厂,然后通过gst_element_factory_create()来实例化多个“videotestsrc” element。gst_element_factory_make()实际上只是这个过程的一个封装。
通过工程,Pad模板实际上已经可以访问了,所以工厂一建立我们立刻打印这些信息。
我们跳过pipeline的创建和启动部分,直接跳到状态切换消息的处理:
-
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 ("
Pipeline state changed from %s to %s:
",
-
gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
-
-
print_pad_capabilities (sink, "sink");
-
}
-
break;
这里只是在每次pipeline状态切换的时候简单地打印当前的Pad Caps。你可以看到,在输出信息里面,初始的Caps(Pad模板的Caps)是如何逐步变化直到确定下来的(仅包含一个类型而且数值固定)。