返回
创建于
状态
公开
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 非常相似,都需要在回调函数内部递归调用自身来处理连续的帧。
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: 浏览器预计该帧真正在屏幕上亮起的时间戳。