Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XMLHttpRequest and http streaming

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.

like image 984
astooooooo Avatar asked Oct 09 '15 11:10

astooooooo


1 Answers

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.

like image 126
astooooooo Avatar answered Oct 04 '22 10:10

astooooooo