美文网首页工具癖我用 LinuxHEXA机器人
HEXA娱乐开发日志番外000——Gstreamer编程入门

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

作者: 阿棍儿_Leon | 来源:发表于2018-05-23 22:31 被阅读10次

    HEXA开发日志目录


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

    前言

    • 如题,本篇要讲的是Gstreamer的简单使用,最好是看官方文档,除非看英文文档对你来说太痛苦,那就来看这篇好了。
    • 本篇虽然是讲编程,不讲命令行,但是在编程或编写命令行之前的准备工作都是类似的,可以作为参考。

    Gstreamer简介

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

    Gstreamer编程模型

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

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

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

    构想流水线

    官方例子

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



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

    • file-source
      即以文件作为源,这里具体就是某个ogg视频文件了。
    • ogg-demuxer
      demuxer即分离器,从最后的结果看,它是把ogg文件中音/视频分开了。
    • vorbis-decoder
      很明显,这应该是一个音频解码器
    • audio-sink
      看图标,这应该就是扬声器的驱动之类的了
    • theora-decoder
      很明显+1,这应该是一个视频解码器
    • video-sink
      看图标+1,这应该就是显示器的驱动之类的了

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

    我的流水线

    • 输入
      视频方面是机器人摄像头采集的原始图像,音频方面反正还没有合适的采集设备,先不考虑
    • 输出
      rtmp网络流
    • 处理
      在上面的输入和输出之间肯定要有个编码器
      后面加音频的话,音视频需要合成为一个流,所以必然需要一个合成器(muxer)

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

    插件选型

    • 当前开发环境下哪些插件是可用的?
      这很简单,只要使用Gstreamer的命令行工具gst-inspect,如果是1.x版本在终端中输入以下命令就能列出所有插件的名字
    gst-inspect-1.0
    很多插件......
    rtmp:  rtmpsink: RTMP output sink
    rtmp:  rtmpsrc: RTMP Source
    很多插件......
    

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

    • 怎么知道那些从名字上看似乎可用的插件是不是能满足需求?
      这个问题隐含一个已知,我们要先大概知道一些名字,然后才能从名字上过滤掉大量的无关插件,比如关于摄像头,需要知道v4l、uvc等。
      首先,找摄像头信号源,可以用正则表达式搜索video.*source,然后再用搜索引擎大概了解一下就能初步选出来了。把那些比较像的找出来后,只要再用用搜索引擎,很容易确定其中的video4linux2: v4l2src: Video (video4linux2) Source是大概率可用的。
      其次,找输出插件,正则表达式搜rtmp.*sink,和找输入类似的,rtmp: rtmpsink: RTMP output sink看起来比较像,先就是它了。
      最后,要把中间那些插件找到,先找编码器,正则表达式搜索video.*encoder,在我的机器人上有3个插件中的6个元件备选,如果在开发环境里装了Gstreamer的libav类插件,就搜出更多了。
      此处筛选可以基于这样一个原则,基于已知的知识找出最像的那个作为备选就好。
      因为官方给我推荐过libimxvpu,所以我就用名字叫imxvpu的插件就行了,它有4个元件,分别是imxvpuenc_mjpegimxvpuenc_mpeg4imxvpuenc_h264imxvpuenc_h263,看起来就是选一种编码格式就行了,先随便选一个,就先选h264吧。如果没有些先验只是,关于合成器(mux)就不是那么好找了,如果知道rtmp和flash有关,这一步可以先确定一个,不知道也没关系,就直接进行下一步好了。
    • 怎么知道这些看上去能用的插件能不能接起来?
      • 为什么会有不能连接的情况?
        所有插件不是一个人或一队人写的,这就好像一个国际公司的两个部门要对接一样,两个部门至少要各出一个人作为接口与对方部门交流,如果这俩部门处在不同的国家,那么这两个接口很可能讲着不同的语言,他们之间就无法有效传达信息了。
        因此,两个元件要想连接,必须知道讲话的接口讲得是什么语言,听话的接口听得懂什么语言,如果语言一致,就是能连接的。
      • 怎么看元件能“讲或听得懂哪种语言”?
        我们可以用gst-inspect-1.0 <元件名>进一步看一下元件详细信息,以便对元件有进一步了解,下面看一下rtmpsink的信息。
    # 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的编程模型还有很多其他的机制来满足更复杂的需求。例如,当两个元件有多种方式对接时,如何指定用哪种方式对接,再比如,如何对流水线甚至单个元件进行更细致的控制等,这些东西也许会继续出文章说明。


    下一篇 也许会有。。。

    相关文章

      网友评论

        本文标题:HEXA娱乐开发日志番外000——Gstreamer编程入门

        本文链接:https://www.haomeiwen.com/subject/myjkjftx.html