summaryrefslogtreecommitdiff
path: root/public/javascripts/rtc-io.js
diff options
context:
space:
mode:
authoryo mama <pepper@scannerjammer.com>2015-03-20 17:33:43 -0700
committeryo mama <pepper@scannerjammer.com>2015-03-20 17:33:43 -0700
commit2afbcf4e7d000d99fdbc582d7113684ee00e80cc (patch)
tree6ba3313b78625735fb295e3d375e59054c31e558 /public/javascripts/rtc-io.js
first
Diffstat (limited to 'public/javascripts/rtc-io.js')
-rw-r--r--public/javascripts/rtc-io.js411
1 files changed, 411 insertions, 0 deletions
diff --git a/public/javascripts/rtc-io.js b/public/javascripts/rtc-io.js
new file mode 100644
index 0000000..7cfdb2e
--- /dev/null
+++ b/public/javascripts/rtc-io.js
@@ -0,0 +1,411 @@
+// Fallbacks for vendor-specific variables until the spec is finalized.
+var URL = window.URL || window.webkitURL || window.msURL || window.oURL;
+var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+var is_chrome = window.chrome;
+if (is_chrome) {
+ var PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection;
+}else{
+ var PeerConnection = mozRTCPeerConnection;
+}
+if (is_chrome){
+ var SessionDescription = RTCSessionDescription;
+}else {
+ var SessionDescription = mozRTCSessionDescription;
+}
+
+(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.
+ if (is_chrome) {
+ rtc.SERVER = {iceServers:[{url:"stun:stun.l.google.com:19302"}]};
+ }else{
+ rtc.SERVER = {iceServers:[{url:"stun:23.2l.150.121"}]};
+ }
+
+ // Reference to the lone PeerConnection instance.
+ rtc.peerConnections = {};
+
+ // Array of known peer socket ids
+ rtc.connections = [];
+ // Array that says if this connect is OK to send data over, otherwise an error will likely occur
+ rtc.connection_ok_to_send =[];
+ //Array of usernames, indexed by socket id
+ rtc.usernames = [];
+ // 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);
+ var 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, username) {
+ room = room || ""; // by default, join a room called the blank string
+ rtc._socket = new WebSocket(server);
+ console.log(username);
+ rtc._socket.onopen = function() {
+ rtc._socket.send(JSON.stringify({
+ "eventName": "join_room",
+ "data":{
+ "room" : room,
+ "username": username
+ }
+ }));
+
+ 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', data.socketId, rtc.usernames[data.socketId]);
+ delete rtc.usernames[data.socketId];
+ delete rtc.peerConnections[rtc._socket.id];
+ };
+
+ rtc.on('get_peers', function(data) {
+ console.log("get_peers");
+ rtc.connections = data.connections;
+ rtc.usernames = data.usernames;
+ rtc._me = data.you;
+ // fire connections event and pass peers
+ rtc.fire('connections', rtc.connections);
+ });
+
+ rtc.on('receive_ice_candidate', function(data) {
+ if (is_chrome) {
+ var candidate = new RTCIceCandidate(data);
+ }else{
+ var candidate = new mozRTCIceCandidate(data);
+ }
+ rtc.peerConnections[data.socketId].addIceCandidate(candidate);
+ rtc.fire('receive ice candidate', candidate);
+ });
+
+ rtc.on('new_peer_connected', function(data) {
+ //add username
+ console.log(data.username + " has joined the room.");
+ rtc.usernames[data.socketId] = data.username;
+ 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.connection_ok_to_send[data.socketId] = false;
+ rtc.fire('disconnect stream', data.socketId, rtc.usernames[data.socketId]);
+ delete rtc.usernames[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) {
+ console.log("creating peer conn");
+ 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;
+ };
+
+ /* SUPER HACK! (for chrome)
+ * This is a wicked impressive hack, lovingly taken from ShareFest
+ * This function should retain the following copyright per the apache 2.0 license:
+ */
+ rtc.transformOutgoingSdp = function (sdp) {
+ var splitted = sdp.split("b=AS:30");
+ var newSDP = splitted[0] + "b=AS:1638400" + splitted[1];
+ return newSDP;
+ };
+
+ rtc.sendOffer = function(socketId) {
+ var pc = rtc.peerConnections[socketId];
+ pc.createOffer( function(session_description) {
+ if(is_chrome){
+ session_description.sdp = rtc.transformOutgoingSdp(session_description.sdp);
+ }
+ 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 SessionDescription(sdp));
+ rtc.sendAnswer(socketId);
+ };
+
+ rtc.sendAnswer = function(socketId) {
+ var pc = rtc.peerConnections[socketId];
+ pc.createAnswer( function(session_description) {
+ if(is_chrome){
+ session_description.sdp = rtc.transformOutgoingSdp(session_description.sdp);
+ }
+ 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 SessionDescription(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("Webcam device is not found. Please check your camera.");
+ 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) {
+ $('#' + domId).attr('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;
+ }
+
+ id = pcOrId;
+ pc = rtc.peerConnections[pcOrId];
+
+ if (!id)
+ throw new Error ('attempt to createDataChannel with unknown id');
+ // need a label
+ label = label || 'fileTransfer' || String(id);
+ if (is_chrome) {
+ options = {reliable: false}; // chrome only supports reliable false :(
+ }else{
+ options = {reliable: true}; // but Firefox supports true!
+ }
+
+ 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.connection_ok_to_send[id] = true;
+ rtc.fire('data stream open', id, rtc.usernames[id]);
+ };
+
+ 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, id, rtc.usernames[id]);
+ };
+
+ 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);