From 5401c566b514a0d32238c2e3053bc6ccc1603106 Mon Sep 17 00:00:00 2001 From: Simon Eisenmann Date: Tue, 17 Feb 2015 19:13:36 +0100 Subject: [PATCH] Bring back send stereo support by processing sdp correctly and disable AEC when sendStereo is true. The old stereo option is now always true. --- .../js/controllers/mediastreamcontroller.js | 5 +- static/js/directives/settings.js | 6 + static/js/mediastream/peercall.js | 29 +- static/js/mediastream/utils.js | 555 +++++++++++++----- static/js/mediastream/webrtc.js | 25 +- static/partials/settings.html | 23 +- 6 files changed, 471 insertions(+), 172 deletions(-) diff --git a/static/js/controllers/mediastreamcontroller.js b/static/js/controllers/mediastreamcontroller.js index f02c45e7..3e5f69fc 100644 --- a/static/js/controllers/mediastreamcontroller.js +++ b/static/js/controllers/mediastreamcontroller.js @@ -140,7 +140,7 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder message: null, settings: { videoQuality: "high", - stereo: true, + sendStereo: false, maxFrameRate: 20, defaultRoom: "", language: "", @@ -226,9 +226,6 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder } mediaStream.webrtc.settings.pcConfig.iceServers = iceServers; - // Stereo. - mediaStream.webrtc.settings.stereo = settings.stereo; - // Refresh constraints. constraints.refresh($scope.master.settings); diff --git a/static/js/directives/settings.js b/static/js/directives/settings.js index 1fb5107f..9d7dab70 100644 --- a/static/js/directives/settings.js +++ b/static/js/directives/settings.js @@ -248,6 +248,12 @@ define(['jquery', 'underscore', 'text!partials/settings.html'], function($, _, t constraints.add("video", "maxFrameRate", parseInt(settings.maxFrameRate, 10), true); } + // Disable AEC if stereo. + // https://github.com/webrtc/apprtc/issues/23 + if (settings.sendStereo) { + constraints.add("audio", "echoCancellation", false); + } + } else { // Other browsers constraints (there are none as of now.); diff --git a/static/js/mediastream/peercall.js b/static/js/mediastream/peercall.js index 41b2fa55..cb036033 100644 --- a/static/js/mediastream/peercall.js +++ b/static/js/mediastream/peercall.js @@ -36,6 +36,7 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection this.pcConstraints = $.extend(true, {}, this.webrtc.settings.pcConstraints); this.sdpConstraints = $.extend(true, {}, this.webrtc.settings.sdpConstraints); this.offerConstraints = $.extend(true, {}, this.webrtc.settings.offerConstraints); + this.sdpParams = $.extend(true, {}, this.webrtc.settings.sdpParams); this.peerconnection = null; this.datachannels = {}; @@ -84,8 +85,7 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection PeerCall.prototype.onCreateAnswerOffer = function(cb, sessionDescription) { - // Prefer Opus. - sessionDescription.sdp = utils.preferOpus(sessionDescription.sdp); + this.setLocalSdp(sessionDescription); // Convert to object to allow custom property injection. var sessionDescriptionObj = sessionDescription; @@ -128,6 +128,9 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection console.log("Got a remote description but not connected -> ignored."); return; } + + this.setRemoteSdp(sessionDescription); + peerconnection.setRemoteDescription(sessionDescription, _.bind(function() { console.log("Set remote session description.", sessionDescription, this); if (cb) { @@ -155,6 +158,28 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection }; + PeerCall.prototype.setLocalSdp = function(sessionDescription) { + + var params = this.sdpParams; + sessionDescription.sdp = utils.maybePreferAudioReceiveCodec(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybePreferVideoReceiveCodec(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybeSetAudioReceiveBitRate(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybeSetVideoReceiveBitRate(sessionDescription.sdp, params); + + }; + + PeerCall.prototype.setRemoteSdp = function(sessionDescription) { + + var params = this.sdpParams; + sessionDescription.sdp = utils.maybeSetOpusOptions(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybePreferAudioSendCodec(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybePreferVideoSendCodec(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybeSetAudioSendBitRate(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybeSetVideoSendBitRate(sessionDescription.sdp, params); + sessionDescription.sdp = utils.maybeSetVideoSendInitialBitRate(sessionDescription.sdp, params); + + }; + PeerCall.prototype.onIceCandidate = function(event) { if (event.candidate) { //console.log("ice candidate", event.candidate); diff --git a/static/js/mediastream/utils.js b/static/js/mediastream/utils.js index 36740466..9f41acda 100644 --- a/static/js/mediastream/utils.js +++ b/static/js/mediastream/utils.js @@ -1,162 +1,423 @@ /* * Spreed WebRTC. - * Copyright (C) 2013-2014 struktur AG + * Copyright (C) 2013-2015 struktur AG * * This file is part of Spreed WebRTC. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This file is a AMD wrapped version of the sdputils.js from the + * WebRTC apprtc example. https://github.com/webrtc/apprtc/blob/master/src * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ "use strict"; define([], function() { - var Utils = function() {} - - Utils.prototype.mergeConstraints = function(cons1, cons2) { - var merged = cons1; - var name; - for (name in cons2.mandatory) { - if (cons2.mandatory.hasOwnProperty(name)) { - merged.mandatory[name] = cons2.mandatory[name]; - } - } - merged.optional.concat(cons2.optional); - return merged; - }; - - Utils.prototype.extractSdp = function(sdpLine, pattern) { - var result = sdpLine.match(pattern); - return (result && result.length == 2) ? result[1] : null; - }; - - Utils.prototype.addStereo = function(sdp) { - // Set Opus in Stereo. - - var sdpLines = sdp.split('\r\n'); - var opusPayload = ""; - var fmtpLineIndex = null; - var i; - - // Find opus payload. - for (i = 0; i < sdpLines.length; i++) { - if (sdpLines[i].search('opus/48000') !== -1) { - opusPayload = this.extractSdp(sdpLines[i], /:(\d+) opus\/48000/i); - break; - } - } - - // Find the payload in fmtp line. - for (i = 0; i < sdpLines.length; i++) { - if (sdpLines[i].search('a=fmtp') !== -1) { - var payload = this.extractSdp(sdpLines[i], /a=fmtp:(\d+)/); - if (payload === opusPayload) { - fmtpLineIndex = i; - break; - } - } - } - // No fmtp line found. - if (fmtpLineIndex === null) { - console.log("Unable to add stereo (no fmtp line for opus payload)", opusPayload); - return sdp; - } - - // Append stereo=1 to fmtp line. - sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat(' stereo=1'); - - sdp = sdpLines.join('\r\n'); - console.log("Enabled opus stereo."); - return sdp; - - }; - - Utils.prototype.preferOpus = function(sdp) { - // Set Opus as the preferred codec in SDP if Opus is present. - - var sdpLines = sdp.split('\r\n'); - var mLineIndex = null; - var i; - - // Search for m line. - for (i = 0; i < sdpLines.length; i++) { - if (sdpLines[i].search('m=audio') !== -1) { - mLineIndex = i; - break; - } - } - if (mLineIndex === null) { - return sdp; - } - - // If Opus is available, set it as the default in m line. - for (i = 0; i < sdpLines.length; i++) { - if (sdpLines[i].search('opus/48000') !== -1) { - var opusPayload = this.extractSdp(sdpLines[i], /:(\d+) opus\/48000/i); - if (opusPayload) { - sdpLines[mLineIndex] = this.setDefaultCodec(sdpLines[mLineIndex], opusPayload); - } - break; - } - } - - // Remove CN in m line and sdp. - sdpLines = this.removeCN(sdpLines, mLineIndex); - - sdp = sdpLines.join('\r\n'); - return sdp; - - }; - - Utils.prototype.setDefaultCodec = function(mLine, payload) { - // Set the selected codec to the first in m line. - var elements = mLine.split(' '); - var newLine = []; - var index = 0; - for (var i = 0; i < elements.length; i++) { - // Format of media starts from the fourth. - if (index === 3) { - newLine[index++] = payload; // Put target payload to the first. - } - if (elements[i] !== payload) { - newLine[index++] = elements[i]; - } - } - return newLine.join(' '); - }; - - Utils.prototype.removeCN = function(sdpLines, mLineIndex) { - // Strip CN from sdp before CN constraints is ready. - var mLineElements = sdpLines[mLineIndex].split(' '); - // Scan from end for the convenience of removing an item. - for (var i = sdpLines.length - 1; i >= 0; i--) { - var payload = this.extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i); - if (payload) { - var cnPos = mLineElements.indexOf(payload); - if (cnPos !== -1) { - // Remove CN payload from m line. - mLineElements.splice(cnPos, 1); - } - // Remove CN line in sdp - sdpLines.splice(i, 1); - } - } - sdpLines[mLineIndex] = mLineElements.join(' '); - return sdpLines; - }; - - var utils = new Utils(); - return utils; + function trace(text) { + // noop + } + + function mergeConstraints(cons1, cons2) { + if (!cons1 || !cons2) { + return cons1 || cons2; + } + var merged = cons1; + for (var name in cons2.mandatory) { + if (cons2.mandatory.hasOwnProperty(name)) { + merged.mandatory[name] = cons2.mandatory[name]; + } + } + merged.optional = merged.optional.concat(cons2.optional); + return merged; + } + + function iceCandidateType(candidateStr) { + return candidateStr.split(' ')[7]; + } + + function maybeSetOpusOptions(sdp, params) { + // Set Opus in Stereo, if stereo is true, unset it, if stereo is false, and + // do nothing if otherwise. + if (params.opusStereo === 'true') { + sdp = setCodecParam(sdp, 'opus/48000', 'stereo', '1'); + } else if (params.opusStereo === 'false') { + sdp = removeCodecParam(sdp, 'opus/48000', 'stereo'); + } + + // Set Opus FEC, if opusfec is true, unset it, if opusfec is false, and + // do nothing if otherwise. + if (params.opusFec === 'true') { + sdp = setCodecParam(sdp, 'opus/48000', 'useinbandfec', '1'); + } else if (params.opusFec === 'false') { + sdp = removeCodecParam(sdp, 'opus/48000', 'useinbandfec'); + } + + // Set Opus maxplaybackrate, if requested. + if (params.opusMaxPbr) { + sdp = setCodecParam( + sdp, 'opus/48000', 'maxplaybackrate', params.opusMaxPbr); + } + return sdp; + } + + function maybeSetAudioSendBitRate(sdp, params) { + if (!params.audioSendBitrate) { + return sdp; + } + trace('Prefer audio send bitrate: ' + params.audioSendBitrate); + return preferBitRate(sdp, params.audioSendBitrate, 'audio'); + } + + function maybeSetAudioReceiveBitRate(sdp, params) { + if (!params.audioRecvBitrate) { + return sdp; + } + trace('Prefer audio receive bitrate: ' + params.audioRecvBitrate); + return preferBitRate(sdp, params.audioRecvBitrate, 'audio'); + } + + function maybeSetVideoSendBitRate(sdp, params) { + if (!params.videoSendBitrate) { + return sdp; + } + trace('Prefer video send bitrate: ' + params.videoSendBitrate); + return preferBitRate(sdp, params.videoSendBitrate, 'video'); + } + + function maybeSetVideoReceiveBitRate(sdp, params) { + if (!params.videoRecvBitrate) { + return sdp; + } + trace('Prefer video receive bitrate: ' + params.videoRecvBitrate); + return preferBitRate(sdp, params.videoRecvBitrate, 'video'); + } + + // Add a b=AS:bitrate line to the m=mediaType section. + function preferBitRate(sdp, bitrate, mediaType) { + var sdpLines = sdp.split('\r\n'); + + // Find m line for the given mediaType. + var mLineIndex = findLine(sdpLines, 'm=', mediaType); + if (mLineIndex === null) { + trace('Failed to add bandwidth line to sdp, as no m-line found'); + return sdp; + } + + // Find next m-line if any. + var nextMLineIndex = findLineInRange(sdpLines, mLineIndex + 1, -1, 'm='); + if (nextMLineIndex === null) { + nextMLineIndex = sdpLines.length; + } + + // Find c-line corresponding to the m-line. + var cLineIndex = findLineInRange(sdpLines, mLineIndex + 1, + nextMLineIndex, 'c='); + if (cLineIndex === null) { + trace('Failed to add bandwidth line to sdp, as no c-line found'); + return sdp; + } + + // Check if bandwidth line already exists between c-line and next m-line. + var bLineIndex = findLineInRange(sdpLines, cLineIndex + 1, + nextMLineIndex, 'b=AS'); + if (bLineIndex) { + sdpLines.splice(bLineIndex, 1); + } + + // Create the b (bandwidth) sdp line. + var bwLine = 'b=AS:' + bitrate; + // As per RFC 4566, the b line should follow after c-line. + sdpLines.splice(cLineIndex + 1, 0, bwLine); + sdp = sdpLines.join('\r\n'); + return sdp; + } + + // Add an a=fmtp: x-google-min-bitrate=kbps line, if videoSendInitialBitrate + // is specified. We'll also add a x-google-min-bitrate value, since the max + // must be >= the min. + function maybeSetVideoSendInitialBitRate(sdp, params) { + var initialBitrate = params.videoSendInitialBitrate; + if (!initialBitrate) { + return sdp; + } + + // Validate the initial bitrate value. + var maxBitrate = initialBitrate; + var bitrate = params.videoSendBitrate; + if (bitrate) { + if (initialBitrate > bitrate) { + trace('Clamping initial bitrate to max bitrate of ' + + bitrate + ' kbps.'); + initialBitrate = bitrate; + params.videoSendInitialBitrate = initialBitrate; + } + maxBitrate = bitrate; + } + + var sdpLines = sdp.split('\r\n'); + + // Search for m line. + var mLineIndex = findLine(sdpLines, 'm=', 'video'); + if (mLineIndex === null) { + trace('Failed to find video m-line'); + return sdp; + } + + sdp = setCodecParam(sdp, 'VP8/90000', 'x-google-min-bitrate', + params.videoSendInitialBitrate.toString()); + sdp = setCodecParam(sdp, 'VP8/90000', 'x-google-max-bitrate', + maxBitrate.toString()); + + return sdp; + } + + // Promotes |audioSendCodec| to be the first in the m=audio line, if set. + function maybePreferAudioSendCodec(sdp, params) { + return maybePreferCodec(sdp, 'audio', 'send', params.audioSendCodec); + } + + // Promotes |audioRecvCodec| to be the first in the m=audio line, if set. + function maybePreferAudioReceiveCodec(sdp, params) { + return maybePreferCodec(sdp, 'audio', 'receive', params.audioRecvCodec); + } + + // Promotes |videoSendCodec| to be the first in the m=audio line, if set. + function maybePreferVideoSendCodec(sdp, params) { + return maybePreferCodec(sdp, 'video', 'send', params.videoSendCodec); + } + + // Promotes |videoRecvCodec| to be the first in the m=audio line, if set. + function maybePreferVideoReceiveCodec(sdp, params) { + return maybePreferCodec(sdp, 'video', 'receive', params.videoRecvCodec); + } + + // Sets |codec| as the default |type| codec if it's present. + // The format of |codec| is 'NAME/RATE', e.g. 'opus/48000'. + function maybePreferCodec(sdp, type, dir, codec) { + var str = type + ' ' + dir + ' codec'; + if (codec === '') { + trace('No preference on ' + str + '.'); + return sdp; + } + + trace('Prefer ' + str + ': ' + codec); + + var sdpLines = sdp.split('\r\n'); + + // Search for m line. + var mLineIndex = findLine(sdpLines, 'm=', type); + if (mLineIndex === null) { + return sdp; + } + + // If the codec is available, set it as the default in m line. + var payload = getCodecPayloadType(sdpLines, codec); + if (payload) { + sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], payload); + } + + sdp = sdpLines.join('\r\n'); + return sdp; + } + + // Set fmtp param to specific codec in SDP. If param does not exists, add it. + function setCodecParam(sdp, codec, param, value) { + var sdpLines = sdp.split('\r\n'); + + var fmtpLineIndex = findFmtpLine(sdpLines, codec); + + var fmtpObj = {}; + if (fmtpLineIndex === null) { + var index = findLine(sdpLines, 'a=rtpmap', codec); + if (index === null) { + return sdp; + } + var payload = getCodecPayloadTypeFromLine(sdpLines[index]); + fmtpObj.pt = payload.toString(); + fmtpObj.params = {}; + fmtpObj.params[param] = value; + sdpLines.splice(index + 1, 0, writeFmtpLine(fmtpObj)); + } else { + fmtpObj = parseFmtpLine(sdpLines[fmtpLineIndex]); + fmtpObj.params[param] = value; + sdpLines[fmtpLineIndex] = writeFmtpLine(fmtpObj); + } + + sdp = sdpLines.join('\r\n'); + return sdp; + } + + // Remove fmtp param if it exists. + function removeCodecParam(sdp, codec, param) { + var sdpLines = sdp.split('\r\n'); + + var fmtpLineIndex = findFmtpLine(sdpLines, codec); + if (fmtpLineIndex === null) { + return sdp; + } + + var map = parseFmtpLine(sdpLines[fmtpLineIndex]); + delete map.params[param]; + + var newLine = writeFmtpLine(map); + if (newLine === null) { + sdpLines.splice(fmtpLineIndex, 1); + } else { + sdpLines[fmtpLineIndex] = newLine; + } + + sdp = sdpLines.join('\r\n'); + return sdp; + } + + // Split an fmtp line into an object including 'pt' and 'params'. + function parseFmtpLine(fmtpLine) { + var fmtpObj = {}; + var spacePos = fmtpLine.indexOf(' '); + var keyValues = fmtpLine.substring(spacePos + 1).split('; '); + + var pattern = new RegExp('a=fmtp:(\\d+)'); + var result = fmtpLine.match(pattern); + if (result && result.length === 2) { + fmtpObj.pt = result[1]; + } else { + return null; + } + + var params = {}; + for (var i = 0; i < keyValues.length; ++i) { + var pair = keyValues[i].split('='); + if (pair.length === 2) { + params[pair[0]] = pair[1]; + } + } + fmtpObj.params = params; + + return fmtpObj; + } + + // Generate an fmtp line from an object including 'pt' and 'params'. + function writeFmtpLine(fmtpObj) { + if (!fmtpObj.hasOwnProperty('pt') || !fmtpObj.hasOwnProperty('params')) { + return null; + } + var pt = fmtpObj.pt; + var params = fmtpObj.params; + var keyValues = []; + var i = 0; + for (var key in params) { + if (params.hasOwnProperty(key)) { + keyValues[i] = key + '=' + params[key]; + ++i; + } + } + if (i === 0) { + return null; + } + return 'a=fmtp:' + pt.toString() + ' ' + keyValues.join('; '); + } + + // Find fmtp attribute for |codec| in |sdpLines|. + function findFmtpLine(sdpLines, codec) { + // Find payload of codec. + var payload = getCodecPayloadType(sdpLines, codec); + // Find the payload in fmtp line. + return payload ? findLine(sdpLines, 'a=fmtp:' + payload.toString()) : null; + } + + // Find the line in sdpLines that starts with |prefix|, and, if specified, + // contains |substr| (case-insensitive search). + function findLine(sdpLines, prefix, substr) { + return findLineInRange(sdpLines, 0, -1, prefix, substr); + } + + // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix| + // and, if specified, contains |substr| (case-insensitive search). + function findLineInRange(sdpLines, startLine, endLine, prefix, substr) { + var realEndLine = endLine !== -1 ? endLine : sdpLines.length; + for (var i = startLine; i < realEndLine; ++i) { + if (sdpLines[i].indexOf(prefix) === 0) { + if (!substr || + sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) { + return i; + } + } + } + return null; + } + + // Gets the codec payload type from sdp lines. + function getCodecPayloadType(sdpLines, codec) { + var index = findLine(sdpLines, 'a=rtpmap', codec); + return index ? getCodecPayloadTypeFromLine(sdpLines[index]) : null; + } + + // Gets the codec payload type from an a=rtpmap:X line. + function getCodecPayloadTypeFromLine(sdpLine) { + var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); + var result = sdpLine.match(pattern); + return (result && result.length === 2) ? result[1] : null; + } + + // Returns a new m= line with the specified codec as the first one. + function setDefaultCodec(mLine, payload) { + var elements = mLine.split(' '); + + // Just copy the first three parameters; codec order starts on fourth. + var newLine = elements.slice(0, 3); + + // Put target payload first and copy in the rest. + newLine.push(payload); + for (var i = 3; i < elements.length; i++) { + if (elements[i] !== payload) { + newLine.push(elements[i]); + } + } + return newLine.join(' '); + } + + // Exported utils. + return { + mergeConstraints: mergeConstraints, + maybeSetOpusOptions: maybeSetOpusOptions, + maybeSetAudioSendBitRate: maybeSetAudioSendBitRate, + maybeSetAudioReceiveBitRate: maybeSetAudioReceiveBitRate, + maybeSetVideoSendBitRate: maybeSetVideoSendBitRate, + maybeSetVideoReceiveBitRate: maybeSetVideoReceiveBitRate, + maybeSetVideoSendInitialBitRate: maybeSetVideoSendInitialBitRate, + maybePreferAudioSendCodec: maybePreferAudioSendCodec, + maybePreferAudioReceiveCodec: maybePreferAudioReceiveCodec, + maybePreferVideoSendCodec: maybePreferVideoSendCodec, + maybePreferVideoReceiveCodec: maybePreferVideoReceiveCodec + } }); diff --git a/static/js/mediastream/webrtc.js b/static/js/mediastream/webrtc.js index 84faaf81..12daf53c 100644 --- a/static/js/mediastream/webrtc.js +++ b/static/js/mediastream/webrtc.js @@ -59,7 +59,6 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u // Settings.are cloned into peer call on call creation. this.settings = { - stereo: false, mediaConstraints: { audio: true, video: { @@ -76,6 +75,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u }] }, pcConstraints: { + mandatory: {}, optional: [] }, // Set up audio and video regardless of what devices are present. @@ -83,7 +83,8 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true - } + }, + optional: [] }, offerConstraints: { mandatory: {}, @@ -97,6 +98,20 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u mandatory: {} } } + }, + // sdpParams values need to be strings. + sdpParams: { + //audioSendBitrate: , + audioSendCodec: "opus/48000", + //audioRecvBitrate: , + //audioRecvCodec: , + //opusMaxPbr: , + opusStereo: "true", + //videoSendBitrate: , + //videoSendInitialBitrate: , + videoSendCodec: "VP8/90000" + //videoRecvBitrate: , + //videoRecvCodec } } @@ -228,9 +243,6 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u switch (type) { case "Offer": console.log("Offer process."); - if (this.settings.stereo) { - data.sdp = utils.addStereo(data.sdp); - } targetcall = this.findTargetCall(from); if (targetcall) { // Hey we know this call. @@ -280,9 +292,6 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u return; } console.log("Answer process."); - if (this.settings.stereo) { - data.sdp = utils.addStereo(data.sdp); - } // TODO(longsleep): In case of negotiation this could switch offer and answer // and result in a offer sdp sent as answer data. We need to handle this. targetcall.setRemoteDescription(new window.RTCSessionDescription(data), function() { diff --git a/static/partials/settings.html b/static/partials/settings.html index f9ad3a99..1bbfd2a0 100644 --- a/static/partials/settings.html +++ b/static/partials/settings.html @@ -130,17 +130,6 @@
-
- -
-
- -
-
-
-
@@ -202,6 +191,18 @@
+
+ +
+
+ +
+ {{_('Sending stereo audio disables echo cancellation. Enable only if you have a stereo microphone.')}} +
+
+