@Override
 public void setRequestProperty(String name, String value) {
   Assertions.checkNotNull(name);
   Assertions.checkNotNull(value);
   synchronized (requestProperties) {
     requestProperties.put(name, value);
   }
 }
  TrackSampleTable(long[] offsets, int[] sizes, long[] timestampsUs, int[] flags) {
    Assertions.checkArgument(sizes.length == timestampsUs.length);
    Assertions.checkArgument(offsets.length == timestampsUs.length);
    Assertions.checkArgument(flags.length == timestampsUs.length);

    this.offsets = offsets;
    this.sizes = sizes;
    this.timestampsUs = timestampsUs;
    this.flags = flags;
    sampleCount = offsets.length;
  }
 /**
  * @param source A source from which samples containing subtitle data can be read.
  * @param textRenderer The text renderer.
  * @param textRendererLooper The looper associated with the thread on which textRenderer should be
  *     invoked. If the renderer makes use of standard Android UI components, then this should
  *     normally be the looper associated with the applications' main thread, which can be obtained
  *     using {@link android.app.Activity#getMainLooper()}. Null may be passed if the renderer
  *     should be invoked directly on the player's internal rendering thread.
  * @param subtitleParsers An array of available subtitle parsers. Where multiple parsers are able
  *     to render a subtitle, the one with the lowest index will be preferred.
  */
 public TextTrackRenderer(
     SampleSource source,
     TextRenderer textRenderer,
     Looper textRendererLooper,
     SubtitleParser... subtitleParsers) {
   this.source = Assertions.checkNotNull(source);
   this.textRenderer = Assertions.checkNotNull(textRenderer);
   this.textRendererHandler =
       textRendererLooper == null ? null : new Handler(textRendererLooper, this);
   this.subtitleParsers = Assertions.checkNotNull(subtitleParsers);
   formatHolder = new MediaFormatHolder();
 }
 /**
  * @param client An {@link OkHttpClient} for use by the source.
  * @param userAgent The User-Agent string that should be used.
  * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
  *     predicate then a {@link
  *     com.google.android.exoplayer.upstream.HttpDataSource.InvalidContentTypeException} is thrown
  *     from {@link #open(DataSpec)}.
  * @param listener An optional listener.
  * @param cacheControl An optional {@link CacheControl} which sets all requests' Cache-Control
  *     header. For example, you could force the network response for all requests.
  */
 public OkHttpDataSource(
     OkHttpClient client,
     String userAgent,
     Predicate<String> contentTypePredicate,
     TransferListener listener,
     CacheControl cacheControl) {
   this.okHttpClient = Assertions.checkNotNull(client);
   this.userAgent = Assertions.checkNotEmpty(userAgent);
   this.contentTypePredicate = contentTypePredicate;
   this.listener = listener;
   this.cacheControl = cacheControl;
   this.requestProperties = new HashMap<>();
 }
 @Override
 public void enable(int track, long positionUs) {
   Assertions.checkState(state == STATE_PREPARED);
   Assertions.checkState(enabledTrackCount++ == 0);
   state = STATE_ENABLED;
   chunkSource.enable(track);
   loadControl.register(this, bufferSizeContribution);
   downstreamFormat = null;
   downstreamMediaFormat = null;
   downstreamPositionUs = positionUs;
   lastSeekPositionUs = positionUs;
   pendingDiscontinuity = false;
   restartFrom(positionUs);
 }
  @Override
  public void seekToUs(long positionUs) {
    Assertions.checkState(state == STATE_ENABLED);

    long currentPositionUs = isPendingReset() ? pendingResetPositionUs : downstreamPositionUs;
    downstreamPositionUs = positionUs;
    lastSeekPositionUs = positionUs;
    if (currentPositionUs == positionUs) {
      return;
    }

    // If we're not pending a reset, see if we can seek within the sample queue.
    boolean seekInsideBuffer = !isPendingReset() && sampleQueue.skipToKeyframeBefore(positionUs);
    if (seekInsideBuffer) {
      // We succeeded. All we need to do is discard any chunks that we've moved past.
      boolean haveSamples = !sampleQueue.isEmpty();
      while (haveSamples
          && mediaChunks.size() > 1
          && mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) {
        mediaChunks.removeFirst();
      }
    } else {
      // We failed, and need to restart.
      restartFrom(positionUs);
    }
    // Either way, we need to send a discontinuity to the downstream components.
    pendingDiscontinuity = true;
  }
 @Override
 public void clearRequestProperty(String name) {
   Assertions.checkNotNull(name);
   synchronized (requestProperties) {
     requestProperties.remove(name);
   }
 }
 @Override
 public boolean continueBuffering(int track, long positionUs) {
   Assertions.checkState(state == STATE_ENABLED);
   downstreamPositionUs = positionUs;
   chunkSource.continueBuffering(positionUs);
   updateLoadControl();
   return loadingFinished || !sampleQueue.isEmpty();
 }
 @Override
 public void release() {
   Assertions.checkState(state != STATE_ENABLED);
   if (loader != null) {
     loader.release();
     loader = null;
   }
   state = STATE_IDLE;
 }
 /**
  * Construct a {@link com.google.android.exoplayer.upstream.DataSpec}.
  *
  * @param uri {@link #uri}.
  * @param absoluteStreamPosition {@link #absoluteStreamPosition}.
  * @param length {@link #length}.
  * @param key {@link #key}.
  * @param position {@link #position}.
  * @param uriIsFullStream {@link #uriIsFullStream}.
  */
 public DataSpec(
     Uri uri,
     long absoluteStreamPosition,
     long length,
     String key,
     long position,
     boolean uriIsFullStream) {
   Assertions.checkArgument(absoluteStreamPosition >= 0);
   Assertions.checkArgument(position >= 0);
   Assertions.checkArgument(length > 0 || length == LENGTH_UNBOUNDED);
   Assertions.checkArgument(absoluteStreamPosition == position || !uriIsFullStream);
   this.uri = uri;
   this.uriIsFullStream = uriIsFullStream;
   this.absoluteStreamPosition = absoluteStreamPosition;
   this.position = position;
   this.length = length;
   this.key = key;
 }
Example #11
0
  @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;
  }
Example #12
0
 @Override
 public void disable(int track) {
   Assertions.checkState(state == STATE_ENABLED);
   Assertions.checkState(--enabledTrackCount == 0);
   state = STATE_PREPARED;
   try {
     chunkSource.disable(mediaChunks);
   } finally {
     loadControl.unregister(this);
     if (loader.isLoading()) {
       loader.cancelLoading();
     } else {
       sampleQueue.clear();
       mediaChunks.clear();
       clearCurrentLoadable();
       loadControl.trimAllocator();
     }
   }
 }
 final void enable(int i, long l, boolean flag) {
   boolean flag1;
   if (state == 1) {
     flag1 = true;
   } else {
     flag1 = false;
   }
   Assertions.checkState(flag1);
   state = 2;
   onEnabled(i, l, flag);
 }
 final void stop() {
   boolean flag;
   if (state == 3) {
     flag = true;
   } else {
     flag = false;
   }
   Assertions.checkState(flag);
   state = 2;
   onStopped();
 }
 final void disable() {
   boolean flag;
   if (state == 2) {
     flag = true;
   } else {
     flag = false;
   }
   Assertions.checkState(flag);
   state = 1;
   onDisabled();
 }
 final void release() {
   boolean flag;
   if (state != 2 && state != 3 && state != -1) {
     flag = true;
   } else {
     flag = false;
   }
   Assertions.checkState(flag);
   state = -1;
   onReleased();
 }
 final void start() {
   boolean flag;
   if (state == 2) {
     flag = true;
   } else {
     flag = false;
   }
   Assertions.checkState(flag);
   state = 3;
   onStarted();
 }
 @Override
 public DataSink open(DataSpec dataSpec) throws CacheDataSinkException {
   // TODO: Support caching for unbounded requests. See TODO in {@link CacheDataSource} for
   // more details.
   Assertions.checkState(dataSpec.length != C.LENGTH_UNBOUNDED);
   try {
     this.dataSpec = dataSpec;
     dataSpecBytesWritten = 0;
     openNextOutputStream();
     return this;
   } catch (FileNotFoundException e) {
     throw new CacheDataSinkException(e);
   }
 }
Example #19
0
 @Override
 public long getBufferedPositionUs() {
   Assertions.checkState(state == STATE_ENABLED);
   if (isPendingReset()) {
     return pendingResetPositionUs;
   } else if (loadingFinished) {
     return TrackRenderer.END_OF_TRACK_US;
   } else {
     long largestParsedTimestampUs = sampleQueue.getLargestParsedTimestampUs();
     return largestParsedTimestampUs == Long.MIN_VALUE
         ? downstreamPositionUs
         : largestParsedTimestampUs;
   }
 }
Example #20
0
 @Override
 public boolean prepare(long positionUs) {
   Assertions.checkState(state == STATE_INITIALIZED || state == STATE_PREPARED);
   if (state == STATE_PREPARED) {
     return true;
   } else if (!chunkSource.prepare()) {
     return false;
   }
   if (chunkSource.getTrackCount() > 0) {
     loader = new Loader("Loader:" + chunkSource.getFormat(0).mimeType);
   }
   state = STATE_PREPARED;
   return true;
 }
 /**
  * @param userAgent The User-Agent string that should be used.
  * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
  *     predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
  *     #open(DataSpec)}.
  * @param listener An optional listener.
  * @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
  *     interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use the
  *     default value.
  * @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as
  *     an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value.
  * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
  *     to HTTPS and vice versa) are enabled.
  */
 public DefaultHttpDataSource(
     String userAgent,
     Predicate<String> contentTypePredicate,
     TransferListener listener,
     int connectTimeoutMillis,
     int readTimeoutMillis,
     boolean allowCrossProtocolRedirects) {
   this.userAgent = Assertions.checkNotEmpty(userAgent);
   this.contentTypePredicate = contentTypePredicate;
   this.listener = listener;
   this.requestProperties = new HashMap<>();
   this.connectTimeoutMillis = connectTimeoutMillis;
   this.readTimeoutMillis = readTimeoutMillis;
   this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
 }
 public final DataSink open(DataSpec dataspec) {
   if (dataspec.length == -1L) {
     stream = new ByteArrayOutputStream();
     return this;
   }
   boolean flag;
   if (dataspec.length <= 0x7fffffffL) {
     flag = true;
   } else {
     flag = false;
   }
   Assertions.checkArgument(flag);
   stream = new ByteArrayOutputStream((int) dataspec.length);
   return this;
 }
 final int prepare(long l) {
   boolean flag;
   if (state == 0) {
     flag = true;
   } else {
     flag = false;
   }
   Assertions.checkState(flag);
   int i;
   if (doPrepare(l)) {
     i = 1;
   } else {
     i = 0;
   }
   state = i;
   return state;
 }
Example #24
0
  /**
   * Resumes loading.
   *
   * <p>If the {@link ChunkSource} returns a chunk equivalent to the backed off chunk B, then the
   * loading of B will be resumed. In all other cases B will be discarded and the new chunk will be
   * loaded.
   */
  private void resumeFromBackOff() {
    currentLoadableException = null;

    Chunk backedOffChunk = currentLoadableHolder.chunk;
    if (!isMediaChunk(backedOffChunk)) {
      doChunkOperation();
      discardUpstreamMediaChunks(currentLoadableHolder.queueSize);
      if (currentLoadableHolder.chunk == backedOffChunk) {
        // Chunk was unchanged. Resume loading.
        loader.startLoading(backedOffChunk, this);
      } else {
        // Chunk was changed. Notify that the existing load was canceled.
        notifyLoadCanceled(backedOffChunk.bytesLoaded());
        // Start loading the replacement.
        maybeStartLoading();
      }
      return;
    }

    if (backedOffChunk == mediaChunks.getFirst()) {
      // We're not able to clear the first media chunk, so we have no choice but to continue
      // loading it.
      loader.startLoading(backedOffChunk, this);
      return;
    }

    // The current loadable is the last media chunk. Remove it before we invoke the chunk source,
    // and add it back again afterwards.
    BaseMediaChunk removedChunk = mediaChunks.removeLast();
    Assertions.checkState(backedOffChunk == removedChunk);
    doChunkOperation();
    mediaChunks.add(removedChunk);

    if (currentLoadableHolder.chunk == backedOffChunk) {
      // Chunk was unchanged. Resume loading.
      loader.startLoading(backedOffChunk, this);
    } else {
      // Chunk was changed. Notify that the existing load was canceled.
      notifyLoadCanceled(backedOffChunk.bytesLoaded());
      // This call will remove and release at least one chunk from the end of mediaChunks. Since
      // the current loadable is the last media chunk, it is guaranteed to be removed.
      discardUpstreamMediaChunks(currentLoadableHolder.queueSize);
      clearCurrentLoadableException();
      maybeStartLoading();
    }
  }
Example #25
0
 private int getVariantIndexForBandwidth(long bitrateEstimate) {
   if (bitrateEstimate == BandwidthMeter.NO_ESTIMATE) {
     // Select the lowest quality.
     bitrateEstimate = 0;
   }
   int effectiveBitrate = (int) (bitrateEstimate * BANDWIDTH_FRACTION);
   int lowestQualityEnabledVariantIndex = -1;
   for (int i = 0; i < variants.length; i++) {
     if (variantBlacklistTimes[i] == 0) {
       if (variants[i].format.bitrate <= effectiveBitrate) {
         return i;
       }
       lowestQualityEnabledVariantIndex = i;
     }
   }
   // At least one variant should always be enabled.
   Assertions.checkState(lowestQualityEnabledVariantIndex != -1);
   return lowestQualityEnabledVariantIndex;
 }
 @Override
 public MediaFormat getFormat(int track) {
   Assertions.checkState(prepared);
   return sampleQueues.valueAt(track).getMediaFormat();
 }
 @Override
 public int getTrackCount() {
   Assertions.checkState(prepared);
   return sampleQueues.size();
 }
 @Override
 public boolean hasSamples(int track) {
   Assertions.checkState(prepared);
   return !sampleQueues.valueAt(track).isEmpty();
 }
 @Override
 public void discardUntil(int track, long timeUs) {
   Assertions.checkState(prepared);
   sampleQueues.valueAt(track).discardUntil(timeUs);
 }
 @Override
 public boolean getSample(int track, SampleHolder holder) {
   Assertions.checkState(prepared);
   return sampleQueues.valueAt(track).getSample(holder);
 }