async initSourceBuffer_(mediaState, reference, adaptation) {
    const ContentType = shaka.util.ManifestParserUtils.ContentType;
    const MimeUtils = shaka.util.MimeUtils;
    const StreamingEngine = shaka.media.StreamingEngine;
    const logPrefix = StreamingEngine.logPrefix_(mediaState);
    const nullLastReferences = mediaState.lastSegmentReference == null;

    /** @type {!Array<!Promise>} */
    const operations = [];

    // Rounding issues can cause us to remove the first frame of a Period, so
    // reduce the window start time slightly.
    const appendWindowStart = Math.max(0,
        Math.max(reference.appendWindowStart, this.playRangeStart_) -
        StreamingEngine.APPEND_WINDOW_START_FUDGE_);
    const appendWindowEnd =
        Math.min(reference.appendWindowEnd, this.playRangeEnd_) +
        StreamingEngine.APPEND_WINDOW_END_FUDGE_;

    goog.asserts.assert(
        reference.startTime <= appendWindowEnd,
        logPrefix + ' segment should start before append window end');

    const fullCodecs = (reference.codecs || mediaState.stream.codecs);
    const codecs = MimeUtils.getCodecBase(fullCodecs);
    const mimeType = MimeUtils.getBasicType(
        reference.mimeType || mediaState.stream.mimeType);
    const timestampOffset = reference.timestampOffset;
    if (timestampOffset != mediaState.lastTimestampOffset ||
        appendWindowStart != mediaState.lastAppendWindowStart ||
        appendWindowEnd != mediaState.lastAppendWindowEnd ||
        codecs != mediaState.lastCodecs ||
        mimeType != mediaState.lastMimeType) {
      shaka.log.v1(logPrefix, 'setting timestamp offset to ' + timestampOffset);
      shaka.log.v1(logPrefix,
          'setting append window start to ' + appendWindowStart);
      shaka.log.v1(logPrefix,
          'setting append window end to ' + appendWindowEnd);

      const isResetMediaSourceNecessary =
          mediaState.lastCodecs && mediaState.lastMimeType &&
          this.playerInterface_.mediaSourceEngine.isResetMediaSourceNecessary(
              mediaState.type, mimeType, fullCodecs);
      if (isResetMediaSourceNecessary) {
        let otherState = null;
        if (mediaState.type === ContentType.VIDEO) {
          otherState = this.mediaStates_.get(ContentType.AUDIO);
        } else if (mediaState.type === ContentType.AUDIO) {
          otherState = this.mediaStates_.get(ContentType.VIDEO);
        }
        if (otherState) {
          // First, abort all operations in progress on the other stream.
          await this.abortOperations_(otherState).catch(() => {});
          // Then clear our cache of the last init segment, since MSE will be
          // reloaded and no init segment will be there post-reload.
          otherState.lastInitSegmentReference = null;
          // Clear cache of append window start and end, since they will need
          // to be reapplied post-reload by streaming engine.
          otherState.lastAppendWindowStart = null;
          otherState.lastAppendWindowEnd = null;
          // Now force the existing buffer to be cleared.  It is not necessary
          // to perform the MSE clear operation, but this has the side-effect
          // that our state for that stream will then match MSE's post-reload
          // state.
          this.forceClearBuffer_(otherState);
        }
      }

      // Dispatching init asynchronously causes the sourceBuffers in
      // the MediaSourceEngine to become detached do to race conditions
      // with mediaSource and sourceBuffers being created simultaneously.
      await this.setProperties_(mediaState, timestampOffset, appendWindowStart,
          appendWindowEnd, reference, codecs, mimeType);
    }

    if (!shaka.media.InitSegmentReference.equal(
        reference.initSegmentReference, mediaState.lastInitSegmentReference)) {
      mediaState.lastInitSegmentReference = reference.initSegmentReference;

      if (reference.isIndependent() && reference.initSegmentReference) {
        shaka.log.v1(logPrefix, 'fetching init segment');

        const fetchInit =
            this.fetch_(mediaState, reference.initSegmentReference);
        const append = async () => {
          try {
            const initSegment = await fetchInit;
            this.destroyer_.ensureNotDestroyed();

            let lastTimescale = null;
            const timescaleMap = new Map();

            /** @type {!shaka.extern.SpatialVideoInfo} */
            const spatialVideoInfo = {
              projection: null,
              hfov: null,
            };

            if (mediaState.stream) {
              const videoLayout = mediaState.stream.videoLayout;
              if (videoLayout) {
                const layouts = videoLayout.split('/');
                if (layouts.includes('PROJ-RECT')) {
                  spatialVideoInfo.projection = 'rect';
                } else if (layouts.includes('PROJ-EQUI')) {
                  spatialVideoInfo.projection = 'equi';
                } else if (layouts.includes('PROJ-HEQU')) {
                  spatialVideoInfo.projection = 'hequ';
                } else if (layouts.includes('PROJ-PRIM')) {
                  spatialVideoInfo.projection = 'prim';
                } else if (layouts.includes('PROJ-AIV')) {
                  spatialVideoInfo.projection = 'hequ';
                }
              }
            }

            const parser = new shaka.util.Mp4Parser();
            const Mp4Parser = shaka.util.Mp4Parser;
            const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
            parser.box('moov', Mp4Parser.children)
                .box('trak', Mp4Parser.children)
                .box('mdia', Mp4Parser.children)
                .fullBox('mdhd', (box) => {
                  goog.asserts.assert(
                      box.version != null,
                      'MDHD is a full box and should have a valid version.');
                  const parsedMDHDBox = Mp4BoxParsers.parseMDHD(
                      box.reader, box.version);
                  lastTimescale = parsedMDHDBox.timescale;
                })
                .box('hdlr', (box) => {
                  const parsedHDLR = Mp4BoxParsers.parseHDLR(box.reader);
                  switch (parsedHDLR.handlerType) {
                    case 'soun':
                      timescaleMap.set(ContentType.AUDIO, lastTimescale);
                      break;
                    case 'vide':
                      timescaleMap.set(ContentType.VIDEO, lastTimescale);
                      break;
                  }
                  lastTimescale = null;
                });
            if (mediaState.type === ContentType.VIDEO &&
                !spatialVideoInfo.projection) {
              parser.box('minf', Mp4Parser.children)
                  .box('stbl', Mp4Parser.children)
                  .fullBox('stsd', Mp4Parser.sampleDescription)
                  .box('encv', Mp4Parser.visualSampleEntry)
                  .box('avc1', Mp4Parser.visualSampleEntry)
                  .box('avc3', Mp4Parser.visualSampleEntry)
                  .box('hev1', Mp4Parser.visualSampleEntry)
                  .box('hvc1', Mp4Parser.visualSampleEntry)
                  .box('dvav', Mp4Parser.visualSampleEntry)
                  .box('dva1', Mp4Parser.visualSampleEntry)
                  .box('dvh1', Mp4Parser.visualSampleEntry)
                  .box('dvhe', Mp4Parser.visualSampleEntry)
                  .box('dvc1', Mp4Parser.visualSampleEntry)
                  .box('dvi1', Mp4Parser.visualSampleEntry)
                  .box('vexu', Mp4Parser.children)
                  .box('proj', Mp4Parser.children)
                  .fullBox('prji', (box) => {
                    const parsedPRJIBox = Mp4BoxParsers.parsePRJI(box.reader);
                    spatialVideoInfo.projection = parsedPRJIBox.projection;
                  })
                  .box('hfov', (box) => {
                    const parsedHFOVBox = Mp4BoxParsers.parseHFOV(box.reader);
                    spatialVideoInfo.hfov = parsedHFOVBox.hfov;
                  });
            }
            parser.parse(initSegment);

            if (mediaState.type === ContentType.VIDEO) {
              this.updateSpatialVideoInfo_(spatialVideoInfo);
            }

            if (timescaleMap.has(mediaState.type)) {
              reference.initSegmentReference.timescale =
                  timescaleMap.get(mediaState.type);
            } else if (lastTimescale != null) {
              // Fallback for segments without HDLR box
              reference.initSegmentReference.timescale = lastTimescale;
            }

            const segmentIndex = mediaState.stream.segmentIndex;
            let continuityTimeline;
            if (segmentIndex instanceof shaka.media.MetaSegmentIndex) {
              continuityTimeline = segmentIndex.getTimelineForTime(
                  reference.startTime);
            }
            shaka.log.v1(logPrefix, 'appending init segment');
            const hasClosedCaptions = mediaState.stream.closedCaptions &&
                mediaState.stream.closedCaptions.size > 0;
            await this.playerInterface_.beforeAppendSegment(
                mediaState.type, initSegment);
            await this.playerInterface_.mediaSourceEngine.appendBuffer(
                mediaState.type, initSegment, /* reference= */ null,
                mediaState.stream, hasClosedCaptions, mediaState.seeked,
                adaptation, /* isChunkedData= */ false, /* fromSplit= */ false,
                continuityTimeline);
          } catch (error) {
            mediaState.lastInitSegmentReference = null;
            throw error;
          }
        };
        let initSegmentTime = reference.startTime;
        if (nullLastReferences) {
          const bufferEnd =
            this.playerInterface_.mediaSourceEngine.bufferEnd(mediaState.type);
          if (bufferEnd != null) {
            // Adjust init segment append time if we have something in
            // a buffer, i.e. due to running switchVariant() with non zero
            // safe margin value.
            initSegmentTime = bufferEnd;
          }
        }
        this.playerInterface_.onInitSegmentAppended(
            initSegmentTime, reference.initSegmentReference);
        operations.push(append());
      }
    }

    const lastDiscontinuitySequence =
        mediaState.lastSegmentReference ?
            mediaState.lastSegmentReference.discontinuitySequence : -1;
    // Across discontinuity bounds, we should resync timestamps.  The next
    // segment appended should land at its theoretical timestamp from the
    // segment index.
    if (reference.discontinuitySequence != lastDiscontinuitySequence) {
      operations.push(this.playerInterface_.mediaSourceEngine.resync(
          mediaState.type, reference.startTime));
    }

    await Promise.all(operations);
  }
Sets the given MediaState's associated SourceBuffer's timestamp offset,
append window, and init segment if they have changed. If an error occurs
then neither the timestamp offset or init segment are unset, since another
call to switch() will end up superseding them.

@param {shaka.media.StreamingEngine.MediaState_} mediaState
@param {!shaka.media.SegmentReference} reference
@param {boolean} adaptation
@return {!Promise}
@private