Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MediaElementAudioSource outputs zeroes due to CORS access restrictions

<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?

like image 398
user1531557 Avatar asked Jun 26 '15 23:06

user1531557


4 Answers

In my response I will assume the following setup:

  • Your stream URL is http://stream.radio.com:8000/mount (or http://stream.radio.com:8000/;stream/1 for Shoutcast)
  • Your paget URL where you place your HTML/JS code URL is http://radio.com/player

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.
like image 138
Alex Paramonov Avatar answered Nov 14 '22 23:11

Alex Paramonov


That is an HTTP header. You would configure your webserver or webapp to send this header. Perhaps in htaccess or PHP. remove the below line

    <header name = "Access-Control-Allow-Origin" value = "*" />
like image 34
Bill Pope Avatar answered Nov 14 '22 21:11

Bill Pope


SHOUTcast servers do not support CORS. There is nothing you can do to change this if you are going to continue to use SHOUTcast.

like image 40
Brad Avatar answered Nov 14 '22 22:11

Brad


First of all, MediaElementAudioSource doesn't have a property named "crossOrigin".

I just find this problem, and mad with the Message:MediaElementAudioSource outputs zeroes due to CORS access restrictions for. But it's just a message, i can still hear the audio. And I googled lots of this, think this link will be helpful:http://www.codingforums.com/javascript-programming/342454-audio-api-js.html

The createMediaElementSource method should create an object that uses the MediaElementAudioSourceNode interface. Such objects are subject to Cross-Origin Resource Sharing (CORS) restrictions based on the latest draft of the Web Audio API spec. (Note that this restriction doesn't appear to be in the outdated W3C version of the spec.) According to the spec, silence should be played when CORS restrictions block access to a resource, which would explain the "outputs zeroes" message; presumably, zero is equivalent to no sound.

To lift the restriction, the owner of the page at http://morebassradio.no-ip.org:8214/;stream/1 would need to configure their server to output an Access-Control-Allow-Origin header with either a list of domains (including yours) or the * value to lift it for all domains. Given that this stream appears to already be unrestricted, public-facing content, maybe you can convince the owners to output that header. You can test whether the header is being sent by pressing Ctrl+Shift+Q in Firefox to open the Network panel, loading the stream through the address bar, and then inspecting the headers associated with that HTTP request in the Network panel.

Note that they can't use a meta element here since the audio stream is, obviously, not an HTML document; that technique only works for HTML and XHTML documents.

(While you're messing with Firefox panels, you may want to make sure Security errors and warnings are enabled (by clicking the Security button or its arrow) in the Console panel (Ctrl+Shift+K). I'm not sure if there's a corresponding CORS message in Firefox like in Chrome, but there might be. I wasted a bunch of time wondering why a page wasn't working one day while troubleshooting a similar technology, Content Security Policy (CSP), only to find that I had the relevant Firefox messages hidden.)

You shouldn't need to mess with the crossorigin property/attribute unless you set crossorigin = "use-credentials" (JavaScript) or crossorigin="use-credentials" (HTML) somewhere, but you probably didn't do that because that part of the HTML spec isn't finalized yet, and it would almost certainly cause your content to "break" after doing so since credentials would be required at that point.

I'm not familiar with the Web Audio API, so I wasn't able to figure out how to output a MediaElementAudioSourceNode and trigger an error message for my own troubleshooting. If I use createMediaElementSource with an HTMLMediaElement (HTMLAudioElement), the result doesn't seem to be a MediaElementAudioSourceNode based on testing using the instanceof operator even though the spec says it should be if I'm reading it right.

Then in my situation, i get the HTTP response Header:

HTTP/1.1 206 Partial Content
Date: Thu, 02 Jun 2016 06:50:43 GMT
Content-Type: audio/mpeg
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Content-Disposition: inline; filename="653ab5685893b4bf.mp3"
Content-Transfer-Encoding: binary
Last-Modified: Mon, 16 May 2016 02:00:05 GMT
Server: nginx
Cache-Control: public, max-age=31536000
ETag: "FpGQqtcf_s2Ce8W_4Mv6ZqSVkVTK"
X-Log: mc.g;IO:2/304
X-Reqid: 71cAAFQgUBiJMVQU
X-Qiniu-Zone: 0
Content-Range: bytes 0-1219327/1219328
Content-Length: 1219328
Age: 1
X-Via: 1.1 xinxiazai211:88 (Cdn Cache Server V2.0), 1.1 hn13:8 (Cdn Cache Server V2.0)
Connection: keep-alive

Note that "Access-Control-Allow-Origin: *", i think this just the right thing, but i still get the message. Hope it help you.

like image 36
Sincoyw Avatar answered Nov 14 '22 22:11

Sincoyw