source.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. function SourcePeer(options) {
  2. // TODO: Update for streams.
  3. // TODO: Allow passing in own ID.
  4. this._config = options.config || { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]};
  5. this._streams = options.streamType || 'd';
  6. this._name = options.name || 'StreamAPI';
  7. // PeerConnections open for this source. Client name => PC.
  8. this._pcs = {};
  9. this._id = null;
  10. // Same for DCs.
  11. this._dcs = {};
  12. this._socket = io.connect('http://localhost');
  13. this.socketInit();
  14. this._handlers = {};
  15. // Firefox
  16. if (browserisms == 'Firefox') {
  17. if (!SourcePeer.usedPorts) {
  18. SourcePeer.usedPorts = [];
  19. }
  20. this.localPort = randomPort();
  21. while (SourcePeer.usedPorts.indexOf(this.localPort) != -1) {
  22. this.localPort = randomPort();
  23. }
  24. this.remotePort = randomPort();
  25. while (this.remotePort == this.localPort ||
  26. SourcePeer.usedPorts.indexOf(this.remotePort) != -1) {
  27. this.remotePort = randomPort();
  28. }
  29. SourcePeer.usedPorts.push(this.remotePort);
  30. SourcePeer.usedPorts.push(this.localPort);
  31. }
  32. };
  33. function randomPort() {
  34. return Math.round(Math.random() * 60535) + 5000;
  35. };
  36. SourcePeer.prototype.socketInit = function() {
  37. var self = this;
  38. this._socket.emit('source', function(data) {
  39. self._id = data.id;
  40. if (!!self._handlers['ready']) {
  41. self._handlers['ready'](self._id);
  42. }
  43. self._socket.on('sink-connected', function(data) {
  44. target = data.sink;
  45. var pc = new RTCPeerConnection(self._config);
  46. self._pcs[target] = pc;
  47. self.handleStream(pc, target, function(pc, target) {
  48. self.maybeBrowserisms(pc, target);
  49. });
  50. });
  51. self._socket.on('answer', function(data) {
  52. self._pcs[data.sink].setRemoteDescription(new RTCSessionDescription(data.sdp),
  53. function() {
  54. // Firefoxism
  55. if (browserisms == 'Firefox') {
  56. self._pcs[data.sink].connectDataConnection(self.localPort, self.remotePort);
  57. self._socket.emit('port', { sink: data.sink, local: self.remotePort, remote: self.localPort });
  58. }
  59. console.log('SOURCE: PeerConnection success');
  60. }, function(err) {
  61. console.log('failed to setRemoteDescription, ', err)
  62. });
  63. });
  64. });
  65. };
  66. // Stream Firefoxism... can be removed when DataChannel no longer requires
  67. // a stream.
  68. SourcePeer.prototype.maybeBrowserisms = function(pc, target) {
  69. var self = this;
  70. if (browserisms == 'Firefox' && !this._video && !this._audio && !this._stream) {
  71. getUserMedia({ audio: true, fake: true }, function(s) {
  72. pc.addStream(s);
  73. self.makeOffer(target);
  74. }, function(err) { console.log('crap'); });
  75. } else {
  76. this.makeOffer(target);
  77. }
  78. };
  79. // Make an offer.
  80. SourcePeer.prototype.makeOffer = function(target) {
  81. var pc = this._pcs[target];
  82. var self = this;
  83. pc.createOffer(function(offer) {
  84. pc.setLocalDescription(offer, function() {
  85. self._socket.emit('offer',
  86. { 'sdp': offer,
  87. 'sink': target,
  88. 'source': self._id });
  89. }, function(err) {
  90. console.log('failed to setLocalDescription, ', err);
  91. });
  92. });
  93. };
  94. // Based on stream type requested, sets up the stream for PC.
  95. SourcePeer.prototype.handleStream = function(pc, target, cb) {
  96. pc.onaddstream = function(obj) {
  97. console.log('SOURCE: data stream get');
  98. };
  99. this.setupDataChannel(pc, target, cb);
  100. };
  101. SourcePeer.prototype.setupDataChannel = function(pc, target, cb) {
  102. var self = this;
  103. pc.onconnection = function() {
  104. console.log('SOURCE: onconnection triggered.');
  105. var dc = pc.createDataChannel(self._name, {}, target);
  106. self._dcs[target] = dc;
  107. dc.binaryType = 'blob';
  108. // User handler
  109. if (!!self._handlers['sink']) {
  110. self._handlers['sink'](target);
  111. }
  112. dc.onmessage = function(e) {
  113. self.handleDataMessage(e);
  114. };
  115. };
  116. pc.ondatachannel = function() {
  117. console.log('SOURCE: data channeled');
  118. };
  119. pc.onclosedconnection = function() {
  120. // ??
  121. };
  122. cb(pc, target);
  123. };
  124. SourcePeer.prototype.send = function(data, sink) {
  125. // TODO: try/catch
  126. var ab = BinaryPack.pack(data);
  127. if (!!sink) {
  128. this._dcs[sink].send(ab);
  129. return;
  130. }
  131. for (var key in this._dcs) {
  132. if (this._dcs.hasOwnProperty(key)) {
  133. this._dcs[key].send(ab);
  134. }
  135. }
  136. }
  137. // Handles a DataChannel message.
  138. SourcePeer.prototype.handleDataMessage = function(e) {
  139. var self = this;
  140. var fr = new FileReader();
  141. fr.onload = function(evt) {
  142. var ab = evt.target.result;
  143. var data = BinaryPack.unpack(ab);
  144. if (!!self._handlers['data']) {
  145. self._handlers['data'](data);
  146. }
  147. };
  148. fr.readAsArrayBuffer(e.data);
  149. }
  150. SourcePeer.prototype.on = function(code, cb) {
  151. // For enduser.
  152. // MAKE A HASH
  153. this._handlers[code] = cb;
  154. };