MediaRecorder does not produce a valid WebM file

5.9k views Asked by At

MediaRecorder.isTypeSupported() returns true for 'video/webm', 'video/webm;codecs=avc1', and 'video/webm;codecs=vp9'.

However, only mimeType='video/webm;codecs=vp9' results in a valid webm file ('video/webm;codecs=vp9,opus').

Setting mimeType to either 'video/webm' and 'video/webm;codecs=avc1' results in 'video/x-matroska;codecs=avc1,opus' file, which is not a valid webm video.

Is this a MediaRecorder bug, Chrome bug or am I missing something?\

Software: Chrome 85, MacOS 10.15. I have not tried this on Windows or Linux.

2

There are 2 answers

7
VC.One On

However, only mimeType='video/webm;codecs=vp9' results in a valid webm file.
Is this a MediaRecorder bug, Chrome bug or am I missing something?

WebM is the correct a/v media container for the VP8 and VP9 video codecs.

AVC1 video codec belongs into an MPEG container (.mp4, .m4v).
This video codec can also be contained inside an MKV media file (Matroska .mkv).

1
Ben Bucksch On

Reproduction:

const wantMimeType = 'video/webm;codecs=h264,opus';
if (MediaRecorder.isTypeSupported(wantMimeType)) {
  let mediaRecorder = new MediaRecorder(stream, {
    mimeType: wantMimeType,
  });
  // ...drive the recorder
  mediaRecorder.onstop = (event) => {
    try {
      // returns 'video/x-matroska;codecs=avc1,opus' in Chrome,
      // and the binary file content also says Matroska instead of WebM
      let mimetype = mediaRecorder.mimeType;
      if (!mimeType.startsWith('video/webm')) {
        throw new Error(`We requested "${wantMimeType},
          but the browser gave us "${mediaRecorder.mimeType}"`);
      }
      let blob = new Blob(chunks, { type: mimeType });
      // ...convert to data: URL
      // ...play it in <video src="data:..."> // won't work, if Matroska
    } catch (ex) {
      alert(ex);
    }
  };
}

Observations:

  1. Chrome responds true to isTypeSupported('video/webm;codecs=h264,opus'), but Chrome actually does not support this combination, i.e. the response is factually incorrect and defeats the entire purpose of isTypeSupported().

  2. Chrome ignores the mimetype that we pass in the MediaRecorder constructor, and instead returns video/x-matroska;codecs=avc1,opus. Which then doesn't work, because <video> won't play the Matroska mimetype.

2 solutions:

  • Hacky: After recording, just change the mimetype back to video/webm;codecs=h264,opus. The file content will still say "Matroska", but <video> on Chrome will play it. It's a bad solution, because we're passing in the wrong mimetype, it doesn't match the content. Other software - or Chrome in later versions - might break over it.
  • Proper: Don't use H.264, but VP9 or VP8. Chrome supports both, and Firefox supports VP8, and they work properly. As a bonus, they are open, while H.264 is riddled with software patents. Depending on the hardware encoder implementation, VP8/9 also may give better image quality results.