当前位置:网站首页>deepstream学习笔记(二):gstreamer与deepstream-test1说明
deepstream学习笔记(二):gstreamer与deepstream-test1说明
2022-07-23 16:19:00 【submarineas】
Gstreamer介绍
GStreamer 是一个用于创建流媒体应用程序的框架。基本设计来自俄勒冈研究生院的视频管道,以及来自 DirectShow 的一些想法。
GStreamer 的开发框架使得编写任何类型的流媒体应用程序成为可能。GStreamer 框架旨在使编写处理音频或视频或两者的应用程序变得容易。它不限于音频和视频,可以处理任何类型的数据流。管道设计的开销比所应用的过滤器所产生的开销小。这使得 GStreamer 成为一个很好的框架,用于设计对延迟有很高要求的高端音频应用程序。
GStreamer 最明显的用途之一是使用它来构建媒体播放器。GStreamer 已经包含用于构建可以支持多种格式的媒体播放器的组件,包括 MP3、Ogg/Vorbis、MPEG-1/2、AVI、Quicktime、mod 等。然而,GStreamer 不仅仅是另一个媒体播放器。它的主要优点是可以将可插拔组件混合并匹配到任意管道中,以便编写成熟的视频或音频编辑应用程序。
gstreamer简介
GStreamer 的核心功能是为插件、数据流和媒体类型处理/协商提供框架。它还提供了一个 API 来使用各种插件编写应用程序。
更具体的说明与文字表述参照官方文档中第一章(What is GStreamer?),这里直接引出架构设计图:
具体来说,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.0 与 gst-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:[email protected]192.168.0.10:554/MediaInput/h264
还有很多很好玩的实例可以自己尝试,这里只是引出一部分,另外,作为对比,之前我写过一篇关于ffmpeg针对图像和视频的一篇博文,可以对照参考:
gstreamer 组成说明
架构图下方蓝色部分,蓝框主要是gstreamer的Element,在官方文档中,gstreamer内部最重要的四个组成部分为: Elements,Pads,Bins and pipelines,Communication 。其中需主要理解的是前三个,第四个字面意思是通信,但其实比较浅显易懂,deepstream的通信方式主推kafka,如果用过的话,基本没有什么问题。
Element
对于应用程序员来说,GStreamer 中最重要的对象就是 GstElement 对象。元素是媒体管道的基本构建块。使用的所有不同的高级组件都源自 GstElement. 每个解码器、编码器、解复用器、视频或音频输出实际上都是一个GstElement ,可以理解为是一个黑盒,一端进去,一端出来,具体见下面四个element实例:
![]() 数据源处理 element (生成器) | ![]() 视频滤波器 element |
![]() 解复用器 element | ![]() 数据输出 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。当要构建复杂的管道时,我们将看到这非常强大,因为它允许将管道分解成更小的块。下图为两个示例:
![]() 构建一个简易 bin 原理图 | ![]() 自定义 playbin 接收器原理 |
图一是bin的一个原理图,element组合与pad相连,图二是基于图一的基础上,带有两个 Element 和一个 Ghost Pad(即没有element 的一种pad) 的 Bin,这个根据播放教程7,就是playbin的原理图,playbin允许选择所需的音频和视频接收器的两个属性:audio-sink和video-sink。应用程序只需要实例化适当的GstElement并 playbin,通过这些属性传递给它。具体的可以看Playback tutorial 7: Custom playbin sinks,会有一个playbin的自定义教程。
而如果将这种bin结构完善成一个完整的图,并给它输入输出,这就具有pipeline的能力,见下图:
这是一个简化的pipeline,其中包含一个解复用器和两个分支,一个用于音频,一个用于视频。我们还可以将其还原成更加真实的管道环境:
如图所示,这是一个比较标准的流程,首先输入从file-sources ,如果是文件就为filesrc中出来,经过demuxer解复用器用于从ogg文件容器中解复用视频和音频,使这些基本流可用于进一步处理(解码),然后再通过vorbis 编解码器音频解码为原始音频格式,再经过一层转换器,如图中将F32LE压成S16LE格式音频,最后获取到输出。
Communication
GStreamer 为应用程序和管道之间的通信和数据交换提供了多种机制:
缓冲区是用于在管道中的元素之间传递流数据的对象。缓冲区总是从源到汇(下游)。
事件是在元素之间或从应用程序发送到元素的对象。事件可以上游和下游传播。下游事件可以同步到数据流。
消息是由管道消息总线上的元素发布的对象,它们将被保存在那里以供应用程序收集。消息可以从发布消息的元素的流线程上下文中同步拦截,但通常由应用程序从应用程序的主线程异步处理。消息用于以线程安全的方式将错误、标签、状态更改、缓冲状态、重定向等信息从元素传输到应用程序。
查询允许应用程序从管道请求信息,例如持续时间或当前播放位置。查询总是同步回答。元素还可以使用查询从其对等元素请求信息(例如文件大小或持续时间)。它们可以在管道中以两种方式使用,但上游查询更为常见。

总结一下上面的话就是,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的介绍了。
这里贴出一些深入的链接为:
- https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_ref_app_deepstream.html (DeepStream Reference Application - deepstream-app)
- 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-test1 与 deepstream-imagedata-multistream 中得到,我们可以通过管道图粗略地看到,test1是只有一个输入源,而它主要做的复杂场景在h264到yuv412之间,其余的和下面的multistream多输入源管道图走向差不多,deepstream-test1的c/python版本逻辑基本一致,根据nvidia的描述,大致顺序为:
- 首先 filesrc 数据源元件负责从磁盘上读取视频数据
- h264parse 解析器元件负责对数据进行解析
- nvv4l2decoder 编码器元件负责对数据进行解码
- nvstreammux 流多路复用器元件负责批处理帧以实现最佳推理性能
- nvinfer 推理元件负责实现加速推理
- nvvideoconvert 转换器元件负责将数据格式转换为输出显示支持的格式
- nvdsosd 可视化元件负责将边框与文本等信息绘制到图像中
- nvegltransform 渲染元件和 nveglglessink 接收器元件负责输出到屏幕上
下一篇将解析deepstream-test1文件具体逻辑与一些插件说明,同时,重新整理出基于python版本的deepstream-yolov5版本测试。
边栏推荐
- Lin Zhiying is still in the intensive care unit and will undergo a second round of surgery: the police said he did not wear a seat belt
- 程序员最想干的三件事 |漫画
- Where should we start to learn modeling from zero foundation? How to learn game modeling well?
- 20220721 积分环节的时频域分析
- 悲观锁和乐观锁
- String length function strlen().. String function header file string.h "suggestions collection"
- JVM – thoroughly understand and break the parental delegation mechanism
- 错误“ Failed to fetch “xxx”Temporary failure resolvingW: Some index files failed to download“解决办法
- MySQL massive write problem optimization scheme MySQL parameter tuning
- 【JZOF】13機器人的運動範圍
猜你喜欢

学次世代建模是场景好还是角色好?选对职业薪资多一半

Rhcsa Notes 6

CSR, SSR and SSG

The great heat of the twenty-four solar terms

有人是靠自学建模找到工作的吗?千万别让这些思维害了你

rhcsa笔记四

Deep learning learning record - update of learning rate of optimizer

本周一问 | Vivado 综合阶段什么约束生效?

How to capture the analyst rating data of Sina Financial Data Center?

go中高并发下的锁是如何工作的(结合源码)
随机推荐
数据库建模
rhcsa笔记七
MySQL事务,从redo log、bin log、undo log说起...
Redis data loss problem
Where should we start to learn modeling from zero foundation? How to learn game modeling well?
Prevent and control the summer market blowout after adjustment, and evaluate the summer activities of Tujia, muniao and meituan
搭建PHP开发环境(Apache+PHP+MySQL)「建议收藏」
ZigBee system development of Internet of things I (Introduction to ZigBee) [easy to understand]
【游戏建模模型制作技巧分享】ZBrush如何调整笔刷大小
SQLZOO——SELECT from Nobel Tutorial
【游戏建模模型制作全流程】用ZBrush制作游戏士兵角色
rhcsa笔记三
【通俗易懂】关系模式范式分解教程 3NF与BCNF口诀!小白也能看懂「建议收藏」
吉时利静电计在新能源电池测试方案的应用
【Coggle 30 Days of ML】糖尿病遗传风险检测挑战赛(2)
Deep learning learning record - update of learning rate of optimizer
80+嘉宾登台,10余国用户参会,7万+观众收看,「GWEI 2022-新加坡」落幕
20220721 积分环节的时频域分析
Ubuntu 22.04 installing mysql8
C language · structure (Introduction to linear table)





