浏览器播放RTSP视频流:VLC转HTTP-TS安防监控实战
发布时间:2026/10/2 15:45:40来源:尧图网络
做安防监控、工业视觉或者设备可视化面板的朋友基本都撞过同一面墙摄像头给出来的地址是rtsp://开头的前端页面里写video srcrtsp://192.168.1.64:554/...结果就是一块纯白的空白区域控制台干净得像什么都没发生。原因其实不复杂浏览器内核从设计之初就没打算实现 RTSP 这个协议它认的是 http(s)、blob、MediaSource 这几类资源。所以“浏览器播放 rtsp 视频流”这件事本质上不是前端写错了代码而是中间必须有人把 rtsp 翻译成浏览器听得懂的话。我目前手上跑着十几路摄像头的看板主机就是一台退役的小工控机方案全程没写一行后端代码靠的就是 VLC 的流输出功能sout把 rtsp 拉下来重新封装或者转码再用 http 协议吐出去前端用 hls.js 或者 mpegts.js 接住播放。这套“VLC 转 http”的路子优点就是零开发成本、上手快、单机就能跑缺点也很明显延迟不如 WebRTC 那么激进多路并发的时候 CPU 是真吃紧。这篇文章我会把选型逻辑、VLC 命令行里每一个参数的含义、前端播放器代码、多路并发的端口规划以及我踩过的坑全部摊开讲。适合做安防大屏、设备监控面板、实验室原型验证的前端和运维同学看就算你只想复制命令跑通也没问题但我更希望你读完能自己判断“为什么这一路画面卡、那一路延迟越拖越高”。1. 浏览器为什么播不了 rtsp先把根因说透1.1 rtsp 是“遥控器协议”不是“画面协议”很多人对 RTSP 有个误解以为它跟 http 一样是直接把视频数据搬过来的。其实 RTSP 更像电视遥控器它负责发“播放”“暂停”“切到主码流”这类控制指令真正的画面数据是通过另一条通道RTP over UDP 或者 RTP over TCP送过来的。浏览器里那个video标签天生只处理“一整个可以直接解码的音视频资源”它既不会发 RTSP 的 DESCRIBE/SETUP/PLAY 握手也不会解析 SDP 描述文件更不会去处理 RTP 包和 RTCP 反馈。这三层能力缺一层video srcrtsp://...就必然是空白。这也解释了一个常见现象你在本地装个 VLC 播放器电脑版双击那个 rtsp 地址立刻就能出画面——因为 VLC 里完整实现了 RTSP 客户端栈和 RTP 解包。浏览器没有这套东西所以必须有一个进程代替浏览器去完成 RTSP 握手、RTP 收包、解封装再换成 HTTP 长连接把数据交给页面。这个“中间人”可以是后端服务FFmpeg、GStreamer、自研服务也可以是 VLC 本身因为 VLC 的 sout 模块天生就是干这个的。1.2 四条主流路线各自适合什么场景在动手之前我建议先想清楚延迟要求因为方案一旦选错后面优化会很痛苦。下面这张表是我实际做过对比之后总结的数值都是局域网环境、H.264 主码流下的经验值。方案中间件浏览器接入方式端到端延迟实现难度适合场景HTTP-TS 直出VLC / FFmpegmpegts.js1.5~3 秒低局域网看板、多路轮播HLS 切片VLC livehttp Nginxhls.js / 原生播放器4~12 秒中需要兼容移动端、弱网WebRTC 转发自研信令服务RTCPeerConnection200~500 毫秒高云台操控、实时对讲后端 MP4 分段FFmpeg 切片 静态服务video 标签10 秒以上低回放、非实时预览如果只是给中控室做一块“看看现场在不在”的看板HTTP-TS 直出是最划算的VLC 一条命令就能起前端几十行代码搞定。如果需要安卓手机、平板也能扫码看HLS 更省心因为安卓原生播放器和 Safari 对 m3u8 是原生支持的连 JS 库都不用引。只有当你需要云台按钮按下去画面立刻响应时才值得上 WebRTC 那套复杂度。我自己的项目里是“双轨”跑的主看板走 HTTP-TS 拿低延迟移动端分享链接走 HLS 拿兼容性两路共用同一台 VLC 实例的不同 sout 输出。这样做的好处是摄像机的取流连接数不会翻倍很多网络摄像机对同时连接的客户端数量有限制常见是 6 路多开一个客户端就可能把后面的人挤掉。2. VLC 转 http 方案的架构设计与关键选型2.1 sout 链的三段式结构看懂这一句就通了VLC 的流输出用一套叫 sout 的链式语法描述形式是#模块1{参数}:模块2{参数}:模块3{参数}从左到右数据依次流过。最常见的三段是#transcode{...}:http{muxts,dst:8080/live.ts} # 或显式写法 #std{accesshttp,muxts,dst:8080/live.ts}第一段transcode是可选环节放进去就重新编码视频和音频不放就是直接把原始码流原样搬出去。很多教程一上来就让人加 transcode其实如果你的摄像机输出本来就是 H.264 AAC完全不需要转码直接封装转发CPU 占用几乎为零一台树莓派能扛七八路。只有遇到 H.265 摄像机才必须转因为 mpegts.js 和 hls.js 在浏览器侧都不支持 H.265 解码这是硬限制跟服务器性能没关系。第二段access决定用什么方式把数据送出去。这里用的是http意思是 VLC 自己起一个 HTTP 服务器客户端来一个连接就推一份流。要区分清楚的是还有一个livehttp那个是往本地磁盘写 m3u8 和 ts 分片必须再配一个 Nginx 之类的静态服务器把文件暴露出去不是同一个东西。第三段mux决定容器格式ts是 MPEG-TSwebm、ogg、mp4也支持但浏览器直播场景下基本只有ts能打。原因后面会展开。2.2 为什么不直接转 mp4非要绕 TS 这一圈这是新手最容易踩的坑想当然地写muxmp4然后video srchttp://ip:8080/live.mp4结果浏览器一直转圈不出画面。原因是标准的 MP4 文件结构要求moov这个索引盒子完整才能开始解码而直播流永远不会结束VLC 只能把 moov 放到文件末尾浏览器就会一直等这个永远不来的东西。除非用 fragmented MP4分片 MP4但目前 VLC 对 fMP4 直播输出的支持并不算稳定折腾成本太高。MPEG-TS 天生就是为流式传输设计的它是 188 字节固定长度的包不需要全局索引随到随解。mpegts.js 做的事情就是把 TS 流里的 H.264 视频和 AAC 音频抽出来重新封装成浏览器 MediaSource 能吃的 fMP4 片段喂进去。这条路是经过大量生产验证的稳定性明显好于直接播 mp4。至于 WebM虽然 Chrome 和 Firefox 原生支持video srcxxx.webm直播播放但它只能装 VP8/VP9 加 Vorbis/Opus意味着 VLC 必须用软件编码器从头编一遍CPU 开销是 H.264 硬件编码的好几倍而且很多国产摄像机的画面转 VP9 之后暗部噪点特别重。我实测过一阵子就放弃了除非你的服务器有富余的核心可以烧。2.3 音频格式是最容易被忽略的一环摄像机的音频通常是 G.711PCMA 或 PCMU或者 G.726这是安防行业的历史遗留。TS 封装能装这些格式但浏览器的解码器清单里根本没有 G.711结果就是画面能出、声音是哑的而且控制台一声不吭。所以只要你的场景需要声音transcode段里就必须显式加acodecaac,ab64,channels1,samplerate44100。单声道 64kbps 对监控音频完全够用一路音频转码的 CPU 消耗远小于视频几乎可以忽略。另外一个细节是采样率。很多摄像机音频是 8000Hz如果转码时保持 8kHz某些浏览器会正常播、某些会变调或者干脆拒绝。统一升到 44100Hz 是最省心的做法代价是码率略微上升但 64kbps 对比视频的 1500kbps 根本不算什么。3. 从命令行到浏览器可播完整实操流程3.1 最小可用命令先用一秒验证能不能通先拿一路摄像头试手别一上来就搞多路。假设海康的取流地址是rtsp://admin:abc12345192.168.1.64:554/Streaming/Channels/101101 是主码流102 是子码流Windows 下打开 CMDC:\Program Files\VideoLAN\VLC\vlc.exe -I dummy ^ --rtsp-tcp ^ --network-caching300 ^ --sout #transcode{vcodech264,vb1500,fps25,acodecaac,ab64,channels1,samplerate44100,vencx264{presetultrafast,tunezerolatency,keyint25,bframes0}}:http{muxts,dst:8081/live.ts} ^ --sout-keep ^ rtsp://admin:abc12345192.168.1.64:554/Streaming/Channels/102Linux 下把vlc.exe换成cvlc换行符从^换成\路径换成/usr/bin/cvlc。几个参数必须逐个说清楚因为它们直接决定你后面会不会被奇怪的问题折磨-I dummy是禁用交互界面让 VLC 以纯后台模式跑。不加这个参数Windows 上会弹出一个播放窗口虽然也能输出流但一旦有人手滑关了窗口流就断了。生产环境必须加。--rtsp-tcp强制用 TCP 承载 RTP 数据替代默认的 UDP。局域网里 UDP 一般没问题但只要经过一层交换机、跨网段或者无线传输UDP 丢包会导致画面花屏、马赛克而且是间歇性的、很难排查的那种。切到 TCP 之后带宽略增、延迟略升但换来的是画面稳定这笔账一定划算。代价是 TCP 模式下摄像机每路连接占用更久注意设备的最大连接数。--network-caching300把网络缓存压到 300 毫秒。VLC 默认是 1000 毫秒甚至更高这是为了抗抖动但对监控看板来说延迟比流畅度更重要。我一般给 200 到 500低于 200 在普通百兆网络上容易开始卡。--sout-keep让流输出实例保持常驻。不加这个参数的时候客户端一断开VLC 会把整条 sout 链销毁重建摄像机连接也跟着重连一次重新握手大概要 2 到 5 秒期间页面就是黑的。加上之后客户端重新连接可以直接挂到已有的流上几乎秒出画面。vencx264{...}这一串是转码性能的命门。presetultrafast让 x264 用最快的编码策略画质略微下降但 CPU 能省一半以上tunezerolatency关掉编码器的前瞻缓冲是低延迟的前提keyint25配合 25fps 表示每秒一个关键帧这个值既影响延迟也影响 HLS 分片后面细说bframes0禁用 B 帧因为 B 帧需要等后续帧才能解码天生引入延迟而且某些播放器在 B 帧密集时容易卡顿。3.2 码率到底给多少动手算一遍视频码率vb是最需要拍脑袋的量给低了糊成一片给高了 CPU 和带宽都扛不住。我通常按这个粗略公式估目标码率(kbps) ≈ 分辨率宽 × 高 × 帧率 × 运动系数 ÷ 1000其中运动系数静态场景取 0.05走动的人多的场景取 0.1。举例1920×1080、25fps、仓库有人来回走。1080×25×0.1 2700kbps所以vb2500到vb3000比较合理。如果是 1280×720、25fps 的办公室场景1280×720×25×0.05 ≈ 1150kbpsvb1200完全够。太低的码率在大面积纯色墙面或者夜间低照度画面下会产生非常明显的块状伪影因为编码器把细节全丢了。带宽这边也要算一遍总账。单路 1080p 按 3000kbps 算10 路就是 30Mbps加上 TS 封装开销和 RTSP 输入的带宽实际大约要 35Mbps。千兆内网完全没问题但如果你走的是百兆的老交换机或者服务器网卡是百兆的那 3 路就该报警了。我曾经在一个项目里排查了半天“为什么第 4 路一直黑屏”最后发现是那台小主机网口只有百兆前三路已经吃满了。CPU 这边软编码 1080p25 用 ultrafast zerolatency一颗现代 x86 核心大约能跑一路J1900 这种老赛扬可能半路都跑不动。所以如果摄像机支持切子码流强烈建议用 102海康或者 subtype1大华取 720p 甚至 D1 的流来做转码源CPU 压力直接砍掉一大半而看板画面本身也不需要 1080p 那么细。3.3 不想敲命令用 GUI 也能配出来对命令行发怵的同学VLC 图形界面里也能做同样的事路径是“媒体 → 流 → 添加网络串流 → 填 rtsp 地址 → 点击串流按钮”然后在“目标设置”里选“HTTP”点“添加”再点下一个页面配置转码参数。界面上那几个下拉框对应的就是命令行里的vcodec、acodec、mux。不过说实话GUI 配出来的参数很不精确而且一旦电脑重启、VLC 被关闭配置就没了没法做成服务。我的建议是先用 GUI 把流跑通确认摄像机能取、端口能通然后立刻转成命令行把命令写进批处理或服务脚本里。这样调试成本最低——GUI 阶段你能看到详细的错误日志命令行阶段你能获得持久化能力。4. 前端接入hls.js 与 mpegts.js 怎么写4.1 HTTP-TS 直出方案mpegts.js 二十行搞定如果走的是第 3 节那条muxtsaccesshttp的路前端就用 mpegts.js它是 flv.js 作者后来重构的项目对 HTTP-TS 的支持更好video idplayer controls muted autoplay playsinline/video script srchttps://cdn.jsdelivr.net/npm/mpegts.js1.7.3/dist/mpegts.js/script script const video document.getElementById(player); if (mpegts.getFeatureList().mseLivePlayback) { const player mpegts.createPlayer({ type: mpegts, isLive: true, url: http://192.168.1.100:8081/live.ts }, { enableWorker: true, // 把解封装放到 Worker主线程不卡 liveBufferLatencyChasing: true, // 自动追帧防止延迟越积越多 liveBufferLatencyMaxLatency: 2.0, liveBufferLatencyMinRemain: 0.5, lazyLoad: false, stashInitialSize: 128 }); player.attachMediaElement(video); player.load(); player.play(); } /scriptliveBufferLatencyChasing这个参数是延迟控制的关键。浏览器出于音视频同步的考虑会不断缓冲如果不开启追帧看板跑上两三个小时后延迟可能累积到十几秒最后变成“看历史”。开启之后播放器会检测缓冲深度超过liveBufferLatencyMaxLatency就快进追上直播点代价是画面偶尔会有一个轻微的跳跃但监控场景完全能接受。enableWorker也建议开。TS 解封装是个计算密集活儿放在主线程会让页面上的按钮、下拉框全部变卡尤其是同时播 4 路以上时非常明显。4.2 HLS 方案兼容性优先的选择HLS 需要 VLC 先把流写成磁盘分片再用 Nginx 暴露出去。VLC 侧的命令是这样cvlc -I dummy --rtsp-tcp --network-caching300 \ --sout #transcode{vcodech264,vb1200,acodecaac,ab64,channels1,samplerate44100,vencx264{presetultrafast,tunezerolatency,keyint25,bframes0}}:std{accesslivehttp,muxts,dst/var/www/hls/cam1.m3u8,index/var/www/hls/cam1-%d.ts,index-url/hls/cam1-%d.ts} \ --sout-keep \ rtsp://admin:abc12345192.168.1.64:554/Streaming/Channels/102Windows 下注意%d要写成%%d因为百分号在批处理里有特殊含义会吃掉一个。dst是 m3u8 索引文件的落盘位置index是分片文件命名模板index-url是写进 m3u8 里的分片 URL 前缀必须和 Nginx 暴露的路径对得上否则播放器会拿着 404 找不到分片表现就是索引能加载、画面死活不出。这里再强调一次keyint25的连带作用。VLC 的 livehttp 是以关键帧为边界切片的25fps 配keyint25意味着每个分片大约 1 秒一路 3000kbps 的流单分片约 375KB。如果 keyint 设成 25010 秒一个关键帧分片就是 3.75MB播放器要等整片下完才能播延迟直接飙到十几秒。所以低延迟 HLS 的第一件事就是加密关键帧而不是去调播放器参数。前端就简单了video idhlsPlayer controls muted autoplay playsinline/video script srchttps://cdn.jsdelivr.net/npm/hls.js1.5.7/dist/hls.min.js/script script const video document.getElementById(hlsPlayer); const src https://monitor.example.com/hls/cam1.m3u8; if (Hls.isSupported()) { const hls new Hls({ lowLatencyMode: true, liveSyncDurationCount: 2, // 只保留 2 个分片的同步窗口 maxBufferLength: 4, maxMaxBufferLength: 8, backBufferLength: 0 // 不保留回看缓冲省内存 }); hls.loadSource(src); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () video.play()); hls.on(Hls.Events.ERROR, (e, data) { if (data.fatal) { if (data.type Hls.ErrorTypes.NETWORK_ERROR) hls.startLoad(); else if (data.type Hls.ErrorTypes.MEDIA_ERROR) hls.recoverMediaError(); } }); } else if (video.canPlayType(application/vnd.apple.mpegurl)) { video.src src; // Safari 和安卓原生直接吃 m3u8 } /scriptliveSyncDurationCount: 2是低延迟 HLS 的核心表示播放点只落后直播边缘 2 个分片。配合 1 秒分片理论延迟就是 2 到 5 秒。设成 1 会更容易卡顿设成 3 以上延迟就明显了。4.3 跨域与 HTTPS 页面下的混合内容这一节是实际部署时翻车率最高的地方必须单独讲。VLC 自带的 HTTP access 不会返回任何 CORS 响应头如果前端页面和流地址不同源比如页面在http://192.168.1.100:3000流在http://192.168.1.100:8081浏览器会直接拦掉。解决办法是在 VLC 前面加一层 Nginx 反向代理既统一了协议和域名又能顺手加上跨域头location /live/cam1.ts { proxy_pass http://127.0.0.1:8081/live.ts; proxy_http_version 1.1; proxy_buffering off; proxy_read_timeout 3600s; proxy_set_header Connection ; add_header Access-Control-Allow-Origin *; }proxy_buffering off这一行是延迟杀手如果不关Nginx 会先把数据攒在自己的缓冲区里再转发延迟会从 2 秒涨到 8 秒以上很多人调了半天播放器参数都没意识到问题出在代理层。proxy_read_timeout要设大因为直播是长连接默认 60 秒会把流掐断表现为每隔一分钟画面停一下。如果前端站点本身是 HTTPS那页面里引http://的流会被浏览器判定为混合内容并直接阻止这不是能靠代码绕过的。唯一稳妥的做法就是让 Nginx 用同一个 HTTPS 域名代理页面里写/live/cam1.ts这种相对路径全部走加密通道。自签名证书在局域网里也能用只是首次访问要点一下信任。5. 多路并发部署与资源规划5.1 端口分配与进程管理多路场景我建议一路 VLC 进程对应一路摄像头而不是在一个 VLC 实例里塞多条 sout 链。原因是一旦某一路的摄像机关机或者网络波动独立进程崩了只影响它自己同进程多链容易一起挂。端口按顺序排开方便记忆和排查通道摄像机 RTSP 地址尾段输出端口前端流地址大门/Streaming/Channels/1028081/live/cam1.ts车间/Streaming/Channels/1028082/live/cam2.ts仓库/cam/realmonitor?channel1subtype18083/live/cam3.ts走廊/Streaming/Channels/1018084/live/cam4.ts大华设备的地址格式和海康不一样通常是rtsp://用户名:密码IP:554/cam/realmonitor?channel1subtype1其中subtype0是主码流subtype1是辅码流。这个符号在命令行和批处理里会被解释成命令分隔符必须用引号把整个地址包起来这是新手非常容易踩的一个坑表现就是 VLC 只取到了?channel1然后报 URL 无效。5.2 Windows 下的自启与守护Windows 上最省事的做法是写一个批处理用:loop加死循环实现崩溃重启echo off chcp 65001 nul set VLCC:\Program Files\VideoLAN\VLC\vlc.exe set CAMrtsp://admin:abc12345192.168.1.64:554/Streaming/Channels/102 :loop %VLC% -I dummy --rtsp-tcp --network-caching300 --sout #transcode{vcodech264,vb1200,fps25,acodecaac,ab64,channels1,samplerate44100,vencx264{presetultrafast,tunezerolatency,keyint25,bframes0}}:http{muxts,dst:8081/live.ts} --sout-keep %CAM% echo [%date% %time%] cam1 退出5 秒后重启 D:\logs\cam1.log timeout /t 5 /nobreak nul goto loopchcp 65001是为了让中文日志不乱码。日志重定向很重要摄像头半夜掉线的时候这份日志是唯一能告诉你“几点断的、断了多久”的东西。然后把这个批处理做成计划任务触发器选“计算机启动时”勾上“不管用户是否登录都要运行”主机重启后自动恢复不用人肉去点。5.3 Linux 下用 systemd 更稳Linux 上我更推荐用一个 shell 脚本包住 VLC然后交给 systemd 管因为 systemd 的单元文件里对#和{}这些字符的处理比较别扭直接写容易踩转义的坑#!/bin/bash # /opt/rtsp2http/cam1.sh exec /usr/bin/cvlc -I dummy \ --rtsp-tcp --network-caching300 --sout-keep \ --sout #transcode{vcodech264,vb1200,fps25,acodecaac,ab64,channels1,samplerate44100,vencx264{presetultrafast,tunezerolatency,keyint25,bframes0}}:http{muxts,dst:8081/live.ts} \ rtsp://admin:abc12345192.168.1.64:554/Streaming/Channels/102对应的 unit 文件[Unit] DescriptionRTSP to HTTP cam1 Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Uservlc ExecStart/opt/rtsp2http/cam1.sh Restartalways RestartSec5 LimitNOFILE65535 [Install] WantedBymulti-user.targetRestartalways配合RestartSec5就是守护逻辑比在脚本里写循环干净。LimitNOFILE调大是为了应对长时间运行后的连接堆积VLC 在 HTTP access 模式下的连接回收偶尔会延迟文件描述符不够的时候会报“无法打开文件”这种莫名其妙的错误。5.4 资源占用的真实数字我给一个在 i5-7200U 小主机上的实测参考方便你做容量规划不转码纯封装单路 1080p25 的 VLC 进程大约占 3% CPU、80MB 内存转码成 720p 用 ultrafast 大约占 35% 到 45% 单核、150MB 内存。8 路转码在这台机器上跑满CPU 常驻 70% 左右夏天要加个风扇否则会降频导致画面掉帧。内存这块要留意VLC 长时间运行内存占用会缓慢上涨大概每天涨十几兆跑一个月之后可能到 300MB 以上。我一般写一个每周日凌晨重启的计划任务比想办法去查内存泄漏划算得多因为重启期间正好没什么人看画面。6. 常见问题排查实录与速查表6.1 页面一直转圈画面不出来这是最高频的问题排查顺序按这个来能省掉大量时间。先看 VLC 本身有没有取到流。判断方法是在服务器上直接运行 VLC 命令但不带--sout看它能不能弹出播放窗口出画面。如果这一级就不行问题在摄像机侧或者地址上。常见的地址错误包括密码里有或#这类特殊符号没做 URL 编码、通道号写成了 100 或者 103、辅码流参数写错。另外一个隐蔽的原因是摄像机开启了白名单或者最大连接数已满表现是密码正确但一直返回 401。如果 VLC 能出画面但浏览器不行先在浏览器地址栏直接敲流地址比如http://192.168.1.100:8081/live.ts。如果浏览器开始下载一个文件或者显示一堆乱码说明流是通的问题在播放器代码通常是 CORS 或者 mpegts.js 没加载成功。如果返回 404 或者 502问题在服务端看下一节。6.2 端口、路径与 502 报错的排查VLC 的 HTTP access 对请求路径的匹配在不同版本上表现不一致有的版本请求根路径/就能拿到流有的必须严格匹配dst里写的路径。我的经验是把dst简化成只写端口dst:8081前端也访问根路径能避开大部分路径匹配的麻烦。如果你看到类似unexpected status 502 bad gateway并且地址是http://127.0.0.1:xxxx这种本地回环地址先检查两件事。第一主机上是不是跑着开发代理类工具很多 IDE 或者本地开发环境会设置系统级代理把 127.0.0.1 的请求劫持走代理不认识 VLC 返回的非标准 HTTP 响应头就给你回一个 502。判断方法是把流地址从127.0.0.1换成局域网 IP 再试一次或者命令行用curl --noproxy *直连。第二确认防火墙放行了端口Windows 防火墙第一次监听端口会弹提示框如果当时点了取消之后就一直是静默拦截日志里什么都看不到。还有一个容易忽略的点是端口冲突。8081 这个端口被开发工具占用得非常频繁起流之前先netstat -ano | findstr 8081Windows或者ss -lntp | grep 8081Linux确认一下比起了半天发现流指向了别的服务要省事。6.3 画面卡顿、延迟越来越大延迟持续增长是 HTTP-TS 方案的典型症状根源是播放器的缓冲策略。浏览器为了音画同步会不断累计缓冲如果流的码率有波动缓冲只增不减。liveBufferLatencyChasing: true是解药但也要配合服务端不加额外缓冲也就是前面提过的 Nginxproxy_buffering off。如果画面是规律性的一卡一卡比如每两秒顿一下多半是 GOP 设置的问题。可以试试把keyint调小到fps的数值25fps 就设 25同时确认bframes0。另外在无线网络下2.4G 频段的干扰会造成周期性丢包换成 5G 或者走有线立刻就好我在一个车间项目里为这事折腾了半天最后发现是隔壁的微波炉。至于音画不同步通常是音频转码引入的。检查是不是漏了samplerate44100或者把channels设成了 1 但源是立体声导致的。如果不需要声音干脆在 transcode 里加scodecnone把音频轨彻底去掉既省 CPU 又免去同步问题监控看板大部分时候确实不需要听声音。6.4 常见问题速查表现象最可能的原因处理动作完全空白无报错浏览器不支持 RTSP必须走转封装方案一直转圈不出画面用了 muxmp4 直播改 muxts有画面没声音源音频是 G.711加 acodecaac 转码画面花屏马赛克UDP 丢包加 --rtsp-tcp客户端重连后黑屏几秒sout 链被销毁加 --sout-keep延迟持续增长播放器缓冲累积开 liveBufferLatencyChasing跨域请求被拦截VLC 不返回 CORS 头前置 Nginx 加响应头HTTPS 页面加载失败混合内容被阻止同域 HTTPS 反向代理第 4 路开始黑屏网口或带宽打满降码率或改子码流运行一天后变卡内存缓慢增长定时重启进程7. 实操心得与几个容易被低估的细节7.1 子码流是救命稻草但要留意分辨率下限我几乎所有的项目都优先取子码流。原因很直接转码的 CPU 开销和像素数量是近似线性的1080p 换 720p 意味着像素少了 55%一台机器能带的路数直接翻倍。而且看板上的小窗口本来就只有几百像素宽播 1080p 是纯粹的浪费。但子码流也有下限我遇到过把子码流设成 D1704×576的摄像机转出来之后文字标牌完全看不清。这种情况的折中做法是主看板用子码流做多路轮播点击放大的那一路单独用主码流起一个 VLC 进程。按需起流还能顺便解决摄像机连接数限制——大部分时间只占一个连接用户点开才占第二个用完后端主动 kill 掉进程释放连接。7.2 别小看摄像机的并发连接数这个坑我在两个项目里都踩过。网络摄像机的规格书里往往写着“支持 6 路同时访问”但实际测试下来如果 6 路里有两路是主码流第三路开始就可能被拒绝连接表现是 VLC 报 401 或者直接超时。而且这个限制是按 RTP 会话算的不按客户端算你同一台 VLC 里配两条 sout 输出会算成两个连接。所以前面提到的“双轨输出”要谨慎共用 VLC 实例的多 sout 确实共用一条 RTSP 会话这是它的优势。但如果用了两个 VLC 进程那就是两条会话连接数要重新盘算。另外有些设备的录像回放和实时预览是分开计数的不要把余量算满。7.3 后端服务化是这套方案的天花板VLC 转 http 的方案省钱、省事、上手快但它的天花板很明显没有健康检查、没有按需启停、没法动态改参数、多路管理全靠脚本。当路数超过 20 路或者需要给不同用户分配不同权限的流时就该考虑上 FFmpeg 自研调度服务或者用 GStreamer 的rtspsrc ! rtph264depay ! h264parse ! mpegtsmux ! tcpserversink这条管线它能更精细地控制重连、缓冲和资源回收。像 RK3588 这类带硬件编码的板子用 GStreamer 还能调用 MPU 硬编跑十几路 1080p 转码 CPU 占用都不到 30%这是纯软件方案没法比的。我的建议是原型验证、小型看板、临时演示果断用 VLC一小时内就能出成果到了要签合同交付、要保证 7×24 稳定的阶段再把它替换成自研服务中间这段时间 VLC 方案能把需求和坑都暴露清楚这个价值其实比它跑得多快更大。7.4 一个容易被忽略的时间同步问题最后分享一个挺偏但很实用的点。如果你的看板上要叠加录像时间戳或者需要和别的事件做时间对齐一定要确认摄像机的 NTP 时间是准的。我遇到过一次排查了两天的问题看板画面播放正常但录播系统里找不对时间段最后发现是摄像机时间和服务器差了 6 分钟而前端显示的是本地时间两边一对就乱了。接入前统一给所有摄像机和服务器做一次 NTP 对时五分钟的事能省掉后面几天的扯皮。
网站建设高端定制企业官网