MediaElementAudioSource outputs zeroes due to CORS access restrictions

user1531557 picture user1531557 · Jun 27, 2015 · Viewed 10.2k times · Source
<script>
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.src = 'http://subdomain.domain.org:port/;stream/1';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;
audio.crossorigin="anonymous";
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
    document.getElementById('audio_box').appendChild(audio);
    context = new (window.AudioContext || window.webkitAudioContext)(); // AudioContext object instance // AudioContext object instance
    analyser = context.createAnalyser(); // AnalyserNode method
    canvas = document.getElementById('analyser_render');
    ctx = canvas.getContext('2d');
    // Re-route audio playback into the processing graph of the AudioContext
    source = context.createMediaElementSource(audio);
 source.crossOrigin = 'anonymous'   
    source.connect(analyser);
    analyser.connect(context.destination);
    frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper(){
    (requestAnimationFrame || webkitRequestAnimationFrame)(frameLooper);
    fbc_array = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(fbc_array);//get frequency

    ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
    ctx.fillStyle = '#00CCFF'; // Color of the bars
    bars = 100;
    for (var i = 0; i < bars; i++) {
        bar_x = i * 3;
        bar_width = 2;
        bar_height = -(fbc_array[i] / 2);
        //  fillRect( x, y, width, height ) // Explanation of the parameters below
        ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
    }
}
</script>

Audio API gives MediaElementAudioSource outputs zeroes due to CORS access restrictions because I'm trying to play a SHOUTcast URL. I don't know what to do; I have tried all solutions on the internet but nothing worked. Any help will be appreciated.

The URL works perfectly with audio element so its not about the URL; I have even tried something like http://subdomain.domain.org:port/file.mp3. And I found on the internet people using Icecast which is .ogg have same problem. How to fix this?

Answer

Alex Paramonov picture Alex Paramonov · Jun 14, 2016

In my response I will assume the following setup:

To get this working you need:

  1. Set the "Access-Control-Allow-Origin" header of your stream to your domain or *
  2. In javascript, set audio tag crossOrigin property to "anonymous" audio.crossOrigin="anonymous";
  3. Another option it to move you stream URL to the original domain using reverse proxy.

With Icecast you cat set the "Access-Control-Allow-Origin" header using configuration file, just add the following lines to your icecast.xml, I usually add them right after the opening <icecast> tag:

<http-headers>
        <header name="Access-Control-Allow-Origin" value="*" />
        <header name="Access-Control-Allow-Headers" value="Origin, Accept, X-Requested-With, Content-Type, If-Modified-Since" />
        <header name="Access-Control-Allow-Methods" value="GET, OPTIONS, HEAD" />
</http-headers>

Don't forget to restart Icecast after these changes. When your Icecast will be back online you can check the headers with this command:

lynx -head -dump http://stream.radio.com:8000/mount 

Response should look something like this:

Server: Icecast 2.4.2
....
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, If
-Modified-Since
Access-Control-Allow-Methods: GET, OPTIONS, HEAD

As you can see, "Access-Control-Allow-Origin: *" header is present.

Shoutcast

Unfortunately, Shoutcast does not allow you to set HTTP headers (.htaccess is not an option too), but we can create a reverse proxy in web server configuration, this will allow you to host the stream from the main domain - radio.com. I will provide proxy configurations for Nginx and Apache.

Nginx

You can add additional headers with "proxy_set_header", but the basic example is:

server {
    listen   80;
    server_name radio.com;
    ....
    location /stream { 
       proxy_set_header X-Forwarded-For $remote_addr; 
       proxy_pass http://stream.radio.com:8000/mount; 
    }
    ....
}

Apache

Activate Apache proxy modules, and update radio.com virtual host configuration configuration:

<VirtualHost *:80>
   ServerName radio.com
   ....
   ProxyPass /stream http://stream.radio.com:8000/mount
</VirtualHost>

Now you can access your stream using http://radio.com/stream URL and the CORS policy will not apply. This solution also brings some additional perks:

  • you can convert your http Shoutcast/Icecast stream to https, so the browsers will not complain about accessing unsecure content when you will embed your stream to the page hosted with https. (Icecast supports SSL configuration itself)
  • 8000 port will be replaced with port 80, that will allow listeners with 8000 port behind firewall to access your stream.