C++实现HLS/DASH低延迟转封装应基于FFmpeg或libdash,而非手写解析器;性能瓶颈在于切片获取时机、时间戳对齐及MSE封装粒度,而非协议文本解析。

直接说结论:C++ 做 HLS/DASH 的解析与低延迟转封装,不推荐从零手写协议解析器,而应基于 libavformat(FFmpeg) + 自定义 demuxer hook 或 dash::mpd::MPDParser(libdash)组合实现;真正卡住性能和延迟的,从来不是解析 m3u8 文本或 XML,而是切片获取时机、TS/fMP4 时间戳对齐、以及 MSE 兼容封装粒度。
用 FFmpeg 解析 m3u8 并实时拉取 ts 切片时,avformat_open_input 为什么常卡住或超时
因为默认行为会尝试完整加载整个播放列表并预读多个分片——这对点播没问题,但对直播或 LL-HLS 场景就是灾难。它会阻塞在 avformat_open_input 内部的 hls_read_header 阶段,等不到 #EXT-X-ENDLIST 就一直轮询。
- 必须设置
AVDictionary *opts传入"allowed_media_types=video+audio"和"timeout=5000000"(单位微秒),避免无限等待 - 对 LL-HLS,强制启用 partial segment 支持:
"fflags=+flush_packets+ignorerequirements",否则 FFmpeg 会忽略#EXT-X-PART标签 - 关键参数
"reconnect=1"和"reconnect_at_eof=1"必须开启,否则 m3u8 更新后无法自动续拉新切片 - 不要依赖
avformat_find_stream_info做全量探测——它会下载前 N 个 ts 片段并解码头,大幅增加首帧延迟;改用avformat_find_stream_info后立即avcodec_parameters_copy复制已知流参数
解析 DASH MPD 时,libdash 与手动 XML 解析哪个更稳
libdash(尤其是 v4.2+)比手写 tinyxml2/pugixml 更可靠,但它默认不做 runtime adaptation,即不会根据带宽变化动态切换 Representation。你得自己 hook MPDParser::GetRepresentationForBandwidth() 并注入实时测速逻辑。
- MPD 中的
SegmentTemplate@initialization和@media是 URL 模板,需用$Number$或$Time$替换——libdash提供SegmentTemplate::GetInitializationSegmentURL(),但不提供GetMediaSegmentURL(uint64_t number)的线程安全版本,多线程下要加锁 - 注意
duration单位是timescale下的 tick 数,不是秒;常见坑是把<period></period>的start当成绝对时间戳,其实它是相对于 MPD 发布时间的偏移 - 若 MPD 含
BaseURL,所有相对路径必须先 resolve;libdash的URL::Resolve()有 bug(v4.1.0),遇到双斜杠//会错拼成http:///xxx,需 patch 或自行处理
低延迟转封装的核心不是“快”,而是“准”:如何让 fMP4 切片对齐 MSE 要求
MSE 要求每个 moof+mdat chunk 必须携带完整 SPS/PPS(或通过 init.mp4 提供),且 moof 中的 traf 时间戳必须严格单调递增、无 gap。随便把 ts 切片 demux 出来塞进 fMP4,大概率花屏或卡死。
立即学习“C++免费学习笔记(深入)”;
- ts 切片里可能含多个 IDR 帧,但 fMP4 要求每个 fragment 至少含一个 sync sample;必须用
av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA)检查是否携带新 SPS/PPS,有则触发新 fragment - 不能直接用
av_write_frame输出到内存 buffer——它默认按 GOP 对齐 flush,导致 fragment 过大;需手动控制AVFormatContext->oformat->flags |= AVFMT_VARIABLE_FPS,并调用av_interleaved_write_frame前检查pkt->pts是否超出上一 fragment 的duration - LL-HLS 的 partial segment 对应 fMP4 的
moofwithtfrabox;FFmpeg 不原生支持生成 tfra,得用libgpac或手写 box 构造逻辑,否则浏览器无法做 partial append
最易被忽略的一点:HLS 和 DASH 的时间基(timebase)完全不同。HLS 的 #EXTINF 是浮点秒,DASH 的 duration 是整数 tick,而 FFmpeg 内部统一用 AV_TIME_BASE(1000000)。三者混用不转换,会导致音画不同步或 MSE 报 Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement's readyState is not HAVE_METADATA。


















