@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; }
@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; }
@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); } }
@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; } }
@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; }
/** * 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(); } }
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); }