354 lines
12 KiB
JavaScript
Executable File
354 lines
12 KiB
JavaScript
Executable File
/*
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree.
|
|
*/
|
|
'use strict';
|
|
import * as utils from '../utils';
|
|
|
|
export function shimLocalStreamsAPI(window) {
|
|
if (typeof window !== 'object' || !window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
|
|
window.RTCPeerConnection.prototype.getLocalStreams =
|
|
function getLocalStreams() {
|
|
if (!this._localStreams) {
|
|
this._localStreams = [];
|
|
}
|
|
return this._localStreams;
|
|
};
|
|
}
|
|
if (!('addStream' in window.RTCPeerConnection.prototype)) {
|
|
const _addTrack = window.RTCPeerConnection.prototype.addTrack;
|
|
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
|
|
if (!this._localStreams) {
|
|
this._localStreams = [];
|
|
}
|
|
if (!this._localStreams.includes(stream)) {
|
|
this._localStreams.push(stream);
|
|
}
|
|
// Try to emulate Chrome's behaviour of adding in audio-video order.
|
|
// Safari orders by track id.
|
|
stream.getAudioTracks().forEach(track => _addTrack.call(this, track,
|
|
stream));
|
|
stream.getVideoTracks().forEach(track => _addTrack.call(this, track,
|
|
stream));
|
|
};
|
|
|
|
window.RTCPeerConnection.prototype.addTrack =
|
|
function addTrack(track, ...streams) {
|
|
if (streams) {
|
|
streams.forEach((stream) => {
|
|
if (!this._localStreams) {
|
|
this._localStreams = [stream];
|
|
} else if (!this._localStreams.includes(stream)) {
|
|
this._localStreams.push(stream);
|
|
}
|
|
});
|
|
}
|
|
return _addTrack.apply(this, arguments);
|
|
};
|
|
}
|
|
if (!('removeStream' in window.RTCPeerConnection.prototype)) {
|
|
window.RTCPeerConnection.prototype.removeStream =
|
|
function removeStream(stream) {
|
|
if (!this._localStreams) {
|
|
this._localStreams = [];
|
|
}
|
|
const index = this._localStreams.indexOf(stream);
|
|
if (index === -1) {
|
|
return;
|
|
}
|
|
this._localStreams.splice(index, 1);
|
|
const tracks = stream.getTracks();
|
|
this.getSenders().forEach(sender => {
|
|
if (tracks.includes(sender.track)) {
|
|
this.removeTrack(sender);
|
|
}
|
|
});
|
|
};
|
|
}
|
|
}
|
|
|
|
export function shimRemoteStreamsAPI(window) {
|
|
if (typeof window !== 'object' || !window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
|
|
window.RTCPeerConnection.prototype.getRemoteStreams =
|
|
function getRemoteStreams() {
|
|
return this._remoteStreams ? this._remoteStreams : [];
|
|
};
|
|
}
|
|
if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
|
|
Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
|
|
get() {
|
|
return this._onaddstream;
|
|
},
|
|
set(f) {
|
|
if (this._onaddstream) {
|
|
this.removeEventListener('addstream', this._onaddstream);
|
|
this.removeEventListener('track', this._onaddstreampoly);
|
|
}
|
|
this.addEventListener('addstream', this._onaddstream = f);
|
|
this.addEventListener('track', this._onaddstreampoly = (e) => {
|
|
e.streams.forEach(stream => {
|
|
if (!this._remoteStreams) {
|
|
this._remoteStreams = [];
|
|
}
|
|
if (this._remoteStreams.includes(stream)) {
|
|
return;
|
|
}
|
|
this._remoteStreams.push(stream);
|
|
const event = new Event('addstream');
|
|
event.stream = stream;
|
|
this.dispatchEvent(event);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
const origSetRemoteDescription =
|
|
window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
window.RTCPeerConnection.prototype.setRemoteDescription =
|
|
function setRemoteDescription() {
|
|
const pc = this;
|
|
if (!this._onaddstreampoly) {
|
|
this.addEventListener('track', this._onaddstreampoly = function(e) {
|
|
e.streams.forEach(stream => {
|
|
if (!pc._remoteStreams) {
|
|
pc._remoteStreams = [];
|
|
}
|
|
if (pc._remoteStreams.indexOf(stream) >= 0) {
|
|
return;
|
|
}
|
|
pc._remoteStreams.push(stream);
|
|
const event = new Event('addstream');
|
|
event.stream = stream;
|
|
pc.dispatchEvent(event);
|
|
});
|
|
});
|
|
}
|
|
return origSetRemoteDescription.apply(pc, arguments);
|
|
};
|
|
}
|
|
}
|
|
|
|
export function shimCallbacksAPI(window) {
|
|
if (typeof window !== 'object' || !window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
const prototype = window.RTCPeerConnection.prototype;
|
|
const origCreateOffer = prototype.createOffer;
|
|
const origCreateAnswer = prototype.createAnswer;
|
|
const setLocalDescription = prototype.setLocalDescription;
|
|
const setRemoteDescription = prototype.setRemoteDescription;
|
|
const addIceCandidate = prototype.addIceCandidate;
|
|
|
|
prototype.createOffer =
|
|
function createOffer(successCallback, failureCallback) {
|
|
const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
|
|
const promise = origCreateOffer.apply(this, [options]);
|
|
if (!failureCallback) {
|
|
return promise;
|
|
}
|
|
promise.then(successCallback, failureCallback);
|
|
return Promise.resolve();
|
|
};
|
|
|
|
prototype.createAnswer =
|
|
function createAnswer(successCallback, failureCallback) {
|
|
const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
|
|
const promise = origCreateAnswer.apply(this, [options]);
|
|
if (!failureCallback) {
|
|
return promise;
|
|
}
|
|
promise.then(successCallback, failureCallback);
|
|
return Promise.resolve();
|
|
};
|
|
|
|
let withCallback = function(description, successCallback, failureCallback) {
|
|
const promise = setLocalDescription.apply(this, [description]);
|
|
if (!failureCallback) {
|
|
return promise;
|
|
}
|
|
promise.then(successCallback, failureCallback);
|
|
return Promise.resolve();
|
|
};
|
|
prototype.setLocalDescription = withCallback;
|
|
|
|
withCallback = function(description, successCallback, failureCallback) {
|
|
const promise = setRemoteDescription.apply(this, [description]);
|
|
if (!failureCallback) {
|
|
return promise;
|
|
}
|
|
promise.then(successCallback, failureCallback);
|
|
return Promise.resolve();
|
|
};
|
|
prototype.setRemoteDescription = withCallback;
|
|
|
|
withCallback = function(candidate, successCallback, failureCallback) {
|
|
const promise = addIceCandidate.apply(this, [candidate]);
|
|
if (!failureCallback) {
|
|
return promise;
|
|
}
|
|
promise.then(successCallback, failureCallback);
|
|
return Promise.resolve();
|
|
};
|
|
prototype.addIceCandidate = withCallback;
|
|
}
|
|
|
|
export function shimGetUserMedia(window) {
|
|
const navigator = window && window.navigator;
|
|
|
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
// shim not needed in Safari 12.1
|
|
const mediaDevices = navigator.mediaDevices;
|
|
const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
|
|
navigator.mediaDevices.getUserMedia = (constraints) => {
|
|
return _getUserMedia(shimConstraints(constraints));
|
|
};
|
|
}
|
|
|
|
if (!navigator.getUserMedia && navigator.mediaDevices &&
|
|
navigator.mediaDevices.getUserMedia) {
|
|
navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
|
|
navigator.mediaDevices.getUserMedia(constraints)
|
|
.then(cb, errcb);
|
|
}.bind(navigator);
|
|
}
|
|
}
|
|
|
|
export function shimConstraints(constraints) {
|
|
if (constraints && constraints.video !== undefined) {
|
|
return Object.assign({},
|
|
constraints,
|
|
{video: utils.compactObject(constraints.video)}
|
|
);
|
|
}
|
|
|
|
return constraints;
|
|
}
|
|
|
|
export function shimRTCIceServerUrls(window) {
|
|
if (!window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
// migrate from non-spec RTCIceServer.url to RTCIceServer.urls
|
|
const OrigPeerConnection = window.RTCPeerConnection;
|
|
window.RTCPeerConnection =
|
|
function RTCPeerConnection(pcConfig, pcConstraints) {
|
|
if (pcConfig && pcConfig.iceServers) {
|
|
const newIceServers = [];
|
|
for (let i = 0; i < pcConfig.iceServers.length; i++) {
|
|
let server = pcConfig.iceServers[i];
|
|
if (!server.hasOwnProperty('urls') &&
|
|
server.hasOwnProperty('url')) {
|
|
utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
|
|
server = JSON.parse(JSON.stringify(server));
|
|
server.urls = server.url;
|
|
delete server.url;
|
|
newIceServers.push(server);
|
|
} else {
|
|
newIceServers.push(pcConfig.iceServers[i]);
|
|
}
|
|
}
|
|
pcConfig.iceServers = newIceServers;
|
|
}
|
|
return new OrigPeerConnection(pcConfig, pcConstraints);
|
|
};
|
|
window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
|
|
// wrap static methods. Currently just generateCertificate.
|
|
if ('generateCertificate' in OrigPeerConnection) {
|
|
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
|
|
get() {
|
|
return OrigPeerConnection.generateCertificate;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
export function shimTrackEventTransceiver(window) {
|
|
// Add event.transceiver member over deprecated event.receiver
|
|
if (typeof window === 'object' && window.RTCTrackEvent &&
|
|
'receiver' in window.RTCTrackEvent.prototype &&
|
|
!('transceiver' in window.RTCTrackEvent.prototype)) {
|
|
Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
|
|
get() {
|
|
return {receiver: this.receiver};
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
export function shimCreateOfferLegacy(window) {
|
|
const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
|
|
window.RTCPeerConnection.prototype.createOffer =
|
|
function createOffer(offerOptions) {
|
|
if (offerOptions) {
|
|
if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
|
|
// support bit values
|
|
offerOptions.offerToReceiveAudio =
|
|
!!offerOptions.offerToReceiveAudio;
|
|
}
|
|
const audioTransceiver = this.getTransceivers().find(transceiver =>
|
|
transceiver.receiver.track.kind === 'audio');
|
|
if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
|
|
if (audioTransceiver.direction === 'sendrecv') {
|
|
if (audioTransceiver.setDirection) {
|
|
audioTransceiver.setDirection('sendonly');
|
|
} else {
|
|
audioTransceiver.direction = 'sendonly';
|
|
}
|
|
} else if (audioTransceiver.direction === 'recvonly') {
|
|
if (audioTransceiver.setDirection) {
|
|
audioTransceiver.setDirection('inactive');
|
|
} else {
|
|
audioTransceiver.direction = 'inactive';
|
|
}
|
|
}
|
|
} else if (offerOptions.offerToReceiveAudio === true &&
|
|
!audioTransceiver) {
|
|
this.addTransceiver('audio', {direction: 'recvonly'});
|
|
}
|
|
|
|
if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
|
|
// support bit values
|
|
offerOptions.offerToReceiveVideo =
|
|
!!offerOptions.offerToReceiveVideo;
|
|
}
|
|
const videoTransceiver = this.getTransceivers().find(transceiver =>
|
|
transceiver.receiver.track.kind === 'video');
|
|
if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
|
|
if (videoTransceiver.direction === 'sendrecv') {
|
|
if (videoTransceiver.setDirection) {
|
|
videoTransceiver.setDirection('sendonly');
|
|
} else {
|
|
videoTransceiver.direction = 'sendonly';
|
|
}
|
|
} else if (videoTransceiver.direction === 'recvonly') {
|
|
if (videoTransceiver.setDirection) {
|
|
videoTransceiver.setDirection('inactive');
|
|
} else {
|
|
videoTransceiver.direction = 'inactive';
|
|
}
|
|
}
|
|
} else if (offerOptions.offerToReceiveVideo === true &&
|
|
!videoTransceiver) {
|
|
this.addTransceiver('video', {direction: 'recvonly'});
|
|
}
|
|
}
|
|
return origCreateOffer.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
export function shimAudioContext(window) {
|
|
if (typeof window !== 'object' || window.AudioContext) {
|
|
return;
|
|
}
|
|
window.AudioContext = window.webkitAudioContext;
|
|
}
|
|
|