Michelle Bu 12 년 전
부모
커밋
9efee50e22
6개의 변경된 파일432개의 추가작업 그리고 683개의 파일을 삭제
  1. 38 19
      README.md
  2. 2 1
      bin/build.js
  3. 294 0
      lib/connection.js
  4. 98 0
      lib/peer.js
  5. 0 481
      lib/sink.js
  6. 0 182
      lib/source.js

+ 38 - 19
README.md

@@ -1,6 +1,6 @@
 # Peers: a node.js PeerConnection library #
 
-Under heavy development and testing.
+New API in the works, nothing is ready yet.
 
 ## Chrome ##
 
@@ -29,34 +29,53 @@ var PeerServer = require('peer').PeerServer({ port: 80, debug: true });
 `<script type="text/javascript" src="/client/dist/peer.js"></script>`
 
 
-#### Source Peer ####
+#### Peer ####
+
+## First peer ##
 
 ```js
-originator = new SinkPeer({ video: true, audio: true, ws: 'ws://www.host.com' });
-originator.on('ready', function(id) {
-  console.log(id);
+var connections = {};
+
+p1 = new Peer({ ws: 'ws://www.host.com' });
+p1.on('ready', function(id) {
+  console.log(id); // => 'some_id_1'
 });
-originator.on('connection', function(recipient) {
+
+p1.on('connection', function(connection) {
   // Sends a message to the other peer. This can even be a blob or JSON.
-  originator.send('Hi there!');
-  originator.send({ file: new Blob([1, 2, 3])});
-});
-originator.on('data', function(data) {
-  // Prints out any messages received.
-  console.log(data);
+  connection.send('Hi there!');
+  connection.send({ file: new Blob([1, 2, 3])});
+
+  // Probably want to save the connection object.
+  connections[connection.metadata.username] = connection;
+
+  if (connection.metadata.username == 'spy') {
+    connection.close();
+  } else {
+    // Add handler for connection data.
+    connection.on('data', function(data) {
+      console.log(data);
+    }
+  }
 });
+
 ```
 
-#### Sink Peer ####
+## Second Peer ##
 
 ```js
-// Sinks start off with an ID of whom to connect to.
-sink = new SinkPeer({ source: source_id, ws: 'ws://localhost' });
+p2 = new Peer({ ws: 'ws://www.host.com' });
+p2.on('ready', function(id) {
+  console.log(id);
+
+  p2.connect('some_id_1', { username: 'friend' }, function(err, connection) {
+    connection.send('Hi, bye.');
+
+    connection.close();
+  });
+});
 ```
 
 #### Other events ####
 
-* `localstream, remotestream`: Callback is called with `type`, `stream` when a
-local or remote stream is added.
-
-* `disconnect`: Called with `id` when a peer disconnects. (TODO)
+* Connection - `close`: Called when a peer disconnects.

+ 2 - 1
bin/build.js

@@ -43,7 +43,8 @@ var base = [
   , '../deps/EventEmitter/EventEmitter.js'
   , 'util.js'
   , 'adapter.js' 
-  , 'sink.js'
+  , 'peer.js'
+  , 'connection.js'
 
 ];
 

+ 294 - 0
lib/connection.js

@@ -0,0 +1,294 @@
+function DataConnection(options, socket, cb) {
+  if (!(this instanceof DataConnection)) return new DataConnection(options);
+  EventEmitter.call(this);
+
+  // Is this the originator?
+  this._originator = options.originator || false;
+  this._cb = cb;
+  this._peer = options.peer;
+  this._id = options.id;
+
+  var sdp = options.sdp;
+  this.metadata = options.metadata;
+
+  // Set up socket handlers.
+  this._socket = socket;
+
+  // Firefoxism: connectDataConnection ports.
+  if (browserisms == 'Firefox') {
+    if (!DataConnection.usedPorts) {
+      DataConnection.usedPorts = [];
+    }
+    this.localPort = util.randomPort();
+    while (DataConnection.usedPorts.indexOf(this.localPort) != -1) {
+      this.localPort = util.randomPort();
+    }
+    this.remotePort = util.randomPort();
+    while (this.remotePort == this.localPort ||
+        DataConnection.usedPorts.indexOf(this.localPort) != -1) {
+      this.remotePort = util.randomPort();
+    }
+    DataConnection.usedPorts.push(this.remotePort);
+    DataConnection.usedPorts.push(this.localPort);
+  }
+
+  // Set up PeerConnection.
+  this._startPeerConnection();
+  if (this._originator) {
+    var self = this;
+    this._setupDataConnection(function() {
+      this._maybeBrowserisms();
+    });
+  } else if (sdp) {
+    try {
+      sdp = new RTCSessionDescription(message.sdp);
+    } catch(e) {
+      util.log('Firefox');
+    }
+    this._pc.setRemoteDescription(sdp, function() {
+      util.log('setRemoteDescription: offer');
+
+      self._setupDataChannel(function() {
+        self._maybeBrowserisms();
+      });
+    }, function(err) {
+      util.log('failed to setRemoteDescription with offer, ', err);
+    });
+  }
+};
+
+
+DataConnection.prototype.handleAnswer(message) {
+  var sdp = message.sdp;
+  try {
+    sdp = new RTCSessionDescription(message.sdp);
+  } catch(e) {
+    util.log('Firefox');
+  }
+  var self = this;
+  this._pc.setRemoteDescription(sdp, function() {
+    util.log('setRemoteDescription: answer');
+    // Firefoxism
+    if (browserisms == 'Firefox') {
+      self._pc.connectDataConnection(self.localPort, self.remotePort);
+      self._socket.send(JSON.stringify({
+        type: 'PORT',
+        dst: self._peer,
+        src: self._id,
+        remote: self.localPort,
+        local: self.remotePort
+      }));
+    }
+    util.log('ORIGINATOR: PeerConnection success');
+  }, function(err) {
+    util.log('failed to setRemoteDescription, ', err);
+  });
+};
+
+
+DataConnection.prototype.handleCandidate(message) {
+  util.log(message.candidate);
+  var candidate = new RTCIceCandidate(message.candidate);
+  this._pc.addIceCandidate(candidate);
+}
+
+
+DataConnection.prototype.handleLeave(message) {
+  util.log('counterpart disconnected');
+  if (!!this._pc && this._pc.readyState != 'closed') {
+    this._pc.close();
+    this._pc = null;
+  }
+  if (!!this._dc && this._dc.readyState != 'closed') {
+    this._dc.close();
+    this._dc = null;
+  }
+  this.emit('close', this._peer);
+}
+
+DataConnection.prototype.handlePort(message) {
+  if (!DataConnection.usedPorts) {
+    DataConnection.usedPorts = [];
+  }
+  DataConnection.usedPorts.push(message.local);
+  DataConnection.usedPorts.push(message.remote);
+  this._pc.connectDataConnection(message.local, message.remote);
+}
+
+
+/** Starts a PeerConnection and sets up handlers. */
+DataConnection.prototype._startPeerConnection = function() {
+  this._pc = new RTCPeerConnection(this._config, { optional:[ { RtpDataChannels: true } ]});
+  this._setupIce();
+};
+
+
+/** Takes care of ice handlers. */
+DataConnection.prototype._setupIce = function() {
+  var self = this;
+  this._pc.onicecandidate = function(event) {
+    util.log('candidates received');
+    if (event.candidate) {
+      self._socket.send(JSON.stringify({
+        type: 'CANDIDATE',
+        candidate: event.candidate,
+        dst: self._peer,
+        src: self._id
+      }));
+    } else {
+      util.log("End of candidates.");
+    }
+  };
+};
+
+
+/** Sets up DataChannel handlers. */
+DataConnection.prototype._setupDataChannel = function(cb) {
+  var self = this;
+  if (this._originator) {
+    /** ORIGINATOR SETUP */
+    if (browserisms == 'Webkit') {
+
+      // TODO: figure out the right thing to do with this.
+      this._pc.onstatechange = function() {
+        util.log('State Change: ', self._pc.readyState);
+      }
+
+    } else {
+      this._pc.onconnection = function() {
+        util.log('ORIGINATOR: onconnection triggered');
+
+        self._startDataChannel();
+      };
+    }
+  } else {
+    /** TARGET SETUP */
+    this._pc.ondatachannel = function(dc) {
+      util.log('SINK: ondatachannel triggered');
+      self._dc = dc;
+      self._dc.binaryType = 'blob';
+
+      self._cb(null, self);
+
+      self._dc.onmessage = function(e) {
+        self._handleDataMessage(e);
+      };
+    };
+
+    this._pc.onconnection = function() {
+      util.log('SINK: onconnection triggered');
+    };
+  }
+
+
+  this._pc.onclosedconnection = function() {
+    // Remove socket handlers perhaps.
+    self.emit('close', self._peer);
+  };
+};
+
+
+DataConnection.prototype._startDataChannel = function() {
+  var self = this;
+  this._dc = this._pc.createDataChannel(this._peer, { reliable: false });
+  this._dc.binaryType = 'blob';
+
+  this._cb(null, self);
+
+  this._dc.onmessage = function(e) {
+    self._handleDataMessage(e);
+  };
+};
+
+
+/** Decide whether to handle Firefoxisms. */
+DataConnection.prototype._maybeBrowserisms = function() {
+  var self = this;
+  if (browserisms == 'Firefox') {
+    getUserMedia({ audio: true, fake: true }, function(s) {
+      self._pc.addStream(s);
+
+      if (self._originator) {
+        self._makeOffer();
+      } else {
+        self._makeAnswer();
+      }
+
+    }, function(err) { util.log('crap'); });
+  } else {
+    if (self._originator) {
+      this._makeOffer();
+    } else {
+      this._makeAnswer();
+    }
+  }
+}
+
+
+/** Create an answer for PC. */
+DataConnection.prototype._makeAnswer = function() {
+  var self = this;
+
+  this._pc.createAnswer(function(answer) {
+    util.log('createAnswer');
+    self._pc.setLocalDescription(answer, function() {
+      util.log('setLocalDescription: answer');
+      self._socket.send(JSON.stringify({
+        type: 'ANSWER',
+        src: self._id,
+        sdp: answer,
+        dst: self._peer
+      }));
+    }, function(err) {
+      util.log('failed to setLocalDescription, ', err)
+    });
+  }, function(err) {
+    util.log('failed to create answer, ', err)
+  });
+};
+
+
+/** Create an offer for PC. */
+DataConnection.prototype._makeOffer = function() {
+  var self = this;
+
+  this._pc.createOffer(function(offer) {
+    util.log('createOffer')
+    self._pc.setLocalDescription(offer, function() {
+      util.log('setLocalDescription: offer');
+      self._socket.send(JSON.stringify({
+        type: 'OFFER',
+        sdp: offer,
+        dst: self._peer,
+        src: self._id,
+        metadata: self._metadata
+      }));
+    }, function(err) {
+      util.log('failed to setLocalDescription, ', err);
+    });
+  });
+};
+
+
+/** Allows user to send data. */
+DataConnection.prototype.send = function(data) {
+  var ab = BinaryPack.pack(data);
+  this._dc.send(ab);
+}
+
+
+// Handles a DataChannel message.
+DataConnection.prototype._handleDataMessage = function(e) {
+  var self = this;
+  var fr = new FileReader();
+  fr.onload = function(evt) {
+    var ab = evt.target.result;
+    var data = BinaryPack.unpack(ab);
+
+    self.emit('data', data);
+  };
+  fr.readAsArrayBuffer(e.data);
+}
+
+exports.DataChannel = DataChannel;
+

+ 98 - 0
lib/peer.js

@@ -0,0 +1,98 @@
+function Peer(options) {
+  if (!(this instanceof Peer)) return new Peer(options);
+  EventEmitter.call(this);
+
+  options = util.extend({
+    debug: false,
+    config: { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] },
+    ws: 'ws://localhost'
+  }, options);
+  util.debug = options.debug;
+
+  this._id = null;
+
+  // Connections
+  this.connections = {};
+  this._socket = new WebSocket(options.ws);
+
+  // Init socket msg handlers
+  var self = this;
+  this._socket.onopen = function() {
+    self.socketInit();
+  };
+};
+
+util.inherits(Peer, EventEmitter);
+
+/** Start up websocket communications. */
+Peer.prototype.socketInit = function() {
+  var self = this;
+  // Announce as a PEER to receive an ID.
+  this._socket.send(JSON.stringify({
+    type: 'PEER'
+  }));
+
+  this._socket.onmessage = function(event) {
+    var message = JSON.parse(event.data);
+    var peer = message.src;
+    var connection = self.connections[peer];
+
+    switch (message.type) {
+      case 'ID':
+        self._id = message.id;
+        self.emit('ready', self._id);
+        break;
+      case 'OFFER':
+        var options = {
+          metadata: message.metadata,
+          peer: peer,
+          id: self._id,
+          originator: false,
+          sdp: message.sdp
+        };
+        var connection = new DataConnection(options, socket, function(err, connection) {
+          if (!err) {
+            self.emit('connection', connection);
+          }
+        });
+        self.connections[peer] = connection;
+        break;
+      case 'ANSWER':
+        if (connection) connection.handleAnswer(message);
+        break;
+      case 'CANDIDATE':
+        if (connection) connection.handleCandidate(message);
+        break;
+      case 'LEAVE':
+        if (connection) connection.handleLeave(message);
+        break;
+      case 'PORT':
+        if (browserisms && browserisms == 'Firefox') {
+          connection.handlePort(message);
+          break;
+        }
+      case 'DEFAULT':
+        util.log('PEER: unrecognized message ', message.type);
+        break;
+    }
+  };
+
+};
+
+
+Peer.prototype.connect = function(peer, metadata, cb) {
+  if (typeof metadata === 'function' && !cb) cb = metadata; metadata = false;
+
+  var options = {
+    metadata: metadata,
+    id: this._id,
+    peer: peer,
+    originator: true
+  };
+  var connection = new DataConnection(options, socket, cb);
+  this.connections[peer] = connection;
+};
+
+
+exports.Peer = Peer;
+

+ 0 - 481
lib/sink.js

@@ -1,481 +0,0 @@
-function Peer(options) {
-  if (!(this instanceof Peer)) return new Peer(options);
-  
-  EventEmitter.call(this);
-  
-  options = util.extend({
-    debug: false
-  }, options);
-  
-  util.debug = options.debug;
-  
-  
-  this._config = options.config || { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] };
-  this._id = null;
-  // User handlers.
-  this._handlers = {};
-
-  // Source to connect to; null if waiting for a connection.
-  this._peer = options.source || null;
-
-  // Booleans to determine what streams to allow.
-  this._video = options.video;
-  this._data = options.data != undefined ? options.data : true;
-  this._audio = options.audio;
-
-  // Connections
-  this._pc = null;
-  this._dc = null;
-  this._socket = new WebSocket(options.ws || 'ws://localhost');
-
-  // Local streams for multiple use.
-  this._localVideo = options.localVideo || null;
-  this._localAudio = options.localAudio || null;
-
-  // Init socket msg handlers
-  var self = this;
-  this._socket.onopen = function() {
-    self.socketInit();
-  };
-
-  // Firefoxism: connectDataConnection ports.
-  if (browserisms == 'Firefox' && !options.source) {
-    if (!Peer.usedPorts) {
-      Peer.usedPorts = [];
-    }
-    this.localPort = util.randomPort();
-    while (Peer.usedPorts.indexOf(this.localPort) != -1) {
-      this.localPort = util.randomPort();
-    }
-    this.remotePort = util.randomPort();
-    while (this.remotePort == this.localPort ||
-        Peer.usedPorts.indexOf(this.localPort) != -1) {
-      this.remotePort = util.randomPort();
-    }
-    Peer.usedPorts.push(this.remotePort);
-    Peer.usedPorts.push(this.localPort);
-  }
-
-};
-
-util.inherits(Peer, EventEmitter);
-
-
-/** Start up websocket communications. */
-Peer.prototype.socketInit = function() {
-  var self = this;
-  if (!!this._peer) {
-    // Announce as a sink if initiated with a source.
-    this._socket.send(JSON.stringify({
-      type: 'SINK',
-      source: this._peer,
-      isms: browserisms
-    }));
-
-    this._socket.onmessage = function(event) {
-      var message = JSON.parse(event.data);
-
-      switch (message.type) {
-        case 'SINK-ID':
-          self._id = message.id;
-          self.emit('ready', self._id);          
-          self.startPeerConnection();
-          break;
-        case 'OFFER':
-          var sdp = message.sdp;
-          try {
-            sdp = new RTCSessionDescription(message.sdp);
-          } catch(e) {
-            util.log('Firefox');
-          }
-          self._pc.setRemoteDescription(sdp, function() {
-            util.log('setRemoteDescription: offer');
-
-            // If we also have to set up a stream on the sink end, do so.
-            self.handleStream(false, function() {
-              self.maybeBrowserisms(false);
-            });
-          }, function(err) {
-            util.log('failed to setRemoteDescription with offer, ', err);
-          });
-          break;
-        case 'CANDIDATE':
-          util.log(message.candidate);
-          var candidate = new RTCIceCandidate(message.candidate);
-          self._pc.addIceCandidate(candidate);
-          break;
-        case 'LEAVE':
-          util.log('counterpart disconnected');
-          if (!!self._pc && self._pc.readyState != 'closed') {
-            self._pc.close();
-            self._pc = null;
-            self._peer = null;
-          }
-          if (!!self._dc && self._dc.readyState != 'closed') {
-            self._dc.close();
-            self._dc = null;
-          }
-          break;
-        case 'PORT':
-          if (browserisms && browserisms == 'Firefox') {
-            if (!Peer.usedPorts) {
-              Peer.usedPorts = [];
-            }
-            Peer.usedPorts.push(message.local);
-            Peer.usedPorts.push(message.remote);
-            self._pc.connectDataConnection(message.local, message.remote);
-            break;
-          }
-        case 'DEFAULT':
-          util.log('SINK: unrecognized message ', message.type);
-          break;
-      }
-    };
-
-  } else {
-    // Otherwise, this sink is the originator to another sink and should wait
-    // for an alert to begin the PC process.
-    this._socket.send(JSON.stringify({
-      type: 'SOURCE',
-      isms: browserisms
-    }));
-
-    this._socket.onmessage = function(event) {
-      var message = JSON.parse(event.data);
-
-      switch (message.type) {
-        case 'SOURCE-ID':
-          self._id = message.id;
-          self.emit('ready', self._id);
-          break;
-        case 'SINK-CONNECTED':
-          self._peer = message.sink;
-          self.startPeerConnection();
-          self.handleStream(true, function() {
-            self.maybeBrowserisms(true);
-          });
-          break;
-        case 'ANSWER':
-          var sdp = message.sdp;
-          try {
-            sdp = new RTCSessionDescription(message.sdp);
-          } catch(e) {
-            util.log('Firefox');
-          }
-          self._pc.setRemoteDescription(sdp, function() {
-            util.log('setRemoteDescription: answer');
-            // Firefoxism
-            if (browserisms == 'Firefox') {
-              self._pc.connectDataConnection(self.localPort, self.remotePort);
-              self._socket.send(JSON.stringify({
-                type: 'PORT',
-                dst: self._peer,
-                remote: self.localPort,
-                local: self.remotePort
-              }));
-            }
-            util.log('ORIGINATOR: PeerConnection success');
-          }, function(err) {
-            util.log('failed to setRemoteDescription, ', err);
-          });
-          break;
-        case 'CANDIDATE':
-          util.log(message.candidate);
-          var candidate = new RTCIceCandidate(message.candidate);
-          self._pc.addIceCandidate(candidate);
-          break;
-        case 'LEAVE':
-          util.log('counterpart disconnected');
-          if (!!self._pc && self._pc.readyState != 'closed') {
-            self._pc.close();
-            self._pc = null;
-            self._peer = null;
-          }
-          if (!!self._dc && self._dc.readyState != 'closed') {
-            self._dc.close();
-            self._dc = null;
-          }
-          break;
-        case 'DEFAULT':
-          util.log('ORIGINATOR: message not recognized ', message.type);
-      }
-    };
-  }
-
-  // Makes sure things clean up neatly when window is closed.
-  window.onbeforeunload = function() {
-    if (!!self._pc && self._pc.readyState != 'closed') {
-      self._pc.close();
-    }
-    if (!!self._socket && !!self._peer) {
-      self._socket.send(JSON.stringify({ type: 'LEAVE', dst: self._peer }));
-      if (!!self._dc && self._dc.readyState != 'closed') {
-        self._dc.close();
-      }
-    }
-  }
-};
-
-
-/** Takes care of ice handlers. */
-Peer.prototype.setupIce = function() {
-  var self = this;
-  this._pc.onicecandidate = function(event) {
-    util.log('candidates received');
-    if (event.candidate) {
-      self._socket.send(JSON.stringify({
-        type: 'CANDIDATE',
-        candidate: event.candidate,
-        dst: self._peer
-      }));
-    } else {
-      util.log("End of candidates.");
-    }
-  };
-};
-
-
-/** Starts a PeerConnection and sets up handlers. */
-Peer.prototype.startPeerConnection = function() {
-  this._pc = new RTCPeerConnection(this._config, { optional:[ { RtpDataChannels: true } ]});
-  this.setupIce();
-  this.setupAudioVideo();
-};
-
-
-/** Decide whether to handle Firefoxisms. */
-Peer.prototype.maybeBrowserisms = function(originator) {
-  var self = this;
-  if (browserisms == 'Firefox' && !this._video && !this._audio/* && !this._stream*/) {
-    getUserMedia({ audio: true, fake: true }, function(s) {
-      self._pc.addStream(s);
-
-      if (originator) {
-        self.makeOffer();
-      } else {
-        self.makeAnswer();
-      }
-
-    }, function(err) { util.log('crap'); });
-  } else {
-    if (originator) {
-      this.makeOffer();
-    } else {
-      this.makeAnswer();
-    }
-  }
-}
-
-
-/** Create an answer for PC. */
-Peer.prototype.makeAnswer = function() {
-  var self = this;
-
-  this._pc.createAnswer(function(answer) {
-    util.log('createAnswer');
-    self._pc.setLocalDescription(answer, function() {
-      util.log('setLocalDescription: answer');
-      self._socket.send(JSON.stringify({
-        type: 'ANSWER',
-        src: self._id,
-        sdp: answer,
-        dst: self._peer
-      }));
-    }, function(err) {
-      util.log('failed to setLocalDescription, ', err)
-    });
-  }, function(err) {
-    util.log('failed to create answer, ', err)
-  });
-};
-
-
-/** Create an offer for PC. */
-Peer.prototype.makeOffer = function() {
-  var self = this;
-
-  this._pc.createOffer(function(offer) {
-    util.log('createOffer')
-    self._pc.setLocalDescription(offer, function() {
-      util.log('setLocalDescription: offer');
-      self._socket.send(JSON.stringify({
-        type: 'OFFER',
-        sdp: offer,
-        dst: self._peer,
-        src: self._id
-      }));
-    }, function(err) {
-      util.log('failed to setLocalDescription, ', err);
-    });
-  });
-};
-
-
-/** Sets up A/V stream handler. */
-Peer.prototype.setupAudioVideo = function() {
-  var self = this;
-  util.log('onaddstream handler added');
-  this._pc.onaddstream = function(obj) {
-    util.log('Remote stream added');
-    //    this._stream = true;
-    self.emit('remotestream', obj.type, obj.stream);
-  };
-};
-
-
-/** Handle the different types of streams requested by user. */
-Peer.prototype.handleStream = function(originator, cb) {
-  if (this._data) {
-    this.setupDataChannel(originator);
-  }
-  this.getAudioVideo(originator, cb);
-};
-
-
-/** Get A/V streams. */
-Peer.prototype.getAudioVideo = function(originator, cb) {
-  var self = this;
-  if (this._video && !this._localVideo) {
-    getUserMedia({ video: true }, function(vstream) {
-      self._pc.addStream(vstream);
-      self._localVideo = vstream;
-      util.log('Local video stream added');
-
-      self.emit('localstream', 'video', vstream);
-      
-      if (self._audio && !self._localAudio) {
-        getUserMedia({ audio: true }, function(astream) {
-          self._pc.addStream(astream);
-          self._localAudio = astream;
-          util.log('Local audio stream added');
-
-          self.emit('localstream', 'audio', astream);
-
-          cb();
-        }, function(err) { util.log('Audio cannot start'); cb(); });
-      } else {
-        if (self._audio) {
-          self._pc.addStream(self._localAudio);
-        }
-        cb();
-      }
-    }, function(err) { util.log('Video cannot start', err); cb(); });
-  } else if (this._audio && !this._localAudio) {
-    getUserMedia({ audio: true }, function(astream) {
-      self._pc.addStream(astream);
-      self._localAudio = astream;
-      util.log('Local audio stream added');
-
-      self.emit('localstream', 'audio', astream);
-      
-      cb();
-    }, function(err) { util.log('Audio cannot start'); cb(); });
-  } else {
-    if (this._audio) {
-      this._pc.addStream(this._localAudio);
-    }
-    if (this._video) {
-      this._pc.addStream(this._localVideo);
-    }
-    util.log('no audio/video streams initiated');
-    cb();
-  }
-
-};
-
-
-/** Sets up DataChannel handlers. */
-Peer.prototype.setupDataChannel = function(originator, cb) {
-  var self = this;
-  if (originator) {
-    /** ORIGINATOR SETUP */
-    if (browserisms == 'Webkit') {
-
-      this._pc.onstatechange = function() {
-        util.log('State Change: ', self._pc.readyState);
-        /*if (self._pc.readyState == 'active') {
-          util.log('ORIGINATOR: active state detected');
-
-          self._dc = self._pc.createDataChannel('StreamAPI', { reliable: false });
-          self._dc.binaryType = 'blob';
-
-          if (!!self._handlers['connection']) {
-            self._handlers['connection'](self._peer);
-          }
-
-          self._dc.onmessage = function(e) {
-            self.handleDataMessage(e);
-          };
-        }*/
-      }
-
-    } else {
-      this._pc.onconnection = function() {
-        util.log('ORIGINATOR: onconnection triggered');
-
-        self.startDataChannel();
-      };
-    }
-  } else {
-    /** TARGET SETUP */
-    this._pc.ondatachannel = function(dc) {
-      util.log('SINK: ondatachannel triggered');
-      self._dc = dc;
-      self._dc.binaryType = 'blob';
-
-      self.emit('connection', self._peer);
-      
-      self._dc.onmessage = function(e) {
-        self.handleDataMessage(e);
-      };
-    };
-
-    this._pc.onconnection = function() {
-      util.log('SINK: onconnection triggered');
-    };
-  }
-
-
-  this._pc.onclosedconnection = function() {
-    // Remove socket handlers perhaps.
-  };
-};
-
-
-Peer.prototype.startDataChannel = function() {
-  var self = this;
-  this._dc = this._pc.createDataChannel(this._peer, { reliable: false });
-  this._dc.binaryType = 'blob';
-
-  this.emit('connection', this._peer);
-  
-  this._dc.onmessage = function(e) {
-    self.handleDataMessage(e);
-  };
-};
-
-
-/** Allows user to send data. */
-Peer.prototype.send = function(data) {
-  var ab = BinaryPack.pack(data);
-  this._dc.send(ab);
-}
-
-
-// Handles a DataChannel message.
-// TODO: have these extend Peer, which will impl these generic handlers.
-Peer.prototype.handleDataMessage = function(e) {
-  var self = this;
-  var fr = new FileReader();
-  fr.onload = function(evt) {
-    var ab = evt.target.result;
-    var data = BinaryPack.unpack(ab);
-    
-    self.emit('data', data);
-    
-  };
-  fr.readAsArrayBuffer(e.data);
-}
-
-exports.Peer = Peer;
-

+ 0 - 182
lib/source.js

@@ -1,182 +0,0 @@
-function SourcePeer(options) {
-  // TODO: Update for streams.
-  // TODO: Allow passing in own ID.
-  this._config = options.config || { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]};
-  this._streams = options.streamType || 'd';
-  this._name = options.name || 'StreamAPI';
-  // PeerConnections open for this source. Client name => PC.
-  this._pcs = {};
-  this._id = null;
-  // Same for DCs.
-  this._dcs = {};
-  this._socket = io.connect('http://localhost');
-  this.socketInit();
-  this._handlers = {};
-
-  // Firefox
-  if (browserisms == 'Firefox') {
-    if (!SourcePeer.usedPorts) {
-      SourcePeer.usedPorts = [];
-    }
-    this.localPort = randomPort();
-    while (SourcePeer.usedPorts.indexOf(this.localPort) != -1) {
-      this.localPort = randomPort();
-    }
-    this.remotePort = randomPort();
-    while (this.remotePort == this.localPort ||
-        SourcePeer.usedPorts.indexOf(this.remotePort) != -1) {
-      this.remotePort = randomPort();
-    }
-    SourcePeer.usedPorts.push(this.remotePort);
-    SourcePeer.usedPorts.push(this.localPort);
-  }
-};
-
-function randomPort() {
-  return Math.round(Math.random() * 60535) + 5000;
-};
-
-
-SourcePeer.prototype.socketInit = function() {
-  var self = this;
-  this._socket.emit('source', function(data) {
-    self._id = data.id;
-
-    if (!!self._handlers['ready']) {
-      self._handlers['ready'](self._id);
-    }
-
-    self._socket.on('sink-connected', function(data) {
-      target = data.sink;
-      var pc = new RTCPeerConnection(self._config);
-      self._pcs[target] = pc;
-      self.handleStream(pc, target, function(pc, target) {
-        self.maybeBrowserisms(pc, target);
-      });
-    });
-
-    self._socket.on('answer', function(data) {
-      self._pcs[data.sink].setRemoteDescription(new RTCSessionDescription(data.sdp),
-          function() {
-        // Firefoxism
-        if (browserisms == 'Firefox') {
-          self._pcs[data.sink].connectDataConnection(self.localPort, self.remotePort);
-          self._socket.emit('port', { sink: data.sink, local: self.remotePort, remote: self.localPort });
-        }
-        console.log('SOURCE: PeerConnection success');
-      }, function(err) {
-        console.log('failed to setRemoteDescription, ', err)
-      });
-    });
-  });
-};
-
-
-// Stream Firefoxism... can be removed when DataChannel no longer requires
-// a stream.
-SourcePeer.prototype.maybeBrowserisms = function(pc, target) {
-  var self = this;
-  if (browserisms == 'Firefox' && !this._video && !this._audio && !this._stream) {
-    getUserMedia({ audio: true, fake: true }, function(s) {
-      pc.addStream(s);
-      self.makeOffer(target);
-    }, function(err) { console.log('crap'); });
-  } else {
-    this.makeOffer(target);
-  }
-};
-
-
-// Make an offer.
-SourcePeer.prototype.makeOffer = function(target) {
-  var pc = this._pcs[target];
-  var self = this;
-  pc.createOffer(function(offer) {
-    pc.setLocalDescription(offer, function() {
-      self._socket.emit('offer',
-          { 'sdp': offer,
-            'sink': target,
-            'source': self._id });
-    }, function(err) {
-      console.log('failed to setLocalDescription, ', err);
-    });
-  });
-};
-
-
-// Based on stream type requested, sets up the stream for PC.
-SourcePeer.prototype.handleStream = function(pc, target, cb) {
-  pc.onaddstream = function(obj) {
-    console.log('SOURCE: data stream get');
-  };
-  this.setupDataChannel(pc, target, cb);
-};
-
-
-SourcePeer.prototype.setupDataChannel = function(pc, target, cb) {
-  var self = this;
-  pc.onconnection = function() {
-    console.log('SOURCE: onconnection triggered.');
-    var dc = pc.createDataChannel(self._name, {}, target);
-    self._dcs[target] = dc;
-    dc.binaryType = 'blob';
-
-    // User handler
-    if (!!self._handlers['sink']) {
-      self._handlers['sink'](target);
-    }
-
-    dc.onmessage = function(e) {
-      self.handleDataMessage(e);
-    };
-  };
-
-  pc.ondatachannel = function() {
-    console.log('SOURCE: data channeled');
-  };
-
-  pc.onclosedconnection = function() {
-    // ??
-  };
-  cb(pc, target);
-};
-
-
-SourcePeer.prototype.send = function(data, sink) {
-  // TODO: try/catch
-  var ab = BinaryPack.pack(data);
-
-  if (!!sink) {
-    this._dcs[sink].send(ab);
-    return;
-  }
-
-  for (var key in this._dcs) {
-    if (this._dcs.hasOwnProperty(key)) {
-      this._dcs[key].send(ab);
-    }
-  }
-}
-
-
-// Handles a DataChannel message.
-SourcePeer.prototype.handleDataMessage = function(e) {
-  var self = this;
-  var fr = new FileReader();
-  fr.onload = function(evt) {
-    var ab = evt.target.result;
-    var data = BinaryPack.unpack(ab);
-    if (!!self._handlers['data']) {
-      self._handlers['data'](data);
-    }
-  };
-  fr.readAsArrayBuffer(e.data);
-}
-
-
-SourcePeer.prototype.on = function(code, cb) {
-  // For enduser.
-  // MAKE A HASH
-  this._handlers[code] = cb;
-};
-