CSS代码:
canvas, video {
width: 300px;
height: 200px;
}
HTML代码:
<canvas id="canvas" width="600" height="400"></canvas>
<p class="flex">
<button id="generate">生成MP4视频</button>
<a id="download" download="muxer-no-audio.mp4">下载</a>
</p>
<div class="view">
<video id="video" width="600" height="400" controls autoplay></video>
<p class="time">视频生成时间:<output id="output"></output></p>
</div>
JS代码:
// 页面内动画示意
handleDraw(document.getElementById('canvas'));
// 构造器,和音视频编码对象
var muxer = null;
var videoEncoder = null;
// 结束编码
const endEncoding = async () => {
await videoEncoder?.flush();
muxer.finalize();
let buffer = muxer.target?.buffer;
var blobUrl = URL.createObjectURL(new Blob([buffer]));
video.src = blobUrl;
download.href = blobUrl;
videoEncoder = null;
muxer = null;
};
// 创建屏幕外 canvas
var canvas = document.createElement('canvas');
canvas.width = 600;
canvas.height = 400;
// 构造包装器
muxer = new Mp4Muxer.Muxer({
target: new Mp4Muxer.ArrayBufferTarget(),
video: {
codec: 'avc',
width: canvas.width,
height: canvas.height,
frameRate: 30
},
firstTimestampBehavior: 'offset'
});
// 音视频编码器,这里使用的是WebCodese API
videoEncoder = new VideoEncoder({
output: (chunk, meta) => muxer.addVideoChunk(chunk, meta),
error: e => console.error(e)
});
videoEncoder.configure({
codec: 'avc1.42001f',
width: canvas.width,
height: canvas.height,
bitrate: 1e6
});
// 点击按钮的mp4生成
generate.onclick = async function () {
// 编码视频数据
var startTime = document.timeline.currentTime;
var frameCounter = 0;
// handleDraw源码可右键页面查看
handleDraw(canvas, function () {
let frame = new VideoFrame(canvas, {
timestamp: (frameCounter * 1000 / 30) * 1000
});
frameCounter++;
videoEncoder.encode(frame, {
keyFrame: frameCounter % 30 === 0
});
frame.close();
}, function () {
// 预期结束时间
// 由于音频和视频的编码时间不一致
// 所以这里需要等待音频编码结束
// 才能结束视频编码
const timeUsed = document.timeline.currentTime - startTime;
endEncoding();
// 时间设置
output.innerHTML = Math.round(timeUsed / 10) / 100;
});
// 一次性点击
this.disabled = true;
this.textContent = '生成中...';
};