mp4-muxer实现纯画面mp4视频实例页面

回到相关文章 »

效果:

canvas效果

canvas动画非必须可见,为了方便大家学习才展示出来的。

下载

视频生成时间:

代码:

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 = '生成中...';
};