createSegmentReference_(
      initSegmentReference, previousReference, hlsSegment, startTime,
      variables, playlist, type, getUris, aesKey) {
    const HlsParser = shaka.hls.HlsParser;

    const getMimeType = (uri) => {
      const parsedUri = new goog.Uri(uri);
      const extension = parsedUri.getPath().split('.').pop();
      const map = HlsParser.EXTENSION_MAP_BY_CONTENT_TYPE_.get(type);
      let mimeType = map.get(extension);
      if (!mimeType) {
        mimeType = HlsParser.RAW_FORMATS_TO_MIME_TYPES_.get(extension);
      }
      return mimeType;
    };

    const tags = hlsSegment.tags;
    const extinfTag =
        shaka.hls.Utils.getFirstTagWithName(tags, 'EXTINF');

    let endTime = 0;
    let startByte = 0;
    let endByte = null;

    if (hlsSegment.partialSegments.length) {
      this.manifest_.isLowLatency = true;
    }

    let syncTime = null;
    if (!this.config_.hls.ignoreManifestProgramDateTime) {
      const dateTimeTag =
          shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-PROGRAM-DATE-TIME');
      if (dateTimeTag && dateTimeTag.value) {
        syncTime = shaka.util.TXml.parseDate(dateTimeTag.value);
        goog.asserts.assert(syncTime != null,
            'EXT-X-PROGRAM-DATE-TIME format not valid');
        this.usesProgramDateTime_ = true;
      }
    }

    let status = shaka.media.SegmentReference.Status.AVAILABLE;
    if (shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-GAP')) {
      this.manifest_.gapCount++;
      status = shaka.media.SegmentReference.Status.MISSING;
    }

    if (!extinfTag) {
      if (hlsSegment.partialSegments.length == 0) {
        // EXTINF tag must be available if the segment has no partial segments.
        throw new shaka.util.Error(
            shaka.util.Error.Severity.CRITICAL,
            shaka.util.Error.Category.MANIFEST,
            shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
      } else if (!this.lowLatencyMode_) {
        // Without EXTINF and without low-latency mode, partial segments get
        // ignored.
        return null;
      }
    }

    // Create SegmentReferences for the partial segments.
    let partialSegmentRefs = [];

    // Optimization for LL-HLS with byterange
    // More info in https://tinyurl.com/hls-open-byte-range
    let segmentWithByteRangeOptimization = false;
    let getUrisOptimization = null;
    let somePartialSegmentWithGap = false;
    let isPreloadSegment = false;

    if (this.lowLatencyMode_ && hlsSegment.partialSegments.length) {
      const byterangeOptimizationSupport =
          initSegmentReference && window.ReadableStream &&
          this.config_.hls.allowLowLatencyByteRangeOptimization;

      let partialSyncTime = syncTime;
      for (let i = 0; i < hlsSegment.partialSegments.length; i++) {
        const item = hlsSegment.partialSegments[i];
        const pPreviousReference = i == 0 ?
          previousReference : partialSegmentRefs[partialSegmentRefs.length - 1];
        const pStartTime = (i == 0) ? startTime : pPreviousReference.endTime;

        // If DURATION is missing from this partial segment, use the target
        // partial duration from the top of the playlist, which is a required
        // attribute for content with partial segments.
        const pDuration = Number(item.getAttributeValue('DURATION')) ||
            this.partialTargetDuration_;

        // If for some reason we have neither an explicit duration, nor a target
        // partial duration, we should SKIP this partial segment to avoid
        // duplicating content in the presentation timeline.
        if (!pDuration) {
          continue;
        }

        const pEndTime = pStartTime + pDuration;

        let pStartByte = 0;
        let pEndByte = null;
        if (item.name == 'EXT-X-PRELOAD-HINT') {
          // A preload hinted partial segment may have byterange start info.
          const pByterangeStart = item.getAttributeValue('BYTERANGE-START');
          pStartByte = pByterangeStart ? Number(pByterangeStart) : 0;
          // A preload hinted partial segment may have byterange length info.
          const pByterangeLength = item.getAttributeValue('BYTERANGE-LENGTH');
          if (pByterangeLength) {
            pEndByte = pStartByte + Number(pByterangeLength) - 1;
          } else if (pStartByte) {
            // If we have a non-zero start byte, but no end byte, follow the
            // recommendation of https://tinyurl.com/hls-open-byte-range and
            // set the end byte explicitly to a large integer.
            pEndByte = Number.MAX_SAFE_INTEGER;
          }
        } else {
          const pByterange = item.getAttributeValue('BYTERANGE');
          [pStartByte, pEndByte] =
            this.parseByteRange_(pPreviousReference, pByterange);
        }
        const pUri = item.getAttributeValue('URI');
        if (!pUri) {
          continue;
        }

        let partialStatus = shaka.media.SegmentReference.Status.AVAILABLE;
        if (item.getAttributeValue('GAP') == 'YES') {
          this.manifest_.gapCount++;
          partialStatus = shaka.media.SegmentReference.Status.MISSING;
          somePartialSegmentWithGap = true;
        }

        let uris = null;
        const getPartialUris = () => {
          if (uris == null) {
            goog.asserts.assert(pUri, 'Partial uri should be defined!');
            uris = shaka.hls.Utils.constructSegmentUris(
                getUris(), pUri, variables);
          }
          return uris;
        };

        if (byterangeOptimizationSupport &&
            pStartByte >= 0 && pEndByte != null) {
          getUrisOptimization = getPartialUris;
          segmentWithByteRangeOptimization = true;
        }

        const partial = new shaka.media.SegmentReference(
            pStartTime,
            pEndTime,
            getPartialUris,
            pStartByte,
            pEndByte,
            initSegmentReference,
            /* timestampOffset= */ 0,
            /* appendWindowStart= */ 0,
            /* appendWindowEnd= */ Infinity,
            /* partialReferences= */ [],
            /* tilesLayout= */ '',
            /* tileDuration= */ null,
            partialSyncTime,
            partialStatus,
            aesKey);
        if (item.name == 'EXT-X-PRELOAD-HINT') {
          partial.markAsPreload();
          isPreloadSegment = true;
        }
        // The spec doesn't say that we can assume INDEPENDENT=YES for the
        // first partial segment. It does call the flag "optional", though, and
        // that cases where there are no such flags on any partial segments, it
        // is sensible to assume the first one is independent.
        if (item.getAttributeValue('INDEPENDENT') != 'YES' && i > 0) {
          partial.markAsNonIndependent();
        }

        const pMimeType = getMimeType(pUri);
        if (pMimeType) {
          partial.mimeType = pMimeType;
          if (HlsParser.MIME_TYPES_WITHOUT_INIT_SEGMENT_.has(pMimeType)) {
            partial.initSegmentReference = null;
          }
        }
        partialSegmentRefs.push(partial);

        if (partialSyncTime) {
          partialSyncTime += pDuration;
        }
      } // for-loop of hlsSegment.partialSegments
    }

    // If the segment has EXTINF tag, set the segment's end time, start byte
    // and end byte based on the duration and byterange information.
    // Otherwise, calculate the end time, start / end byte based on its partial
    // segments.
    // Note that the sum of partial segments durations may be slightly different
    // from the parent segment's duration. In this case, use the duration from
    // the parent segment tag.
    if (extinfTag) {
      // The EXTINF tag format is '#EXTINF:<duration>,[<title>]'.
      // We're interested in the duration part.
      const extinfValues = extinfTag.value.split(',');
      const duration = Number(extinfValues[0]);
      // Skip segments without duration
      if (duration == 0) {
        return null;
      }
      endTime = startTime + duration;
    } else if (partialSegmentRefs.length) {
      endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
    } else {
      // Skip segments without duration and without partial segments
      return null;
    }

    if (segmentWithByteRangeOptimization) {
      // We cannot optimize segments with gaps, or with a start byte that is
      // not 0.
      if (somePartialSegmentWithGap || partialSegmentRefs[0].startByte != 0) {
        segmentWithByteRangeOptimization = false;
        getUrisOptimization = null;
      } else {
        partialSegmentRefs = [];
      }
    }

    // If the segment has EXT-X-BYTERANGE tag, set the start byte and end byte
    // base on the byterange information. If segment has no EXT-X-BYTERANGE tag
    // and has partial segments, set the start byte and end byte base on the
    // partial segments.
    const byterangeTag =
         shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-BYTERANGE');
    if (byterangeTag) {
      [startByte, endByte] =
          this.parseByteRange_(previousReference, byterangeTag.value);
    } else if (partialSegmentRefs.length) {
      startByte = partialSegmentRefs[0].startByte;
      endByte = partialSegmentRefs[partialSegmentRefs.length - 1].endByte;
    }

    let tilesLayout = '';
    let tileDuration = null;
    if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
      // By default in HLS the tilesLayout is 1x1
      tilesLayout = '1x1';
      const tilesTag =
          shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
      if (tilesTag) {
        tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
        const duration = tilesTag.getAttributeValue('DURATION');
        if (duration) {
          tileDuration = Number(duration);
        }
      }
    }

    let uris = null;
    const getSegmentUris = () => {
      if (getUrisOptimization) {
        return getUrisOptimization();
      }
      if (uris == null) {
        uris = shaka.hls.Utils.constructSegmentUris(getUris(),
            hlsSegment.verbatimSegmentUri, variables);
      }
      return uris || [];
    };

    const allPartialSegments = partialSegmentRefs.length > 0 &&
        !!hlsSegment.verbatimSegmentUri;

    const reference = new shaka.media.SegmentReference(
        startTime,
        endTime,
        getSegmentUris,
        startByte,
        endByte,
        initSegmentReference,
        /* timestampOffset= */ 0,
        /* appendWindowStart= */ 0,
        /* appendWindowEnd= */ Infinity,
        partialSegmentRefs,
        tilesLayout,
        tileDuration,
        syncTime,
        status,
        aesKey,
        allPartialSegments,
    );

    const mimeType = getMimeType(hlsSegment.verbatimSegmentUri);
    if (mimeType) {
      reference.mimeType = mimeType;
      if (HlsParser.MIME_TYPES_WITHOUT_INIT_SEGMENT_.has(mimeType)) {
        reference.initSegmentReference = null;
      }
    }

    if (segmentWithByteRangeOptimization) {
      this.lowLatencyByterangeOptimization_ = true;
      reference.markAsByterangeOptimization();

      if (isPreloadSegment) {
        reference.markAsPreload();
      }
    }

    return reference;
  }
Parses one shaka.hls.Segment object into a shaka.media.SegmentReference.

@param {shaka.media.InitSegmentReference} initSegmentReference
@param {shaka.media.SegmentReference} previousReference
@param {!shaka.hls.Segment} hlsSegment
@param {number} startTime
@param {!Map<string, string>} variables
@param {!shaka.hls.Playlist} playlist
@param {string} type
@param {function(): !Array<string>} getUris
@param {shaka.extern.aesKey=} aesKey
@return {shaka.media.SegmentReference}
@private