processManifest_(mss, finalManifestUri) {
    const TXml = shaka.util.TXml;

    if (!this.presentationTimeline_) {
      this.presentationTimeline_ = new shaka.media.PresentationTimeline(
          /* presentationStartTime= */ null, /* delay= */ 0);
    }

    const isLive = TXml.parseAttr(mss, 'IsLive',
        TXml.parseBoolean, /* defaultValue= */ false);

    if (isLive) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.MSS_LIVE_CONTENT_NOT_SUPPORTED);
    }

    this.presentationTimeline_.setStatic(!isLive);

    const timescale = TXml.parseAttr(mss, 'TimeScale',
        TXml.parseNonNegativeInt, shaka.mss.MssParser.DEFAULT_TIME_SCALE_);
    goog.asserts.assert(timescale && timescale >= 0,
        'Timescale must be defined!');

    let dvrWindowLength = TXml.parseAttr(mss, 'DVRWindowLength',
        TXml.parseNonNegativeInt);
    // If the DVRWindowLength field is omitted for a live presentation or set
    // to 0, the DVR window is effectively infinite
    if (isLive && (dvrWindowLength === 0 || isNaN(dvrWindowLength))) {
      dvrWindowLength = Infinity;
    }
    // Start-over
    const canSeek = TXml.parseAttr(mss, 'CanSeek',
        TXml.parseBoolean, /* defaultValue= */ false);
    if (dvrWindowLength === 0 && canSeek) {
      dvrWindowLength = Infinity;
    }

    let segmentAvailabilityDuration = null;
    if (dvrWindowLength && dvrWindowLength > 0) {
      segmentAvailabilityDuration = dvrWindowLength / timescale;
    }

    // If it's live, we check for an override.
    if (isLive && !isNaN(this.config_.availabilityWindowOverride)) {
      segmentAvailabilityDuration = this.config_.availabilityWindowOverride;
    }

    // If it's null, that means segments are always available.  This is always
    // the case for VOD, and sometimes the case for live.
    if (segmentAvailabilityDuration == null) {
      segmentAvailabilityDuration = Infinity;
    }

    this.presentationTimeline_.setSegmentAvailabilityDuration(
        segmentAvailabilityDuration);

    // Duration in timescale units.
    const duration = TXml.parseAttr(mss, 'Duration',
        TXml.parseNonNegativeInt, Infinity);
    goog.asserts.assert(duration && duration >= 0,
        'Duration must be defined!');

    if (!isLive) {
      this.presentationTimeline_.setDuration(duration / timescale);
    }

    /** @type {!shaka.mss.MssParser.Context} */
    const context = {
      variants: [],
      textStreams: [],
      timescale: timescale,
      duration: duration / timescale,
    };

    this.parseStreamIndexes_(mss, context);

    // These steps are not done on manifest update.
    if (!this.manifest_) {
      this.manifest_ = {
        presentationTimeline: this.presentationTimeline_,
        variants: context.variants,
        textStreams: context.textStreams,
        imageStreams: [],
        offlineSessionIds: [],
        sequenceMode: this.config_.mss.sequenceMode,
        ignoreManifestTimestampsInSegmentsMode: false,
        type: shaka.media.ManifestParser.MSS,
        serviceDescription: null,
        nextUrl: null,
        periodCount: 1,
        gapCount: 0,
        isLowLatency: false,
        startTime: null,
      };

      // This is the first point where we have a meaningful presentation start
      // time, and we need to tell PresentationTimeline that so that it can
      // maintain consistency from here on.
      this.presentationTimeline_.lockStartTime();
    } else {
      // Just update the variants and text streams.
      this.manifest_.variants = context.variants;
      this.manifest_.textStreams = context.textStreams;

      // Re-filter the manifest.  This will check any configured restrictions on
      // new variants, and will pass any new init data to DrmEngine to ensure
      // that key rotation works correctly.
      this.playerInterface_.filter(this.manifest_);
    }
  }
Takes a formatted MSS and converts it into a manifest.

@param {!shaka.extern.xml.Node} mss
@param {string} finalManifestUri The final manifest URI, which may
  differ from this.manifestUri_ if there has been a redirect.
@private