Path: blob/master/core/main/client/lib/webrtcadapter.js
1154 views
/*1* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.2*3* Use of this source code is governed by a BSD-style license4* that can be found in the LICENSE file in the root of the source5* tree.6*/78/* More information about these options at jshint.com/docs/options */9/* jshint browser: true, camelcase: true, curly: true, devel: true,10eqeqeq: true, forin: false, globalstrict: true, node: true,11quotmark: single, undef: true, unused: strict */12/* global mozRTCIceCandidate, mozRTCPeerConnection, Promise,13mozRTCSessionDescription, webkitRTCPeerConnection, MediaStreamTrack */14/* exported trace,requestUserMedia */1516'use strict';1718var getUserMedia = null;19var attachMediaStream = null;20var reattachMediaStream = null;21var webrtcDetectedBrowser = null;22var webrtcDetectedVersion = null;23var webrtcMinimumVersion = null;2425function trace(text) {26// This function is used for logging.27if (text[text.length - 1] === '\n') {28text = text.substring(0, text.length - 1);29}30if (window.performance) {31var now = (window.performance.now() / 1000).toFixed(3);32beef.debug(now + ': ' + text);33} else {34beef.debug(text);35}36}3738if (navigator.mozGetUserMedia) {3940webrtcDetectedBrowser = 'firefox';4142// the detected firefox version.43webrtcDetectedVersion =44parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);4546// the minimum firefox version still supported by adapter.47webrtcMinimumVersion = 31;4849// The RTCPeerConnection object.50window.RTCPeerConnection = function(pcConfig, pcConstraints) {51if (webrtcDetectedVersion < 38) {52// .urls is not supported in FF < 38.53// create RTCIceServers with a single url.54if (pcConfig && pcConfig.iceServers) {55var newIceServers = [];56for (var i = 0; i < pcConfig.iceServers.length; i++) {57var server = pcConfig.iceServers[i];58if (server.hasOwnProperty('urls')) {59for (var j = 0; j < server.urls.length; j++) {60var newServer = {61url: server.urls[j]62};63if (server.urls[j].indexOf('turn') === 0) {64newServer.username = server.username;65newServer.credential = server.credential;66}67newIceServers.push(newServer);68}69} else {70newIceServers.push(pcConfig.iceServers[i]);71}72}73pcConfig.iceServers = newIceServers;74}75}76return new mozRTCPeerConnection(pcConfig, pcConstraints);77};7879try {80// The RTCSessionDescription object.81window.RTCSessionDescription = mozRTCSessionDescription;8283// The RTCIceCandidate object.84window.RTCIceCandidate = mozRTCIceCandidate;8586}catch(err) {8788}8990// getUserMedia constraints shim.91getUserMedia = (webrtcDetectedVersion < 38) ?92function(c, onSuccess, onError) {93var constraintsToFF37 = function(c) {94if (typeof c !== 'object' || c.require) {95return c;96}97var require = [];98Object.keys(c).forEach(function(key) {99var r = c[key] = (typeof c[key] === 'object') ?100c[key] : {ideal: c[key]};101if (r.exact !== undefined) {102r.min = r.max = r.exact;103delete r.exact;104}105if (r.min !== undefined || r.max !== undefined) {106require.push(key);107}108if (r.ideal !== undefined) {109c.advanced = c.advanced || [];110var oc = {};111oc[key] = {min: r.ideal, max: r.ideal};112c.advanced.push(oc);113delete r.ideal;114if (!Object.keys(r).length) {115delete c[key];116}117}118});119if (require.length) {120c.require = require;121}122return c;123};124beef.debug('spec: ' + JSON.stringify(c));125c.audio = constraintsToFF37(c.audio);126c.video = constraintsToFF37(c.video);127beef.debug('ff37: ' + JSON.stringify(c));128return navigator.mozGetUserMedia(c, onSuccess, onError);129} : navigator.mozGetUserMedia.bind(navigator);130131navigator.getUserMedia = getUserMedia;132133// Shim for mediaDevices on older versions.134if (!navigator.mediaDevices) {135navigator.mediaDevices = {getUserMedia: requestUserMedia,136addEventListener: function() { },137removeEventListener: function() { }138};139}140navigator.mediaDevices.enumerateDevices =141navigator.mediaDevices.enumerateDevices || function() {142return new Promise(function(resolve) {143var infos = [144{kind: 'audioinput', deviceId: 'default', label:'', groupId:''},145{kind: 'videoinput', deviceId: 'default', label:'', groupId:''}146];147resolve(infos);148});149};150151if (webrtcDetectedVersion < 41) {152// Work around http://bugzil.la/1169665153var orgEnumerateDevices =154navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);155navigator.mediaDevices.enumerateDevices = function() {156return orgEnumerateDevices().then(undefined, function(e) {157if (e.name === 'NotFoundError') {158return [];159}160throw e;161});162};163}164// Attach a media stream to an element.165attachMediaStream = function(element, stream) {166beef.debug('Attaching media stream');167element.mozSrcObject = stream;168};169170reattachMediaStream = function(to, from) {171beef.debug('Reattaching media stream');172to.mozSrcObject = from.mozSrcObject;173};174175} else if (navigator.webkitGetUserMedia) {176177webrtcDetectedBrowser = 'chrome';178179// the detected chrome version.180webrtcDetectedVersion =181parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);182183// the minimum chrome version still supported by adapter.184webrtcMinimumVersion = 38;185186// The RTCPeerConnection object.187window.RTCPeerConnection = function(pcConfig, pcConstraints) {188var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);189var origGetStats = pc.getStats.bind(pc);190pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line191// If selector is a function then we are in the old style stats so just192// pass back the original getStats format to avoid breaking old users.193if (typeof selector === 'function') {194return origGetStats(selector, successCallback);195}196197var fixChromeStats = function(response) {198var standardReport = {};199var reports = response.result();200reports.forEach(function(report) {201var standardStats = {202id: report.id,203timestamp: report.timestamp,204type: report.type205};206report.names().forEach(function(name) {207standardStats[name] = report.stat(name);208});209standardReport[standardStats.id] = standardStats;210});211212return standardReport;213};214var successCallbackWrapper = function(response) {215successCallback(fixChromeStats(response));216};217return origGetStats(successCallbackWrapper, selector);218};219220return pc;221};222223// add promise support224['createOffer', 'createAnswer'].forEach(function(method) {225var nativeMethod = webkitRTCPeerConnection.prototype[method];226webkitRTCPeerConnection.prototype[method] = function() {227var self = this;228if (arguments.length < 1 || (arguments.length === 1 &&229typeof(arguments[0]) === 'object')) {230var opts = arguments.length === 1 ? arguments[0] : undefined;231return new Promise(function(resolve, reject) {232nativeMethod.apply(self, [resolve, reject, opts]);233});234} else {235return nativeMethod.apply(this, arguments);236}237};238});239240['setLocalDescription', 'setRemoteDescription',241'addIceCandidate'].forEach(function(method) {242var nativeMethod = webkitRTCPeerConnection.prototype[method];243webkitRTCPeerConnection.prototype[method] = function() {244var args = arguments;245var self = this;246return new Promise(function(resolve, reject) {247nativeMethod.apply(self, [args[0],248function() {249resolve();250if (args.length >= 2) {251args[1].apply(null, []);252}253},254function(err) {255reject(err);256if (args.length >= 3) {257args[2].apply(null, [err]);258}259}]260);261});262};263});264265// getUserMedia constraints shim.266getUserMedia = function(c, onSuccess, onError) {267var constraintsToChrome = function(c) {268if (typeof c !== 'object' || c.mandatory || c.optional) {269return c;270}271var cc = {};272Object.keys(c).forEach(function(key) {273if (key === 'require' || key === 'advanced') {274return;275}276var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};277if (r.exact !== undefined && typeof r.exact === 'number') {278r.min = r.max = r.exact;279}280var oldname = function(prefix, name) {281if (prefix) {282return prefix + name.charAt(0).toUpperCase() + name.slice(1);283}284return (name === 'deviceId') ? 'sourceId' : name;285};286if (r.ideal !== undefined) {287cc.optional = cc.optional || [];288var oc = {};289if (typeof r.ideal === 'number') {290oc[oldname('min', key)] = r.ideal;291cc.optional.push(oc);292oc = {};293oc[oldname('max', key)] = r.ideal;294cc.optional.push(oc);295} else {296oc[oldname('', key)] = r.ideal;297cc.optional.push(oc);298}299}300if (r.exact !== undefined && typeof r.exact !== 'number') {301cc.mandatory = cc.mandatory || {};302cc.mandatory[oldname('', key)] = r.exact;303} else {304['min', 'max'].forEach(function(mix) {305if (r[mix] !== undefined) {306cc.mandatory = cc.mandatory || {};307cc.mandatory[oldname(mix, key)] = r[mix];308}309});310}311});312if (c.advanced) {313cc.optional = (cc.optional || []).concat(c.advanced);314}315return cc;316};317beef.debug('spec: ' + JSON.stringify(c)); // whitespace for alignment318c.audio = constraintsToChrome(c.audio);319c.video = constraintsToChrome(c.video);320beef.debug('chrome: ' + JSON.stringify(c));321return navigator.webkitGetUserMedia(c, onSuccess, onError);322};323navigator.getUserMedia = getUserMedia;324325// Attach a media stream to an element.326attachMediaStream = function(element, stream) {327if (typeof element.srcObject !== 'undefined') {328element.srcObject = stream;329} else if (typeof element.src !== 'undefined') {330element.src = URL.createObjectURL(stream);331} else {332beef.debug('Error attaching stream to element.');333}334};335336reattachMediaStream = function(to, from) {337to.src = from.src;338};339340if (!navigator.mediaDevices) {341navigator.mediaDevices = {getUserMedia: requestUserMedia,342enumerateDevices: function() {343return new Promise(function(resolve) {344var kinds = {audio: 'audioinput', video: 'videoinput'};345return MediaStreamTrack.getSources(function(devices) {346resolve(devices.map(function(device) {347return {label: device.label,348kind: kinds[device.kind],349deviceId: device.id,350groupId: ''};351}));352});353});354}};355// in case someone wants to listen for the devicechange event.356navigator.mediaDevices.addEventListener = function() { };357navigator.mediaDevices.removeEventListener = function() { };358}359} else if (navigator.mediaDevices && navigator.userAgent.match(360/Edge\/(\d+).(\d+)$/)) {361webrtcDetectedBrowser = 'edge';362363webrtcDetectedVersion =364parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10);365366// the minimum version still supported by adapter.367webrtcMinimumVersion = 12;368369attachMediaStream = function(element, stream) {370element.srcObject = stream;371};372reattachMediaStream = function(to, from) {373to.srcObject = from.srcObject;374};375} else {376// console.log('Browser does not appear to be WebRTC-capable');377}378379// Returns the result of getUserMedia as a Promise.380function requestUserMedia(constraints) {381return new Promise(function(resolve, reject) {382getUserMedia(constraints, resolve, reject);383});384}385386if (typeof module !== 'undefined') {387module.exports = {388RTCPeerConnection: window.RTCPeerConnection,389getUserMedia: getUserMedia,390attachMediaStream: attachMediaStream,391reattachMediaStream: reattachMediaStream,392webrtcDetectedBrowser: webrtcDetectedBrowser,393webrtcDetectedVersion: webrtcDetectedVersion,394webrtcMinimumVersion: webrtcMinimumVersion395//requestUserMedia: not exposed on purpose.396//trace: not exposed on purpose.397};398} else if ((typeof require === 'function') && (typeof define === 'function')) {399// Expose objects and functions when RequireJS is doing the loading.400define([], function() {401return {402RTCPeerConnection: window.RTCPeerConnection,403getUserMedia: getUserMedia,404attachMediaStream: attachMediaStream,405reattachMediaStream: reattachMediaStream,406webrtcDetectedBrowser: webrtcDetectedBrowser,407webrtcDetectedVersion: webrtcDetectedVersion,408webrtcMinimumVersion: webrtcMinimumVersion409//requestUserMedia: not exposed on purpose.410//trace: not exposed on purpose.411};412});413}414415416