diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
index 4efe730cc914a0e90c76b6ee8a394c3d05315c92..972cefcc6a57e568f3e3d9ddb0078b7dcbf71e17 100755
--- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
+++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js
@@ -11,6 +11,7 @@ import iosWebviewAudioPolyfills from '/imports/utils/ios-webview-audio-polyfills
 import { monitorAudioConnection } from '/imports/utils/stats';
 import AudioErrors from './error-codes';
 import {Meteor} from "meteor/meteor";
+import browserInfo from '/imports/utils/browserInfo';
 
 const STATS = Meteor.settings.public.stats;
 const MEDIA = Meteor.settings.public.media;
@@ -20,6 +21,8 @@ const MAX_LISTEN_ONLY_RETRIES = 1;
 const LISTEN_ONLY_CALL_TIMEOUT_MS = MEDIA.listenOnlyCallTimeout || 25000;
 const DEFAULT_INPUT_DEVICE_ID = 'default';
 const DEFAULT_OUTPUT_DEVICE_ID = 'default';
+const EXPERIMENTAL_USE_KMS_TRICKLE_ICE_FOR_MICROPHONE = Meteor.settings
+  .public.app.experimentalUseKmsTrickleIceForMicrophone;
 
 const CALL_STATES = {
   STARTED: 'started',
@@ -110,12 +113,21 @@ class AudioManager {
   }
 
   async trickleIce() {
-    if (!this.listenOnlyBridge) return [];
+    const { isFirefox, isIe, isSafari } = browserInfo;
+
+    if (!this.listenOnlyBridge
+      || isFirefox
+      || isIe
+      || isSafari) return [];
 
     if (this.validIceCandidates && this.validIceCandidates.length) {
+      logger.info({ logCode: 'audiomanager_trickle_ice_reuse_candidate' },
+        'Reusing trickle-ice information before activating microphone');
       return this.validIceCandidates;
     }
 
+    logger.info({ logCode: 'audiomanager_trickle_ice_get_local_candidate' },
+      'Performing trickle-ice before activating microphone');
     this.validIceCandidates = await this.listenOnlyBridge.trickleIce() || [];
     return this.validIceCandidates;
   }
@@ -145,7 +157,11 @@ class AudioManager {
 
     return this.onAudioJoining.bind(this)()
       .then(async () => {
-        const validIceCandidates = await this.trickleIce();
+        let validIceCandidates = [];
+        if (EXPERIMENTAL_USE_KMS_TRICKLE_ICE_FOR_MICROPHONE) {
+          validIceCandidates = await this.trickleIce();
+        }
+
         const callOptions = {
           isListenOnly: false,
           extension: ECHO_TEST_NUMBER,
diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml
index 8e6918b8daea4bddf6532da91c33b81416950bd2..462f9dc9d0d51215ca3952aa322a087274c695b3 100755
--- a/bigbluebutton-html5/private/config/settings.yml
+++ b/bigbluebutton-html5/private/config/settings.yml
@@ -79,6 +79,15 @@ public:
     showAudioFilters: true
     raiseHandActionButton:
       enabled: true
+    # If enabled, before joining microphone the client will perform a trickle
+    # ICE against Kurento and use the information about successfull
+    # candidate-pairs to filter out local candidates in SIP.js's SDP.
+    # Try enabling this setting in scenarios where the listenonly mode works,
+    # but microphone doesn't (for example, when using VPN).
+    # For compatibility check "Browser compatbility" section in:
+    # https://developer.mozilla.org/en-US/docs/Web/API/RTCDtlsTransport/iceTransport
+    # This is an EXPERIMENTAL setting and the default value is false
+    # experimentalUseKmsTrickleIceForMicrophone: false
     defaultSettings:
       application:
         animations: true