w3c / webcodecs

WebCodecs is a flexible web API for encoding and decoding audio and video.
https://w3c.github.io/webcodecs/
Other
978 stars 136 forks source link

How to encode canvas stream to h264 #546

Closed HammerMax closed 2 years ago

HammerMax commented 2 years ago

I want to encode canvas stream and download as h264 file. I try to run the code as follow but the file can not play.

<body>
    <canvas id="canvas" height="480" width="720"></canvas>
    <video id="video" src="./video.mp4" controls></video>

    <script type="module">
        const canvas = document.getElementById("canvas")
        const video = document.getElementById("video")
        const ctx = canvas.getContext("2d")

        video.onloadedmetadata = () => {
            video.height = video.videoHeight
            video.width = video.videoWidth
        }

        let fn = () => {
            ctx.drawImage(video, 0, 0)
            requestAnimationFrame(() => {
                fn()
            })
        }

        fn()

        let result = new Uint8Array(0)
        const encoder = new VideoEncoder({
            output: (chunk, metadata) => {
                let data = new Uint8Array(chunk.byteLength)
                chunk.copyTo(data)
                result = concatTypedArrays(result, data)
            },
            error: (e) => {
                console.log("[error]", e)
            }
        })

        const config = {
            codec: "avc1.64001f"
            width: 640,
            height: 480,
            bitrate: 2_000_000,
            framerate: 30,
        };
        encoder.configure(config);

        const stream = canvas.captureStream(30)
        const track = stream.getVideoTracks()[0];

        setTimeout(() => {
            track.stop()
        }, 3000)

        const trackProcessor = new MediaStreamTrackProcessor(track);

        const reader = trackProcessor.readable.getReader();
        while (true) {
            const result = await reader.read();
            if (result.done) break;

            const frame = result.value;
            encoder.encode(frame, { keyframe: true });
            frame.close();
        }

        function concatTypedArrays(a, b) {
            var c = new (a.constructor)(a.length + b.length);
            c.set(a, 0);
            c.set(b, a.length);
            return c;
        }

        await encoder.flush()

        const blob = new Blob([result]);
        const videoURL = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = videoURL;
        a.download = "video.264";
        a.click();
    </script>
</body>

the file can not play, and I use ffprobe video.h264 return error Invalid data found when processing input I am new to encode/decode

I am not sure what missed

HammerMax commented 2 years ago

miss some init param { codec: "avc1.42001E", width: 640, height: 480, avc : { format: "annexb" }, hardwareAcceleration : "prefer-software", bitrate: 2_000_000, // 2 Mbps framerate: 30, }