Codec and container switching in MSE Sample

Available in Chrome 70+ | View on GitHub | Browse Samples

Background

Chrome adds support for improved cross-codec or cross-bytestream transitions in Media Source Extensions (MSE) playback using a new changeType() method on SourceBuffer. It basically allows the type of media bytes subsequently appended to the SourceBuffer to be changed.

This sample demonstrates the use of this new method by appending different types of media bytes to a single SourceBuffer of a MediaSource attached to an audio element.

Live Output


JavaScript Snippet

const mediaSource = new MediaSource();
const audio = document.querySelector('audio');
audio.src = URL.createObjectURL(mediaSource);

const resources = [
  {
    url: 'https://storage.googleapis.com/media-session/bear-opus.webm',
    type: 'audio/webm; codecs="opus"'
  }, {
    url: 'https://storage.googleapis.com/media-session/bear-flac_frag.mp4',
    type: 'audio/mp4; codecs="flac"'
  }
];

mediaSource.addEventListener('sourceopen', function() {
  URL.revokeObjectURL(audio.src);
  feedAudioElement(0 /* resources index */);
});

async function feedAudioElement(resourcesIndex) {
  if (resourcesIndex == resources.length) {
    mediaSource.endOfStream();
    return;
  }

  try {
    let sourceBuffer;
    const resource = resources[resourcesIndex];

    if (mediaSource.sourceBuffers.length == 0) {
      log('Adding source buffer with type: ' + resource.type);
      sourceBuffer = mediaSource.addSourceBuffer(resource.type);
      // The 'sequence' mode is used here purely for simplicity. A more
      // sophisticated app would know which timestampOffset and appendWindow to
      // use for each appendBuffer operation when splicing media.
      sourceBuffer.mode = 'sequence';
    } else {
      log('Changing source buffer type to: ' + resource.type);
      sourceBuffer = mediaSource.sourceBuffers[0];
      sourceBuffer.changeType(resource.type);
    }

    log('Fetching audio data...');
    const response = await fetch(resource.url);
    const data = await response.arrayBuffer();

    log('Appending data to audio element...');
    sourceBuffer.appendBuffer(data);
    sourceBuffer.addEventListener('updateend', function() {
      log('Source buffer updated');
      feedAudioElement(++resourcesIndex);
    }, { once: true });

  } catch(error) {
    log('Argh! ' + error);
    feedAudioElement(++resourcesIndex);
  }
}