工具癖我用 LinuxHEXA机器人

HEXA娱乐开发日志番外000——Gstreamer编程入门

2018-05-23  本文已影响10人  阿棍儿_Leon

HEXA开发日志目录


番外篇从此开始,番外用于讲一些HEXA开发过程中的一些在日志中不便展开的一些点点滴滴。

前言

Gstreamer简介

它的来历我还没有了解,目前我只知道它是一个在Linux环境上很容易制作流媒体应用的程序框架。
单说Gstreamer的话,它只是个框架,如果把框架比喻为骨架的话,插件就是它的血肉了,框架+插件就可以构成一个处理流媒体的流水线,基于Gstreamer的应用可以通过对流水线的控制实现各种流媒体处理的功能。
应用开发者只要在丰富的插件中找到适合自己应用的,再把他们连成流水线,立刻就可以构建好一个流媒体处理模块。关于使这个过程变得容易的原因,一方面是插件非常丰富,大多数情况下,没有必要自己制作插件;另一方面原因是插件的连接非常方便,在五花八门的插件中,有很简单的方法可以确定插件之间是否可以连接。

Gstreamer编程模型

我理解的Gstreamer编程要分为以下几步

  1. 构想流水线
    在编程之前,首先要在脑中构造一个流程,即输入是什么,大概要经过哪些处理,最后输出什么。
    输入要想好输入的形式或设备,比如处理视频的话,可以是摄像头、某种格式的视频文件或是网络流等,音频可以是话筒或某种格式的音频文件等。
    处理过程可以大致考虑一下,应该需要哪种编解码器或是格式转换器,需要哪种muxer或demuxer。
    输出也是要想好形式或设备,比如视频的话,可以是显示器、液晶屏、某种格式的文件或是网络流等,音频可以是扬声器或某种格式的音频文件等。
  2. 插件选型
    要在插件库中找出能够满足构想的那些插件,并确认它们能够连接起来,构成我们想要的流水线。那就需要确认以下问题
    • 当前开发环境下哪些插件是可用的?
    • 怎么知道那些从名字上看似乎可用的插件是不是能满足需求?
    • 怎么知道这些看上去能用的插件能不能接起来?
  3. 写代码
    把构想中的对象和代码对应起来,实现流水线。

经过上述过程后,基本的编程就算完成了。下面以我写的simple.c为例,用上述过程复盘一下。

构想流水线

官方例子

下图来自官方文档,看起来这是一个简单的ogg视频文件播放器。整个大方框是一个pipeline,大方框中的小方框(例如file-source)是构成pipeline的一个个插件中的元件,有src/sink字样的蓝色小方框是用来对接元件的东西,其中src表示输出,sink表示输入。



单从字面上也不难理解这些元件

上面这个例子的输入是ogg视频文件,中间要demuxer分离音视频,然后分别解码,最后输出给音视频设备播放。其中的每个元件都是可以替换的,比如想把输入源换成rtmp流,只要把file-source换成rtmp-src元件就行了;再比如想做一个编码器只要把整个图左右翻转一下,播放设备sink换成采集设备src,xxx-decoder换成xxx-encoder,xxx-demuxer换成xxx-encoder就可以了。

我的流水线

摄像头==>编码器==>合成器==>rtmp流,我的流水线就这么简单,然后我们来插件选型。

插件选型

gst-inspect-1.0
很多插件......
rtmp:  rtmpsink: RTMP output sink
rtmp:  rtmpsrc: RTMP Source
很多插件......

这里我只列了两个比较好认的,rtmp是插件名字,rtmpsinkrtmpsrc是这个插件的2个元件类型,第二个冒号后面是对这个元件的说明。

# gst-inspect-1.0 rtmpsink
Factory Details:
  Rank                     primary (256)
  Long-name                RTMP output sink
  Klass                    Sink/Network
  Description              Sends FLV content to a server via RTMP
  Author                   Jan Schmidt <thaytan@noraisin.net>

Plugin Details:
  Name                     rtmp
  Description              RTMP source and sink
  Filename                 /usr/lib/arm-linux-gnueabihf/gstreamer-1.0/libgstrtmp.so
  Version                  1.2.4
  License                  LGPL
  Source module            gst-plugins-bad
  Source release date      2014-04-18
  Binary package           GStreamer Bad Plugins (Ubuntu)
  Origin URL               https://launchpad.net/distros/ubuntu/+source/gst-plugins-bad1.0

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseSink
                         +----GstRTMPSink
...
Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-flv
...

Element Properties:
...
  location            : RTMP url
                        flags: readable, writable
                        String. Default: null

稍微解读一下

  1. Factory Details和Plugin Details都是一些更详尽的描述,比如Source module gst-plugins-bad是说这个插件属于gst-plugins-bad插件库的,如果你想看代码,可以去github搜这个名字。/usr/lib/arm-linux-gnueabihf/gstreamer-1.0/libgstrtmp.so是这个插件库文件的位置。
  2. Pad Templates
    Pad概念后面再讨论,总之这里是描述这个元件可以“讲什么语言”或“听什么语言”的。这里有个SINK template: 'sink'是说它有“听”的能力,如果能“说”,就会有SRC template了。
    Capabilities是对SINK template: 'sink'的能力的具体描述,即video/x-flv,可以理解为一种视频格式,也就是说这个元件能接收video/x-flv格式的视频输入,如果对方的SRC template能输出这个格式,那么就可以连接。
    这里的flv字眼说明,这个元件前面的合成器可以搜索mux、flv或flash找到哦。
  3. Element Properties
    这里是属性列表,是插件自己定制的,所以别的插件可能有不同的属性列表。location属性的值就是rtmp流推送的地址。如果是官方例子中的file-source,应该也会有location属性,它代表着要播放的视频文件路径,即同样的属性名在不同插件中的意思可能不同,属性名和含义完全是插件设计者决定的。

至此,判断插件能否对接的方法有了,即对比SINK templateSRC templateCapabilities,如果有重叠的部分,就是可以对接的
我完成插件选型后设计的流水线如下,下一步就是用代码实现它了。

我的流水线

写代码

最简单的实现,只要遵循以下套路就能满足大多数需求了。

#include <gst/gst.h>
#include <glib.h>

int main (int argc,char *argv[])
{
    GMainLoop *loop;
    GstElement *pipeline, *元件0, *元件1, ..., *元件n;

    /* Initialisation */
    gst_init (&argc, &argv);

    loop = g_main_loop_new (NULL, FALSE);

    /* 创建流水线 */
    pipeline = gst_pipeline_new ("自己起的流水线名");
    /* 创建元件 */
    元件0 = gst_element_factory_make ("元件名0", "自己起的元件实例名0");
    元件1 = gst_element_factory_make ("元件名1", "自己起的元件实例名1");
    //其他元件...

    /* 如果需要给一些原件设置属性 */
    g_object_set (G_OBJECT (元件0), "属性名", 属性值, NULL);

    /* 把元件与流水线绑定 */
    gst_bin_add_many (GST_BIN (pipeline), 元件0, 元件1, ..., 元件n, NULL);
    /* 连接元件,也可以用gst_element_link_many一句连接 */
    if (gst_element_link (元件0, 元件1)){
        g_print ("link success %d\n", __LINE__);
    }
    else{
        return -1;
    }
    if (gst_element_link (元件1, 元件2)){
        g_print ("link success %d\n", __LINE__);
    }
    else{
        return -1;
    }
    //......
    if (gst_element_link (元件n-1, 元件n)){
        g_print ("link success %d\n", __LINE__);
    }
    else{
        return -1;
    }

    /* 启动播放*/
    gst_element_set_state (pipeline, GST_STATE_PLAYING);

    /* 运行主循环,ctrl+c可以退出这个循环 */
    g_main_loop_run (loop);

    /* 释放内存之类的收尾动作 */
    //略...
    return 0;
}

套路是不是很简单,也就是以下几个步骤

  1. 创建流水线pipeline,想好名字就行了;
  2. 创建元件元件0~元件n,第一个参数是元件类型,第二个参数是给这个元件的实例起的名字,两者就像类和对象、数据类型和变量的关系一样;
  3. 对一些需要配置的元件设置属性(g_object_set);
  4. gst_bin_add_many相当于把元件装配到流水线上,准备连接;
  5. 按顺序连接元件(gst_element_linkgst_element_link_many);
  6. 设置流水线状态为GST_STATE_PLAYING,这个状态就意味着流水线开始播放了;
  7. g_main_loop_run运行一个死循环,只要流水线正常运行,这个循环就不会退出,如果用户ctrl+c或者播放出错了,这个循环就退出了;
  8. 在循环退出后进行收尾工作。

我的代码比这个套路多了一点点东西,不过要是没有多这一点点,也是可以运作的。
上面套路中涉及的概念和数据类型如下表,其中GstElementFactory并没有出现。这是因为gst_element_factory_make把使用GstElementFactory的过程封装了,官方讲elements的文档里有对此说明。

概念 数据类型
元件类型 GstElementFactory
元件实例 GstElement
流水线 GstElement

工厂(factory)?元件(element)?这些名字是不是很形象?其设计思想就是,先请不同的工厂制造出各种元件,然后把元件组装成一条流水线,这条流水线上流动的就是流媒体的不同形态,包括文件或内存等不同形式、编/解码前/后等不同状态。

总结

Gstreamer的编程模型还有很多其他的机制来满足更复杂的需求。例如,当两个元件有多种方式对接时,如何指定用哪种方式对接,再比如,如何对流水线甚至单个元件进行更细致的控制等,这些东西也许会继续出文章说明。


下一篇 也许会有。。。

上一篇下一篇

猜你喜欢

热点阅读