static chooseCodecsAndFilterManifest(manifest, preferredVideoCodecs,
      preferredAudioCodecs, preferredDecodingAttributes, preferredTextFormats) {
    const StreamUtils = shaka.util.StreamUtils;
    const MimeUtils = shaka.util.MimeUtils;

    if (preferredTextFormats.length) {
      let subset = manifest.textStreams;
      for (const textFormat of preferredTextFormats) {
        const filtered = subset.filter((textStream) => {
          if (textStream.codecs.startsWith(textFormat) ||
              textStream.mimeType.startsWith(textFormat)) {
            return true;
          }
          return false;
        });
        if (filtered.length) {
          subset = filtered;
          break;
        }
      }
      manifest.textStreams = subset;
    }

    let variants = manifest.variants;
    // To start, choose the codecs based on configured preferences if available.
    if (preferredVideoCodecs.length || preferredAudioCodecs.length) {
      variants = StreamUtils.choosePreferredCodecs(variants,
          preferredVideoCodecs, preferredAudioCodecs);
    }

    if (preferredDecodingAttributes.length) {
      // group variants by resolution and choose preferred variants only
      /** @type {!shaka.util.MultiMap<shaka.extern.Variant>} */
      const variantsByResolutionMap = new shaka.util.MultiMap();
      for (const variant of variants) {
        variantsByResolutionMap
            .push(String(variant.video.width || 0), variant);
      }
      const bestVariants = [];
      variantsByResolutionMap.forEach((width, variantsByResolution) => {
        let highestMatch = 0;
        let matchingVariants = [];
        for (const variant of variantsByResolution) {
          const matchCount = preferredDecodingAttributes.filter(
              (attribute) => variant.decodingInfos[0][attribute],
          ).length;
          if (matchCount > highestMatch) {
            highestMatch = matchCount;
            matchingVariants = [variant];
          } else if (matchCount == highestMatch) {
            matchingVariants.push(variant);
          }
        }
        bestVariants.push(...matchingVariants);
      });
      variants = bestVariants;
    }

    const audioStreamsSet = new Set();
    const videoStreamsSet = new Set();
    for (const variant of variants) {
      if (variant.audio) {
        audioStreamsSet.add(variant.audio);
      }
      if (variant.video) {
        videoStreamsSet.add(variant.video);
      }
    }

    const audioStreams = Array.from(audioStreamsSet).sort((v1, v2) => {
      return v1.bandwidth - v2.bandwidth;
    });
    const validAudioIds = [];
    const validAudioStreamsMap = new Map();
    const getAudioId = (stream) => {
      let id = stream.language + (stream.channelsCount || 0) +
        (stream.audioSamplingRate || 0) + stream.roles.join(',') +
        stream.label + stream.groupId + stream.fastSwitching;
      if (stream.dependencyStream) {
        id += stream.dependencyStream.baseOriginalId || '';
      }
      return id;
    };
    for (const stream of audioStreams) {
      const groupId = getAudioId(stream);
      const validAudioStreams = validAudioStreamsMap.get(groupId) || [];
      if (!validAudioStreams.length) {
        validAudioStreams.push(stream);
        validAudioIds.push(stream.id);
      } else {
        const previousStream = validAudioStreams[validAudioStreams.length - 1];
        const previousCodec =
          MimeUtils.getNormalizedCodec(previousStream.codecs);
        const currentCodec =
          MimeUtils.getNormalizedCodec(stream.codecs);
        if (previousCodec == currentCodec) {
          if (!stream.bandwidth || !previousStream.bandwidth ||
              stream.bandwidth > previousStream.bandwidth) {
            validAudioStreams.push(stream);
            validAudioIds.push(stream.id);
          }
        }
      }
      validAudioStreamsMap.set(groupId, validAudioStreams);
    }

    // Keys based in MimeUtils.getNormalizedCodec. Lower is better
    const videoCodecPreference = {
      'vp8': 1,
      'avc': 1,
      'dovi-avc': 0.95,
      'vp9': 0.9,
      'vp09': 0.9,
      'hevc': 0.85,
      'dovi-hevc': 0.8,
      'dovi-p5': 0.75,
      'av01': 0.7,
      'dovi-av1': 0.65,
      'vvc': 0.6,
    };

    const videoStreams = Array.from(videoStreamsSet)
        .sort((v1, v2) => {
          if (!v1.bandwidth || !v2.bandwidth || v1.bandwidth == v2.bandwidth) {
            if (v1.codecs && v2.codecs && v1.codecs != v2.codecs &&
                v1.width == v2.width) {
              const v1Codecs = MimeUtils.getNormalizedCodec(v1.codecs);
              const v2Codecs = MimeUtils.getNormalizedCodec(v2.codecs);
              if (v1Codecs != v2Codecs) {
                const indexV1 = videoCodecPreference[v1Codecs] || 1;
                const indexV2 = videoCodecPreference[v2Codecs] || 1;
                return indexV1 - indexV2;
              }
            }
            return v1.width - v2.width;
          }
          return v1.bandwidth - v2.bandwidth;
        });

    const isChangeTypeSupported =
      shaka.media.Capabilities.isChangeTypeSupported();

    const validVideoIds = [];
    const validVideoStreamsMap = new Map();
    const getVideoGroupId = (stream) => {
      let id = String(stream.width || '') + String(stream.height || '') +
          String(Math.round(stream.frameRate || 0)) + (stream.hdr || '') +
          stream.fastSwitching;
      if (stream.dependencyStream) {
        id += stream.dependencyStream.baseOriginalId || '';
      }
      return id;
    };
    for (const stream of videoStreams) {
      const groupId = getVideoGroupId(stream);
      const validVideoStreams = validVideoStreamsMap.get(groupId) || [];
      if (!validVideoStreams.length) {
        validVideoStreams.push(stream);
        validVideoIds.push(stream.id);
      } else {
        const previousStream = validVideoStreams[validVideoStreams.length - 1];
        if (!isChangeTypeSupported) {
          const previousCodec =
            MimeUtils.getNormalizedCodec(previousStream.codecs);
          const currentCodec =
            MimeUtils.getNormalizedCodec(stream.codecs);
          if (previousCodec !== currentCodec) {
            continue;
          }
        }
        const previousCodec =
          MimeUtils.getNormalizedCodec(previousStream.codecs);
        const currentCodec =
          MimeUtils.getNormalizedCodec(stream.codecs);
        if (previousCodec == currentCodec) {
          if (!stream.bandwidth || !previousStream.bandwidth ||
              stream.bandwidth > previousStream.bandwidth) {
            validVideoStreams.push(stream);
            validVideoIds.push(stream.id);
          }
        }
      }
      validVideoStreamsMap.set(groupId, validVideoStreams);
    }

    // Filter out any variants that don't match, forcing AbrManager to choose
    // from a single video codec and a single audio codec possible.
    manifest.variants = manifest.variants.filter((variant) => {
      const audio = variant.audio;
      const video = variant.video;
      if (audio) {
        if (!validAudioIds.includes(audio.id)) {
          shaka.log.debug('Dropping Variant (better codec available)', variant);
          return false;
        }
      }
      if (video) {
        if (!validVideoIds.includes(video.id)) {
          shaka.log.debug('Dropping Variant (better codec available)', variant);
          return false;
        }
      }
      return true;
    });
  }
In case of multiple usable codecs, choose one based on lowest average
bandwidth and filter out the rest.
Also filters out variants that have too many audio channels.
@param {!shaka.extern.Manifest} manifest
@param {!Array<string>} preferredVideoCodecs
@param {!Array<string>} preferredAudioCodecs
@param {!Array<string>} preferredDecodingAttributes
@param {!Array<string>} preferredTextFormats