Commit e74fc80a by olly Committed by Oliver Woodman

Loader improvements.

This change moves generally useful functionality (load timing
and fatal error propagation) inside of Loader, so that callers
don't have to duplicate effort.

The change also makes use of generics so that the callback
receives a Loadable with a more specific type.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123304369
parent db79f798
...@@ -31,8 +31,8 @@ import java.util.List; ...@@ -31,8 +31,8 @@ import java.util.List;
/** /**
* A {@link SampleSource} that loads the data at a given {@link Uri} as a single sample. * A {@link SampleSource} that loads the data at a given {@link Uri} as a single sample.
*/ */
public final class SingleSampleSource implements SampleSource, TrackStream, Loader.Callback, public final class SingleSampleSource implements SampleSource, TrackStream,
Loadable { Loader.Callback<SingleSampleSource>, Loadable {
/** /**
* Interface definition for a callback to be notified of {@link SingleSampleSource} events. * Interface definition for a callback to be notified of {@link SingleSampleSource} events.
...@@ -65,7 +65,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load ...@@ -65,7 +65,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
private final Uri uri; private final Uri uri;
private final DataSource dataSource; private final DataSource dataSource;
private final Loader loader; private final Loader<SingleSampleSource> loader;
private final Format format; private final Format format;
private final long durationUs; private final long durationUs;
private final TrackGroupArray tracks; private final TrackGroupArray tracks;
...@@ -99,7 +99,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load ...@@ -99,7 +99,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
this.eventSourceId = eventSourceId; this.eventSourceId = eventSourceId;
loader = new Loader("Loader:SingleSampleSource", minLoadableRetryCount); loader = new Loader<>("Loader:SingleSampleSource", minLoadableRetryCount);
tracks = new TrackGroupArray(new TrackGroup(format)); tracks = new TrackGroupArray(new TrackGroup(format));
sampleData = new byte[INITIAL_SAMPLE_SIZE]; sampleData = new byte[INITIAL_SAMPLE_SIZE];
} }
...@@ -214,17 +214,17 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load ...@@ -214,17 +214,17 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
// Loader.Callback implementation. // Loader.Callback implementation.
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCompleted(SingleSampleSource loadable, long elapsedMs) {
loadingFinished = true; loadingFinished = true;
} }
@Override @Override
public void onLoadCanceled(Loadable loadable) { public void onLoadCanceled(SingleSampleSource loadable, long elapsedMs) {
maybeStartLoading(); maybeStartLoading();
} }
@Override @Override
public int onLoadError(Loadable loadable, IOException e) { public int onLoadError(SingleSampleSource loadable, long elapsedMs, IOException e) {
notifyLoadError(e); notifyLoadError(e);
return Loader.RETRY; return Loader.RETRY;
} }
......
...@@ -24,7 +24,6 @@ import com.google.android.exoplayer.TrackStream; ...@@ -24,7 +24,6 @@ import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener.EventDispatcher; import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener.EventDispatcher;
import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.os.Handler; import android.os.Handler;
...@@ -38,9 +37,9 @@ import java.util.List; ...@@ -38,9 +37,9 @@ import java.util.List;
/** /**
* A {@link TrackStream} that loads media in {@link Chunk}s, obtained from a {@link ChunkSource}. * A {@link TrackStream} that loads media in {@link Chunk}s, obtained from a {@link ChunkSource}.
*/ */
public class ChunkTrackStream implements TrackStream, Loader.Callback { public class ChunkTrackStream implements TrackStream, Loader.Callback<Chunk> {
private final Loader loader; private final Loader<Chunk> loader;
private final ChunkSource chunkSource; private final ChunkSource chunkSource;
private final LinkedList<BaseMediaChunk> mediaChunks; private final LinkedList<BaseMediaChunk> mediaChunks;
private final List<BaseMediaChunk> readOnlyMediaChunks; private final List<BaseMediaChunk> readOnlyMediaChunks;
...@@ -57,8 +56,6 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -57,8 +56,6 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
private long lastSeekPositionUs; private long lastSeekPositionUs;
private long pendingResetPositionUs; private long pendingResetPositionUs;
private Chunk currentLoadable;
private long currentLoadStartTimeMs;
private boolean loadingFinished; private boolean loadingFinished;
private boolean released; private boolean released;
...@@ -79,7 +76,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -79,7 +76,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
ChunkTrackStreamEventListener eventListener, int eventSourceId, int minLoadableRetryCount) { ChunkTrackStreamEventListener eventListener, int eventSourceId, int minLoadableRetryCount) {
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.loadControl = loadControl; this.loadControl = loadControl;
loader = new Loader("Loader:ChunkTrackStream", minLoadableRetryCount); loader = new Loader<>("Loader:ChunkTrackStream", minLoadableRetryCount);
eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId); eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId);
nextChunkHolder = new ChunkHolder(); nextChunkHolder = new ChunkHolder();
mediaChunks = new LinkedList<>(); mediaChunks = new LinkedList<>();
...@@ -124,7 +121,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -124,7 +121,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
} else { } else {
long bufferedPositionUs = downstreamPositionUs; long bufferedPositionUs = downstreamPositionUs;
BaseMediaChunk lastMediaChunk = mediaChunks.getLast(); BaseMediaChunk lastMediaChunk = mediaChunks.getLast();
BaseMediaChunk lastCompletedMediaChunk = lastMediaChunk != currentLoadable ? lastMediaChunk BaseMediaChunk lastCompletedMediaChunk = lastMediaChunk.isLoadCompleted() ? lastMediaChunk
: mediaChunks.size() > 1 ? mediaChunks.get(mediaChunks.size() - 2) : null; : mediaChunks.size() > 1 ? mediaChunks.get(mediaChunks.size() - 2) : null;
if (lastCompletedMediaChunk != null) { if (lastCompletedMediaChunk != null) {
bufferedPositionUs = Math.max(bufferedPositionUs, lastCompletedMediaChunk.endTimeUs); bufferedPositionUs = Math.max(bufferedPositionUs, lastCompletedMediaChunk.endTimeUs);
...@@ -210,26 +207,24 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -210,26 +207,24 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
// Loader.Callback implementation. // Loader.Callback implementation.
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCompleted(Chunk loadable, long elapsedMs) {
long now = SystemClock.elapsedRealtime(); long now = SystemClock.elapsedRealtime();
long loadDurationMs = now - currentLoadStartTimeMs; chunkSource.onChunkLoadCompleted(loadable);
chunkSource.onChunkLoadCompleted(currentLoadable); if (isMediaChunk(loadable)) {
if (isMediaChunk(currentLoadable)) { BaseMediaChunk mediaChunk = (BaseMediaChunk) loadable;
BaseMediaChunk mediaChunk = (BaseMediaChunk) currentLoadable; eventDispatcher.loadCompleted(loadable.bytesLoaded(), mediaChunk.type,
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), mediaChunk.type,
mediaChunk.trigger, mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs, now, mediaChunk.trigger, mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs, now,
loadDurationMs); elapsedMs);
} else { } else {
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), currentLoadable.type, eventDispatcher.loadCompleted(loadable.bytesLoaded(), loadable.type, loadable.trigger,
currentLoadable.trigger, currentLoadable.format, -1, -1, now, loadDurationMs); loadable.format, -1, -1, now, elapsedMs);
} }
clearCurrentLoadable();
maybeStartLoading(); maybeStartLoading();
} }
@Override @Override
public void onLoadCanceled(Loadable loadable) { public void onLoadCanceled(Chunk loadable, long elapsedMs) {
eventDispatcher.loadCanceled(currentLoadable.bytesLoaded()); eventDispatcher.loadCanceled(loadable.bytesLoaded());
if (!released) { if (!released) {
restartFrom(pendingResetPositionUs); restartFrom(pendingResetPositionUs);
} else { } else {
...@@ -239,20 +234,19 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -239,20 +234,19 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
} }
@Override @Override
public int onLoadError(Loadable loadable, IOException e) { public int onLoadError(Chunk loadable, long elapsedMs, IOException e) {
long bytesLoaded = currentLoadable.bytesLoaded(); long bytesLoaded = loadable.bytesLoaded();
boolean isMediaChunk = isMediaChunk(currentLoadable); boolean isMediaChunk = isMediaChunk(loadable);
boolean cancelable = !isMediaChunk || bytesLoaded == 0 || mediaChunks.size() > 1; boolean cancelable = !isMediaChunk || bytesLoaded == 0 || mediaChunks.size() > 1;
if (chunkSource.onChunkLoadError(currentLoadable, cancelable, e)) { if (chunkSource.onChunkLoadError(loadable, cancelable, e)) {
if (isMediaChunk) { if (isMediaChunk) {
BaseMediaChunk removed = mediaChunks.removeLast(); BaseMediaChunk removed = mediaChunks.removeLast();
Assertions.checkState(removed == currentLoadable); Assertions.checkState(removed == loadable);
sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex()); sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex());
if (mediaChunks.isEmpty()) { if (mediaChunks.isEmpty()) {
pendingResetPositionUs = lastSeekPositionUs; pendingResetPositionUs = lastSeekPositionUs;
} }
} }
clearCurrentLoadable();
eventDispatcher.loadError(e); eventDispatcher.loadError(e);
eventDispatcher.loadCanceled(bytesLoaded); eventDispatcher.loadCanceled(bytesLoaded);
maybeStartLoading(); maybeStartLoading();
...@@ -279,11 +273,6 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -279,11 +273,6 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
private void clearState() { private void clearState() {
sampleQueue.clear(); sampleQueue.clear();
mediaChunks.clear(); mediaChunks.clear();
clearCurrentLoadable();
}
private void clearCurrentLoadable() {
currentLoadable = null;
} }
private void maybeStartLoading() { private void maybeStartLoading() {
...@@ -304,7 +293,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -304,7 +293,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
pendingResetPositionUs != C.UNSET_TIME_US ? pendingResetPositionUs : downstreamPositionUs, pendingResetPositionUs != C.UNSET_TIME_US ? pendingResetPositionUs : downstreamPositionUs,
nextChunkHolder); nextChunkHolder);
boolean endOfStream = nextChunkHolder.endOfStream; boolean endOfStream = nextChunkHolder.endOfStream;
Chunk nextLoadable = nextChunkHolder.chunk; Chunk loadable = nextChunkHolder.chunk;
nextChunkHolder.clear(); nextChunkHolder.clear();
if (endOfStream) { if (endOfStream) {
...@@ -313,24 +302,22 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { ...@@ -313,24 +302,22 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback {
return; return;
} }
if (nextLoadable == null) { if (loadable == null) {
return; return;
} }
currentLoadStartTimeMs = now; if (isMediaChunk(loadable)) {
currentLoadable = nextLoadable;
if (isMediaChunk(currentLoadable)) {
pendingResetPositionUs = C.UNSET_TIME_US; pendingResetPositionUs = C.UNSET_TIME_US;
BaseMediaChunk mediaChunk = (BaseMediaChunk) currentLoadable; BaseMediaChunk mediaChunk = (BaseMediaChunk) loadable;
mediaChunk.init(sampleQueue); mediaChunk.init(sampleQueue);
mediaChunks.add(mediaChunk); mediaChunks.add(mediaChunk);
eventDispatcher.loadStarted(mediaChunk.dataSpec.length, mediaChunk.type, mediaChunk.trigger, eventDispatcher.loadStarted(mediaChunk.dataSpec.length, mediaChunk.type, mediaChunk.trigger,
mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs); mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs);
} else { } else {
eventDispatcher.loadStarted(currentLoadable.dataSpec.length, currentLoadable.type, eventDispatcher.loadStarted(loadable.dataSpec.length, loadable.type, loadable.trigger,
currentLoadable.trigger, currentLoadable.format, -1, -1); loadable.format, -1, -1);
} }
loader.startLoading(currentLoadable, this); loader.startLoading(loadable, this);
// Update the load control again to indicate that we're now loading. // Update the load control again to indicate that we're now loading.
loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), true); loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), true);
} }
......
...@@ -39,6 +39,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -39,6 +39,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
private volatile int bytesLoaded; private volatile int bytesLoaded;
private volatile boolean loadCanceled; private volatile boolean loadCanceled;
private volatile boolean loadCompleted;
/** /**
* @param dataSource A {@link DataSource} for loading the data. * @param dataSource A {@link DataSource} for loading the data.
...@@ -63,6 +64,11 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -63,6 +64,11 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
} }
@Override @Override
public boolean isLoadCompleted() {
return loadCompleted;
}
@Override
public final long bytesLoaded() { public final long bytesLoaded() {
return bytesLoaded; return bytesLoaded;
} }
...@@ -112,6 +118,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -112,6 +118,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
} finally { } finally {
dataSource.close(); dataSource.close();
} }
loadCompleted = true;
} }
} }
...@@ -56,8 +56,16 @@ public abstract class MediaChunk extends Chunk { ...@@ -56,8 +56,16 @@ public abstract class MediaChunk extends Chunk {
this.chunkIndex = chunkIndex; this.chunkIndex = chunkIndex;
} }
/**
* Returns the next chunk index.
*/
public final int getNextChunkIndex() { public final int getNextChunkIndex() {
return chunkIndex + 1; return chunkIndex + 1;
} }
/**
* Returns whether the chunk has been fully loaded.
*/
public abstract boolean isLoadCompleted();
} }
...@@ -35,6 +35,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -35,6 +35,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
private volatile int bytesLoaded; private volatile int bytesLoaded;
private volatile boolean loadCanceled; private volatile boolean loadCanceled;
private volatile boolean loadCompleted;
/** /**
* @param dataSource A {@link DataSource} for loading the data. * @param dataSource A {@link DataSource} for loading the data.
...@@ -53,6 +54,11 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -53,6 +54,11 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
} }
@Override @Override
public boolean isLoadCompleted() {
return loadCompleted;
}
@Override
public long bytesLoaded() { public long bytesLoaded() {
return bytesLoaded; return bytesLoaded;
} }
...@@ -93,6 +99,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -93,6 +99,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
} finally { } finally {
dataSource.close(); dataSource.close();
} }
loadCompleted = true;
} }
} }
...@@ -18,7 +18,6 @@ package com.google.android.exoplayer.dash.mpd; ...@@ -18,7 +18,6 @@ package com.google.android.exoplayer.dash.mpd;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable;
import com.google.android.exoplayer.upstream.UriLoadable; import com.google.android.exoplayer.upstream.UriLoadable;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
...@@ -39,7 +38,7 @@ import java.util.concurrent.CancellationException; ...@@ -39,7 +38,7 @@ import java.util.concurrent.CancellationException;
/** /**
* Resolves a {@link UtcTimingElement}. * Resolves a {@link UtcTimingElement}.
*/ */
public final class UtcTimingElementResolver implements Loader.Callback { public final class UtcTimingElementResolver implements Loader.Callback<UriLoadable<Long>> {
/** /**
* Callback for timing element resolution. * Callback for timing element resolution.
...@@ -70,8 +69,7 @@ public final class UtcTimingElementResolver implements Loader.Callback { ...@@ -70,8 +69,7 @@ public final class UtcTimingElementResolver implements Loader.Callback {
private final long timingElementElapsedRealtime; private final long timingElementElapsedRealtime;
private final UtcTimingCallback callback; private final UtcTimingCallback callback;
private Loader singleUseLoader; private Loader<UriLoadable<Long>> singleUseLoader;
private UriLoadable<Long> singleUseLoadable;
/** /**
* Resolves a {@link UtcTimingElement}. * Resolves a {@link UtcTimingElement}.
...@@ -123,25 +121,26 @@ public final class UtcTimingElementResolver implements Loader.Callback { ...@@ -123,25 +121,26 @@ public final class UtcTimingElementResolver implements Loader.Callback {
} }
private void resolveHttp(UriLoadable.Parser<Long> parser) { private void resolveHttp(UriLoadable.Parser<Long> parser) {
singleUseLoader = new Loader("utctiming", 0); singleUseLoader = new Loader<>("Loader:UtcTiming", 0);
singleUseLoadable = new UriLoadable<>(Uri.parse(timingElement.value), dataSource, parser); singleUseLoader.startLoading(
singleUseLoader.startLoading(singleUseLoadable, this); new UriLoadable<>(Uri.parse(timingElement.value), dataSource, parser), this);
} }
@Override @Override
public void onLoadCanceled(Loadable loadable) { public void onLoadCompleted(UriLoadable<Long> loadable, long elapsedMs) {
onLoadError(loadable, new IOException("Load cancelled", new CancellationException())); releaseLoader();
long elapsedRealtimeOffset = loadable.getResult() - SystemClock.elapsedRealtime();
callback.onTimestampResolved(timingElement, elapsedRealtimeOffset);
} }
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCanceled(UriLoadable<Long> loadable, long elapsedMs) {
releaseLoader(); onLoadError(loadable, elapsedMs,
long elapsedRealtimeOffset = singleUseLoadable.getResult() - SystemClock.elapsedRealtime(); new IOException("Load cancelled", new CancellationException()));
callback.onTimestampResolved(timingElement, elapsedRealtimeOffset);
} }
@Override @Override
public int onLoadError(Loadable loadable, IOException exception) { public int onLoadError(UriLoadable<Long> loadable, long elapsedMs, IOException exception) {
releaseLoader(); releaseLoader();
callback.onTimestampError(timingElement, exception); callback.onTimestampError(timingElement, exception);
return Loader.DONT_RETRY; return Loader.DONT_RETRY;
......
...@@ -67,7 +67,8 @@ import java.util.List; ...@@ -67,7 +67,8 @@ import java.util.List;
* constructor. When reading a new stream, the first {@link Extractor} that returns {@code true} * constructor. When reading a new stream, the first {@link Extractor} that returns {@code true}
* from {@link Extractor#sniff(ExtractorInput)} will be used. * from {@link Extractor#sniff(ExtractorInput)} will be used.
*/ */
public final class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loader.Callback { public final class ExtractorSampleSource implements SampleSource, ExtractorOutput,
Loader.Callback<Loadable> {
/** /**
* Interface definition for a callback to be notified of {@link ExtractorSampleSource} events. * Interface definition for a callback to be notified of {@link ExtractorSampleSource} events.
...@@ -111,7 +112,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -111,7 +112,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
// Lazily initialized default extractor classes in priority order. // Lazily initialized default extractor classes in priority order.
private static List<Class<? extends Extractor>> defaultExtractorClasses; private static List<Class<? extends Extractor>> defaultExtractorClasses;
private final Loader loader; private final Loader<Loadable> loader;
private final ExtractorHolder extractorHolder; private final ExtractorHolder extractorHolder;
private final Allocator allocator; private final Allocator allocator;
private final int requestedBufferSize; private final int requestedBufferSize;
...@@ -139,7 +140,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -139,7 +140,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private long pendingResetPositionUs; private long pendingResetPositionUs;
private ExtractingLoadable loadable; private ExtractingLoadable loadable;
private IOException fatalException;
private int extractedSamplesCountAtStartOfLoad; private int extractedSamplesCountAtStartOfLoad;
private boolean loadingFinished; private boolean loadingFinished;
...@@ -233,7 +233,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -233,7 +233,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
// Assume on-demand until we know otherwise. // Assume on-demand until we know otherwise.
int initialMinRetryCount = minLoadableRetryCount == MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA int initialMinRetryCount = minLoadableRetryCount == MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA
? DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND : minLoadableRetryCount; ? DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND : minLoadableRetryCount;
loader = new Loader("Loader:ExtractorSampleSource", initialMinRetryCount); loader = new Loader<>("Loader:ExtractorSampleSource", initialMinRetryCount);
extractorHolder = new ExtractorHolder(extractors, this); extractorHolder = new ExtractorHolder(extractors, this);
pendingResetPositionUs = C.UNSET_TIME_US; pendingResetPositionUs = C.UNSET_TIME_US;
sampleQueues = new DefaultTrackOutput[0]; sampleQueues = new DefaultTrackOutput[0];
...@@ -476,9 +476,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -476,9 +476,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
} }
/* package */ void maybeThrowError() throws IOException { /* package */ void maybeThrowError() throws IOException {
if (fatalException != null) {
throw fatalException;
}
loader.maybeThrowError(); loader.maybeThrowError();
} }
...@@ -493,12 +490,12 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -493,12 +490,12 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
// Loader.Callback implementation. // Loader.Callback implementation.
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCompleted(Loadable loadable, long elapsedMs) {
loadingFinished = true; loadingFinished = true;
} }
@Override @Override
public void onLoadCanceled(Loadable loadable) { public void onLoadCanceled(Loadable loadable, long elapsedMs) {
if (enabledTrackCount > 0) { if (enabledTrackCount > 0) {
restartFrom(pendingResetPositionUs); restartFrom(pendingResetPositionUs);
} else { } else {
...@@ -508,11 +505,10 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -508,11 +505,10 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
} }
@Override @Override
public int onLoadError(Loadable ignored, IOException e) { public int onLoadError(Loadable loadable, long elapsedMs, IOException e) {
notifyLoadError(e); notifyLoadError(e);
if (isLoadableExceptionFatal(e)) { if (isLoadableExceptionFatal(e)) {
fatalException = e; return Loader.DONT_RETRY_FATAL;
return Loader.DONT_RETRY;
} }
int extractedSamplesCount = getExtractedSamplesCount(); int extractedSamplesCount = getExtractedSamplesCount();
boolean madeProgress = extractedSamplesCount > extractedSamplesCountAtStartOfLoad; boolean madeProgress = extractedSamplesCount > extractedSamplesCountAtStartOfLoad;
...@@ -640,7 +636,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -640,7 +636,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private void clearState() { private void clearState() {
clearSampleQueues(); clearSampleQueues();
loadable = null; loadable = null;
fatalException = null;
} }
private void clearSampleQueues() { private void clearSampleQueues() {
......
...@@ -47,6 +47,7 @@ import java.io.IOException; ...@@ -47,6 +47,7 @@ import java.io.IOException;
private int bytesLoaded; private int bytesLoaded;
private volatile boolean loadCanceled; private volatile boolean loadCanceled;
private volatile boolean loadCompleted;
/** /**
* @param dataSource A {@link DataSource} for loading the data. * @param dataSource A {@link DataSource} for loading the data.
...@@ -95,6 +96,11 @@ import java.io.IOException; ...@@ -95,6 +96,11 @@ import java.io.IOException;
} }
@Override @Override
public boolean isLoadCompleted() {
return loadCompleted;
}
@Override
public long bytesLoaded() { public long bytesLoaded() {
return bytesLoaded; return bytesLoaded;
} }
...@@ -143,6 +149,7 @@ import java.io.IOException; ...@@ -143,6 +149,7 @@ import java.io.IOException;
} finally { } finally {
dataSource.close(); dataSource.close();
} }
loadCompleted = true;
} }
// Private methods // Private methods
......
...@@ -32,7 +32,6 @@ import com.google.android.exoplayer.extractor.DefaultTrackOutput; ...@@ -32,7 +32,6 @@ import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.extractor.ExtractorOutput; import com.google.android.exoplayer.extractor.ExtractorOutput;
import com.google.android.exoplayer.extractor.SeekMap; import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
...@@ -48,7 +47,7 @@ import java.util.List; ...@@ -48,7 +47,7 @@ import java.util.List;
* Loads {@link HlsMediaChunk}s obtained from a {@link HlsChunkSource}, and provides * Loads {@link HlsMediaChunk}s obtained from a {@link HlsChunkSource}, and provides
* {@link TrackStream}s from which the loaded media can be consumed. * {@link TrackStream}s from which the loaded media can be consumed.
*/ */
/* package */ final class HlsTrackStreamWrapper implements Loader.Callback, ExtractorOutput { /* package */ final class HlsTrackStreamWrapper implements Loader.Callback<Chunk>, ExtractorOutput {
/** /**
* The default minimum number of times to retry loading data prior to failing. * The default minimum number of times to retry loading data prior to failing.
...@@ -60,7 +59,7 @@ import java.util.List; ...@@ -60,7 +59,7 @@ import java.util.List;
private static final int PRIMARY_TYPE_AUDIO = 2; private static final int PRIMARY_TYPE_AUDIO = 2;
private static final int PRIMARY_TYPE_VIDEO = 3; private static final int PRIMARY_TYPE_VIDEO = 3;
private final Loader loader; private final Loader<Chunk> loader;
private final HlsChunkSource chunkSource; private final HlsChunkSource chunkSource;
private final SparseArray<DefaultTrackOutput> sampleQueues; private final SparseArray<DefaultTrackOutput> sampleQueues;
private final LinkedList<HlsMediaChunk> mediaChunks; private final LinkedList<HlsMediaChunk> mediaChunks;
...@@ -88,8 +87,6 @@ import java.util.List; ...@@ -88,8 +87,6 @@ import java.util.List;
private long lastSeekPositionUs; private long lastSeekPositionUs;
private long pendingResetPositionUs; private long pendingResetPositionUs;
private Chunk currentLoadable;
private long currentLoadStartTimeMs;
private boolean loadingFinished; private boolean loadingFinished;
/** /**
...@@ -135,7 +132,7 @@ import java.util.List; ...@@ -135,7 +132,7 @@ import java.util.List;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.loadControl = loadControl; this.loadControl = loadControl;
this.bufferSizeContribution = bufferSizeContribution; this.bufferSizeContribution = bufferSizeContribution;
loader = new Loader("Loader:HLS", minLoadableRetryCount); loader = new Loader<>("Loader:HLS", minLoadableRetryCount);
eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId); eventDispatcher = new EventDispatcher(eventHandler, eventListener, eventSourceId);
nextChunkHolder = new ChunkHolder(); nextChunkHolder = new ChunkHolder();
sampleQueues = new SparseArray<>(); sampleQueues = new SparseArray<>();
...@@ -264,7 +261,7 @@ import java.util.List; ...@@ -264,7 +261,7 @@ import java.util.List;
} else { } else {
long bufferedPositionUs = downstreamPositionUs; long bufferedPositionUs = downstreamPositionUs;
HlsMediaChunk lastMediaChunk = mediaChunks.getLast(); HlsMediaChunk lastMediaChunk = mediaChunks.getLast();
HlsMediaChunk lastCompletedMediaChunk = lastMediaChunk != currentLoadable ? lastMediaChunk HlsMediaChunk lastCompletedMediaChunk = lastMediaChunk.isLoadCompleted() ? lastMediaChunk
: mediaChunks.size() > 1 ? mediaChunks.get(mediaChunks.size() - 2) : null; : mediaChunks.size() > 1 ? mediaChunks.get(mediaChunks.size() - 2) : null;
if (lastCompletedMediaChunk != null) { if (lastCompletedMediaChunk != null) {
bufferedPositionUs = Math.max(bufferedPositionUs, lastCompletedMediaChunk.endTimeUs); bufferedPositionUs = Math.max(bufferedPositionUs, lastCompletedMediaChunk.endTimeUs);
...@@ -324,26 +321,23 @@ import java.util.List; ...@@ -324,26 +321,23 @@ import java.util.List;
// Loader.Callback implementation. // Loader.Callback implementation.
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCompleted(Chunk loadable, long elapsedMs) {
long now = SystemClock.elapsedRealtime(); long now = SystemClock.elapsedRealtime();
long loadDurationMs = now - currentLoadStartTimeMs; chunkSource.onChunkLoadCompleted(loadable);
chunkSource.onChunkLoadCompleted(currentLoadable); if (isMediaChunk(loadable)) {
if (isMediaChunk(currentLoadable)) { HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable;
HlsMediaChunk mediaChunk = (HlsMediaChunk) currentLoadable; eventDispatcher.loadCompleted(loadable.bytesLoaded(), mediaChunk.type, mediaChunk.trigger,
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), mediaChunk.type, mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs, now, elapsedMs);
mediaChunk.trigger, mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs, now,
loadDurationMs);
} else { } else {
eventDispatcher.loadCompleted(currentLoadable.bytesLoaded(), currentLoadable.type, eventDispatcher.loadCompleted(loadable.bytesLoaded(), loadable.type, loadable.trigger,
currentLoadable.trigger, currentLoadable.format, -1, -1, now, loadDurationMs); loadable.format, -1, -1, now, elapsedMs);
} }
clearCurrentLoadable();
maybeStartLoading(); maybeStartLoading();
} }
@Override @Override
public void onLoadCanceled(Loadable loadable) { public void onLoadCanceled(Chunk loadable, long elapsedMs) {
eventDispatcher.loadCanceled(currentLoadable.bytesLoaded()); eventDispatcher.loadCanceled(loadable.bytesLoaded());
if (enabledTrackCount > 0) { if (enabledTrackCount > 0) {
restartFrom(pendingResetPositionUs); restartFrom(pendingResetPositionUs);
} else { } else {
...@@ -353,19 +347,18 @@ import java.util.List; ...@@ -353,19 +347,18 @@ import java.util.List;
} }
@Override @Override
public int onLoadError(Loadable loadable, IOException e) { public int onLoadError(Chunk loadable, long elapsedMs, IOException e) {
long bytesLoaded = currentLoadable.bytesLoaded(); long bytesLoaded = loadable.bytesLoaded();
boolean isMediaChunk = isMediaChunk(currentLoadable); boolean isMediaChunk = isMediaChunk(loadable);
boolean cancelable = !isMediaChunk || bytesLoaded == 0; boolean cancelable = !isMediaChunk || bytesLoaded == 0;
if (chunkSource.onChunkLoadError(currentLoadable, cancelable, e)) { if (chunkSource.onChunkLoadError(loadable, cancelable, e)) {
if (isMediaChunk) { if (isMediaChunk) {
HlsMediaChunk removed = mediaChunks.removeLast(); HlsMediaChunk removed = mediaChunks.removeLast();
Assertions.checkState(removed == currentLoadable); Assertions.checkState(removed == loadable);
if (mediaChunks.isEmpty()) { if (mediaChunks.isEmpty()) {
pendingResetPositionUs = lastSeekPositionUs; pendingResetPositionUs = lastSeekPositionUs;
} }
} }
clearCurrentLoadable();
eventDispatcher.loadError(e); eventDispatcher.loadError(e);
eventDispatcher.loadCanceled(bytesLoaded); eventDispatcher.loadCanceled(bytesLoaded);
maybeStartLoading(); maybeStartLoading();
...@@ -590,11 +583,6 @@ import java.util.List; ...@@ -590,11 +583,6 @@ import java.util.List;
sampleQueues.valueAt(i).clear(); sampleQueues.valueAt(i).clear();
} }
mediaChunks.clear(); mediaChunks.clear();
clearCurrentLoadable();
}
private void clearCurrentLoadable() {
currentLoadable = null;
} }
private void maybeStartLoading() { private void maybeStartLoading() {
...@@ -608,7 +596,7 @@ import java.util.List; ...@@ -608,7 +596,7 @@ import java.util.List;
pendingResetPositionUs != C.UNSET_TIME_US ? pendingResetPositionUs : downstreamPositionUs, pendingResetPositionUs != C.UNSET_TIME_US ? pendingResetPositionUs : downstreamPositionUs,
nextChunkHolder); nextChunkHolder);
boolean endOfStream = nextChunkHolder.endOfStream; boolean endOfStream = nextChunkHolder.endOfStream;
Chunk nextLoadable = nextChunkHolder.chunk; Chunk loadable = nextChunkHolder.chunk;
nextChunkHolder.clear(); nextChunkHolder.clear();
if (endOfStream) { if (endOfStream) {
...@@ -619,24 +607,22 @@ import java.util.List; ...@@ -619,24 +607,22 @@ import java.util.List;
return; return;
} }
if (nextLoadable == null) { if (loadable == null) {
return; return;
} }
currentLoadStartTimeMs = SystemClock.elapsedRealtime(); if (isMediaChunk(loadable)) {
currentLoadable = nextLoadable;
if (isMediaChunk(currentLoadable)) {
pendingResetPositionUs = C.UNSET_TIME_US; pendingResetPositionUs = C.UNSET_TIME_US;
HlsMediaChunk mediaChunk = (HlsMediaChunk) currentLoadable; HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable;
mediaChunk.init(this); mediaChunk.init(this);
mediaChunks.addLast(mediaChunk); mediaChunks.addLast(mediaChunk);
eventDispatcher.loadStarted(mediaChunk.dataSpec.length, mediaChunk.type, mediaChunk.trigger, eventDispatcher.loadStarted(mediaChunk.dataSpec.length, mediaChunk.type, mediaChunk.trigger,
mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs); mediaChunk.format, mediaChunk.startTimeUs, mediaChunk.endTimeUs);
} else { } else {
eventDispatcher.loadStarted(currentLoadable.dataSpec.length, currentLoadable.type, eventDispatcher.loadStarted(loadable.dataSpec.length, loadable.type, loadable.trigger,
currentLoadable.trigger, currentLoadable.format, -1, -1); loadable.format, -1, -1);
} }
loader.startLoading(currentLoadable, this); loader.startLoading(loadable, this);
if (prepared) { if (prepared) {
// Update the load control again to indicate that we're now loading. // Update the load control again to indicate that we're now loading.
loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), true); loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), true);
......
...@@ -23,6 +23,7 @@ import android.annotation.SuppressLint; ...@@ -23,6 +23,7 @@ import android.annotation.SuppressLint;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import java.io.IOException; import java.io.IOException;
...@@ -31,7 +32,7 @@ import java.util.concurrent.ExecutorService; ...@@ -31,7 +32,7 @@ import java.util.concurrent.ExecutorService;
/** /**
* Manages the background loading of {@link Loadable}s. * Manages the background loading of {@link Loadable}s.
*/ */
public final class Loader { public final class Loader<T extends Loader.Loadable> {
/** /**
* Thrown when an unexpected exception is encountered during loading. * Thrown when an unexpected exception is encountered during loading.
...@@ -74,37 +75,42 @@ public final class Loader { ...@@ -74,37 +75,42 @@ public final class Loader {
/** /**
* Interface definition for a callback to be notified of {@link Loader} events. * Interface definition for a callback to be notified of {@link Loader} events.
*/ */
public interface Callback { public interface Callback<T extends Loadable> {
/** /**
* Invoked when a load has been canceled. * Invoked when a load has been canceled.
* *
* @param loadable The loadable whose load has been canceled. * @param loadable The loadable whose load has been canceled.
* @param elapsedMs The elapsed time in milliseconds since loading started.
*/ */
void onLoadCanceled(Loadable loadable); void onLoadCanceled(T loadable, long elapsedMs);
/** /**
* Invoked when a load has completed. * Invoked when a load has completed.
* *
* @param loadable The loadable whose load has completed. * @param loadable The loadable whose load has completed.
* @param elapsedMs The elapsed time in milliseconds since loading started.
*/ */
void onLoadCompleted(Loadable loadable); void onLoadCompleted(T loadable, long elapsedMs);
/** /**
* Invoked when a load encounters an error. * Invoked when a load encounters an error.
* *
* @param loadable The loadable whose load has encountered an error. * @param loadable The loadable whose load has encountered an error.
* @param elapsedMs The elapsed time in milliseconds since loading started.
* @param exception The error. * @param exception The error.
* @return The desired retry action. One of {@link Loader#DONT_RETRY}, {@link Loader#RETRY} and * @return The desired retry action. One of {@link Loader#RETRY},
* {@link Loader#RETRY_RESET_ERROR_COUNT}. * {@link Loader#RETRY_RESET_ERROR_COUNT}, {@link Loader#DONT_RETRY} and
* {@link Loader#DONT_RETRY_FATAL}.
*/ */
int onLoadError(Loadable loadable, IOException exception); int onLoadError(T loadable, long elapsedMs, IOException exception);
} }
public static final int DONT_RETRY = 0; public static final int RETRY = 0;
public static final int RETRY = 1; public static final int RETRY_RESET_ERROR_COUNT = 1;
public static final int RETRY_RESET_ERROR_COUNT = 2; public static final int DONT_RETRY = 2;
public static final int DONT_RETRY_FATAL = 3;
private static final int MSG_START = 0; private static final int MSG_START = 0;
private static final int MSG_CANCEL = 1; private static final int MSG_CANCEL = 1;
...@@ -116,6 +122,7 @@ public final class Loader { ...@@ -116,6 +122,7 @@ public final class Loader {
private int minRetryCount; private int minRetryCount;
private LoadTask currentTask; private LoadTask currentTask;
private IOException fatalError;
/** /**
* @param threadName A name for the loader's thread. * @param threadName A name for the loader's thread.
...@@ -136,7 +143,7 @@ public final class Loader { ...@@ -136,7 +143,7 @@ public final class Loader {
* @param callback A callback to invoke when the load ends. * @param callback A callback to invoke when the load ends.
* @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}.
*/ */
public void startLoading(Loadable loadable, Callback callback) { public void startLoading(T loadable, Callback<T> callback) {
Looper looper = Looper.myLooper(); Looper looper = Looper.myLooper();
Assertions.checkState(looper != null); Assertions.checkState(looper != null);
new LoadTask(looper, loadable, callback).start(0); new LoadTask(looper, loadable, callback).start(0);
...@@ -161,14 +168,16 @@ public final class Loader { ...@@ -161,14 +168,16 @@ public final class Loader {
} }
/** /**
* If the current {@link Loadable} has incurred a number of errors greater than the minimum * If a fatal error has been encountered, or if the current {@link Loadable} has incurred a number
* number of retries and if the load is currently backed off, then the most recent error is * of errors greater than the minimum number of retries and if the load is currently backed off,
* thrown. Else does nothing. * then an error is thrown. Else does nothing.
* *
* @throws IOException The most recent error encountered by the current {@link Loadable}. * @throws IOException The error.
*/ */
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
if (currentTask != null) { if (fatalError != null) {
throw fatalError;
} else if (currentTask != null) {
currentTask.maybeThrowError(minRetryCount); currentTask.maybeThrowError(minRetryCount);
} }
} }
...@@ -199,18 +208,20 @@ public final class Loader { ...@@ -199,18 +208,20 @@ public final class Loader {
private static final String TAG = "LoadTask"; private static final String TAG = "LoadTask";
private final Loadable loadable; private final T loadable;
private final Loader.Callback callback; private final Loader.Callback<T> callback;
private final long startTimeMs;
private IOException currentError; private IOException currentError;
private int errorCount; private int errorCount;
private volatile Thread executorThread; private volatile Thread executorThread;
public LoadTask(Looper looper, Loadable loadable, Loader.Callback callback) { public LoadTask(Looper looper, T loadable, Loader.Callback<T> callback) {
super(looper); super(looper);
this.loadable = loadable; this.loadable = loadable;
this.callback = callback; this.callback = callback;
this.startTimeMs = SystemClock.elapsedRealtime();
} }
public void maybeThrowError(int minRetryCount) throws IOException { public void maybeThrowError(int minRetryCount) throws IOException {
...@@ -285,21 +296,24 @@ public final class Loader { ...@@ -285,21 +296,24 @@ public final class Loader {
throw (Error) msg.obj; throw (Error) msg.obj;
} }
finish(); finish();
long elapsedMs = SystemClock.elapsedRealtime() - startTimeMs;
if (loadable.isLoadCanceled()) { if (loadable.isLoadCanceled()) {
callback.onLoadCanceled(loadable); callback.onLoadCanceled(loadable, elapsedMs);
return; return;
} }
switch (msg.what) { switch (msg.what) {
case MSG_CANCEL: case MSG_CANCEL:
callback.onLoadCanceled(loadable); callback.onLoadCanceled(loadable, elapsedMs);
break; break;
case MSG_END_OF_SOURCE: case MSG_END_OF_SOURCE:
callback.onLoadCompleted(loadable); callback.onLoadCompleted(loadable, elapsedMs);
break; break;
case MSG_IO_EXCEPTION: case MSG_IO_EXCEPTION:
currentError = (IOException) msg.obj; currentError = (IOException) msg.obj;
int retryAction = callback.onLoadError(loadable, currentError); int retryAction = callback.onLoadError(loadable, elapsedMs, currentError);
if (retryAction != DONT_RETRY) { if (retryAction == DONT_RETRY_FATAL) {
fatalError = currentError;
} else if (retryAction != DONT_RETRY) {
errorCount = retryAction == RETRY_RESET_ERROR_COUNT ? 1 : errorCount + 1; errorCount = retryAction == RETRY_RESET_ERROR_COUNT ? 1 : errorCount + 1;
start(getRetryDelayMillis()); start(getRetryDelayMillis());
} }
......
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer.util; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer.util;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable;
import com.google.android.exoplayer.upstream.UriLoadable; import com.google.android.exoplayer.upstream.UriLoadable;
import android.net.Uri; import android.net.Uri;
...@@ -32,7 +31,7 @@ import java.io.IOException; ...@@ -32,7 +31,7 @@ import java.io.IOException;
* *
* @param <T> The type of manifest. * @param <T> The type of manifest.
*/ */
public class ManifestFetcher<T> implements Loader.Callback { public class ManifestFetcher<T> implements Loader.Callback<UriLoadable<T>> {
/** /**
* Thrown when an error occurs trying to fetch a manifest. * Thrown when an error occurs trying to fetch a manifest.
...@@ -69,7 +68,7 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -69,7 +68,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
} }
private final Loader loader; private final Loader<UriLoadable<T>> loader;
private final UriLoadable.Parser<T> parser; private final UriLoadable.Parser<T> parser;
private final DataSource dataSource; private final DataSource dataSource;
private final Handler eventHandler; private final Handler eventHandler;
...@@ -77,7 +76,6 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -77,7 +76,6 @@ public class ManifestFetcher<T> implements Loader.Callback {
private volatile Uri manifestUri; private volatile Uri manifestUri;
private UriLoadable<T> currentLoadable;
private long currentLoadStartTimestamp; private long currentLoadStartTimestamp;
private volatile T manifest; private volatile T manifest;
...@@ -108,7 +106,7 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -108,7 +106,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
this.dataSource = dataSource; this.dataSource = dataSource;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
loader = new Loader("Loader:ManifestFetcher", 1); loader = new Loader<>("Loader:ManifestFetcher", 1);
} }
/** /**
...@@ -174,9 +172,8 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -174,9 +172,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
if (loader.isLoading()) { if (loader.isLoading()) {
return; return;
} }
currentLoadable = new UriLoadable<>(manifestUri, dataSource, parser);
currentLoadStartTimestamp = SystemClock.elapsedRealtime(); currentLoadStartTimestamp = SystemClock.elapsedRealtime();
loader.startLoading(currentLoadable, this); loader.startLoading(new UriLoadable<>(manifestUri, dataSource, parser), this);
notifyManifestRefreshStarted(); notifyManifestRefreshStarted();
} }
...@@ -192,8 +189,8 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -192,8 +189,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
// Loadable.Callback implementation. // Loadable.Callback implementation.
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCompleted(UriLoadable<T> loadable, long elapsedMs) {
manifest = currentLoadable.getResult(); manifest = loadable.getResult();
manifestLoadStartTimestamp = currentLoadStartTimestamp; manifestLoadStartTimestamp = currentLoadStartTimestamp;
manifestLoadCompleteTimestamp = SystemClock.elapsedRealtime(); manifestLoadCompleteTimestamp = SystemClock.elapsedRealtime();
if (manifest instanceof RedirectingManifest) { if (manifest instanceof RedirectingManifest) {
...@@ -207,12 +204,12 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -207,12 +204,12 @@ public class ManifestFetcher<T> implements Loader.Callback {
} }
@Override @Override
public void onLoadCanceled(Loadable loadable) { public void onLoadCanceled(UriLoadable<T> loadable, long elapsedMs) {
// Do nothing. // Do nothing.
} }
@Override @Override
public int onLoadError(Loadable loadable, IOException exception) { public int onLoadError(UriLoadable<T> loadable, long elapsedMs, IOException exception) {
notifyManifestError(new ManifestIOException(exception)); notifyManifestError(new ManifestIOException(exception));
return Loader.RETRY; return Loader.RETRY;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment