前端摄像头 RTSP 流视频多路实时监控解决方案实践

本文记录我在摄像头 RTSP 流视频多路实时监控项目里,落地的一套「多路 RTSP 低延迟播放」方案的全过程:从选型、编码、到Web/桌面端播放与硬解优化。

一、需求现状

现场有一个远程监控端,需要同时监控多台车载设备的摄像头画面,每台设备约 6 路摄像头,摄像头输出 RTSP(视频 H.264;部分摄像头型号还有音频),由于是车载实时摄像头,关键的不是能播,而是多路、低延迟(由于在现场操作需要实时反馈,所以需要 1 秒以内)、低 CPU 占用,因此核心需求可以总结成四点:

  1. 多路并发:同屏 6+ 路播放,最多一个监控端同时播放 12 路视频;
  2. 低延迟:操作链路希望接近实时(目标 < 500ms 级);
  3. 桌面端:需要有编码控制能力,最好 Qt 桌面程序;
  4. 性能与稳定性:客户端需要稳定走 GPU 硬解,否则多路全走 CPU 软解解码会被拖死;
  5. 部署/运维复杂度:比如要更换视频流接入的时候要足够方便,最好能配置后一键部署;

二、技术选型

经过一番调研,主要有下面几个方案:

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;           # 启用 RTC 服务器     listen 8000;          # WebRTC UDP 端口     candidate $CANDIDATE; # 自动获取服务器 IP } http_server {     enabled on;            # 启用 HTTP 服务器     listen 8080;     dir ./objs/nginx/html; # 默认打开是控制台,也可以改为自己的前端页面     crossdomain on; } vhost __defaultVhost__ {     rtc {         enabled on;     # 启用 RTC 功能         rtmp_to_rtc on; # 开启 RTMP 到 RTC 的转换         nack on;        # 开启丢包重传         twcc on;        # 开启拥塞控制     }     ingest camera_RIG001_0 {      # 定义 Ingest 拉流配置,主动拉取摄像头 RTSP 流         enabled on;         input {             type stream;             url rtsp://admin:[email protected]:554/Streaming/Channels/101;  # 你摄像头的rtsp地址         }         ffmpeg ./objs/ffmpeg/bin/ffmpeg; # 使用 SRS 内置的 FFmpeg         engine {             enabled on;             perfile {                 rtsp_transport tcp;                 fflags nobuffer;                 flags low_delay;                 probesize 32;                 analyzeduration 0;                 max_delay 0;             }             vcodec copy; # 视频流直接复制,不转码             acodec copy; # 音频流直接复制,我将摄像头的音频输出格式配为 AAC             output rtmp://127.0.0.1:[port]/live/camera_RIG001_0?vhost=[vhost];         }     } }

3.2 Docker 一键配置

由于现场设备的 IP 和摄像头数量可能会变化,手动修改 srs.conf 比较繁琐且容易出错。我做了一套自动化配置流程:

  1. 配置文件:提供一个 JSON 文件,维护用户摄像头的 IP/Port 列表。
  2. 生成脚本:读取摄像头列表,生成 srs.conf,并在上一部配置变更时更新文件。
  3. 自动重启:检测到配置变更后,执行 docker restart srs 让配置生效。

Docker 命令:

# 拉取 SRS 镜像并打标签 docker pull registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5 docker tag registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5 ossrs/srs:5 # 运行 SRS 容器 docker run -d --name srs \    --restart=always \     -p 1935:1935 \     -p 1985:1985 \     -p 8080:8080 \     -p 8000:8000/udp \     -e CANDIDATE="127.0.0.1" \     -v ~/hello/config/srs_latest.conf:/usr/local/srs/conf/srs.conf \    ossrs/srs:5 \    ./objs/srs -c conf/srs.conf

其中:

  • 1935/tcp:RTMP(SRS 内部回环推流也会用到)
  • 1985/tcp:HTTP API
  • 8080/tcp:HTTP Server(SRS 自带的控制台页面)
  • 8000/udp:WebRTC(RTC Server)

我实际工程里会把 改摄像头配置 -> 生成 srs.conf -> 重启容器 这套动作做成一键脚本,避免手工改配置带来的维护成本,规范一点可以弄个网页让用户维护摄像头配置。

四、前端落地

1. Web 端播放

由于浏览器对 WebRTC 原生支持比较好,标准 HTML5 <video> 可以直接接住 WebRTC 流播放,我在前端页面里直接用 <video> 标签播放 SRS 输出的 WebRTC 流,让浏览器原生解码器(通常能自动走 H.264 硬解)解码,前端不需要引入其他第三方库,CPU 占用也更可控。

前端的静态资源服务器可以是 Nginx,也可以是其它轻量方案,比如 SRS 自带的 HTTP Server 就可以直接用来托管前端页面,或者 nodejs、python 都行。

2. 桌面化(Qt / QWebEngine)

为了集成到现有的 [email protected] 桌面应用中,我用 QWebEngineView 直接嵌入前端页面,这样 UI 与 Web 端可以最大化复用。

这里有一个必踩的坑:

QT 官方包里带的 QWebEngine 因为专利原因是不包含 H.264 编解码能力的,需要下载源码自行编译并增加 -webengine-proprietary-codecs 编译指令启用专有编解码器支持 ,才能让 QWebEngineView 的 Chromium 内核支持 H.264 编码。

另外注意让内置浏览器的视频解码尽可能走 VA-API 等硬解路径,可以在 QWebEngineView 里打开 chrome://gpu 看一下最下面的 Video Acceleration Information 有没有 H.264 硬解支持。我增加的 QT 环境变量如下:

// 开启 H.264 和 WebRTC 支持 qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--enable-features=VaapiVideoDecoder,VaapiVideoEncoder,VaapiIgnoreDriverChecks,VaapiVideoDecodeLinuxGL --ignore-gpu-blocklist --enable-gpu-rasterization --in-process-gpu --disable-features=UseChromeOSDirectVideoDecoder --limit-fps=20 --num-raster-threads=4 ");
优化项:SmartH264

注意在摄像头的配置中,将摄像头的 H.264 编码参数调优为 SmartH264,可以让摄像头在画面静止时降低码率(在画面不怎么动的情况下可以最大降低 90% 码流),减少网络带宽占用,同时在画面有变化时提升码率和帧率,保证画面质量。这样在多路并发时,可以显著降低整体的网络和解码压力。

优化项:硬解验证与驱动选择

在使用 intel 集显的 i7-7700 测试时,通过指定集显使用特定的 VA-API 硬件加速驱动 export LIBVA_DRIVER_NAME=iHD,强制开启 VA-API 硬解:

# 通过下面这个命令查看当前 GPU 使用情况 sudo intel_gpu_top

如果 intel_gpu_top 命令 Engine 的 Video 不为 0,则说明硬解成功,事实证明如果可以成功开启 GPU 硬解,那么即使使用集显,CPU 负载也能保持较低水平。如果使用 AMD/NVIDIA 显卡,需要参考对应的 VA-API 驱动文档,确保硬解被正确启用。

五、总结

通过 SRS 将 RTSP + H.264 视频流转封装为 WebRTC + H.264,并在前端浏览器和 Qt 桌面应用中播放,成功实现了多路低延迟的实时视频监控需求。我这边在实际测试中,i7-7700 的 CPU 上使用集显 GPU 播放 6 路 1080p H.264 流,CPU 总占用保持在 20% 左右,延迟控制在 300-500ms 之间。


参考:

SRS Getting StartedQt WebEngine H.264 Feature

最后

如果你觉得这篇内容对你挺有启发,我想邀请你帮我个小忙:

  1. 点个「喜欢」或「在看」,让更多的人也能看到这篇内容
  2. 我组建了个氛围非常好的前端群,里面有很多前端小伙伴,欢迎加我微信「sherlocked_93」拉你加群,一起交流和学习
  3. 关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。
图片

点个喜欢支持我吧,在看就更好了

Read more

万字长文带你梳理Llama开源家族:从Llama-1到Llama-3,看这一篇就够了!

万字长文带你梳理Llama开源家族:从Llama-1到Llama-3,看这一篇就够了!

在AI领域,大模型的发展正以前所未有的速度推进技术的边界。 北京时间4月19日凌晨,Meta在官网上官宣了Llama-3,作为继Llama-1、Llama-2和Code-Llama之后的第三代模型,Llama-3在多个基准测试中实现了全面领先,性能优于业界同类最先进的模型。 纵观Llama系列模型,从版本1到3,展示了大规模预训练语言模型的演进及其在实际应用中的显著潜力。这些模型不仅在技术上不断刷新纪录,更在商业和学术界产生了深远的影响。因此,对Llama模型不同版本之间的系统对比,不仅可以揭示技术进步的具体细节,也能帮助我们理解这些高级模型如何解决现实世界的复杂问题。 1、Llama进化史 本节将对每个版本的Llama模型进行简要介绍,包括它们发布的时间和主要特点。 1.1 Llama-1 系列 Llama-1 [1]是Meta在2023年2月发布的大语言模型,是当时性能非常出色的开源模型之一,有7B、13B、30B和65B四个参数量版本。Llama-1各个参数量版本都在超过1T token的语料上进行了预训训练,其中,最大的65B参数的模型在2,048张A100 80

Llama Factory

1. Llama Factory 到底是什么? 1.1 简单比喻 想象你要定制一辆汽车: 传统方式(没有 Llama Factory): * 你需要自己造发动机、设计车身、组装零件 * 需要懂机械工程、电子技术、材料科学 * 整个过程复杂、容易出错、耗时很长 使用 Llama Factory: * 你只需要: 1. 选择基础车型(预训练模型) 2. 告诉工厂你的需求(训练数据) 3. 选择改装方案(训练方法) 4. 工厂自动完成所有改装 * 你不需要懂技术细节,只需要提需求 1.2 一句话定义 Llama Factory 是一个"AI模型定制工厂",它让普通人也能轻松地定制和训练自己的大语言模型。 2. 为什么需要

【AIGC】冷启动数据与多阶段训练在 DeepSeek 中的作用

【AIGC】冷启动数据与多阶段训练在 DeepSeek 中的作用

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]本文专栏: AIGC |ChatGPT 文章目录 * 💯前言 * 💯冷启动数据的作用 * 冷启动数据设计 * 💯多阶段训练的作用 * 阶段 1:冷启动微调 * 阶段 2:推理导向强化学习(RL) * 阶段 3:拒绝采样与监督微调(SFT) * 阶段 4:多场景强化学习 * 💯代码示例:冷启动数据与多阶段训练的实现 * 1. 冷启动微调阶段 * 作用与应用: * 2. 推理导向的强化学习阶段 * 作用与应用: * 3. 拒绝采样与监督微调阶段 * 作用与应用: * 4. 多场景强化学习 * 作用与应用: * 总体流程 * DeepSeek 中的应用 * 💯总结 💯前言 在人工智能领域,深度学习模型的训练和优化往往需要大量的标注数据和计算资源。然而,面对复杂任务时,即使是最先进的技术和大量的训练数据也未必能够保证模型的最优表现。DeepSeek

GPU PRO 4 - 5.1 An Aspect-Based Engine Architecture 笔记

本笔记仅为个人的理解,如果有误欢迎指出 An Aspect-Based Engine Architecture 一种基于方面的引擎架构         不是很明白为什么GPU的书籍会有游戏引擎架构的文章。         这里Aspect在文章中的意义更像是表述一个功能模块,在Java中有将Aspect翻译成切面,但是Java切面主要是横向的代码注入,与本文的概念不相符。 大多数系统架构都会考虑将各个功能封装成模块或者组件,在面向对象编程的思想下,这个封装是基于对象去实现的,本文则描述了一种在引擎层面的封装功能的架构思想,封装后的产物被称为Aspect,每一个Aspect负责提供一些功能子集,并通过一个通用的接口与引擎核心通信。 引擎核心:         引擎核心的功能是保存游戏或者仿真时的数据结构以及相关状态,功能Aspect将会与这些数据进行交互。一般来说引擎核心会定义一些接口,外部的Aspect则通过接口访问当前的游戏数据                  用MVC架构的角度去理解的话引擎核心相当于M层,而各个Aspect则相当于C层。