Things I Learned (2020-11)

Dropped Frame Count in Video🔗

JavaScript

如果在哔哩哔哩播放视频的过程中,出现了比较明显的卡顿,播放器的左下角会出现一个 Toast,提示“丢帧严重,已自动关闭防挡弹幕”。那么,播放器是如何检测出当前的丢帧是否严重的呢?

通过阅读哔哩哔哩播放器的部分源码,不难发现核心实现如下:

i.prototype.autoVisible = function() {
  var i = this;
  i.danmakuMask.maskIsShow() && (i.dropFramesTimer = setTimeout((() => {
    // 默认检测间隔是 600ms
    i.dropFramesTime = 600;
    // 当前总丢失帧数
    var e = i.droppedFrameCount();
    // i.dropFrames 是上次检查时总丢失帧数
    // 丢失超过 14 帧就认为丢帧严重
    if (e - i.dropFrames > 14)
      return i.player.toast.addTopHinter("丢帧严重,已自动关闭防挡弹幕", 2e3),
      i.maskDanmaku.value(!1),
      void i.danmakuMask.setting("dmask", !1);
    // 更新总丢失帧数
    i.dropFrames = e,
    i.autoVisible()
  }
  ), i.dropFramesTime))
}
,
i.prototype.droppedFrameCount = function() {
  return this.player.video && this.player.video.webkitDroppedFrameCount || 0
}

简单来说,代码判断了 600 毫秒内,是否有超过 14 帧丢失,如果有的话,就认为丢帧严重,需要做一些计算的降级。而这里判断丢帧数量的核心代码,是获取了 video 上的 webkitDroppedFrameCount 属性。因为是 webkit 开头,可以知道这个是一个非标准的 API。

通过查询 MDN 相关文档,不难发现,在正式的标准(草稿阶段)中,对应的应该是 VideoPlaybackQuality 中的 droppedVideoFrames 属性。

要获取当前 video 的丢失情况,可以使用下面的代码:

const quality = video.getVideoPlaybackQuality();
const droppedVideoFrames = quality.droppedVideoFrames;

这里 droppedVideoFrameswebkitDroppedFrameCount 是一致的。

如果想知道丢帧的百分比,可以配合 totalVideoFrames 一起判断:

const quality = video.getVideoPlaybackQuality();
const percent = quality.droppedVideoFrames / quality.totalVideoFrames;

注:视频解码前后都有可能发生丢帧。只要浏览器判断认为当前帧已经不可能在正确的时间被播放,就会触发丢帧。