261 lines
9.3 KiB
JavaScript
261 lines
9.3 KiB
JavaScript
/*
|
|
* 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.
|
|
*/
|
|
/* eslint-env node */
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.compactObject = compactObject;
|
|
exports.deprecated = deprecated;
|
|
exports.detectBrowser = detectBrowser;
|
|
exports.disableLog = disableLog;
|
|
exports.disableWarnings = disableWarnings;
|
|
exports.extractVersion = extractVersion;
|
|
exports.filterStats = filterStats;
|
|
exports.log = log;
|
|
exports.walkStats = walkStats;
|
|
exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent;
|
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
|
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
|
|
var logDisabled_ = true;
|
|
var deprecationWarnings_ = true;
|
|
|
|
/**
|
|
* Extract browser version out of the provided user agent string.
|
|
*
|
|
* @param {!string} uastring userAgent string.
|
|
* @param {!string} expr Regular expression used as match criteria.
|
|
* @param {!number} pos position in the version string to be returned.
|
|
* @return {!number} browser version.
|
|
*/
|
|
function extractVersion(uastring, expr, pos) {
|
|
var match = uastring.match(expr);
|
|
return match && match.length >= pos && parseInt(match[pos], 10);
|
|
}
|
|
|
|
// Wraps the peerconnection event eventNameToWrap in a function
|
|
// which returns the modified event object (or false to prevent
|
|
// the event).
|
|
function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
|
|
if (!window.RTCPeerConnection) {
|
|
return;
|
|
}
|
|
var proto = window.RTCPeerConnection.prototype;
|
|
var nativeAddEventListener = proto.addEventListener;
|
|
proto.addEventListener = function (nativeEventName, cb) {
|
|
if (nativeEventName !== eventNameToWrap) {
|
|
return nativeAddEventListener.apply(this, arguments);
|
|
}
|
|
var wrappedCallback = function wrappedCallback(e) {
|
|
var modifiedEvent = wrapper(e);
|
|
if (modifiedEvent) {
|
|
if (cb.handleEvent) {
|
|
cb.handleEvent(modifiedEvent);
|
|
} else {
|
|
cb(modifiedEvent);
|
|
}
|
|
}
|
|
};
|
|
this._eventMap = this._eventMap || {};
|
|
if (!this._eventMap[eventNameToWrap]) {
|
|
this._eventMap[eventNameToWrap] = new Map();
|
|
}
|
|
this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
|
|
return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]);
|
|
};
|
|
var nativeRemoveEventListener = proto.removeEventListener;
|
|
proto.removeEventListener = function (nativeEventName, cb) {
|
|
if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) {
|
|
return nativeRemoveEventListener.apply(this, arguments);
|
|
}
|
|
if (!this._eventMap[eventNameToWrap].has(cb)) {
|
|
return nativeRemoveEventListener.apply(this, arguments);
|
|
}
|
|
var unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
|
|
this._eventMap[eventNameToWrap]["delete"](cb);
|
|
if (this._eventMap[eventNameToWrap].size === 0) {
|
|
delete this._eventMap[eventNameToWrap];
|
|
}
|
|
if (Object.keys(this._eventMap).length === 0) {
|
|
delete this._eventMap;
|
|
}
|
|
return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]);
|
|
};
|
|
Object.defineProperty(proto, 'on' + eventNameToWrap, {
|
|
get: function get() {
|
|
return this['_on' + eventNameToWrap];
|
|
},
|
|
set: function set(cb) {
|
|
if (this['_on' + eventNameToWrap]) {
|
|
this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]);
|
|
delete this['_on' + eventNameToWrap];
|
|
}
|
|
if (cb) {
|
|
this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb);
|
|
}
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
}
|
|
function disableLog(bool) {
|
|
if (typeof bool !== 'boolean') {
|
|
return new Error('Argument type: ' + _typeof(bool) + '. Please use a boolean.');
|
|
}
|
|
logDisabled_ = bool;
|
|
return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled';
|
|
}
|
|
|
|
/**
|
|
* Disable or enable deprecation warnings
|
|
* @param {!boolean} bool set to true to disable warnings.
|
|
*/
|
|
function disableWarnings(bool) {
|
|
if (typeof bool !== 'boolean') {
|
|
return new Error('Argument type: ' + _typeof(bool) + '. Please use a boolean.');
|
|
}
|
|
deprecationWarnings_ = !bool;
|
|
return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
|
|
}
|
|
function log() {
|
|
if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object') {
|
|
if (logDisabled_) {
|
|
return;
|
|
}
|
|
if (typeof console !== 'undefined' && typeof console.log === 'function') {
|
|
console.log.apply(console, arguments);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows a deprecation warning suggesting the modern and spec-compatible API.
|
|
*/
|
|
function deprecated(oldMethod, newMethod) {
|
|
if (!deprecationWarnings_) {
|
|
return;
|
|
}
|
|
console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.');
|
|
}
|
|
|
|
/**
|
|
* Browser detector.
|
|
*
|
|
* @return {object} result containing browser and version
|
|
* properties.
|
|
*/
|
|
function detectBrowser(window) {
|
|
// Returned result object.
|
|
var result = {
|
|
browser: null,
|
|
version: null
|
|
};
|
|
|
|
// Fail early if it's not a browser
|
|
if (typeof window === 'undefined' || !window.navigator || !window.navigator.userAgent) {
|
|
result.browser = 'Not a browser.';
|
|
return result;
|
|
}
|
|
var navigator = window.navigator;
|
|
if (navigator.mozGetUserMedia) {
|
|
// Firefox.
|
|
result.browser = 'firefox';
|
|
result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
|
|
} else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection) {
|
|
// Chrome, Chromium, Webview, Opera.
|
|
// Version matches Chrome/WebRTC version.
|
|
// Chrome 74 removed webkitGetUserMedia on http as well so we need the
|
|
// more complicated fallback to webkitRTCPeerConnection.
|
|
result.browser = 'chrome';
|
|
result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
|
|
} else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
|
|
// Safari.
|
|
result.browser = 'safari';
|
|
result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
|
|
result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
|
|
} else {
|
|
// Default fallthrough: not supported.
|
|
result.browser = 'Not a supported browser.';
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if something is an object.
|
|
*
|
|
* @param {*} val The something you want to check.
|
|
* @return true if val is an object, false otherwise.
|
|
*/
|
|
function isObject(val) {
|
|
return Object.prototype.toString.call(val) === '[object Object]';
|
|
}
|
|
|
|
/**
|
|
* Remove all empty objects and undefined values
|
|
* from a nested object -- an enhanced and vanilla version
|
|
* of Lodash's `compact`.
|
|
*/
|
|
function compactObject(data) {
|
|
if (!isObject(data)) {
|
|
return data;
|
|
}
|
|
return Object.keys(data).reduce(function (accumulator, key) {
|
|
var isObj = isObject(data[key]);
|
|
var value = isObj ? compactObject(data[key]) : data[key];
|
|
var isEmptyObject = isObj && !Object.keys(value).length;
|
|
if (value === undefined || isEmptyObject) {
|
|
return accumulator;
|
|
}
|
|
return Object.assign(accumulator, _defineProperty({}, key, value));
|
|
}, {});
|
|
}
|
|
|
|
/* iterates the stats graph recursively. */
|
|
function walkStats(stats, base, resultSet) {
|
|
if (!base || resultSet.has(base.id)) {
|
|
return;
|
|
}
|
|
resultSet.set(base.id, base);
|
|
Object.keys(base).forEach(function (name) {
|
|
if (name.endsWith('Id')) {
|
|
walkStats(stats, stats.get(base[name]), resultSet);
|
|
} else if (name.endsWith('Ids')) {
|
|
base[name].forEach(function (id) {
|
|
walkStats(stats, stats.get(id), resultSet);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/* filter getStats for a sender/receiver track. */
|
|
function filterStats(result, track, outbound) {
|
|
var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
|
|
var filteredResult = new Map();
|
|
if (track === null) {
|
|
return filteredResult;
|
|
}
|
|
var trackStats = [];
|
|
result.forEach(function (value) {
|
|
if (value.type === 'track' && value.trackIdentifier === track.id) {
|
|
trackStats.push(value);
|
|
}
|
|
});
|
|
trackStats.forEach(function (trackStat) {
|
|
result.forEach(function (stats) {
|
|
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
|
|
walkStats(result, stats, filteredResult);
|
|
}
|
|
});
|
|
});
|
|
return filteredResult;
|
|
}
|