Reputation: 10287
Is there a way to send images, videos, and audio to an AirPlay server using JavaScript in the browser?
Upvotes: 5
Views: 4321
Reputation: 1091
This Works for me
var xhr = new XMLHttpRequest(),
xhr_stop = new XMLHttpRequest(),
hostname = "apple-tv.local",
port =":7000",
position = "0";
xhr_stop.open("POST", "http://" + hostname + port + "/stop", true, "AirPlay", null);
xhr_stop.send(null);
xhr.open("POST", "http://" + hostname + port + "/play", true, "AirPlay", null);
xhr.setRequestHeader("Content-Type", "text/parameters");
xhr.send("Content-Location: " + url + "\nStart-Position: " + position + "\n");
// set timer to prevent playback from aborting
xhr.addEventListener("load", function() {
var timer = setInterval(function() {
var xhr = new XMLHttpRequest(),
// 0 something wrong; 2 ready to play; >2 playing
playback_info_keys_count = 0,
terminate_loop, playback_started;
xhr.open("GET", "http://" + hostname + port + "/playback-info", true, "AirPlay", null);
xhr.addEventListener("load", function() {
playback_info_keys_count = xhr.responseXML.getElementsByTagName("key").length;
console.log("playback: " + playback_started + "; keys: " + playback_info_keys_count)
// if we're getting some actual playback info
if (!playback_started && playback_info_keys_count > 2) {
playback_started = true;
console.log("setting playback_started = true")
terminate_loop = false;
}
// playback terminated
if (terminate_loop && playback_info_keys_count <= 2) {
console.log("stopping loop & setting playback_started = false")
clearInterval(timer);
var xhr_stop = new XMLHttpRequest();
xhr_stop.open("POST", "http://" + hostname + port + "/stop", true, "AirPlay", null);
xhr_stop.send(null);
playback_started = false;
}
// playback stopped, AppleTV is "readyToPlay"
if (playback_started && playback_info_keys_count == 2) {
console.log("sending /stop signal, setting playback_started = false")
var xhr_stop = new XMLHttpRequest();
xhr_stop.open("POST", "http://" + hostname + port + "/stop", true, "AirPlay", null);
xhr_stop.send(null);
playback_started = false;
terminate_loop = true;
}
}, false);
xhr.addEventListener("error", function() {
clearInterval(timer);
}, false);
xhr.send(null);
}, 5000);
}, false);
Upvotes: 1
Reputation: 14881
It is not possible to achieve this in JavaScript. However, you might be able to run it from the browser with an NPAPI plugin (with great pain).
If you can run a local server, there are several node.js modules which make this much easier. The following example will stream any audio file that is posted to a nearby AirPlay device.
You can test it with:
curl -X POST --data-binary @sample.mp3 http://localhost:8080/audio
It assumes that FFmpeg is located in /usr/local/bin/ffmpeg, and that an AirPlay device is available on localhost:5000 (you can try with Airfoil Speakers).
var airtunes = require('airtunes'),
express = require('express'),
app = express(),
device = airtunes.add('localhost'),
spawn = require('child_process').spawn;
app.post('/audio', function(req, res) {
// use ffmpeg to reencode data on the fly
var ffmpeg = spawn('/usr/local/bin/ffmpeg', [
'-i', 'pipe:0', // Read from stdin
'-f', 's16le', // PCM 16bits, little-endian
'-ar', '44100', // Sampling rate
'-ac', 2, // Stereo
'pipe:1' // Output to stdout
]);
// pipe data to AirTunes
ffmpeg.stdout.pipe(airtunes, { end: false });
// detect if ffmpeg was not spawned correctly
ffmpeg.stderr.setEncoding('utf8');
ffmpeg.stderr.on('data', function(data) {
if(/^execvp\(\)/.test(data)) {
console.log('failed to start ' + argv.ffmpeg);
process.exit(1);
}
});
req.pipe(ffmpeg.stdin);
req.on('end', function() {
res.end();
});
});
device.on('status', function(status) {
console.log('status: ' + status);
});
console.log('listening on port 8080');
app.listen(8080);
Upvotes: 3