From 2afbcf4e7d000d99fdbc582d7113684ee00e80cc Mon Sep 17 00:00:00 2001 From: yo mama Date: Fri, 20 Mar 2015 17:33:43 -0700 Subject: first --- public/javascripts/rtc-io.js | 411 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 public/javascripts/rtc-io.js (limited to 'public/javascripts/rtc-io.js') 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); -- cgit v1.2.3-70-g09d2