diff --git a/bigbluebutton-html5/imports/api/audio/client/bridge/kurento.js b/bigbluebutton-html5/imports/api/audio/client/bridge/kurento.js
index 97f0cbbe95..812d9f1c9f 100755
--- a/bigbluebutton-html5/imports/api/audio/client/bridge/kurento.js
+++ b/bigbluebutton-html5/imports/api/audio/client/bridge/kurento.js
@@ -8,6 +8,7 @@ import {
getMappedFallbackStun
} from '/imports/utils/fetchStunTurnServers';
import getFromMeetingSettings from '/imports/ui/services/meeting-settings';
+import { shouldForceRelay } from '/imports/ui/services/bbb-webrtc-sfu/utils';
const SFU_URL = Meteor.settings.public.kurento.wsUrl;
const DEFAULT_LISTENONLY_MEDIA_SERVER = Meteor.settings.public.kurento.listenOnlyMediaServer;
@@ -267,6 +268,7 @@ export default class KurentoAudioBridge extends BaseAudioBridge {
offering: OFFERING,
mediaServer: getMediaServerAdapter(),
signalCandidates: SIGNAL_CANDIDATES,
+ forceRelay: shouldForceRelay(),
};
this.broker = new ListenOnlyBroker(
diff --git a/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js b/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js
index 157dafa555..e63eac9d8d 100755
--- a/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js
+++ b/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js
@@ -4,6 +4,7 @@ import BridgeService from './service';
import ScreenshareBroker from '/imports/ui/services/bbb-webrtc-sfu/screenshare-broker';
import { setSharingScreen, screenShareEndAlert } from '/imports/ui/components/screenshare/service';
import { SCREENSHARING_ERRORS } from './errors';
+import { shouldForceRelay } from '/imports/ui/services/bbb-webrtc-sfu/utils';
const SFU_CONFIG = Meteor.settings.public.kurento;
const SFU_URL = SFU_CONFIG.wsUrl;
@@ -227,6 +228,7 @@ export default class KurentoScreenshareBridge {
offering: OFFERING,
mediaServer: BridgeService.getMediaServerAdapter(),
signalCandidates: SIGNAL_CANDIDATES,
+ forceRelay: shouldForceRelay(),
};
this.broker = new ScreenshareBroker(
@@ -287,6 +289,7 @@ export default class KurentoScreenshareBridge {
offering: true,
mediaServer: BridgeService.getMediaServerAdapter(),
signalCandidates: SIGNAL_CANDIDATES,
+ forceRelay: shouldForceRelay(),
};
this.broker = new ScreenshareBroker(
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
index 61629d74db..d85dd782f2 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx
@@ -19,6 +19,7 @@ import {
getSessionVirtualBackgroundInfo,
} from '/imports/ui/services/virtual-background/service';
import { notify } from '/imports/ui/services/notification';
+import { shouldForceRelay } from '/imports/ui/services/bbb-webrtc-sfu/utils';
// Default values and default empty object to be backwards compat with 2.2.
// FIXME Remove hardcoded defaults 2.3.
@@ -605,6 +606,9 @@ class VideoProvider extends Component {
video: constraints,
},
onicecandidate: this._getOnIceCandidateCallback(stream, isLocal),
+ configuration: {
+ iceTransportPolicy: shouldForceRelay() ? 'relay' : undefined,
+ }
};
try {
@@ -623,7 +627,6 @@ class VideoProvider extends Component {
iceServers = getMappedFallbackStun();
} finally {
if (iceServers.length > 0) {
- peerOptions.configuration = {};
peerOptions.configuration.iceServers = iceServers;
}
diff --git a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/listenonly-broker.js b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/listenonly-broker.js
index b5e606c24c..f98eb5e129 100644
--- a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/listenonly-broker.js
+++ b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/listenonly-broker.js
@@ -34,10 +34,9 @@ class ListenOnlyBroker extends BaseBroker {
video: false,
},
onicecandidate: this.signalCandidates ? this.onIceCandidate.bind(this) : null,
+ configuration: this.populatePeerConfiguration(),
};
- this.addIceServers(options);
-
this.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, (error) => {
if (error) {
// 1305: "PEER_NEGOTIATION_FAILED",
diff --git a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/screenshare-broker.js b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/screenshare-broker.js
index 589ff51d58..adaedc9e62 100644
--- a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/screenshare-broker.js
+++ b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/screenshare-broker.js
@@ -157,9 +157,9 @@ class ScreenshareBroker extends BaseBroker {
const options = {
onicecandidate: this.signalCandidates ? this.onIceCandidate.bind(this) : null,
videoStream: this.stream,
+ configuration: this.populatePeerConfiguration(),
};
- this.addIceServers(options);
this.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, (error) => {
if (error) {
// 1305: "PEER_NEGOTIATION_FAILED",
@@ -226,10 +226,9 @@ class ScreenshareBroker extends BaseBroker {
audio: !!this.hasAudio,
},
onicecandidate: this.signalCandidates ? this.onIceCandidate.bind(this) : null,
+ configuration: this.populatePeerConfiguration(),
};
- this.addIceServers(options);
-
this.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, (error) => {
if (error) {
// 1305: "PEER_NEGOTIATION_FAILED",
diff --git a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/sfu-base-broker.js b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/sfu-base-broker.js
index 6916df0a97..ecde2e88d8 100644
--- a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/sfu-base-broker.js
+++ b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/sfu-base-broker.js
@@ -25,6 +25,7 @@ class BaseBroker {
this.started = false;
this.signallingTransportOpen = false;
this.logCodePrefix = `${this.sfuComponent}_broker`;
+ this.peerConfiguration = {};
this.onbeforeunload = this.onbeforeunload.bind(this);
window.addEventListener('beforeunload', this.onbeforeunload);
@@ -168,13 +169,23 @@ class BaseBroker {
}
}
- addIceServers (options) {
- if (this.iceServers && this.iceServers.length > 0) {
- options.configuration = {};
- options.configuration.iceServers = this.iceServers;
+ populatePeerConfiguration () {
+ this.addIceServers();
+ if (this.forceRelay) {
+ this.setRelayTransportPolicy();
}
- return options;
+ return this.peerConfiguration;
+ }
+
+ addIceServers () {
+ if (this.iceServers && this.iceServers.length > 0) {
+ this.peerConfiguration.iceServers = this.iceServers;
+ }
+ }
+
+ setRelayTransportPolicy () {
+ this.peerConfiguration.iceTransportPolicy = 'relay';
}
handleConnectionStateChange (eventIdentifier) {
diff --git a/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/utils.js b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/utils.js
new file mode 100644
index 0000000000..15cbbc905a
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/services/bbb-webrtc-sfu/utils.js
@@ -0,0 +1,23 @@
+import browserInfo from '/imports/utils/browserInfo';
+import deviceInfo from '/imports/utils/deviceInfo';
+
+const FORCE_RELAY_ON_FF = Meteor.settings.public.kurento.forceRelayOnFirefox;
+
+/*
+ * Whether TURN/relay usage should be forced to work around Firefox's lack of
+ * support for regular nomination when dealing with ICE-litee peers (e.g.:
+ * mediasoup). See: https://bugzilla.mozilla.org/show_bug.cgi?id=1034964
+ *
+ * iOS endpoints are ignored from the trigger because _all_ iOS browsers
+ * are either native WebKit or WKWebView based (so they shouldn't be affected)
+ */
+const shouldForceRelay = () => {
+ const { isFirefox } = browserInfo;
+ const { isIos } = deviceInfo;
+
+ return (isFirefox && !isIos) && FORCE_RELAY_ON_FF;
+};
+
+export {
+ shouldForceRelay,
+};
diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml
index 2511ebe05e..6ccb4f9151 100755
--- a/bigbluebutton-html5/private/config/settings.yml
+++ b/bigbluebutton-html5/private/config/settings.yml
@@ -200,6 +200,7 @@ public:
# Experiment(al). Controls whether ICE candidates should be signaled.
# Applies to webcams, listen only and screen sharing. True is "stable behavior".
signalCandidates: true
+ forceRelayOnFirefox: false
cameraTimeouts:
# Base camera timeout: used as the camera *sharing* timeout and
# as the minimum camera subscribe reconnection timeout