How to Stream Truncated Audio Using MediaSource API

With the MediaSource API, you can generate and configure media streams right in the browser. It allows you to perform a variety of operations on media data held by media-related HTML tags such as <audio> or <video>. For instance, you can mix different streams, create overlapping media, lazy load media, and edit media metrics such as change the volume or the frequency.

In this post, we’ll specifically see how to stream an audio sample (a truncated MP3 file) with the MediaSource API right in the browser in order to pre-show music to your audience. We will cover how to detect support for the API, how to connect the HTML media element to the API, how to fetch the media via Ajax, and finally how to stream it.

If you want to see in advance what we are up to, have a look at the source code on Github, or check out the demo page.

MediaSource API

Step 1. Create the HTML

To create the HTML, add an <audio> tag with a controls attribute to your page. For backward compatibility, also add a default error message for users whose browsers don’t support the feature. We will use JavaScript to turn on/off this message.

<audio controls>
Your browser doesn't support HTML audio element.
</audio>

Step 2. Detect browser support

In JavaScript, create a try…catch block that will throw an error if the MediaSource API is not supported by the user’s browser, or, with other words if MediaSource (the key) does not exist in the window object.

try {
  if (!'MediaSource' in window)
    throw new ReferenceError('There is no MediaSource property
          in window object.')
  } catch (e) {
    console.log(e);
}

Step 3. Detect MIME support

After the support check, also check for the support of the MIME type. If the MIME type of the media you want to stream is not supported by the browser, alert the user and throw an error.

var mime = 'audio/mpeg';
if (!MediaSource.isTypeSupported(mime)) {
  alert('Can not play the media. Media of MIME type ' +
        mime + ' is not supported.');
  throw ('Media of type ' + mime +
        ' is not supported.');
}

Note that the code snippet above needs to be placed inside the try block, before the catch block (for reference, follow the line numbering or check out the final JS file on Github).

Step 4. Link the <audio> tag to the MediaSource API

Create a new MediaSource object, and assign it as the source of the <audio> tag by using the URL.createObjectURL() method.

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

Step 5. Add a SourceBuffer object to MediaSource

When a HTML media element accesses a media source and is ready to create SourceBuffer objects, the MediaSource API fires a sourceopen event .

The SourceBuffer object holds a chunk of media that is eventually decoded, processed and played. A single MediaSource object can have multiple SourceBuffer objects.

Inside the event handler of the sourceopen event, add a SourceBuffer object to MediaSource with the addSourceBuffer() method.

mediaSource.addEventListener('sourceopen', function() {
  var sourceBuffer = this.addSourceBuffer(mime);
});

Step 6. Fetch the media

Now that you have a SourceBuffer object, it’s time to fetch the MP3 file. In our example, we’ll do so by using an AJAX request.

Use arraybuffer as responseType, which denotes binary data. When the response is successfully fetched, append it to SourceBuffer with the appendBuffer() method.

mediaSource.addEventListener('sourceopen', function() {
  var sourceBuffer = this.addSourceBuffer(mime);
  var xhr = new XMLHttpRequest;
  xhr.open('GET', 'sample.mp3');
  xhr.responseType = 'arraybuffer';
  xhr.onload = function() {
      try {
          switch (this.status) {
              case 200:
                sourceBuffer.appendBuffer(this.response);
                break;
              case 404:
                throw 'File Not Found';
              default:
                throw 'Failed to fetch the file';
         }
      } catch (e) {
        console.error(e);
      }
    };
    xhr.send();
});

Step 7. Indicate the end of the stream

When the API has finished appending the data to SourceBuffer an event called updatend is fired. Inside an event handler, call the endOfStream() method of MediaSource to indicate that the stream has ended.

mediaSource.addEventListener('sourceopen', function() {
  var sourceBuffer = this.addSourceBuffer(mime);
  var xhr = new XMLHttpRequest;
  xhr.open('GET', 'sample.mp3');
  xhr.responseType = 'arraybuffer';
  xhr.onload = function() {
    try {
      switch (this.status) {
        case 200:
            sourceBuffer.appendBuffer(this.response);
            sourceBuffer.addEventListener('updateend', function (_){
                mediaSource.endOfStream();
            });
            break;
        case 404:
            throw 'File Not Found';
        default:
            throw 'Failed to fetch the file';
      }
    } catch (e) {
      console.error(e);
    }
  };
  xhr.send();
});

Step 8. Truncate the media file

The SourceBuffer object has two properties called appendWindowStart and appendWindowEnd representing the start and end time of the media data you want to filter. The highlighted code below filters the first four seconds of the MP3.

mediaSource.addEventListener('sourceopen', function() {
  var sourceBuffer = this.addSourceBuffer(mime);
  sourceBuffer.appendWindowEnd = 4.0;
  ...
});

Demo

And that’s all, our audio sample is streamed right from the web page. For the source code, have a look at our Github repo and for the final result, check out the demo page.

Browser support

As of writing this post, the MediaSource API is officially supported in all major browsers. But the testing shows that the implementation is buggy in Firefox, and Webkit browsers still have troubles with the appendWindowStart property.

As the MediaSource API is still in its experimental stage, access to higher editing functions may be limited but the basic streaming feature is something you can make use of right away.

FacebookTwitterInstagramPinterestLinkedInGoogle+YoutubeRedditDribbbleBehanceGithubCodePenEmail