浏览器视频帧操作方法 requestVideoFrameCallback() 简介
近期发现一个很酷的应用场景,对着摄像头做动作,然后分析器动画渲染到 3D 模型上,在了解其实现原理后,对一些技术点做一些学习笔记。 requestVideoFrameCallback()
就是其中的技术点,使用方法可以在浏览器中高效的处理视频。
HTMLVideoElement.requestVideoFrameCallback() 是一个新的WEB API,2021 年 1 月 25 日提交的草案。requestVideoFrameCallback()
方法允许WEB开发者注册一个回调方法,回调方法在新视频帧发送到合成器时在渲染步骤中运行。这是为了让开发人员对视频执行高效的每帧视频操作,例如视频处理和绘制到画布上(截屏)、视频分析或与外部音频源同步。
与 requestAnimationFrame() 的区别
可以通过API执行方法 drawImage() 将视频帧绘制到画布等操作将尽可能的与屏幕上播放的视频的帧速率同步。与通常每秒触发约 60
次的 window.requestAnimationFrame()
不同,requestVideoFrameCallback()
与实际视频帧速率绑定,但也有一个重要的例外:
运行回调的有效速率是视频速率和浏览器速率之间的较小速率。这意味着在以
60Hz
绘制的浏览器中播放的25fps
视频将以25Hz
触发回调。在同一个60Hz
浏览器中的120fps
视频会以60Hz
触发回调。
由于它与 window.requestAnimationFrame()
相似,该方法最初被提议为 video.requestAnimationFrame()
,最终使用新的名称 requestVideoFrameCallback()
,这是在漫长的讨论后达成一致的,这个名称相对来说比较更加直观。
在前端开发中,通常在使用一些新的WEB API的时候需要检测其可用性,同样可以使用一下代码进行检测:
if ("requestVideoFrameCallback" in HTMLVideoElement.prototype) {
// 支持相应的API
}
浏览器支持
可以点击链接查看相应的支持度。
使用方法
如果曾经使用过 requestAnimationFrame()
方法,那么会立即对 requestVideoFrameCallback()
方法并不陌生。注册一次初始回调,然后在回调触发时重新注册。
const videoFrameCallback = (now, metadata) => {
console.log(now, metadata);
// 重新注册回调以获得关于下一帧的通知。
videoElement.requestVideoFrameCallback(videoFrameCallback);
};
// 最初注册回调,以便在第一帧时得到通知。
videoElement.requestVideoFrameCallback(videoFrameCallback);
在回调中,now
是一个 DOMHighResTimeStamp
,metadata
是一个 VideoFrameMetadata
字典,具有以下属性:
presentationTime
:DOMHighResTimeStamp
类型,用户代理提交帧以进行合成的时间。expectedDisplayTime
:DOMHighResTimeStamp
类型,用户代理期望框架可见的时间。width
:unsigned long
类型,视频帧的宽度,以像素为单位。height
:unsigned long
类型,视频帧的高度,以像素为单位。mediaTime
:double
类型,媒体呈现时间戳 (PTS),以呈现帧的秒数为单位(例如,它在video.currentTime
时间线上的时间戳)。presentedFrames
:unsigned long
类型,提交用于合成的帧数的计数。允许客户端确定VideoFrameRequestCallback
实例之间是否丢失帧。processingDuration
:double
类型,从将具有与此帧相同的呈现时间戳 (PTS) 的编码数据包(例如,与mediaTime
相同)提交到解码器直到解码的帧准备好呈现所经过的持续时间(以秒为单位)。
对于 WebRTC
应用程序,可能会出现其他属性:
captureTime
:DOMHighResTimeStamp
类型,对于来自本地或远程源的视频帧,这是摄像机捕获帧的时间。对于远程源,使用时钟同步和RTCP
发送方报告来估计捕获时间,以将 RTP 时间戳转换为捕获时间。receiveTime
:DOMHighResTimeStamp
类型,对于来自远程源的视频帧,这是平台接收到编码帧的时间,即通过网络接收到属于该帧的最后一个数据包的时间。rtpTimestamp
:unsigned long
类型,与此视频帧关联的 RTP 时间戳。
请注意,在某些情况下,
width
和height
可能与videoWidth
和videoHeight
不同(例如,变形视频可能具有矩形像素)。
在上面的参数列表中特别值得关注的是 mediaTime
,在 Chromium
的实现中,使用音频时钟作为支持 video.currentTime
的时间源。而 mediaTime
直接由帧的 presentationTimestamp
填充。如果希望以可再现的方式准确地标识帧,包括准确地识别错过的帧,那么应该使用 mediaTime
。
总结
能够在浏览器中访问相机而不使用第三方软件是一个不可思议的进步。与 canvas
和一些JavaScript相结合,相机变得快速而容易访问。它不仅可以使用相机,而且因为 canvas
是超灵活的,将能够在未来添加性感的 instagram
风格的图像滤镜。