视频分片技术全解:从上传到播放的全链路架构
为什么视频处理的每一步都在「分片」
一个 4K 60fps 的 10 分钟视频,原始文件可以轻松超过 20GB。把这样的文件当作一个整体来传输、处理、分发,几乎在每个环节都会遇到硬性瓶颈:
- 上传:单个 HTTP 连接传 20GB,任何一次网络抖动都意味着从头再来
- 转码:单机串行处理一个 20GB 文件,可能要跑几个小时
- 分发:CDN 缓存一个 20GB 的完整文件,缓存命中率极低,回源成本爆炸
- 播放:用户必须等待整个文件下载完才能开始观看,首帧时间以分钟计
分片的本质就是 把一个不可控的大问题拆成若干个可控的小问题。这个思路贯穿了视频处理的全链路:
| 环节 | 分片对象 | 典型分片大小 | 核心收益 |
|---|---|---|---|
| 上传 | 原始文件 | 2MB - 10MB | 断点续传、并发加速 |
| 转码 | GOP 组 | 2s - 10s 片段 | 分布式并行、弹性扩缩 |
| 封装 | 媒体流 | 2s - 10s segment | 流式传输、即时播放 |
| CDN | HLS/DASH segment | 2s - 6s | 边缘缓存、按需回源 |
| 播放 | 码率变体 | 逐片请求 | 自适应码率、Seek 定位 |
下面我们沿着视频数据流的方向,从上传到播放,逐环节拆解每种分片的策略和工程实现。
上传分片:断点续传与秒传
为什么不能直接上传
用户在抖音发一个 3 分钟的 1080p 视频,大约 500MB。在 4G 网络下,上行带宽通常只有 5-10Mbps,完成上传需要 7-14 分钟。这期间用户可能切换 WiFi、进电梯、接电话——任何中断都会导致上传失败。
分片上传解决的核心问题:把一次不可靠的长连接,变成多次可重试的短连接。
分片上传的基本流程
整个流程拆解如下:
- 客户端切片:将文件按固定大小(通常 2-5MB)切割为多个 chunk
- 计算指纹:对每个 chunk 计算 MD5,对整个文件计算 SHA-256
- 并发上传:同时开 3-6 个连接并行上传不同的 chunk
- 服务端校验:每个 chunk 到达后立即校验 MD5,不匹配则要求重传
- 合并写入:所有 chunk 到齐后按序合并,校验整体 SHA-256
- 写入对象存储:合并后的完整文件写入 S3/OSS/COS
TUS 协议:断点续传的标准方案
TUS(可恢复上传协议)是目前最被广泛采用的断点续传开放标准,Vimeo 是其主要推动者。核心思路非常简洁:
# 1. 创建上传资源
POST /files HTTP/1.1
Upload-Length: 524288000
Tus-Resumable: 1.0.0
# 响应返回资源地址
HTTP/1.1 201 Created
Location: /files/abc123
# 2. 上传分片(带 offset)
PATCH /files/abc123 HTTP/1.1
Upload-Offset: 0
Content-Type: application/offset+octet-stream
Content-Length: 5242880
[chunk data...]
# 3. 查询上传进度(断点续传的关键)
HEAD /files/abc123 HTTP/1.1
HTTP/1.1 200 OK
Upload-Offset: 15728640 # 已上传的字节数
Upload-Length: 524288000
TUS 相比自研协议的优势在于:客户端 SDK 覆盖全平台(iOS / Android / Web / Flutter),服务端有 tusd 开源参考实现,协议本身通过扩展机制支持并发上传、校验和、过期清理等功能。
秒传:文件级去重
秒传的本质是 内容寻址——上传前先算文件整体的 SHA-256 hash,发给服务端查询。如果存储中已有相同 hash 的文件,直接建立引用关系,跳过实际数据传输。
// 客户端伪代码
async function uploadWithDedup(file) {
const hash = await computeSHA256(file);
// 1. 先查是否已存在
const { exists, fileId } = await api.checkHash(hash);
if (exists) {
// 秒传:直接关联,0 字节传输
return api.linkFile(fileId, file.name);
}
// 2. 不存在,走正常分片上传
return chunkedUpload(file, hash);
}
抖音和 Bilibili 的视频秒传命中率实际并不高(用户上传的视频重复率远低于文件分享场景),但在企业内部视频平台、教育平台等场景下,秒传可以节省 30%-50% 的上传流量。
分片大小的选择
分片大小不是拍脑袋定的,需要平衡多个因素:
| 分片大小 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| 1MB | 重传代价小,弱网友好 | HTTP 请求数多,overhead 大 | 移动端弱网 |
| 2-5MB | 平衡点 | - | 通用场景 |
| 10MB | 吞吐效率高,请求数少 | 重传代价大 | 局域网/高速网络 |
| 动态分片 | 自适应网络状况 | 实现复杂 | 高级客户端 |
实际工程中比较推荐的做法是 动态分片:初始 chunk 设为 2MB,根据前几个 chunk 的上传速度动态调整——上传快就增大到 5-8MB 减少请求数,上传慢就缩小到 1MB 降低重传成本。七牛云和阿里云 OSS 的 SDK 都实现了类似策略。
一句话总结:上传分片把一次赌博式的长传输变成了多次确定性的短传输,配合 TUS 协议和秒传机制,在弱网和大文件场景下提供了可靠的用户体验。
视频编码基础:理解 GOP 是理解后续一切的前提
要理解转码分片和流媒体分片,必须先搞清楚视频编码的基本结构。这里只讲和分片直接相关的部分。
I 帧、P 帧、B 帧
视频编码的核心思想是 去除时间冗余——相邻帧之间大部分内容是相同的,只需要编码差异部分:
- I 帧 (Intra-coded):关键帧,包含完整画面信息,可以独立解码。相当于一张完整的 JPEG
- P 帧 (Predicted):只存储与前一个参考帧的差异,需要依赖前面的 I 帧或 P 帧才能解码
- B 帧 (Bi-directional):双向预测帧,同时参考前后两个帧的差异,压缩率最高但解码复杂度也最高
典型的帧大小比例:I 帧约 100KB,P 帧约 30KB,B 帧约 15KB(以 1080p H.264 为例)。
GOP:分片的天然边界
GOP(Group of Pictures)就是从一个 I 帧开始到下一个 I 帧之前的所有帧组成的序列。一个典型的 GOP 结构:
I B B P B B P B B P B B P B B I B B P ...
|<------------ GOP (通常 2-10s) ---------->|
GOP 是视频分片的 天然切割点,原因很简单:
- 每个 GOP 以 I 帧开头,可以独立解码,不依赖前面的 GOP
- 在 GOP 边界切割不会破坏帧间依赖关系
- 如果在 GOP 中间切割,切割点后的 P/B 帧会因为丢失参考帧而无法解码
这就是为什么 HLS/DASH 的 segment 长度通常是 GOP 长度的整数倍——不是巧合,而是编码结构的硬性约束。
编码器选型
| 编码器 | 标准 | 压缩效率 | 编码速度 | 硬件加速 | 适用场景 |
|---|---|---|---|---|---|
| x264 | H.264/AVC | 基准线 | 快 | NVENC/QSV/AMF | 兼容性优先,移动端 |
| x265 | H.265/HEVC | +30-40% | 慢 3-5x | NVENC/QSV | 4K 内容,带宽敏感 |
| libsvtav1 | AV1 | +40-50% | 慢 5-10x | 有限 | 长尾内容,无版权费 |
| libvpx-vp9 | VP9 | +30% | 慢 3x | 有限 | YouTube 生态 |
目前的行业现状:YouTube 已经全面拥抱 AV1(2024 年起新上传视频默认 AV1 编码),Netflix 在中低码率场景大量使用 AV1,Bilibili 主力仍是 H.264 + H.265 组合。编码器选型的核心权衡是 压缩效率 vs 编码成本 vs 终端兼容性。
一句话总结:GOP 是视频编码中帧间依赖的基本单元,所有后续的分片操作——转码切割、HLS segment、播放器 Seek——都必须对齐到 GOP 边界。
转码分片:用并行把小时级任务压到分钟级
为什么转码是最耗资源的环节
一个 10 分钟的 4K H.264 视频,用 x264 以 medium preset 转码为 1080p,单核 CPU 大约需要 30-40 分钟。如果要输出 4 个码率档位(1080p/720p/480p/360p),串行处理就是 2 小时起步。
用户上传视频后等 2 小时才能发布?这在任何内容平台都是不可接受的。
分片并行转码流程
核心步骤:
- GOP 边界分析:用
ffprobe扫描源文件,找到所有 IDR(Instantaneous Decoder Refresh)帧的位置 - 分片切割:在 IDR 帧位置切割源文件,每个分片包含完整的若干个 GOP
- 分布式转码:每个分片独立发到转码集群的不同节点并行处理
- 多码率输出:每个节点同时输出多个码率档位
- 合并:转码完成后按顺序合并各分片的输出
# Step 1: 获取所有关键帧位置
ffprobe -select_streams v -show_entries frame=pkt_pts_time,key_frame \
-of csv=p=0 input.mp4 | grep ",1$" | cut -d',' -f1
# Step 2: 在关键帧位置切割(假设每 30s 一个分片)
ffmpeg -i input.mp4 -c copy -f segment \
-segment_times 30,60,90,120 \
-reset_timestamps 1 \
segment_%03d.mp4
# Step 3: 并行转码每个分片(实际中用任务队列调度)
ffmpeg -i segment_001.mp4 \
-map 0:v -map 0:a \
-c:v libx264 -preset medium -crf 23 \
-s 1920x1080 -b:v 8M \
output_001_1080p.mp4
转码调度:从 FFmpeg 到分布式集群
小规模场景下,一台多核服务器跑多个 FFmpeg 进程就够了。但平台级别的转码(日均百万级视频上传)需要完整的分布式调度系统:
任务编排层:接收上传完成事件,根据源文件信息生成转码 DAG(有向无环图)。DAG 里包含分析、切割、多码率转码、合并、切片、索引生成等节点。
调度层:把 DAG 中的转码任务分发到集群节点。关键的调度策略包括:
- 数据本地性:优先把任务分配到源文件所在节点,减少数据搬运
- GPU 亲和性:H.264 用 NVENC 硬件编码的任务分配到有 GPU 的节点
- 负载均衡:根据各节点的 CPU/GPU 使用率和队列长度做均衡
执行层:每个节点运行 FFmpeg worker,通过容器化实现资源隔离。Kubernetes + 自定义 operator 是目前比较主流的方案,字节跳动和快手都有类似架构的内部系统。
多码率 ABR Ladder
ABR Ladder(自适应码率阶梯)定义了一组码率/分辨率组合,让播放器根据网络状况选择最优档位。
传统的固定 ABR Ladder:
| 档位 | 分辨率 | 视频码率 | 音频码率 | 目标带宽 |
|---|---|---|---|---|
| 1 | 1920x1080 | 8 Mbps | 192 kbps | 10+ Mbps |
| 2 | 1280x720 | 5 Mbps | 128 kbps | 6 Mbps |
| 3 | 854x480 | 2.5 Mbps | 128 kbps | 3 Mbps |
| 4 | 640x360 | 1 Mbps | 96 kbps | 1.5 Mbps |
| 5 | 426x240 | 400 kbps | 64 kbps | 600 kbps |
Netflix Per-Title Encoding
Netflix 在 2015 年提出了一个影响深远的观点:不同内容的编码复杂度差异巨大,用固定码率编码是一种浪费。
一个静态演讲视频,720p 下 1.5Mbps 就能达到很好的画质;而一个动作片的爆炸场景,720p 下 5Mbps 可能还不够。Netflix 的解法是 per-title encoding:
- 对每个视频独立做编码分析,生成 Rate-Quality 曲线
- 在曲线上找到每个分辨率的最优码率点(质量边际收益开始递减的拐点)
- 生成该视频专属的 ABR Ladder
后来这个思路进一步演进为 per-shot encoding(按镜头级别优化)和 per-frame encoding——Netflix 公开数据显示,per-title encoding 相比固定 ladder 平均节省 20% 的带宽,画质不变甚至更好。
YouTube 也有类似的 VP9/AV1 编码优化策略。Bilibili 则在 2023 年推出了基于机器学习的智能编码系统,根据视频内容自动调整编码参数。
一句话总结:转码分片通过 GOP 对齐切割实现分布式并行,多码率 ABR Ladder 提供了适配不同网络的能力,而 per-title encoding 则在此基础上进一步压缩了编码冗余。
流媒体封装协议:HLS vs DASH vs CMAF
转码完成后,视频需要被封装成流媒体协议格式,才能被播放器以分片方式请求和播放。
HLS(HTTP Live Streaming)
Apple 在 2009 年推出的 HLS 是目前最广泛部署的流媒体协议。其核心思路:
- 把视频切成固定时长(通常 6s)的小文件(.ts 或 .fmp4)
- 生成一个播放列表文件(.m3u8)索引这些分片
- 播放器通过 HTTP 逐个下载分片播放
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:6
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1920x1080
1080p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1280x720
720p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=854x480
480p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=640x360
360p/playlist.m3u8
子播放列表的内容:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:6.006,
segment_000.ts
#EXTINF:6.006,
segment_001.ts
#EXTINF:6.006,
segment_002.ts
#EXTINF:5.338,
segment_003.ts
#EXT-X-ENDLIST
DASH(Dynamic Adaptive Streaming over HTTP)
MPEG-DASH 是国际标准(ISO/IEC 23009),与 HLS 的核心思路相同,但在协议设计上更灵活:
- 使用 XML 格式的 MPD(Media Presentation Description)替代 m3u8
- 原生支持 fMP4 容器(而非 HLS 早期的 MPEG-TS)
- 支持更丰富的时间模型和内容保护方案
CMAF:统一的容器格式
HLS 最初用 .ts(MPEG-2 Transport Stream)作为分片容器,DASH 用 .m4s(fragmented MP4)。两套格式意味着同一个视频要存两份分片,存储成本翻倍。
CMAF(Common Media Application Format)的目标就是统一容器格式——使用 fMP4 作为通用容器,HLS 和 DASH 共享同一套分片文件,只需要各自的 manifest(m3u8 / MPD)。
Apple 从 HLS 第 7 版开始支持 fMP4 容器,这使得 CMAF 在实践中成为可能。
协议对比
| 特性 | HLS | DASH | CMAF |
|---|---|---|---|
| 提出方 | Apple (2009) | MPEG (2012) | MPEG + Apple (2018) |
| Manifest 格式 | m3u8 (文本) | MPD (XML) | 复用 HLS/DASH |
| 分片容器 | .ts 或 .fmp4 | .m4s (fmp4) | .fmp4 |
| iOS 支持 | 原生 | 需 JS Player | 通过 fMP4 |
| Android 支持 | ExoPlayer | 原生 | ExoPlayer |
| DRM 集成 | FairPlay | Widevine/PlayReady | 两者皆可 |
| 低延迟版本 | LL-HLS | LL-DASH | 支持 |
| 生态成熟度 | 最广泛 | 开放标准 | 逐步推广 |
| 典型用户 | Apple/抖音/Bilibili | YouTube/Netflix | 新建系统推荐 |
分片时长的取舍
segment 时长是一个关键参数,直接影响用户体验和系统效率:
| 分片时长 | 延迟 | 编码效率 | CDN 缓存效率 | Seek 精度 | 适用场景 |
|---|---|---|---|---|---|
| 2s | 低 (~4-6s) | 较差 | 分片数多 | 高 | 准直播、低延迟 |
| 4s | 中 (~8-12s) | 中等 | 平衡 | 中 | 通用点播 |
| 6s | 高 (~12-18s) | 好 | 高效 | 较低 | Apple 推荐默认 |
| 10s | 很高 | 最好 | 最高效 | 低 | 长内容、带宽节省 |
Apple 推荐 HLS 使用 6s 的分片时长,但在实际工程中,4s 是目前更主流的选择——在延迟和效率之间取得了较好的平衡。
LL-HLS 与 LL-DASH:低延迟方案
传统 HLS 的延迟通常在 15-30s(3 个 segment 的缓冲),这对直播场景来说太高了。
LL-HLS(Low-Latency HLS,Apple 2019 年提出)的核心改进:
- 部分分片(Partial Segments):把一个 6s 的 segment 进一步拆成多个 200ms-500ms 的 partial segment
- 阻塞式播放列表加载:播放器请求 playlist 时可以阻塞等待新的 partial segment 出现,避免轮询
- 预加载提示:服务端可以告诉播放器下一个 partial segment 的 URL,实现提前请求
LL-HLS 可以把延迟从 15-30s 降到 2-5s。
LL-DASH 使用 Chunked Transfer Encoding 实现类似效果,播放器在 segment 还没完全生成时就可以开始下载已经产出的部分。
一句话总结:HLS 是兼容性之王,DASH 是开放标准的选择,CMAF 是统一格式的未来方向。分片时长在延迟和效率之间取舍,低延迟场景用 LL-HLS/LL-DASH 把延迟压到秒级。
CDN 分发:让分片离用户更近
视频内容天然适合 CDN 加速——单个 segment 文件小(几百 KB 到几 MB),访问模式有明显的时间局部性(热门视频的前几个 segment 被大量请求),而且 segment 一旦生成就不会修改(天然的不可变对象)。
CDN 缓存策略
视频分片的缓存策略和普通网页资源有很大区别:
分层缓存架构:
| 层级 | 角色 | 缓存内容 | TTL |
|---|---|---|---|
| L1 边缘节点 | 靠近用户,毫秒级响应 | 热门视频的前 N 个 segment | 24-48h |
| L2 中间层 | 区域汇聚,减少回源 | 全量 segment | 7-30d |
| 源站 | 存储全量数据 | 所有文件 | 永久 |
缓存预热策略:
- 头部分片优先:视频的前 3-5 个 segment 缓存优先级最高(大部分用户的首次播放集中在开头)
- 热度驱动:根据视频的实时播放热度动态调整缓存策略
- 码率分层:720p 的缓存优先级高于 1080p 和 480p(覆盖最广的中间码率)
Netflix 公开数据显示,其 CDN(Open Connect)的缓存命中率超过 95%,回源流量不到总流量的 5%。这得益于他们的 预填充策略:新内容上线前,主动把热门内容推送到边缘节点。
多 CDN 调度
大型视频平台不会只用一家 CDN。Bilibili 同时使用了自建 CDN、阿里云 CDN、腾讯云 CDN 等多家供应商。多 CDN 的调度逻辑:
- DNS 层调度:根据用户 IP 的地理位置,解析到最近的 CDN 节点
- HTTP 302 调度:请求到达调度服务后,根据实时状态 302 重定向到最优节点
- 客户端调度:播放器 SDK 内置调度逻辑,根据历史测速数据选择 CDN
核心调度因子包括:
- 各 CDN 节点的实时可用带宽和延迟
- 当前 CDN 供应商的成本(不同供应商的流量单价不同)
- 节点健康状态(故障自动摘除)
- 区域覆盖能力(有些 CDN 在某些省份覆盖更好)
回源优化
CDN 回源是成本的大头(源站出口带宽很贵),优化回源有几个关键策略:
合并回源:当多个边缘节点同时回源请求同一个 segment 时,中间层做请求合并,只向源站发一次请求。这在新视频刚上线时特别有用——瞬间涌入的大量请求如果每个都回源,源站扛不住。
Range 回源:用户 Seek 跳转时,播放器可能只需要 segment 的一部分。支持 Range 请求可以减少无效数据传输。
源站多地部署:源站本身做多区域部署(比如华北、华东、华南各一套),CDN 回源时就近选择源站,减少跨地域回源的延迟和带宽成本。
一句话总结:CDN 分发的核心是把视频分片缓存到离用户最近的地方,通过分层缓存、多 CDN 调度和回源优化,把缓存命中率做到 90% 以上,源站压力控制在极低水平。
播放器 ABR:每一个分片请求都是一次决策
ABR(Adaptive Bitrate)是流媒体播放的灵魂。播放器在请求每一个 segment 之前,都要根据当前的网络状况和缓冲区状态,决定请求哪个码率的版本。
ABR 算法的两个流派
Throughput-based(基于吞吐量)
核心思路:用最近几个 segment 的下载速度来估算当前带宽,选择不超过估算带宽的最高码率。
estimated_bandwidth = EWMA(recent_download_speeds)
selected_bitrate = max(bitrate for bitrate in ladder
if bitrate < estimated_bandwidth * safety_factor)
优点是实现简单,反应灵敏。缺点是对带宽波动敏感,可能频繁切换码率(用户感知为画面质量忽高忽低)。
Buffer-based(基于缓冲区)
核心思路:根据当前缓冲区的水位决定码率——缓冲区水位高说明网络充裕可以升码率,水位低说明网络不足需要降码率。
if buffer_level > high_threshold:
# 缓冲充足,尝试升码率
selected_bitrate = next_higher_bitrate
elif buffer_level < low_threshold:
# 缓冲紧张,紧急降码率
selected_bitrate = lowest_bitrate
else:
# 缓冲区适中,线性映射码率
selected_bitrate = linear_map(buffer_level, lowest, highest)
优点是码率切换更平滑,不会因为短暂的带宽波动而频繁跳档。缺点是反应较慢,在带宽突变时可能来不及调整导致卡顿。
实际应用中的混合策略:
YouTube 的 ABR(基于 MPC——Model Predictive Control)综合考虑吞吐量估计、缓冲区水位、未来几个 segment 的码率切换成本,在一个优化模型中求解最优决策。Netflix 的 ABR 也是混合方案,并且加入了 QoE(Quality of Experience)评分模型。
hls.js(最流行的开源 HLS 播放器库)默认使用 throughput-based + buffer-based 的混合策略,开发者可以通过配置调整两者的权重。
首帧优化
首帧时间(Time to First Frame)是视频体验的关键指标。YouTube 的数据显示,首帧时间每增加 1 秒,用户放弃率增加约 6%。
优化手段包括:
- 低码率起播:首个 segment 用最低码率(360p),后续快速升档。用户宁可先看到模糊画面,也不愿意盯着加载圈
- 预加载 manifest:在用户点击播放前就提前请求 m3u8/MPD
- Preload Hints:LL-HLS 支持在 playlist 中声明下一个 segment 的 URL,播放器提前建连
- CDN 预热:热门视频的前几个 segment 提前推送到边缘节点
- 连接复用:使用 HTTP/2 或 HTTP/3 复用 TCP/QUIC 连接,避免每个 segment 都重新握手
抖音的首帧优化做得非常激进——通过预下载策略(在 feed 流中预加载即将可能被点击的视频的首个 segment),把首帧时间压到了 200ms 以内。
Seek 优化
用户拖动进度条时,播放器需要快速定位到目标时间点并开始播放。Seek 性能取决于几个因素:
- segment 边界对齐:目标时间点通常不在 segment 边界上,播放器需要请求包含目标时间点的 segment,然后在本地丢弃目标点之前的帧
- 关键帧距离:解码必须从 I 帧开始,如果 GOP 很长(比如 10s),Seek 到 GOP 中间位置时需要从 GOP 头部的 I 帧开始解码,丢弃中间的帧
- Range 请求:如果知道目标时间点在 segment 文件中的字节偏移,可以用 HTTP Range 只请求需要的部分
Netflix 使用了 trick play 技术:预先生成低分辨率的 I-frame-only 流,用户拖动进度条时显示这个流的预览图,松手后再切回正常播放流。
一句话总结:ABR 的核心在于用合理的模型估算网络状态,在画质和流畅性之间找到最优平衡。首帧和 Seek 优化则通过预加载、低码率起播等工程手段提升交互体验。
DRM 与安全:分片级别的内容保护
付费内容、版权内容需要 DRM(Digital Rights Management)保护。在分片架构下,DRM 加密是以 segment 为单位进行的。
三大 DRM 系统
| 特性 | Widevine | FairPlay | PlayReady |
|---|---|---|---|
| 所有方 | Apple | Microsoft | |
| 覆盖平台 | Android/Chrome/Smart TV | iOS/macOS/Safari | Windows/Xbox/Edge |
| 安全级别 | L1(硬件)/L2/L3(软件) | 仅硬件 | SL150(软件)/SL2000/SL3000(硬件) |
| 支持的封装 | DASH/HLS(CMAF) | HLS | DASH/HLS(CMAF) |
| 许可证费用 | 免费 | 需 Apple 开发者账号 | 按量付费 |
| 离线播放 | 支持 | 支持 | 支持 |
覆盖全平台至少需要 Widevine + FairPlay 两套方案。大多数商业视频平台(爱奇艺、腾讯视频、Netflix)都三套全部部署。
分片级加密
以 AES-128-CBC(HLS 标准加密)和 SAMPLE-AES(样本级加密)为例:
AES-128-CBC:对整个 .ts segment 文件做 AES-128 加密。密钥通过 Key Server 获取,m3u8 中声明密钥 URI:
#EXT-X-KEY:METHOD=AES-128,URI="https://key.example.com/key/abc123",IV=0x00000000000000000000000000000001
#EXTINF:6.006,
encrypted_segment_000.ts
SAMPLE-AES / CENC:只加密媒体样本(视频的 NAL 单元和音频帧),不加密容器头部。这意味着播放器可以先解析容器结构(知道分辨率、时长等信息),再解密实际的媒体数据。CENC(Common Encryption)是 MPEG 标准,允许同一份加密内容被 Widevine、FairPlay、PlayReady 解密——实现了 加密一次,多 DRM 解密 的目标。
密钥轮换
为了增强安全性,高安全要求的场景(如院线窗口期的电影)会做密钥轮换——每隔 N 个 segment 更换一次加密密钥。这样即使某一段时间的密钥被破解,也只能解密对应的几个 segment,无法解密整个视频。
Netflix 的策略是对不同安全级别的内容采用不同的密钥轮换频率:普通内容不轮换,高价值内容(如独占首映)每 5 分钟轮换一次。
一句话总结:DRM 在分片架构下天然地以 segment 为加密单位运作,CENC 标准实现了加密一次多端解密,密钥轮换在安全性和性能之间提供了灵活的选择空间。
直播场景:实时切片与延迟控制
直播和点播的核心区别在于:点播的分片在播放前就全部生成好了,而直播的分片必须实时生成。这带来了完全不同的工程挑战。
实时切片流程
直播的转码-切片-分发链路必须在极短时间内完成:
- 推流端:主播通过 RTMP/SRT 把音视频流推到服务端
- 转码:实时转码为多个码率档位(延迟要求比点播严格得多,通常用
-preset veryfast或硬件编码) - 切片:以 GOP 为单位生成 segment,同时更新 m3u8 playlist
- 分发:新生成的 segment 立即推送到 CDN 边缘节点
整个链路的延迟预算:
编码延迟 (1 GOP, ~2s)
+ 转码延迟 (~0.5s)
+ 切片上传 (~0.3s)
+ CDN 分发 (~0.5s)
+ 播放器缓冲 (3 segments = ~6s)
= 总延迟约 9-10s (传统 HLS)
延迟控制对比
| 方案 | 典型延迟 | 核心机制 | 兼容性 | 适用场景 |
|---|---|---|---|---|
| 传统 HLS | 15-30s | 3-segment 缓冲 | 最好 | 非交互直播 |
| LL-HLS | 2-5s | Partial Segments + 阻塞加载 | iOS 14+, 需支持的 CDN | 互动直播 |
| 传统 DASH | 10-20s | 类似 HLS | 好 | 非交互直播 |
| LL-DASH | 2-5s | Chunked Transfer | 需 DASH 播放器 | 互动直播 |
| WebRTC | 0.5-1s | P2P + UDP | 浏览器原生 | 连麦、实时互动 |
| RTMP | 2-5s | 持久 TCP 连接 | Flash 退役后受限 | 推流端(非播放端) |
| SRT | 0.5-2s | UDP + ARQ | 需专用播放器 | 专业级远程制作 |
实际案例
抖音直播:推流用 RTMP/QUIC,播放端用 HTTP-FLV + HLS 双协议。HTTP-FLV 提供 2-3s 的低延迟用于互动场景,HLS 作为回退方案保证兼容性。
Bilibili 直播:推流用 RTMP,播放端主推 HTTP-FLV,辅以 HLS。2023 年开始试点 LL-HLS。
YouTube Live:推流支持 RTMP 和 HLS,播放端用 DASH,超低延迟模式可以达到 2-3s。
Twitch:推流用 RTMP,播放端用 LL-HLS,延迟约 2-3s。Twitch 是 LL-HLS 在大规模直播场景下的标杆案例。
直播分片的特殊挑战
时间窗口管理:直播的 m3u8 playlist 是滑动窗口——只保留最近 N 个 segment 的引用。播放器如果暂停太久,恢复播放时可能发现之前的 segment 已经从 playlist 中移除,需要跳到直播最新位置。
CDN 缓存一致性:直播的 m3u8 每隔几秒就更新一次,CDN 对 playlist 文件不能像点播那样长时间缓存。通常的策略是 playlist 文件的 CDN TTL 设为 0.5-1s,而 segment 文件设为较长的 TTL(segment 一旦生成就不变)。
推流端异常处理:主播断网、推流软件崩溃等情况下,服务端需要在最后一个 segment 的 m3u8 中加入 #EXT-X-ENDLIST 标记,通知播放器直播已结束,而不是让播放器一直轮询等待新 segment。
一句话总结:直播场景下,分片从预生成变成了实时生成,延迟控制的核心在于压缩编码-切片-分发-缓冲这条链路上每个环节的时间开销,LL-HLS 和 WebRTC 分别适用于不同的延迟要求。
架构全景与成本优化
视频平台的成本结构
对于一个日活千万级的视频平台,成本结构大致如下:
| 成本项 | 占比 | 量级参考 |
|---|---|---|
| CDN 带宽 | 40-55% | 最大头,随播放量线性增长 |
| 转码计算 | 20-30% | GPU 实例成本高,但可弹性 |
| 存储 | 10-15% | 多码率版本 + 原始文件 |
| 源站带宽 | 5-10% | 回源流量,单价高 |
| 其他 | 5-10% | DRM License Server、监控等 |
对于中小平台,CDN 费用是最大的成本项。以国内云厂商的价格为参考:CDN 带宽按峰值计费约 10-30 元/Mbps/月,按流量计费约 0.15-0.25 元/GB。一个日均 1 亿次播放、平均观看 3 分钟的平台,日均 CDN 流量约 150-300TB,月 CDN 成本可达数百万元。
成本优化策略
编码优化(效果最显著)
提升编码效率是最根本的降本手段——同样的画质用更少的码率就意味着更少的 CDN 流量和存储。
- 从 H.264 迁移到 H.265:相同画质下节省 30-40% 码率
- 从 H.265 迁移到 AV1:进一步节省 20-30%
- Per-title encoding:根据内容复杂度自动调整码率,平均再省 20%
- 两遍编码(Two-pass):第一遍分析内容分配码率预算,第二遍按预算编码,比单遍编码质量更高
YouTube 估算,AV1 编码相比 VP9 每年为其节省数十亿美元的 CDN 成本。
存储优化
- 冷热分离:最近 7 天的视频存热存储(SSD),7-90 天的存温存储(HDD),90 天以上存冷存储(归档级)
- 延迟转码:不是所有上传的视频都需要立刻生成全部码率。可以只先生成 720p,当视频播放量达到阈值后再生成 1080p 和 480p
- 原始文件清理:转码完成并确认质量后,可以删除原始文件只保留转码后的版本(需要权衡是否有重新转码的需求)
CDN 优化
- 缓存命中率提升:优化缓存 key 设计,避免不必要的缓存穿透(比如 URL 中包含用户 ID 导致同一个 segment 被缓存多份)
- 多 CDN 成本博弈:同时接入 3-4 家 CDN,根据实时价格和质量动态调度。大客户可以跟 CDN 厂商谈阶梯价格
- P2P 辅助分发:在客户端之间做 P2P 传输,减少 CDN 回源。Bilibili 的 P2P 方案在高峰期可以分担 30-40% 的 CDN 流量
- HTTP/3 + QUIC:减少连接建立开销,在弱网环境下提高传输效率,间接减少重传导致的带宽浪费
转码优化
- 弹性伸缩:使用 Spot Instance / 竞价实例做转码,成本降低 50-70%
- 硬件编码:GPU 的 NVENC 硬件编码器速度是 CPU 软编码的 5-10 倍,虽然同码率下质量略逊于 x264/x265 的 slow preset,但在时效性要求高的场景下性价比极高
- 智能转码策略:只转码有人看的码率。统计数据显示,80% 的播放集中在 720p 和 480p,1080p 的播放占比通常不到 15%。可以按需生成冷门码率
监控与质量保障
完善的监控体系是保障视频体验的基础:
播放侧指标:
- 首帧时间(P50 < 1s,P99 < 3s)
- 卡顿率(rebuffer ratio < 1%)
- 码率切换频率(每分钟不超过 2 次)
- 播放失败率(< 0.1%)
服务侧指标:
- CDN 缓存命中率(> 90%)
- 转码排队时间(P99 < 5min)
- 源站 5xx 错误率(< 0.01%)
YouTube 有一个叫 Video Quality Report 的内部系统,实时监控全球数亿设备的播放质量。Netflix 的 Telescope 系统则从 ISP 级别的网络质量到播放器帧级别的渲染质量全链路监控。
一句话总结:视频平台的成本大头是 CDN 带宽,最有效的降本路径是提升编码效率;存储分层、按需转码、P2P 辅助分发等策略叠加起来,可以在不影响体验的前提下把成本降低 40-60%。
总结:分片思维是视频工程的第一性原理
回顾全文,视频处理全链路的每个环节都在做同一件事——把大的变成小的,把串行的变成并行的,把集中的变成分散的:
| 环节 | 分片策略 | 核心收益 |
|---|---|---|
| 上传 | 文件切块 + 并发传输 | 断点续传、秒传 |
| 转码 | GOP 对齐切割 + 分布式并行 | 分钟级完成小时级任务 |
| 封装 | 固定时长 segment + manifest 索引 | 流式传输、即时播放 |
| CDN | segment 级缓存 + 多级分发 | 高命中率、低延迟 |
| 播放 | 逐片请求 + ABR 决策 | 自适应码率、流畅体验 |
| DRM | 分片级加密 + 密钥轮换 | 安全与性能兼顾 |
| 直播 | 实时切片 + Partial Segment | 秒级延迟 |
这种分片思维不只适用于视频,它是处理大规模数据问题的通用范式——大文件传输要分片(BitTorrent),大数据处理要分片(MapReduce),大模型推理要分片(Tensor Parallelism)。
对于想要深入视频技术的工程师,我的建议是:先用 FFmpeg 手动走一遍完整的切片-转码-封装-播放流程,建立直觉后再去理解上层平台的设计选择。很多看起来复杂的系统,拆开来看都是围绕「分片」这一个核心动作的不同变体。