Browse Source

Merge pull request #173 from longsleep/send-stereo

Bring back send stereo support by processing sdp correctly and disable A...
pull/176/head
Simon Eisenmann 11 years ago
parent
commit
fff2fef963
  1. 5
      static/js/controllers/mediastreamcontroller.js
  2. 6
      static/js/directives/settings.js
  3. 29
      static/js/mediastream/peercall.js
  4. 555
      static/js/mediastream/utils.js
  5. 25
      static/js/mediastream/webrtc.js
  6. 23
      static/partials/settings.html

5
static/js/controllers/mediastreamcontroller.js

@ -140,7 +140,7 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
message: null, message: null,
settings: { settings: {
videoQuality: "high", videoQuality: "high",
stereo: true, sendStereo: false,
maxFrameRate: 20, maxFrameRate: 20,
defaultRoom: "", defaultRoom: "",
language: "", language: "",
@ -226,9 +226,6 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
} }
mediaStream.webrtc.settings.pcConfig.iceServers = iceServers; mediaStream.webrtc.settings.pcConfig.iceServers = iceServers;
// Stereo.
mediaStream.webrtc.settings.stereo = settings.stereo;
// Refresh constraints. // Refresh constraints.
constraints.refresh($scope.master.settings); constraints.refresh($scope.master.settings);

6
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); 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 { } else {
// Other browsers constraints (there are none as of now.); // Other browsers constraints (there are none as of now.);

29
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.pcConstraints = $.extend(true, {}, this.webrtc.settings.pcConstraints);
this.sdpConstraints = $.extend(true, {}, this.webrtc.settings.sdpConstraints); this.sdpConstraints = $.extend(true, {}, this.webrtc.settings.sdpConstraints);
this.offerConstraints = $.extend(true, {}, this.webrtc.settings.offerConstraints); this.offerConstraints = $.extend(true, {}, this.webrtc.settings.offerConstraints);
this.sdpParams = $.extend(true, {}, this.webrtc.settings.sdpParams);
this.peerconnection = null; this.peerconnection = null;
this.datachannels = {}; this.datachannels = {};
@ -84,8 +85,7 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
PeerCall.prototype.onCreateAnswerOffer = function(cb, sessionDescription) { PeerCall.prototype.onCreateAnswerOffer = function(cb, sessionDescription) {
// Prefer Opus. this.setLocalSdp(sessionDescription);
sessionDescription.sdp = utils.preferOpus(sessionDescription.sdp);
// Convert to object to allow custom property injection. // Convert to object to allow custom property injection.
var sessionDescriptionObj = sessionDescription; var sessionDescriptionObj = sessionDescription;
@ -128,6 +128,9 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
console.log("Got a remote description but not connected -> ignored."); console.log("Got a remote description but not connected -> ignored.");
return; return;
} }
this.setRemoteSdp(sessionDescription);
peerconnection.setRemoteDescription(sessionDescription, _.bind(function() { peerconnection.setRemoteDescription(sessionDescription, _.bind(function() {
console.log("Set remote session description.", sessionDescription, this); console.log("Set remote session description.", sessionDescription, this);
if (cb) { 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) { PeerCall.prototype.onIceCandidate = function(event) {
if (event.candidate) { if (event.candidate) {
//console.log("ice candidate", event.candidate); //console.log("ice candidate", event.candidate);

555
static/js/mediastream/utils.js

@ -1,162 +1,423 @@
/* /*
* Spreed WebRTC. * Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG * Copyright (C) 2013-2015 struktur AG
* *
* This file is part of Spreed WebRTC. * This file is part of Spreed WebRTC.
* *
* This program is free software: you can redistribute it and/or modify * This file is a AMD wrapped version of the sdputils.js from the
* it under the terms of the GNU Affero General Public License as published by * WebRTC apprtc example. https://github.com/webrtc/apprtc/blob/master/src
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
* 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.
* *
* You should have received a copy of the GNU Affero General Public License * Redistribution and use in source and binary forms, with or without
* along with this program. If not, see <http://www.gnu.org/licenses/>. * 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"; "use strict";
define([], function() { define([], function() {
var Utils = function() {} function trace(text) {
// noop
Utils.prototype.mergeConstraints = function(cons1, cons2) { }
var merged = cons1;
var name; function mergeConstraints(cons1, cons2) {
for (name in cons2.mandatory) { if (!cons1 || !cons2) {
if (cons2.mandatory.hasOwnProperty(name)) { return cons1 || cons2;
merged.mandatory[name] = cons2.mandatory[name]; }
} var merged = cons1;
} for (var name in cons2.mandatory) {
merged.optional.concat(cons2.optional); if (cons2.mandatory.hasOwnProperty(name)) {
return merged; merged.mandatory[name] = cons2.mandatory[name];
}; }
}
Utils.prototype.extractSdp = function(sdpLine, pattern) { merged.optional = merged.optional.concat(cons2.optional);
var result = sdpLine.match(pattern); return merged;
return (result && result.length == 2) ? result[1] : null; }
};
function iceCandidateType(candidateStr) {
Utils.prototype.addStereo = function(sdp) { return candidateStr.split(' ')[7];
// Set Opus in Stereo. }
var sdpLines = sdp.split('\r\n'); function maybeSetOpusOptions(sdp, params) {
var opusPayload = ""; // Set Opus in Stereo, if stereo is true, unset it, if stereo is false, and
var fmtpLineIndex = null; // do nothing if otherwise.
var i; if (params.opusStereo === 'true') {
sdp = setCodecParam(sdp, 'opus/48000', 'stereo', '1');
// Find opus payload. } else if (params.opusStereo === 'false') {
for (i = 0; i < sdpLines.length; i++) { sdp = removeCodecParam(sdp, 'opus/48000', 'stereo');
if (sdpLines[i].search('opus/48000') !== -1) { }
opusPayload = this.extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
break; // 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');
// Find the payload in fmtp line. } else if (params.opusFec === 'false') {
for (i = 0; i < sdpLines.length; i++) { sdp = removeCodecParam(sdp, 'opus/48000', 'useinbandfec');
if (sdpLines[i].search('a=fmtp') !== -1) { }
var payload = this.extractSdp(sdpLines[i], /a=fmtp:(\d+)/);
if (payload === opusPayload) { // Set Opus maxplaybackrate, if requested.
fmtpLineIndex = i; if (params.opusMaxPbr) {
break; sdp = setCodecParam(
} sdp, 'opus/48000', 'maxplaybackrate', params.opusMaxPbr);
} }
} return sdp;
// No fmtp line found. }
if (fmtpLineIndex === null) {
console.log("Unable to add stereo (no fmtp line for opus payload)", opusPayload); function maybeSetAudioSendBitRate(sdp, params) {
return sdp; if (!params.audioSendBitrate) {
} return sdp;
}
// Append stereo=1 to fmtp line. trace('Prefer audio send bitrate: ' + params.audioSendBitrate);
sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat(' stereo=1'); return preferBitRate(sdp, params.audioSendBitrate, 'audio');
}
sdp = sdpLines.join('\r\n');
console.log("Enabled opus stereo."); function maybeSetAudioReceiveBitRate(sdp, params) {
return sdp; if (!params.audioRecvBitrate) {
return sdp;
}; }
trace('Prefer audio receive bitrate: ' + params.audioRecvBitrate);
Utils.prototype.preferOpus = function(sdp) { return preferBitRate(sdp, params.audioRecvBitrate, 'audio');
// Set Opus as the preferred codec in SDP if Opus is present. }
var sdpLines = sdp.split('\r\n'); function maybeSetVideoSendBitRate(sdp, params) {
var mLineIndex = null; if (!params.videoSendBitrate) {
var i; return sdp;
}
// Search for m line. trace('Prefer video send bitrate: ' + params.videoSendBitrate);
for (i = 0; i < sdpLines.length; i++) { return preferBitRate(sdp, params.videoSendBitrate, 'video');
if (sdpLines[i].search('m=audio') !== -1) { }
mLineIndex = i;
break; function maybeSetVideoReceiveBitRate(sdp, params) {
} if (!params.videoRecvBitrate) {
} return sdp;
if (mLineIndex === null) { }
return sdp; trace('Prefer video receive bitrate: ' + params.videoRecvBitrate);
} return preferBitRate(sdp, params.videoRecvBitrate, 'video');
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) { // Add a b=AS:bitrate line to the m=mediaType section.
if (sdpLines[i].search('opus/48000') !== -1) { function preferBitRate(sdp, bitrate, mediaType) {
var opusPayload = this.extractSdp(sdpLines[i], /:(\d+) opus\/48000/i); var sdpLines = sdp.split('\r\n');
if (opusPayload) {
sdpLines[mLineIndex] = this.setDefaultCodec(sdpLines[mLineIndex], opusPayload); // Find m line for the given mediaType.
} var mLineIndex = findLine(sdpLines, 'm=', mediaType);
break; if (mLineIndex === null) {
} trace('Failed to add bandwidth line to sdp, as no m-line found');
} return sdp;
}
// Remove CN in m line and sdp.
sdpLines = this.removeCN(sdpLines, mLineIndex); // Find next m-line if any.
var nextMLineIndex = findLineInRange(sdpLines, mLineIndex + 1, -1, 'm=');
sdp = sdpLines.join('\r\n'); if (nextMLineIndex === null) {
return sdp; nextMLineIndex = sdpLines.length;
}
};
// Find c-line corresponding to the m-line.
Utils.prototype.setDefaultCodec = function(mLine, payload) { var cLineIndex = findLineInRange(sdpLines, mLineIndex + 1,
// Set the selected codec to the first in m line. nextMLineIndex, 'c=');
var elements = mLine.split(' '); if (cLineIndex === null) {
var newLine = []; trace('Failed to add bandwidth line to sdp, as no c-line found');
var index = 0; return sdp;
for (var i = 0; i < elements.length; i++) { }
// Format of media starts from the fourth.
if (index === 3) { // Check if bandwidth line already exists between c-line and next m-line.
newLine[index++] = payload; // Put target payload to the first. var bLineIndex = findLineInRange(sdpLines, cLineIndex + 1,
} nextMLineIndex, 'b=AS');
if (elements[i] !== payload) { if (bLineIndex) {
newLine[index++] = elements[i]; sdpLines.splice(bLineIndex, 1);
} }
}
return newLine.join(' '); // Create the b (bandwidth) sdp line.
}; var bwLine = 'b=AS:' + bitrate;
// As per RFC 4566, the b line should follow after c-line.
Utils.prototype.removeCN = function(sdpLines, mLineIndex) { sdpLines.splice(cLineIndex + 1, 0, bwLine);
// Strip CN from sdp before CN constraints is ready. sdp = sdpLines.join('\r\n');
var mLineElements = sdpLines[mLineIndex].split(' '); return sdp;
// 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); // Add an a=fmtp: x-google-min-bitrate=kbps line, if videoSendInitialBitrate
if (payload) { // is specified. We'll also add a x-google-min-bitrate value, since the max
var cnPos = mLineElements.indexOf(payload); // must be >= the min.
if (cnPos !== -1) { function maybeSetVideoSendInitialBitRate(sdp, params) {
// Remove CN payload from m line. var initialBitrate = params.videoSendInitialBitrate;
mLineElements.splice(cnPos, 1); if (!initialBitrate) {
} return sdp;
// Remove CN line in sdp }
sdpLines.splice(i, 1);
} // Validate the initial bitrate value.
} var maxBitrate = initialBitrate;
sdpLines[mLineIndex] = mLineElements.join(' '); var bitrate = params.videoSendBitrate;
return sdpLines; if (bitrate) {
}; if (initialBitrate > bitrate) {
trace('Clamping initial bitrate to max bitrate of ' +
var utils = new Utils(); bitrate + ' kbps.');
return utils; 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
}
}); });

25
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. // Settings.are cloned into peer call on call creation.
this.settings = { this.settings = {
stereo: false,
mediaConstraints: { mediaConstraints: {
audio: true, audio: true,
video: { video: {
@ -76,6 +75,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
}] }]
}, },
pcConstraints: { pcConstraints: {
mandatory: {},
optional: [] optional: []
}, },
// Set up audio and video regardless of what devices are present. // Set up audio and video regardless of what devices are present.
@ -83,7 +83,8 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
mandatory: { mandatory: {
OfferToReceiveAudio: true, OfferToReceiveAudio: true,
OfferToReceiveVideo: true OfferToReceiveVideo: true
} },
optional: []
}, },
offerConstraints: { offerConstraints: {
mandatory: {}, mandatory: {},
@ -97,6 +98,20 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
mandatory: {} 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) { switch (type) {
case "Offer": case "Offer":
console.log("Offer process."); console.log("Offer process.");
if (this.settings.stereo) {
data.sdp = utils.addStereo(data.sdp);
}
targetcall = this.findTargetCall(from); targetcall = this.findTargetCall(from);
if (targetcall) { if (targetcall) {
// Hey we know this call. // Hey we know this call.
@ -280,9 +292,6 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
return; return;
} }
console.log("Answer process."); 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 // 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. // and result in a offer sdp sent as answer data. We need to handle this.
targetcall.setRemoteDescription(new window.RTCSessionDescription(data), function() { targetcall.setRemoteDescription(new window.RTCSessionDescription(data), function() {

23
static/partials/settings.html

@ -130,17 +130,6 @@
<div> <div>
<div class="form-group">
<label class="col-xs-4 control-label">{{_('Stereo audio')}}</label>
<div class="col-xs-8">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="user.settings.stereo"/>&nbsp;
</label>
</div>
</div>
</div>
<div class="form-group" ng-show="isChrome && supported.renderToAssociatedSink"> <div class="form-group" ng-show="isChrome && supported.renderToAssociatedSink">
<label class="col-xs-4 control-label">{{_('Play audio on same device as selected microphone')}}</label> <label class="col-xs-4 control-label">{{_('Play audio on same device as selected microphone')}}</label>
<div class="col-xs-8"> <div class="col-xs-8">
@ -202,6 +191,18 @@
</div> </div>
</div> </div>
<div class="form-group" ng-show="isChrome">
<label class="col-xs-4 control-label">{{_('Send stereo audio')}}</label>
<div class="col-xs-8">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="user.settings.sendStereo"/>&nbsp;
</label>
</div>
<span class="help-block">{{_('Sending stereo audio disables echo cancellation. Enable only if you have a stereo microphone.')}}</span>
</div>
</div>
<div ng-show="user.settings.experimental.enabled"> <div ng-show="user.settings.experimental.enabled">
<div class="form-group" ng-show="isChrome"> <div class="form-group" ng-show="isChrome">

Loading…
Cancel
Save