返回
创建于
状态
公开

requestVideoFrameCallback (通常简称为 rVFC) 是一个非常强大且相对现代的 Web API,专门用于在 HTML <video> 元素中处理视频帧。

简而言之,它允许你在视频的一个新帧即将被渲染到屏幕上时,精确地执行一段 JavaScript 回调函数。

以下是关于它的核心原理、优势以及 JavaScript 代码示例的详细拆解:

1. 为什么需要它?(对比 requestAnimationFrame)

在 rVFC 出现之前,开发者通常使用 requestAnimationFrame (rAF) 将视频绘制到 Canvas 上进行处理(比如做绿幕抠图、弹幕同步或添加滤镜)。

但这存在一个致命的同步问题

  • requestAnimationFrame 是与显示器的刷新率同步的(通常是 60Hz,即每秒 60 次)。
  • 视频 通常有自己独立的帧率(比如 24fps 的电影,或 30fps 的录像)。

如果你用 60Hz 的 rAF 去抓取 24fps 的视频帧,你会不可避免地抓取到大量重复的帧,这不仅浪费了 CPU/GPU 性能,在进行复杂的视频转码或 AI 逐帧分析时还会导致数据冗余。

requestVideoFrameCallback 完美解决了这个问题。 它的回调函数只会在视频真正产生新的一帧时才会触发。视频是 24fps,它就每秒执行 24 次。100% 帧同步,没有任何性能浪费。

2. 常见应用场景

  • 视频特效与滤镜: 高效地将视频帧绘制到 WebGL 或 Canvas 2D 上下文中并应用特效(如背景模糊、色彩替换)。
  • 计算机视觉与 AI: 为 WebRTC 或浏览器端的机器学习模型(如 TensorFlow.js 识别人脸、手势)提供精确的不重复图像数据。
  • 精准的字幕/覆盖物同步: 确保 DOM 元素或弹幕与视频画面达到毫秒级的完美同步。
  • 视频截图与抽帧: 精确获取特定时间点的画面。

3. JavaScript 代码示例

rVFC 的用法与 requestAnimationFrame 非常相似,都需要在回调函数内部递归调用自身来处理连续的帧。

javascript
1// 获取 DOM 元素
2const video = document.querySelector('video');
3const canvas = document.querySelector('canvas');
4const ctx = canvas.getContext('2d');
5
6// 定义每一帧触发的回调函数
7const updateCanvas = (now, metadata) => {
8  // 1. 将当前最新的视频帧绘制到 Canvas 上
9  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
10
11  // 2. 利用 metadata (元数据) 可以获取大量精准的底层信息
12  // 例如:当前帧在视频中的真实时间 (mediaTime)
13  // 以及自播放以来总共呈现了多少帧 (presentedFrames)
14  if (metadata.presentedFrames % 30 === 0) {
15      console.log(`已播放到: ${metadata.mediaTime}`);
16  }
17
18  // 3. 递归调用,请求处理下一帧
19  video.requestVideoFrameCallback(updateCanvas);
20};
21
22// 当视频准备好播放时,启动第一次回调
23video.addEventListener('play', () => {
24  video.requestVideoFrameCallback(updateCanvas);
25});

4. metadata 对象包含的信息

回调函数的第二个参数 metadata 是一个包含了视频底层渲染信息的宝库,其中最常用的属性包括:

  • mediaTime: 该帧在视频时间轴上的具体秒数(极其精准)。
  • width / height: 视频帧的真实像素尺寸。
  • presentedFrames: 已经提交给屏幕进行显示的帧总数。
  • expectedDisplayTime: 浏览器预计该帧真正在屏幕上亮起的时间戳。
rVFC:视频帧精确同步神器