rtcpeerconnection
Advanced tools
Comparing version 1.1.1 to 2.0.0
{ | ||
"name": "rtcpeerconnection", | ||
"version": "1.1.1", | ||
"description": "A tiny browser module that gives normalizes and simplifies the API for WebRTC peer connections.", | ||
"version": "2.0.0", | ||
"description": "A tiny browser module that normalizes and simplifies the API for WebRTC peer connections.", | ||
"main": "rtcpeerconnection.js", | ||
@@ -13,3 +13,4 @@ "repository": { | ||
"RTCPeerConnection", | ||
"WebRTC" | ||
"WebRTC", | ||
"Jingle" | ||
], | ||
@@ -19,11 +20,23 @@ "author": "Henrik Joreteg <henrik@andyet.net>", | ||
"dependencies": { | ||
"wildemitter": "0.0.5", | ||
"webrtcsupport": "0.7.1" | ||
"wildemitter": "1.x", | ||
"webrtcsupport": "0.7.x", | ||
"traceablepeerconnection": "0.1.x", | ||
"sdp-jingle-json": "1.x", | ||
"underscore": "1.x" | ||
}, | ||
"devDependencies": { | ||
"browserify": "2.x" | ||
"browserify": "2.x", | ||
"precommit-hook": "0.3.x", | ||
"tape": "2.x" | ||
}, | ||
"jshintConfig": { | ||
"expr": true | ||
"testling": { | ||
"files": "test/*.js", | ||
"browsers": [ | ||
"ie/10..latest", | ||
"chrome/33..latest", | ||
"firefox/24..latest", | ||
"safari/latest", | ||
"opera/18..latest" | ||
] | ||
} | ||
} |
@@ -119,19 +119,28 @@ # RTCPeerConnection | ||
connection.on('offer', function (offer) { | ||
// you can just call answer | ||
pc.answer(offer, function (err, answer) { | ||
if (!err) connection.send('answer', answer); | ||
}); | ||
// let the peerconnection handle the offer | ||
// by calling handleOffer | ||
pc.handleOffer(offer, function (err) { | ||
if (err) { | ||
// handle error | ||
return; | ||
} | ||
// you can call answer with contstraints | ||
pc.answer(offer, MY_CONSTRAINTS, function (err, answer) { | ||
if (!err) connection.send('answer', answer); | ||
}); | ||
// you can just call answer | ||
pc.answer(function (err, answer) { | ||
if (!err) connection.send('answer', answer); | ||
}); | ||
// or you can use one of the shortcuts answers | ||
// you can call answer with contstraints | ||
pc.answer(MY_CONSTRAINTS, function (err, answer) { | ||
if (!err) connection.send('answer', answer); | ||
}); | ||
// for video only | ||
pc.answerVideoOnly(offer, function (err, answer) { ... }); | ||
// or you can use one of the shortcuts answers | ||
// and audio only | ||
pc.answerAudioOnly(offer, function (err, answer) { ... }); | ||
// for video only | ||
pc.answerVideoOnly(function (err, answer) { ... }); | ||
// and audio only | ||
pc.answerAudioOnly(function (err, answer) { ... }); | ||
}); | ||
}); | ||
@@ -138,0 +147,0 @@ |
@@ -0,10 +1,22 @@ | ||
var _ = require('underscore'); | ||
var util = require('util'); | ||
var webrtc = require('webrtcsupport'); | ||
var SJJ = require('sdp-jingle-json'); | ||
var WildEmitter = require('wildemitter'); | ||
var webrtc = require('webrtcsupport'); | ||
var peerconn = require('traceablepeerconnection'); | ||
function PeerConnection(config, constraints) { | ||
var self = this; | ||
var item; | ||
this.pc = new webrtc.PeerConnection(config, constraints); | ||
WildEmitter.call(this); | ||
config = config || {}; | ||
config.iceServers = config.iceServers || []; | ||
this.pc = new peerconn(config, constraints); | ||
// proxy events | ||
this.pc.on('*', function () { | ||
self.emit.apply(self, arguments); | ||
}); | ||
// proxy some events directly | ||
@@ -21,6 +33,19 @@ this.pc.onremovestream = this.emit.bind(this, 'removeStream'); | ||
// whether to use SDP hack for faster data transfer | ||
this.localDescription = { | ||
contents: [] | ||
}; | ||
this.remoteDescription = { | ||
contents: [] | ||
}; | ||
this.localStream = null; | ||
this.remoteStreams = []; | ||
this.config = { | ||
debug: false, | ||
sdpHack: true | ||
ice: {}, | ||
sid: '', | ||
isInitiator: true, | ||
sdpSessionID: Date.now(), | ||
useJingle: false | ||
}; | ||
@@ -41,8 +66,5 @@ | ||
PeerConnection.prototype = Object.create(WildEmitter.prototype, { | ||
constructor: { | ||
value: PeerConnection | ||
} | ||
}); | ||
util.inherits(PeerConnection, WildEmitter); | ||
// Add a stream to the peer connection object | ||
@@ -56,4 +78,40 @@ PeerConnection.prototype.addStream = function (stream) { | ||
// Init and add ice candidate object with correct constructor | ||
PeerConnection.prototype.processIce = function (candidate) { | ||
this.pc.addIceCandidate(new webrtc.IceCandidate(candidate)); | ||
PeerConnection.prototype.processIce = function (update, cb) { | ||
cb = cb || function () {}; | ||
var self = this; | ||
if (update.contents) { | ||
var contentNames = _.pluck(this.remoteDescription.contents, 'name'); | ||
var contents = update.contents; | ||
contents.forEach(function (content) { | ||
var transport = content.transport || {}; | ||
var candidates = transport.candidates || []; | ||
var mline = contentNames.indexOf(content.name); | ||
var mid = content.name; | ||
candidates.forEach(function (candidate) { | ||
console.log('addicecandidate'); | ||
var iceCandidate = SJJ.toCandidateSDP(candidate) + '\r\n'; | ||
self.pc.addIceCandidate(new webrtc.IceCandidate({ | ||
candidate: iceCandidate, | ||
sdpMLineIndex: mline, | ||
sdpMid: mid | ||
}) | ||
/* not yet, breaks Chrome M32 */ | ||
/* | ||
, function () { | ||
// well, this success callback is pretty meaningless | ||
}, | ||
function (err) { | ||
self.emit('error', err); | ||
} | ||
*/ | ||
); | ||
}); | ||
}); | ||
} else { | ||
self.pc.addIceCandidate(new webrtc.IceCandidate(update.candidate)); | ||
} | ||
cb(); | ||
}; | ||
@@ -71,3 +129,4 @@ | ||
}; | ||
var callback = hasConstraints ? cb : constraints; | ||
cb = hasConstraints ? cb : constraints; | ||
cb = cb || function () {}; | ||
@@ -77,10 +136,40 @@ // Actually generate the offer | ||
function (offer) { | ||
offer.sdp = self._applySdpHack(offer.sdp); | ||
self.pc.setLocalDescription(offer); | ||
self.emit('offer', offer); | ||
if (callback) callback(null, offer); | ||
self.pc.setLocalDescription(offer, | ||
function () { | ||
var jingle; | ||
var expandedOffer = { | ||
type: 'offer', | ||
sdp: offer.sdp | ||
}; | ||
if (self.config.useJingle) { | ||
jingle = SJJ.toSessionJSON(offer.sdp, self.config.isInitiator ? 'initiator' : 'responder'); | ||
jingle.sid = self.config.sid; | ||
self.localDescription = jingle; | ||
// Save ICE credentials | ||
_.each(jingle.contents, function (content) { | ||
var transport = content.transport || {}; | ||
if (transport.ufrag) { | ||
self.config.ice[content.name] = { | ||
ufrag: transport.ufrag, | ||
pwd: transport.pwd | ||
}; | ||
} | ||
}); | ||
expandedOffer.jingle = jingle; | ||
} | ||
self.emit('offer', expandedOffer); | ||
cb(null, expandedOffer); | ||
}, | ||
function (err) { | ||
self.emit('error', err); | ||
cb(err); | ||
} | ||
); | ||
}, | ||
function (err) { | ||
self.emit('error', err); | ||
if (callback) callback(err); | ||
cb(err); | ||
}, | ||
@@ -91,4 +180,19 @@ mediaConstraints | ||
// Process an incoming offer so that ICE may proceed before deciding | ||
// to answer the request. | ||
PeerConnection.prototype.handleOffer = function (offer, cb) { | ||
cb = cb || function () {}; | ||
var self = this; | ||
offer.type = 'offer'; | ||
if (offer.jingle) { | ||
offer.sdp = SJJ.toSessionSDP(offer.jingle, self.config.sdpSessionID); | ||
} | ||
self.pc.setRemoteDescription(new webrtc.SessionDescription(offer), function () { | ||
cb(); | ||
}, cb); | ||
}; | ||
// Answer an offer with audio only | ||
PeerConnection.prototype.answerAudioOnly = function (offer, cb) { | ||
PeerConnection.prototype.answerAudioOnly = function (cb) { | ||
var mediaConstraints = { | ||
@@ -100,7 +204,7 @@ mandatory: { | ||
}; | ||
this._answer(offer, mediaConstraints, cb); | ||
this._answer(mediaConstraints, cb); | ||
}; | ||
// Answer an offer without offering to recieve | ||
PeerConnection.prototype.answerBroadcastOnly = function (offer, cb) { | ||
PeerConnection.prototype.answerBroadcastOnly = function (cb) { | ||
var mediaConstraints = { | ||
@@ -112,9 +216,9 @@ mandatory: { | ||
}; | ||
this._answer(offer, mediaConstraints, cb); | ||
this._answer(mediaConstraints, cb); | ||
}; | ||
// Answer an offer with given constraints default is audio/video | ||
PeerConnection.prototype.answer = function (offer, constraints, cb) { | ||
PeerConnection.prototype.answer = function (constraints, cb) { | ||
var self = this; | ||
var hasConstraints = arguments.length === 3; | ||
var hasConstraints = arguments.length === 2; | ||
var callback = hasConstraints ? cb : constraints; | ||
@@ -128,8 +232,20 @@ var mediaConstraints = hasConstraints ? constraints : { | ||
this._answer(offer, mediaConstraints, callback); | ||
this._answer(mediaConstraints, callback); | ||
}; | ||
// Process an answer | ||
PeerConnection.prototype.handleAnswer = function (answer) { | ||
this.pc.setRemoteDescription(new webrtc.SessionDescription(answer)); | ||
PeerConnection.prototype.handleAnswer = function (answer, cb) { | ||
cb = cb || function () {}; | ||
var self = this; | ||
if (answer.jingle) { | ||
answer.sdp = SJJ.toSessionSDP(answer.jingle, self.config.sdpSessionID); | ||
self.remoteDescription = answer.jingle; | ||
} | ||
self.pc.setRemoteDescription( | ||
new webrtc.SessionDescription(answer), | ||
function () { | ||
cb(null); | ||
}, | ||
cb | ||
); | ||
}; | ||
@@ -144,14 +260,35 @@ | ||
// Internal code sharing for various types of answer methods | ||
PeerConnection.prototype._answer = function (offer, constraints, cb) { | ||
PeerConnection.prototype._answer = function (constraints, cb) { | ||
cb = cb || function () {}; | ||
var self = this; | ||
this.pc.setRemoteDescription(new webrtc.SessionDescription(offer)); | ||
this.pc.createAnswer( | ||
if (!this.pc.remoteDescription) { | ||
// the old API is used, call handleOffer | ||
throw new Error('remoteDescription not set'); | ||
} | ||
self.pc.createAnswer( | ||
function (answer) { | ||
answer.sdp = self._applySdpHack(answer.sdp); | ||
self.pc.setLocalDescription(answer); | ||
self.emit('answer', answer); | ||
if (cb) cb(null, answer); | ||
}, function (err) { | ||
self.pc.setLocalDescription(answer, | ||
function () { | ||
var expandedAnswer = { | ||
type: 'answer', | ||
sdp: answer.sdp | ||
}; | ||
if (self.config.useJingle) { | ||
var jingle = SJJ.toSessionJSON(answer.sdp); | ||
jingle.sid = self.config.sid; | ||
self.localDescription = jingle; | ||
expandedAnswer.jingle = jingle; | ||
} | ||
self.emit('answer', expandedAnswer); | ||
cb(null, expandedAnswer); | ||
}, | ||
function (err) { | ||
self.emit('error', err); | ||
cb(err); | ||
} | ||
); | ||
}, | ||
function (err) { | ||
self.emit('error', err); | ||
if (cb) cb(err); | ||
cb(err); | ||
}, | ||
@@ -164,4 +301,40 @@ constraints | ||
PeerConnection.prototype._onIce = function (event) { | ||
var self = this; | ||
if (event.candidate) { | ||
this.emit('ice', event.candidate); | ||
var ice = event.candidate; | ||
var expandedCandidate = { | ||
candidate: event.candidate | ||
}; | ||
if (self.config.useJingle) { | ||
if (!self.config.ice[ice.sdpMid]) { | ||
var jingle = SJJ.toSessionJSON(self.pc.localDescription.sdp, self.config.isInitiator ? 'initiator' : 'responder'); | ||
_.each(jingle.contents, function (content) { | ||
var transport = content.transport || {}; | ||
if (transport.ufrag) { | ||
self.config.ice[content.name] = { | ||
ufrag: transport.ufrag, | ||
pwd: transport.pwd | ||
}; | ||
} | ||
}); | ||
} | ||
expandedCandidate.jingle = { | ||
contents: [{ | ||
name: ice.sdpMid, | ||
creator: self.config.isInitiator ? 'initiator' : 'responder', | ||
transport: { | ||
transType: 'iceUdp', | ||
ufrag: self.config.ice[ice.sdpMid].ufrag, | ||
pwd: self.config.ice[ice.sdpMid].pwd, | ||
candidates: [ | ||
SJJ.toCandidateJSON(ice.candidate) | ||
] | ||
} | ||
}] | ||
}; | ||
} | ||
this.emit('ice', expandedCandidate); | ||
} else { | ||
@@ -180,50 +353,10 @@ this.emit('endOfCandidates'); | ||
PeerConnection.prototype._onAddStream = function (event) { | ||
this.remoteStream = event.stream; | ||
this.remoteStreams.push(event.stream); | ||
this.emit('addStream', event); | ||
}; | ||
// SDP hack for increasing AS (application specific) data transfer speed allowed in chrome | ||
PeerConnection.prototype._applySdpHack = function (sdp) { | ||
if (!this.config.sdpHack) return sdp; | ||
var parts = sdp.split('b=AS:30'); | ||
if (parts.length === 2) { | ||
// increase max data transfer bandwidth to 100 Mbps | ||
return parts[0] + 'b=AS:102400' + parts[1]; | ||
} else { | ||
return sdp; | ||
} | ||
}; | ||
// Create a data channel spec reference: | ||
// http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCDataChannelInit | ||
PeerConnection.prototype.createDataChannel = function (name, opts) { | ||
opts || (opts = {}); | ||
var reliable = !!opts.reliable; | ||
var protocol = opts.protocol || 'text/plain'; | ||
var negotiated = !!(opts.negotiated || opts.preset); | ||
var settings; | ||
var channel; | ||
// firefox is a bit more finnicky | ||
if (webrtc.prefix === 'moz') { | ||
if (reliable) { | ||
settings = { | ||
protocol: protocol, | ||
preset: negotiated, | ||
stream: name | ||
}; | ||
} else { | ||
settings = {}; | ||
} | ||
channel = this.pc.createDataChannel(name, settings); | ||
channel.binaryType = 'blob'; | ||
} else { | ||
if (reliable) { | ||
settings = { | ||
reliable: true | ||
}; | ||
} else { | ||
settings = {reliable: false}; | ||
} | ||
channel = this.pc.createDataChannel(name, settings); | ||
} | ||
var channel = this.pc.createDataChannel(name, opts); | ||
return channel; | ||
@@ -230,0 +363,0 @@ }; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
130817
10
3478
183
5
3
3
+ Addedsdp-jingle-json@1.x
+ Addedunderscore@1.x
+ Addedsdp-jingle-json@1.3.0(transitive)
+ Addedtraceablepeerconnection@0.1.5(transitive)
+ Addedunderscore@1.13.7(transitive)
+ Addedwebrtcsupport@0.7.3(transitive)
+ Addedwildemitter@1.2.1(transitive)
- Removedwebrtcsupport@0.7.1(transitive)
- Removedwildemitter@0.0.5(transitive)
Updatedwebrtcsupport@0.7.x
Updatedwildemitter@1.x