requestVideoFrameCallback 实现低延迟视频特效的核心是帧对齐、零拷贝与主动时序控制,需确保视频可播放、启用playsinline、用户手势触发自动播放、disableRemotePlayback为false,并采用递归注册+frame.close()防丢帧和内存泄漏。

直接用 requestVideoFrameCallback 实现低延迟视频特效叠加与分析,核心在于“帧对齐 + 零拷贝 + 主动时序控制”。它不替代 canvas 渲染本身,而是把 JS 处理逻辑精准钉在每一帧真正准备上屏的那一刻,避开传统 drawImage + requestAnimationFrame 组合带来的 1–2 帧滞后。
必须确保视频已就绪并启用关键属性
回调不会触发,往往不是 API 不支持,而是前置条件没满足:
- 视频需处于可播放状态:监听
loadeddata或canplay后再注册回调,paused或ended状态下不触发 - 移动端必须显式启用内联播放:
playsinline和webkit-playsinline属性不可省略 - 自动播放需用户手势触发(如点击按钮后调用
play()),否则部分 iOS/Android WebView 会静默拒绝回调 - 确保
video.disableRemotePlayback = false(避免被系统投屏策略拦截)
正确注册与防丢帧的递归结构
该 API 是单次注册机制,漏掉重注册就会只处理第一帧。但盲目重复注册又可能引发竞态或内存泄漏。稳妥写法是:
- 用布尔标记位控制注册节奏,例如
let isPending = false - 在回调开头检查
if (isPending) return; isPending = true - 所有同步逻辑(含 WebGL 更新、滤镜计算、时间戳记录)完成后,再调用
video.requestVideoFrameCallback(callback) - 结尾务必调用
frame.close()释放 VideoFrame 对象,否则持续占用解码内存
高效获取 RGBA 数据用于滤镜或分析
VideoFrame 默认是 YUV420 格式,不能直接传给 canvas 或 WebGL。绕过主线程瓶颈的关键是:
- 优先使用
frame.copyTo(target, { format: 'rgba' }),浏览器内部会硬件加速完成 YUV→RGBA 转换 - 目标
ImageData尺寸必须严格匹配frame.displayWidth和frame.displayHeight(不是video.videoWidth),否则复制失败或图像错位 - 若需 GPU 加速滤镜,直接将
frame作为纹理源传入 WebGL(Chrome/Firefox 支持texImage2D接收 VideoFrame),完全跳过 CPU 拷贝 - 仅当必须 JS 分析像素(如肤色统计、亮度直方图)时,才用
copyTo获取Uint8ClampedArray,且建议限制采样区域(如中心 200×200)降低计算压力
与 UI 动画和音频同步的时序策略
不要混用 requestAnimationFrame 驱动视频逻辑——它的节奏和视频帧天然不同步。统一时钟源才能避免抖动:
- 以回调参数
metadata.presentationTime为唯一可信时间戳,所有 UI 更新(如进度条位置、AR 锚点偏移)都基于它线性插值 - 做音画同步时,用
metadata.mediaTime对齐 Web Audio 的audioContext.currentTime,误差通常 - 若需叠加 Canvas 动画(如动态遮罩),仍用
requestAnimationFrame,但渲染逻辑读取最新缓存的presentationTime和帧数据,而非自行推算当前帧 - 避免在回调中执行
ctx.getImageData或大尺寸drawImage,这些操作会阻塞合成线程,导致下一帧注册延迟甚至跳帧










