<!doctype html>
<html>
<head>
  <title>MediaRecorder mimeType</title>
  <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#dom-mediarecorder-mimeType">
  <script src="/resources/testharness.js"></script>
  <script src="/resources/testharnessreport.js"></script>
  <script src="utils/sources.js"></script>
</head>
<body>
<script>
const AUDIO_ONLY_MIME_TYPES = [
  'audio/mp4',
  'audio/ogg',
  'audio/webm',
];

const AUDIO_CODECS_MIME_TYPES = [
  'audio/ogg; codecs="vorbis"',
  'audio/ogg; codecs="opus"',
  'audio/webm; codecs="vorbis"',
  'audio/webm; codecs="opus"',
];

const VIDEO_ONLY_MIME_TYPES = [
  'video/mp4',
  'video/webm',
];

const VIDEO_CODECS_MIME_TYPES = [
  'video/webm; codecs="vp8"',
  'video/webm; codecs="vp9"',
  'video/webm; codecs="av1"',
];

const AUDIO_VIDEO_MIME_TYPES = [
  'video/webm; codecs="vp8, vorbis"',
  'video/webm; codecs="vp8, opus"',
  'video/webm; codecs="vp9, vorbis"',
  'video/webm; codecs="vp9, opus"',
  'video/webm; codecs="av1, opus"',
];

const AUDIO_MIME_TYPES = [
  ...AUDIO_ONLY_MIME_TYPES,
  ...AUDIO_CODECS_MIME_TYPES,
  ...AUDIO_VIDEO_MIME_TYPES,
];

const VIDEO_MIME_TYPES = [
  ...VIDEO_ONLY_MIME_TYPES,
  ...VIDEO_CODECS_MIME_TYPES,
  ...AUDIO_VIDEO_MIME_TYPES,
];

const MIME_TYPES = [
  ...AUDIO_ONLY_MIME_TYPES,
  ...AUDIO_CODECS_MIME_TYPES,
  ...VIDEO_ONLY_MIME_TYPES,
  ...VIDEO_CODECS_MIME_TYPES,
  ...AUDIO_VIDEO_MIME_TYPES,
];

test(t => {
  const recorder = new MediaRecorder(createAudioStream(t).stream);
  assert_equals(recorder.mimeType, "",
    "MediaRecorder has no default mimeType");
}, "MediaRecorder sets no default mimeType in the constructor for audio");

test(t => {
  const recorder = new MediaRecorder(createVideoStream(t).stream);
  assert_equals(recorder.mimeType, "",
    "MediaRecorder has no default mimeType");
}, "MediaRecorder sets no default mimeType in the constructor for video");

test(t => {
  const recorder = new MediaRecorder(createAudioVideoStream(t).stream);
  assert_equals(recorder.mimeType, "",
    "MediaRecorder has no default mimeType");
}, "MediaRecorder sets no default mimeType in the constructor for audio/video");

test(t => {
  assert_throws_dom("NotSupportedError",
    () => new MediaRecorder(new MediaStream(), {mimeType: "audio/banana"}));
}, "MediaRecorder invalid audio mimeType throws");

test(t => {
  assert_false(MediaRecorder.isTypeSupported("audio/banana"));
}, "MediaRecorder invalid audio mimeType is unsupported");

test(t => {
  assert_throws_dom("NotSupportedError",
    () => new MediaRecorder(new MediaStream(), {mimeType: "video/pineapple"}));
}, "MediaRecorder invalid video mimeType throws");

test(t => {
  assert_false(MediaRecorder.isTypeSupported("video/pineapple"));
}, "MediaRecorder invalid video mimeType is unsupported");

for (const mimeType of MIME_TYPES) {
  if (MediaRecorder.isTypeSupported(mimeType)) {
    test(t => {
      const recorder = new MediaRecorder(new MediaStream(), {mimeType});
      assert_equals(recorder.mimeType, mimeType, "Supported mimeType is set");
    }, `Supported mimeType ${mimeType} is set immediately after constructing`);
  } else {
    test(t => {
      assert_throws_dom("NotSupportedError",
        () => new MediaRecorder(new MediaStream(), {mimeType}));
    }, `Unsupported mimeType ${mimeType} throws`);
  }
}

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioStream(t).stream);
  recorder.start();
  await new Promise(r => recorder.onstart = r);
  assert_not_equals(recorder.mimeType, "");
}, "MediaRecorder sets a nonempty mimeType on 'onstart' for audio");

for (const mimeType of AUDIO_MIME_TYPES) {
  if (MediaRecorder.isTypeSupported(mimeType)) {
    test(t => {
      const recorder = new MediaRecorder(createFlowingAudioStream(t).stream, {mimeType});
      recorder.start();
    }, `MediaRecorder start() does not throw with ${mimeType} for audio`);
  } else {
    test(t => {
      assert_throws_dom("NotSupportedError",
        () => new MediaRecorder(createFlowingAudioStream(t).stream, {mimeType}));
    }, `Unsupported mimeType ${mimeType} throws for audio`);
  }
}

for (const mimeType of VIDEO_MIME_TYPES) {
  if (MediaRecorder.isTypeSupported(mimeType)) {
    test(t => {
      const recorder = new MediaRecorder(createFlowingVideoStream(t).stream, {mimeType});
      recorder.start();
    }, `MediaRecorder start() does not throw with ${mimeType} for video`);
  } else {
    test(t => {
      assert_throws_dom("NotSupportedError",
        () => new MediaRecorder(createFlowingVideoStream(t).stream, {mimeType}));
    }, `Unsupported mimeType ${mimeType} throws for video`);
  }
}

for (const mimeType of VIDEO_ONLY_MIME_TYPES) {
  if (MediaRecorder.isTypeSupported(mimeType)) {
    test(t => {
      const recorder = new MediaRecorder(createFlowingAudioStream(t).stream, {mimeType});
      recorder.start();
    }, `MediaRecorder start() does not throw with ${mimeType} for audio`);
  }
}

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingVideoStream(t).stream);
  recorder.start();
  await new Promise(r => recorder.onstart = r);
  assert_not_equals(recorder.mimeType, "");
}, "MediaRecorder sets a nonempty mimeType on 'onstart' for video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioVideoStream(t).stream);
  recorder.start();
  await new Promise(r => recorder.onstart = r);
  assert_not_equals(recorder.mimeType, "");
}, "MediaRecorder sets a nonempty mimeType on 'onstart' for audio/video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioStream(t).stream);
  recorder.start();
  assert_equals(recorder.mimeType, "");
}, "MediaRecorder mimeType is not set before 'onstart' for audio");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingVideoStream(t).stream);
  recorder.start();
  assert_equals(recorder.mimeType, "");
}, "MediaRecorder mimeType is not set before 'onstart' for video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioVideoStream(t).stream);
  recorder.start();
  assert_equals(recorder.mimeType, "");
}, "MediaRecorder mimeType is not set before 'onstart' for audio/video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioStream(t).stream);
  const onstartPromise = new Promise(resolve => {
    recorder.onstart = () => {
      recorder.onstart = () => t.step_func(() => {
        assert_not_reached("MediaRecorder doesn't fire 'onstart' twice");
      });
      resolve();
    }
  });
  recorder.start();
  await onstartPromise;
  await new Promise(r => t.step_timeout(r, 1000));
}, "MediaRecorder doesn't fire 'onstart' multiple times for audio");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingVideoStream(t).stream);
  const onstartPromise = new Promise(resolve => {
    recorder.onstart = () => {
      recorder.onstart = () => t.step_func(() => {
        assert_not_reached("MediaRecorder doesn't fire 'onstart' twice");
      });
      resolve();
    }
  });
  recorder.start();
  await onstartPromise;
  await new Promise(r => t.step_timeout(r, 1000));
}, "MediaRecorder doesn't fire 'onstart' multiple times for video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioVideoStream(t).stream);
  const onstartPromise = new Promise(resolve => {
    recorder.onstart = () => {
      recorder.onstart = () => t.step_func(() => {
        assert_not_reached("MediaRecorder doesn't fire 'onstart' twice");
      });
      resolve();
    }
  });
  recorder.start();
  await onstartPromise;
  await new Promise(r => t.step_timeout(r, 1000));
}, "MediaRecorder doesn't fire 'onstart' multiple times for audio/video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioStream(t).stream);
  recorder.start();
  await new Promise(r => recorder.onstart = r);
  assert_regexp_match(recorder.mimeType, /^audio\//,
    "mimeType has an expected media type");
  assert_regexp_match(recorder.mimeType, /^[a-z]+\/[a-z]+/,
    "mimeType has a container subtype");
  assert_regexp_match(
    recorder.mimeType, /^[a-z]+\/[a-z]+;[ ]*codecs=[^,]+$/,
    "mimeType has one codec a");
}, "MediaRecorder formats mimeType well after 'start' for audio");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingVideoStream(t).stream);
  recorder.start();
  await new Promise(r => recorder.onstart = r);
  assert_regexp_match(recorder.mimeType, /^video\//,
    "mimeType has an expected media type");
  assert_regexp_match(recorder.mimeType, /^[a-z]+\/[a-z]+/,
    "mimeType has a container subtype");
  assert_regexp_match(
    recorder.mimeType, /^[a-z]+\/[a-z]+;[ ]*codecs=[^,]+$/,
    "mimeType has one codec a");
}, "MediaRecorder formats mimeType well after 'start' for video");

promise_test(async t => {
  const recorder = new MediaRecorder(createFlowingAudioVideoStream(t).stream);
  recorder.start();
  await new Promise(r => recorder.onstart = r);
  assert_regexp_match(recorder.mimeType, /^video\//,
    "mimeType has an expected media type");
  assert_regexp_match(recorder.mimeType, /^[a-z]+\/[a-z]+/,
    "mimeType has a container subtype");
  assert_regexp_match(
    recorder.mimeType, /^[a-z]+\/[a-z]+;[ ]*codecs=[^,]+,[^,]+$/,
    "mimeType has two codecs");
}, "MediaRecorder formats mimeType well after 'start' for audio/video");
</script>
</body>
</html>
