7

My goal is to read an HTTP MP3 audio stream from the browser and have access to the raw audio data.

  • HTML5 < audio > lets me easily play the stream, but, as far as I know, does not grant access to the raw audio data. It just plays it.

  • JS XMLHTTPRequest can download files through HTTP and process the raw audio data. It seems to be a good candidate, but it suffers from a limitation: it does not grant access to the binary data until the download is finished (readystate = 4). In my case, the stream is unlimited, so the readystate stays permanently at 3 and the XHR response is null (this behavior is detailed in the mozilla documentation). Note that the cross-origin policy of the server I am connecting to is Access-Control-Allow-Origin: *

Code sample that works for local regular files, but not for streams. I get a null pointer exception at request.response.length

request = new XMLHttpRequest();
//request.open('GET', 'test.mp3', true);
request.open('GET', 'http://domain.com/stream.mp3', true);
request.responseType = 'arraybuffer';
request.onload = function() {
  console.log("request onload");
  var audioData = request.response;
  audioCtx.decodeAudioData(audioData, 
    function(buffer) { myBuffer = buffer; source.buffer = myBuffer; }, 
    function(e){"Error with decoding audio data" + e.err}
  );
}
request.onreadystatechange = function() {
    console.log("ready state = " + request.readyState);
    console.log(request.response.length);
}
request.send();

Does anybody know alternatives or workarounds to those options, so that the raw binary packets can be read while downloading the stream?

Note that I don't have control on the server. It's an icecast http stream. Also, on the browser side, I'd like to avoid using Flash. Thank you

Edit: to clarify possible cross-origin questions, the JS is run on a page hosted in a localhost server.

astooooooo
  • 362
  • 2
  • 13
  • To be clear, you want the raw decoded PCM audio data, not the bitstream from the server, yes? If so, the Web Audio API is what you need. – Brad Oct 09 '15 at 14:44
  • @Brad indeed I use the Web Audio API (as a hint see for example the decodeAudioData method above, in my partial code). I just had trouble getting a buffer from the stream and putting it in the API. I think I found a solution, see my solution below. – astooooooo Oct 09 '15 at 15:15

1 Answers1

10

The following workaround worked:

As stated in MDN https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data, it is possible to override the MIME type of http request, setting it to custom, and call responseText.

function load_binary_resource(url) {
  var req = new XMLHttpRequest();
  req.open('GET', url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType('text\/plain; charset=x-user-defined');
  req.send(null);
  if (req.status != 200) return '';
  return req.responseText;
} 

The point is that req.responseText does not suffer from the same limitation of req.response. It is not null in the state readystate=3. Then, the binary responseText is accessed with

var filestream = load_binary_resource(url);
var abyte = filestream.charCodeAt(x) & 0xff; // throw away high-order byte (f7)

A significant drawback is that req.responseText keeps growing as the stream is downloaded. The request should be reset from time to time to avoid excessive RAM consumption.

astooooooo
  • 362
  • 2
  • 13