Vanilagy / mp4-muxer

MP4 multiplexer in pure TypeScript with support for WebCodecs API, video & audio.
https://vanilagy.github.io/mp4-muxer/demo
MIT License
419 stars 32 forks source link

Some players don't show the last frame #35

Closed nuthinking closed 8 months ago

nuthinking commented 8 months ago

Maybe this is a problem with mp4 of with the players, but I noticed that if I export a simple video such as this:

https://github.com/Vanilagy/mp4-muxer/assets/163635/caac5b2c-dd48-4ea5-9bdd-015591f99c3b

Quicktime on my mac, Photos on my iPhone, and Instagram story creator don't show the last frame where the canvas is all red. In the browser, like you can see above, it works correctly.

In instagram:

https://github.com/Vanilagy/mp4-muxer/assets/163635/f131de36-ed44-4daa-81c9-ed514f6969d8

Here's my code:

    function drawFrame(nFrame: number) {
      if (ctx === null) return;
      const p = nFrame === 0 ? 0 : nFrame / (TOTAL_FRAMES - 1);
      ctx.fillStyle = 'blue';
      ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
      ctx.fillStyle = 'red';
      ctx.fillRect(0, 0, CANVAS_WIDTH * p, CANVAS_HEIGHT * p);
    }

    // Create an MP4 muxer with a video track and maybe an audio track
    const muxer = new Muxer({
      target: new ArrayBufferTarget(),

      video: {
        codec: 'avc',
        width: CANVAS_WIDTH,
        height: CANVAS_HEIGHT,
      },

      // Puts metadata to the start of the file. Since we're using ArrayBufferTarget anyway, this makes no difference
      // to memory footprint.
      fastStart: 'in-memory',

      // Because we're directly pumping a MediaStreamTrack's data into it, which doesn't start at timestamp = 0
      firstTimestampBehavior: 'offset',
    });

    const 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,
    });

    currentFrame.current = 0;
    while (currentFrame.current < TOTAL_FRAMES) {
      // add frame
      drawFrame(currentFrame.current);
      const frame = new VideoFrame(canvasRef.current, {
        timestamp: (currentFrame.current * 1e6) / 30, // Ensure equally-spaced frames every 1/30th of a second
      });
      videoEncoder.encode(frame);
      frame.close();
      currentFrame.current++;
    }
    await videoEncoder.flush();
    muxer.finalize();

    let buffer = muxer.target.buffer;
    downloadBlob(new Blob([buffer]));

I hope it's not a silly mistake with my code. Thanks! :)

Vanilagy commented 8 months ago

I only skimmed over your post, but could it be related to this? https://github.com/Vanilagy/mp4-muxer/issues/31

This was the exact same issue, with the last frame not showing in some players, and how to fix it.

nuthinking commented 8 months ago

Adding the frame duration did indeed fixed the issue. Thanks a lot! 🙏

(@Vanilagy do you still use Ko-Fi? I wrote you there about a project.)

Vanilagy commented 8 months ago

I do use Ko-Fi, of course! When did you donate? Maybe I forgot to read the donation message...

nuthinking commented 8 months ago

yesterday