async createVariantsForTags_(tags, sessionKeyTags, mediaTags, getUris,
      variables, iFrameStreams) {
    // EXT-X-SESSION-KEY processing
    const drmInfos = [];
    const keyIds = new Set();
    if (!this.config_.ignoreDrmInfo && sessionKeyTags.length > 0) {
      for (const drmTag of sessionKeyTags) {
        const method = drmTag.getRequiredAttrValue('METHOD');
        // According to the HLS spec, KEYFORMAT is optional and implicitly
        // defaults to "identity".
        // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-11#section-4.4.4.4
        const keyFormat =
            drmTag.getAttributeValue('KEYFORMAT') || 'identity';
        let drmInfo = null;
        if (method == 'NONE') {
          continue;
        } else if (this.isAesMethod_(method)) {
          const keyUris = shaka.hls.Utils.constructSegmentUris(
              getUris(), drmTag.getRequiredAttrValue('URI'), variables);
          const keyMapKey = keyUris.sort().join('');
          if (!this.aesKeyMap_.has(keyMapKey)) {
            const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
            const request = shaka.net.NetworkingEngine.makeRequest(
                keyUris, this.config_.retryParameters);
            const keyResponse = this.makeNetworkRequest_(request, requestType)
                .promise;
            this.aesKeyMap_.set(keyMapKey, keyResponse);
          }
          continue;
        } else if (keyFormat == 'identity') {
          // eslint-disable-next-line no-await-in-loop
          drmInfo = await this.identityDrmParser_(
              drmTag, /* mimeType= */ '', getUris,
              /* initSegmentRef= */ null, variables);
        } else {
          const drmParser =
              this.keyFormatsToDrmParsers_.get(keyFormat);

          drmInfo = drmParser ?
            // eslint-disable-next-line no-await-in-loop
            await drmParser(drmTag, /* mimeType= */ '',
                /* initSegmentRef= */ null) : null;
        }
        if (drmInfo) {
          if (drmInfo.keyIds) {
            for (const keyId of drmInfo.keyIds) {
              keyIds.add(keyId);
            }
          }
          drmInfos.push(drmInfo);
        } else {
          shaka.log.warning('Unsupported HLS KEYFORMAT', keyFormat);
        }
      }
    }

    const groupedTags = {};
    for (const tag of tags) {
      const key = tag.getTagKey(!this.contentSteeringManager_);
      if (!groupedTags[key]) {
        groupedTags[key] = [tag];
      } else {
        groupedTags[key].push(tag);
      }
    }

    const allVariants = [];
    // Create variants for each group of variant tag.
    for (const key in groupedTags) {
      const tags = groupedTags[key];
      const firstTag = tags[0];

      const frameRate = firstTag.getAttributeValue('FRAME-RATE');
      const bandwidth =
          Number(firstTag.getAttributeValue('AVERAGE-BANDWIDTH')) ||
          Number(firstTag.getRequiredAttrValue('BANDWIDTH'));

      const resolution = firstTag.getAttributeValue('RESOLUTION');
      const [width, height] = resolution ? resolution.split('x') : [null, null];

      const videoRange = firstTag.getAttributeValue('VIDEO-RANGE');

      let videoLayout = firstTag.getAttributeValue('REQ-VIDEO-LAYOUT');
      if (videoLayout && videoLayout.includes(',')) {
        // If multiple video layout strings are present, pick the first valid
        // one.
        const layoutStrings = videoLayout.split(',').filter((layoutString) => {
          return layoutString == 'CH-STEREO' || layoutString == 'CH-MONO';
        });
        videoLayout = layoutStrings[0];
      }
      // According to the HLS spec:
      // By default a video variant is monoscopic, so an attribute
      // consisting entirely of REQ-VIDEO-LAYOUT="CH-MONO" is unnecessary
      // and SHOULD NOT be present.
      videoLayout = videoLayout || 'CH-MONO';

      const streamInfos = this.createStreamInfosForVariantTags_(tags,
          mediaTags, resolution, frameRate);

      goog.asserts.assert(streamInfos.audio.length ||
          streamInfos.video.length, 'We should have created a stream!');

      allVariants.push(...this.createVariants_(
          streamInfos.audio,
          streamInfos.video,
          bandwidth,
          width,
          height,
          frameRate,
          videoRange,
          videoLayout,
          drmInfos,
          keyIds,
          iFrameStreams));
    }
    return allVariants.filter((variant) => variant != null);
  }
@param {!Array<!shaka.hls.Tag>} tags Variant tags from the playlist.
@param {!Array<!shaka.hls.Tag>} sessionKeyTags EXT-X-SESSION-KEY tags
from the playlist.
@param {!Array<!shaka.hls.Tag>} mediaTags EXT-X-MEDIA tags from the
playlist.
@param {function(): !Array<string>} getUris
@param {?Map<string, string>} variables
@param {!Array<!shaka.extern.Stream>} iFrameStreams
@return {!Promise<!Array<!shaka.extern.Variant>>}
@private