引用链接:https://www.cnblogs.com/xleng/p/11113405.html
Pad
Gstreamer 时利用Pad来控制数据的传输。
pad作为element之间的数据的接口,一个src pad只能与一个sink pad相连。每个element可以通过pad过滤数据,接收自己支持的数据类型。
pad通过pad caps来描述支持的数据类型。
表示分辨率为300x200,帧率为30fps的RGB视频的Caps:
“video/x-raw,format=RGB,width=300,height=200,framerate=30/1”
表示采样位宽为16位,采样率44.1kHz,双通道PCM音频的Caps:
“audio/x-raw,format=S16LE,rate=44100,channels=2”
或者直接描述编码数据格式Voribis,VP8:
“audio/x-vorbis” "video/x-vp8"
一个Pad可以支持多种类型的Caps(比如一个video sink可以同时支持RGB或YUV格式的数据),同时可以指定Caps支持的数据范围(比如一个audio sink可以支持1~48k的采样率)。但是,在一个Pipeline中,Pad之间所传输的数据类型必须是唯一的。GStreamer在进行element连接时,会通过协商(negotiation)的方式选择一个双方都支持的类型。
因此,为了能使两个Element能够正确的连接,双方的Pad Caps之间必须有交集,从而在协商阶段选择相同的数据类型,这就是Pad Caps的主要作用。在实际使用中,我们可以通过gst-inspect工具查看Element所支持的Pad Caps,从而才能知道在连接出错时如何处理。
通过示例代码basic-tutorial-6可以看到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;
}
gcc basic-tutorial-6.c -o basic-tutorial-6 `pkg-config --cflags --libs gstreamer-1.0`
源码分析:
输出可读信息:
print_field, print_caps and print_pad_templates_information实现类似功能,打印GStreamer的数据结构,可以查看相应GStreamer 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);
}
因为我们使用的source和sink都具有static(always)pad,所以这里使用gst_element_get_static_pad()获取Pad, 其他情况可以使用gst_element_foreach_pad()或gst_element_iterate_pads()获取动态创建的Pad。
接着使用gst_pad_get_current_caps()获取pad当前的caps,根据不同的element状态会有不同的结果,甚至可能不存在caps。如果没有,我们通过gst_pad_query_caps()获取当前可以支持的caps,当element处于NULL状态时,这个caps为Pad Template所支持的caps,其值可随状态变化而变化。
print_pad_templates_information (source_factory);
print_pad_templates_information (sink_factory);
//source_factory
Pad Templates for Audio test source:
SRC template: 'src'
Availability: Always
Capabilities:
audio/x-raw
format: { (string)S16LE, (string)S16BE, (string)U16LE, (string)U16BE, (string)S24_32LE, (string)S24_32BE, (string)U24_32LE, (string)U24_32BE, (string)S32LE, (string)S32BE, (string)U32LE, (string)U32BE, (string)S24LE, (string)S24BE, (string)U24LE, (string)U24BE, (string)S20LE, (string)S20BE, (string)U20LE, (string)U20BE, (string)S18LE, (string)S18BE, (string)U18LE, (string)U18BE, (string)F32LE, (string)F32BE, (string)F64LE, (string)F64BE, (string)S8, (string)U8 }
layout: { (string)interleaved, (string)non-interleaved }
rate: [ 1, 2147483647 ]
channels: [ 1, 2147483647 ]
//sink_factory
Pad Templates for Auto audio sink:
SINK template: 'sink'
Availability: Always
Capabilities:
ANY
print_pad_capabilities (sink, "sink");
Caps for the sink pad:
ANY
//状态改变
print_pad_capabilities (sink, "sink");
Pipeline state changed from NULL to READY:
Caps for the sink pad:
audio/x-raw
format: { (string)S16LE, (string)S16BE, (string)F32LE, (string)F32BE, (string)S32LE, (string)S32BE, (string)S24LE, (string)S24BE, (string)S24_32LE, (string)S24_32BE, (string)U8 }
layout: interleaved
rate: [ 1, 192000 ]
channels: [ 1, 32 ]
audio/x-alaw
rate: [ 1, 192000 ]
channels: [ 1, 32 ]
audio/x-mulaw
rate: [ 1, 192000 ]
channels: [ 1, 32 ]
Pipeline state changed from READY to PAUSED:
Caps for the sink pad:
audio/x-raw
format: S16LE
layout: interleaved
rate: 44100
channels: 1
Pipeline state changed from PAUSED to PLAYING:
Caps for the sink pad:
audio/x-raw
format: S16LE
layout: interleaved
rate: 44100
channels: 1
NULL状态为Element的初始化状态,此时,“autoaudiosink”的sink pad caps与Pad Template相同,支持所有的格式。
状态从NULL转到READY时,GStreamer会获取音频输出设备所支持的所有类型,这里可以看到sink pad caps列出了输出设备所能支持的类型。
状态从READY转到PAUSED时,GStreamer会协商一个所有element都支持的类型。当进入PLAYING状态时,sink会采用协商后的类型进行数据传输。