async addChaptersTrack(uri, language, mimeType) {
    if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE &&
        this.loadMode_ != shaka.Player.LoadMode.SRC_EQUALS) {
      shaka.log.error(
          'Must call load() and wait for it to resolve before adding ' +
          'chapters tracks.');
      throw new shaka.util.Error(
          shaka.util.Error.Severity.RECOVERABLE,
          shaka.util.Error.Category.PLAYER,
          shaka.util.Error.Code.CONTENT_NOT_LOADED);
    }

    if (!mimeType) {
      mimeType = await this.getTextMimetype_(uri);
    }

    const ContentType = shaka.util.ManifestParserUtils.ContentType;
    const seekRange = this.seekRange();
    let duration = seekRange.end - seekRange.start;
    if (this.manifest_) {
      duration = this.manifest_.presentationTimeline.getDuration();
    }
    if (duration == Infinity) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.RECOVERABLE,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_CHAPTERS_TO_LIVE_STREAM);
    }

    goog.asserts.assert(
        this.networkingEngine_, 'Need networking engine.');
    const buffer = await this.getTextData_(uri,
        this.networkingEngine_,
        this.config_.streaming.retryParameters);

    const factory = shaka.text.TextEngine.findParser(mimeType);
    if (!factory) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.TEXT,
          shaka.util.Error.Code.MISSING_TEXT_PLUGIN,
          mimeType);
    }
    const textParser = factory();
    const time = {
      periodStart: 0,
      segmentStart: 0,
      segmentEnd: duration,
      vttOffset: 0,
    };
    const data = shaka.util.BufferUtils.toUint8(buffer);
    const cues = textParser.parseMedia(data, time, uri, /* images= */ []);

    const references = [];
    for (const cue of cues) {
      const reference = new shaka.media.SegmentReference(
          cue.startTime,
          cue.endTime,
          () => [cue.payload],
          /* startByte= */ 0,
          /* endByte= */ null,
          /* initSegmentReference= */ null,
          /* timestampOffset= */ 0,
          /* appendWindowStart= */ 0,
          /* appendWindowEnd= */ Infinity,
      );
      references.push(reference);
    }

    const chaptersMimeType = 'text/plain';

    /** @type {shaka.extern.Stream} */
    const stream = {
      id: this.nextExternalStreamId_++,
      originalId: null,
      groupId: null,
      createSegmentIndex: () => Promise.resolve(),
      segmentIndex: new shaka.media.SegmentIndex(references),
      mimeType: chaptersMimeType,
      codecs: '',
      kind: '',
      encrypted: false,
      drmInfos: [],
      keyIds: new Set(),
      language: language,
      originalLanguage: language,
      label: null,
      type: ContentType.TEXT,
      primary: false,
      trickModeVideo: null,
      dependencyStream: null,
      emsgSchemeIdUris: null,
      roles: [],
      forced: false,
      channelsCount: null,
      audioSamplingRate: null,
      spatialAudio: false,
      closedCaptions: null,
      accessibilityPurpose: null,
      external: true,
      fastSwitching: false,
      fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
          chaptersMimeType, '')]),
      isAudioMuxedInVideo: false,
      baseOriginalId: null,
    };

    this.externalChaptersStreams_.push(stream);
    this.onTracksChanged_();
    return shaka.util.StreamUtils.textStreamToTrack(stream);
  }
Adds the given chapters track to the loaded manifest.  <code>load()</code>
must resolve before calling.  The presentation must have a duration.

This returns the created track.

@param {string} uri
@param {string} language
@param {string=} mimeType
@return {!Promise<shaka.extern.TextTrack>}
@export