导语 | 所谓图传,就是把相机模组捕捉到的画面,实时传输到另一个可接收该数据的设备上,并且在该设备上进行实时播放。本文将介绍如何基于 VisionSeed 和 Raspberry Pi(4B)搭建一套完整的图传系统,希望与大家一同交流。文章作者:毛江云,腾讯优图实验室研发工程师。
VisionSeed 智能小车实际跑圈演示视频
一、概念介绍
1. Raspberry Pi
树莓派[1]其实不用笔者过多介绍,这应该是做的最成功的开源硬件芯片,深受技术和数码爱好者们的拥护。下图摘自淘宝某店家的中文说明图,总之第四代比第三代功能强了很多,而且好多接口都与时俱进了。
2. VisionSeed
VisionSeed[2] 是腾讯优图推出的一款具备 AI 功能的摄像头模组,产品如下图所示。它的体型很小,有点类似 Raspberry Pi Zero,不过麻雀虽小五脏俱全。
右边是整块 VisionSeed 的核心模块,包括 2 个摄像头(一个 UVC 摄像头,一个红外摄像头),剩下一整块都是 AI 计算单元;左边是控制板块,主要是对外的接口,如串口、TypeC 接口。两块直接通过 FP C 连接起来。
VisionSeed 模组可以搭载很多 CV 的 AI 能力,目前官方已经推出的有疲劳驾驶监测仪[3],笔者目前参加的智能小车就是正在孵化的另一个项目,期待越来越多的 AI 爱好者们参与进来,把 VisionSeed “玩出花”。
二、系统搭建
本文所介绍的是利用 VisionSeed 和 Raspberry Pi(4B) 搭建的一套 基于 FFMPEG 编码+SRS+WIFI 协议+RTMP 协议+FFPLAY 解码播放 的完整图传系统,该实时图传全过程示意图如下所示:
1. RTMP 推流服务器
推流服务器怎么选择 ?其实推流服务器有很多种选型,具体该选择哪种比较好?笔者结合自身经验给出 nginx-rtmp 服务 和 srs 服务 的使用心得和实际对比:
补充说明一下 延时 这块:首先笔者给出的具体延迟时间并非真正服务器推流的延迟,而是端(VisionSeed 采集到视频)到端(播放设备播放视频)的延迟。
这中间涉及到的环节多且复杂:VisionSeed 的 UVC 视频采集,FFMPEG 编码、推流服务器推流、FFPLAY 解码、以及显示器显示。其中推流服务器推流还包括服务器内部 buffer 缓存、网络数据包拼接和组装、以及网络包的传输等。
2. FFMPEG 编码和推流
那么,该 怎么捕捉 VisionSeed 摄像头的视频呢 ?
VisionSeed 上的摄像头是 UVC。 UVC 全称 Usb Video Class ,是一种标准的 USB 视频设备协议,也就是传说中的免驱摄像头。也就是说,这款摄像头可以通过 USB 即插即用,不需要安装驱动。
于是,我们用一根 TypeC 数据线把 VisionSeed 和树莓派连接在一起。如下图所示:
登录 Raspberry Pi,打开 Terminal,查看 UVC 的状态:$
lsusb。如果出现下图红框的部分,就说明 UVC 被系统识别了。
然后,查看 UVC 被挂在哪个节点上:$
ls /dev/video*:
也可以用 v4l2-ctl 进一步仔细查看 /dev/video* 的信息,如命令 v4l2-ctl --device=/dev/video0 --all 可以查看到摄像头的细节信息,命令 v4l2-ctl --list-devices 查看系统中所有的设备等。
”Linux 系统一切皆文件“,也就是说,系统是直接把 /dev/video0 当作文件来进行处理,也就是在 ffmpeg 的 -i 的参数。
那么, 怎么用 FFMPEG 做编码呢 ?
虽然本地可以直接用 ffplay 播放 UVC 的原始流,但是要走 RTMP 协议做图传的话,必须要使用编码流。因此必须在搭载 VisionSeed 的 Raspberry Pi 上做编码,然后推流出去。
FFMPEG CMD 功能强大:
-i :指定文件输入路径;
-an :指不处理音频数据;
-vcodec:指定视频 codec;
-f:指定视频输出格式。
下述命令就是把从 /dev/video0 获取到的原始视频流编码成 flv,并保存成本地文件 test.flv。
综上,FFMPEG 对 UVC 原始视频流做编码就完成了。
3. FFPLAY 收流
必须要确保客户端设备和推流端的 Raspberry Pi 处于同一个局域网下,互相可以联通。
笔者因为项目需要,客户端也选择了 Raspberry Pi +显示屏。但是实际上,选择手上的笔记本或者台式机就可以,只要确保安装了 FFMPEG(FFMPEG、FFPLAY 和 FFPROBE 是打包的)。
那么, 如何用 FFPLAY 实时显示 RTMP 视频呢 ?
笔者原本以为播放端出不了什么问题,万万没想到 FFPLAY 的问题竟然无比的“坑”。首先,很多类似的网站教程提供的播放命令大多是:ffplay -i rtmp://$
{ip}:$
{port}/$
{api}。
这个命令存在相当大的延时,导致笔者最开始就走错了方向。ffplay 内部有 buffer,因此看到的播放画面其实是好几十秒之前的!
ffplay 播放时间长了会有累积延时,也就是越播放到后来,延时越大。并且画面时不时出现卡顿、有时候还会发现画面帧率不稳定,时快时慢。当推流端/服务端断开时,ffplay 画面就卡住了,超过 2 min 也并不会退出。
这些问题该怎样解决呢?下文将会来详细讨论。
三、优化延时
按照以上的流程搭建好之后,就可以在客户端上看到 VisionSeed 的视频画面了。但是,延时巨大,主观感受至少有 10s 的延迟,所以还需要进一步做优化。
1. FFMPEG 硬解码
细心的同学在使用 FFMPEG 做编码的时候,应该发现实际编码推流的帧率大约在 18 左右,运行到后来大概稳定在 10 左右,笔者这边的情况如下图所示:
但是,VisionSeed(关闭算法功能后)的原始视频帧率是 28 fps,分辨率是 1280x720。由此可见 Raspberry Pi 4B 的 CPU 编码速率跟不上,必须要优化。
虽然这个 CMD 没有开启多线程,但是根据笔者经验来看,即使多线程开满了,也很难满足性能要求。
要知道 Raspberry Pi 4B 是有专用编解码模块的,官方号称性能是 1080p@30fps 的编码能力,而端侧开发就是要“榨干”每一个芯片模组的过程。
查了资料后了解到,Raspberry Pi 的硬解码支持 OPENMAX 标准[4],这是一种类似 vaapi 等多媒体硬件加速的统一接口,因此可以直接用 h264_max 来调用底层硬解码,然后再推送到推流服务器上,命令如下:
此外,对实时性要求再高一点的同学,不妨再多了解些 FFMPEG 的参数,参见 FFmpeg Formats Documentation[5] 和 H.264 Video Encoding Guide[6],笔者下面摘录一些跟效率相关的参数,大家可以选择使用(如果有更多未列出来的,欢迎大家留言补充):
而笔者最后使用的命令如下:
2. 用 srs 推流服务器,开启优化参数
从最终结果上来看,替换了 srs 服务器之后,时延确实比用 nignx-rtmp 提升了 400 ms。
但是这到底是因为 srs 确实比 nginx-rtmp 优秀呢,还是因为笔者打开 nginx-rtmp 方式不正确,还有待讨论。对这块有了解的大佬们,欢迎留言告知,不胜感激!
修改 srs.conf 如下:
配置修改之后的确实时性得到了很大的提升。
3. 优化 FFPLAY
上文出现的问题,在这里也为大家一一解答。
问题一: ffplay 内部有 buffer,因此看到播放的画面是好几十秒之前的。
解决方法:关闭 buffer!
参考 ffplay Documentation[7],参数 -fflags nobuffer(FFMPEG 命令里面也有这个参数)是最关键的。笔者最后采用了:
问题二: ffplay 播放时间长了会有累积延时,也就是越播放到后来,延时越大。并且画面时不时出现卡顿、有时候还会发现画面帧率不稳定,时快时慢。
解决方法:自从用了 srs,累积延时的问题就没有了。至于画面不稳定的问题,可能和网络有关,笔者后面也会提到怎么在树莓派上搭建无线 AP 来提供专有无线局域网。
替换到无线 AP 之后,画面卡顿的情况会好很多。但是经过长时间的观察,还是会有帧率不稳定的情况。
问题三 :当推流端/服务端断开时,ffplay 画面就卡主了!超过 2 min 也并不会退出。
解决方法:这个其实就是 FFPLAY 的 bug !其实,ffplay 提供了几个参数,一个是 -autoexit,但是它对 RTMP 还有 RTSP 都不起作用,当流断开或者网断开的时候, ffplay 还是卡住的。
要想解决就必须修改源码,重新编译 ffplay。修改办法可以参考文档[8] 。
另一个是 -timeout 参数,但是一旦加上它,ffplay 就跑不起来,具体原因参考文章[9]。
4. Raspberry Pi 上搭建无线 AP
怎么基于 Raspberry Pi 搭建无线 AP 是有官方教程的:How to use your Raspberry Pi as a wireless access point[10]。但是,官方教程是有坑的,下文将重点介绍哪些坑需要避开。
无线 AP,全称 Wireless Access Point,其实就是常说的 WIFI 热点,生活中的路由器也是一种无线 AP 设备。
在我们的环境中,可能会在没有无线网环境下,甚至在网络条件很糟糕的环境下。另外 Raspberry Pi 4B 本身自带了无线网卡,因此不妨用它搭建无线 AP,客户端直接接入它的网络就可以接受它的流数据。
而且,从网络链路上来说,无线 AP 还能在原来基础上减少路由器转发的环节。网络拓扑图如下图所示,优化的链路环节是把蓝色虚线替换了红色虚线和实线。
接下来,跟着官方教程一步步走:
(1)升级 apt-get 工具
建议国内的小伙伴们替换一下源,笔者早就已经替换到了清华源,教程网上很多,推荐树莓派 3b 更换国内源[11] 。
/etc/apt/sources.list 修改为:
/etc/apt/sources.list.d/raspi.list 修改为:
(2)安装 hostapd 和 dnsmasq
hostapd 就是对外提供热点的主要服务,dnsmasq 则是负责 dns 和 dhcp 作用的。如果操作完毕后,没有搜索到自己设置的热点 WIFI,大概率是 hostapd 的问题(原因:hostapd 没有工作,因此没有对外提供 AP);如果搜到了热点网络,但是一直连不上的话,大概率是 dnsmasq 的问题(原因:dnsmasq 没有工作,没办法为客户端分配 ip)。
Raspberry Pi 为自己分为静态 IP。笔者这里的设置如下:
(3)配置 DHCP
笔者这里的配置如下:
(4)修改 hostapd 配置
这一个步骤是最容易出问题的步骤,而且官方提供的配置在笔者的环境中并不能起作用。笔者给出自己的配置,并在注释中说明为什么这么配置:
其实做到这一步之后,基本就达到目标了。一个可用的无线 AP 就搭建好了。客户端只需要连接到刚刚设置的网络,就可以和这台 Raspberry Pi 通信。
终于,笔者把端到端的延时从十几秒优化到 400 ms 左右(一把辛酸泪)!最后,如果您有更多的优化方案,欢迎留言与我讨论~
参考资料:
[1] Raspberry Pi:
[2] VisionSeed:
https://visionseed.youtu.qq.com/#/home
[3] 疲劳驾驶监测仪:
https://zhuanlan.zhihu.com/p/77190381
[4] OPENMAX 标准:
https://zh.wikipedia.org/wiki/OpenMAX
[5] FFmpeg Formats Documentation:
https://ffmpeg.org/ffmpeg-formats.html
[6] H.264 Video Encoding Guide:
https://trac.ffmpeg.org/wiki/Encode/H.264
[7] ffplay Documentation:
https://ffmpeg.org/ffplay-all.html
[8] -autoexit 参数修改参考:
http://ffmpeg.org/pipermail/ffmpeg-devel/2020-August/268749.html
[9] -timeout 参数修改参考:
https://www.jianshu.com/p/e75e3f1fb6b0
[10] How to use your Raspberry Pi as a wireless access point:
https://thepi.io/how-to-use-your-raspberry-pi-as-a-wireless-access-point/
[11] 树莓派 3b 更换国内源:
https://my.oschina.net/TimeCarving/blog/1622950
本文转载自公众号云加社区(ID:QcloudCommunity)。
原文链接:
如何利用VisionSeed+树莓派,实现智能小车实时图传系统?
评论 2 条评论