Michelle Bu 12 gadi atpakaļ
vecāks
revīzija
e3766f4b12
2 mainītis faili ar 103 papildinājumiem un 517 dzēšanām
  1. 0 319
      lib/connectionmanager.js
  2. 103 198
      lib/provider.js

+ 0 - 319
lib/connectionmanager.js

@@ -1,319 +0,0 @@
-/**
- * Manages all negotiations between Peers.
- */
-// TODO: LOCKS.
-// TODO: FIREFOX new PC after offer made for DC.
-var Negotiator = {
-  pcs: {}, // pc id => pc.
-  providers: {}, // provider's id => providers (there may be multiple providers/client.
-  options: {},
-  queue: [] // connections that are delayed due to a PC being in use.
-}
-
-Negotiator._idPrefix = 'pc_'
-
-Negotiator.startConnection = function(type, peer, connection, provider, options) {
-  Negotiator._addProvider(peer, provider);
-
-  var pc;
-  // options.pc is the PC's ID.
-  pc = Negotiator.pcs[options.pc]
-  if (!pc || pc.signalingState !== 'stable') {
-    pc = Negotiator._startPeerConnection(peer, provider);
-  }
-
-  if (type === 'media' && options._stream) {
-    // Add the stream.
-    pc.addStream(options._stream);
-  }
-
-  // What do we need to do now?
-  if (options.originator) {
-    if (type === 'data') {
-      // Create the datachannel.
-      dc = pc.createDataChannel(options.label, {reliable: reliable});
-      connection = provider.getConnection(peer, connection);
-      connection.initialize(dc);
-      Negotiator._attachConnectionListeners(dc);
-    }
-
-    if (!util.supports.onnegotiationneeded) {
-      Negotiator._makeOffer(peer, connection, options);
-    }
-  } else {
-    Negotiator._handleSDP(peer, connection, options);
-  }
-
-  return pc;
-}
-
-Negotiator._addProvider = function(peer, provider) {
-  if ((!provider.id && !provider.disconnected) || !provider.socket.open) {
-    // Wait for provider to obtain an ID.
-    provider.on('open', function(id) {
-      Negotiator._addProvider(peer, provider);
-    });
-  } else {
-    Negotiator.providers[provider.id] = provider;
-  }
-}
-
-
-/** Start a PC. */
-Negotiator._startPeerConnection = function(peer, provider) {
-  util.log('Creating RTCPeerConnection.');
-
-  var id = Negotiator._idPrefix + util.randomToken();
-  pc = new RTCPeerConnection(provider.options.config, {optional: [{RtpDataChannels: true}]});
-  Negotiator.pcs[id] = pc;
-
-  Negotiator._startListeners(peer, provider, pc, id);
-
-  return pc;
-}
-
-/** Set up various WebRTC listeners. */
-Negotiator._setupListeners = function(peer, provider, pc, id) {
-  // ICE CANDIDATES.
-  util.log('Listening for ICE candidates.');
-  pc.onicecandidate = function(evt) {
-    if (evt.candidate) {
-      util.log('Received ICE candidates.');
-      provider.socket.send({
-        type: 'CANDIDATE',
-        payload: {
-          candidate: evt.candidate,
-          pc: id  // Send along this PC's ID.
-        },
-        dst: peer,
-      });
-    }
-  };
-
-  pc.oniceconnectionstatechange = function() {
-    switch (pc.iceConnectionState) {
-      case 'failed':
-        util.log('iceConnectionState is disconnected, closing connections to ' + self.peer);
-        Negotiator._cleanup();
-        break;
-      case 'completed':
-        pc.onicecandidate = null;
-        break;
-    }
-  };
-
-  // Fallback for older Chrome impls.
-  pc.onicechange = pc.oniceconnectionstatechange;
-
-  // ONNEGOTIATIONNEEDED (Chrome)
-  util.log('Listening for `negotiationneeded`');
-  pc.onnegotiationneeded = function() {
-    util.log('`negotiationneeded` triggered');
-    Negotiator._makeOffer();
-  };
-
-  // DATACONNECTION.
-  util.log('Listening for data channel');
-  // Fired between offer and answer, so options should already be saved
-  // in the options hash.
-  pc.ondatachannel = function(evt) {
-    util.log('Received data channel');
-    var dc = evt.channel;
-    connection = provider.getConnection(peer, connection);
-    connection.initialize(dc);
-    Negotiator._attachConnectionListeners(dc);
-  };
-
-  // MEDIACONNECTION.
-  util.log('Listening for remote stream');
-  pc.onaddstream = function(evt) {
-    util.log('Received remote stream');
-    var stream = evt.stream;
-    provider.getConnection(peer, id).receiveStream(stream);
-  };
-}
-
-Negotiator._cleanup = function() {
-  // TODO
-}
-
-Negotiator._makeOffer = function() {
-  // TODO
-  pc.createOffer(function(offer) {
-    util.log('Created offer.');
-    // Firefox currently does not support multiplexing once an offer is made.
-    self.firefoxSingular = true;
-
-    if (util.browserisms === 'Webkit') {
-      //offer.sdp = Reliable.higherBandwidthSDP(offer.sdp);
-    }
-
-    self.pc.setLocalDescription(offer, function() {
-      util.log('Set localDescription to offer');
-      self._socket.send({
-        type: 'OFFER',
-        payload: {
-          sdp: offer,
-          config: self._options.config,
-          labels: self.labels,
-          call: !!self._call
-        },
-        dst: self.peer,
-        manager: self._managerId
-      });
-      // We can now reset labels because all info has been communicated.
-      self.labels = {};
-    }, function(err) {
-      self.emit('error', err);
-      util.log('Failed to setLocalDescription, ', err);
-    });
-  }, function(err) {
-    self.emit('error', err);
-    util.log('Failed to createOffer, ', err);
-  });
-}
-
-Negotiator._makeAnswer = function() {
-  // TODO
-}
-
-/** Create an answer for PC. */
-ConnectionManager.prototype._makeAnswer = function() {
-  var self = this;
-  this.pc.createAnswer(function(answer) {
-    util.log('Created answer.');
-
-    if (util.browserisms === 'Webkit') {
-      //answer.sdp = Reliable.higherBandwidthSDP(answer.sdp);
-    }
-
-    self.pc.setLocalDescription(answer, function() {
-      util.log('Set localDescription to answer.');
-      self._socket.send({
-        type: 'ANSWER',
-        payload: {
-          sdp: answer
-        },
-        dst: self.peer,
-        manager: self._managerId
-      });
-    }, function(err) {
-      self.emit('error', err);
-      util.log('Failed to setLocalDescription, ', err);
-    });
-  }, function(err) {
-    self.emit('error', err);
-    util.log('Failed to create answer, ', err);
-  });
-}
-
-/** Clean up PC, close related DCs. */
-ConnectionManager.prototype._cleanup = function() {
-  util.log('Cleanup ConnectionManager for ' + this.peer);
-  if (!!this.pc && (this.pc.readyState !== 'closed' || this.pc.signalingState !== 'closed')) {
-    this.pc.close();
-    this.pc = null;
-  }
-
-  var self = this;
-  this._socket.send({
-    type: 'LEAVE',
-    dst: self.peer
-  });
-
-  this.destroyed = true;
-  this.emit('close');
-}
-
-/** Attach connection listeners. */
-ConnectionManager.prototype._attachConnectionListeners = function(connection) {
-  var self = this;
-  connection.on('close', function() {
-    if (!!self.connections[connection.label]) {
-      delete self.connections[connection.label];
-    }
-
-    if (!Object.keys(self.connections).length) {
-      self._cleanup();
-    }
-  });
-  connection.on('open', function() {
-    self._lock = false;
-    self._processQueue();
-  });
-}
-
-/** Handle an SDP. */
-ConnectionManager.prototype.handleSDP = function(sdp, type, call) {
-  sdp = new RTCSessionDescription(sdp);
-
-  var self = this;
-  this.pc.setRemoteDescription(sdp, function() {
-    util.log('Set remoteDescription: ' + type);
-    if (type === 'OFFER') {
-      if (call && !self._call) {
-        self._call = new MediaConnection(self.peer);
-        self._call.on('answer', function(stream){
-          if (stream) {
-            self.pc.addStream(stream);
-          }
-          self._makeAnswer();
-          util.setZeroTimeout(function(){
-            // Add remote streams
-            self._call.receiveStream(self.pc.getRemoteStreams()[0]);
-          });
-        });
-        self.emit('call', self._call);
-      } else {
-        self._makeAnswer();
-      }
-    } else {
-      // Got answer from remote
-      self._lock = false;
-    }
-  }, function(err) {
-    self.emit('error', err);
-    util.log('Failed to setRemoteDescription, ', err);
-  });
-}
-
-/** Handle a candidate. */
-ConnectionManager.prototype.handleCandidate = function(message) {
-  var candidate = new RTCIceCandidate(message.candidate);
-  this.pc.addIceCandidate(candidate);
-  util.log('Added ICE candidate.');
-}
-
-/** Updates label:[serialization, reliable, metadata] pairs from offer. */
-ConnectionManager.prototype.handleUpdate = function(updates) {
-  var labels = Object.keys(updates);
-  for (var i = 0, ii = labels.length; i < ii; i += 1) {
-    var label = labels[i];
-    this.labels[label] = updates[label];
-  }
-}
-
-/** Handle peer leaving. */
-ConnectionManager.prototype.handleLeave = function() {
-  util.log('Peer ' + this.peer + ' disconnected.');
-  this.close();
-}
-
-/** Closes manager and all related connections. */
-ConnectionManager.prototype.close = function() {
-  if (this.destroyed) {
-    this.emit('error', new Error('Connections to ' + this.peer + 'are already closed.'));
-    return;
-  }
-
-  var labels = Object.keys(this.connections);
-  for (var i = 0, ii = labels.length; i < ii; i += 1) {
-    var label = labels[i];
-    var connection = this.connections[label];
-    connection.close();
-  }
-
-  // TODO: close the call.
-  this.connections = null;
-  this._cleanup();
-}

+ 103 - 198
lib/provider.js

@@ -1,172 +1,145 @@
 /**
  * Manages all negotiations between Peers.
  */
+// TODO: LOCKS.
+// TODO: FIREFOX new PC after offer made for DC.
 var Negotiator = {
   pcs: {}, // pc id => pc.
-  providers: {} // peer id => provider.
-};
+  providers: {}, // provider's id => providers (there may be multiple providers/client.
+  options: {},
+  queue: [] // connections that are delayed due to a PC being in use.
+}
 
 Negotiator._idPrefix = 'pc_'
 
-Negotiator.startConnection = function(peer, connection, provider, options) {
-  // TODO
-  if (!Negotiator.providers[peer]) {
-    Negotiator.providers[peer] = provider;
-  }
+Negotiator.startConnection = function(type, peer, connection, provider, options) {
+  Negotiator._addProvider(peer, provider);
 
   var pc;
-  if (!options || !options._pc) {
-    Negotiator._startPeerConnection(provider);
+  // options.pc is the PC's ID.
+  pc = Negotiator.pcs[options.pc]
+  if (!pc || pc.signalingState !== 'stable') {
+    pc = Negotiator._startPeerConnection(peer, provider);
   }
 
-  if (options) {
-    pc = 
-    if (options._stream) {
-      if (options.sdp) { // Is a MC receiver.
-        Negotiator.handleSDP(peer, connection, options);
-      } else { // Is a MC originator.
+  if (type === 'media' && options._stream) {
+    // Add the stream.
+    pc.addStream(options._stream);
+  }
 
-      }
-    } else { // Is a DC receiver.
+  // What do we need to do now?
+  if (options.originator) {
+    if (type === 'data') {
+      // Create the datachannel.
+      dc = pc.createDataChannel(options.label, {reliable: reliable});
+      connection = provider.getConnection(peer, connection);
+      connection.initialize(dc);
+      Negotiator._attachConnectionListeners(dc);
+    }
 
+    if (!util.supports.onnegotiationneeded) {
+      Negotiator._makeOffer(peer, connection, options);
     }
-  } else { // Is a DC originator.
-    
+  } else {
+    Negotiator._handleSDP(peer, connection, options);
   }
 
+  return pc;
+}
 
-  // Return the PeerConnection.
-
-  // Set up PeerConnection.
-  this._startPeerConnection();
-
-  // Process queued DCs.
-  this._processQueue();
-
-  // Listen for ICE candidates.
-  this._setupIce();
-
-  // Listen for negotiation needed.
-  // Chrome only **
-  this._setupNegotiationHandler();
-
-  // Listen for data channel.
-  this._setupDataChannel();
-  
-  // Listen for remote streams.
-  this._setupStreamListener();
-
+Negotiator._addProvider = function(peer, provider) {
+  if ((!provider.id && !provider.disconnected) || !provider.socket.open) {
+    // Wait for provider to obtain an ID.
+    provider.on('open', function(id) {
+      Negotiator._addProvider(peer, provider);
+    });
+  } else {
+    Negotiator.providers[provider.id] = provider;
+  }
 }
 
 
 /** Start a PC. */
-Negotiator._startPeerConnection = function(provider) {
+Negotiator._startPeerConnection = function(peer, provider) {
   util.log('Creating RTCPeerConnection.');
 
   var id = Negotiator._idPrefix + util.randomToken();
-  Negotiator.pcs[id] = new RTCPeerConnection(provider.options.config, { optional: [ { RtpDataChannels: true } ]});
-};
-
-/** Add DataChannels to all queued DataConnections. */
-ConnectionManager.prototype._processQueue = function() {
-  for (var i = 0; i < this._queued.length; i++) {
-    var conn = this._queued[i];
-    if (conn.constructor == MediaConnection) {
-      console.log('adding', conn.localStream);
-      this.pc.addStream(conn.localStream);
-    } else if (conn.constructor == DataConnection) {
-      // reliable: true not yet implemented in Chrome
-      var reliable = util.browserisms === 'Firefox' ? conn.reliable : false;
-      conn.addDC(this.pc.createDataChannel(conn.label, { reliable: reliable }));
-    }
-  }
-  // onnegotiationneeded not yet implemented in Firefox, must manually create offer
-  if (util.browserisms === 'Firefox' && this._queued.length > 0) {
-    this._makeOffer();
-  }
-  this._queued = [];
-};
+  pc = new RTCPeerConnection(provider.options.config, {optional: [{RtpDataChannels: true}]});
+  Negotiator.pcs[id] = pc;
+
+  Negotiator._startListeners(peer, provider, pc, id);
 
-/** Set up ICE candidate handlers. */
-ConnectionManager.prototype._setupIce = function() {
+  return pc;
+}
+
+/** Set up various WebRTC listeners. */
+Negotiator._setupListeners = function(peer, provider, pc, id) {
+  // ICE CANDIDATES.
   util.log('Listening for ICE candidates.');
-  var self = this;
-  this.pc.onicecandidate = function(evt) {
+  pc.onicecandidate = function(evt) {
     if (evt.candidate) {
       util.log('Received ICE candidates.');
-      self._socket.send({
+      provider.socket.send({
         type: 'CANDIDATE',
         payload: {
-          candidate: evt.candidate
+          candidate: evt.candidate,
+          pc: id  // Send along this PC's ID.
         },
-        dst: self.peer,
-        manager: self._managerId
+        dst: peer,
       });
     }
   };
-  this.pc.oniceconnectionstatechange = function() {
-    if (!!self.pc) {
-      switch (self.pc.iceConnectionState) {
-        case 'failed':
-          util.log('iceConnectionState is disconnected, closing connections to ' + self.peer);
-          self.close();
-          break;
-        case 'completed':
-          self.pc.onicecandidate = null;
-          break;
-      }
+
+  pc.oniceconnectionstatechange = function() {
+    switch (pc.iceConnectionState) {
+      case 'failed':
+        util.log('iceConnectionState is disconnected, closing connections to ' + self.peer);
+        Negotiator._cleanup();
+        break;
+      case 'completed':
+        pc.onicecandidate = null;
+        break;
     }
   };
+
   // Fallback for older Chrome impls.
-  this.pc.onicechange = this.pc.oniceconnectionstatechange;
-};
+  pc.onicechange = pc.oniceconnectionstatechange;
 
-/** Set up onnegotiationneeded. */
-ConnectionManager.prototype._setupNegotiationHandler = function() {
-  var self = this;
+  // ONNEGOTIATIONNEEDED (Chrome)
   util.log('Listening for `negotiationneeded`');
-  this.pc.onnegotiationneeded = function() {
+  pc.onnegotiationneeded = function() {
     util.log('`negotiationneeded` triggered');
-    self._makeOffer();
+    Negotiator._makeOffer();
   };
-};
 
-/** Set up Data Channel listener. */
-ConnectionManager.prototype._setupDataChannel = function() {
-  var self = this;
+  // DATACONNECTION.
   util.log('Listening for data channel');
-  this.pc.ondatachannel = function(evt) {
+  // Fired between offer and answer, so options should already be saved
+  // in the options hash.
+  pc.ondatachannel = function(evt) {
     util.log('Received data channel');
     var dc = evt.channel;
-    var label = dc.label;
-    // This should not be empty.
-    var options = self.labels[label] || {};
-    var connection  = new DataConnection(self.peer, dc, options);
-    self._attachConnectionListeners(connection);
-    self.connections[label] = connection;
-    self.emit('connection', connection);
+    connection = provider.getConnection(peer, connection);
+    connection.initialize(dc);
+    Negotiator._attachConnectionListeners(dc);
   };
-};
 
-/** Set up remote stream listener. */
-ConnectionManager.prototype._setupStreamListener = function() {
-  var self = this;
+  // MEDIACONNECTION.
   util.log('Listening for remote stream');
-  this.pc.onaddstream = function(evt) {
+  pc.onaddstream = function(evt) {
     util.log('Received remote stream');
     var stream = evt.stream;
-    if (!!self._call) {
-      self._call.receiveStream(stream);
-      
-    }
+    provider.getConnection(peer, id).receiveStream(stream);
   };
-};
+}
 
+Negotiator._cleanup = function() {
+  // TODO
+}
 
-/** Send an offer. */
-ConnectionManager.prototype._makeOffer = function() {
-  var self = this;
-  this.pc.createOffer(function(offer) {
+Negotiator._makeOffer = function() {
+  // TODO
+  pc.createOffer(function(offer) {
     util.log('Created offer.');
     // Firefox currently does not support multiplexing once an offer is made.
     self.firefoxSingular = true;
@@ -198,7 +171,11 @@ ConnectionManager.prototype._makeOffer = function() {
     self.emit('error', err);
     util.log('Failed to createOffer, ', err);
   });
-};
+}
+
+Negotiator._makeAnswer = function() {
+  // TODO
+}
 
 /** Create an answer for PC. */
 ConnectionManager.prototype._makeAnswer = function() {
@@ -228,7 +205,7 @@ ConnectionManager.prototype._makeAnswer = function() {
     self.emit('error', err);
     util.log('Failed to create answer, ', err);
   });
-};
+}
 
 /** Clean up PC, close related DCs. */
 ConnectionManager.prototype._cleanup = function() {
@@ -246,7 +223,7 @@ ConnectionManager.prototype._cleanup = function() {
 
   this.destroyed = true;
   this.emit('close');
-};
+}
 
 /** Attach connection listeners. */
 ConnectionManager.prototype._attachConnectionListeners = function(connection) {
@@ -264,7 +241,7 @@ ConnectionManager.prototype._attachConnectionListeners = function(connection) {
     self._lock = false;
     self._processQueue();
   });
-};
+}
 
 /** Handle an SDP. */
 ConnectionManager.prototype.handleSDP = function(sdp, type, call) {
@@ -298,14 +275,14 @@ ConnectionManager.prototype.handleSDP = function(sdp, type, call) {
     self.emit('error', err);
     util.log('Failed to setRemoteDescription, ', err);
   });
-};
+}
 
 /** Handle a candidate. */
 ConnectionManager.prototype.handleCandidate = function(message) {
   var candidate = new RTCIceCandidate(message.candidate);
   this.pc.addIceCandidate(candidate);
   util.log('Added ICE candidate.');
-};
+}
 
 /** Updates label:[serialization, reliable, metadata] pairs from offer. */
 ConnectionManager.prototype.handleUpdate = function(updates) {
@@ -314,13 +291,13 @@ ConnectionManager.prototype.handleUpdate = function(updates) {
     var label = labels[i];
     this.labels[label] = updates[label];
   }
-};
+}
 
 /** Handle peer leaving. */
 ConnectionManager.prototype.handleLeave = function() {
   util.log('Peer ' + this.peer + ' disconnected.');
   this.close();
-};
+}
 
 /** Closes manager and all related connections. */
 ConnectionManager.prototype.close = function() {
@@ -335,80 +312,8 @@ ConnectionManager.prototype.close = function() {
     var connection = this.connections[label];
     connection.close();
   }
-  
-  // TODO: close the call
-  
+
+  // TODO: close the call.
   this.connections = null;
   this._cleanup();
-};
-
-/** Create and returns a DataConnection with the peer with the given label. */
-ConnectionManager.prototype.connect = function(options) {
-  if (this.destroyed) {
-    return;
-  }
-console.log('trying to connect');
-  options = util.extend({
-    label: 'peerjs',
-    reliable: (util.browserisms === 'Firefox')
-  }, options);
-
-  // Check if label is taken...if so, generate a new label randomly.
-  while (!!this.connections[options.label]) {
-    options.label = 'peerjs' + this._default;
-    this._default += 1;
-  }
-
-  this.labels[options.label] = options;
-
-  var dc;
-  if (!!this.pc && !this._lock) {
-    var reliable = util.browserisms === 'Firefox' ? options.reliable : false;
-    dc = this.pc.createDataChannel(options.label, { reliable: reliable });
-    if (util.browserisms === 'Firefox') {
-      this._makeOffer();
-    }
-  }
-  var connection = new DataConnection(this.peer, dc, options);
-  this._attachConnectionListeners(connection);
-  this.connections[options.label] = connection;
-
-  if (!this.pc || this._lock) {
-    console.log('qing', this._lock);
-    this._queued.push(connection);
-  }
-
-  this._lock = true
-  return connection;
-};
-
-ConnectionManager.prototype.call = function(stream, options) {
-  if (this.destroyed) {
-    return;
-  }
-
-  options = util.extend({
-    
-  }, options);
-
-  if (!!this.pc && !this._lock) {
-    this.pc.addStream(stream);
-    if (util.browserisms === 'Firefox') {
-      this._makeOffer();
-    }
-  }
-  
-  var connection = new MediaConnection(this.peer, stream, options);
-  this._call = connection;
-  
-  if (!this.pc || this._lock) {
-    this._queued.push(connection);
-  }
-
-  this._lock = true;
-  
-  
-  return connection;
-};
-
-
+}