本文记录了在摄像头 RTSP 流视频多路实时监控项目中,落地的一套「多路 RTSP 低延迟播放」方案的全过程:从选型、编码、到 Web/桌面端播放与硬解优化。
一、需求现状
现场有一个远程监控端,需要同时监控多台车载设备的摄像头画面,每台设备约 6 路摄像头,摄像头输出 RTSP(视频 H.264;部分摄像头型号还有音频)。由于是车载实时摄像头,关键的不是能播,而是多路、低延迟(由于在现场操作需要实时反馈,所以需要 1 秒以内)、低 CPU 占用,因此核心需求可以总结成四点:
- 多路并发:同屏 6+ 路播放,最多一个监控端同时播放 12 路视频;
- 低延迟:操作链路希望接近实时(目标 < 500ms 级);
- 桌面端:需要有编码控制能力,最好 Qt 桌面程序;
- 性能与稳定性:客户端需要稳定走 GPU 硬解,否则多路全走 CPU 软解解码会被拖死;
- 部署/运维复杂度:比如要更换视频流接入的时候要足够方便,最好能配置后一键部署;
二、技术选型
经过一番调研,主要有下面几个方案:
HLS
HLS 的核心思路是把连续视频切成一段段 TS 分片(segment),服务器创建并动态更新一个 .m3u8 播放列表文件,这个文件里记录了所有 .ts 视频切片的文件名、顺序、时长等信息,客户端按 m3u8 索引列表去拉分片播放。它的优点是兼容性很好,所有现代浏览器和操作系统都原生支持,适合普通直播、点播和 CDN 分发。
在一个 TS 分片没有播完并同步到 m3u8 之前,客户端是无法看到最新画面的。这天生决定了 HLS 的实时性不高,一般在 3 秒以上,加上各个链路的延迟和缓冲,总延迟轻松到达 10 秒左右,这对于实时监控是完全不可接受的。
HTTP-FLV
HTTP-FLV 通常是服务器将音视频数据用 FLV(Flash Video)格式进行封装,通过客户端和服务端建立的 HTTP 长连接流式传输到播放器上,延迟在 1-3s,可以满足一定的实时性需求。缺点是需要前端引入 flv.js 之类的 JS 库在 JS 层对 FLV 流解封装成 H.264、音频等数据,尤其在多路并发时,CPU 占用会比较高,且浏览器对 FLV 的支持不如 HLS/WebRTC 原生。
海康 WebSDK 的 WebSocket
海康的 WebSDK 提供了基于 WebSocket 的视频流传输能力,延迟可以做到 1 秒以内,适合海康设备的接入,但我看有些老一些的海康设备可能不支持 WebSocket,而且 WebSDK 要求 Chrome 版本不低于 91,如果你的摄像头都支持 WebSocket 且能保证浏览器的 chromium 内核版本在 91+,那么这个方式也是可行的。
WebRTC
WebRTC(Web Real-Time Communication)是目前实时音视频通信的主流技术,现代浏览器(Chrome, Firefox, Safari 等)都原生支持 WebRTC 协议栈,无需安装插件,实时性通常能做到 500ms 左右,非常适合对实时性要求高的监控场景。WebRTC 的设计目标就是实时通话与互动,浏览器对其实现非常成熟。对于 H.264 等常见编码格式,浏览器能直接调用 GPU 进行硬件加速解码,显著降低 CPU 占用。
三、核心实践:通过 SRS 将 RTSP + H.264 视频流转封装为 WebRTC + H.264
我最终使用 SRS(Simple Realtime Server)开源流媒体服务器,一直在稳定维护,也有不少用到生产级环境的案例,文档有中文,部署也比较简单,直接 docker 拉一个镜像然后一行命令就能启动,另外它自带视频流录制功能,后期做录制也方便。
在远程监控服务器上用 Docker 部署 SRS,把摄像头 RTSP 拉到本机后,再以 WebRTC 的方式提供给前端播放。
这里要强调一个关键点:尽量做转封装(Remux),避免转码(Transcode),摄像头已经输出 RTSP 包着的 H.264 服务器只需要转协议成 WebRTC 包着 H.264 即可,不需要重新编码,转码会带来很大的 CPU 负担和延迟。
整体思路:摄像头推流 RTSP(H.264) ,经过 SRS ingest 拉流并通过 rtmp_to_rtc 转封装为 WebRTC(H.264) ,前端浏览器用 <video> 播放。
3.1 SRS 核心配置解析
SRS 的配置文件 srs.conf 是核心。下面用一个最小配置片段:
listen 1935; max_connections 1000; daemon off; srs_log_tank file; srs_log_file /usr/local/srs/objs/logs/srs.log; rtc_server {
enabled on;
listen 8000
candidate $CANDIDATE
}
http_server {
enabled on
listen 8080
dir ./objs/nginx/html
crossdomain on
}
vhost __defaultVhost__ {
rtc {
enabled on
rtmp_to_rtc on
nack on
twcc on
}
ingest camera_RIG001_0 {
enabled on
input {
type stream
url rtsp://admin:[email protected]:554/Streaming/Channels/101
}
ffmpeg ./objs/ffmpeg/bin/ffmpeg
engine {
enabled on
perfile {
rtsp_transport tcp
fflags nobuffer
flags low_delay
probesize 32
analyzeduration 0
max_delay 0
}
vcodec copy
acodec copy
output rtmp://127.0.0.1:/live/camera_RIG001_0?=[vhost]
}
}
}

