function restartWhenInadvertentlyStopped(localMediaTrack) {
  var log = localMediaTrack._log,
      kind = localMediaTrack.kind;

  var detectSilence = { audio: detectSilentAudio, video: detectSilentVideo }[kind];

  var el = localMediaTrack._dummyEl,
      mediaStreamTrack = localMediaTrack.mediaStreamTrack;

  var trackChangeInProgress = null;

  function checkSilence() {
    // The dummy element is paused, so play it and then detect silence.
    return el.play().then(function () {
      return detectSilence(el);
    }).then(function (isSilent) {
      if (isSilent) {
        log.warn('Silence detected');
      } else {
        log.info('Non-silence detected');
      }
      return isSilent;
    }).catch(function (error) {
      log.warn('Failed to detect silence:', error);
    }).finally(function () {
      // Pause the dummy element again.
      el.pause();
    });
  }

  function shouldReacquireTrack() {
    var _workaroundWebKitBug1208516Cleanup = localMediaTrack._workaroundWebKitBug1208516Cleanup,
        isStopped = localMediaTrack.isStopped,
        muted = localMediaTrack.mediaStreamTrack.muted;


    var isInadvertentlyStopped = isStopped && !!_workaroundWebKitBug1208516Cleanup;

    // NOTE(mmalavalli): Restart the LocalMediaTrack if:
    // 1. The app is foregrounded, and
    // 2. A restart is not already in progress, and
    // 3. The LocalMediaTrack is either muted, inadvertently stopped or silent
    return Promise.resolve().then(function () {
      return document.visibilityState === 'visible' && !trackChangeInProgress && (muted || isInadvertentlyStopped || checkSilence());
    });
  }

  function maybeRestart() {
    return Promise.race([waitForEvent(mediaStreamTrack, 'unmute'), waitForSometime(50)]).then(function () {
      return shouldReacquireTrack();
    }).then(function (shouldReacquire) {
      if (shouldReacquire && !trackChangeInProgress) {
        trackChangeInProgress = defer();

        // NOTE(mmalavalli): If we try and restart a silent MediaStreamTrack
        // without stopping it first, then a NotReadableError is raised in case of
        // video, or the restarted audio will still be silent. Hence, we stop the
        // MediaStreamTrack here.
        localMediaTrack._stop();

        localMediaTrack._restart().finally(function () {
          el = localMediaTrack._dummyEl;
          removeMediaStreamTrackListeners();
          mediaStreamTrack = localMediaTrack.mediaStreamTrack;
          addMediaStreamTrackListeners();
          trackChangeInProgress.resolve();
          trackChangeInProgress = null;
        });
      }

      // NOTE(mmalavalli): If the MediaStreamTrack ends before the DOM is visible,
      // then this makes sure that visibility callback for phase 2 is called only
      // after the MediaStreamTrack is re-acquired.
      var promise = trackChangeInProgress && trackChangeInProgress.promise || Promise.resolve();
      return promise.finally(function () {
        return localMediaRestartDeferreds.resolveDeferred(kind);
      });
    });
  }

  function onMute() {
    var log = localMediaTrack._log,
        kind = localMediaTrack.kind;

    log.info('Muted');
    log.debug('LocalMediaTrack:', localMediaTrack);

    // NOTE(mmalavalli): When a LocalMediaTrack is muted without the app being
    // backgrounded, and the inadvertently paused elements are played before it
    // is restarted, it never gets unmuted due to the WebKit Bug 213853. Hence,
    // setting this Deferred will make sure that the inadvertently paused elements
    // are played only after the LocalMediaTrack is unmuted.
    //
    // Bug: https://bugs.webkit.org/show_bug.cgi?id=213853
    //
    localMediaRestartDeferreds.startDeferred(kind);
  }

  function addMediaStreamTrackListeners() {
    mediaStreamTrack.addEventListener('ended', maybeRestart);
    mediaStreamTrack.addEventListener('mute', onMute);
    mediaStreamTrack.addEventListener('unmute', maybeRestart);
  }

  function removeMediaStreamTrackListeners() {
    mediaStreamTrack.removeEventListener('ended', maybeRestart);
    mediaStreamTrack.removeEventListener('mute', onMute);
    mediaStreamTrack.removeEventListener('unmute', maybeRestart);
  }

  // NOTE(mpatwardhan): listen for document visibility callback on phase 1.
  // this ensures that any we acquire media tracks before RemoteMediaTrack
  // tries to `play` them (in phase 2). This order is important because
  // play can fail on safari if audio is not being captured.
  documentVisibilityMonitor.onVisible(1, maybeRestart);
  addMediaStreamTrackListeners();

  return function () {
    documentVisibilityMonitor.offVisible(1, maybeRestart);
    removeMediaStreamTrackListeners();
  };
}
/**
 * Restart the given {@link LocalMediaTrack} if it has been inadvertently stopped.
 * @private
 * @param {LocalAudioTrack|LocalVideoTrack} localMediaTrack
 * @returns {function} Clean up listeners attached by the workaround
 */
Restart the given  LocalMediaTrack if it has been inadvertently stopped.
@private
@param {LocalAudioTrack|LocalVideoTrack} localMediaTrack
@returns {function} Clean up listeners attached by the workaround
Restart the given LocalMediaTrack if it has been inadvertently stopped . @private @param { LocalAudioTrack|LocalVideoTrack } localMediaTrack @returns { function } Clean up listeners attached by the workaround
function restartWhenInadvertentlyStopped(localMediaTrack) {
  var log = localMediaTrack._log,
      kind = localMediaTrack.kind;
  var detectSilence = { audio: detectSilentAudio, video: detectSilentVideo }[kind];
  var el = localMediaTrack._dummyEl,
      mediaStreamTrack = localMediaTrack.mediaStreamTrack;
  var trackChangeInProgress = null;
  function checkSilence() {
    return el.play().then(function () {
      return detectSilence(el);
    }).then(function (isSilent) {
      if (isSilent) {
        log.warn('Silence detected');
      } else {
        log.info('Non-silence detected');
      }
      return isSilent;
    }).catch(function (error) {
      log.warn('Failed to detect silence:', error);
    }).finally(function () {
      el.pause();
    });
  }
  function shouldReacquireTrack() {
    var _workaroundWebKitBug1208516Cleanup = localMediaTrack._workaroundWebKitBug1208516Cleanup,
        isStopped = localMediaTrack.isStopped,
        muted = localMediaTrack.mediaStreamTrack.muted;
    var isInadvertentlyStopped = isStopped && !!_workaroundWebKitBug1208516Cleanup;
    return Promise.resolve().then(function () {
      return document.visibilityState === 'visible' && !trackChangeInProgress && (muted || isInadvertentlyStopped || checkSilence());
    });
  }
  function maybeRestart() {
    return Promise.race([waitForEvent(mediaStreamTrack, 'unmute'), waitForSometime(50)]).then(function () {
      return shouldReacquireTrack();
    }).then(function (shouldReacquire) {
      if (shouldReacquire && !trackChangeInProgress) {
        trackChangeInProgress = defer();
        localMediaTrack._stop();
        localMediaTrack._restart().finally(function () {
          el = localMediaTrack._dummyEl;
          removeMediaStreamTrackListeners();
          mediaStreamTrack = localMediaTrack.mediaStreamTrack;
          addMediaStreamTrackListeners();
          trackChangeInProgress.resolve();
          trackChangeInProgress = null;
        });
      }
      var promise = trackChangeInProgress && trackChangeInProgress.promise || Promise.resolve();
      return promise.finally(function () {
        return localMediaRestartDeferreds.resolveDeferred(kind);
      });
    });
  }
  function onMute() {
    var log = localMediaTrack._log,
        kind = localMediaTrack.kind;
    log.info('Muted');
    log.debug('LocalMediaTrack:', localMediaTrack);
     Bug: https:bugs.webkit.org/show_bug.cgi?id=213853
    localMediaRestartDeferreds.startDeferred(kind);
  }
  function addMediaStreamTrackListeners() {
    mediaStreamTrack.addEventListener('ended', maybeRestart);
    mediaStreamTrack.addEventListener('mute', onMute);
    mediaStreamTrack.addEventListener('unmute', maybeRestart);
  }
  function removeMediaStreamTrackListeners() {
    mediaStreamTrack.removeEventListener('ended', maybeRestart);
    mediaStreamTrack.removeEventListener('mute', onMute);
    mediaStreamTrack.removeEventListener('unmute', maybeRestart);
  }
   NOTE(mpatwardhan): listen for document visibility callback on phase 1.
   this ensures that any we acquire media tracks before RemoteMediaTrack
   tries to `play` them (in phase 2). This order is important because
   play can fail on safari if audio is not being captured.
  documentVisibilityMonitor.onVisible(1, maybeRestart);
  addMediaStreamTrackListeners();
  return function () {
    documentVisibilityMonitor.offVisible(1, maybeRestart);
    removeMediaStreamTrackListeners();
  };
}
function restartWhenInadvertentlyStopped ( localMediaTrack ) { var log = localMediaTrack . _log , kind = localMediaTrack . kind ; var detectSilence = { audio : detectSilentAudio , video : detectSilentVideo } [ kind ] ; var el = localMediaTrack . _dummyEl , mediaStreamTrack = localMediaTrack . mediaStreamTrack ; var trackChangeInProgress = null ; function checkSilence ( ) { return el . play ( ) . then ( function ( ) { return detectSilence ( el ) ; } ) . then ( function ( isSilent ) { if ( isSilent ) { log . warn ( 'Silence detected' ) ; } else { log . info ( 'Non-silence detected' ) ; } return isSilent ; } ) . catch ( function ( error ) { log . warn ( 'Failed to detect silence:' , error ) ; } ) . finally ( function ( ) { el . pause ( ) ; } ) ; } function shouldReacquireTrack ( ) { var _workaroundWebKitBug1208516Cleanup = localMediaTrack . _workaroundWebKitBug1208516Cleanup , isStopped = localMediaTrack . isStopped , muted = localMediaTrack . mediaStreamTrack . muted ; var isInadvertentlyStopped = isStopped && ! ! _workaroundWebKitBug1208516Cleanup ; return Promise . resolve ( ) . then ( function ( ) { return document . visibilityState === 'visible' && ! trackChangeInProgress && ( muted || isInadvertentlyStopped || checkSilence ( ) ) ; } ) ; } function maybeRestart ( ) { return Promise . race ( [ waitForEvent ( mediaStreamTrack , 'unmute' ) , waitForSometime ( 50 ) ] ) . then ( function ( ) { return shouldReacquireTrack ( ) ; } ) . then ( function ( shouldReacquire ) { if ( shouldReacquire && ! trackChangeInProgress ) { trackChangeInProgress = defer ( ) ; localMediaTrack . _stop ( ) ; localMediaTrack . _restart ( ) . finally ( function ( ) { el = localMediaTrack . _dummyEl ; removeMediaStreamTrackListeners ( ) ; mediaStreamTrack = localMediaTrack . mediaStreamTrack ; addMediaStreamTrackListeners ( ) ; trackChangeInProgress . resolve ( ) ; trackChangeInProgress = null ; } ) ; } var promise = trackChangeInProgress && trackChangeInProgress . promise || Promise . resolve ( ) ; return promise . finally ( function ( ) { return localMediaRestartDeferreds . resolveDeferred ( kind ) ; } ) ; } ) ; } function onMute ( ) { var log = localMediaTrack . _log , kind = localMediaTrack . kind ; log . info ( 'Muted' ) ; log . debug ( 'LocalMediaTrack:' , localMediaTrack ) ; localMediaRestartDeferreds . startDeferred ( kind ) ; } function addMediaStreamTrackListeners ( ) { mediaStreamTrack . addEventListener ( 'ended' , maybeRestart ) ; mediaStreamTrack . addEventListener ( 'mute' , onMute ) ; mediaStreamTrack . addEventListener ( 'unmute' , maybeRestart ) ; } function removeMediaStreamTrackListeners ( ) { mediaStreamTrack . removeEventListener ( 'ended' , maybeRestart ) ; mediaStreamTrack . removeEventListener ( 'mute' , onMute ) ; mediaStreamTrack . removeEventListener ( 'unmute' , maybeRestart ) ; } documentVisibilityMonitor . onVisible ( 1 , maybeRestart ) ; addMediaStreamTrackListeners ( ) ; return function ( ) { documentVisibilityMonitor . offVisible ( 1 , maybeRestart ) ; removeMediaStreamTrackListeners ( ) ; } ; }
Restart the given {@link LocalMediaTrack} if it has been inadvertently stopped.
Restart the given { @link LocalMediaTrack } if it has been inadvertently stopped .
// The dummy element is paused, so play it and then detect silence. // Pause the dummy element again. // NOTE(mmalavalli): Restart the LocalMediaTrack if: // 1. The app is foregrounded, and // 2. A restart is not already in progress, and // 3. The LocalMediaTrack is either muted, inadvertently stopped or silent // NOTE(mmalavalli): If we try and restart a silent MediaStreamTrack // without stopping it first, then a NotReadableError is raised in case of // video, or the restarted audio will still be silent. Hence, we stop the // MediaStreamTrack here. // NOTE(mmalavalli): If the MediaStreamTrack ends before the DOM is visible, // then this makes sure that visibility callback for phase 2 is called only // after the MediaStreamTrack is re-acquired. // NOTE(mmalavalli): When a LocalMediaTrack is muted without the app being // backgrounded, and the inadvertently paused elements are played before it // is restarted, it never gets unmuted due to the WebKit Bug 213853. Hence, // setting this Deferred will make sure that the inadvertently paused elements // are played only after the LocalMediaTrack is unmuted. // // Bug: https://bugs.webkit.org/show_bug.cgi?id=213853 // // NOTE(mpatwardhan): listen for document visibility callback on phase 1. // this ensures that any we acquire media tracks before RemoteMediaTrack // tries to `play` them (in phase 2). This order is important because // play can fail on safari if audio is not being captured.