private void maybeStartLoading() {
   Chunk currentLoadable = currentLoadableHolder.chunk;
   if (currentLoadable == null) {
     // Nothing to load.
     return;
   }
   currentLoadStartTimeMs = SystemClock.elapsedRealtime();
   if (isMediaChunk(currentLoadable)) {
     BaseMediaChunk mediaChunk = (BaseMediaChunk) currentLoadable;
     mediaChunk.init(sampleQueue);
     mediaChunks.add(mediaChunk);
     if (isPendingReset()) {
       pendingResetPositionUs = NO_RESET_PENDING;
     }
     notifyLoadStarted(
         mediaChunk.dataSpec.length,
         mediaChunk.type,
         mediaChunk.trigger,
         mediaChunk.format,
         mediaChunk.startTimeUs,
         mediaChunk.endTimeUs);
   } else {
     notifyLoadStarted(
         currentLoadable.dataSpec.length,
         currentLoadable.type,
         currentLoadable.trigger,
         currentLoadable.format,
         -1,
         -1);
   }
   loader.startLoading(currentLoadable, this);
 }
  @Override
  public int readData(
      int track, long positionUs, MediaFormatHolder formatHolder, SampleHolder sampleHolder) {
    Assertions.checkState(state == STATE_ENABLED);
    downstreamPositionUs = positionUs;

    if (pendingDiscontinuity || isPendingReset()) {
      return NOTHING_READ;
    }

    boolean haveSamples = !sampleQueue.isEmpty();
    BaseMediaChunk currentChunk = mediaChunks.getFirst();
    while (haveSamples
        && mediaChunks.size() > 1
        && mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) {
      mediaChunks.removeFirst();
      currentChunk = mediaChunks.getFirst();
    }

    Format format = currentChunk.format;
    if (!format.equals(downstreamFormat)) {
      notifyDownstreamFormatChanged(format, currentChunk.trigger, currentChunk.startTimeUs);
    }
    downstreamFormat = format;

    if (haveSamples || currentChunk.isMediaFormatFinal) {
      MediaFormat mediaFormat = currentChunk.getMediaFormat();
      if (!mediaFormat.equals(downstreamMediaFormat)) {
        formatHolder.format = mediaFormat;
        formatHolder.drmInitData = currentChunk.getDrmInitData();
        downstreamMediaFormat = mediaFormat;
        return FORMAT_READ;
      }
      // If mediaFormat and downstreamMediaFormat are equal but different objects then the equality
      // check above will have been expensive, comparing the fields in each format. We update
      // downstreamMediaFormat here so that referential equality can be cheaply established during
      // subsequent calls.
      downstreamMediaFormat = mediaFormat;
    }

    if (!haveSamples) {
      if (loadingFinished) {
        return END_OF_STREAM;
      }
      return NOTHING_READ;
    }

    if (sampleQueue.getSample(sampleHolder)) {
      boolean decodeOnly = sampleHolder.timeUs < lastSeekPositionUs;
      sampleHolder.flags |= decodeOnly ? C.SAMPLE_FLAG_DECODE_ONLY : 0;
      onSampleRead(currentChunk, sampleHolder);
      return SAMPLE_READ;
    }

    return NOTHING_READ;
  }
  /**
   * Discard upstream media chunks until the queue length is equal to the length specified.
   *
   * @param queueLength The desired length of the queue.
   * @return True if chunks were discarded. False otherwise.
   */
  private boolean discardUpstreamMediaChunks(int queueLength) {
    if (mediaChunks.size() <= queueLength) {
      return false;
    }
    long startTimeUs = 0;
    long endTimeUs = mediaChunks.getLast().endTimeUs;

    BaseMediaChunk removed = null;
    while (mediaChunks.size() > queueLength) {
      removed = mediaChunks.removeLast();
      startTimeUs = removed.startTimeUs;
      loadingFinished = false;
    }
    sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex());

    notifyUpstreamDiscarded(startTimeUs, endTimeUs);
    return true;
  }