src/snapshot.js
/*
 * Attempts to modify the specified player so that its state is equivalent to
 * the state of the snapshot.
 * @param {Object} player - the videojs player object
 * @param {Object} snapshotObject - the player state to apply
 */
Attempts to modify the specified player so that its state is equivalent to
the state of the snapshot.
@param {Object} player - the videojs player object
@param {Object} snapshotObject - the player state to apply
Attempts to modify the specified player so that its state is equivalent to the state of the snapshot . @param { Object } player - the videojs player object @param { Object } snapshotObject - the player state to apply
function restorePlayerSnapshot(player, callback) {
  const snapshotObject = player.ads.snapshot;
  if (callback === undefined) {
    callback = () => {};
  }
  if (player.ads.disableNextSnapshotRestore === true) {
    player.ads.disableNextSnapshotRestore = false;
    delete player.ads.snapshot;
    callback();
    return;
  }
  let tech = player.$('.vjs-tech');
  let attempts = 20;
  const suppressedTracks = snapshotObject.suppressedTracks;
  let trackSnapshot;
  const restoreTracks = function() {
    for (let i = 0; i < suppressedTracks.length; i++) {
      trackSnapshot = suppressedTracks[i];
      trackSnapshot.track.mode = trackSnapshot.mode;
    }
  };
  const resume = function() {
    let currentTime;
    if (videojs.browser.IS_IOS && player.ads.isLive(player)) {
      if (snapshotObject.currentTime < 0) {
        if (player.seekable().length > 0) {
          currentTime = player.seekable().end(0) + snapshotObject.currentTime;
        } else {
          currentTime = player.currentTime();
        }
        player.currentTime(currentTime);
      }
      if (player.paused()) {
        const playPromise = player.play();
        if (playPromise && playPromise.catch) {
          playPromise.catch((error) => {
            videojs.log.warn('Play promise rejected in IOS snapshot resume', error);
          });
        }
      }
    } else if (snapshotObject.ended) {
      player.currentTime(player.duration());
    } else {
      player.currentTime(snapshotObject.currentTime);
      const playPromise = player.play();
      if (playPromise && playPromise.catch) {
        playPromise.catch((error) => {
          videojs.log.warn('Play promise rejected in snapshot resume', error);
        });
      }
    }
    if (player.ads.shouldRemoveAutoplay_) {
      player.autoplay(false);
      player.ads.shouldRemoveAutoplay_ = false;
    }
  };
  const tryToResume = function() {
    player.off('contentcanplay', tryToResume);
    if (tryToResumeTimeout_) {
      player.clearTimeout(tryToResumeTimeout_);
    }
    tech = player.el().querySelector('.vjs-tech');
    if (tech.readyState > 1) {
      return resume();
    }
    if (tech.seekable === undefined) {
      return resume();
    }
    if (tech.seekable.length > 0) {
      return resume();
    }
    if (attempts--) {
      player.setTimeout(tryToResume, 50);
    } else {
      try {
        resume();
      } catch (e) {
        videojs.log.warn('Failed to resume the content after an advertisement', e);
      }
    }
  };
  if ('style' in snapshotObject) {
    tech.setAttribute('style', snapshotObject.style || '');
  }
  if (player.ads.videoElementRecycled()) {
    player.one('resumeended', () => {
      delete player.ads.snapshot;
      callback();
    });
    player.one('contentloadedmetadata', restoreTracks);
    if (videojs.browser.IS_IOS && !player.autoplay()) {
      player.autoplay(true);
      player.ads.shouldRemoveAutoplay_ = true;
    }
    player.src(snapshotObject.sources);
    player.one('contentcanplay', tryToResume);
    tryToResumeTimeout_ = player.setTimeout(tryToResume, 2000);
  } else {
    restoreTracks();
    if (!player.ended()) {
      const playPromise = player.play();
      if (playPromise && playPromise.catch) {
        playPromise.catch((error) => {
          videojs.log.warn('Play promise rejected in snapshot restore', error);
        });
      }
    }
    delete player.ads.snapshot;
    callback();
  }
}
function restorePlayerSnapshot ( player , callback ) { const snapshotObject = player . ads . snapshot ; if ( callback === undefined ) { callback = ( ) => { } ; } if ( player . ads . disableNextSnapshotRestore === true ) { player . ads . disableNextSnapshotRestore = false ; delete player . ads . snapshot ; callback ( ) ; return ; } let tech = player . $ ( '.vjs-tech' ) ; let attempts = 20 ; const suppressedTracks = snapshotObject . suppressedTracks ; let trackSnapshot ; const restoreTracks = function ( ) { for ( let i = 0 ; i < suppressedTracks . length ; i ++ ) { trackSnapshot = suppressedTracks [ i ] ; trackSnapshot . track . mode = trackSnapshot . mode ; } } ; const resume = function ( ) { let currentTime ; if ( videojs . browser . IS_IOS && player . ads . isLive ( player ) ) { if ( snapshotObject . currentTime < 0 ) { if ( player . seekable ( ) . length > 0 ) { currentTime = player . seekable ( ) . end ( 0 ) + snapshotObject . currentTime ; } else { currentTime = player . currentTime ( ) ; } player . currentTime ( currentTime ) ; } if ( player . paused ( ) ) { const playPromise = player . play ( ) ; if ( playPromise && playPromise . catch ) { playPromise . catch ( ( error ) => { videojs . log . warn ( 'Play promise rejected in IOS snapshot resume' , error ) ; } ) ; } } } else if ( snapshotObject . ended ) { player . currentTime ( player . duration ( ) ) ; } else { player . currentTime ( snapshotObject . currentTime ) ; const playPromise = player . play ( ) ; if ( playPromise && playPromise . catch ) { playPromise . catch ( ( error ) => { videojs . log . warn ( 'Play promise rejected in snapshot resume' , error ) ; } ) ; } } if ( player . ads . shouldRemoveAutoplay_ ) { player . autoplay ( false ) ; player . ads . shouldRemoveAutoplay_ = false ; } } ; const tryToResume = function ( ) { player . off ( 'contentcanplay' , tryToResume ) ; if ( tryToResumeTimeout_ ) { player . clearTimeout ( tryToResumeTimeout_ ) ; } tech = player . el ( ) . querySelector ( '.vjs-tech' ) ; if ( tech . readyState > 1 ) { return resume ( ) ; } if ( tech . seekable === undefined ) { return resume ( ) ; } if ( tech . seekable . length > 0 ) { return resume ( ) ; } if ( attempts -- ) { player . setTimeout ( tryToResume , 50 ) ; } else { try { resume ( ) ; } catch ( e ) { videojs . log . warn ( 'Failed to resume the content after an advertisement' , e ) ; } } } ; if ( 'style' in snapshotObject ) { tech . setAttribute ( 'style' , snapshotObject . style || '' ) ; } if ( player . ads . videoElementRecycled ( ) ) { player . one ( 'resumeended' , ( ) => { delete player . ads . snapshot ; callback ( ) ; } ) ; player . one ( 'contentloadedmetadata' , restoreTracks ) ; if ( videojs . browser . IS_IOS && ! player . autoplay ( ) ) { player . autoplay ( true ) ; player . ads . shouldRemoveAutoplay_ = true ; } player . src ( snapshotObject . sources ) ; player . one ( 'contentcanplay' , tryToResume ) ; tryToResumeTimeout_ = player . setTimeout ( tryToResume , 2000 ) ; } else { restoreTracks ( ) ; if ( ! player . ended ( ) ) { const playPromise = player . play ( ) ; if ( playPromise && playPromise . catch ) { playPromise . catch ( ( error ) => { videojs . log . warn ( 'Play promise rejected in snapshot restore' , error ) ; } ) ; } } delete player . ads . snapshot ; callback ( ) ; } }
Attempts to modify the specified player so that its state is equivalent to
 the state of the snapshot.
Attempts to modify the specified player so that its state is equivalent to the state of the snapshot .
// The playback tech // the number of[ remaining attempts to restore the snapshot // Finish restoring the playback state. // This only happens if the content video element was reused for ad playback. // Live video on iOS has special logic to try to seek to the right place after // an ad. // Playback was behind real time, so seek backwards to match // iOS live play after restore if player was paused (would not be paused if // ad played muted behind ad) // Restore the video position after an ad. // We check snapshotObject.ended because the content starts at the beginning again // after being restored. // For postrolls, seek to the player's current duration. // It could be different from the snapshot's currentTime due to // inaccuracy in HLS. // Prerolls and midrolls, just seek to the player time before the ad. // if we added autoplay to force content loading on iOS, remove it now // that it has served its purpose // Determine if the video element has loaded enough of the snapshot source // to be ready to apply the rest of the state. // This only happens if the content video element was reused for ad playback. // tryToResume can either have been called through the `contentcanplay` // event or fired through setTimeout. // When tryToResume is called, we should make sure to clear out the other // way it could've been called by removing the listener and clearing out // the timeout. // Tech may have changed depending on the differences in sources of the // original video and that of the ad // some browsers and media aren't "seekable". // readyState greater than 1 allows for seeking without exceptions // if the tech doesn't expose the seekable time ranges, try to // resume playback immediately // if some period of the video is seekable, resume playback // delay a bit and then check again unless we're out of attempts // overwrite all css style properties to restore state precisely // Determine whether the player needs to be restored to its state // before ad playback began. With a custom ad display or burned-in // ads, the content player state hasn't been modified and so no // restoration is required // Snapshot restore is done, so now we're really finished. // on ios7, fiddling with textTracks too early will cause safari to crash // adding autoplay guarantees that Safari will load the content so we can // seek back to the correct time after ads // if we get here, the player was not originally configured to autoplay, // so we should remove it after it has served its purpose // if the src changed for ad playback, reset it // and then resume from the snapshots time once the original src has loaded // in some browsers (firefox) `canplay` may not fire correctly. // Reace the `canplay` event with a timeout. // if we didn't change the src, just restore the tracks // we don't need to check snapshotObject.ended here because the content video // element wasn't recycled // the src didn't change and this wasn't a postroll // just resume playback at the current time. // snapshot restore is complete