|
@@ -1043,11 +1043,14 @@ exports.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSes
|
|
exports.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.RTCPeerConnection;
|
|
exports.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.RTCPeerConnection;
|
|
exports.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
|
|
exports.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
|
|
var defaultConfig = {'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]};
|
|
var defaultConfig = {'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]};
|
|
|
|
+var dataCount = 1;
|
|
|
|
+
|
|
var util = {
|
|
var util = {
|
|
noop: function() {},
|
|
noop: function() {},
|
|
|
|
|
|
CLOUD_HOST: '0.peerjs.com',
|
|
CLOUD_HOST: '0.peerjs.com',
|
|
CLOUD_PORT: 9000,
|
|
CLOUD_PORT: 9000,
|
|
|
|
+ chunkedMTU: 120000, // 120KB
|
|
|
|
|
|
// Logging logic
|
|
// Logging logic
|
|
logLevel: 0,
|
|
logLevel: 0,
|
|
@@ -1126,8 +1129,8 @@ var util = {
|
|
var data = true;
|
|
var data = true;
|
|
var audioVideo = true;
|
|
var audioVideo = true;
|
|
|
|
|
|
- var binary = false;
|
|
|
|
- var reliable = false;
|
|
|
|
|
|
+ var binaryBlob = false;
|
|
|
|
+ var sctp = false;
|
|
var onnegotiationneeded = !!window.webkitRTCPeerConnection;
|
|
var onnegotiationneeded = !!window.webkitRTCPeerConnection;
|
|
|
|
|
|
var pc, dc;
|
|
var pc, dc;
|
|
@@ -1150,7 +1153,7 @@ var util = {
|
|
// Binary test
|
|
// Binary test
|
|
try {
|
|
try {
|
|
dc.binaryType = 'blob';
|
|
dc.binaryType = 'blob';
|
|
- binary = true;
|
|
|
|
|
|
+ binaryBlob = true;
|
|
} catch (e) {
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1160,7 +1163,7 @@ var util = {
|
|
var reliablePC = new RTCPeerConnection(defaultConfig, {});
|
|
var reliablePC = new RTCPeerConnection(defaultConfig, {});
|
|
try {
|
|
try {
|
|
var reliableDC = reliablePC.createDataChannel('_PEERJSRELIABLETEST', {});
|
|
var reliableDC = reliablePC.createDataChannel('_PEERJSRELIABLETEST', {});
|
|
- reliable = reliableDC.reliable;
|
|
|
|
|
|
+ sctp = reliableDC.reliable;
|
|
} catch (e) {
|
|
} catch (e) {
|
|
}
|
|
}
|
|
reliablePC.close();
|
|
reliablePC.close();
|
|
@@ -1197,8 +1200,10 @@ var util = {
|
|
return {
|
|
return {
|
|
audioVideo: audioVideo,
|
|
audioVideo: audioVideo,
|
|
data: data,
|
|
data: data,
|
|
- binary: binary,
|
|
|
|
- reliable: reliable,
|
|
|
|
|
|
+ binaryBlob: binaryBlob,
|
|
|
|
+ binary: sctp, // deprecated; sctp implies binary support.
|
|
|
|
+ reliable: sctp, // deprecated; sctp implies reliable data.
|
|
|
|
+ sctp: sctp,
|
|
onnegotiationneeded: onnegotiationneeded
|
|
onnegotiationneeded: onnegotiationneeded
|
|
};
|
|
};
|
|
}()),
|
|
}()),
|
|
@@ -1286,6 +1291,33 @@ var util = {
|
|
}(this)),
|
|
}(this)),
|
|
|
|
|
|
// Binary stuff
|
|
// Binary stuff
|
|
|
|
+
|
|
|
|
+ // chunks a blob.
|
|
|
|
+ chunk: function(bl) {
|
|
|
|
+ var chunks = [];
|
|
|
|
+ var size = bl.size;
|
|
|
|
+ var start = index = 0;
|
|
|
|
+ var total = Math.ceil(size / util.chunkedMTU);
|
|
|
|
+ while (start < size) {
|
|
|
|
+ var end = Math.min(size, start + util.chunkedMTU);
|
|
|
|
+ var b = bl.slice(start, end);
|
|
|
|
+
|
|
|
|
+ var chunk = {
|
|
|
|
+ __peerData: dataCount,
|
|
|
|
+ n: index,
|
|
|
|
+ data: b,
|
|
|
|
+ total: total
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ chunks.push(chunk);
|
|
|
|
+
|
|
|
|
+ start = end;
|
|
|
|
+ index += 1;
|
|
|
|
+ }
|
|
|
|
+ dataCount += 1;
|
|
|
|
+ return chunks;
|
|
|
|
+ },
|
|
|
|
+
|
|
blobToArrayBuffer: function(blob, cb){
|
|
blobToArrayBuffer: function(blob, cb){
|
|
var fr = new FileReader();
|
|
var fr = new FileReader();
|
|
fr.onload = function(evt) {
|
|
fr.onload = function(evt) {
|
|
@@ -1726,6 +1758,9 @@ function DataConnection(peer, provider, options) {
|
|
this.serialization = this.options.serialization;
|
|
this.serialization = this.options.serialization;
|
|
this.reliable = this.options.reliable;
|
|
this.reliable = this.options.reliable;
|
|
|
|
|
|
|
|
+ // For storing large data.
|
|
|
|
+ this._chunkedData = {};
|
|
|
|
+
|
|
Negotiator.startConnection(
|
|
Negotiator.startConnection(
|
|
this,
|
|
this,
|
|
this.options._payload || {
|
|
this.options._payload || {
|
|
@@ -1746,8 +1781,7 @@ DataConnection.prototype.initialize = function(dc) {
|
|
|
|
|
|
DataConnection.prototype._configureDataChannel = function() {
|
|
DataConnection.prototype._configureDataChannel = function() {
|
|
var self = this;
|
|
var self = this;
|
|
- if (util.supports.binary) {
|
|
|
|
- // Webkit doesn't support binary yet
|
|
|
|
|
|
+ if (util.supports.sctp) {
|
|
this._dc.binaryType = 'arraybuffer';
|
|
this._dc.binaryType = 'arraybuffer';
|
|
}
|
|
}
|
|
this._dc.onopen = function() {
|
|
this._dc.onopen = function() {
|
|
@@ -1757,7 +1791,7 @@ DataConnection.prototype._configureDataChannel = function() {
|
|
}
|
|
}
|
|
|
|
|
|
// Use the Reliable shim for non Firefox browsers
|
|
// Use the Reliable shim for non Firefox browsers
|
|
- if (!util.supports.reliable && this.reliable) {
|
|
|
|
|
|
+ if (!util.supports.sctp && this.reliable) {
|
|
this._reliable = new Reliable(this._dc, util.debug);
|
|
this._reliable = new Reliable(this._dc, util.debug);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1799,6 +1833,29 @@ DataConnection.prototype._handleDataMessage = function(e) {
|
|
} else if (this.serialization === 'json') {
|
|
} else if (this.serialization === 'json') {
|
|
data = JSON.parse(data);
|
|
data = JSON.parse(data);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // Check if we've chunked--if so, piece things back together.
|
|
|
|
+ // We're guaranteed that this isn't 0.
|
|
|
|
+ if (data.__peerData) {
|
|
|
|
+ var id = data.__peerData;
|
|
|
|
+ var chunkInfo = this._chunkedData[id] || {data: [], count: 0, total: data.total};
|
|
|
|
+
|
|
|
|
+ chunkInfo.data[data.n] = data.data;
|
|
|
|
+ chunkInfo.count += 1;
|
|
|
|
+
|
|
|
|
+ if (chunkInfo.total === chunkInfo.count) {
|
|
|
|
+ // We've received all the chunks--time to construct the complete data.
|
|
|
|
+ data = new Blob(chunkInfo.data);
|
|
|
|
+ this._handleDataMessage({data: data});
|
|
|
|
+
|
|
|
|
+ // We can also just delete the chunks now.
|
|
|
|
+ delete this._chunkedData[id];
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._chunkedData[id] = chunkInfo;
|
|
|
|
+ }
|
|
|
|
+
|
|
this.emit('data', data);
|
|
this.emit('data', data);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1817,7 +1874,7 @@ DataConnection.prototype.close = function() {
|
|
}
|
|
}
|
|
|
|
|
|
/** Allows user to send data. */
|
|
/** Allows user to send data. */
|
|
-DataConnection.prototype.send = function(data) {
|
|
|
|
|
|
+DataConnection.prototype.send = function(data, chunked) {
|
|
if (!this.open) {
|
|
if (!this.open) {
|
|
this.emit('error', new Error('Connection is not open. You should listen for the `open` event before sending messages.'));
|
|
this.emit('error', new Error('Connection is not open. You should listen for the `open` event before sending messages.'));
|
|
return;
|
|
return;
|
|
@@ -1834,11 +1891,23 @@ DataConnection.prototype.send = function(data) {
|
|
} else if ('binary-utf8'.indexOf(this.serialization) !== -1) {
|
|
} else if ('binary-utf8'.indexOf(this.serialization) !== -1) {
|
|
var utf8 = (this.serialization === 'binary-utf8');
|
|
var utf8 = (this.serialization === 'binary-utf8');
|
|
var blob = util.pack(data, utf8);
|
|
var blob = util.pack(data, utf8);
|
|
|
|
+
|
|
|
|
+ if (!chunked && blob.size > util.chunkedMTU) {
|
|
|
|
+ this._sendChunks(blob);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
// DataChannel currently only supports strings.
|
|
// DataChannel currently only supports strings.
|
|
- if (!util.supports.binary) {
|
|
|
|
- util.blobToBinaryString(blob, function(str){
|
|
|
|
|
|
+ if (!util.supports.sctp) {
|
|
|
|
+ util.blobToBinaryString(blob, function(str) {
|
|
self._dc.send(str);
|
|
self._dc.send(str);
|
|
});
|
|
});
|
|
|
|
+ } else if (!util.supports.binaryBlob) {
|
|
|
|
+ // We only do this if we really need to (e.g. blobs are not supported),
|
|
|
|
+ // because this conversion is costly.
|
|
|
|
+ util.blobToArrayBuffer(blob, function(ab) {
|
|
|
|
+ self._dc.send(ab);
|
|
|
|
+ });
|
|
} else {
|
|
} else {
|
|
this._dc.send(blob);
|
|
this._dc.send(blob);
|
|
}
|
|
}
|
|
@@ -1847,6 +1916,14 @@ DataConnection.prototype.send = function(data) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+DataConnection.prototype._sendChunks = function(blob) {
|
|
|
|
+ var blobs = util.chunk(blob);
|
|
|
|
+ for (var i = 0, ii = blobs.length; i < ii; i += 1) {
|
|
|
|
+ var blob = blobs[i];
|
|
|
|
+ this.send(blob, true);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
DataConnection.prototype.handleMessage = function(message) {
|
|
DataConnection.prototype.handleMessage = function(message) {
|
|
var payload = message.payload;
|
|
var payload = message.payload;
|
|
|
|
|
|
@@ -1984,12 +2061,12 @@ Negotiator.startConnection = function(connection, options) {
|
|
var config = {};
|
|
var config = {};
|
|
// Dropping reliable:false support, since it seems to be crashing
|
|
// Dropping reliable:false support, since it seems to be crashing
|
|
// Chrome.
|
|
// Chrome.
|
|
- /*if (util.supports.reliable && !options.reliable) {
|
|
|
|
|
|
+ /*if (util.supports.sctp && !options.reliable) {
|
|
// If we have canonical reliable support...
|
|
// If we have canonical reliable support...
|
|
config = {maxRetransmits: 0};
|
|
config = {maxRetransmits: 0};
|
|
}*/
|
|
}*/
|
|
// Fallback to ensure older browsers don't crash.
|
|
// Fallback to ensure older browsers don't crash.
|
|
- if (!util.supports.reliable) {
|
|
|
|
|
|
+ if (!util.supports.sctp) {
|
|
config = {reliable: options.reliable};
|
|
config = {reliable: options.reliable};
|
|
}
|
|
}
|
|
var dc = pc.createDataChannel(connection.label, config);
|
|
var dc = pc.createDataChannel(connection.label, config);
|
|
@@ -2055,7 +2132,7 @@ Negotiator._startPeerConnection = function(connection) {
|
|
var id = Negotiator._idPrefix + util.randomToken();
|
|
var id = Negotiator._idPrefix + util.randomToken();
|
|
var optional = {};
|
|
var optional = {};
|
|
|
|
|
|
- if (connection.type === 'data' && !util.supports.reliable) {
|
|
|
|
|
|
+ if (connection.type === 'data' && !util.supports.sctp) {
|
|
optional = {optional: [{RtpDataChannels: true}]};
|
|
optional = {optional: [{RtpDataChannels: true}]};
|
|
} else if (connection.type === 'media') {
|
|
} else if (connection.type === 'media') {
|
|
// Interop req for chrome.
|
|
// Interop req for chrome.
|
|
@@ -2156,7 +2233,7 @@ Negotiator._makeOffer = function(connection) {
|
|
pc.createOffer(function(offer) {
|
|
pc.createOffer(function(offer) {
|
|
util.log('Created offer.');
|
|
util.log('Created offer.');
|
|
|
|
|
|
- if (!util.supports.reliable && connection.type === 'data' && connection.reliable) {
|
|
|
|
|
|
+ if (!util.supports.sctp && connection.type === 'data' && connection.reliable) {
|
|
offer.sdp = Reliable.higherBandwidthSDP(offer.sdp);
|
|
offer.sdp = Reliable.higherBandwidthSDP(offer.sdp);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2172,7 +2249,7 @@ Negotiator._makeOffer = function(connection) {
|
|
serialization: connection.serialization,
|
|
serialization: connection.serialization,
|
|
metadata: connection.metadata,
|
|
metadata: connection.metadata,
|
|
connectionId: connection.id,
|
|
connectionId: connection.id,
|
|
- sctp: util.supports.reliable
|
|
|
|
|
|
+ sctp: util.supports.sctp
|
|
},
|
|
},
|
|
dst: connection.peer
|
|
dst: connection.peer
|
|
});
|
|
});
|
|
@@ -2192,7 +2269,7 @@ Negotiator._makeAnswer = function(connection) {
|
|
pc.createAnswer(function(answer) {
|
|
pc.createAnswer(function(answer) {
|
|
util.log('Created answer.');
|
|
util.log('Created answer.');
|
|
|
|
|
|
- if (!util.supports.reliable && connection.type === 'data' && connection.reliable) {
|
|
|
|
|
|
+ if (!util.supports.sctp && connection.type === 'data' && connection.reliable) {
|
|
answer.sdp = Reliable.higherBandwidthSDP(answer.sdp);
|
|
answer.sdp = Reliable.higherBandwidthSDP(answer.sdp);
|
|
}
|
|
}
|
|
|
|
|