async convertParsedPlaylistIntoStreamInfo_(streamId, variables, playlist,
      getUris, codecs, type, languageValue, primary, name,
      channelsCount, closedCaptions, characteristics, forced, sampleRate,
      spatialAudio, mimeType = undefined, requestBasicInfo = true,
      allowOverrideMimeType = true) {
    const playlistSegments = playlist.segments || [];
    const allAreMissing = playlistSegments.every((seg) => {
      if (shaka.hls.Utils.getFirstTagWithName(seg.tags, 'EXT-X-GAP')) {
        return true;
      }
      return false;
    });

    if (!playlistSegments.length || allAreMissing) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.HLS_EMPTY_MEDIA_PLAYLIST);
    }

    this.determinePresentationType_(playlist);

    if (this.isLive_()) {
      this.determineLastTargetDuration_(playlist);
    }

    const mediaSequenceToStartTime = this.isLive_() ?
        this.mediaSequenceToStartTimeByType_.get(type) : new Map();

    const {segments, bandwidth} = this.createSegments_(
        playlist, mediaSequenceToStartTime, variables, getUris, type);

    let width = null;
    let height = null;
    let videoRange = null;
    let colorGamut = null;
    let frameRate = null;
    if (segments.length > 0 && requestBasicInfo) {
      const basicInfo = await this.getBasicInfoFromSegments_(segments);

      type = basicInfo.type;
      languageValue = basicInfo.language;
      channelsCount = basicInfo.channelCount;
      sampleRate = basicInfo.sampleRate;
      if (!this.config_.disableText) {
        closedCaptions = basicInfo.closedCaptions;
      }

      height = basicInfo.height;
      width = basicInfo.width;
      videoRange = basicInfo.videoRange;
      colorGamut = basicInfo.colorGamut;
      frameRate = basicInfo.frameRate;

      if (allowOverrideMimeType) {
        mimeType = basicInfo.mimeType;
        codecs = basicInfo.codecs;
      }
    }

    if (!mimeType) {
      mimeType = await this.guessMimeType_(type, codecs, segments);

      // Some manifests don't say what text codec they use, this is a problem
      // if the cmft extension is used because we identify the mimeType as
      // application/mp4. In this case if we don't detect initialization
      // segments, we assume that the mimeType is text/vtt.
      if (type == shaka.util.ManifestParserUtils.ContentType.TEXT &&
          !codecs && mimeType == 'application/mp4' &&
          segments[0] && !segments[0].initSegmentReference) {
        mimeType = 'text/vtt';
      }
    }

    const {drmInfos, keyIds, encrypted, aesEncrypted} =
        await this.parseDrmInfo_(playlist, mimeType, getUris, variables);

    if (encrypted && !drmInfos.length && !aesEncrypted) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.HLS_KEYFORMATS_NOT_SUPPORTED);
    }

    const stream = this.makeStreamObject_(streamId, codecs, type,
        languageValue, primary, name, channelsCount, closedCaptions,
        characteristics, forced, sampleRate, spatialAudio);
    stream.encrypted = encrypted && !aesEncrypted;
    stream.drmInfos = drmInfos;
    stream.keyIds = keyIds;
    stream.mimeType = mimeType;
    if (bandwidth) {
      stream.bandwidth = bandwidth;
    }
    this.setFullTypeForStream_(stream);

    if (type == shaka.util.ManifestParserUtils.ContentType.VIDEO &&
        (width || height || videoRange || colorGamut)) {
      this.addVideoAttributes_(stream, width, height,
          frameRate, videoRange, /* videoLayout= */ null, colorGamut);
    }

    // This new calculation is necessary for Low Latency streams.
    if (this.isLive_()) {
      this.determineLastTargetDuration_(playlist);
    }

    const firstStartTime = segments[0].startTime;
    const lastSegment = segments[segments.length - 1];
    const lastEndTime = lastSegment.endTime;
    /** @type {!shaka.media.SegmentIndex} */
    const segmentIndex = new shaka.media.SegmentIndex(segments);
    stream.segmentIndex = segmentIndex;

    const serverControlTag = shaka.hls.Utils.getFirstTagWithName(
        playlist.tags, 'EXT-X-SERVER-CONTROL');
    const canSkipSegments = serverControlTag ?
          serverControlTag.getAttribute('CAN-SKIP-UNTIL') != null : false;
    const canBlockReload = serverControlTag ?
          serverControlTag.getAttribute('CAN-BLOCK-RELOAD') != null : false;

    const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
        playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);

    const {nextMediaSequence, nextPart} =
        this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments);

    return {
      stream,
      type,
      redirectUris: [],
      getUris,
      minTimestamp: firstStartTime,
      maxTimestamp: lastEndTime,
      canSkipSegments,
      canBlockReload,
      hasEndList: false,
      firstSequenceNumber: -1,
      nextMediaSequence,
      nextPart,
      mediaSequenceToStartTime,
      loadedOnce: false,
    };
  }
@param {number} streamId
@param {!Map<string, string>} variables
@param {!shaka.hls.Playlist} playlist
@param {function(): !Array<string>} getUris
@param {string} codecs
@param {string} type
@param {?string} languageValue
@param {boolean} primary
@param {?string} name
@param {?number} channelsCount
@param {Map<string, string>} closedCaptions
@param {?string} characteristics
@param {boolean} forced
@param {?number} sampleRate
@param {boolean} spatialAudio
@param {(string|undefined)} mimeType
@param {boolean=} requestBasicInfo
@param {boolean=} allowOverrideMimeType
@return {!Promise<!shaka.hls.HlsParser.StreamInfo>}
@private