createSegments_(playlist, mediaSequenceToStartTime, variables,
      getUris, type) {
    /** @type {Array<!shaka.hls.Segment>} */
    const hlsSegments = playlist.segments;
    goog.asserts.assert(hlsSegments.length, 'Playlist should have segments!');

    /** @type {shaka.media.InitSegmentReference} */
    let initSegmentRef;

    /** @type {shaka.extern.aesKey|undefined} */
    let aesKey = undefined;

    let discontinuitySequence = shaka.hls.Utils.getFirstTagWithNameAsNumber(
        playlist.tags, 'EXT-X-DISCONTINUITY-SEQUENCE', -1);
    const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
        playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
    const skipTag = shaka.hls.Utils.getFirstTagWithName(
        playlist.tags, 'EXT-X-SKIP');
    const skippedSegments =
        skipTag ? Number(skipTag.getAttributeValue('SKIPPED-SEGMENTS')) : 0;
    let position = mediaSequenceNumber + skippedSegments;
    let firstStartTime = 0;

    // For live stream, use the cached value in the mediaSequenceToStartTime
    // map if available.
    if (this.isLive_() && mediaSequenceToStartTime.has(position)) {
      firstStartTime = mediaSequenceToStartTime.get(position);
    }
    // This is for recovering from disconnects.
    if (firstStartTime === 0 &&
        this.presentationType_ == shaka.hls.HlsParser.PresentationType_.LIVE &&
        mediaSequenceToStartTime.size > 0 &&
        !mediaSequenceToStartTime.has(position) &&
        this.presentationTimeline_.getPresentationStartTime() != null) {
      firstStartTime = this.presentationTimeline_.getSegmentAvailabilityStart();
    }

    /** @type {!Array<!shaka.media.SegmentReference>} */
    const references = [];

    let previousReference = null;

    /** @type {!Array<{bitrate: number, duration: number}>} */
    const bitrates = [];

    for (let i = 0; i < hlsSegments.length; i++) {
      const item = hlsSegments[i];
      const startTime =
          (i == 0) ? firstStartTime : previousReference.endTime;
      position = mediaSequenceNumber + skippedSegments + i;

      const discontinuityTag = shaka.hls.Utils.getFirstTagWithName(
          item.tags, 'EXT-X-DISCONTINUITY');
      if (discontinuityTag) {
        discontinuitySequence++;

        if (previousReference && previousReference.initSegmentReference) {
          previousReference.initSegmentReference.boundaryEnd = startTime;
        }
      }

      // Apply new AES tags as you see them, keeping a running total.
      for (const drmTag of item.tags) {
        if (drmTag.name == 'EXT-X-KEY') {
          if (this.isAesMethod_(drmTag.getRequiredAttrValue('METHOD'))) {
            aesKey =
                this.parseAESDrmTag_(drmTag, playlist, getUris, variables);
          } else {
            aesKey = undefined;
          }
        }
      }

      mediaSequenceToStartTime.set(position, startTime);

      initSegmentRef = this.getInitSegmentReference_(playlist,
          item.tags, getUris, variables);

      const reference = this.createSegmentReference_(
          initSegmentRef,
          previousReference,
          item,
          startTime,
          variables,
          playlist,
          type,
          getUris,
          aesKey);

      if (reference) {
        const bitrate = shaka.hls.Utils.getFirstTagWithNameAsNumber(
            item.tags, 'EXT-X-BITRATE');
        if (bitrate) {
          bitrates.push({
            bitrate,
            duration: reference.endTime - reference.startTime,
          });
        } else if (bitrates.length) {
          // It applies to every segment between it and the next EXT-X-BITRATE,
          // so we use the latest bitrate value
          const prevBitrate = bitrates.pop();
          prevBitrate.duration += reference.endTime - reference.startTime;
          bitrates.push(prevBitrate);
        }

        previousReference = reference;
        reference.discontinuitySequence = discontinuitySequence;

        if (this.ignoreManifestProgramDateTimeFor_(type) &&
            this.minSequenceNumber_ != null &&
            position < this.minSequenceNumber_) {
          // This segment is ignored as part of our fallback synchronization
          // method.
        } else {
          references.push(reference);
        }
      }
    }

    let bandwidth = undefined;
    if (bitrates.length) {
      const duration = bitrates.reduce((sum, value) => {
        return sum + value.duration;
      }, 0);
      bandwidth = Math.round(bitrates.reduce((sum, value) => {
        return sum + value.bitrate * value.duration;
      }, 0) / duration * 1000);
    }

    // If some segments have sync times, but not all, extrapolate the sync
    // times of the ones with none.
    const someSyncTime = references.some((ref) => ref.syncTime != null);
    if (someSyncTime) {
      for (let i = 0; i < references.length; i++) {
        const reference = references[i];
        if (reference.syncTime != null) {
          // No need to extrapolate.
          continue;
        }
        // Find the nearest segment with syncTime, in either direction.
        // This looks forward and backward simultaneously, keeping track of what
        // to offset the syncTime it finds by as it goes.
        let forwardAdd = 0;
        let forwardI = i;
        /**
         * Look forwards one reference at a time, summing all durations as we
         * go, until we find a reference with a syncTime to use as a basis.
         * This DOES count the original reference, but DOESN'T count the first
         * reference with a syncTime (as we approach it from behind).
         * @return {?number}
         */
        const lookForward = () => {
          const other = references[forwardI];
          if (other) {
            if (other.syncTime != null) {
              return other.syncTime + forwardAdd;
            }
            forwardAdd -= other.endTime - other.startTime;
            forwardI += 1;
          }
          return null;
        };
        let backwardAdd = 0;
        let backwardI = i;
        /**
         * Look backwards one reference at a time, summing all durations as we
         * go, until we find a reference with a syncTime to use as a basis.
         * This DOESN'T count the original reference, but DOES count the first
         * reference with a syncTime (as we approach it from ahead).
         * @return {?number}
         */
        const lookBackward = () => {
          const other = references[backwardI];
          if (other) {
            if (other != reference) {
              backwardAdd += other.endTime - other.startTime;
            }
            if (other.syncTime != null) {
              return other.syncTime + backwardAdd;
            }
            backwardI -= 1;
          }
          return null;
        };
        while (reference.syncTime == null) {
          reference.syncTime = lookBackward();
          if (reference.syncTime == null) {
            reference.syncTime = lookForward();
          }
        }
      }
    }

    // Split the sync times properly among partial segments.
    if (someSyncTime) {
      for (const reference of references) {
        let syncTime = reference.syncTime;
        for (const partial of reference.partialReferences) {
          partial.syncTime = syncTime;
          syncTime += partial.endTime - partial.startTime;
        }
      }
    }

    // lowestSyncTime is a value from a previous playlist update.  Use it to
    // set reference start times.  If this is the first playlist parse, we will
    // skip this step, and wait until we have sync time across stream types.
    const lowestSyncTime = this.lowestSyncTime_;
    if (someSyncTime && lowestSyncTime != Infinity) {
      if (!this.ignoreManifestProgramDateTimeFor_(type)) {
        for (const reference of references) {
          reference.syncAgainst(lowestSyncTime);
        }
      }
    }

    return {
      segments: references,
      bandwidth,
    };
  }
Parses shaka.hls.Segment objects into shaka.media.SegmentReferences and
get the bandwidth necessary for this segments If it's defined in the
playlist.

@param {!shaka.hls.Playlist} playlist
@param {!Map<number, number>} mediaSequenceToStartTime
@param {!Map<string, string>} variables
@param {function(): !Array<string>} getUris
@param {string} type
@return {{segments: !Array<!shaka.media.SegmentReference>,
         bandwidth: (number|undefined)}}
@private