HTMLVideoElement: requestVideoFrameCallback() 方法
HTMLVideoElement 接口的 requestVideoFrameCallback() 方法会注册一个回调函数,该函数会在新的视频帧发送到合成器时运行。这使得开发者能够对每个视频帧执行高效的操作。
语法
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 时间戳。
注意:在某些情况下,width 和 height 可能与 HTMLVideoElement.videoWidth 和 HTMLVideoElement.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 延迟。如果 expectedDisplayTime 与 now 的差值在十微秒左右,则该帧已渲染。如果 expectedDisplayTime 大约在十六毫秒之后(假设您的浏览器/屏幕以 60Hz 刷新),那么回调就是 v-sync 错过。
示例
在画布上绘制视频帧
此示例展示了如何使用 requestVideoFrameCallback() 以与视频完全相同的帧率将视频帧绘制到 <canvas> 元素上。它还记录帧元数据到屏幕上以便于调试。
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);
video,
canvas {
max-width: 49%;
}
<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 |
浏览器兼容性
加载中…
另见
<video>元素HTMLVideoElement.cancelVideoFrameCallback()- 在 developer.chrome.com 上使用
requestVideoFrameCallback()对视频执行高效的每视频帧操作 (2023): Perform efficient per-video-frame operations on video withrequestVideoFrameCallback()