|
@@ -4,7 +4,6 @@
|
|
*/
|
|
*/
|
|
function ConnectionManager(id, peer, socket, options) {
|
|
function ConnectionManager(id, peer, socket, options) {
|
|
if (!(this instanceof ConnectionManager)) return new ConnectionManager(id, peer, socket, options);
|
|
if (!(this instanceof ConnectionManager)) return new ConnectionManager(id, peer, socket, options);
|
|
- // TODO: need eventemitter?
|
|
|
|
EventEmitter.call(this);
|
|
EventEmitter.call(this);
|
|
|
|
|
|
options = util.extend({
|
|
options = util.extend({
|
|
@@ -22,9 +21,8 @@ function ConnectionManager(id, peer, socket, options) {
|
|
this.pc = null;
|
|
this.pc = null;
|
|
|
|
|
|
// Mapping labels to metadata and serialization.
|
|
// Mapping labels to metadata and serialization.
|
|
- this.metadatas = {};
|
|
|
|
- this.serializations = {};
|
|
|
|
- this.reliables = {};
|
|
|
|
|
|
+ // label => { metadata: ..., serialization: ..., reliable: ...}
|
|
|
|
+ this.labels = {}
|
|
|
|
|
|
// DataConnections on this PC.
|
|
// DataConnections on this PC.
|
|
this.connections = {};
|
|
this.connections = {};
|
|
@@ -62,13 +60,13 @@ ConnectionManager.prototype.initialize = function(id, socket) {
|
|
this.initialize = function() { };
|
|
this.initialize = function() { };
|
|
};
|
|
};
|
|
|
|
|
|
-// Start a PC.
|
|
|
|
|
|
+/** Start a PC. */
|
|
ConnectionManager.prototype._startPeerConnection = function() {
|
|
ConnectionManager.prototype._startPeerConnection = function() {
|
|
util.log('Creating RTCPeerConnection');
|
|
util.log('Creating RTCPeerConnection');
|
|
this.pc = new RTCPeerConnection(this._options.config, { optional: [ { rtpDataChannels: true } ]});
|
|
this.pc = new RTCPeerConnection(this._options.config, { optional: [ { rtpDataChannels: true } ]});
|
|
};
|
|
};
|
|
|
|
|
|
-// Set up ICE candidate handlers.
|
|
|
|
|
|
+/** Set up ICE candidate handlers. */
|
|
ConnectionManager.prototype._setupIce = function() {
|
|
ConnectionManager.prototype._setupIce = function() {
|
|
util.log('Listening for ICE candidates.');
|
|
util.log('Listening for ICE candidates.');
|
|
var self = this;
|
|
var self = this;
|
|
@@ -86,7 +84,7 @@ ConnectionManager.prototype._setupIce = function() {
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
-// Set up onnegotiationneeded.
|
|
|
|
|
|
+/** Set up onnegotiationneeded. */
|
|
ConnectionManager.prototype._setupNegotiationHandler = function() {
|
|
ConnectionManager.prototype._setupNegotiationHandler = function() {
|
|
var self = this;
|
|
var self = this;
|
|
util.log('Listening for `negotiationneeded`');
|
|
util.log('Listening for `negotiationneeded`');
|
|
@@ -96,16 +94,22 @@ ConnectionManager.prototype._setupNegotiationHandler = function() {
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
-// Set up Data Channel listener.
|
|
|
|
|
|
+/** Set up Data Channel listener. */
|
|
ConnectionManager.prototype._setupDataChannel = function() {
|
|
ConnectionManager.prototype._setupDataChannel = function() {
|
|
var self = this;
|
|
var self = this;
|
|
- this._pc.ondatachannel = function(evt) {
|
|
|
|
|
|
+ this.pc.ondatachannel = function(evt) {
|
|
util.log('Received data channel');
|
|
util.log('Received data channel');
|
|
// TODO: Create DataConnection object.
|
|
// TODO: Create DataConnection object.
|
|
|
|
+ var dc = evt.channel;
|
|
|
|
+ var label = dc.label;
|
|
|
|
+ var options = self.labels[label] || {};
|
|
|
|
+ var connection = new DataConnection(dc, options);
|
|
|
|
+ self.connections[label] = connection;
|
|
|
|
+ self.emit('connection', connection);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
-// Send an offer.
|
|
|
|
|
|
+/** Send an offer. */
|
|
ConnectionManager.prototype._makeOffer = function() {
|
|
ConnectionManager.prototype._makeOffer = function() {
|
|
var self = this;
|
|
var self = this;
|
|
this.pc.createOffer(function(offer) {
|
|
this.pc.createOffer(function(offer) {
|
|
@@ -116,12 +120,12 @@ ConnectionManager.prototype._makeOffer = function() {
|
|
type: 'OFFER',
|
|
type: 'OFFER',
|
|
payload: {
|
|
payload: {
|
|
sdp: offer,
|
|
sdp: offer,
|
|
- serialization: self.serializations,
|
|
|
|
- metadata: self.metadatas,
|
|
|
|
- reliable: self.reliables
|
|
|
|
|
|
+ labels: self.labels
|
|
},
|
|
},
|
|
dst: self.peer
|
|
dst: self.peer
|
|
});
|
|
});
|
|
|
|
+ // We can now reset labels because all info has been communicated.
|
|
|
|
+ self.labels = {};
|
|
}, function(err) {
|
|
}, function(err) {
|
|
self.emit('error', err);
|
|
self.emit('error', err);
|
|
util.log('Failed to setLocalDescription, ', err);
|
|
util.log('Failed to setLocalDescription, ', err);
|
|
@@ -129,7 +133,7 @@ ConnectionManager.prototype._makeOffer = function() {
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
-// Create an answer for PC.
|
|
|
|
|
|
+/** Create an answer for PC. */
|
|
ConnectionManager.prototype._makeAnswer = function() {
|
|
ConnectionManager.prototype._makeAnswer = function() {
|
|
var self = this;
|
|
var self = this;
|
|
this.pc.createAnswer(function(answer) {
|
|
this.pc.createAnswer(function(answer) {
|
|
@@ -153,13 +157,39 @@ ConnectionManager.prototype._makeAnswer = function() {
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
-// Clean up PC, close related DCs.
|
|
|
|
|
|
+/** Clean up PC, close related DCs. */
|
|
ConnectionManager.prototype._cleanup = function() {
|
|
ConnectionManager.prototype._cleanup = function() {
|
|
|
|
+ util.log('Cleanup ConnectionManager for ' + this.peer);
|
|
|
|
|
|
-}
|
|
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** Handle an SDP. */
|
|
|
|
+ConnectionManager.prototype.handleSDP = function(sdp, type) {
|
|
|
|
+ sdp = new RTCSessionDescription(sdp);
|
|
|
|
+
|
|
|
|
+ var self = this;
|
|
|
|
+ this.pc.setRemoteDescription(sdp, function() {
|
|
|
|
+ util.log('Set remoteDescription: ' + type);
|
|
|
|
+ if (type === 'OFFER') {
|
|
|
|
+ self._makeAnswer();
|
|
|
|
+ }
|
|
|
|
+ }, function(err) {
|
|
|
|
+ self.emit('error', err);
|
|
|
|
+ util.log('Failed to setRemoteDescription, ', err);
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
|
|
-ConnectionManager.prototype.handleSDP = function(sdp) {
|
|
|
|
|
|
+/** Handle a candidate. */
|
|
|
|
+ConnectionManager.prototype.handleCandidate = function(message) {
|
|
|
|
+ var candidate = new RTCIceCandidate(message.candidate);
|
|
|
|
+ this.pc.addIceCandidate(candidate);
|
|
|
|
+ util.log('Added ICE candidate.');
|
|
|
|
+};
|
|
|
|
|
|
|
|
+/** Handle peer leaving. */
|
|
|
|
+ConnectionManager.prototype.handleLeave = function() {
|
|
|
|
+ util.log('Peer ' + this.peer + ' disconnected.');
|
|
|
|
+ this.close();
|
|
};
|
|
};
|
|
|
|
|
|
/** Closes manager and all related connections. */
|
|
/** Closes manager and all related connections. */
|
|
@@ -177,6 +207,30 @@ ConnectionManager.prototype.close = function() {
|
|
};
|
|
};
|
|
|
|
|
|
/** Create and returns a DataConnection with the peer with the given label. */
|
|
/** Create and returns a DataConnection with the peer with the given label. */
|
|
-ConnectionManager.prototype.connect = function(label) {
|
|
|
|
|
|
+// TODO: queue in case no ID/PC.
|
|
|
|
+ConnectionManager.prototype.connect = function(label, options) {
|
|
|
|
+ options = util.extend({
|
|
|
|
+ reliable: false,
|
|
|
|
+ serialization: 'binary'
|
|
|
|
+ }, options);
|
|
|
|
|
|
|
|
+ this.labels[label] = {
|
|
|
|
+ reliable: options.reliable,
|
|
|
|
+ serialization: options.serialization,
|
|
|
|
+ metadata: options.metadata
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var connection = new DataConnection(this.pc.createDataChannel(this.peer, { reliable: false }), options);
|
|
|
|
+ this.connections[label] = connection;
|
|
|
|
+ return connection;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** Updates label:[serialization, reliable, metadata] pairs from offer. */
|
|
|
|
+// TODO: queue in case no ID/PC.
|
|
|
|
+ConnectionManager.prototype.update = 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];
|
|
|
|
+ }
|
|
};
|
|
};
|