音视频修炼之容器与协议(二):HLS和DASH流媒体

HLS / DASH 流媒体

HLS(Apple 主推)和 DASH(MPEG 开放标准)本质上都是 HTTP + 清单 + 分片 + ABR:播放器先拉清单文件,知道有哪些码率、哪些分片、每片多长;再一个个拉媒体分片,边下载边播放,并根据带宽和 buffer 状态切换码率。


本文速览

章节 读完要掌握什么
0. 核心模型 HLS/DASH 为什么都是"清单描述索引,分片承载媒体"。
1. 服务端工作原理 编码多档码率、按 GOP 切片、生成清单、放到 HTTP/CDN 分发。
2. HLS 工作原理 播放 HLS 时如何先拉 m3u8,再拉 ts/m4s 分片。
3. DASH 工作原理 播放 DASH 时如何先拉 MPD,再拉 init.mp4 + m4s 分片。
4. HLS vs DASH 对象映射 把 m3u8/MPD/segment/init 放到同一张表里对照。
5. ABR 工作流程 播放器如何根据吞吐、buffer、设备能力选择下一片码率。
6. CMAF 与低延迟 为什么现在常说"一套 fMP4 分片,两套清单"。
7. 加密 / DRM HLS AES-128/FairPlay 与 DASH CENC/Widevine/PlayReady。
8. 服务端切片工具 FFmpeg、Shaka Packager、Bento4 怎么落地。
9. 客户端集成 Android、iOS、Web 上怎么接 HLS/DASH。
10. 协议对比与选型 什么时候选 HLS、DASH、CMAF、LL-HLS、LL-DASH。
11. 直播架构 RTMP/SRT 输入如何转成 HLS/DASH 输出。
12. 工程坑与测试 GOP 对齐、时间戳、缓存、ABR 对齐、抓包验证。
13. 总结 用一条主线记住 HLS/DASH。

0. 核心模型:不是下载完整视频,而是下载清单和小分片

HLS 和 DASH 都把一个长视频或直播流拆成很多小文件,播放器按需下载。

问题 传统整文件下载 HLS / DASH 的做法
起播速度 要等较大文件下载一部分甚至全部。 先拉清单,再拉第一个小分片,buffer 足够就起播。
网络波动 单一清晰度,卡了只能等。 多码率分片并存,播放器可在分片边界切换码率。
直播 文件没有结束,普通下载模型不好表达。 清单持续更新,播放器定期刷新清单拿新分片。
CDN 分发 大文件 range/cache 策略复杂。 清单和分片都是普通 HTTP 资源,CDN 缓存友好。
跨平台播放 容器/协议适配成本高。 HLS 适合 Apple 生态,DASH 适合 MPEG-DASH/MSE 生态。

一句话拆解:

层级 HLS DASH 作用
清单层 master.m3u8 + media playlist.m3u8 manifest.mpd 描述有哪些码率、音轨、字幕、分片 URL、时长、时间线。
媒体层 .ts 或 .m4s .m4s 真正承载音视频数据。
传输层 HTTP/HTTPS HTTP/HTTPS 播放器用普通 HTTP GET 拉清单和分片。
自适应层 ABR 选 variant ABR 选 representation 根据带宽、buffer、设备能力切换码率。

1. 服务端工作原理:编码 ladder → 切片 → 生成清单 → CDN

HLS/DASH 播放端看起来只是"拉 URL",但服务端需要提前把媒体整理成多码率、多分片、多清单。

阶段 输入 输出 关键要求
采集 / 输入 文件、RTMP、SRT、WebRTC、SDI 等。 原始或已编码音视频流。 时间戳连续,音视频同步。
多码率编码 一路输入流。 480p、720p、1080p 等多档码率。 不同档位 GOP 边界对齐,便于 ABR 切换。
切片 每档编码流。 seg_001.ts / seg_001.m4s 等分片。 分片通常从关键帧开始,时长稳定。
生成清单 分片列表和媒体属性。 HLS m3u8 或 DASH MPD。 写清 URL、时长、码率、分辨率、codec、时间线。
源站发布 清单 + 分片文件。 HTTP 可访问资源。 设置合理 cache-control。
CDN 分发 源站 HTTP 文件。 边缘节点缓存。 分片可长缓存,直播清单短缓存或不缓存。
播放器拉取 播放 URL。 解码播放。 根据清单逐片下载,并做 ABR。

多码率 ladder 可以这样理解:

档位 分辨率 码率示例 HLS 表达 DASH 表达
低清 854×480 800 kbps 一个 variant playlist。 一个 Representation。
高清 1280×720 2 Mbps 一个 variant playlist。 一个 Representation。
全高清 1920×1080 4 Mbps 一个 variant playlist。 一个 Representation。
音频 AAC 2ch 128 kbps 可内嵌在 TS,也可独立 audio rendition。 常是独立 AdaptationSet。

关键点:ABR 不是把一个分片"实时转成不同清晰度",而是服务端提前准备好多档分片,播放器每次下载下一片前决定"下一片拉哪一档"。


2. HLS 工作原理:先拉 m3u8,再拉一个个 ts/m4s 分片

HLS 的清单是 m3u8 文本文件。最常见的播放链路是:

顺序 播放器动作 HTTP 请求示例 拿到什么 下一步
1 拉主清单 GET /master.m3u8 多档码率列表。 选择初始 variant。
2 拉子清单 GET /720p.m3u8 这一档的分片列表。 找到第一个要播的 segment。
3 拉第一个分片 GET /720p/seg_001.ts 约几秒媒体数据。 解封装、解码,填充 buffer。
4 拉后续分片 GET /720p/seg_002.ts 下一段媒体数据。 边播边下载。
5 估算网络 根据分片大小 / 下载耗时。 当前吞吐估计。 决定下一片是否升降码率。
6 切换码率 GET /480p/seg_003.ts 或 GET /1080p/seg_003.ts 同一时间位置的另一档分片。 在分片边界无缝切换。
7 直播刷新清单 GET /720p.m3u8 最新滑动窗口。 继续拉新生成的 segment。

如果播放 URL 直接就是某个 media playlist,例如 https://example.com/live/720p.m3u8,播放器可以跳过主清单,直接从子清单开始。

2.1 HLS 主清单:告诉播放器有哪些档位

master.m3u8 用来列出多档码率、分辨率、codec 和对应的 media playlist。

m3u8 复制代码
#EXTM3U
#EXT-X-VERSION:6

#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=854x480,CODECS="avc1.4d4015,mp4a.40.2"
480p.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1280x720,CODECS="avc1.4d401f,mp4a.40.2"
720p.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=4000000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
1080p.m3u8

主清单重点字段:

标签 / 属性 示例 作用
#EXTM3U #EXTM3U 表示这是 M3U/M3U8 清单。
#EXT-X-VERSION 6 HLS 协议版本。
#EXT-X-STREAM-INF BANDWIDTH=2000000 描述一个 variant stream。
BANDWIDTH 2000000 该档码率估计值,ABR 初选会参考。
RESOLUTION 1280x720 该档分辨率。
CODECS avc1.4d401f,mp4a.40.2 音视频 codec 标识,播放器判断能否解码。
URI 行 720p.m3u8 该档 media playlist 的 URL。

2.2 HLS 子清单:告诉播放器这一档有哪些分片

传统 HLS 常用 MPEG-TS 分片:

m3u8 复制代码
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:9.984,
segment_001.ts
#EXTINF:10.000,
segment_002.ts
#EXTINF:10.000,
segment_003.ts

#EXT-X-ENDLIST

子清单重点字段:

标签 示例 作用
#EXT-X-TARGETDURATION 10 最大分片时长的上限提示,播放器据此安排刷新和 buffer。
#EXT-X-MEDIA-SEQUENCE 0 当前 playlist 第一个 segment 的序号。直播滑动窗口会增长。
#EXTINF 9.984 下一个 segment 的实际时长。
segment URI segment_001.ts 真实媒体分片 URL,可以是相对路径或绝对路径。
#EXT-X-ENDLIST 点播结尾出现 表示清单不会再追加新分片;直播一般没有。

2.3 HLS 分片到底是什么

分片类型 文件后缀 内部封装 初始化段 典型场景
MPEG-TS HLS .ts MPEG-TS packet,常见 H.264/AAC。 通常不需要独立 init。 传统 HLS,兼容性好。
fMP4 HLS .m4s fragmented MP4,moof + mdat。 需要 #EXT-X-MAP 指向 init segment。 CMAF、LL-HLS、HEVC、现代链路。

fMP4 HLS 的 media playlist 通常长这样:

m3u8 复制代码
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:4
#EXT-X-MAP:URI="init.mp4"
#EXT-X-MEDIA-SEQUENCE:1

#EXTINF:4.000,
seg_001.m4s
#EXTINF:4.000,
seg_002.m4s
#EXTINF:4.000,
seg_003.m4s

#EXT-X-ENDLIST
字段 TS HLS fMP4 HLS
媒体数据 .ts 文件里直接有 TS packet。 .m4s 文件里是 moof + mdat。
初始化信息 TS 流里周期性携带 PAT/PMT 等。 #EXT-X-MAP 指向 init.mp4,里面有 ftyp/moov。
和 DASH 复用 很难直接复用。 可与 DASH 共用 CMAF/fMP4 分片。
现代 codec 支持受限或兼容性复杂。 更适合 HEVC、AV1、HDR 等现代能力。

2.4 HLS 直播:滑动窗口不是完整历史

直播 HLS 的 media playlist 通常只保留最近几个分片,随着时间向前滑动。

m3u8 复制代码
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:1234

#EXTINF:6.000,
seg_1234.ts
#EXTINF:6.000,
seg_1235.ts
#EXTINF:6.000,
seg_1236.ts

滑动窗口变化:

时间 Playlist 内容 MEDIA-SEQUENCE 变化
T=0 seg_1234, seg_1235, seg_1236 1234 初始窗口。
T=6s seg_1235, seg_1236, seg_1237 1235 移除旧片,追加新片。
T=12s seg_1236, seg_1237, seg_1238 1236 窗口继续向前。
T=18s seg_1237, seg_1238, seg_1239 1237 播放器继续刷新清单。

播放器直播拉流要点:

要点 说明
定期刷新 m3u8 直播没有 #EXT-X-ENDLIST,播放器需要重复 GET media playlist。
从 live edge 附近起播 为避免追太近卡顿,通常落后最新分片几个 segment 起播。
旧分片可能被清理 如果播放器拿到旧 playlist,但切片已被源站清理,就会 404。
延迟来自多个 segment 传统 HLS 往往要缓存 2-3 个完整 segment,segment 越长延迟越高。

3. DASH 工作原理:先拉 MPD,再拉 init 和 m4s 分片

DASH 的清单是 XML 格式的 MPD。播放 DASH 时,播放器通常按下面流程工作:

顺序 播放器动作 HTTP 请求示例 拿到什么 下一步
1 拉 MPD GET /manifest.mpd Period、AdaptationSet、Representation、SegmentTemplate。 解析可用音视频轨和码率。
2 选初始 Representation 选 video_720p + audio_128k。 初始视频档和音频档。 计算 init 和 segment URL。
3 拉初始化段 GET /video/720p/init.mp4 ftyp/moov,解码配置和 track 信息。 初始化 demuxer/decoder/MSE SourceBuffer。
4 拉第一个媒体段 GET /video/720p/seg-1.m4s moof + mdat。 解封装、解码、填充 buffer。
5 拉音频段 GET /audio/init.mp4、GET /audio/seg-1.m4s 音频初始化和媒体段。 和视频按时间戳同步。
6 继续拉后续段 seg-2.m4s、seg-3.m4s。 后续媒体数据。 边播边下。
7 ABR 切换 GET /video/1080p/seg-4.m4s 或 GET /video/480p/seg-4.m4s 同一时间位置的另一档视频段。 在 segment 边界切换。
8 直播刷新 MPD GET /manifest.mpd 最新时间线。 找到新可用分片。

3.1 DASH MPD 示例

DASH 的 MPD 表达力比 HLS 文本清单更强,常见结构如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<MPD type="static" mediaPresentationDuration="PT60S" minBufferTime="PT2S">
  <Period id="p0" start="PT0S">
    <AdaptationSet id="v1" mimeType="video/mp4" codecs="avc1.640028" segmentAlignment="true">
      <Representation id="1080p" bandwidth="4000000" width="1920" height="1080">
        <BaseURL>1080p/</BaseURL>
        <SegmentTemplate timescale="1000" duration="4000"
                         initialization="init.mp4"
                         media="seg-$Number$.m4s"
                         startNumber="1" />
      </Representation>
      <Representation id="720p" bandwidth="2000000" width="1280" height="720">
        <BaseURL>720p/</BaseURL>
        <SegmentTemplate timescale="1000" duration="4000"
                         initialization="init.mp4"
                         media="seg-$Number$.m4s"
                         startNumber="1" />
      </Representation>
    </AdaptationSet>

    <AdaptationSet id="a1" mimeType="audio/mp4" codecs="mp4a.40.2" lang="zh">
      <Representation id="audio" bandwidth="128000">
        <BaseURL>audio/</BaseURL>
        <SegmentTemplate timescale="1000" duration="4000"
                         initialization="init.mp4"
                         media="seg-$Number$.m4s"
                         startNumber="1" />
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>

DASH 核心概念:

DASH 概念 含义 HLS 类比 说明
MPD Media Presentation Description。 master + media playlist 的综合体。 XML 根清单,描述整个媒体。
Period 一段连续时间区间。 一段节目 / 广告段。 插广告、切节目时常分多个 Period。
AdaptationSet 一组可替换的同类轨道。 video variants / audio renditions。 例如视频集合、音频集合、字幕集合。
Representation 某一档具体编码版本。 HLS variant。 例如 720p 2Mbps H.264。
Initialization Segment 初始化段。 HLS fMP4 的 #EXT-X-MAP。 常见 init.mp4,包含 ftyp/moov。
Media Segment 媒体分片。 .ts / .m4s segment。 常见 moof + mdat。
SegmentTemplate 分片 URL 模板。 media playlist 中的 segment URI 列表。 用 $Number$ 或 $Time$ 生成 URL。
SegmentTimeline 精确时间线。 #EXTINF 列表。 适合分片时长不完全一致的场景。

3.2 DASH 分片格式

文件 典型 box 作用
init.mp4 ftyp + moov 初始化解封装器和解码器,包含 track、codec、timescale 等信息。
seg-1.m4s moof + mdat 第一个媒体分片,moof 描述样本,mdat 放样本数据。
seg-2.m4s moof + mdat 后续媒体分片。
seg-N.m4s moof + mdat 按时间线连续播放。

DASH 的 URL 生成方式:

方式 示例 特点
SegmentList MPD 中列出每个 <SegmentURL media="seg-1.m4s"/> 直观,但 MPD 可能很大。
SegmentTemplate + Number media="seg-$Number$.m4s" 按序号生成 URL,点播和直播都常见。
SegmentTemplate + Time media="seg-$Time$.m4s" 按时间戳生成 URL,配合 SegmentTimeline。
SegmentBase 单文件 + byte range 点播或特殊封装中使用。

3.3 DASH 直播:动态 MPD 和可用时间线

DASH 直播通常使用 type="dynamic" 的 MPD,播放器要根据时间计算哪些 segment 已经可用。

MPD 字段 作用 播放器怎么用
type="dynamic" 表示直播或动态内容。 不能当成完整 VOD,一段时间后要刷新 MPD。
availabilityStartTime 媒体时间线起点。 结合当前墙钟时间计算 live edge。
minimumUpdatePeriod MPD 建议刷新间隔。 到时间后重新 GET MPD。
timeShiftBufferDepth 可回看的直播窗口长度。 超出窗口的旧 segment 可能不可用。
suggestedPresentationDelay 建议播放落后 live edge 多久。 避免追得太近导致卡顿。
SegmentTimeline 明确每个 segment 的时间和重复次数。 精准定位当前应该下载的 segment。

DASH 直播和 HLS 直播的共同点:

共同点 说明
清单会更新 HLS 刷新 media playlist;DASH 刷新 dynamic MPD。
有直播窗口 只保留最近一段时间的 segment。
播放器不追最新一帧 通常落后 live edge 一段时间以保证稳定。
分片 URL 可预测 HLS 通过 playlist 明列;DASH 常通过模板计算。

4. HLS vs DASH 对象映射

把两套协议放到一张表里,理解会更快:

目标 HLS DASH 说明
总入口 master.m3u8 manifest.mpd 播放 URL 通常指向这里。
某一档码率 #EXT-X-STREAM-INF + 子 m3u8 Representation ABR ladder 中的一档。
视频集合 多个 variant stream video AdaptationSet 表示可互相切换的视频档位。
音频集合 #EXT-X-MEDIA:TYPE=AUDIO audio AdaptationSet 多语言、多声道时常见。
分片列表 media playlist 中的 URI 列表 SegmentList / SegmentTemplate / SegmentTimeline 告诉播放器去哪拉分片。
分片时长 #EXTINF、#EXT-X-TARGETDURATION duration、SegmentTimeline 播放器安排 buffer 和下载节奏。
初始化段 #EXT-X-MAP Initialization / initialization fMP4/CMAF 场景需要。
媒体分片 .ts / .m4s .m4s 真正音视频数据。
点播结束 #EXT-X-ENDLIST type="static" + duration 表示内容完整且不会再追加。
直播更新 media playlist 滑动窗口 dynamic MPD 播放器周期性刷新清单。

核心差异:

维度 HLS DASH
清单语法 M3U8 文本,简单直接。 XML,表达能力强。
生态优势 Apple 原生支持,iOS/Safari 最稳。 开放标准,DASH-IF、MSE、Android/Web 生态常见。
传统分片 .ts 历史包袱较重,但兼容好。 以 fMP4 为主。
CMAF 统一 可用 fMP4 HLS。 天然适合 fMP4。
DRM 生态 FairPlay。 Widevine / PlayReady。

5. ABR 工作流程:下一片拉哪一档

ABR(Adaptive Bitrate)发生在客户端。服务端只提供多档资源,真正做选择的是播放器。

阶段 播放器做什么 关键输入 输出
1. 解析清单 读取所有 variant / representation。 码率、分辨率、codec、URL。 可选档位列表。
2. 选初始码率 通常保守起播。 网络类型、历史带宽、屏幕大小、默认策略。 初始档位。
3. 下载分片 拉当前档位的第一个 segment。 URL、range、缓存状态。 下载耗时和分片大小。
4. 估算吞吐 segment size / download time。 最近多个分片下载记录。 平滑后的带宽估计。
5. 观察 buffer 看已缓冲时长、丢帧、解码压力。 播放队列和渲染状态。 是否需要降档或可升档。
6. 决策下一片 选择下一 segment 的码率。 吞吐、buffer、安全系数、设备能力。 下一片 URL。
7. 边界切换 在 segment 边界切到新码率。 时间对齐的分片。 尽量无缝播放。

常见 ABR 策略:

策略 思路 优点 风险
吞吐优先 根据最近下载速度选择低于估计带宽的码率。 简单直接。 网络抖动大时容易上下跳。
Buffer 优先 buffer 低就降档,buffer 高才升档。 起播和抗卡顿更稳。 可能保守,画质提升慢。
混合策略 吞吐 + buffer + 设备能力 + 历史状态。 工程上最常见。 参数多,需要调优。
低延迟策略 更关注 live edge 距离和下载截止时间。 延迟更低。 容错空间小,弱网更容易卡。

ABR 切换的硬要求:

要求 说明
不同码率分片时间对齐 720p 的第 10 片和 1080p 的第 10 片应覆盖同一段播放时间。
关键帧边界对齐 切片最好从 IDR/关键帧开始,切换后能独立解码。
codec 兼容 不能在播放器不支持的 codec 档位上升档。
音视频时间线一致 独立音频和视频 AdaptationSet/Rendition 要能同步。
初始化段匹配 切 representation 时可能需要对应的 init segment。

6. CMAF 与低延迟:同切片不同清单

CMAF(Common Media Application Format)把 HLS 和 DASH 的媒体分片统一到 fMP4/CMAF chunk 思路上。

项 传统 HLS DASH CMAF 统一后
清单 m3u8 MPD 仍然各用各的清单。
分片 .ts 常见 .m4s 常见 共用 .m4s / CMAF fragment。
初始化段 TS 通常不需要;fMP4 用 #EXT-X-MAP。 init.mp4。 共用 init 和 media segment 的内容结构。
服务端成本 HLS 和 DASH 可能各切一套。 单独一套。 媒体分片切一次,生成两套 manifest。
现代 codec TS 支持复杂。 fMP4 友好。 更适合 HEVC、AV1、HDR、低延迟。

低延迟的核心不是"协议名字变了",而是减少等待完整大分片的时间。

方案 传统做法 低延迟做法 延迟降低点
HLS 等完整 segment 出来后写入 playlist。 LL-HLS 用 partial segment、preload hint、blocking playlist reload。 播放器更早拿到未完全结束的媒体片段。
DASH 等完整 segment 可用后下载。 LL-DASH/CMAF chunk 用更小 chunk 和 chunked transfer。 边生成边传输,减少 segment 等待。
播放器 buffer 通常缓存多个 segment。 低延迟播放器减少目标 buffer。 更靠近 live edge。
CDN 传输 缓存完整 segment。 支持 chunked transfer / range / 更短缓存策略。 新媒体数据更快到边缘。

LL-HLS 关键标签和机制:

机制 示例 / 名称 作用
Partial Segment #EXT-X-PART 一个 segment 内再拆更小 part。
Part Target #EXT-X-PART-INF:PART-TARGET=0.333 告诉播放器 part 目标时长。
Preload Hint #EXT-X-PRELOAD-HINT 提示下一个即将生成的 part。
Blocking Reload _HLS_msn、_HLS_part 查询参数 客户端请求可阻塞等待新 part 出现。
Server Control #EXT-X-SERVER-CONTROL 描述服务端支持的低延迟能力。
Rendition Report #EXT-X-RENDITION-REPORT 低延迟 ABR 切换时帮助定位其他码率的最新位置。

低延迟方案的代价:

代价 说明
CDN 要配合 边生成边传输、阻塞刷新、短缓存都要求 CDN 行为正确。
播放器容错更小 buffer 越短,网络抖动越容易卡顿。
服务端状态更多 part、preload、blocking reload 对打包器和源站要求更高。
调试更复杂 不能只看完整 segment,还要看 part/chunk 到达时机。

7. 加密 / DRM 支持

7.1 HLS AES-128 简单加密

m3u8 复制代码
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-KEY:METHOD=AES-128,URI="https://license.example.com/key.bin",IV=0x1234567890abcdef1234567890abcdef

#EXTINF:10.000,
seg_001.ts
#EXTINF:10.000,
seg_002.ts
项 说明
加密对象 整个 TS segment 或相关媒体数据。
Key 获取 播放器按 URI 通过 HTTPS 获取 key。
安全强度 适合基础防盗链,不等价于完整商业 DRM。
常见配套 URL 鉴权、referer 校验、token 过期、HTTPS。

7.2 HLS FairPlay

m3u8 复制代码
#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES,KEYFORMAT="com.apple.streamingkeydelivery",URI="skd://asset-id"
项 说明
生态 Apple FairPlay Streaming。
加密粒度 SAMPLE-AES 更偏样本级加密。
播放端 iOS、tvOS、Safari 生态支持最好。
业务流程 播放器拿到 license challenge,业务服务返回 license。

7.3 DASH Widevine / PlayReady

xml 复制代码
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
  <cenc:pssh>BASE64_PSSH_DATA</cenc:pssh>
</ContentProtection>
项 说明
通用加密 DASH 常用 CENC(Common Encryption)。
Widevine Android / Chrome 生态常见。
PlayReady Windows / Xbox / 部分电视生态常见。
PSSH Protection System Specific Header,告诉播放器如何请求 license。

8. 服务端切片工具

8.1 FFmpeg 切 HLS

bash 复制代码
ffmpeg -i input.mp4 \
  -c:v libx264 -profile:v main -crf 20 \
  -c:a aac -b:a 128k \
  -f hls \
  -hls_time 10 \
  -hls_list_size 0 \
  -hls_segment_type mpegts \
  output.m3u8

输出结果:

文件 说明
output.m3u8 HLS media playlist。
output0.ts / output1.ts MPEG-TS 分片。
#EXT-X-ENDLIST 点播输出通常会有结束标记。

关键参数:

参数 说明
-f hls 使用 HLS muxer。
-hls_time 10 目标 segment 时长 10 秒。
-hls_list_size 0 点播保留全部 segment;直播通常设置滑动窗口大小。
-hls_segment_type mpegts 输出 TS 分片;也可选择 fMP4。

8.2 FFmpeg 切 DASH

bash 复制代码
ffmpeg -i input.mp4 \
  -map 0 \
  -c:v libx264 -c:a aac \
  -f dash \
  output.mpd

输出结果:

文件 说明
output.mpd DASH manifest。
init-stream*.m4s 初始化段。
chunk-stream*-00001.m4s 媒体分片。

8.3 多码率切片

bash 复制代码
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]split=3[v1][v2][v3]; \
                   [v1]scale=854:480[480p]; \
                   [v2]scale=1280:720[720p]; \
                   [v3]scale=1920:1080[1080p]" \
  -map "[480p]"  -c:v libx264 -b:v 800k  -g 60 -hls_time 2 -hls_segment_filename "480p_%03d.ts"  480p.m3u8 \
  -map "[720p]"  -c:v libx264 -b:v 2000k -g 60 -hls_time 2 -hls_segment_filename "720p_%03d.ts"  720p.m3u8 \
  -map "[1080p]" -c:v libx264 -b:v 4000k -g 60 -hls_time 2 -hls_segment_filename "1080p_%03d.ts" 1080p.m3u8
注意点 说明
主清单 上面示例还需要生成或手写 master.m3u8。
GOP 对齐 各档 -g、帧率、切片时长要一致。
音频处理 实战常把音频单独 map,避免三份重复音频浪费。
生产工具 大规模生产更常用专用 packager。

8.4 专业打包工具

工具 适合场景 特点
Shaka Packager DASH、HLS、CMAF、DRM。 Google 生态,CMAF 友好。
Bento4 MP4/fMP4/HLS/DASH 打包分析。 老牌工具,适合调试 MP4 box 和 DASH/HLS。
Apple mediafilesegmenter Apple HLS 工作流。 HLS 官方工具链之一。
商业云转码 生产直播/点播。 集成转码、截图、DRM、CDN、监控。

9. 客户端集成

9.1 Android / Media3 ExoPlayer

kotlin 复制代码
// HLS
val hlsSource = HlsMediaSource.Factory(dataSourceFactory)
    .createMediaSource(MediaItem.fromUri(m3u8Url))

// DASH
val dashSource = DashMediaSource.Factory(dataSourceFactory)
    .createMediaSource(MediaItem.fromUri(mpdUrl))

player.setMediaSource(hlsSource)
player.prepare()
player.play()
能力 说明
HLS Media3/ExoPlayer 可通过 HLS module 播放。
DASH Media3/ExoPlayer 可通过 DASH module 播放。
ABR 播放器内部根据 TrackSelection 策略切换。
DRM 可集成 Widevine 等 DRM。

9.2 iOS AVFoundation

swift 复制代码
let url = URL(string: "https://example.com/master.m3u8")!
let player = AVPlayer(url: url)
player.play()
协议 iOS 支持情况
HLS 系统原生支持,是 iOS/tvOS/Safari 首选。
DASH 系统一般不原生支持,常用第三方播放器或转 HLS。
FairPlay Apple DRM 生态集成最好。

9.3 Web

html 复制代码
<video id="video" controls></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
  const video = document.getElementById('video');
  const hls = new Hls();
  hls.loadSource('https://example.com/master.m3u8');
  hls.attachMedia(video);
</script>

Web 播放对比:

场景 常见方案 说明
Safari 播 HLS <video src="master.m3u8"> Safari 原生 HLS 能力强。
Chrome 播 HLS hls.js + MSE hls.js 解析 m3u8 和 TS/fMP4,喂给 MSE。
Chrome 播 DASH dash.js / Shaka Player + MSE DASH Web 生态成熟。
DRM Web 播放 EME + Widevine/PlayReady/FairPlay 依赖浏览器和平台 DRM。

10. HLS vs DASH 对比与选型

维度 HLS DASH
主推方 Apple。 MPEG 开放标准。
清单格式 m3u8 文本。 MPD XML。
传统切片 MPEG-TS。 fMP4。
现代切片 fMP4 / CMAF。 fMP4 / CMAF。
iOS / Safari 原生支持,首选。 通常需第三方或转封装。
Android Media3/ExoPlayer 支持。 Media3/ExoPlayer 支持。
Web Safari 原生;Chrome 常用 hls.js。 dash.js / Shaka Player 常见。
DRM FairPlay。 Widevine / PlayReady。
传统延迟 常见 10-30 秒,取决于 segment 和 buffer。 常见 10-30 秒,取决于 segment 和 buffer。
低延迟 LL-HLS。 LL-DASH / CMAF low latency。
表达能力 简洁,易读。 XML 表达能力更强,时间线更灵活。

实战推荐:

场景 推荐方案 原因
面向 iOS/Safari HLS。 系统原生支持最好。
面向 Android/Web 国际生态 DASH + HLS 双出。 兼容不同浏览器、DRM 和设备。
大规模点播 HLS + DASH + CMAF。 同媒体分片,不同清单,节省存储和打包成本。
普通直播 HLS / DASH。 CDN 友好,稳定优先。
低延迟直播 LL-HLS / LL-DASH / WebRTC。 看延迟目标和互动需求。
强互动连麦 WebRTC。 HLS/DASH 即使低延迟也不是强互动首选。

11. 直播 HLS/DASH 架构

直播链路经常是 RTMP/SRT 输入,HLS/DASH 输出。

阶段 组件 输入 输出 作用
采集 摄像头 / OBS / App SDK 音视频采集数据。 编码流。 采集和编码。
推流 RTMP / SRT / WebRTC 编码流。 到源站的实时流。 把主播流送到服务端。
源站接入 SRS / Nginx-RTMP / 云直播源站 RTMP/SRT。 内部媒体流。 鉴权、接流、转发。
转码 FFmpeg / 云转码 / GPU 转码。 一路高码率流。 多码率 ladder。 生成 480p/720p/1080p 等。
切片打包 Packager。 多码率编码流。 TS/fMP4 分片 + m3u8/MPD。 按 GOP 切片并更新清单。
分发 HTTP 源站 + CDN。 清单和分片。 边缘缓存。 大规模分发。
播放 App / Web / TV 播放器。 播放 URL。 画面和声音。 拉清单、拉分片、ABR 播放。

直播延迟来源:

来源 说明 优化方向
编码器缓存 B 帧、lookahead、码率控制会引入延迟。 低延迟编码参数、减少 B 帧。
GOP 等待 切片通常要等关键帧边界。 缩短 GOP,GOP 和切片对齐。
Segment 时长 传统 6-10 秒 segment 会直接推高延迟。 缩短 segment 或使用 partial/chunk。
播放器 buffer 为抗抖动会缓存多个 segment。 调低目标 buffer,但要接受更高卡顿风险。
CDN 缓存策略 清单缓存过长会拿不到新分片。 m3u8/MPD 短缓存,分片长缓存。
网络抖动 下载慢时 buffer 被耗尽。 ABR 降档、CDN 调度、弱网优化。

12. 工程坑与测试

12.1 GOP 跟切片不对齐

问题 表现 正确做法
切片从非关键帧开始 播放器切到该片后不能独立解码,出现花屏/黑屏。 segment 边界对齐 IDR。
各码率 GOP 不一致 ABR 切换后时间线或解码失败。 所有 ladder 使用一致帧率、GOP、切片时长。
GOP 太长 起播慢,seek/切换慢,直播延迟高。 按延迟目标设置 GOP,例如 2s 或更短。

示例:

bash 复制代码
ffmpeg -i input.mp4 -r 30 -g 60 -keyint_min 60 -sc_threshold 0 -hls_time 2 output.m3u8
参数 说明
-r 30 输出帧率 30fps。
-g 60 2 秒一个 GOP。
-keyint_min 60 最小关键帧间隔也为 60。
-sc_threshold 0 避免场景切换导致额外关键帧破坏对齐。
-hls_time 2 segment 目标时长 2 秒。

12.2 时间戳不连续

检查项 要求 异常表现
TS 内部 PCR 稳定连续增长。 播放时钟抖动。
PTS/DTS 音视频时间戳单调合理。 卡顿、跳跃、音画不同步。
segment 间时间线 前后 segment 不能断裂或重叠异常。 播放器报 discontinuity 或 seek 错。
DASH timescale MPD 时间线和 m4s 内部 timescale 对应。 播放位置计算错误。

12.3 m3u8 / MPD 与切片缓存不一致

缓存对象 推荐思路 风险
直播 m3u8 / MPD 短缓存或不缓存。 缓存太久会拿不到最新分片。
直播 segment 可缓存一段时间,至少覆盖回看窗口。 清单还指向旧片但源站已删,会 404。
点播 m3u8 / MPD 可长缓存,版本化 URL 更好。 更新同名文件可能导致新旧不一致。
点播 segment 长缓存。 需要内容 hash 或版本号避免污染。

12.4 ABR 切换破坏对齐

ABR 切换要求 说明
只能在分片边界切换 不能在 segment 中间突然换码率文件。
不同码率 segment 数量/时间一致 同一序号应覆盖同一时间范围。
初始化段正确切换 DASH/fMP4 切 representation 时可能要加载对应 init。
音频和视频同步 分离音视频时,不能只切视频而破坏音频时间线。

12.5 抓 HLS

场景 命令 / 写法 看什么
看主清单 curl -L https://example.com/master.m3u8 variant 列表、码率、分辨率、codec。
看子清单 curl -L https://example.com/720p.m3u8 TARGETDURATION、MEDIA-SEQUENCE、segment URL。
下载切片 wget https://example.com/seg_001.ts 文件是否存在、大小是否合理。
看响应头 curl -I https://example.com/720p.m3u8 cache-control、content-type、CDN 命中。

12.6 抓 DASH

场景 命令 / 写法 看什么
看 MPD curl -L https://example.com/manifest.mpd Period、AdaptationSet、Representation、SegmentTemplate。
下载 init wget https://example.com/720p/init.mp4 初始化段是否可访问。
下载 m4s wget https://example.com/720p/seg-1.m4s 分片是否存在、大小是否合理。
看 box mp4dump seg-1.m4s 是否有 moof/mdat。

12.7 ffprobe 看切片

bash 复制代码
ffprobe -hide_banner seg_001.ts
ffprobe -hide_banner init.mp4
ffprobe -hide_banner seg-1.m4s
输出 重点关注
codec H.264/H.265/AAC 等是否符合清单声明。
duration segment 时长是否接近清单描述。
start_time segment 起始时间是否连续。
bitrate 是否接近 ladder 配置。

12.8 hls.js / dash.js 调试

js 复制代码
const hls = new Hls({ debug: true });
hls.loadSource('https://example.com/master.m3u8');
hls.attachMedia(videoElement);
日志 说明
manifest parsed 主清单解析完成。
level switch ABR 档位切换。
frag loading / loaded 分片下载开始和完成。
buffer appended 媒体数据写入 MSE buffer。
error 网络、解复用、解码、媒体错误。

13. 总结

HLS / DASH 核心模型:

按"播放一个 HLS"来记最直观:

顺序 HLS 播放动作 DASH 对应动作 记忆点
1 拉 master.m3u8。 拉 manifest.mpd。 先拿"地图"。
2 解析 variant 列表。 解析 AdaptationSet/Representation。 知道有哪些码率和轨道。
3 拉某档 720p.m3u8。 计算某 Representation 的 init/segment URL。 定位具体媒体资源。
4 拉 segment_001.ts 或 seg_001.m4s。 拉 init.mp4 + seg-1.m4s。 真正下载媒体数据。
5 解封装、解码、填 buffer。 解封装、解码、填 buffer。 buffer 足够就起播。
6 边播边拉后续分片。 边播边拉后续分片。 播放不是一次性下载完整文件。
7 根据网络切到 480p/1080p 下一片。 根据网络切 Representation 下一片。 ABR 发生在分片边界。
8 直播定期刷新 m3u8。 直播定期刷新 dynamic MPD。 清单持续更新,窗口向前滑。

最终金句:HLS 和 DASH 都是"HTTP 文件系统上的流媒体协议"------清单负责索引,分片负责媒体,ABR 负责选择下一片。HLS 更偏 Apple 生态,DASH 更偏开放标准;CMAF 让两者可以共用 fMP4 分片,但清单仍然各写一套。

相关推荐
承渊政道1 小时前
Linux网络学习【从深入理解网络层原理到IP分片与重组全过程剖析】
linux·计算机网络·ubuntu·ip分片与重组
橘色的喵1 小时前
嵌入式Linux编程,如何把硬件中断交给用户态--UIO 的机制
linux·mcu·isr·中断·uio
allanGold1 小时前
Android Studio 无线调试
android·ide·android studio
~城南花已开~1 小时前
Redfish 事件接收服务搭建指南(Ubuntu Server 实测)
linux·服务器·python·ubuntu
GreenHandBruce2 小时前
基于Qt 6.12.0 + FFmpeg 9.0.2的4k视频播放器
qt·ffmpeg·音视频
倔强的石头1062 小时前
【Linux指南】进程通信系列(四):命名管道 FIFO —— 让陌生进程按名字见面
linux·服务器·网络
美摄科技2 小时前
美摄科技智能视频创作服务解决方案
科技·音视频
2601_965448872 小时前
AI视频制作:做历史视频,用 AE 做人物时间线太难,AI怎么直接生成人物年表动画?
人工智能·音视频
Kapaseker2 小时前
AI 会干掉 React Native 吗?
android·kotlin