deepstream学习笔记(二):gstreamer与deepstream-test1说明

  • Gstreamer介绍
    • gstreamer简介
    • gstreamer tools 相关工具介绍
  • gstreamer 组成说明
    • Element
    • pad
    • bin 与 pipeline
    • Communication
  • deepstream-test1 与 deepstream-imagedata-multistream

Gstreamer介绍

GStreamer 是一个用于创建流媒体应用程序的框架。基本设计来自俄勒冈研究生院的视频管道,以及来自 DirectShow 的一些想法。

GStreamer 的开发框架使得编写任何类型的流媒体应用程序成为可能。GStreamer 框架旨在使编写处理音频或视频或两者的应用程序变得容易。它不限于音频和视频,可以处理任何类型的数据流。管道设计的开销比所应用的过滤器所产生的开销小。这使得 GStreamer 成为一个很好的框架,用于设计对延迟有很高要求的高端音频应用程序。

GStreamer 最明显的用途之一是使用它来构建媒体播放器。GStreamer 已经包含用于构建可以支持多种格式的媒体播放器的组件,包括 MP3、Ogg/Vorbis、MPEG-1/2、AVI、Quicktime、mod 等。然而,GStreamer 不仅仅是另一个媒体播放器。它的主要优点是可以将可插拔组件混合并匹配到任意管道中,以便编写成熟的视频或音频编辑应用程序。

gstreamer简介

GStreamer 的核心功能是为插件、数据流和媒体类型处理/协商提供框架。它还提供了一个 API 来使用各种插件编写应用程序。

更具体的说明与文字表述参照官方文档中第一章(What is GStreamer?),这里直接引出架构设计图:
deepstream学习笔记(二):gstreamer与deepstream-test1说明_第1张图片
具体来说,GStreamer 提供:

  • 用于多媒体应用程序的 API

  • 插件架构

  • 管道架构

  • 一种媒体类型处理/协商的机制

  • 同步机制

  • 超过 250 个插件,提供超过 1000 个元素

  • 一套工具

GStreamer 插件可以分为:

  • 协议处理

  • 来源:用于音频和视频(涉及协议插件)

  • 格式:解析器、格式化程序、复用器、解复用器、元数据、字幕

  • 编解码器:编码器和解码器

  • 过滤器:转换器,混音器,效果器,…

  • sinks:用于音频和视频(涉及协议插件)

gstreamer tools 相关工具介绍

结构图中蓝色部分皆为gstreamer带有,或者可以使用的部分。我们先看左上角的蓝色部分,即gstreamer 工具部分,一些主要的工具见如下表格:

姓名 概要
gst-launch-1.0 构建和运行基本GStreamer管道的工具
gst-inspect-1.0 可以打印出可用GStreamer 插件的信息、特定插件的信息或特定元素的信息
gst-play-1.0 gst-play-1.0 - 简单的命令行播放测试工具
gst-typefind-1.0 文件的打印介质类型
gst-discoverer-1.0 显示文件元数据和流信息
gst-device-monitor-1.0 用于 GStreamer 设备监视器的简单命令行测试工具

gstreamer与ffmpeg类似,同样也提供了不同的命令行工具用于快速的查看信息以及验证Pipeline的是否能够正确运行,在开发过程中,可以跟SQL一样,首先在命令行进行验证,再将Pipeline集成到应用中,即Gst.parse_launch(PIPE)。我这里会提到gst-inspect-1.0,gst-discoverer-1.0,gst-launch-1.0等命令行工具的使用。

用一个mp3文件举例,正好现在在听歌,gstreamer的gst-play命令能在有桌面的系统下直接打开音频测试,并且还有较丰富的选项。

$ gst-play-1.0 ceshi.mp3

Press 'k' to see a list of keyboard shortcuts.
Now playing /data/Music/ceshi.mp3
Redistribute latency...
0:00:00.2 / 0:00:18.0

Interactive mode - keyboard controls:

        space    : pause/unpause
        q or ESC : quit
        > or n   : play next
        < or b   : play previous
        →        : seek forward
        ←        : seek backward
        ↑        : volume up
        ↓        : volume down
        +        : increase playback rate
        -        : decrease playback rate
        d        : change playback direction
        t        : enable/disable trick modes
        a        : change audio track
        v        : change video track
        s        : change subtitle track
        0        : seek to beginning
        k        : show keyboard shortcuts

0:00:05.6 / 0:00:18.0

gst-discoverer-1.0可以很方便的查看媒体文件的编码,帧率等信息:

$ gst-discoverer-1.0 ceshi.mp3

Analyzing file:///home/Music/ceshi.mp3
Done discovering file:///home/Music/ceshi.mp3

Topology:
  unknown: ID3 tag
    audio: MPEG-1 Layer 3 (MP3)

Properties:
  Duration: 0:04:07.118367346
  Seekable: yes
  Live: no
  Tags:
      title: 科幻
      artist: 许嵩
      album: 呼吸之野
      track number: 3
      genre: Blues
      container format: ID3 tag
      ID3v2 frame: buffer of 41 bytes
      image: buffer of 49606 bytes, type: image/jpeg, width=(int)500, height=(int)500, sof-marker=(int)0
      has crc: false
      channel mode: joint-stereo
      audio codec: MPEG-1 Layer 3 (MP3)

没错,ceshi.mp3就是许嵩的科幻,gst-discoverer-1.0输出了详细的歌曲包含信息,这同样适用于视频或者视频流,如果还想看封装信息,可以使用gst-typefind,这个命令行下用处不大,主要是接口方面会使用:

$ gst-typefind-1.0 ceshi.mp3

ceshi.mp3 - application/x-id3

其余的两个主要的命令为gst-inspect-1.0gst-launch-1.0,前者是打印有关 GStreamer 插件或元素的信息,当前系统所拥有的GStreamer插件以及每个插件的详细信息都能通过它全部显示出来,具体命令为:

$ gst-inspect-1.0
"""
不带任何参数。这样会列出当前系统中支持的所有Element,这些Element可用于构造Pipeline.具体信息就不复制黏贴了,很多,通过这里显示的参数,可以再使用gst-inspect-1.0进行输出
"""

$ gst-inspect-1.0 rtsp
"""
Plugin Details:
  Name                     rtsp
  Description              transfer data via RTSP
  Filename                 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstrtsp.so
  Version                  1.14.5
  License                  LGPL
  Source module            gst-plugins-good
  Source release date      2019-05-29
  Binary package           GStreamer Good Plugins (Ubuntu)
  Origin URL               https://launchpad.net/distros/ubuntu/+source/gst-plugins-good1.0

  rtpdec: RTP Decoder
  rtspsrc: RTSP packet receiver

  2 features:
  +-- 2 elements

"""

另一个gst-launch-1.0 为使用最多的一个命令,它接收一个用字符串方式描述的Pipline,将其实例化并运行,我们可以用此命令快速的检查Pipeline中各个元素是否能够正确的连接起来。当我们需要构建的Pipeline很复杂时,也可以将Pipeline进行拆分,逐步通过gst-launch验证Pipeline的合法性。而同样,我上面提到的Gst.parse_launch(PIPE),为在python中的方法,C里面应该是gst_parse_launch(),通过这个api可以将其转化为GstPipeline对象。具体的使用实例,在官方文档中也提到了很多,这里进行一定部分的引用:

# 查看说明书
$ man gst-launch-1.0

# 使用 playbin 播放文件:
$ gst-launch-1.0 playbin \
    uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm

# 也可以用“!”将元素链接在一起:
$ gst-launch-1.0 audiotestsrc ! alsasink

# 在管道中创建不同的流:
$ gst-launch-1.0 audiotestsrc !alsasink videotestsrc !xvimagesink

# 制作单帧 JPEG
$ gst-launch-1.0 videotestsrc num-buffers=1 ! jpegenc ! filesink location=img8.jpg

 ! filesink location=img8.jpg
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
Got EOS from element "pipeline0".
Execution ended after 0:00:00.000077116
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...

# 使用 fakesink dump = true 转储输入,并连接到流检查数据是否在传输,GST_DEBUG是日志等级,分为1到9
$ GST_DEBUG=3 gst-launch-1.0 -v rtspsrc location=rtsp://192.168.0.10:554/MediaInput/h264  user-id="admin" user-pw="password" ! fakesink dump=true

# decodebin! 可以使用 autovideosink 很好地解码它并在屏幕上显示视频。
$ GST_DEBUG=3 gst-launch-1.0 -v rtspsrc location=rtsp://192.168.0.10:554/MediaInput/h264 user-id="admin" user-pw="password" ! decodebin ! autovideosink

# 使用 gst-play-1.0 播放流并在屏幕上查看。
$ gst-play-1.0 rtsp://admin:password@192.168.0.10:554/MediaInput/h264 

还有很多很好玩的实例可以自己尝试,这里只是引出一部分,另外,作为对比,之前我写过一篇关于ffmpeg针对图像和视频的一篇博文,可以对照参考:

ffmpeg图片与视频命令笔记

gstreamer 组成说明

架构图下方蓝色部分,蓝框主要是gstreamer的Element,在官方文档中,gstreamer内部最重要的四个组成部分为: ElementsPadsBins and pipelinesCommunication 。其中需主要理解的是前三个,第四个字面意思是通信,但其实比较浅显易懂,deepstream的通信方式主推kafka,如果用过的话,基本没有什么问题。

Element

对于应用程序员来说,GStreamer 中最重要的对象就是 GstElement 对象。元素是媒体管道的基本构建块。使用的所有不同的高级组件都源自 GstElement. 每个解码器、编码器、解复用器、视频或音频输出实际上都是一个GstElement ,可以理解为是一个黑盒,一端进去,一端出来,具体见下面四个element实例:

deepstream学习笔记(二):gstreamer与deepstream-test1说明_第2张图片
数据源处理 element (生成器)
deepstream学习笔记(二):gstreamer与deepstream-test1说明_第3张图片
视频滤波器 element
deepstream学习笔记(二):gstreamer与deepstream-test1说明_第4张图片
解复用器 element
deepstream学习笔记(二):gstreamer与deepstream-test1说明_第5张图片
数据输出 element (接收器)

每个element被定义出来,都具有不同的作用,而两个element必须通过pad才能连接起来,我们也可以从图中看到,左边一般为sink,而右边为src,这其实可以理解为生产者,和消费者。

pad

上面提到了两个element必须通过pad才能连接起来,这里引出pad的概念,pad主要有两个属性——数据导向(direction)以及它的时效性(availability),在gstreamer中, Pad是一个element的输入/输出接口,即sink pad (生产者)和 src pad(消费者)。

通俗来讲,一个类比在这里可能会有所帮助。pad似于物理设备上的插头或插孔。例如,考虑一个由放大器、DVD 播放器和(静音)视频投影仪组成的家庭影院系统。允许将 DVD 播放器连接到放大器,因为这两个设备都有音频插孔,并且允许将投影仪连接到 DVD 播放器,因为两个设备都有兼容的视频插孔。由于投影机和放大器的插孔类型不同,可能无法在投影机和放大器之间建立链接。GStreamer 中的Pads 与家庭影院系统中的插孔具有相同的用途。

因此,pad可以被视为元素上的“位置”或“端口”,与其他Element建立链接,并且数据可以通过这些element流入或流出这些element。pad具有特定的数据处理能力:限制流经它的数据类型。只有当两个pad允许的数据类型兼容时,才允许之间进行链接(link)。

从上节图可以看到,sources element(上述数据处理 element)仅包含src pad,sink element(数据输出)仅包含sink pad,而filter 两者皆有,数据的流动形式一般从左到右,即一个pipeline正常来讲,最左边是源pad,最右边为sink pad,但这个顺序是可以反着来的,如果去查询一些gstreamer的pad 与 buffer资料,不过很少反着做就是了。

那一个element有pad了,就可以与其它element两两相link,这就是下面要引出的pipeline与bin。

bin 与 pipeline

bin简单来讲,就是一个element的组合,一个container,就类似于k8s中的pod,编程语言里的类之余element属性。而pipeline就是比bin更完整的一个管道,即有输入、输出以及中间处理的可以启动的pipeline。

bin 将一组链接元素组合成一个逻辑元素。这让整体的逻辑不再处理单个element,而只处理一个element,即 bin。当要构建复杂的管道时,我们将看到这非常强大,因为它允许将管道分解成更小的块。下图为两个示例:

deepstream学习笔记(二):gstreamer与deepstream-test1说明_第6张图片
构建一个简易 bin 原理图
deepstream学习笔记(二):gstreamer与deepstream-test1说明_第7张图片
自定义 playbin 接收器原理

图一是bin的一个原理图,element组合与pad相连,图二是基于图一的基础上,带有两个 Element 和一个 Ghost Pad(即没有element 的一种pad) 的 Bin,这个根据播放教程7,就是playbin的原理图,playbin允许选择所需的音频和视频接收器的两个属性:audio-sinkvideo-sink。应用程序只需要实例化适当的GstElement并 playbin,通过这些属性传递给它。具体的可以看Playback tutorial 7: Custom playbin sinks,会有一个playbin的自定义教程。

而如果将这种bin结构完善成一个完整的图,并给它输入输出,这就具有pipeline的能力,见下图:
deepstream学习笔记(二):gstreamer与deepstream-test1说明_第8张图片
这是一个简化的pipeline,其中包含一个解复用器和两个分支,一个用于音频,一个用于视频。我们还可以将其还原成更加真实的管道环境:deepstream学习笔记(二):gstreamer与deepstream-test1说明_第9张图片

如图所示,这是一个比较标准的流程,首先输入从file-sources ,如果是文件就为filesrc中出来,经过demuxer解复用器用于从ogg文件容器中解复用视频和音频,使这些基本流可用于进一步处理(解码),然后再通过vorbis 编解码器音频解码为原始音频格式,再经过一层转换器,如图中将F32LE压成S16LE格式音频,最后获取到输出。

Communication

GStreamer 为应用程序和管道之间的通信和数据交换提供了多种机制:

  • 缓冲区是用于在管道中的元素之间传递流数据的对象。缓冲区总是从源到汇(下游)。

  • 事件是在元素之间或从应用程序发送到元素的对象。事件可以上游和下游传播。下游事件可以同步到数据流。

  • 消息是由管道消息总线上的元素发布的对象,它们将被保存在那里以供应用程序收集。消息可以从发布消息的元素的流线程上下文中同步拦截,但通常由应用程序从应用程序的主线程异步处理。消息用于以线程安全的方式将错误、标签、状态更改、缓冲状态、重定向等信息从元素传输到应用程序。

  • 查询允许应用程序从管道请求信息,例如持续时间或当前播放位置。查询总是同步回答。元素还可以使用查询从其对等元素请求信息(例如文件大小或持续时间)。它们可以在管道中以两种方式使用,但上游查询更为常见。

deepstream学习笔记(二):gstreamer与deepstream-test1说明_第10张图片
总结一下上面的话就是,gstreamer除了位于下层的buffer,还提供了bus系统以及多种数据类型(Buffers、Events、Messages,Queries)来达到此目的。

这里主要说明一下bus,其余几种字如其名,而bus相当于web框架中的中间件,而且是各种类似HTTP 400以上异常捕获的中间件。这里直接看deepstream-6.1关于common/bus_call.py文件中函数bus的描述:

def bus_call(bus, message, loop):
    t = message.type
    if t == Gst.MessageType.EOS:
        print("Bus call: End-of-stream\n")
        # loop.quit()
    elif t == Gst.MessageType.WARNING:
        err, debug = message.parse_warning()
        sys.stderr.write("Bus call: Warning: %s: %s\n" % (err, debug))
    elif t == Gst.MessageType.ERROR:
        err, debug = message.parse_error()
        sys.stderr.write("Bus call: Error: %s: %s\n" % (err, debug))
        # loop.quit()
    elif t == Gst.MessageType.BUFFERING:
        print("Bus call: Buffering\n")
    elif t == Gst.MessageType.STATE_CHANGED:
        old_state, new_state, pending_state = message.parse_state_changed()
        print((
            f"Bus call: Pipeline state changed from {old_state.value_nick} to {new_state.value_nick} "
            f"(pending {pending_state.value_nick})"
        ))
    else:
        print(f"Bus call: {message}\n")
    return True

此处的message,就是各种出现异常的情况,这里还可以根据没有提到的异常做出不同的判断,比如说接入视频流的时候,突然流断了,这个问题好像在deepstream-6.1的时候解决了,我之前测试5.1的时候还需要自己写,不过当时是测试的c版本。而bus的启动也就是如下几句代码:

# create an event loop and feed gstreamer bus mesages to it
loop = GLib.MainLoop()
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", bus_call, loop)

至此,关于gstreamer一个简单的介绍,就结束了。关于里面更深入层次的东西,比如sink的种类,pad的种类,还有各种element的一些使用情况,再加上nvidia的一些插件进入的时候,这就需要转去看nvidia写得关于gstreamer的一些api的介绍了。

这里贴出一些深入的链接为:

  1. https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_ref_app_deepstream.html (DeepStream Reference Application - deepstream-app)
  2. https://lazka.github.io/pgi-docs/ (python版本的gstreamer 1.0版本的所有接口api介绍)

deepstream-test1 与 deepstream-imagedata-multistream

最后,如果对于上述管道图还理解的不深,可以看看关于管道图的导出,参考Nvidia Deepstream小细节系列:Deepstream python保存pipeline结构图 一文,gstreamer给出了debug_bin_to_dot_file 接口,还需加一个文件保存路径即可生成。

在这里插入图片描述
图:deepstream-test1

在这里插入图片描述
图:deepstream-imagedata-multistream

两幅图为 deepstream-test1deepstream-imagedata-multistream 中得到,我们可以通过管道图粗略地看到,test1是只有一个输入源,而它主要做的复杂场景在h264到yuv412之间,其余的和下面的multistream多输入源管道图走向差不多,deepstream-test1的c/python版本逻辑基本一致,根据nvidia的描述,大致顺序为:

  1. 首先 filesrc 数据源元件负责从磁盘上读取视频数据
  2. h264parse 解析器元件负责对数据进行解析
  3. nvv4l2decoder 编码器元件负责对数据进行解码
  4. nvstreammux 流多路复用器元件负责批处理帧以实现最佳推理性能
  5. nvinfer 推理元件负责实现加速推理
  6. nvvideoconvert 转换器元件负责将数据格式转换为输出显示支持的格式
  7. nvdsosd 可视化元件负责将边框与文本等信息绘制到图像中
  8. nvegltransform 渲染元件和 nveglglessink 接收器元件负责输出到屏幕上

下一篇将解析deepstream-test1文件具体逻辑与一些插件说明,同时,重新整理出基于python版本的deepstream-yolov5版本测试。

你可能感兴趣的:(流媒体相关,音视频,deepstream,nvidia,计算机视觉)