1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
#include <gst/gst.h> #include <glib.h> static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) { GMainLoop *loop = (GMainLoop *) data; switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_EOS: g_print ("End of stream\n"); g_main_loop_quit (loop); break; case GST_MESSAGE_ERROR: { gchar *debug; GError *error; gst_message_parse_error (msg, &error, &debug); g_free (debug); g_printerr ("Error: %s\n", error->message); g_error_free (error); g_main_loop_quit (loop); break; } default: break; } return TRUE; } static void on_pad_added (GstElement *element, GstPad *pad, gpointer data) { GstPad *sinkpad; GstElement *decoder = (GstElement *) data; /* We can now link this pad with the vorbis-decoder sink pad */ g_print ("Dynamic pad created, linking demuxer/decoder\n"); sinkpad = gst_element_get_static_pad (decoder, "sink"); gst_pad_link (pad, sinkpad); gst_object_unref (sinkpad); } int main (int argc, char *argv[]) { GMainLoop *loop; GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink; GstBus *bus; guint bus_watch_id; /* Initialisation */ gst_init (&argc, &argv); loop = g_main_loop_new (NULL, FALSE); /* Check input arguments */ if (argc != 2) { g_printerr ("Usage: %s <Ogg/Vorbis filename>\n", argv[0]); return -1; } /* Create gstreamer elements */ pipeline = gst_pipeline_new ("audio-player"); source = gst_element_factory_make ("filesrc", "file-source"); //用于连接文件 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer"); //分离器 decoder = gst_element_factory_make ("vorbisdec", "vorbis-decoder"); //解码器 conv = gst_element_factory_make ("audioconvert", "converter"); //转换器 sink = gst_element_factory_make ("autoaudiosink", "audio-output"); //接收器 if (!pipeline || !source || !demuxer || !decoder || !conv || !sink) { g_printerr ("One element could not be created. Exiting.\n"); return -1; } /* Set up the pipeline */ /* we set the input filename to the source element */ g_object_set (G_OBJECT (source), "location", argv[1], NULL); /* we add a message handler */ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); bus_watch_id = gst_bus_add_watch (bus, bus_call, loop); gst_object_unref (bus); /* we add all elements into the pipeline */ /* file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output */ gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv, sink, NULL); /* we link the elements together */ /* file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output */ gst_element_link (source, demuxer); gst_element_link_many (decoder, conv, sink, NULL); g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder); /* note that the demuxer will be linked to the decoder dynamically. The reason is that Ogg may contain various streams (for example audio and video). The source pad(s) will be created at run time, by the demuxer when it detects the amount and nature of streams. Therefore we connect a callback function which will be executed when the "pad-added" is emitted.*/ /* Set the pipeline to "playing" state*/ g_print ("Now playing: %s\n", argv[1]); gst_element_set_state (pipeline, GST_STATE_PLAYING); /* Iterate */ g_print ("Running...\n"); g_main_loop_run (loop); /* Out of the main loop, clean up nicely */ g_print ("Returned, stopping playback\n"); gst_element_set_state (pipeline, GST_STATE_NULL); g_print ("Deleting pipeline\n"); gst_object_unref (GST_OBJECT (pipeline)); g_source_remove (bus_watch_id); g_main_loop_unref (loop); return 0; } |
编译 gcc -Wall helloworld.c -o helloworld $(pkg-config --cflags --libs gstreamer-1.0),运行
./helloworld filename.ogg #注意修改成自己的ogg音频文件
代码分析:
代码工作原理图如下:
首先创建file-source element用于从源文件中读取数据,因为Ogg可以以各种格式(如Dirac,MNG,CELT,MPEG-4,MP3等)嵌入音频和视频,所以从本质上来说,从file-source读取出的是音视频复用在一起的数据流。
尽管有些ogg文件只有音频数据,但是既然其可以复用音频和视频,那么其内部的数据格式还是按照复用的协议进行排布的,有区别于一般的单音频文件,所以还是需要使用分离器像对待一般复用了音视频的ogg文件一样进行音频和视频的提取,只不过这时提取到的视频流是空的,这就是为什么上图中需要ogg-demuxer的原因。(注:此段为作者推断)
ogg-demuxer在读取到元数据并进行分离提取音视频数据流的时候,会根据数据流格式动态创建pads(可能是音频数据流,视频数据流,也可能同时包含了两者),并发出pad-added通知事件,所以我们需要在bus中监听捕获demuxer element的pad-added信号,并调用函数 on_pad_added来完成pad的链接,由以下几个函数来完成该功能:
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline) );
bus_watch_id = gst_bus_add_watch (bus, bus_call, loop)
其中bus_call为bus通知消息到来时候的回调函数。
g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder);
其中on_pad_added为pad-added通知事件的回调函数。
1 2 3 4 |
g_object_set (G_OBJECT (source), "location", argv[1], NULL); #指定element对象源文件的位置 |
再之后,通过解码器(decoder),变流器(converter),和音频输出器(audio-output)将ogg文件转化成声音。
其他函数功能解析:
1 2 3 4 5 |
gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv, sink, NULL); #添加elements到pipeline中 |
1 2 3 4 5 6 |
gst_element_link (source, demuxer); gst_element_link_many (decoder, conv, sink, NULL); #链接各个elements,demuxer和decoder会在回调函数on_pad_adder中完成链接 |
1 2 3 4 5 |
gst_element_set_state (pipeline, GST_STATE_PLAYING); #开通管道pipeline |
1 2 3 |
g_main_loop_run (loop); #主事件循环等待管道完成任务,并监测bus消息 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* Out of the main loop, clean up nicely */ g_print ("Returned, stopping playback\n"); gst_element_set_state (pipeline, GST_STATE_NULL); //解引用之前要先对管道进行空标志设置 g_print ("Deleting pipeline\n"); gst_object_unref (GST_OBJECT (pipeline)); //解引用 g_source_remove (bus_watch_id); //清除bus事件监听 g_main_loop_unref (loop); |