05月14, 2020

如何把动态 canvas 转成视频

随着短视频的越来越流行,有时候需要把动态的 canvas 转换为视频然后发布到对应的平台上。当然最简单的办法就是利用录屏软件(如:QuickTime)直接将屏幕录制下来,然后通过一些视频编辑软件(如:iMovie)加工处理下。

作为一名很懒的程序员,当然是希望能够自动转换为视频,一气呵成,不需要手工来处理。你还别说,现在的浏览器还真提供了对应的 API 来处理这个事情,本文就来简单介绍下。

captureStream API

通过 canvas 的 captureStream API,就可以把 canvas 变成一个 stream,然后结合 MediaRecorder API 就可以转换为 video。

const stream = canvas.captureStream();
const recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
const data = [];
recorder.ondataavailable = function (event) {
  if (event.data && event.data.size) {
    data.push(event.data);
  }
};
recorder.onstop = () => {
  const url = URL.createObjectURL(new Blob(data, { type: 'video/webm' }));
  console.log('video url', url)
};

通过开始的时候调用 recorder.start() 和结束的时候调用 recorder.stop() 即可完成视频的录制工作。

注意:如果一个 canvas 操作完成后面不在变动的话,录制的时候会发现最终的视频并没有想要的长度。这是因为如果 canvas 不动的话后续就不再录制了。

解决办法也很简单,找到 canvas 中的一个元素,然后循环改变这个元素的透明度就可以(为了减少对视觉的影响,可以让透明度在 0.99 和 1 之间变化,这个目的就是让 canvas 一直在操作。当然用其他的方案也是可以)

MediaRecorder 支持的 mimeType

通过 MediaRecorder 来录制视频时,需要指定录制视频的编码格式,如:video/webmvide/mp4。 如果当前浏览器不支持的话则会报错。

可以先通过 MediaRecorder.isTypeSupported 方法检测浏览器是否支持,如:

MediaRecorder.isTypeSupported('video/webm');
MediaRecorder.isTypeSupported('video/mp4');

从目前的测试来看,video/webm 格式 chrome v81/firefox v76 都支持。但 video/mp4 目前都还不支持。

由于 webm 格式现在还没有那么流行,有些网站还不支持直接支持上传这个格式的视频,那么就需要把 webm 转换为 mp4 格式的视频。

通过测试发现,虽然 chrome v81 还不能直接支持 video/mp4 的格式,但已经支持了 video/webm;codecs=h264,也就是录制的视频是用 h264 格式来编码的。这样后续转码的时候视频部分可以直接拷贝,大大提升转码的速度。

视频转码和添加音频

通过这个方式录制的视频没有音频部分,而一般发布的视频都希望有音频作为 BGM。此时就可以通过 WebAssembly 版本的 ffmpeg 来完成了。

转换命令如下:

ffmpeg -i video.webm output.mp4; //转换格式
ffmpeg -i video.webm -i audio.mp3 -c:v copy -af apad -map 0:v -map 1:a -shortest  out.mp4; // 添加音频

具体的可以通过 ffmpeg.wasm 库来完成,大致代码如下:

private async convertVideoUrl(url: string): Promise<string> {
    const { audio, outVideoType, mimeType, workerOptions, transcodeOptions, concatDemuxerOptions } = this.config;
    const { createFFmpeg } = window.FFmpeg;
    const ffmpeg = createFFmpeg(workerOptions || {});
    await ffmpeg.load();
    const type = mimeType.split(';')[0].split('/')[1];
    await ffmpeg.write(`video.${type}`, url);

    if (audio) {
      const audioType = audio.split('.').pop();
      await ffmpeg.write(`1.${audioType}`, audio);
      await ffmpeg.run(`-i video.${type} -i 1.${audioType} ${concatDemuxerOptions} out.${outVideoType}`);
    } else {
      if (type !== outVideoType) {
        await ffmpeg.transcode(`video.${type} `, `out.${outVideoType}`, transcodeOptions);
      }
    }
    const data  = await ffmpeg.read(`out.${outVideoType}`);
    const blob = new Blob([data.buffer], { type: `video/${outVideoType}` })
    const mp4Url = URL.createObjectURL(blob);
    return mp4Url;
  }

canvas2video 库

为了方面使用,已经将录制视频和转换视频等操作封装成了一个库 canvas2video 。代码不多,功能也比较聚焦,可以直接使用。

本文链接:http://www.welefen.com/post/how-to-convert-dynamic-canvas-to-video.html

-- EOF --