6.7 KiB
草原指南 4K 原片播放缓冲优化通知
日期:2026-07-16
前端仓库:
mmg/hl-ui影响页面:草原指南 H5 详情/播放页
本次后端接口、字段和 OSS 地址均无变化
Important
2026-07-16 线上验证发现:原通知中的“主动暂停并等待固定缓冲秒数”会与浏览器原生媒体加载策略形成死锁,已经撤销。前端必须按独立更正通知
16_fix_grassland_guide_playback_buffer_deadlock.md处理,不得再以8/15秒阈值阻塞播放或切换倍速。
1. 问题与诊断结论
草原指南详情直接播放后端返回的 OSS 原始 MP4。正式环境真实 4K 样本的媒体参数为:
- 文件约
451.74 MiB,时长约95秒。 - 分辨率
3812 × 2160,60 fps。 - H.264 Main Profile,Level
5.2。 - 平均码率约
40.20 Mbps。 1x播放时短时峰值约72.98 Mbps。2x播放时平均网络消耗约80.40 Mbps;短时峰值约142.17 Mbps。
正式 OSS 已支持 HTTP Range,请求返回 206 Partial Content。同一诊断环境连续读取 OSS Range 的平均速度约 230.52 Mbps,高于该视频 2x 播放的短时峰值。因此目前没有证据表明 Java 服务或 OSS Range 能力是主要瓶颈;但该结果不代表每个用户到 OSS 的实时链路都能达到相同速度。
现已确认 1x 也会偶发卡顿。结合 1x 接近 73 Mbps 的短时峰值,更可能是用户链路瞬时波动与浏览器前向缓冲较浅共同造成:即使平均网速高于视频平均码率,只要短时间下载速度低于瞬时消耗速度,缓冲仍可能耗尽。因此首次 1x 播放和卡顿恢复也必须执行缓冲水位保护,不能只处理 2x。
此外,4K 60 fps 在 2x 下相当于设备需要承担接近 120 fps 的解码节奏。部分设备即使网络充足,也可能因硬件解码能力不足出现掉帧;前端需要把“等待网络缓冲”和“设备解码掉帧”分别记录。
2. 前端处理要求
2.1 保持原生 Range 播放
- 继续直接使用接口返回的
videoUrl作为<video src>。 - 设置
preload="auto",允许浏览器提前加载媒体数据。 - 不要使用
fetch/axios把完整 MP4 下载为 Blob 后再播放,避免一次性占用数百 MiB 内存并破坏原生 Range 调度。 - 不要改写 OSS 域名,不转成 HLS/M3U8,不接直播播放器。
- 本轮不降低码率、不转码、不新增清晰度档位。
2.2 计算真实前向缓冲
不能只读取 video.buffered.end(video.buffered.length - 1)。应找到包含 currentTime 的 buffered 区间,再计算:
function getBufferAhead(video) {
const currentTime = video.currentTime
for (let index = 0; index < video.buffered.length; index += 1) {
const start = video.buffered.start(index)
const end = video.buffered.end(index)
if (currentTime >= start && currentTime <= end) {
return Math.max(0, end - currentTime)
}
}
return 0
}
2.3 1x 与倍速播放缓冲策略
- 用户点击播放时直接调用原生
video.play(),不得先暂停等待固定缓冲秒数。 - 用户切换
2x时只设置video.playbackRate = 2,不得调用pause(),也不得重新执行“初始缓冲门槛”。 waiting事件只负责显示“正在缓冲”和记录诊断;不得在事件处理器内再次调用pause()。- 保持原生播放请求后,浏览器会继续 Range 取流;数据恢复后通过
playing事件关闭缓冲提示。 stalled用于记录网络加载停滞;仅当仍有播放意图且readyState < HTMLMediaElement.HAVE_FUTURE_DATA时显示缓冲提示,不主动暂停。timeupdate只采集缓冲指标,不得因低于人为水位而主动暂停。- 用户主动暂停、拖动进度或离开页面时清除缓冲提示,避免与真实播放意图混淆。
preload="auto"只是浏览器提示,不能强制浏览器预取指定秒数。
video.buffered 用于诊断和观测,不再作为阻塞 play() 或切换倍速的硬门槛。简单的原生 <video> 无法保证“暂停后一定继续预取到 N 秒”;若业务将来要求确定性分片缓冲,需要另行评估 MSE/HLS 或 CDN,不应在原生 MP4 播放器中模拟。
2.4 记录卡顿证据
至少监听并记录以下事件和状态:
- 事件:
loadstart、loadedmetadata、canplay、canplaythrough、progress、waiting、stalled、playing、seeking、seeked、error。 - 状态:
currentTime、playbackRate、前向缓冲秒数、readyState、networkState。 - 浏览器支持时记录
getVideoPlaybackQuality()的droppedVideoFrames和totalVideoFrames。
日志不得记录完整带签名 URL、Token 或其他凭证。建议只记录 videoId、事件时间和上述播放指标。
判断口径:
- 出现
waiting/stalled,同时前向缓冲接近0:网络或缓冲调度不足。 - 前向缓冲充足、没有
waiting,但droppedVideoFrames持续上升:设备解码能力不足。
3. 接口契约
播放接口保持不变:
GET /admin/grassland-guide/public/videos/{videoId}/play
前端继续使用:
| 字段 | 类型 | 说明 |
|---|---|---|
videoId |
string | 视频业务 ID |
videoUrl |
string | OSS 原始 MP4 地址,直接交给原生 <video> |
durationSeconds |
integer | 后端从素材解析的时长 |
effectiveCoverUrl |
string | 生效封面 |
雪花 ID 必须按字符串处理。本次不增加缓冲、码率或清晰度字段,前端不得等待后端返回这些字段后才处理播放。
相关接口说明见:
4. 前端验收清单
<video>使用接口原始videoUrl,并设置preload="auto"。- 没有将完整 MP4 下载为 Blob。
- 首次
1x播放不受固定缓冲秒数阻塞。 - 切换
2x不调用pause(),不等待15秒缓冲。 waiting/stalled只更新提示与日志,不主动暂停;playing能可靠关闭提示。- 用户暂停、拖动和离开页面时不会被自动恢复播放。
- 能区分并记录缓冲耗尽与设备解码掉帧。
- 使用正式 4K 样本分别连续验证
1x和2x,记录等待次数、累计等待时长、最小前向缓冲和掉帧数。 - Chrome 桌面端和目标移动设备均完成验证。
若完成上述缓冲策略后,多个地区和设备仍普遍出现“前向缓冲耗尽”,再单独评估 OSS 前置 CDN Range 缓存或 OSS 传输加速;该基础设施调整会新增费用,不属于本次前端通知范围。