io.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. (function() {
  2. var rtc = this.rtc = {};
  3. // Fallbacks for vendor-specific variables until the spec is finalized.
  4. var PeerConnection = window.PeerConnection || window.webkitPeerConnection00;
  5. var URL = window.URL || window.webkitURL || window.msURL || window.oURL;
  6. var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
  7. navigator.mozGetUserMedia || navigator.msGetUserMedia;
  8. // Holds a connection to the server.
  9. rtc._socket = null;
  10. // Holds callbacks for certain events.
  11. rtc._events = {};
  12. // Holds the STUN server to use for PeerConnections.
  13. rtc.SERVER = "STUN stun.l.google.com:19302";
  14. // Reference to the lone PeerConnection instance.
  15. rtc.peerConnections = {};
  16. // Array of known peer socket ids
  17. rtc.connections = [];
  18. // Stream-related variables.
  19. rtc.streams = [];
  20. rtc.numStreams = 0;
  21. rtc.initializedStreams = 0;
  22. /**
  23. * Connects to the socket.io server.
  24. */
  25. rtc.connect = function(server, room) {
  26. room = room || ''; // by default, join a room called the blank string
  27. rtc._socket = io.connect(server);
  28. rtc._socket.on('connect', function() {
  29. rtc._socket.emit('join room', room);
  30. rtc.fire('connect');
  31. });
  32. // TODO: Fix possible race condition if get peers is not emitted
  33. // before the "ready" event is fired.
  34. rtc._socket.on('get peers', function(data) {
  35. var peers = data.connections;
  36. rtc.connections = peers;
  37. // fire connections event and pass peers
  38. rtc.fire('connections', peers);
  39. });
  40. rtc._socket.on('receive ice candidate', function(data) {
  41. var candidate = new IceCandidate(data.label, data.candidate);
  42. rtc.peerConnections[data.socketId].processIceMessage(candidate);
  43. rtc.fire('receive ice candidate', candidate);
  44. });
  45. rtc._socket.on('new peer connected', function(data) {
  46. var pc = rtc.createPeerConnection(data.socketId);
  47. for (var i = 0; i < rtc.streams.length; i++) {
  48. var stream = rtc.streams[i];
  49. pc.addStream(stream);
  50. }
  51. });
  52. rtc._socket.on('remove peer connected', function(data) {
  53. console.log(data);
  54. //the actual onremovestream function is not yet supported. Here is a temporary workaround
  55. rtc.fire('disconnect stream', data.socketId);
  56. onClose(data.socketId);
  57. //rtc.peerConnections[data.socketId].close();
  58. delete rtc.peerConnections[data.socketId];
  59. });
  60. rtc._socket.on('receive offer', function(data) {
  61. rtc.receiveOffer(data.socketId, data.sdp);
  62. rtc.fire('receive offer', data);
  63. });
  64. rtc._socket.on('receive answer', function(data) {
  65. rtc.receiveAnswer(data.socketId, data.sdp);
  66. rtc.fire('receive answer', data);
  67. });
  68. };
  69. rtc.sendOffers = function() {
  70. for (var i = 0, len = rtc.connections.length; i < len; i++) {
  71. var socketId = rtc.connections[i];
  72. rtc.sendOffer(socketId);
  73. }
  74. }
  75. rtc.onClose = function(data) {
  76. rtc._socket.on('close stream', function() {
  77. rtc.fire('close stream', data);
  78. });
  79. }
  80. rtc.on = function(eventName, callback) {
  81. rtc._events[eventName] = rtc._events[eventName] || [];
  82. rtc._events[eventName].push(callback);
  83. };
  84. rtc.fire = function(eventName, _) {
  85. var events = rtc._events[eventName];
  86. var args = Array.prototype.slice.call(arguments, 1);
  87. if (!events) {
  88. return;
  89. }
  90. for (var i = 0, len = events.length; i < len; i++) {
  91. events[i].apply(null, args);
  92. }
  93. };
  94. rtc.createPeerConnections = function() {
  95. for (var i = 0; i < rtc.connections.length; i++) {
  96. rtc.createPeerConnection(rtc.connections[i]);
  97. }
  98. };
  99. rtc.createPeerConnection = function(id) {
  100. var pc = rtc.peerConnections[id] = new PeerConnection(rtc.SERVER, function(candidate, moreToFollow) {
  101. if (candidate) {
  102. rtc._socket.emit('receive ice candidate', {
  103. label: candidate.label,
  104. candidate: candidate.toSdp(),
  105. socketId: id
  106. });
  107. }
  108. rtc.fire('ice candidate', candidate, moreToFollow);
  109. });
  110. pc.onopen = function() {
  111. // TODO: Finalize this API
  112. rtc.fire('peer connection opened');
  113. };
  114. pc.onaddstream = function(event) {
  115. // TODO: Finalize this API
  116. rtc.fire('add remote stream', event.stream, id);
  117. };
  118. return pc;
  119. };
  120. rtc.sendOffer = function(socketId) {
  121. var pc = rtc.peerConnections[socketId];
  122. // TODO: Abstract away video: true, audio: true for offers
  123. var offer = pc.createOffer({ video: true, audio: true });
  124. pc.setLocalDescription(pc.SDP_OFFER, offer);
  125. rtc._socket.emit('send offer', { socketId: socketId, sdp: offer.toSdp() });
  126. pc.startIce();
  127. };
  128. rtc.receiveOffer = function(socketId, sdp) {
  129. var pc = rtc.peerConnections[socketId];
  130. pc.setRemoteDescription(pc.SDP_OFFER, new SessionDescription(sdp));
  131. rtc.sendAnswer(socketId);
  132. };
  133. rtc.sendAnswer = function(socketId) {
  134. var pc = rtc.peerConnections[socketId];
  135. var offer = pc.remoteDescription;
  136. // TODO: Abstract away video: true, audio: true for answers
  137. var answer = pc.createAnswer(offer.toSdp(), {video: true, audio: true});
  138. pc.setLocalDescription(pc.SDP_ANSWER, answer);
  139. rtc._socket.emit('send answer', { socketId: socketId, sdp: answer.toSdp() });
  140. pc.startIce();
  141. };
  142. rtc.receiveAnswer = function(socketId, sdp) {
  143. var pc = rtc.peerConnections[socketId];
  144. pc.setRemoteDescription(pc.SDP_ANSWER, new SessionDescription(sdp));
  145. };
  146. rtc.createStream = function(domId, onSuccess, onFail) {
  147. var el = document.getElementById(domId);
  148. var options;
  149. onSuccess = onSuccess || function() {};
  150. onFail = onFail || function() {};
  151. if (el.tagName.toLowerCase() === "audio") {
  152. options = { audio: true };
  153. } else {
  154. options = { video: true, audio: true };
  155. }
  156. if (getUserMedia) {
  157. rtc.numStreams++;
  158. getUserMedia.call(navigator, options, function(stream) {
  159. el.src = URL.createObjectURL(stream);
  160. rtc.streams.push(stream);
  161. rtc.initializedStreams++;
  162. onSuccess(stream);
  163. if (rtc.initializedStreams === rtc.numStreams) {
  164. rtc.fire('ready');
  165. }
  166. }, function() {
  167. alert("Could not connect stream.");
  168. onFail();
  169. });
  170. } else {
  171. alert('webRTC is not yet supported in this browser.');
  172. }
  173. }
  174. rtc.addStreams = function() {
  175. for (var i = 0; i < rtc.streams.length; i++) {
  176. var stream = rtc.streams[i];
  177. for (var connection in rtc.peerConnections) {
  178. rtc.peerConnections[connection].addStream(stream);
  179. }
  180. }
  181. };
  182. rtc.attachStream = function(stream, domId) {
  183. document.getElementById(domId).src = URL.createObjectURL(stream);
  184. };
  185. rtc.on('ready', function() {
  186. rtc.createPeerConnections();
  187. rtc.addStreams();
  188. rtc.sendOffers();
  189. });
  190. }).call(this);