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 @@