Java Code Examples for com.google.android.exoplayer2.C#LENGTH_UNSET

The following examples show how to use com.google.android.exoplayer2.C#LENGTH_UNSET . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Ac3Util.java    From TelePlus-Android with GNU General Public License v2.0 6 votes vote down vote up
private static int getAc3SyncframeSize(int fscod, int frmsizecod) {
  int halfFrmsizecod = frmsizecod / 2;
  if (fscod < 0 || fscod >= SAMPLE_RATE_BY_FSCOD.length || frmsizecod < 0
      || halfFrmsizecod >= SYNCFRAME_SIZE_WORDS_BY_HALF_FRMSIZECOD_44_1.length) {
    // Invalid values provided.
    return C.LENGTH_UNSET;
  }
  int sampleRate = SAMPLE_RATE_BY_FSCOD[fscod];
  if (sampleRate == 44100) {
    return 2 * (SYNCFRAME_SIZE_WORDS_BY_HALF_FRMSIZECOD_44_1[halfFrmsizecod] + (frmsizecod % 2));
  }
  int bitrate = BITRATE_BY_HALF_FRMSIZECOD[halfFrmsizecod];
  if (sampleRate == 32000) {
    return 6 * bitrate;
  } else { // sampleRate == 48000
    return 4 * bitrate;
  }
}
 
Example 2
Source File: ExtractorMediaPeriod.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
    ConditionVariable loadCondition) {
  this.uri = Assertions.checkNotNull(uri);
  this.dataSource = new StatsDataSource(dataSource);
  this.extractorHolder = Assertions.checkNotNull(extractorHolder);
  this.loadCondition = loadCondition;
  this.positionHolder = new PositionHolder();
  this.pendingExtractorSeek = true;
  this.length = C.LENGTH_UNSET;
  dataSpec = new DataSpec(uri, positionHolder.position, C.LENGTH_UNSET, customCacheKey);
}
 
Example 3
Source File: DefaultHttpDataSource.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
/**
 * On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can
 * block for a long time if the stream has a lot of data remaining. Call this method before
 * closing the input stream to make a best effort to cause the input stream to encounter an
 * unexpected end of input, working around this issue. On other platform API levels, the method
 * does nothing.
 *
 * @param connection The connection whose {@link InputStream} should be terminated.
 * @param bytesRemaining The number of bytes remaining to be read from the input stream if its
 *     length is known. {@link C#LENGTH_UNSET} otherwise.
 */
private static void maybeTerminateInputStream(HttpURLConnection connection, long bytesRemaining) {
  if (Util.SDK_INT != 19 && Util.SDK_INT != 20) {
    return;
  }

  try {
    InputStream inputStream = connection.getInputStream();
    if (bytesRemaining == C.LENGTH_UNSET) {
      // If the input stream has already ended, do nothing. The socket may be re-used.
      if (inputStream.read() == -1) {
        return;
      }
    } else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) {
      // There isn't much data left. Prefer to allow it to drain, which may allow the socket to be
      // re-used.
      return;
    }
    String className = inputStream.getClass().getName();
    if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream".equals(className)
        || "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream"
            .equals(className)) {
      Class<?> superclass = inputStream.getClass().getSuperclass();
      Method unexpectedEndOfInput = superclass.getDeclaredMethod("unexpectedEndOfInput");
      unexpectedEndOfInput.setAccessible(true);
      unexpectedEndOfInput.invoke(inputStream);
    }
  } catch (Exception e) {
    // If an IOException then the connection didn't ever have an input stream, or it was closed
    // already. If another type of exception then something went wrong, most likely the device
    // isn't using okhttp.
  }
}
 
Example 4
Source File: Mp3Extractor.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (peekEndOfStreamOrHeader(extractorInput)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if (!headersMatch(sampleHeaderData, synchronizedHeaderData)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example 5
Source File: AdtsExtractor.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
/**
 * @param firstStreamSampleTimestampUs The timestamp to be used for the first sample of the stream
 *     output from this extractor.
 * @param flags Flags that control the extractor's behavior.
 */
public AdtsExtractor(long firstStreamSampleTimestampUs, @Flags int flags) {
  this.firstStreamSampleTimestampUs = firstStreamSampleTimestampUs;
  this.firstSampleTimestampUs = firstStreamSampleTimestampUs;
  this.flags = flags;
  reader = new AdtsReader(true);
  packetBuffer = new ParsableByteArray(MAX_PACKET_SIZE);
  averageFrameSize = C.LENGTH_UNSET;
  firstFramePosition = C.POSITION_UNSET;
  scratch = new ParsableByteArray(10);
  scratchBits = new ParsableBitArray(scratch.data);
}
 
Example 6
Source File: SegmentDownloader.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
@Override
public final float getDownloadPercentage() {
  // Take local snapshot of the volatile fields
  int totalSegments = this.totalSegments;
  int downloadedSegments = this.downloadedSegments;
  if (totalSegments == C.LENGTH_UNSET || downloadedSegments == C.LENGTH_UNSET) {
    return C.PERCENTAGE_UNSET;
  }
  return totalSegments == 0 ? 100f : (downloadedSegments * 100f) / totalSegments;
}
 
Example 7
Source File: AmrExtractor.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (currentSampleBytesRemaining == 0) {
    try {
      currentSampleSize = peekNextSampleSize(extractorInput);
    } catch (EOFException e) {
      return RESULT_END_OF_INPUT;
    }
    currentSampleBytesRemaining = currentSampleSize;
    if (firstSampleSize == C.LENGTH_UNSET) {
      firstSamplePosition = extractorInput.getPosition();
      firstSampleSize = currentSampleSize;
    }
    if (firstSampleSize == currentSampleSize) {
      numSamplesWithSameSize++;
    }
  }

  int bytesAppended =
      trackOutput.sampleData(
          extractorInput, currentSampleBytesRemaining, /* allowEndOfInput= */ true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  currentSampleBytesRemaining -= bytesAppended;
  if (currentSampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }

  trackOutput.sampleMetadata(
      timeOffsetUs + currentSampleTimeUs,
      C.BUFFER_FLAG_KEY_FRAME,
      currentSampleSize,
      /* offset= */ 0,
      /* encryptionData= */ null);
  currentSampleTimeUs += SAMPLE_TIME_PER_FRAME_US;
  return RESULT_CONTINUE;
}
 
Example 8
Source File: SegmentBase.java    From Telegram with GNU General Public License v2.0 5 votes vote down vote up
@Override
public RangedUri getSegmentUrl(Representation representation, long sequenceNumber) {
  long time;
  if (segmentTimeline != null) {
    time = segmentTimeline.get((int) (sequenceNumber - startNumber)).startTime;
  } else {
    time = (sequenceNumber - startNumber) * duration;
  }
  String uriString = mediaTemplate.buildUri(representation.format.id, sequenceNumber,
      representation.format.bitrate, time);
  return new RangedUri(uriString, 0, C.LENGTH_UNSET);
}
 
Example 9
Source File: Mp3Extractor.java    From K-Sonic with MIT License 5 votes vote down vote up
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException {
  if (sampleBytesRemaining == 0) {
    extractorInput.resetPeekPosition();
    if (!extractorInput.peekFully(scratch.data, 0, 4, true)) {
      return RESULT_END_OF_INPUT;
    }
    scratch.setPosition(0);
    int sampleHeaderData = scratch.readInt();
    if ((sampleHeaderData & HEADER_MASK) != (synchronizedHeaderData & HEADER_MASK)
        || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) {
      // We have lost synchronization, so attempt to resynchronize starting at the next byte.
      extractorInput.skipFully(1);
      synchronizedHeaderData = 0;
      return RESULT_CONTINUE;
    }
    MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader);
    if (basisTimeUs == C.TIME_UNSET) {
      basisTimeUs = seeker.getTimeUs(extractorInput.getPosition());
      if (forcedFirstSampleTimestampUs != C.TIME_UNSET) {
        long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0);
        basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs;
      }
    }
    sampleBytesRemaining = synchronizedHeader.frameSize;
  }
  int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true);
  if (bytesAppended == C.RESULT_END_OF_INPUT) {
    return RESULT_END_OF_INPUT;
  }
  sampleBytesRemaining -= bytesAppended;
  if (sampleBytesRemaining > 0) {
    return RESULT_CONTINUE;
  }
  long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate);
  trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, synchronizedHeader.frameSize, 0,
      null);
  samplesRead += synchronizedHeader.samplesPerFrame;
  sampleBytesRemaining = 0;
  return RESULT_CONTINUE;
}
 
Example 10
Source File: CacheDataSink.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
@Override
public void open(DataSpec dataSpec) throws CacheDataSinkException {
  if (dataSpec.length == C.LENGTH_UNSET
      && !dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH)) {
    this.dataSpec = null;
    return;
  }
  this.dataSpec = dataSpec;
  dataSpecBytesWritten = 0;
  try {
    openNextOutputStream();
  } catch (IOException e) {
    throw new CacheDataSinkException(e);
  }
}
 
Example 11
Source File: ExtractorMediaPeriod.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
private void maybeFinishPrepare() {
  if (released || prepared || seekMap == null || !sampleQueuesBuilt) {
    return;
  }
  for (SampleQueue sampleQueue : sampleQueues) {
    if (sampleQueue.getUpstreamFormat() == null) {
      return;
    }
  }
  loadCondition.close();
  int trackCount = sampleQueues.length;
  TrackGroup[] trackArray = new TrackGroup[trackCount];
  trackIsAudioVideoFlags = new boolean[trackCount];
  trackEnabledStates = new boolean[trackCount];
  trackFormatNotificationSent = new boolean[trackCount];
  durationUs = seekMap.getDurationUs();
  for (int i = 0; i < trackCount; i++) {
    Format trackFormat = sampleQueues[i].getUpstreamFormat();
    trackArray[i] = new TrackGroup(trackFormat);
    String mimeType = trackFormat.sampleMimeType;
    boolean isAudioVideo = MimeTypes.isVideo(mimeType) || MimeTypes.isAudio(mimeType);
    trackIsAudioVideoFlags[i] = isAudioVideo;
    haveAudioVideoTracks |= isAudioVideo;
  }
  tracks = new TrackGroupArray(trackArray);
  if (minLoadableRetryCount == ExtractorMediaSource.MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA
      && length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET) {
    actualMinLoadableRetryCount = ExtractorMediaSource.DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE;
  }
  prepared = true;
  listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
  callback.onPrepared(this);
}
 
Example 12
Source File: CacheUtil.java    From Telegram-FOSS with GNU General Public License v2.0 5 votes vote down vote up
private static long getRequestLength(DataSpec dataSpec, Cache cache, String key) {
  if (dataSpec.length != C.LENGTH_UNSET) {
    return dataSpec.length;
  } else {
    long contentLength = ContentMetadata.getContentLength(cache.getContentMetadata(key));
    return contentLength == C.LENGTH_UNSET
        ? C.LENGTH_UNSET
        : contentLength - dataSpec.absoluteStreamPosition;
  }
}
 
Example 13
Source File: ExtractorMediaPeriod.java    From TelePlus-Android with GNU General Public License v2.0 5 votes vote down vote up
private void maybeFinishPrepare() {
  if (released || prepared || seekMap == null || !sampleQueuesBuilt) {
    return;
  }
  for (SampleQueue sampleQueue : sampleQueues) {
    if (sampleQueue.getUpstreamFormat() == null) {
      return;
    }
  }
  loadCondition.close();
  int trackCount = sampleQueues.length;
  TrackGroup[] trackArray = new TrackGroup[trackCount];
  trackIsAudioVideoFlags = new boolean[trackCount];
  trackEnabledStates = new boolean[trackCount];
  trackFormatNotificationSent = new boolean[trackCount];
  durationUs = seekMap.getDurationUs();
  for (int i = 0; i < trackCount; i++) {
    Format trackFormat = sampleQueues[i].getUpstreamFormat();
    trackArray[i] = new TrackGroup(trackFormat);
    String mimeType = trackFormat.sampleMimeType;
    boolean isAudioVideo = MimeTypes.isVideo(mimeType) || MimeTypes.isAudio(mimeType);
    trackIsAudioVideoFlags[i] = isAudioVideo;
    haveAudioVideoTracks |= isAudioVideo;
  }
  tracks = new TrackGroupArray(trackArray);
  if (minLoadableRetryCount == ExtractorMediaSource.MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA
      && length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET) {
    actualMinLoadableRetryCount = ExtractorMediaSource.DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE;
  }
  prepared = true;
  listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
  callback.onPrepared(this);
}
 
Example 14
Source File: SegmentBase.java    From MediaSDK with Apache License 2.0 5 votes vote down vote up
@Override
public RangedUri getSegmentUrl(Representation representation, long sequenceNumber) {
  long time;
  if (segmentTimeline != null) {
    time = segmentTimeline.get((int) (sequenceNumber - startNumber)).startTime;
  } else {
    time = (sequenceNumber - startNumber) * duration;
  }
  String uriString = mediaTemplate.buildUri(representation.format.id, sequenceNumber,
      representation.format.bitrate, time);
  return new RangedUri(uriString, 0, C.LENGTH_UNSET);
}
 
Example 15
Source File: StreamReader.java    From MediaSDK with Apache License 2.0 4 votes vote down vote up
private int readHeaders(ExtractorInput input) throws IOException, InterruptedException {
  boolean readingHeaders = true;
  while (readingHeaders) {
    if (!oggPacket.populate(input)) {
      state = STATE_END_OF_INPUT;
      return Extractor.RESULT_END_OF_INPUT;
    }
    lengthOfReadPacket = input.getPosition() - payloadStartPosition;

    readingHeaders = readHeaders(oggPacket.getPayload(), payloadStartPosition, setupData);
    if (readingHeaders) {
      payloadStartPosition = input.getPosition();
    }
  }

  sampleRate = setupData.format.sampleRate;
  if (!formatSet) {
    trackOutput.format(setupData.format);
    formatSet = true;
  }

  if (setupData.oggSeeker != null) {
    oggSeeker = setupData.oggSeeker;
  } else if (input.getLength() == C.LENGTH_UNSET) {
    oggSeeker = new UnseekableOggSeeker();
  } else {
    OggPageHeader firstPayloadPageHeader = oggPacket.getPageHeader();
    boolean isLastPage = (firstPayloadPageHeader.type & 0x04) != 0; // Type 4 is end of stream.
    oggSeeker =
        new DefaultOggSeeker(
            this,
            payloadStartPosition,
            input.getLength(),
            firstPayloadPageHeader.headerSize + firstPayloadPageHeader.bodySize,
            firstPayloadPageHeader.granulePosition,
            isLastPage);
  }

  setupData = null;
  state = STATE_READ_PAYLOAD;
  // First payload packet. Trim the payload array of the ogg packet after headers have been read.
  oggPacket.trimPayload();
  return Extractor.RESULT_CONTINUE;
}
 
Example 16
Source File: MpegAudioHeader.java    From TelePlus-Android with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Returns the size of the frame associated with {@code header}, or {@link C#LENGTH_UNSET} if it
 * is invalid.
 */
public static int getFrameSize(int header) {
  if ((header & 0xFFE00000) != 0xFFE00000) {
    return C.LENGTH_UNSET;
  }

  int version = (header >>> 19) & 3;
  if (version == 1) {
    return C.LENGTH_UNSET;
  }

  int layer = (header >>> 17) & 3;
  if (layer == 0) {
    return C.LENGTH_UNSET;
  }

  int bitrateIndex = (header >>> 12) & 15;
  if (bitrateIndex == 0 || bitrateIndex == 0xF) {
    // Disallow "free" bitrate.
    return C.LENGTH_UNSET;
  }

  int samplingRateIndex = (header >>> 10) & 3;
  if (samplingRateIndex == 3) {
    return C.LENGTH_UNSET;
  }

  int samplingRate = SAMPLING_RATE_V1[samplingRateIndex];
  if (version == 2) {
    // Version 2
    samplingRate /= 2;
  } else if (version == 0) {
    // Version 2.5
    samplingRate /= 4;
  }

  int bitrate;
  int padding = (header >>> 9) & 1;
  if (layer == 3) {
    // Layer I (layer == 3)
    bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
    return (12000 * bitrate / samplingRate + padding) * 4;
  } else {
    // Layer II (layer == 2) or III (layer == 1)
    if (version == 3) {
      bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1];
    } else {
      // Version 2 or 2.5.
      bitrate = BITRATE_V2[bitrateIndex - 1];
    }
  }

  if (version == 3) {
    // Version 1
    return 144000 * bitrate / samplingRate + padding;
  } else {
    // Version 2 or 2.5
    return (layer == 1 ? 72000 : 144000) * bitrate / samplingRate + padding;
  }
}
 
Example 17
Source File: ExtractorMediaPeriod.java    From TelePlus-Android with GNU General Public License v2.0 4 votes vote down vote up
private void copyLengthFromLoader(ExtractingLoadable loadable) {
  if (length == C.LENGTH_UNSET) {
    length = loadable.length;
  }
}
 
Example 18
Source File: AdtsExtractor.java    From MediaSDK with Apache License 2.0 4 votes vote down vote up
private void calculateAverageFrameSize(ExtractorInput input)
    throws IOException, InterruptedException {
  if (hasCalculatedAverageFrameSize) {
    return;
  }
  averageFrameSize = C.LENGTH_UNSET;
  input.resetPeekPosition();
  if (input.getPosition() == 0) {
    // Skip any ID3 headers.
    peekId3Header(input);
  }

  int numValidFrames = 0;
  long totalValidFramesSize = 0;
  try {
    while (input.peekFully(
        scratch.data, /* offset= */ 0, /* length= */ 2, /* allowEndOfInput= */ true)) {
      scratch.setPosition(0);
      int syncBytes = scratch.readUnsignedShort();
      if (!AdtsReader.isAdtsSyncWord(syncBytes)) {
        // Invalid sync byte pattern.
        // Constant bit-rate seeking will probably fail for this stream.
        numValidFrames = 0;
        break;
      } else {
        // Read the frame size.
        if (!input.peekFully(
            scratch.data, /* offset= */ 0, /* length= */ 4, /* allowEndOfInput= */ true)) {
          break;
        }
        scratchBits.setPosition(14);
        int currentFrameSize = scratchBits.readBits(13);
        // Either the stream is malformed OR we're not parsing an ADTS stream.
        if (currentFrameSize <= 6) {
          hasCalculatedAverageFrameSize = true;
          throw new ParserException("Malformed ADTS stream");
        }
        totalValidFramesSize += currentFrameSize;
        if (++numValidFrames == NUM_FRAMES_FOR_AVERAGE_FRAME_SIZE) {
          break;
        }
        if (!input.advancePeekPosition(currentFrameSize - 6, /* allowEndOfInput= */ true)) {
          break;
        }
      }
    }
  } catch (EOFException e) {
    // We reached the end of the input during a peekFully() or advancePeekPosition() operation.
    // This is OK, it just means the input has an incomplete ADTS frame at the end. Ideally
    // ExtractorInput would allow these operations to encounter end-of-input without throwing an
    // exception [internal: b/145586657].
  }
  input.resetPeekPosition();
  if (numValidFrames > 0) {
    averageFrameSize = (int) (totalValidFramesSize / numValidFrames);
  } else {
    averageFrameSize = C.LENGTH_UNSET;
  }
  hasCalculatedAverageFrameSize = true;
}
 
Example 19
Source File: SimpleCacheSpan.java    From MediaSDK with Apache License 2.0 2 votes vote down vote up
/**
 * Creates an open hole span.
 *
 * @param key The cache key.
 * @param position The position of the {@link CacheSpan} in the original stream.
 * @return The span.
 */
public static SimpleCacheSpan createOpenHole(String key, long position) {
  return new SimpleCacheSpan(key, position, C.LENGTH_UNSET, C.TIME_UNSET, null);
}
 
Example 20
Source File: DefaultHttpDataSource.java    From Telegram-FOSS with GNU General Public License v2.0 2 votes vote down vote up
/**
 * Returns the number of bytes that are still to be read for the current {@link DataSpec}.
 * <p>
 * If the total length of the data being read is known, then this length minus {@code bytesRead()}
 * is returned. If the total length is unknown, {@link C#LENGTH_UNSET} is returned.
 *
 * @return The remaining length, or {@link C#LENGTH_UNSET}.
 */
protected final long bytesRemaining() {
  return bytesToRead == C.LENGTH_UNSET ? bytesToRead : bytesToRead - bytesRead;
}