createStreamInfoFromMediaTags_(tags, groupIdPathwayIdMapping,
      requireUri = true) {
    const verbatimMediaPlaylistUris = [];
    const globalGroupIds = [];
    const groupIdUriMapping = new Map();
    for (const tag of tags) {
      goog.asserts.assert(tag.name == 'EXT-X-MEDIA',
          'Should only be called on media tags!');
      const uri = requireUri ? tag.getRequiredAttrValue('URI') :
          (tag.getAttributeValue('URI') || shaka.hls.HlsParser.FAKE_MUXED_URL_);
      const groupId = tag.getRequiredAttrValue('GROUP-ID');
      verbatimMediaPlaylistUris.push(uri);
      globalGroupIds.push(groupId);
      groupIdUriMapping.set(groupId, uri);
    }

    const globalGroupId = globalGroupIds.sort().join(',');
    const firstTag = tags[0];
    let codecs = '';
    /** @type {string} */
    const type = this.getType_(firstTag);
    if (type == shaka.util.ManifestParserUtils.ContentType.TEXT) {
      codecs = firstTag.getAttributeValue('CODECS') || '';
    } else {
      for (const groupId of globalGroupIds) {
        if (this.groupIdToCodecsMap_.has(groupId)) {
          codecs = this.groupIdToCodecsMap_.get(groupId);
          break;
        }
      }
    }

    // Check if the stream has already been created as part of another Variant
    // and return it if it has.
    const key = verbatimMediaPlaylistUris.sort().join(',');
    if (this.uriToStreamInfosMap_.has(key)) {
      return this.uriToStreamInfosMap_.get(key);
    }
    const streamId = this.globalId_++;
    if (this.contentSteeringManager_) {
      for (const [groupId, uri] of groupIdUriMapping) {
        const pathwayId = groupIdPathwayIdMapping.get(groupId);
        if (pathwayId) {
          this.contentSteeringManager_.addLocation(streamId, pathwayId, uri);
        }
      }
    }

    const language = firstTag.getAttributeValue('LANGUAGE');
    const name = firstTag.getAttributeValue('NAME');

    // NOTE: According to the HLS spec, "DEFAULT=YES" requires "AUTOSELECT=YES".
    // However, we don't bother to validate "AUTOSELECT", since we don't
    // actually use it in our streaming model, and we treat everything as
    // "AUTOSELECT=YES".  A value of "AUTOSELECT=NO" would imply that it may
    // only be selected explicitly by the user, and we don't have a way to
    // represent that in our model.
    const defaultAttrValue = firstTag.getAttributeValue('DEFAULT');
    const primary = defaultAttrValue == 'YES';

    const channelsCount =
        type == 'audio' ? this.getChannelsCount_(firstTag) : null;
    const spatialAudio =
        type == 'audio' ? this.isSpatialAudio_(firstTag) : false;
    const characteristics = firstTag.getAttributeValue('CHARACTERISTICS');

    const forcedAttrValue = firstTag.getAttributeValue('FORCED');
    const forced = forcedAttrValue == 'YES';
    const sampleRate = type == 'audio' ? this.getSampleRate_(firstTag) : null;
    // TODO: Should we take into account some of the currently ignored
    // attributes: INSTREAM-ID, Attribute descriptions: https://bit.ly/2lpjOhj
    const streamInfo = this.createStreamInfo_(
        streamId, verbatimMediaPlaylistUris, codecs, type, language,
        primary, name, channelsCount, /* closedCaptions= */ null,
        characteristics, forced, sampleRate, spatialAudio);
    if (streamInfo.stream) {
      streamInfo.stream.groupId = globalGroupId;
    }
    if (this.groupIdToStreamInfosMap_.has(globalGroupId)) {
      this.groupIdToStreamInfosMap_.get(globalGroupId).push(streamInfo);
    } else {
      this.groupIdToStreamInfosMap_.set(globalGroupId, [streamInfo]);
    }

    this.uriToStreamInfosMap_.set(key, streamInfo);
    return streamInfo;
  }
Parse EXT-X-MEDIA media tag into a Stream object.

@param {!Array<!shaka.hls.Tag>} tags
@param {!Map<string, string>} groupIdPathwayIdMapping
@param {boolean=} requireUri
@return {!shaka.hls.HlsParser.StreamInfo}
@private