async updateStream_(streamInfo) {
    if (streamInfo.stream.isAudioMuxedInVideo) {
      return;
    }
    const manifestUris = [];
    for (const uri of streamInfo.getUris()) {
      const uriObj = new goog.Uri(uri);
      const queryData = uriObj.getQueryData();
      if (streamInfo.canBlockReload) {
        if (streamInfo.nextMediaSequence >= 0) {
          // Indicates that the server must hold the request until a Playlist
          // contains a Media Segment with Media Sequence
          queryData.add('_HLS_msn', String(streamInfo.nextMediaSequence));
        }
        if (streamInfo.nextPart >= 0) {
          // Indicates, in combination with _HLS_msn, that the server must hold
          // the request until a Playlist contains Partial Segment N of Media
          // Sequence Number M or later.
          queryData.add('_HLS_part', String(streamInfo.nextPart));
        }
      }
      if (streamInfo.canSkipSegments) {
        // Enable delta updates. This will replace older segments with
        // 'EXT-X-SKIP' tag in the media playlist.
        queryData.add('_HLS_skip', 'YES');
      }
      if (queryData.getCount()) {
        uriObj.setQueryData(queryData.toDecodedString());
      }
      manifestUris.push(uriObj.toString());
    }
    let response;
    try {
      response = await this.requestManifest_(
          manifestUris, /* isPlaylist= */ true).promise;
    } catch (e) {
      if (this.playerInterface_) {
        this.playerInterface_.disableStream(streamInfo.stream);
      }
      throw e;
    }
    if (!streamInfo.stream.segmentIndex) {
      // The stream was closed since the update was first requested.
      return;
    }

    /** @type {shaka.hls.Playlist} */
    const playlist = this.manifestTextParser_.parsePlaylist(response.data);

    if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
    }

    // Record the final URI after redirects.
    const responseUri = response.uri;
    if (responseUri != response.originalUri &&
        !streamInfo.getUris().includes(responseUri)) {
      streamInfo.redirectUris.push(responseUri);
    }

    /** @type {!Array<!shaka.hls.Tag>} */
    const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
        'EXT-X-DEFINE');

    const mediaVariables = this.parseMediaVariables_(
        variablesTags, responseUri);

    const stream = streamInfo.stream;

    const mediaSequenceToStartTime =
        this.getMediaSequenceToStartTimeFor_(streamInfo);
    const {keyIds, drmInfos, encrypted, aesEncrypted} =
        await this.parseDrmInfo_(playlist, stream.mimeType,
            streamInfo.getUris, mediaVariables);

    if (!stream.encrypted && encrypted && !aesEncrypted) {
      stream.encrypted = true;
    }

    const keysAreEqual =
      (a, b) => a.size === b.size && [...a].every((value) => b.has(value));

    if (!keysAreEqual(stream.keyIds, keyIds)) {
      stream.keyIds = keyIds;
      stream.drmInfos = drmInfos;
      this.playerInterface_.newDrmInfo(stream);
    }

    const {segments, bandwidth} = this.createSegments_(
        playlist, mediaSequenceToStartTime, mediaVariables,
        streamInfo.getUris, streamInfo.type);
    if (bandwidth) {
      stream.bandwidth = bandwidth;
    }

    const qualityInfo =
        shaka.media.QualityObserver.createQualityInfo(stream);
    for (const segment of segments) {
      if (segment.initSegmentReference) {
        segment.initSegmentReference.mediaQuality = qualityInfo;
      }
    }

    stream.segmentIndex.mergeAndEvict(
        segments, this.presentationTimeline_.getSegmentAvailabilityStart());
    if (segments.length) {
      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;
      const {nextMediaSequence, nextPart} =
          this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments);
      streamInfo.nextMediaSequence = nextMediaSequence + skippedSegments;
      streamInfo.nextPart = nextPart;
      const playlistStartTime = mediaSequenceToStartTime.get(
          mediaSequenceNumber);
      stream.segmentIndex.evict(playlistStartTime);
    }
    const oldSegment = stream.segmentIndex.earliestReference();
    if (oldSegment) {
      streamInfo.minTimestamp = oldSegment.startTime;

      const newestSegment = segments[segments.length - 1];
      goog.asserts.assert(newestSegment, 'Should have segments!');

      streamInfo.maxTimestamp = newestSegment.endTime;
    }

    // Once the last segment has been added to the playlist,
    // #EXT-X-ENDLIST tag will be appended.
    // If that happened, treat the rest of the EVENT presentation as VOD.
    const endListTag =
        shaka.hls.Utils.getFirstTagWithName(playlist.tags, 'EXT-X-ENDLIST');

    if (endListTag) {
      // Flag this for later.  We don't convert the whole presentation into VOD
      // until we've seen the ENDLIST tag for all active playlists.
      streamInfo.hasEndList = true;
    }

    this.determineLastTargetDuration_(playlist);

    this.processDateRangeTags_(
        playlist.tags, stream.type, mediaVariables, streamInfo.getUris);
  }
Updates a stream.

@param {!shaka.hls.HlsParser.StreamInfo} streamInfo
@return {!Promise}
@private