source.js 4.9 KB

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