GStreamer开发笔记(七):gstreamer播放ogg详解

13 阅读5分钟

前言

  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源码

/******************** 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关联。其管道过程不一样。   在这里插入图片描述