HTML5播放RTSP流一直是安防和直播领域的高频需求。RTSP基于TCP或UDP传输RTP包,设计之初面向的是专用播放器而非浏览器沙箱,浏览器既没有原生RTSP解析能力,也不允许直接建立任意TCP/UDP连接。因此所有在网页里播RTSP的尝试,本质上都是先把RTSP转成浏览器支持的分发协议,再讨论码率自适应才有意义。如果有人在没有转流的前提下谈HTML5 RTSP码率自适应,那大概率是把浏览器端某个播放器的缓冲策略误当成了自适应码率。转流层的多码率输出与带宽反馈机制,才是真正的优化起点。

先厘清转流架构,否则自适应无从谈起
RTSP流通常来自网络摄像头或NVR,单路码率由设备端编码参数决定,可能是固定码率CBR也可能是可变码率VBR。要支持自适应,转流服务必须在拉取原始RTSP之后重新转码出多个档位。常见做法是用FFmpeg作为核心引擎,拉流后同时输出三条HLS变体:比如1080p@2Mbps、720p@1.2Mbps、480p@600kbps,并生成一个主播放列表引用这三个子流。主列表里写明每个子流的带宽属性和分辨率,浏览器原生HLS播放器如Safari或者集成了hls.js的Chrome,就能根据自身网络状况在三个档位间切换。如果只是把RTSP转成单一的HLS流,无论播放器缓存策略多聪明,本质上仍然只有一个可选码率,不能称为码率自适应。
转码成本需要纳入评估。三路实时转码对CPU要求不低,尤其1080p软编。实践中可以采用硬件加速,如Intel QSV或NVIDIA NVENC,能显著降低转码延迟和资源占用。对于不需要多分辨率的场景,也可以只转出不同码率的同分辨率流,比如2Mbps、1Mbps、500kbps都是720p,画质有差异但切换时不会因分辨率突变导致画面跳变。这种同分辨率多码率的做法,在IPC监控画面中比多分辨率更实用,因为分辨率变化会让UI层需要重排,体验不如平滑降码率。
转流服务可以是独立的Golang或Node.js进程,调用FFmpeg子进程管理多路输出。也可以用现成的媒体服务器,如SRS、ZLMediaKit、MediaMTX。ZLMediaKit支持RTSP拉流后直接按需转HLS或HTTP-FLV,也支持配置多码率转码参数。但要注意,这些服务器默认不一定开启转码,需要外部转码器配合,否则拉到的只是原始单码率流。搭建转流层时,最好让转码输出直接落到HTTP静态目录或内存分片,避免多一次复制。
基于HLS的码率自适应切换逻辑与带宽估测
HLS是最容易落地的多码率方案,因为播放器侧几乎不需要额外开发。hls.js在检测到分片下载变慢时会触发ABR逻辑,自动降档。但默认的带宽估测算法偏保守,而且对初始码率选择不够智能。我们可以自行实现一套更符合监控场景的策略:用最近三个分片的下载字节数除以下载耗时,得到滑动平均吞吐量,再乘以一个安全系数比如0.85,作为可选码率的上限。这样当网络从WiFi切换到4G时,播放器能在两到三个分片内完成降档,而不是继续卡顿等待HLS内置的试探机制。
上行切换要更激进一些。如果当前档位远低于估测带宽,比如估测有5Mbps而当前只有600kbps,可以直接跳到2Mbps档位,而不是一级一级向上爬。但下行切换必须保留滞后区间:只有当估测带宽持续低于当前码率的80%并且持续超过两个分片周期时,才允许降档。否则会出现网络轻微抖动就反复切换,视频质量忽高忽低,同时切换瞬间还可能出现短暂黑屏或音画不同步。最小停留时间建议设为10到15秒,避免频繁切换带来的分片请求风暴。
HLS方案的缺点是延迟较高,通常3到6秒甚至更多,不适合实时对讲或PTZ云台控制。如果业务要求低于1秒,就必须引入WebRTC。WebRTC网关可以用SRS或Janus,它们支持将RTSP转成WebRTC流,浏览器通过原生RTCPeerConnection接收。WebRTC内部集成了Google Congestion Control和REMB反馈,本质上是基于RTP丢包和延迟的码率自适应,比HLS基于吞吐量的ABR更灵敏。但WebRTC的服务端推流和信令交互复杂度比HLS高一个量级。对于多数监控预览场景,HLS加自定义ABR策略已经足够。
使用WASM播放器与MSE的折中路径
有一种无需服务端转码的自适应思路:在浏览器里用WebAssembly运行FFmpeg,拉取RTSP流并动态转封装为fMP4,再通过MediaSource Extensions喂给video标签。理论上这样可以实现一个网页版万能播放器,但RTSP的TCP连接在浏览器里依然无法建立,需要服务端提供WebSocket代理转发RTSP数据。这种方案的自适应码率比较尴尬,因为原始RTSP只有一档码率,想要自适应就得在WASM内部做转码,每秒十几帧的软编性能在浏览器里几乎不可行,只能做转封装降级,比如丢弃B帧或者降低帧率来间接降低输出码率,但这不是真正的码率自适应,而是以牺牲流畅度换带宽非常有限。
更现实的折中做法是:服务端只做RTSP到WebSocket的转发,同时携带原始流的SPS/PPS和RTP时间戳,浏览器端WASM负责解包、解封装并可选地执行轻量级抽帧。当检测到WebSocket接收缓冲持续增长或RTP序号间隔变大(丢包增多),就主动降低输出帧率或停止解码部分非参考帧,从而减少MSE缓冲的压力。这种“伪自适应”在弱网下能显著改善卡顿,但画质会下降,而且实现复杂度远高于HLS方案。除非有强烈的低延迟且服务端无法部署转码的需求,否则不建议作为首选。
另一种基于MSE的方案是使用MPEG-DASH替代HLS。DASH的MPD清单同样支持多码率,ffmpeg可以直接输出DASH分片和初始化段。相比HLS,DASH在部分浏览器上需要引入dash.js,但dash.js的ABR规则更丰富,支持自定义RuleSet。例如可以配置基于缓冲区长度的切换规则:当缓冲区剩余小于2秒时强制降档,大于8秒时允许升档。这种方式对网络瞬时波动的容忍度更好,适合移动端网络频繁变化的环境。不过DASH生态在国内不如HLS普及,CDN支持和排障资料相对少一些。
无论采用HLS、DASH还是WebRTC,带宽估测和切换策略都是码率自适应的核心。估测算法推荐使用指数加权移动平均,对最新样本赋予更高权重,同时配合丢包率修正:如果最近5秒内丢包率超过3%,无论吞吐量多高都强制锁定当前档位10秒。切换瞬间要主动清理播放器缓冲,避免旧码率分片积压导致延迟增加。对于HLS,可以在切换档位时调用hls.js的currentLevel属性并配合hls.startLoad()重置加载状态;对于原生Safari,则依赖其自身的ABR无法干预,只能通过减少主列表档位数量来降低切换频次。
最终是否值得在HTML5里去做RTSP码率自适应,取决于设备并发量和带宽成本。如果只是几路固定摄像头预览,单码率转HLS配合较大的播放缓冲就能满足,自适应反而增加转码服务器压力。但如果面向大量移动端用户、网络环境复杂,那么建设多码率转流层并优化ABR切换逻辑,能显著提升视频打开速度和弱网下的流畅度。优化方向可以总结为:服务端转码出合理档位、主清单带宽标注准确、客户端带宽估测平滑、切换策略带滞后区间、降档优先于升档。把这五点落地,HTML5播放RTSP的卡顿问题就能得到实质性改善。