VideoFrame 学习篇

VideoFrame是什么

官方的解释是:VideoFrame是js提供的面向webCodecs的端口, 代表了视频的一帧数据

也就是说,学习VideoFrame后,咱们才能更好的入手webCodec

如何创建VideoFrame

  1. MediaStreamTrackProcessor
  2. 可以用构造函数VideoFrame,传入ArrayBuffer, TypedArray, 或者DataView得到。
  3. 可以通过构造函数VideoFrame,传入 SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap 或者 OffscreenCanvas得到。

我们这里主要关注第二种方式,既可以熟悉VideoFrame又能充分了解视频帧在js中的存储形式。

构造RGBA帧

js 复制代码
// html
<p>RGBA</p>
<canvas id="RGBA"></canvas>
// js
let rgbaFrame;
function createRgbaVideoFrame() {
    const pixelSize = 4;
    const init = {timestamp: 0, codedWidth: 320, codedHeight: 200, format: "RGBA"};
    const data = new Uint8Array(init.codedWidth * init.codedHeight * pixelSize);
    for (let x = 0; x < init.codedWidth; x++) {
        for (let y = 0; y < init.codedHeight; y++) {
            let offset = (y * init.codedWidth + x) * pixelSize;
            data[offset] = 0xFF;      // Red
            data[offset + 1] = 0x00;  // Green
            data[offset + 2] = 0x00;  // Blue
            data[offset + 3] = 0xFF; // Alpha
        }
    }
    rgbaFrame = new VideoFrame(data, init);
}
function renderRgbaVideoFrame() {
    const canvasDom = document.getElementById("RGBA");
    const ctx = canvasDom.getContext("2d");
    ctx.drawImage(rgbaFrame, 0, 0)
}

这样我们就创建了一帧红色画面,并且把它渲染在了canvas上。

接下来我们分析一下:一个320 * 200像素的canvas,在RGBA编码下,需要:

320 * 200 * 4 = 256000个字节存放。

这个4分别存放R(红色分量)、G(绿色分量)、B(蓝色分量)、A(透明度)。

并且我们可以知道,其在JS中的存放方式很简单,就是按顺序存放:

上面我们将所有红色分量和透明度设置成255,其他设置为0,便得到了一帧红色的画面。

瞬间帧这个概念就用具体的数据鲜明的表示出来了,不再那么抽象了!!

构造YUV444帧

这么小小的一张图,就要256KB个字节,那咱图像,视频都可以歇歇了,带宽得拉满。咱再来看看其他格式的帧数据。

下面试试YUV444帧,不了解YUV的可以看看这一篇: YUV知识点归纳

js 复制代码
// html
<p>YUV444</p>
<canvas id="YUV444"></canvas>
// js
let yuv444Frame;
function createYuv444VideoFrame() {
    const pixelSize = 4;
    const init = {timestamp: 0, codedWidth: 320, codedHeight: 200, format: "I444"};
    const rgbaData = new Uint8Array(init.codedWidth * init.codedHeight * pixelSize);
    for (let x = 0; x < init.codedWidth; x++) {
        for (let y = 0; y < init.codedHeight; y++) {
            let offset = (y * init.codedWidth + x) * pixelSize;
            rgbaData[offset] = 0xFF;      // Red
            rgbaData[offset + 1] = 0x00;  // Green
            rgbaData[offset + 2] = 0x00;  // Blue
            rgbaData[offset + 3] = 0xFF; // Alpha
        }
    }

    const yuvData = new Uint8Array(init.codedWidth * init.codedHeight * (pixelSize - 1));
    for(let i = 0; i < rgbaData.length; i += pixelSize) {
        const [R, G, B] = rgbaData.slice(i, i + pixelSize);
        yuvData[i / pixelSize] = 0.257 * R + 0.504 * G + 0.098 * B + 16;
        yuvData[i / pixelSize + init.codedWidth * init.codedHeight] = -0.148 * R - 0.291 * G + 0.439 * B + 128;
        yuvData[i / pixelSize + 2 * init.codedWidth * init.codedHeight] = 0.439 * R - 0.368 * G - 0.071 * B + 128;
    }

    yuv444Frame = new VideoFrame(yuvData, init);
}
function renderYuv444VideoFrame() {
    const canvasDom = document.getElementById("YUV444");
    const ctx = canvasDom.getContext("2d");
    ctx.drawImage(rgbaFrame, 0, 0)
}
createYuv444VideoFrame();
renderYuv444VideoFrame();

最终得到:

这里就不解释转化算法了,总之可以发现,YUV444格式,绘制一个相同大小的帧,只需要

320 * 200 * 3 = 192000也就是RGBA格式的四分之三

根据上面的算法,我们还可以发现,YUV444格式的视频存储方式是:

比RGBA格式的数据更好分离!

其他格式帧

可以通过VideoFrame.format查看当前视频帧的编码格式。chrome浏览器支持的格式可以参考相关文档:developer.mozilla.org/en-US/docs/...

我们日常中最常用的还是YUV420,大小是RGBA的八分之三!!

相关推荐
benbenAItalk3 小时前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
2601_962885723 小时前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
西瓜太郎12344 小时前
Claude Code、Codex CLI、Gemini CLI 能否共用一枚 Key?先看协议选择矩阵
前端·api 网关·claude code·gemini cli·codex cli
陈随易6 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
水域安全老周6 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师7 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing987 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
EatFan7 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app
梦想平凡8 小时前
百游棋牌源代码开发搭建教程(五):房间创建、座位分配与请求幂等实现
前端·javascript·数据库·源代码管理
IT_陈寒8 小时前
Redis的Set操作居然能把我的服务整挂了?
前端·人工智能·后端