nicozica
nicozica

Reputation: 423

Ionic 3: playing an Icecast/Shoutcast audio stream

I'm working on an internet radio app using the Ionic 3 framework. I came up with this simple code that uses the HTML5 audio element. Many people suggest that this approach is better than using the @ionic-native/streaming-media plugin.

The following is my implementation:

HTML

<audio id="audioStream" src="http://icecastserverIP:8000/icecastchannel" autoplay></audio>

<a href="#" id="player">
  <div class="btn-play" id="con-btn-play">
    <img src="assets/img/btn-play.png" alt="Play">
  </div>
</a>

JS

$(document).ready(function(){

    var audio = $('#audioStream')[0]


    // Preloader animation

    audio.addEventListener('waiting', function () {
        $('#con-btn-play').html('<img src="assets/img/preloader.gif">');
    }, false);
    audio.addEventListener('playing', function () {
        $('#con-btn-play').html('<img src="assets/img/btn-pause.png">');
    }, false);


    // Play button behaviour

    $('#player').click(function(){
        if (audio.paused){
            audio.play();
            $('#con-btn-play').html('<img src="assets/img/btn-pause.png" alt="Pause">');
        }else {
            audio.pause();
            $('#con-btn-play').html('<img src="assets/imgs/btn-play.png" alt="Play">');
        }
    });

});

The stream plays fine in Android and iOS. However, at the slightest connection drop the audio will stop and won't reconnect. Not to mention that if I press pause, the stream will continue using data in the background.

My question is: is there a better way to handle Icecast streams? A third-party plugin that you would recommend for a better buffering management and playing experience?

Upvotes: 2

Views: 1454

Answers (2)

miknik
miknik

Reputation: 5941

First thing I would recommend is in your Icecast server settings increase the values of <queue-size> and <burst-size>.

By default the server will only send clients 64kb of the audio stream as a buffer when they connect, which for a 320kbps mp3 stream is less than 2 seconds.

Setting the preload tag of your <audio> element to none or metadata should prevent endless data streaming on paused, alternatively you can set the playbackRate parameter to 0.0 or change the src to a null value and then call .load() on the audio element to disconnect clients from the stream on pause.

It's possible to have your Icecast server encode the metadata directly into the audio stream by adding an extra request header when you connect. The advantage being it's then simple to sync the metadata updates with the audio.

I wrote a basic service worker script to handle the process, It adds the necessary header to the request and then parses the returned stream to extract the metadata and create a readable stream containing just the audio data which is passed to your audio element.

The code is on Github here if you want to try it out.

Upvotes: 2

Brad
Brad

Reputation: 163234

However, at the slightest connection drop the audio will stop and won't reconnect.

You can reconnect with your own code by handling the error event on the Audio element. https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/onerror

Not to mention that if I press pause, the stream will continue using data in the background.

Yes, you'll want to make your own UI to control this element. When someone pauses, you'll want to call .stop() instead. By default, the audio element is assuming a regular audio file that ends at some point.

A third-party plugin...

No plugin needed.

...a better buffering management and playing experience?

With the two suggestions I've made in this answer, you can have a useful experience for your users. However, there are two other issues you haven't touched on yet which you may run into.

The first is that regular ICY metadata isn't requested, nor decoded by the browser.

The second issue is that since the browser is treating your stream like a regular audio file, it's also buffering all of that audio data into memory. This isn't very useful for a live stream where you will never go back and play previously played audio. (This is rarely an issue you run into, since audio data requires relatively small amount of memory, but the problem does exist.)

Both of these are solved by using MediaSource Extensions. With MSE, you have direct control over retrieving the data, demuxing the metadata, and pushing data to a buffer to be played back. Since your code is in control, you can stream indefinitely without the memory leak.

Upvotes: 1

Related Questions