HTMLVideoElement: requestVideoFrameCallback() 方法

Baseline 2024
新推出

自 2024 年 10 月以来,此功能已可在最新的设备和浏览器版本上使用。此功能可能不适用于旧设备或浏览器。

HTMLVideoElement 接口的 requestVideoFrameCallback() 方法会注册一个回调函数,该函数会在新的视频帧发送到合成器时运行。这使得开发者能够对每个视频帧执行高效的操作。

语法

js
requestVideoFrameCallback(callback)

参数

回调

当新的视频帧发送到合成器时运行的回调函数。它包含两个参数

now

一个 DOMHighResTimeStamp,表示回调函数被调用的时间。

metadata(元数据)

包含以下属性的对象:

expectedDisplayTime

一个 DOMHighResTimeStamp,表示浏览器期望该帧可见的时间。

height

一个数字,以媒体像素为单位,表示视频帧的高度(可见解码像素,不考虑宽高比调整)。

mediaTime

一个数字,以秒为单位,表示已呈现帧的媒体呈现时间戳。这等于 HTMLMediaElement.currentTime 时间线上的帧时间戳。

presentationTime

一个 DOMHighResTimeStamp,表示浏览器提交该帧进行合成的时间。

presentedFrames

一个数字,表示到目前为止与当前回调一起提交进行合成的帧数。这可用于检测回调实例之间是否错过了帧。

processingDuration

一个数字,以秒为单位,表示具有与该帧相同的呈现时间戳的编码数据包提交到解码器(即 mediaTime)与解码后的帧准备好呈现之间的时间。

width

一个数字,以媒体像素为单位,表示视频帧的宽度(可见解码像素,不考虑宽高比调整)。

WebRTC 应用程序中使用的 requestVideoFrameCallback() 回调中可能还会提供其他元数据属性。

captureTime

一个 DOMHighResTimeStamp,表示捕获帧的时间。这适用于来自本地或远程源的视频帧。对于远程源,捕获时间是使用时钟同步和 RTCP 发送方报告估算的,以将 RTP 时间戳转换为捕获时间。

receiveTime

一个 DOMHighResTimeStamp,表示编码帧被平台接收的时间。这适用于来自远程源的视频帧。具体来说,这对应于属于该帧的最后一个数据包通过网络接收的时间。

rtpTimestamp

一个数字,表示与该视频帧关联的 RTP 时间戳。

注意:在某些情况下,widthheight 可能与 HTMLVideoElement.videoWidthHTMLVideoElement.videoHeight 不同(例如,变形视频可能具有矩形像素)。

返回值

一个数字,表示一个唯一的回调 ID。

这可以传递给 HTMLVideoElement.cancelVideoFrameCallback() 来取消回调注册。

描述

requestVideoFrameCallback() 的典型用例包括视频处理和绘制到画布、视频分析以及与外部音频源同步。过去,通过在 timeupdate 事件触发时在当前视频显示上运行操作来执行每帧处理,这种方式效率低下且不准确。这种技术无法访问实际的视频帧。

requestVideoFrameCallback() 的用法与 Window.requestAnimationFrame() 类似。您使用它来运行一个回调函数,该函数在下一个视频帧发送到合成器时执行某些操作。回调通过再次调用 requestVideoFrameCallback() 来完成,以便在下一个视频帧合成时运行回调,以此类推。然而,requestVideoFrameCallback() 在几个方面针对视频操作进行了优化:

  • requestVideoFrameCallback() 提供了对每个独立视频帧的可靠访问。
  • requestAnimationFrame() 尝试匹配显示刷新率,通常为 60Hz。另一方面,requestVideoFrameCallback() 尝试匹配视频帧率。更具体地说,回调的运行速率将是视频帧率和浏览器绘制刷新率中的较低者。例如,在以 60Hz 绘制的浏览器中播放的帧率为 25fps 的视频,其回调速率为 25Hz。在同一 60Hz 浏览器中运行的帧率为 120fps 的视频,其回调速率为 60Hz。
  • requestVideoFrameCallback() 在回调函数中提供了有用的视频元数据。

需要注意的一点是,requestVideoFrameCallback() 不提供任何严格的保证,确保您的回调输出将与视频帧率保持同步。它可能比新视频帧呈现时晚一个垂直同步(v-sync)才被触发。(V-sync 是一种图形技术,用于将视频的帧率与显示器的刷新率同步)。

该 API 在主线程上运行,而视频合成很可能发生在单独的合成线程上。您必须考虑这些操作完成所需的时间,以及视频本身以及您的 requestVideoFrameCallback() 操作结果显示在屏幕上所需的时间。

您可以比较 now 回调参数和 expectedDisplayTime 元数据属性来确定您的回调是否是 v-sync 延迟。如果 expectedDisplayTimenow 的差值在十微秒左右,则该帧已渲染。如果 expectedDisplayTime 大约在十六毫秒之后(假设您的浏览器/屏幕以 60Hz 刷新),那么回调就是 v-sync 错过。

示例

在画布上绘制视频帧

此示例展示了如何使用 requestVideoFrameCallback() 以与视频完全相同的帧率将视频帧绘制到 <canvas> 元素上。它还记录帧元数据到屏幕上以便于调试。

js
const button = document.querySelector("button");
const video = document.querySelector("video");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const fpsInfo = document.querySelector("#fps-info");
const metadataInfo = document.querySelector("#metadata-info");

button.addEventListener("click", () =>
  video.paused ? video.play() : video.pause(),
);

video.addEventListener("play", () => {
  if (!("requestVideoFrameCallback" in HTMLVideoElement.prototype)) {
    console.error(
      "Your browser does not support the `Video.requestVideoFrameCallback()` API.",
    );
  }
});

let width = canvas.width;
let height = canvas.height;

let paintCount = 0;
let startTime = 0.0;

const updateCanvas = (now, metadata) => {
  if (startTime === 0.0) {
    startTime = now;
  }

  ctx.drawImage(video, 0, 0, width, height);

  const elapsed = (now - startTime) / 1000.0;
  const fps = (++paintCount / elapsed).toFixed(3);
  fpsInfo.innerText = !isFinite(fps) ? 0 : fps;
  metadataInfo.innerText = JSON.stringify(metadata, null, 2);

  video.requestVideoFrameCallback(updateCanvas);
};

video.src = "https://mdn.github.io/shared-assets/videos/flower.mp4";
video.requestVideoFrameCallback(updateCanvas);
css
video,
canvas {
  max-width: 49%;
}
html
<p>
  Start <button type="button">⏯</button> playing the video. Pause the video to
  read the metadata. Drawing video frames on the canvas is synced with the
  actual video framerate.
</p>
<video controls playsinline></video>
<canvas width="960" height="540"></canvas>
<p><span id="fps-info">0</span>fps</p>
<pre id="metadata-info"></pre>

规范

规范
HTMLVideoElement.requestVideoFrameCallback()
# dom-htmlvideoelement-requestvideoframecallback

浏览器兼容性

另见