前言
gtreamer播放ogg音频文件的流程讲解。
Ogg
Ogg全称是OGGVobis(oggVorbis)是一种音频压缩容器格式。Ogg格式不仅是一个高效的容器格式,它还融合了Vorbis、Theora和Speex等多种媒体压缩技术。Ogg不仅是一个容器格式,更融合了多种媒体压缩技术,如Vorbis(音频)、Theora(视频)和Speex(语音)。值得一提的是,我们之前介绍的高保真FLAC格式也可以被纳入Ogg框架之中。因此,我们所探讨的Ogg,通常指的是Ogg与Vorbis的组合。
GStreamer
GStreamer是一个非常强大和通用的框架,用于创建流媒体应用程序。GStreamer框架的许多优点来自其模块化:GStreamer可以无缝地整合新的插件模块。但是,由于模块化和强大的功能往往以更高的复杂性为代价,编写新的应用程序并不总是那么容易。
GStreamer分为:
- gstreamer:核心包
- gst-plugins-base:一组基本的示例元素
- gst-plugins-good:LGPL下的一组高质量插件
- gst-plugins-ugly:一组可能造成分发问题的高质量插件
- gst-plugins bad:一组需要更高质量的插件
- gst-libav:一组封装libav进行解码和编码的插件
- 其他几个包
GStreamer架构图

探测本地组件
使用gst-inspect-1.0可以检测到本地安装了哪些模块,如下:

播放ogg文件
步骤一:初始化gst

步骤二:创建消息循环

步骤三:创建所有需要的组件

步骤四:设置本地文件路径属性

步骤五:添加一个检测管道消息处理

步骤六:将组件都放入管道

步骤七:连接组件
连接组件后,这里分割器分割一个流就会回调产出一个pad,要对pad在创建时动态关联解码器,解码器才到sink播放。

步骤八:开始运行

步骤九:开始播放,进去循环阻塞

步骤十:清理资源

Demo源码
cpp
/******************** test003 ********************/
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; // 现在可以将这个焊盘与vorbis解码器接收焊盘连接起来 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); } void test003PlayOggDemo(int *argc, char **argv[]) { char const * fileName = "/home/yang/work/test/1.ogg"; // char const * fileName = "/home/yang/work/test/1.mp3"; // char const * fileName = "/home/yang/work/test/1.mov"; GMainLoop *loop; GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink; GstBus *bus; guint bus_watch_id; // 步骤一:初始化gst gst_init (argc, argv); // 步骤二:创建gmain消息循环 loop = g_main_loop_new (NULL, FALSE); // 步骤三:创建组建 pipeline = gst_pipeline_new ("audio-player"); // 组件类型 组件唯一名称 source = gst_element_factory_make ("filesrc", "file-source"); demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer"); // demuxer = gst_element_factory_make ("mpeg123", "mp3-demuxer"); // mp3: 不用分割,所以步骤不一样 // demuxer = gst_element_factory_make ("splitmuxsink", "mov-demuxer");// mov: 分割格式不一样,要修改代码,没改了 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; } // 步骤四:设置本地原的文件属性"location",文件名 g_object_set (G_OBJECT (source), "location", fileName, NULL); // 步骤五:添加一个消息处理程序 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); // 消息回调函数原型:gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) bus_watch_id = gst_bus_add_watch (bus, bus_call, loop); gst_object_unref(bus); // 步骤六:将所有的组件添加到管道 */ // file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output gst_bin_add_many(GST_BIN(pipeline), source, demuxer, decoder, conv, sink, NULL); // 步骤七:链接组件 // file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output gst_element_link (source, demuxer); gst_element_link_many (decoder, conv, sink, NULL); // 注意1:这里demuxer的pad-added消息会出发on_pad_added回调函数 // 注意2:demuxer是解码器,解码出来有多个流,每个流会产出一个pad,然后动态链接 // 注意2:在这个回调函数里面,会把added的焊盘关联到decoder上,就实现了demuxer->decoder的链接 // 注意3: g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder); /* 请注意,解复用器将动态链接到解码器。原因是Ogg可能包含各种流(例如音频和视频)。 * 当解复用器检测到流的数量和性质时,它将在运行时创建源焊盘。 * 因此,我们连接了一个回调函数,该函数将在发出"添加焊盘"时执行 */ // 步骤八:设置状态运行 g_print ("Now playing: %s\n", fileName); gst_element_set_state (pipeline, GST_STATE_PLAYING); // 步骤九:进入消息循环 g_print ("Running...\n"); g_main_loop_run (loop); // 步骤十:离开主循环,好好清理 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); }
工程模板v1.2.0

入坑
入坑一:创建mp3分流器是吧
问题
举一反三,试一试mp3分流器,结果失败:

尝试
使用gst-inspect-1.0可以检测到本地安装了哪些模块,举例部分如下:
继续扫描,可以看到:
会扫描,然后又刷这些,定期更新吧,我们查找下ogg:
那么找一下mp3:
测试:

解决
未尝试,mp3就是一个文件格式,不是封装容器,不需要分割器进行动态pad关联。其管道过程不一样。
