学习完这篇教程,你讲具备开始Playback tutorial的必要知识:
- 如何在连接elements的时候实现更好的控制。
- 如何获取感兴趣的事件通知以便及时做出处理。
#include <gst/gst.h>
/* Structure to contain all our information, so we can pass it to callbacks */
//涉及回调,把所有数据分组到一个结构便于处理。
typedef struct _CustomData {
//指向GstElement的指针
GstElement* pipeline;
GstElement* source;
GstElement* convert;
GstElement* resample;
GstElement* sink;
} CustomData;
/* Handler for the pad-added signal */
//只是一个声明,后面会用到
static void pad_added_handler(GstElement* src, GstPad* pad, CustomData* data);
int main(int argc, char* argv[]) {
CustomData data;
GstBus* bus;
GstMessage* msg;
GstStateChangeReturn ret;
gboolean terminate = FALSE;
/* Initialize GStreamer */
gst_init(&argc, &argv);
/* Create the elements */
data.source = gst_element_factory_make("uridecodebin", "source");
data.convert = gst_element_factory_make("audioconvert", "convert");
data.resample = gst_element_factory_make("audioresample", "resample");
data.sink = gst_element_factory_make("autoaudiosink", "sink");
/* Create the empty pipeline */
data.pipeline = gst_pipeline_new("test-pipeline");
if (!data.pipeline || !data.source || !data.convert || !data.resample || !data.sink) {
g_printerr("Not all elements could be created.\n");
return -1;
}
/* Build the pipeline. Note that we are NOT linking the source at this
* point. We will do it later. */
gst_bin_add_many(GST_BIN(data.pipeline), data.source, data.convert, data.resample, data.sink, NULL);
//这里我们将audioconvert,audioresample和autoaudiosink连接起来,但是并没有将它们和source连接
//因为这这个阶段,uridecodebin还没有生成source pad
if (!gst_element_link_many(data.convert, data.resample, data.sink, NULL)) {
g_printerr("Elements could not be linked.\n");
gst_object_unref(data.pipeline);
return -1;
}
/* Set the URI to play */
//设置uridecodebin的uri属性。
g_object_set(data.source, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
/* Connect to the pad-added signal */
/*
gulong g_signal_connect(
gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data );
instance:信号发出者,可以认为我们操作的控件,如按下按钮,这个就为按钮指针
detiled_signal:信号标志,如"pressed"
ndler:回调函数的名称,需要用G_CALLBACK()进行转换
data:给回调函数传的参数,gpointer 相当于C语言的 void *
返回值:注册函数的标志
*/
g_signal_connect(data.source, "pad-added", G_CALLBACK(pad_added_handler), &data);
/* Start playing */
ret = gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr("Unable to set the pipeline to the playing state.\n");
gst_object_unref(data.pipeline);
return -1;
}
/* Listen to the bus */
//监听pipeline的erroe和EOS信号,并在发生时进行了打印了相应的信息,便于debug
bus = gst_element_get_bus(data.pipeline);
do {
msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
/* 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(data.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:\n",
gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
}
break;
default:
/* We should not reach here */
g_printerr("Unexpected message received.\n");
break;
}
gst_message_unref(msg);
}
} while (!terminate);
/* Free resources */
gst_object_unref(bus);
gst_element_set_state(data.pipeline, GST_STATE_NULL);
gst_object_unref(data.pipeline);
return 0;
}
/* This function will be called by the pad-added signal */
/*
src 是触发信号的 GstElement。在此示例中,它只能是 uridecodebin,因为它是我们附加的唯一信号。信号处理程序的第一个参数始终是触发它的对象。
new_pad是加到src上的pad。通常来说,是我们需要连接的pad。
data指针是跟随信号一起过来的参数,在这个例子中,我们传递的时CustomData指针。
*/
static void pad_added_handler(GstElement* src, GstPad* new_pad, CustomData* data) {
//我们从CustomData中获取autoaudioconvertelement,并使用gst_element_get_static_pad()获取它的sink pad,要与new_pad连接的pad
GstPad* sink_pad = gst_element_get_static_pad(data->convert, "sink");
GstPadLinkReturn ret;
GstCaps* new_pad_caps = NULL;
GstStructure* new_pad_struct = NULL;
const gchar* new_pad_type = NULL;
g_print("Received new pad '%s' from '%s':\n", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(src));
/* If our converter is already linked, we have nothing to do here */
//uridecodebin将生成尽可能多的pads,这取决于它能够出处理的数据类型,并且没生成一个pad,回调都会被调用一次,
//以下代码将避免我们连接后还尝试将new_pad与一个已经连接了的element连接。
if (gst_pad_is_linked(sink_pad)) {
g_print("We are already linked. Ignoring.\n");
goto exit;
}
/* Check the new pad's type */
//gst_pad_get_current_caps()检查当前pad的capabilities(当前输出的数据类型)
new_pad_caps = gst_pad_get_current_caps(new_pad);
//一个pad当前的caps上只有一个GstStructure代表了单一的媒体格式。在这个pad中我们想要的只有音频capability,所以我们使用gst_caps_get_structure()检索pad的第一个GstStructure。
new_pad_struct = gst_caps_get_structure(new_pad_caps, 0);
//使用gst_structure_get_name()来恢复结构体的name成员,它包含媒体数据格式的主要描述。
new_pad_type = gst_structure_get_name(new_pad_struct);
if (!g_str_has_prefix(new_pad_type, "audio/x-raw")) {
g_print("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
goto exit;
}
/* Attempt the link */
ret = gst_pad_link(new_pad, sink_pad);
if (GST_PAD_LINK_FAILED(ret)) {
g_print("Type is '%s' but link failed.\n", new_pad_type);
}
else {
g_print("Link succeeded (type '%s').\n", new_pad_type);
}
exit:
/* Unreference the new pad's caps, if we got them */
if (new_pad_caps != NULL)
gst_caps_unref(new_pad_caps);
/* Unreference the sink pad */
gst_object_unref(sink_pad);
}