forwardTimeForCrossBoundary() {
    if (this.config_.crossBoundaryStrategy ===
          shaka.config.CrossBoundaryStrategy.KEEP) {
      // When crossBoundaryStrategy changed to keep mid stream, we can bail
      // out early.
      return;
    }

    // Stop timer first, in case someone seeked back during the time a timer
    // was scheduled.
    this.crossBoundaryTimer_.stop();

    const presentationTime = this.playerInterface_.getPresentationTime();

    const ContentType = shaka.util.ManifestParserUtils.ContentType;
    const mediaState = this.mediaStates_.get(ContentType.VIDEO) ||
      this.mediaStates_.get(ContentType.AUDIO);
    if (!mediaState) {
      return;
    }

    const lastInitRef = mediaState.lastInitSegmentReference;
    if (!lastInitRef || lastInitRef.boundaryEnd === null) {
      return;
    }

    const threshold = shaka.media.StreamingEngine.CROSS_BOUNDARY_END_THRESHOLD_;
    const fromEnd = lastInitRef.boundaryEnd - presentationTime;
    // Check if within threshold and not smaller than 0 to eliminate
    // a backwards seek.
    if (fromEnd < 0 || fromEnd > threshold) {
      return;
    }

    // Set the intended time to seek to in order to cross the boundary.
    this.boundaryTime_ = lastInitRef.boundaryEnd +
        shaka.media.StreamingEngine.APPEND_WINDOW_END_FUDGE_;
    // Schedule a time tick when the boundary theoretically should be reached,
    // else we'd risk getting stalled if a waiting event doesn't come due to
    // a segment misalignment near a boundary.
    this.crossBoundaryTimer_.tickAfter(fromEnd);
  }
Checks if need to push time forward to cross a boundary. If so,
an MSE reset will happen. If the strategy is KEEP, this logic is skipped.
Called on timeupdate to schedule a theoretical, future, offset or on
waiting, which is another indicator we might need to cross a boundary.