前段时间处理过一例在Chrome中视频虚拟背景图显示范围不正确的问题,在阅读Chromium源码并结合一些试验后,这里简单记录一下media::VideoFrame这个类其中的与此次问题排查相关的三个属性的含义。
media::VideoFrame的代码位于 media\base\video_frame.h(.cc),下面代码摘抄自其中(Chromium 85,branch 4183):
// The full dimensions of the video frame data.
const gfx::Size& coded_size() const { return layout_.coded_size(); }
// A subsection of [0, 0, coded_size().width(), coded_size.height()]. This
// can be set to "soft-apply" a cropping. It determines the pointers into
// the data returned by visible_data().
const gfx::Rect& visible_rect() const { return visible_rect_; }
// Specifies that the |visible_rect| section of the frame is supposed to be
// scaled to this size when being presented. This can be used to represent
// anamorphic frames, or to "soft-apply" any custom scaling.
const gfx::Size& natural_size() const { return natural_size_; }
OK,从注释的字面意思上看:
- coded_size表示的是视频帧数据的全尺寸。
- visible_rect是(0,0,coded_size.width,coded_size.height)这个显示区域的子范围(也可能是全部显示范围)。
- natural_size表示的是视频帧按照visible_rect设置的呈现范围来缩放后的大小
我翻译的可能有点抽象,我来举个例子说明一下。
假设我们在web上通过 getUserMedia 请求的视频分辨率是 640x480:
const constraints = {
video: {
width: 640,
height: 480,
frameRate: 25
}
}
navigator.mediaDevices.getUserMedia(constraints).then(...).catch(...);
通常情况下,media::VideoFrame的这三个属性就是:
- coded_size : width=640, height=480
- visible_rect : 0, 0, 640, 480
- natural_size : width=640, height=480
OK,这个时候,似乎coded_size和natural_size一样没有什么区别。但是情况不总是这样,我们换一台摄像头,也许情况就变成了这样:
- coded_size : width=864, height=480
- visible_rect : 112, 0, 640, 480
- natural_size : width=640, height=480
看明白了吗?如果没有的话,我画一张图:
正常情况下,我们从网页上请求640x480,Chromium采集到的原始视频帧就是640x480。但情况不总是这样,因为还要受到其他的采集属性(如颜色空间、帧率等)的影响,Chromium会根据情况,采集一个大等于你希望显示的视频宽高,然后再裁出用户期望的大小。因此,上面的图中,就很好的说明了这种关系。
OK,上面我们看到,natural_size的大小和visible_rect的大小是相同的,还有一些情况下,这两个是不同的,例如:
- coded_size : width=1920, height=1080
- visible_rect : 240, 0, 1440, 1080
- natural_size : width=1280, height=960
这个时候情况就变成了这样:
什么意思呢?就是当你在网页上请求的视频宽高是 1280x960(这是一个4:3的显示比例),实际上Chromium会从摄像头按照1920x1080采集(这是一个16:9的显示比例),然后显示范围锁定在 (240,0, 1440, 1080) 这个区域内(1440x1080与目标分辨率1280x960的比例相同,都是4:3),最后进行缩放,由1440x1080缩放为1280x960去显示。
相信看到这里,media::VideoFrame的这三个属性的关系应该非常清楚了。不过,这个只是Chromium的做法,我发现在不同浏览器上的处理方法有所不同,例如使用相同的摄像头,相同的测试网页,在Chrome和Safari上,展现出来的摄像头显示范围是不同的,猜测Safari内部又是另外一种处理逻辑了,咱没有它的源码,无法给出科学的解释了。