@Override
 public void maybeThrowError() throws IOException {
   if (fatalError != null) {
     throw fatalError;
   } else {
     manifestFetcher.maybeThrowError();
   }
 }
 @Override
 public void enable(int track) {
   enabledTrack = tracks.get(track);
   if (enabledTrack.isAdaptive()) {
     adaptiveFormatEvaluator.enable();
   }
   if (manifestFetcher != null) {
     manifestFetcher.enable();
   }
 }
 @Override
 public void disable(List<? extends MediaChunk> queue) {
   if (enabledTrack.isAdaptive()) {
     adaptiveFormatEvaluator.disable();
   }
   if (manifestFetcher != null) {
     manifestFetcher.disable();
   }
   evaluation.format = null;
   fatalError = null;
 }
  @Override
  public void continueBuffering(long playbackPositionUs) {
    if (manifestFetcher == null || !currentManifest.isLive || fatalError != null) {
      return;
    }

    SmoothStreamingManifest newManifest = manifestFetcher.getManifest();
    if (currentManifest != newManifest && newManifest != null) {
      StreamElement currentElement = currentManifest.streamElements[enabledTrack.elementIndex];
      int currentElementChunkCount = currentElement.chunkCount;
      StreamElement newElement = newManifest.streamElements[enabledTrack.elementIndex];
      if (currentElementChunkCount == 0 || newElement.chunkCount == 0) {
        // There's no overlap between the old and new elements because at least one is empty.
        currentManifestChunkOffset += currentElementChunkCount;
      } else {
        long currentElementEndTimeUs =
            currentElement.getStartTimeUs(currentElementChunkCount - 1)
                + currentElement.getChunkDurationUs(currentElementChunkCount - 1);
        long newElementStartTimeUs = newElement.getStartTimeUs(0);
        if (currentElementEndTimeUs <= newElementStartTimeUs) {
          // There's no overlap between the old and new elements.
          currentManifestChunkOffset += currentElementChunkCount;
        } else {
          // The new element overlaps with the old one.
          currentManifestChunkOffset += currentElement.getChunkIndex(newElementStartTimeUs);
        }
      }
      currentManifest = newManifest;
      needManifestRefresh = false;
    }

    if (needManifestRefresh
        && (SystemClock.elapsedRealtime()
            > manifestFetcher.getManifestLoadStartTimestamp()
                + MINIMUM_MANIFEST_REFRESH_PERIOD_MS)) {
      manifestFetcher.requestRefresh();
    }
  }
 /**
  * Constructor to use for live streaming.
  *
  * <p>May also be used for fixed duration content, in which case the call is equivalent to calling
  * the other constructor, passing {@code manifestFetcher.getManifest()} is the first argument.
  *
  * @param manifestFetcher A fetcher for the manifest, which must have already successfully
  *     completed an initial load.
  * @param trackSelector Selects tracks from the manifest to be exposed by this source.
  * @param dataSource A {@link DataSource} suitable for loading the media data.
  * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
  * @param liveEdgeLatencyMs For live streams, the number of milliseconds that the playback should
  *     lag behind the "live edge" (i.e. the end of the most recently defined media in the
  *     manifest). Choosing a small value will minimize latency introduced by the player, however
  *     note that the value sets an upper bound on the length of media that the player can buffer.
  *     Hence a small value may increase the probability of rebuffering and playback failures.
  */
 public SmoothStreamingChunkSource(
     ManifestFetcher<SmoothStreamingManifest> manifestFetcher,
     SmoothStreamingTrackSelector trackSelector,
     DataSource dataSource,
     FormatEvaluator adaptiveFormatEvaluator,
     long liveEdgeLatencyMs) {
   this(
       manifestFetcher,
       manifestFetcher.getManifest(),
       trackSelector,
       dataSource,
       adaptiveFormatEvaluator,
       liveEdgeLatencyMs);
 }