parseRepresentation_(context, contentProtection, kind, language, label,
      isPrimary, roles, closedCaptions, node, accessibilityPurpose,
      lastSegmentNumber) {
    const TXml = shaka.util.TXml;
    const ContentType = shaka.util.ManifestParserUtils.ContentType;

    context.representation =
        this.createFrame_(node, context.adaptationSet, null);

    const representationId = context.representation.id;

    this.minTotalAvailabilityTimeOffset_ =
        Math.min(this.minTotalAvailabilityTimeOffset_,
            context.representation.availabilityTimeOffset);

    this.isLowLatency_ = this.minTotalAvailabilityTimeOffset_ > 0;

    if (!this.verifyRepresentation_(context.representation)) {
      shaka.log.warning('Skipping Representation', context.representation);
      return null;
    }
    const periodStart = context.periodInfo.start;

    // NOTE: bandwidth is a mandatory attribute according to the spec, and zero
    // does not make sense in the DASH spec's bandwidth formulas.
    // In some content, however, the attribute is missing or zero.
    // To avoid NaN at the variant level on broken content, fall back to zero.
    // https://github.com/shaka-project/shaka-player/issues/938#issuecomment-317278180
    context.bandwidth =
        TXml.parseAttr(node, 'bandwidth', TXml.parsePositiveInt) || 0;

    context.roles = roles;

    const supplementalPropertyElements =
        TXml.findChildren(node, 'SupplementalProperty');
    const essentialPropertyElements =
        TXml.findChildren(node, 'EssentialProperty');
    const contentProtectionElements =
        TXml.findChildren(node, 'ContentProtection');

    let representationUrlParams = null;
    let urlParamsElement = essentialPropertyElements.find((element) => {
      const schemeId = element.attributes['schemeIdUri'];
      return schemeId == 'urn:mpeg:dash:urlparam:2014';
    });
    if (urlParamsElement) {
      representationUrlParams =
          this.getURLParametersFunction_(urlParamsElement);
    } else {
      urlParamsElement = supplementalPropertyElements.find((element) => {
        const schemeId = element.attributes['schemeIdUri'];
        return schemeId == 'urn:mpeg:dash:urlparam:2014';
      });
      if (urlParamsElement) {
        representationUrlParams =
            this.getURLParametersFunction_(urlParamsElement);
      }
    }

    if (representationUrlParams) {
      context.urlParams = representationUrlParams;
    }

    /** @type {?shaka.dash.DashParser.StreamInfo} */
    let streamInfo;

    const contentType = context.representation.contentType;
    const isText = contentType == ContentType.TEXT ||
                   contentType == ContentType.APPLICATION;
    const isImage = contentType == ContentType.IMAGE;

    if (contentProtectionElements.length) {
      context.adaptationSet.encrypted = true;
    }

    try {
      /** @type {shaka.extern.aesKey|undefined} */
      let aesKey = undefined;
      if (contentProtection.aes128Info) {
        const getBaseUris = context.representation.getBaseUris;
        const urlParams = context.urlParams;
        const uris = shaka.util.ManifestParserUtils.resolveUris(
            getBaseUris(), [contentProtection.aes128Info.keyUri], urlParams());
        const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
        const request = shaka.net.NetworkingEngine.makeRequest(
            uris, this.config_.retryParameters);

        aesKey = {
          bitsKey: 128,
          blockCipherMode: 'CBC',
          iv: contentProtection.aes128Info.iv,
          firstMediaSequenceNumber: 0,
        };

        // Don't download the key object until the segment is parsed, to
        // avoid a startup delay for long manifests with lots of keys.
        aesKey.fetchKey = async () => {
          const keyResponse =
              await this.makeNetworkRequest_(request, requestType);

          // keyResponse.status is undefined when URI is
          // "data:text/plain;base64,"
          if (!keyResponse.data || keyResponse.data.byteLength != 16) {
            throw new shaka.util.Error(
                shaka.util.Error.Severity.CRITICAL,
                shaka.util.Error.Category.MANIFEST,
                shaka.util.Error.Code.AES_128_INVALID_KEY_LENGTH);
          }

          const algorithm = {
            name: 'AES-CBC',
          };
          aesKey.cryptoKey = await window.crypto.subtle.importKey(
              'raw', keyResponse.data, algorithm, true, ['decrypt']);
          aesKey.fetchKey = undefined; // No longer needed.
        };
      }
      context.representation.aesKey = aesKey;

      const requestSegment = (uris, startByte, endByte, isInit) => {
        return this.requestSegment_(uris, startByte, endByte, isInit);
      };
      if (context.representation.segmentBase) {
        streamInfo = shaka.dash.SegmentBase.createStreamInfo(
            context, requestSegment, aesKey);
      } else if (context.representation.segmentList) {
        streamInfo = shaka.dash.SegmentList.createStreamInfo(
            context, this.streamMap_, aesKey);
      } else if (context.representation.segmentTemplate) {
        const hasManifest = !!this.manifest_;

        streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
            context, requestSegment, this.streamMap_, hasManifest,
            this.config_.dash.initialSegmentLimit, this.periodDurations_,
            aesKey, lastSegmentNumber, /* isPatchUpdate= */ false,
            this.continuityCache_);
      } else {
        goog.asserts.assert(isText,
            'Must have Segment* with non-text streams.');

        const duration = context.periodInfo.duration || 0;
        const getBaseUris = context.representation.getBaseUris;
        const mimeType = context.representation.mimeType;
        const codecs = context.representation.codecs;
        streamInfo = {
          endTime: -1,
          timeline: -1,
          generateSegmentIndex: () => {
            const segmentIndex = shaka.media.SegmentIndex.forSingleSegment(
                periodStart, duration, getBaseUris());
            segmentIndex.forEachTopLevelReference((ref) => {
              ref.mimeType = mimeType;
              ref.codecs = codecs;
            });
            return Promise.resolve(segmentIndex);
          },
          timescale: 1,
        };
      }
    } catch (error) {
      if ((isText || isImage) &&
          error.code == shaka.util.Error.Code.DASH_NO_SEGMENT_INFO) {
        // We will ignore any DASH_NO_SEGMENT_INFO errors for text/image
        // streams.
        return null;
      }

      // For anything else, re-throw.
      throw error;
    }

    const keyId = shaka.dash.ContentProtection.parseFromRepresentation(
        contentProtectionElements, contentProtection,
        this.config_.ignoreDrmInfo,
        this.config_.dash.keySystemsByURI);
    const keyIds = new Set(keyId ? [keyId] : []);

    // Detect the presence of E-AC3 JOC audio content, using DD+JOC signaling.
    // See: ETSI TS 103 420 V1.2.1 (2018-10)
    const hasJoc = supplementalPropertyElements.some((element) => {
      const expectedUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
      const expectedValue = 'JOC';
      return element.attributes['schemeIdUri'] == expectedUri &&
          element.attributes['value'] == expectedValue;
    });
    let spatialAudio = false;
    if (hasJoc) {
      spatialAudio = true;
    }

    let forced = false;
    if (isText) {
      // See: https://github.com/shaka-project/shaka-player/issues/2122 and
      // https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/165
      forced = roles.includes('forced_subtitle') ||
          roles.includes('forced-subtitle');
    }

    let tilesLayout;
    if (isImage) {
      const thumbnailTileElem = essentialPropertyElements.find((element) => {
        const expectedUris = [
          'http://dashif.org/thumbnail_tile',
          'http://dashif.org/guidelines/thumbnail_tile',
        ];
        return expectedUris.includes(element.attributes['schemeIdUri']);
      });
      if (thumbnailTileElem) {
        tilesLayout = thumbnailTileElem.attributes['value'];
      }
      // Filter image adaptation sets that has no tilesLayout.
      if (!tilesLayout) {
        return null;
      }
    }

    let hdr;
    const profiles = context.profiles;
    const codecs = context.representation.codecs;

    const hevcHDR = 'http://dashif.org/guidelines/dash-if-uhd#hevc-hdr-pq10';
    if (profiles.includes(hevcHDR) && (codecs.includes('hvc1.2.4.L153.B0') ||
        codecs.includes('hev1.2.4.L153.B0'))) {
      hdr = 'PQ';
    }

    const contextId = context.representation.id ?
        context.period.id + ',' + context.representation.id : '';

    if (this.patchLocationNodes_.length && representationId) {
      this.contextCache_.set(`${context.period.id},${representationId}`,
          this.cloneContext_(context));
    }

    if (context.representation.producerReferenceTime) {
      this.parseProducerReferenceTime_(
          context.representation.producerReferenceTime,
          streamInfo,
          context.presentationTimeline);
    }

    if (streamInfo.endTime != -1 &&
      context.period.id != null &&
      context.representation.id != null) {
      const cache = this.continuityCache_.get(context.period.id);
      if (cache) {
        cache.endTime = streamInfo.endTime;
        if (!cache.reps.includes(context.representation.id)) {
          cache.reps.push(context.representation.id);
        }
        this.continuityCache_.set(context.period.id, cache);
      } else {
        const cache = {
          endTime: streamInfo.endTime,
          timeline: streamInfo.timeline,
          reps: [context.representation.id],
        };
        this.continuityCache_.set(context.period.id, cache);
      }
    }

    /** @type {shaka.extern.Stream} */
    let stream;

    if (contextId && this.streamMap_.has(contextId)) {
      stream = this.streamMap_.get(contextId);
    } else {
      stream = {
        id: this.globalId_++,
        originalId: context.representation.id,
        groupId: null,
        createSegmentIndex: () => Promise.resolve(),
        closeSegmentIndex: () => {
          if (stream.segmentIndex) {
            stream.segmentIndex.release();
            stream.segmentIndex = null;
          }
        },
        segmentIndex: null,
        mimeType: context.representation.mimeType,
        codecs,
        frameRate: context.representation.frameRate,
        pixelAspectRatio: context.representation.pixelAspectRatio,
        bandwidth: context.bandwidth,
        width: context.representation.width,
        height: context.representation.height,
        kind,
        encrypted: contentProtection.drmInfos.length > 0,
        drmInfos: contentProtection.drmInfos,
        keyIds,
        language,
        originalLanguage: context.adaptationSet.language,
        label,
        type: context.adaptationSet.contentType,
        primary: isPrimary,
        trickModeVideo: null,
        dependencyStream: null,
        emsgSchemeIdUris:
            context.representation.emsgSchemeIdUris,
        roles,
        forced,
        channelsCount: context.representation.numChannels,
        audioSamplingRate: context.representation.audioSamplingRate,
        spatialAudio,
        closedCaptions,
        hdr,
        colorGamut: undefined,
        videoLayout: undefined,
        tilesLayout,
        accessibilityPurpose,
        external: false,
        fastSwitching: false,
        fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
            context.representation.mimeType, context.representation.codecs)]),
        isAudioMuxedInVideo: false,
        baseOriginalId: null,
      };
    }

    stream.createSegmentIndex = async () => {
      if (!stream.segmentIndex) {
        stream.segmentIndex = await streamInfo.generateSegmentIndex();
      }
    };

    if (contextId && context.dynamic && !this.streamMap_.has(contextId)) {
      const periodId = context.period.id || '';
      if (!this.indexStreamMap_.has(periodId)) {
        this.indexStreamMap_.set(periodId, []);
      }
      this.streamMap_.set(contextId, stream);
      this.indexStreamMap_.get(periodId).push(contextId);
    }

    return stream;
  }
Parses a Representation XML element.

@param {shaka.dash.DashParser.Context} context
@param {shaka.dash.ContentProtection.Context} contentProtection
@param {(string|undefined)} kind
@param {string} language
@param {?string} label
@param {boolean} isPrimary
@param {!Array<string>} roles
@param {Map<string, string>} closedCaptions
@param {!shaka.extern.xml.Node} node
@param {?shaka.media.ManifestParser.AccessibilityPurpose
       } accessibilityPurpose
@param {?number} lastSegmentNumber

@return {?shaka.extern.Stream} The Stream, or null when there is a
  non-critical parsing error.
@private