static concatenateStreams_(output, input) {
    // We keep the original stream's resolution, frame rate,
    // sample rate, and channel count to ensure that it's properly
    // matched with similar content in other periods further down
    // the line.

    // Combine arrays, keeping only the unique elements
    const combineArrays = (output, input) => {
      if (!output) {
        output = [];
      }
      for (const item of input) {
        if (!output.includes(item)) {
          output.push(item);
        }
      }
      return output;
    };
    output.roles = combineArrays(output.roles, input.roles);

    if (input.emsgSchemeIdUris) {
      output.emsgSchemeIdUris = combineArrays(
          output.emsgSchemeIdUris, input.emsgSchemeIdUris);
    }

    for (const keyId of input.keyIds) {
      output.keyIds.add(keyId);
    }

    if (output.originalId == null) {
      output.originalId = input.originalId;
    } else {
      const newOriginalId = (input.originalId || '');
      if (newOriginalId && !output.originalId.endsWith(newOriginalId)) {
        output.originalId += ',' + newOriginalId;
      }
    }

    const commonDrmInfos = shaka.drm.DrmUtils.getCommonDrmInfos(
        output.drmInfos, input.drmInfos);
    if (input.drmInfos.length && output.drmInfos.length &&
        !commonDrmInfos.length) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.INCONSISTENT_DRM_ACROSS_PERIODS);
    }
    output.drmInfos = commonDrmInfos;

    // The output is encrypted if any input was encrypted.
    output.encrypted = output.encrypted || input.encrypted;

    // Combine the closed captions maps.
    if (input.closedCaptions) {
      if (!output.closedCaptions) {
        output.closedCaptions = new Map();
      }
      for (const [key, value] of input.closedCaptions) {
        output.closedCaptions.set(key, value);
      }
    }

    // Prioritize the highest bandwidth
    if (output.bandwidth && input.bandwidth) {
      output.bandwidth = Math.max(output.bandwidth, input.bandwidth);
    }

    // Combine trick-play video streams, if present.
    if (input.trickModeVideo) {
      if (!output.trickModeVideo) {
        // Create a fresh output stream for trick-mode playback.
        output.trickModeVideo = shaka.util.PeriodCombiner.cloneStream_(
            input.trickModeVideo);
        output.trickModeVideo.matchedStreams = [];
        output.trickModeVideo.createSegmentIndex = () => {
          if (output.trickModeVideo.segmentIndex) {
            return Promise.resolve();
          }
          const segmentIndex = new shaka.media.MetaSegmentIndex();
          goog.asserts.assert(output.trickModeVideo.matchedStreams,
              'trickmode matched streams should exist');
          for (const stream of output.trickModeVideo.matchedStreams) {
            goog.asserts.assert(stream.segmentIndex,
                'trickmode segment index should exist');
            segmentIndex.appendSegmentIndex(stream.segmentIndex);
          }
          output.trickModeVideo.segmentIndex = segmentIndex;

          return Promise.resolve();
        };
      }

      // Concatenate the trick mode input onto the trick mode output.
      output.trickModeVideo.matchedStreams.push(input.trickModeVideo);
      shaka.util.PeriodCombiner.concatenateStreams_(
          output.trickModeVideo, input.trickModeVideo);
    } else if (output.trickModeVideo) {
      // We have a trick mode output, but no input from this Period.  Fill it in
      // from the standard input Stream.
      output.trickModeVideo.matchedStreams.push(input);
      shaka.util.PeriodCombiner.concatenateStreams_(
          output.trickModeVideo, input);
    }

    // Combine dependency streams, if present.
    if (input.dependencyStream) {
      if (!output.dependencyStream) {
        // Create a fresh output stream for trick-mode playback.
        output.dependencyStream = shaka.util.PeriodCombiner.cloneStream_(
            input.dependencyStream);
        output.dependencyStream.matchedStreams = [];
        output.dependencyStream.createSegmentIndex = () => {
          if (output.dependencyStream.segmentIndex) {
            return Promise.resolve();
          }
          const segmentIndex = new shaka.media.MetaSegmentIndex();
          goog.asserts.assert(output.dependencyStream.matchedStreams,
              'dependency video matched streams should exist');
          for (const stream of output.dependencyStream.matchedStreams) {
            goog.asserts.assert(stream.segmentIndex,
                'dependency video segment index should exist');
            segmentIndex.appendSegmentIndex(stream.segmentIndex);
          }
          output.dependencyStream.segmentIndex = segmentIndex;

          return Promise.resolve();
        };
      }

      // Concatenate the dependency input onto the dependency output.
      output.dependencyStream.matchedStreams.push(input.dependencyStream);
      shaka.util.PeriodCombiner.concatenateStreams_(
          output.dependencyStream, input.dependencyStream);
    } else if (output.dependencyStream) {
      // We have a dependency output, but no input from this Period.
      // Fill it in from the standard input Stream.
      output.dependencyStream.matchedStreams.push(input);
      shaka.util.PeriodCombiner.concatenateStreams_(
          output.dependencyStream, input);
    }
  }
Combine the various fields of the input Stream into the output.

@param {shaka.extern.Stream} output
@param {shaka.extern.Stream} input
@private