预计阅读时间:20 分钟
本章你会理解:播放器点击"播放"到画面出现都做了什么、Web/iOS/Android 播放器各有什么限制、首帧优化的招数、自研和开源怎么选。
你点击"播放"后,播放器内部要完成至少 10 件事:
同时还要:
• 持续下载后面的切片
• 运行 ABR 算法
• 上报 QoE 数据
• 响应用户操作(暂停、seek、调清晰度)
• 处理 DRM challenge
一个现代播放器动辄十几万行代码,不是随便写一个 <video> 标签那么简单。
这能播一个 MP4,但不能播 HLS/DASH/CMAF:
• <video> 只能收一个连续的 MP4 文件
• HLS/DASH 是"一堆切片",需要 JS 把切片塞进 <video> 里
Safari 是个例外——它原生支持 HLS(Apple 自家的东西),直接 <video src="index.m3u8"> 能播。
MSE 是 W3C 的 API,允许 JS 动态往 video 里塞字节。
有了 MSE,JS 就能:
• 解析 m3u8/mpd 自己实现
• 按需下载切片
• 塞进 SourceBuffer
• 浏览器负责渲染
hls.js 和 Shaka Player 就是基于 MSE 建立的。
EME 是 W3C 的 DRM API,让 JS 对接浏览器内置的 CDM(Widevine in Chrome、PlayReady in Edge、FairPlay in Safari)。
| 库 | 主打 | 维护者 | 规模 |
|---|---|---|---|
| hls.js | HLS | Dailymotion / video-dev | 最成熟 HLS JS |
| Shaka Player | DASH + HLS | 功能最全 | |
| dash.js | DASH | DASH-IF | DASH 参考实现 |
| Video.js | UI 框架 | Brightcove | 支持多种后端 |
推荐:HLS-only 选 hls.js;DASH 或混合选 Shaka Player。
• iOS 系统原生播放器
• 唯一官方方式播放 FairPlay DRM 内容
• 苹果的 HLS 起源和最强支持
限制 1:协议只能用 HLS(不原生支持 DASH)。
限制 2:ABR 是黑盒。只有少量 API 可调:
无法自定义"选哪档切片"的逻辑。
限制 3:切片下载队列不可见。想预载、想自定义 cache 策略要绕开。
要自定义行为需要用 AVAssetResourceLoaderDelegate —— 劫持 manifest 和 segment 请求,返回自己准备的字节:
工程复杂,但头部 APP(TikTok、短剧 APP)常这么做以实现零 TTFF。
AVQueuePlayer 会自动预载队列里的下一个 item(部分预载,具体由系统决定)。
Google 官方的开源播放器,已在 Android 生态取代老的 MediaPlayer:
• 支持 HLS、DASH、SmoothStreaming、Progressive
• 原生 Widevine DRM(通过 MediaDrm API)
• 完全开源可自定义
AndroidX Media3 是 ExoPlayer 的"下一代封装"(2022+):
• TrackSelector:码率选择、分辨率限制
• LoadControl:缓冲参数
• MediaSourceFactory:自定义下载、CDN 调度
• RenderersFactory:自定义渲染(后处理滤镜等)
跨平台:想在 iOS 和 Android 一套代码?常见方案 React Native Video(底层 iOS=AVPlayer、Android=ExoPlayer)、Flutter video_player、或自己用 FFmpeg + MediaCodec/VideoToolbox 实现。
首帧时间 TTFF(Time To First Frame)是 VOD/短视频最敏感的指标。影响因素:
累加起来动辄 1-2 秒。怎么压到 300ms 以下?
| 手段 | 节省时间 |
|---|---|
| DNS Prefetch(APP 启动就解析域名) | 20-100ms |
| HTTP/3 + 0-RTT(之前连过直接建连) | 50-200ms |
| Preconnect(提前建 TLS) | 50-200ms |
| Manifest Prefetch(下一集 manifest 提前拉) | 200ms |
| 短 GOP(1-2s)+ 短 segment(2s) | 1-2 秒(启动不用等 6s) |
| 启动档位用低码率 | 切片小下载快 |
| Init Segment 本地缓存 | 50-100ms |
| 预载下一集首 3 片 | 对切集几乎零延迟 |
| 硬件解码 | 解码 ~0ms 开销 |
核心思想:
Buffer(缓冲区)是已下载但还没播的秒数。经典设计:
三个阈值:
• Min Buffer(起播需要的最小缓冲,如 3s)
• Target Buffer(理想缓冲,如 30s)
• Max Buffer(缓冲上限,如 60s,防止预下太多浪费)
| 场景 | Min | Target | Max |
|---|---|---|---|
| 长电影 | 3s | 30s | 120s |
| 短剧 | 1s | 10s | 20s |
| 低延迟直播 | 0.5s | 2s | 6s |
缓冲归零 → 必须停下等 → 用户看到转圈圈。策略:
• 缓冲 < 安全线 → ABR 下一个切片强制选最低档(保命)
• 切换 CDN 节点重试
• 上报事件触发告警
视频帧和音频帧是分别解码的,怎么保持同步?
依据 PTS(Presentation Timestamp):每帧都有一个"该什么时候显示"的时间戳。
大多数播放器用音频作为主时钟(人耳对时间偏差更敏感),视频按音频对齐。
| 硬件解码 | 软件解码 | |
|---|---|---|
| 性能 | 快,能解 4K 60fps | 慢,4K 可能吃不消 |
| 功耗 | 低 | 高 |
| 灵活性 | 受硬件支持限制 | 任意格式 |
| 兼容性问题 | 某些手机对特殊码流兼容差 | 稳定 |
优先用硬解。只有硬解不支持(AV1 在老芯片、不常见编码参数)时再回落到软解。
| 方案 | 适用 | 成本 |
|---|---|---|
| 直接用开源(hls.js / ExoPlayer / AVPlayer) | 99% 的 VOD 平台 | 低,几人月集成 |
| 在开源上做少量定制 | 有特殊 UI / ABR / 埋点需求 | 中 |
| 深度自研(替换 ExoPlayer 内核、iOS 绕开 AVPlayer) | 头部 APP(TikTok、短剧 APP、直播平台) | 高,几十人月 |
不要轻易自研播放器内核。除非你有明确的性能或体验问题是开源播放器无法满足,且有预算维护。
无论用开源还是自研,下面埋点一定要做:
| 事件 | 含义 |
|---|---|
video_attempt |
用户触发播放 |
video_start |
第一帧渲染 |
video_rebuffer_start |
卡顿开始 |
video_rebuffer_end |
卡顿结束 |
bitrate_change |
切换了码率档位 |
video_complete |
播放完成 |
video_error |
出错 |
video_exit |
用户退出 |
每事件附带:video_id、user_id、cdn、network_type、device、bitrate、buffer_level 等维度。
详见下一章:QoE 数据体系。
1. Web 播放 HLS/DASH 必须靠 MSE;DRM 必须靠 EME。
2. iOS 播放 HLS + FairPlay 只能用 AVPlayer,ABR 是黑盒。
3. Android ExoPlayer / Media3 开源可自定义,灵活度高。
4. TTFF 优化的核心手段:DNS Prefetch + HTTP/3 + 短 GOP + 预载。
5. 缓冲三阈值:Min / Target / Max;按业务场景调整。
6. 音视频同步以音频为主时钟。
7. 尽量用开源播放器,别轻易自研内核。
© 2026 Zmead · VOD 流媒体技术全解 · 第 9 章 / 共 12 章