Skip to content

Latest commit

 

History

History
65 lines (43 loc) · 3.67 KB

superRecorder.md

File metadata and controls

65 lines (43 loc) · 3.67 KB

超级录屏

通过记录鼠标的坐标和点击,自动运镜。

核心是视频裁切。使用 FFmpeg 是自然的选择,wasm 太慢,普通录屏的裁剪是通过命令控制 FFmpeg 二进制来实现的。

观察 Electron(Chrome)的运行目录,我们可以发现有一个ffmpeg.dlllibffmpeg.so库,用于浏览器的媒体解码和渲染。浏览器有的,我们就不需要额外引入 FFmpeg 了,现在有一个新的 web api:WebCodecs,可以调用浏览器的媒体解码编码能力。

在这之前,我先说明媒体编码的重要性,如果我们用连续截屏的方式代替录屏,虽然 ImageData 可以很方便地进行图片截取,但内存会被迅速占用:

单帧全彩色高清(1920x1080)视频(每像素 4 字节)为 8,294,400 字节。

在典型的每秒 30 帧的情况下,每秒高清视频将占用 248,832,000 字节(~249 MB)。

一分钟的高清视频需要 14.93 GB 的存储空间。

视频编码其实就是一个压缩过程,把相同像素合并,还有其他高级的压缩操作。这样,我们借助视频编码压缩存储帧,编辑时解码成 ctx,编辑后再次编码压缩,导出时也使用了编码。

WebCodecs

具体请见MDNchrome dev 博客w3c等文档,或者学习WebAV这个项目,由于这个 API 复杂且新,网络上的文档不多,我也在学习中,下面是我的部分总结。

这里主要使用VideoDecoderVideoEncoder,通过new确定回调(类似reader之类的 api),然后必须configure。使用时调用各自的decodeencode即可。

VideoFrame是原始帧数据,需要及时close

flush方法可以理解为 Promise onend之类的方法,需要注意的是,for循环后用flush能正常等待完成操作,但其他异步的循环会提前结束(与其是结束,其实的处理队列暂时为空),所以要确保异步循环完毕再flush

close也要注意调用。

转换(编辑)

我们处理一个视频,需要解码(解压缩)->编辑处理->编码(压缩)

const decoder = new VideoDecoder({
  output: (frame: VideoFrame) => {
    // 解码 处理 编码
    const nFrame = transformX(frame);
    encoder.encode(nFrame);
    nFrame.close();
  },
  error: (e) => console.error("Decode error:", e),
});

decoder解码后再编码,output可以视为异步循环,所以要注意flush的调用问题。

for (const chunk of src) {
  decoder.decode(chunk);
}
await decoder.flush();
await encoder.flush();

这里先对decoder flush,因为 decode 用的是for同步循环,确保把所有任务都放到 encode 处理队列后,再对encoder进行flush。不能交换两者顺序,否则导致提前结束,代码提交 944a8a0就是解决这个 bug 的。

打包

我们平时接触的视频格式:mp4、webm 等记录了不止视频,还有帧率、声音、甚至字幕等信息。他们被成为容器。

不同容器支持的视频格式也不同,参考mdn

浏览器并没有提供容器包装器,只提供了编码器,所以我们需要其他的库,如 mp4box.js,我使用 mp4-muxer 和 wemb-muxer。