topical media & game development
mobile-query-three-plugins-webrtcio-vendor-webrtc.io-client-webrtc.io.js / js
//CLIENT
// Fallbacks for vendor-specific variables until the spec is finalized.
var PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection;
var URL = window.URL || window.webkitURL || window.msURL || window.oURL;
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
(function() {
var rtc;
if ('undefined' === typeof module) {
rtc = this.rtc = {};
} else {
rtc = module.exports = {};
}
// Holds a connection to the server.
rtc._socket = null;
// Holds identity for the client
rtc._me = null;
// Holds callbacks for certain events.
rtc._events = {};
rtc.on = function(eventName, callback) {
rtc._events[eventName] = rtc._events[eventName] || [];
rtc._events[eventName].push(callback);
};
rtc.fire = function(eventName, _) {
var events = rtc._events[eventName];
var args = Array.prototype.slice.call(arguments, 1);
if (!events) {
return;
}
for (var i = 0, len = events.length; i < len; i++) {
events[i].apply(null, args);
}
};
// Holds the STUN/ICE server to use for PeerConnections.
rtc.SERVER = {iceServers:[{url:"stun:stun.l.google.com:19302"}]};
// Reference to the lone PeerConnection instance.
rtc.peerConnections = {};
// Array of known peer socket ids
rtc.connections = [];
// Stream-related variables.
rtc.streams = [];
rtc.numStreams = 0;
rtc.initializedStreams = 0;
// Reference to the data channels
rtc.dataChannels = {};
// PeerConnection datachannel configuration
rtc.dataChannelConfig = {optional: [ {RtpDataChannels: true} ] };
// check whether data channel is supported.
rtc.checkDataChannelSupport = function() {
try {
// raises exception if createDataChannel is not supported
var pc = new PeerConnection(rtc.SERVER, rtc.dataChannelConfig);
channel = pc.createDataChannel('supportCheck', {reliable: false});
channel.close();
return true;
} catch(e) {
return false;
}
};
rtc.dataChannelSupport = rtc.checkDataChannelSupport();
Connects to the websocket server.
rtc.connect = function(server, room) {
room = room || ""; // by default, join a room called the blank string
rtc._socket = new WebSocket(server);
rtc._socket.onopen = function() {
rtc._socket.send(JSON.stringify({
"eventName": "join_room",
"data":{
"room": room
}
}));
rtc._socket.onmessage = function(msg) {
var json = JSON.parse(msg.data);
rtc.fire(json.eventName, json.data);
};
rtc._socket.onerror = function(err) {
console.error('onerror');
console.error(err);
};
rtc._socket.onclose = function(data) {
rtc.fire('disconnect stream', rtc._socket.id);
delete rtc.peerConnections[rtc._socket.id];
};
rtc.on('get_peers', function(data) {
rtc.connections = data.connections;
rtc._me = data.you;
// fire connections event and pass peers
rtc.fire('connections', rtc.connections);
});
rtc.on('receive_ice_candidate', function(data) {
var candidate = new RTCIceCandidate(data);
rtc.peerConnections[data.socketId].addIceCandidate(candidate);
rtc.fire('receive ice candidate', candidate);
});
rtc.on('new_peer_connected', function(data) {
rtc.connections.push(data.socketId);
var pc = rtc.createPeerConnection(data.socketId);
for (var i = 0; i < rtc.streams.length; i++) {
var stream = rtc.streams[i];
pc.addStream(stream);
}
});
rtc.on('remove_peer_connected', function(data) {
rtc.fire('disconnect stream', data.socketId);
delete rtc.peerConnections[data.socketId];
});
rtc.on('receive_offer', function(data) {
rtc.receiveOffer(data.socketId, data.sdp);
rtc.fire('receive offer', data);
});
rtc.on('receive_answer', function(data) {
rtc.receiveAnswer(data.socketId, data.sdp);
rtc.fire('receive answer', data);
});
rtc.fire('connect');
};
};
rtc.sendOffers = function() {
for (var i = 0, len = rtc.connections.length; i < len; i++) {
var socketId = rtc.connections[i];
rtc.sendOffer(socketId);
}
};
rtc.onClose = function(data) {
rtc.on('close_stream', function() {
rtc.fire('close_stream', data);
});
};
rtc.createPeerConnections = function() {
for (var i = 0; i < rtc.connections.length; i++) {
rtc.createPeerConnection(rtc.connections[i]);
}
};
rtc.createPeerConnection = function(id) {
var config;
if (rtc.dataChannelSupport)
config = rtc.dataChannelConfig;
var pc = rtc.peerConnections[id] = new PeerConnection(rtc.SERVER, config);
pc.onicecandidate = function(event) {
if (event.candidate) {
rtc._socket.send(JSON.stringify({
"eventName": "send_ice_candidate",
"data": {
"label": event.candidate.label,
"candidate": event.candidate.candidate,
"socketId": id
}
}));
}
rtc.fire('ice candidate', event.candidate);
};
pc.onopen = function() {
// TODO: Finalize this API
rtc.fire('peer connection opened');
};
pc.onaddstream = function(event) {
// TODO: Finalize this API
rtc.fire('add remote stream', event.stream, id);
};
if (rtc.dataChannelSupport) {
pc.ondatachannel = function (evt) {
console.log('data channel connecting ' + id);
rtc.addDataChannel(id, evt.channel);
};
}
return pc;
};
rtc.sendOffer = function(socketId) {
var pc = rtc.peerConnections[socketId];
pc.createOffer( function(session_description) {
pc.setLocalDescription(session_description);
rtc._socket.send(JSON.stringify({
"eventName": "send_offer",
"data":{
"socketId": socketId,
"sdp": session_description
}
}));
});
};
rtc.receiveOffer = function(socketId, sdp) {
var pc = rtc.peerConnections[socketId];
pc.setRemoteDescription(new RTCSessionDescription(sdp));
rtc.sendAnswer(socketId);
};
rtc.sendAnswer = function(socketId) {
var pc = rtc.peerConnections[socketId];
pc.createAnswer( function(session_description) {
pc.setLocalDescription(session_description);
rtc._socket.send(JSON.stringify({
"eventName": "send_answer",
"data":{
"socketId": socketId,
"sdp": session_description
}
}));
var offer = pc.remoteDescription;
});
};
rtc.receiveAnswer = function(socketId, sdp) {
var pc = rtc.peerConnections[socketId];
pc.setRemoteDescription(new RTCSessionDescription(sdp));
};
rtc.createStream = function(opt, onSuccess, onFail) {
var options;
onSuccess = onSuccess ||
function() {};
onFail = onFail ||
function() {};
options = {
video: !!opt.video,
audio: !!opt.audio
};
if (getUserMedia) {
rtc.numStreams++;
getUserMedia.call(navigator, options, function(stream) {
rtc.streams.push(stream);
rtc.initializedStreams++;
onSuccess(stream);
if (rtc.initializedStreams === rtc.numStreams) {
rtc.fire('ready');
}
}, function() {
alert("Could not connect stream.");
onFail();
});
} else {
alert('webRTC is not yet supported in this browser.');
}
};
rtc.addStreams = function() {
for (var i = 0; i < rtc.streams.length; i++) {
var stream = rtc.streams[i];
for (var connection in rtc.peerConnections) {
rtc.peerConnections[connection].addStream(stream);
}
}
};
rtc.attachStream = function(stream, domId) {
document.getElementById(domId).src = URL.createObjectURL(stream);
};
rtc.createDataChannel = function(pcOrId, label) {
if (!rtc.dataChannelSupport) {
alert('webRTC data channel is not yet supported in this browser,' +
' or you must turn on experimental flags');
return;
}
if (typeof(pcOrId) === 'string') {
id = pcOrId;
pc = rtc.peerConnections[pcOrId];
} else {
pc = pcOrId;
id = undefined;
for (var key in rtc.peerConnections) {
if (rtc.peerConnections[key] === pc)
id = key;
}
}
if (!id)
throw new Error ('attempt to createDataChannel with unknown id');
if (!pc || !(pc instanceof PeerConnection))
throw new Error ('attempt to createDataChannel without peerConnection');
// need a label
label = label || 'fileTransfer' || String(id);
// chrome only supports reliable false atm.
options = {reliable: false};
try {
console.log('createDataChannel ' + id);
channel = pc.createDataChannel(label, options);
} catch (error) {
console.log('seems that DataChannel is NOT actually supported!');
throw error;
}
return rtc.addDataChannel(id, channel);
};
rtc.addDataChannel = function(id, channel) {
channel.onopen = function() {
console.log('data stream open ' + id);
rtc.fire('data stream open', channel);
};
channel.onclose = function(event) {
delete rtc.dataChannels[id];
console.log('data stream close ' + id);
rtc.fire('data stream close', channel);
};
channel.onmessage = function(message) {
console.log('data stream message ' + id);
console.log(message);
rtc.fire('data stream data', channel, message.data);
};
channel.onerror = function(err) {
console.log('data stream error ' + id + ': ' + err);
rtc.fire('data stream error', channel, err);
};
// track dataChannel
rtc.dataChannels[id] = channel;
return channel;
};
rtc.addDataChannels = function() {
if (!rtc.dataChannelSupport)
return;
for (var connection in rtc.peerConnections)
rtc.createDataChannel(connection);
};
rtc.on('ready', function() {
rtc.createPeerConnections();
rtc.addStreams();
rtc.addDataChannels();
rtc.sendOffers();
});
}).call(this);
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.