前端摄像头 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

Clawdbot直连Qwen3-32B教程:Webhook事件通知与外部系统自动触发实践

Clawdbot直连Qwen3-32B教程:Webhook事件通知与外部系统自动触发实践 1. 为什么需要直连Qwen3-32B?从被动响应到主动协同 你有没有遇到过这样的场景:用户在聊天界面提问后,系统只是简单返回答案,但后续该做什么——比如创建工单、同步客户信息、触发审批流程——还得手动操作?Clawdbot + Qwen3-32B 的直连方案,正是为了解决这个“最后一公里”问题。 它不只是把大模型接入聊天框,而是让AI真正成为业务流程的“触发器”。当Qwen3-32B在对话中识别出关键意图(例如“我要报修”“申请延期”“查询合同编号”),Clawdbot能立刻通过Webhook,把结构化事件推送给CRM、OA、ERP等任何支持HTTP接收的系统。整个过程无需中间数据库、不依赖定时轮询、没有消息队列配置负担——纯HTTP,轻量、可靠、可追溯。 更重要的是,这套方案用的是你私有部署的Qwen3-32B(320亿参数版本),所有对话数据不出内网,推理结果由Ollama本地托管,安全可控。而Clawdbot作为智能网关,既承担了协议转换(WebSocket ↔ HTTP)、上下

前端小白别慌:3分钟搞定页面插图(附避坑指南+性能彩蛋)

前端小白别慌:3分钟搞定页面插图(附避坑指南+性能彩蛋)

前端小白别慌:3分钟搞定页面插图(附避坑指南+性能彩蛋) * 前端小白别慌:3分钟搞定页面插图(附避坑指南+性能彩蛋) * 为啥前端连个图片都插不明白? * 浏览器加载一张图背后到底在偷偷干啥? * img 标签真就万能了吗? * 响应式图片怎么搞才不被设计师追着骂? * 懒加载、WebP、CDN——这些词听着高大上,其实你早就用过 * 图片加载失败时别让页面变"裂图坟场" * 别再一股脑扔高清大图了,用户流量不是大风刮来的 * 你以为写个 src 就完事了?SEO 和无障碍访问正在偷笑 * 开发时本地图片路径乱成一锅粥?模块化方案来救场 * Webpack/Vite 里图片到底该放哪?public 还是 assets? * 用 CSS 背景图还是 HTML img?这事儿得看场合 * 移动端图片模糊到像开了十级美颜?分辨率适配讲清楚 * 别让图片拖垮首屏速度,Lighthouse 分数掉得比工资还快 * 设计师给的图太大?教你几招无损压缩还不背锅

SpringBoot+Vue 农商对接系统平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

SpringBoot+Vue 农商对接系统平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

摘要 随着乡村振兴战略的深入推进,农产品产销对接成为促进农村经济发展的关键环节。传统农商对接模式存在信息不对称、交易效率低、资源整合不足等问题,亟需通过数字化手段构建高效、透明的对接平台。农商对接系统平台旨在整合农产品生产端与销售端资源,通过线上化交易流程降低中间成本,提升农产品流通效率。该系统聚焦于解决小农户与大市场之间的连接难题,为农产品供需双方提供精准匹配、订单管理、物流跟踪等一站式服务。关键词:乡村振兴、农产品产销、数字化平台、资源整合、供需匹配。 该系统基于SpringBoot+Vue的前后端分离架构开发,后端采用SpringBoot框架实现RESTful API接口,提供用户管理、商品管理、订单管理、数据分析等功能模块。前端使用Vue.js框架构建响应式界面,结合Element UI组件库提升用户体验。数据库采用MySQL存储结构化数据,通过Redis缓存高频访问数据以提升系统性能。系统支持多角色权限控制(农户、采购商、管理员),并集成第三方支付接口与物流查询接口,实现交易闭环。关键词:SpringBoot、Vue.js、MySQL、权限控制、接口集成。 数据表结构说

conda环境怎么配?Hunyuan-MT-7B-WEBUI依赖管理揭秘

conda环境怎么配?Hunyuan-MT-7B-WEBUI依赖管理揭秘 你有没有遇到过这样的情况:下载好 Hunyuan-MT-7B-WEBUI 镜像,兴冲冲启动 Jupyter,双击运行 1键启动.sh,结果终端突然跳出一长串红色报错——ModuleNotFoundError: No module named 'transformers'、ImportError: cannot import name 'AutoTokenizer',甚至更糟的 CUDA version mismatch?别急,这不是模型坏了,也不是你操作错了,而是conda 环境没配对。 这恰恰是绝大多数用户卡在“最后一公里”的真实写照。镜像文档里那句轻描淡写的“运行 1键启动.sh”,背后其实藏着一套精心设计、层层校验的依赖管理体系。它不靠魔法,也不靠运气,而是一套可复现、可调试、可迁移的工程实践。本文就带你一层层剥开