private void restartFrom(long positionUs) { pendingResetPositionUs = positionUs; loadingFinished = false; if (loader.isLoading()) { loader.cancelLoading(); } else { sampleQueue.clear(); mediaChunks.clear(); clearCurrentLoadable(); updateLoadControl(); } }
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); }
/** * 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(); } }
@Override public void release() { Assertions.checkState(state != STATE_ENABLED); if (loader != null) { loader.release(); loader = null; } state = STATE_IDLE; }
@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(); } } }
private void updateLoadControl() { long now = SystemClock.elapsedRealtime(); long nextLoadPositionUs = getNextLoadPositionUs(); boolean isBackedOff = currentLoadableException != null; boolean loadingOrBackedOff = loader.isLoading() || isBackedOff; // If we're not loading or backed off, evaluate the operation if (a) we don't have the next // chunk yet and we're not finished, or (b) if the last evaluation was over 2000ms ago. if (!loadingOrBackedOff && ((currentLoadableHolder.chunk == null && nextLoadPositionUs != -1) || (now - lastPerformedBufferOperation > 2000))) { // Perform the evaluation. lastPerformedBufferOperation = now; doChunkOperation(); boolean chunksDiscarded = discardUpstreamMediaChunks(currentLoadableHolder.queueSize); // Update the next load position as appropriate. if (currentLoadableHolder.chunk == null) { // Set loadPosition to -1 to indicate that we don't have anything to load. nextLoadPositionUs = -1; } else if (chunksDiscarded) { // Chunks were discarded, so we need to re-evaluate the load position. nextLoadPositionUs = getNextLoadPositionUs(); } } // Update the control with our current state, and determine whether we're the next loader. boolean nextLoader = loadControl.update(this, downstreamPositionUs, nextLoadPositionUs, loadingOrBackedOff); if (isBackedOff) { long elapsedMillis = now - currentLoadableExceptionTimestamp; if (elapsedMillis >= getRetryDelayMillis(currentLoadableExceptionCount)) { resumeFromBackOff(); } return; } if (!loader.isLoading() && nextLoader) { maybeStartLoading(); } }