基于 HTML Video 元素的 Web 播放器,通常需要在播放卡顿时呈现加载中的交互。它的代码实现可能是这样的:
video.addEventListener('waiting', function() {
console.info('show loading');
}, false);
video.addEventListener('playing', function() {
console.info('hide loading');
}, false);然而,这个方案是不可靠的,在移动设备播放 HLS(M3U8) 直播流的场景下会有诸多问题:
- 部分机型或浏览器在缓冲视频时不会触发 waiting,恢复播放后不会触发 playing。
- 部分机型或浏览器在播放尚未恢复时就触发了 playing。
于是,就有了基于 timeupdate 事件的改良方案:
let timer;
function onTimeUpdate() {
if (timer) { clearTimeout(timer); }
console.info('hide loading');
timer = setTimeout(function() {
if (!video.paused) {
console.info('show loading');
}
}, 1000);
}
video.addEventListener('timeupdate', onTimeUpdate, false);只要视频在播放,timeupdate 事件就会不断触发,从而清理上一次回调时创建的定时器,通过定时器设定的函数就不会执行。反之,只要 1 秒内没有触发 timeupdate 事件,通过定时器设定的函数就会执行,从而显示加载中的交互。
这个方案在大部分情况下是适用的。然而,在后来的一次通过代理进行的限速测试中发现:在某些 iOS 版本中,即使直播卡顿,timeupdate 仍在继续触发,从而导致加载中的交互没有显示。苦恼之际,我发现了 requestVideoFrameCallback。

