三种分片,解决三个不同的问题
同样是把视频切成小块,上传分片、转码任务分片和播放分片的边界并不相同。上传关心字节偏移与重试,转码关心帧间依赖与任务调度,播放关心时间轴、随机访问和码率切换。把三者混为一谈,容易得到“按固定字节切开后,每块都能独立播放”这样的错误设计。
下面以一个假设的教学视频平台为例:用户上传长视频,平台生成多个清晰度,观众在不同网络下连续观看。贯穿这条链路的问题是:哪些工作可以独立重试、并行处理或按需获取,又需要在哪里重新校验完整性?
分片不是视频播放的必要前提。普通 MP4 在索引可及时获取时也能渐进播放,HTTP Range 可以支持部分下载;大文件是否适合缓存取决于访问分布和缓存实现。引入流媒体分片的主要价值,是让不同码率、不同时间段成为播放器可以独立选择的资源。
| 环节 | 划分依据 | 需要保持的不变量 |
|---|---|---|
| 上传 | 字节范围或对象存储 part | 不丢字节、不重复拼接,完成后校验整体 |
| 转码任务 | 可恢复解码的时间范围 | 帧依赖完整,输出音画时间轴连续 |
| HLS/DASH 封装 | 媒体时间与随机访问点 | 索引和媒体对应,各档位切换点对齐 |
| CDN 与播放 | 已发布的媒体对象 | 相同版本 URL 对应相同内容,鉴权不串用 |
上传:把失败范围限制在可恢复的进度内
假设文件为 500 MB,上行有效吞吐为 5 Mbps,仅传输就约需 800 秒。这是按文件字节数计算的示例,不代表某种分辨率的视频必然这么大。视频时长、编码和码率共同决定文件大小。
上传会话应记录用户、文件大小、已接收范围、校验结果和过期时间。重试时查询服务端确认的进度,而不是把客户端“已发送”当成服务端“已持久化”。对于允许乱序分片的协议,同一个会话和分片编号必须有明确的重复提交语义。
实现上可以让业务服务接收分片,也可以签发受限凭据,让客户端直接使用对象存储的 multipart upload。后者通常由存储服务完成 part 合并,不必先在业务服务器拼出一个大文件再上传。
分片大小首先服从存储协议。例如 S3 multipart upload 除最后一个 part 外,最小为 5 MiB;不能把通用的“2 MB 分片”直接套到这条链路。大文件还应根据 part 数量上限计算最小分片大小。并发请求数则根据带宽、内存和重试成本调整;HTTP/2 下多个请求可以复用连接,并发请求数不等于 TCP 连接数。S3 分段上传限制
tus 用偏移量恢复上传
tus 的核心协议以服务端确认的偏移量恢复传输。下面是一个省略鉴权、仅展示协议字段的请求示例:
HEAD /files/example HTTP/1.1
Host: upload.example.com
Tus-Resumable: 1.0.0
HTTP/1.1 200 OK
Tus-Resumable: 1.0.0
Upload-Offset: 15728640
Upload-Length: 524288000
Cache-Control: no-store
客户端随后从 15728640 继续发送,PATCH 请求携带同样的 Tus-Resumable、正确的 Upload-Offset 和 Content-Type: application/offset+octet-stream。偏移冲突时必须重新同步进度。核心协议不意味着可以对同一个偏移并发 PATCH;并行上传应使用服务端支持的 concatenation 等扩展。tus 协议
去重必须保留文件的访问边界
内容摘要可以用于查重和完整性核验,但“知道文件 hash”不能成为获得文件访问权的凭据。企业平台可以在同一授权空间内复用已经拥有的文件;跨用户复用则需要额外的所有权验证,不能暴露他人的文件是否存在。
计算整文件 SHA-256 也要读取文件。所谓秒传通常省去的是媒体字节上传,仍有本地计算、鉴权和元数据请求。是否值得引入,取决于实际重复率,不能预先承诺固定比例的流量节省。
编码依赖:能切开,不代表能独立解码
上传块按字节切割,不需要理解视频;转码和播放必须理解编码结构。
I 帧使用帧内编码,但“I 帧”不等于后续所有帧都不再依赖之前的画面。以 H.264 为例,IDR 提供解码刷新的边界;开放 GOP 可能存在跨组参考。P、B 帧使用参考图像预测,参考数量和关系由编码方式决定,并不固定为“只依赖上一帧”或“前后各一帧”。
因此,独立转码任务需要可恢复的解码起点;必要时从更早的随机访问点开始解码,再裁出目标区间。对于播放分片,则应在编码阶段安排随机访问点,使各清晰度在同一媒体时间切换。简单地对源文件每 30 秒执行一次字节切割,不能满足这些条件。
选择编码器也要区分标准与实现:H.264、HEVC、AV1 是编码标准,libx264、libx265、SVT-AV1 是软件实现,NVENC 是硬件编码路径。画质、速度和码率应在相同素材、分辨率及质量目标下测量,不能用一个固定“压缩提升百分比”决定所有业务的选型。
转码:先明确并行的是时间段还是清晰度
平台可以把同一视频的不同清晰度并行编码,也可以将长视频拆成多个时间范围处理。二者形成“时间范围 × 输出档位”的任务集合,汇总时只能把同一档位的时间段按序拼接或编入播放列表,不能把不同分辨率直接合成一条连续媒体流。
任务需要稳定的源文件版本、编码配置版本和输出路径。重试先写临时对象,校验成功后发布;旧任务不能覆盖新配置的结果。除视频帧数外,还应检查音频边界、时间戳、色彩信息和接缝。对于短视频,分布式切割、传输和汇总开销可能超过并行收益,直接按清晰度并行会更简单。
用 FFmpeg 走通一条可检查的 HLS 链路
以下示例将输入统一为 30 fps,输出 720p 画布,按约 2 秒安排关键帧并生成 MPEG-TS 分片。它用于理解编码与封装的关系,不是保留所有源片特征的通用生产配方。运行前准备支持 libx264 的 FFmpeg,并在独立输出目录中放置 input.mp4。
ffmpeg -i input.mp4 \
-map 0:v:0 -map '0:a:0?' \
-vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2,setsar=1" \
-r 30 -c:v libx264 -preset medium -crf 23 -pix_fmt yuv420p \
-g 60 -keyint_min 60 -sc_threshold 0 -flags +cgop \
-force_key_frames "expr:gte(t,n_forced*2)" \
-c:a aac -b:a 128k \
-f hls -hls_time 2 -hls_playlist_type vod \
-hls_flags independent_segments \
-hls_segment_filename 'segment_%03d.ts' playlist.m3u8
这里没有同时把 CRF 和目标平均码率当成两个独立的质量承诺。-hls_time 指定目标分片时长,实际切割仍受关键帧位置影响;independent_segments 写入独立分片声明,并不会替你修复不合适的源编码。示例通过重新编码和封闭 GOP 安排边界。FFmpeg HLS 封装说明
生成后逐个独立解码,可以发现丢失参考帧等问题:
for part in segment_*.ts; do
ffmpeg -v error -xerror -i "$part" -f null - || exit 1
done
还需要对比完整播放列表的时长、视频帧数和音画同步。无报错只说明这些样本能够解码,不代表各终端的播放体验已经通过验收。
码率阶梯按内容和终端决定
平台可以先设一组清晰度,再用代表性素材测量各档位的质量与流量。演讲、动画和高速运动画面不应机械共用相同码率。
Netflix 的 per-title encoding 提供了一种具体思路:对每个标题分析不同分辨率与码率下的质量,构造适合该内容的阶梯。它说明优化可以从“统一配置”推进到“按内容选择”,不意味着任意平台都会获得相同收益。Netflix 原始介绍
封装:HLS、DASH 与 CMAF 分别负责什么
HLS 用 m3u8 播放列表描述变体和媒体片段,DASH 用 MPD 描述媒体表示与时间信息。CMAF 是媒体格式约束,不是与二者并列的第三种播放列表协议。满足编码、时间轴、加密方案和终端兼容条件时,HLS 与 DASH 可以复用一组 CMAF 媒体片段。
HLS 主播放列表描述可选择的变体。下面的带宽数字仅用于展示语法,发布时应根据实际媒体测量,包括音频及相关开销:
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1800000,RESOLUTION=1280x720
720p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p/playlist.m3u8
媒体播放列表再描述每个时间段:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:2.000,
segment_000.ts
#EXTINF:2.000,
segment_001.ts
#EXTINF:1.000,
segment_002.ts
#EXT-X-ENDLIST
TARGETDURATION 属于媒体播放列表,不能混入主播放列表。fMP4 媒体还需要正确关联初始化段,例如通过 EXT-X-MAP。播放列表先于媒体对象发布,会让用户拿到索引却请求不到分片,因此发布顺序和完整性检查同样属于封装工作。HLS RFC 8216
分片更短会增加请求、对象和索引开销,并可能因更密的随机访问点影响压缩效率;更长则可能增加下载等待与直播追赶成本。端到端延迟还受编码、网络、分发和播放器缓冲影响,不能只用“分片长度乘三”推算。
分发和 ABR:缓存负责供应,播放器负责选择
媒体对象发布后应使用带内容或版本标识的稳定 URL。不可变分片可以长时间缓存,直播播放列表持续更新,需要与更新周期匹配的缓存策略。点播完整播放列表与直播滑动窗口不能共用同一套过期假设。
CDN 中间层可以合并重复回源,边缘节点缓存热点,但并不意味着中间层保存全量媒体。预热哪些时间段、哪些档位,应看用户观看分布;统一预热所有视频的最高档位可能只增加成本。鉴权参数与缓存键可以分离设计,但每个请求仍必须经过正确鉴权,不能为了提高命中率绕过访问控制。
播放器的 ABR 同时关心下载吞吐、缓冲水位、解码能力和切换代价。一个简化的吞吐选择函数如下,真实播放器还需要平滑、滞回和异常处理:
def choose_bitrate(ladder, estimated_bps, safety_factor=0.8):
if not ladder or any(rate <= 0 for rate in ladder):
raise ValueError("ladder must contain positive bitrates")
if estimated_bps < 0 or not 0 < safety_factor <= 1:
raise ValueError("invalid bandwidth estimate or safety factor")
candidates = [rate for rate in ladder
if rate <= estimated_bps * safety_factor]
return max(candidates) if candidates else min(ladder)
估算带宽低于最低档位时返回最低档,并不能保证不卡顿;此时媒体需求仍可能大于网络供给。升档应更谨慎,缓冲持续下降则需及时降档。首帧优化还可以结合合理的起播档位、连接复用和有限预加载,但预下载未观看的视频也消耗用户流量。
Seek 需要找到目标时间之前的可解码位置,获取必要媒体后解码并跳过前面的画面。缩略图或 I-frame-only 预览可以改善拖动反馈,不必为了展示预览而下载整段高码率视频。
加密与直播:时间边界之外还有协议状态
HLS 的 AES-128 整段加密与样本级加密是不同方式;DRM 还包含许可证、设备能力和密钥策略,不能把下载一个 AES 密钥等同于完整 DRM。跨 DRM 复用加密媒体,需要匹配终端支持的加密模式与封装方式;“采用 CENC”不自动保证所有设备都能使用同一份字节。
直播的关键变化是媒体仍在生产。LL-HLS 通过 partial segments、播放列表阻塞加载等机制缩短等待;低延迟 DASH 也可在完整片段生成前传送已产出的媒体部分。服务端、CDN 和播放器都必须支持相应行为,仅缩短分片不能完成低延迟改造。Apple 低延迟 HLS
直播还需要明确三个状态:
- 暂时中断:保留重连窗口,按时间轴变化处理 discontinuity,不立刻宣告永久结束
- 正常结束:确认不会继续产生媒体后,向媒体播放列表写入结束标记
- 观众落后:请求超出保留窗口时重新定位;如果提供回看,则由独立保留策略支持
互动连麦可以考虑 WebRTC,但它不等同于浏览器之间直接 P2P,服务端 SFU 转发和中继也很常见。应从互动目标倒推协议和延迟预算,而不是给协议名称贴上固定延迟数值。
用观看量、码率和质量约束计算成本
流量估算先看单位。假设每天 1 亿次观看,平均观看 180 秒,实际交付音视频平均总码率为 2 Mbps,则:
每天交付字节数
= 100,000,000 × 180 × 2,000,000 ÷ 8
= 4,500,000,000,000,000 bytes
= 4.5 PB(十进制)
这还没有计算预加载浪费、传输开销和重试。CDN 命中率主要影响回源流量,不会自动减少观众需要接收的媒体字节。实际账单还要代入合同计费方式,不能把流量计费和峰值带宽计费混算。
优化应比较完整代价:新编码器节省多少分发流量,增加多少转码成本,多少终端需要回退;按需转码减少了多少冷门产物,又是否推迟了发布;删除源文件节省了存储,却可能失去未来重新编码的能力。不同优化的收益不能直接相加,更不能在没有测试时承诺统一降本比例。
验收也沿同一条链路展开:上传完成率与恢复成本,转码排队与失败重试,媒体和索引一致性,首帧、卡顿和播放失败率。目标值应结合平台用户和网络分布设定,并区分终端、地域和内容类型。
视频分片设计的落点,是让失败可以局部恢复,让资源可以按需处理,同时保持完整文件、媒体时间轴和观看体验之间的一致性。字节块、转码任务和播放片段各有边界;把这些边界说清楚,平台才能在扩展规模时保留可验证的行为。