rtcmulticonnection-v3
Version:
RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.
378 lines (311 loc) • 12.4 kB
JavaScript
// RTCPeerConnection.js
var defaults = {};
function setSdpConstraints(config) {
var sdpConstraints;
var sdpConstraints_mandatory = {
OfferToReceiveAudio: !!config.OfferToReceiveAudio,
OfferToReceiveVideo: !!config.OfferToReceiveVideo
};
sdpConstraints = {
mandatory: sdpConstraints_mandatory,
optional: [{
VoiceActivityDetection: false
}]
};
if (!!navigator.mozGetUserMedia && firefoxVersion > 34) {
sdpConstraints = {
OfferToReceiveAudio: !!config.OfferToReceiveAudio,
OfferToReceiveVideo: !!config.OfferToReceiveVideo
};
}
return sdpConstraints;
}
var RTCPeerConnection;
if (typeof mozRTCPeerConnection !== 'undefined') {
RTCPeerConnection = mozRTCPeerConnection;
} else if (typeof webkitRTCPeerConnection !== 'undefined') {
RTCPeerConnection = webkitRTCPeerConnection;
} else if (typeof window.RTCPeerConnection !== 'undefined') {
RTCPeerConnection = window.RTCPeerConnection;
} else {
console.error('WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.');
RTCPeerConnection = window.RTCSessionDescription = window.RTCIceCandidate = function() {};
}
var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription;
var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate;
var MediaStreamTrack = window.MediaStreamTrack;
var Plugin = {};
function onPluginRTCInitialized(pluginRTCObject) {
Plugin = pluginRTCObject;
MediaStreamTrack = Plugin.MediaStreamTrack;
RTCPeerConnection = Plugin.RTCPeerConnection;
RTCIceCandidate = Plugin.RTCIceCandidate;
RTCSessionDescription = Plugin.RTCSessionDescription;
}
if (typeof PluginRTC !== 'undefined') onPluginRTCInitialized(PluginRTC);
function PeerInitiator(config) {
var connection = config.rtcMultiConnection;
this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra;
this.userid = config.userid;
this.streams = [];
this.channels = [];
this.connectionDescription = config.connectionDescription;
var that = this;
if (config.remoteSdp) {
this.connectionDescription = config.remoteSdp.connectionDescription;
}
var allRemoteStreams = {};
if (Object.observe) {
var that = this;
Object.observe(this.channels, function(changes) {
changes.forEach(function(change) {
if (change.type === 'add') {
change.object[change.name].addEventListener('close', function() {
delete that.channels[that.channels.indexOf(change.object[change.name])];
that.channels = removeNullEntries(that.channels);
}, false);
}
if (change.type === 'remove' || change.type === 'delete') {
if (that.channels.indexOf(change.object[change.name]) !== -1) {
delete that.channels.indexOf(change.object[change.name]);
}
}
that.channels = removeNullEntries(that.channels);
});
});
}
defaults.sdpConstraints = setSdpConstraints({
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
});
var peer;
var renegotiatingPeer = !!config.renegotiatingPeer;
if (config.remoteSdp) {
renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer;
}
var localStreams = [];
connection.attachStreams.forEach(function(stream) {
if (!!stream) localStreams.push(stream);
});
if (!renegotiatingPeer) {
peer = new RTCPeerConnection(navigator.onLine ? {
iceServers: connection.iceServers,
iceTransports: 'all'
} : null, connection.optionalArgument);
} else {
peer = config.peerRef;
peer.getLocalStreams().forEach(function(stream) {
localStreams.forEach(function(localStream, index) {
if (stream == localStream) {
delete localStreams[index];
}
});
connection.removeStreams.forEach(function(streamToRemove, index) {
if (stream === streamToRemove) {
stream = connection.beforeRemovingStream(stream);
if (stream && !!peer.removeStream) {
peer.removeStream(stream);
}
localStreams.forEach(function(localStream, index) {
if (streamToRemove == localStream) {
delete localStreams[index];
}
});
}
});
});
}
if (connection.DetectRTC.browser.name === 'Firefox') {
peer.removeStream = function(stream) {
stream.mute();
connection.StreamsHandler.onSyncNeeded(stream.streamid, 'stream-removed');
};
}
peer.onicecandidate = function(event) {
if (!event.candidate) return;
config.onLocalCandidate({
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex
});
};
var isFirefoxOffered = !isFirefox;
if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered) {
isFirefoxOffered = true;
}
localStreams.forEach(function(localStream) {
if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) {
return;
}
if (config.dontAttachLocalStream) {
return;
}
localStream = connection.beforeAddingStream(localStream);
if (localStream) {
peer.addStream(localStream);
}
});
peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() {
var extra = that.extra;
if (connection.peers[that.userid]) {
extra = connection.peers[that.userid].extra || extra;
}
if (!peer) {
return;
}
config.onPeerStateChanged({
iceConnectionState: peer.iceConnectionState,
iceGatheringState: peer.iceGatheringState,
signalingState: peer.signalingState,
extra: extra,
userid: that.userid
});
};
var sdpConstraints = {
OfferToReceiveAudio: !!localStreams.length,
OfferToReceiveVideo: !!localStreams.length
};
if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints;
defaults.sdpConstraints = setSdpConstraints(sdpConstraints);
peer.onaddstream = function(event) {
var streamsToShare = {};
if (config.remoteSdp && config.remoteSdp.streamsToShare) {
streamsToShare = config.remoteSdp.streamsToShare;
} else if (config.streamsToShare) {
streamsToShare = config.streamsToShare;
}
var streamToShare = streamsToShare[event.stream.id];
if (streamToShare) {
event.stream.isAudio = streamToShare.isAudio;
event.stream.isVideo = streamToShare.isVideo;
event.stream.isScreen = streamToShare.isScreen;
}
event.stream.streamid = event.stream.id;
if (!event.stream.stop) {
event.stream.stop = function() {
if (isFirefox) {
fireEvent(this, 'ended');
}
};
}
allRemoteStreams[event.stream.id] = event.stream;
config.onRemoteStream(event.stream);
};
peer.onremovestream = function(event) {
event.stream.streamid = event.stream.id;
if (allRemoteStreams[event.stream.id]) {
delete allRemoteStreams[event.stream.id];
}
config.onRemoteStreamRemoved(event.stream);
};
this.addRemoteCandidate = function(remoteCandidate) {
peer.addIceCandidate(new RTCIceCandidate(remoteCandidate));
};
this.addRemoteSdp = function(remoteSdp) {
remoteSdp.sdp = connection.processSdp(remoteSdp.sdp);
peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), function() {}, function(error) {
if (!!connection.enableLogs) {
console.error(JSON.stringify(error, null, '\t'));
}
});
};
var isOfferer = true;
if (config.remoteSdp) {
isOfferer = false;
}
if (connection.session.data === true) {
createDataChannel();
}
if (config.remoteSdp) {
if (config.remoteSdp.remotePeerSdpConstraints) {
sdpConstraints = config.remoteSdp.remotePeerSdpConstraints;
}
defaults.sdpConstraints = setSdpConstraints(sdpConstraints);
this.addRemoteSdp(config.remoteSdp);
}
function createDataChannel() {
if (!isOfferer) {
peer.ondatachannel = function(event) {
var channel = event.channel;
setChannelEvents(channel);
};
return;
}
var channel = peer.createDataChannel('RTCDataChannel', {});
setChannelEvents(channel);
}
function setChannelEvents(channel) {
// force ArrayBuffer in Firefox; which uses "Blob" by default.
channel.binaryType = 'arraybuffer';
channel.onmessage = function(event) {
config.onDataChannelMessage(event.data);
};
channel.onopen = function() {
config.onDataChannelOpened(channel);
};
channel.onerror = function(error) {
config.onDataChannelError(error);
};
channel.onclose = function(event) {
config.onDataChannelClosed(event);
};
channel.internalSend = channel.send;
channel.send = function(data) {
if (channel.readyState !== 'open') {
return;
}
channel.internalSend(data);
};
peer.channel = channel;
}
if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') {
defaults.sdpConstraints = setSdpConstraints({
OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio),
OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio)
});
}
var streamsToShare = {};
peer.getLocalStreams().forEach(function(stream) {
streamsToShare[stream.streamid] = {
isAudio: !!stream.isAudio,
isVideo: !!stream.isVideo,
isScreen: !!stream.isScreen
};
});
peer[isOfferer ? 'createOffer' : 'createAnswer'](function(localSdp) {
localSdp.sdp = connection.processSdp(localSdp.sdp);
peer.setLocalDescription(localSdp);
config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || false,
renegotiatingPeer: !!config.renegotiatingPeer || false,
connectionDescription: that.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare,
isFirefoxOffered: isFirefox
});
}, function(error) {
if (!!connection.enableLogs) {
console.error('sdp-error', error);
}
}, defaults.sdpConstraints);
peer.nativeClose = peer.close;
peer.close = function() {
if (!peer) {
return;
}
try {
if (peer.iceConnectionState.search(/closed|failed/gi) === -1) {
peer.getRemoteStreams().forEach(function(stream) {
stream.stop();
});
}
peer.nativeClose();
} catch (e) {}
peer = null;
that.peer = null;
};
this.peer = peer;
}