zlmediakit是c++生态中唯一生产可用的flv转webrtc低延迟方案,其自研flv解析器绕过ffmpeg demuxer、强制vp8/opus编码、零偏移时间戳校正、pli穿透触发关键帧,并实时处理音画同步与时钟漂移。

ZLMediaKit 是目前 C++ 生态中唯一能真正实现 FLV 直播流向 WebRTC 低延迟转协议且生产可用的方案。其他自研或拼凑方案(比如 FFmpeg + libwebrtc)在关键帧对齐、时间戳映射、音频重采样同步等环节必然掉帧、花屏或音画不同步,不建议投入开发。
FLV 解析必须绕过完整 demuxer,直接按 tag 流解析
FLV 文件头 + 连续 Tag 的结构决定了它不适合走 avformat_open_input 这类通用 demux 流程——开销大、启动慢、无法实时响应推流端的动态关键帧插入。ZLMediaKit 内部用纯 C++ 手写 FLV parser,逐字节读取 previous tag size 和 tag header,跳过所有 script tag,只提取 audio tag 和 video tag 的原始负载。
- 必须校验
tag type字段:18=script,8=audio,9=video,其他值直接丢弃 - 必须解析
timestamp字段为相对起始时间(单位毫秒),不能直接当绝对 PTS 用 - 遇到
AVC sequence header或AudioSpecificConfig时,需缓存并触发 WebRTC 端的 SDP 重协商(尤其是 Opus/AAC 切换)
WebRTC 推流端必须用 Unified-Plan + VP8/VP9 编码路径
FLV 原生视频是 H.264(AVC),但 WebRTC 标准栈对 H.264 的支持存在兼容性陷阱:iOS Safari 仅支持 baseline profile,Android 某些厂商 Webview 拒绝接收 SPS/PPS 变更。ZLMediaKit 默认将 FLV 中的 H.264 转为 VP8 输出,规避 profile 不一致和 SPS/PPS 重传失败问题。
- 禁用
h264在 WebRTC offer 中的 codec preference,强制优先协商VP8 - FLV 中的
AVC NALU必须按0x00000001分割后,重新打包为 VP8 的FlexFEC兼容 RTP payload - 音频若为 AAC,必须经
libfdk-aac或libopus实时转码为 Opus,采样率统一为 48kHz,frame size 固定为 20ms
时间戳映射必须做“零偏移累积校正”
FLV 的 timestamp 是 uint32_t 毫秒值,会溢出回绕;WebRTC 的 RTP timestamp 是 90kHz 单调递增。二者不能简单线性换算。ZLMediaKit 在首次收到 video tag 后,记录其 flv_ts 和当前系统 monotonic_clock::now(),后续每个包都基于该锚点做差值补偿,并用滑动窗口检测突发抖动(如推流端卡顿后恢复)。
- 不依赖
RTCP Sender Report反馈做校正——首帧就需出图,等不到 SR - 音频 timestamp 必须与视频 anchor 同源,否则音画漂移不可逆
- 若 FLV 流中断超过 5 秒,必须重置 anchor,否则累积误差 > 200ms
关键帧请求(PLI/FIR)必须穿透到 FLV 源端
WebRTC 播放端网络波动时会发 PLI(Picture Loss Indication)请求关键帧,但 FLV 源(如 OBS、FFmpeg 推流)并不监听 RTCP。ZLMediaKit 在收到 PLI 后,不是丢弃或静音等待,而是向 FLV 源端注入一个模拟的 keyframe request 信号(通过 RTMP control message 或 HTTP API 触发)。
- 对 RTMP 推流源:发送
onStatusevent with codeNetStream.Play.Reset强制刷新 GOP - 对 HTTP-FLV 源:调用 ZLMediaKit 内置
/index/api/getMediaList+/index/api/forceKeyFrame接口 - 若源端不支持强制关键帧(如老旧 IPC),则本地用 VP8 编码器生成黑帧 I-frame 插入,保证解码器不卡死
flv_ts 差值,一旦偏离 > 80ms,立即插入 silence packet 或丢弃音频包,而不是靠 jitter buffer 硬扛——后者会导致 WebRTC 端播放器主动断连。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











