diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/LoadEventInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/source/LoadEventInfo.java index ae61cf45b5..cfef4eee68 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/LoadEventInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/LoadEventInfo.java @@ -20,10 +20,19 @@ import android.os.SystemClock; import com.google.android.exoplayer2.upstream.DataSpec; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; /** {@link MediaSource} load event information. */ public final class LoadEventInfo { + /** Used for the generation of unique ids. */ + private static final AtomicLong idSource = new AtomicLong(); + + /** Returns an non-negative identifier which is unique to the JVM instance. */ + public static long getNewId() { + return idSource.getAndIncrement(); + } + /** Defines the requested data. */ public final DataSpec dataSpec; /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java index 598fc32a3b..733566bb26 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java @@ -543,8 +543,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Loader.Callback implementation. @Override - public void onLoadCompleted(ExtractingLoadable loadable, long elapsedRealtimeMs, - long loadDurationMs) { + public void onLoadCompleted( + ExtractingLoadable loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs) { if (durationUs == C.TIME_UNSET && seekMap != null) { boolean isSeekable = seekMap.isSeekable(); long largestQueuedTimestampUs = getLargestQueuedTimestampUs(); @@ -572,8 +572,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } @Override - public void onLoadCanceled(ExtractingLoadable loadable, long elapsedRealtimeMs, - long loadDurationMs, boolean released) { + public void onLoadCanceled( + ExtractingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + boolean released) { eventDispatcher.loadCanceled( loadable.dataSpec, loadable.dataSource.getLastOpenedUri(), @@ -602,6 +606,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public LoadErrorAction onLoadError( ExtractingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java index 3e8ad19548..dff61cbe79 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java @@ -212,8 +212,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Loader.Callback implementation. @Override - public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs, - long loadDurationMs) { + public void onLoadCompleted( + SourceLoadable loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs) { sampleSize = (int) loadable.dataSource.getBytesRead(); sampleData = Assertions.checkNotNull(loadable.sampleData); loadingFinished = true; @@ -234,7 +234,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } @Override - public void onLoadCanceled(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, + public void onLoadCanceled( + SourceLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, boolean released) { eventDispatcher.loadCanceled( loadable.dataSpec, @@ -255,6 +259,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public LoadErrorAction onLoadError( SourceLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java index 19804450ff..50e5f595f6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java @@ -393,7 +393,8 @@ public class ChunkSampleStream implements SampleStream, S // Loader.Callback implementation. @Override - public void onLoadCompleted(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs) { + public void onLoadCompleted( + Chunk loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs) { chunkSource.onChunkLoadCompleted(loadable); eventDispatcher.loadCompleted( loadable.dataSpec, @@ -413,7 +414,11 @@ public class ChunkSampleStream implements SampleStream, S } @Override - public void onLoadCanceled(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, + public void onLoadCanceled( + Chunk loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, boolean released) { eventDispatcher.loadCanceled( loadable.dataSpec, @@ -441,6 +446,7 @@ public class ChunkSampleStream implements SampleStream, S @Override public LoadErrorAction onLoadError( Chunk loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java index 4ff58b108c..6f4b91635e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java @@ -23,6 +23,7 @@ import android.os.SystemClock; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.TraceUtil; @@ -79,11 +80,12 @@ public final class Loader implements LoaderErrorThrower { * and this callback being called. * * @param loadable The loadable whose load has completed. + * @param loadTaskId A non-negative identifier for the load task. Remains associated to the load + * task after retries until completion or cancellation. * @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the load ended. * @param loadDurationMs The duration in milliseconds of the load since {@link #startLoading} - * was called. */ - void onLoadCompleted(T loadable, long elapsedRealtimeMs, long loadDurationMs); + void onLoadCompleted(T loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs); /** * Called when a load has been canceled. @@ -94,13 +96,15 @@ public final class Loader implements LoaderErrorThrower { * exits. * * @param loadable The loadable whose load has been canceled. + * @param loadTaskId A non-negative identifier for the load task. Remains associated to the load + * task after retries until completion or cancellation. * @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the load was canceled. * @param loadDurationMs The duration in milliseconds of the load since {@link #startLoading} * was called up to the point at which it was canceled. * @param released True if the load was canceled because the {@link Loader} was released. False - * otherwise. */ - void onLoadCanceled(T loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released); + void onLoadCanceled( + T loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, boolean released); /** * Called when a load encounters an error. @@ -109,6 +113,8 @@ public final class Loader implements LoaderErrorThrower { * and this callback being called. * * @param loadable The loadable whose load has encountered an error. + * @param loadTaskId A non-negative identifier for the load task. Remains associated to the load + * task after retries until completion or cancellation. * @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the error occurred. * @param loadDurationMs The duration in milliseconds of the load since {@link #startLoading} * was called up to the point at which the error occurred. @@ -119,7 +125,12 @@ public final class Loader implements LoaderErrorThrower { * Loader#DONT_RETRY_FATAL} or a retry action created by {@link #createRetryAction}. */ LoadErrorAction onLoadError( - T loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error, int errorCount); + T loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + IOException error, + int errorCount); } /** @@ -166,10 +177,7 @@ public final class Loader implements LoaderErrorThrower { public static final LoadErrorAction DONT_RETRY_FATAL = new LoadErrorAction(ACTION_TYPE_DONT_RETRY_FATAL, C.TIME_UNSET); - /** - * Action that can be taken in response to {@link Callback#onLoadError(Loadable, long, long, - * IOException, int)}. - */ + /** Action that can be taken in response to {@link Callback#onLoadError}. */ public static final class LoadErrorAction { private final @RetryActionType int type; @@ -315,6 +323,7 @@ public final class Loader implements LoaderErrorThrower { public final int defaultMinRetryCount; private final T loadable; + private final long id; private final long startTimeMs; @Nullable private Loader.Callback callback; @@ -328,6 +337,7 @@ public final class Loader implements LoaderErrorThrower { public LoadTask(Looper looper, T loadable, Loader.Callback callback, int defaultMinRetryCount, long startTimeMs) { super(looper); + id = LoadEventInfo.getNewId(); this.loadable = loadable; this.callback = callback; this.defaultMinRetryCount = defaultMinRetryCount; @@ -370,7 +380,7 @@ public final class Loader implements LoaderErrorThrower { finish(); long nowMs = SystemClock.elapsedRealtime(); Assertions.checkNotNull(callback) - .onLoadCanceled(loadable, nowMs, nowMs - startTimeMs, true); + .onLoadCanceled(loadable, id, nowMs, nowMs - startTimeMs, true); // If loading, this task will be referenced from a GC root (the loading thread) until // cancellation completes. The time taken for cancellation to complete depends on the // implementation of the Loadable that the task is loading. We null the callback reference @@ -441,16 +451,16 @@ public final class Loader implements LoaderErrorThrower { long durationMs = nowMs - startTimeMs; Loader.Callback callback = Assertions.checkNotNull(this.callback); if (canceled) { - callback.onLoadCanceled(loadable, nowMs, durationMs, false); + callback.onLoadCanceled(loadable, id, nowMs, durationMs, false); return; } switch (msg.what) { case MSG_CANCEL: - callback.onLoadCanceled(loadable, nowMs, durationMs, false); + callback.onLoadCanceled(loadable, id, nowMs, durationMs, false); break; case MSG_END_OF_SOURCE: try { - callback.onLoadCompleted(loadable, nowMs, durationMs); + callback.onLoadCompleted(loadable, id, nowMs, durationMs); } catch (RuntimeException e) { // This should never happen, but handle it anyway. Log.e(TAG, "Unexpected exception handling load completed", e); @@ -461,7 +471,7 @@ public final class Loader implements LoaderErrorThrower { currentError = (IOException) msg.obj; errorCount++; LoadErrorAction action = - callback.onLoadError(loadable, nowMs, durationMs, currentError, errorCount); + callback.onLoadError(loadable, id, nowMs, durationMs, currentError, errorCount); if (action.type == ACTION_TYPE_DONT_RETRY_FATAL) { fatalError = currentError; } else if (action.type != ACTION_TYPE_DONT_RETRY) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/SntpClient.java b/library/core/src/main/java/com/google/android/exoplayer2/util/SntpClient.java index fa2edf253d..b68e59ee0a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/SntpClient.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/SntpClient.java @@ -281,7 +281,8 @@ public final class SntpClient { } @Override - public void onLoadCompleted(Loadable loadable, long elapsedRealtimeMs, long loadDurationMs) { + public void onLoadCompleted( + Loadable loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs) { Assertions.checkState(SntpClient.isInitialized()); if (callback != null) { callback.onInitialized(); @@ -290,13 +291,18 @@ public final class SntpClient { @Override public void onLoadCanceled( - Loadable loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) { + Loadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + boolean released) { // Ignore. } @Override public LoadErrorAction onLoadError( Loadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index c2d61dd800..c399175bb8 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -73,6 +73,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.Allocation; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Loader; +import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Clock; @@ -6363,15 +6364,20 @@ public final class ExoPlayerTest { private static final class DummyLoaderCallback implements Loader.Callback { @Override public void onLoadCompleted( - Loader.Loadable loadable, long elapsedRealtimeMs, long loadDurationMs) {} + Loadable loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs) {} @Override public void onLoadCanceled( - Loader.Loadable loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) {} + Loadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + boolean released) {} @Override public Loader.LoadErrorAction onLoadError( - Loader.Loadable loadable, + Loadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 13be5b6abd..667a98d3e7 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -1347,20 +1347,28 @@ public final class DashMediaSource extends BaseMediaSource { private final class ManifestCallback implements Loader.Callback> { @Override - public void onLoadCompleted(ParsingLoadable loadable, - long elapsedRealtimeMs, long loadDurationMs) { + public void onLoadCompleted( + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs) { onManifestLoadCompleted(loadable, elapsedRealtimeMs, loadDurationMs); } @Override - public void onLoadCanceled(ParsingLoadable loadable, - long elapsedRealtimeMs, long loadDurationMs, boolean released) { + public void onLoadCanceled( + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + boolean released) { DashMediaSource.this.onLoadCanceled(loadable, elapsedRealtimeMs, loadDurationMs); } @Override public LoadErrorAction onLoadError( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, @@ -1373,20 +1381,28 @@ public final class DashMediaSource extends BaseMediaSource { private final class UtcTimestampCallback implements Loader.Callback> { @Override - public void onLoadCompleted(ParsingLoadable loadable, long elapsedRealtimeMs, + public void onLoadCompleted( + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, long loadDurationMs) { onUtcTimestampLoadCompleted(loadable, elapsedRealtimeMs, loadDurationMs); } @Override - public void onLoadCanceled(ParsingLoadable loadable, long elapsedRealtimeMs, - long loadDurationMs, boolean released) { + public void onLoadCanceled( + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + boolean released) { DashMediaSource.this.onLoadCanceled(loadable, elapsedRealtimeMs, loadDurationMs); } @Override public LoadErrorAction onLoadError( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 9a69cbcb18..29d492df35 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -703,7 +703,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // Loader.Callback implementation. @Override - public void onLoadCompleted(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs) { + public void onLoadCompleted( + Chunk loadable, long loadTaskId, long elapsedRealtimeMs, long loadDurationMs) { chunkSource.onChunkLoadCompleted(loadable); eventDispatcher.loadCompleted( loadable.dataSpec, @@ -727,7 +728,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } @Override - public void onLoadCanceled(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, + public void onLoadCanceled( + Chunk loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, boolean released) { eventDispatcher.loadCanceled( loadable.dataSpec, @@ -754,6 +759,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; @Override public LoadErrorAction onLoadError( Chunk loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java index e624027d75..33f98188e8 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java @@ -221,7 +221,10 @@ public final class DefaultHlsPlaylistTracker @Override public void onLoadCompleted( - ParsingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs) { HlsPlaylist result = loadable.getResult(); HlsMasterPlaylist masterPlaylist; boolean isMediaPlaylist = result instanceof HlsMediaPlaylist; @@ -254,6 +257,7 @@ public final class DefaultHlsPlaylistTracker @Override public void onLoadCanceled( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, boolean released) { @@ -270,6 +274,7 @@ public final class DefaultHlsPlaylistTracker @Override public LoadErrorAction onLoadError( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, @@ -516,7 +521,10 @@ public final class DefaultHlsPlaylistTracker @Override public void onLoadCompleted( - ParsingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs) { HlsPlaylist result = loadable.getResult(); if (result instanceof HlsMediaPlaylist) { processLoadedPlaylist((HlsMediaPlaylist) result, loadDurationMs); @@ -536,6 +544,7 @@ public final class DefaultHlsPlaylistTracker @Override public void onLoadCanceled( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, boolean released) { @@ -552,6 +561,7 @@ public final class DefaultHlsPlaylistTracker @Override public LoadErrorAction onLoadError( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error, diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index 899000380c..127625cfed 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -618,7 +618,10 @@ public final class SsMediaSource extends BaseMediaSource // Loader.Callback implementation @Override - public void onLoadCompleted(ParsingLoadable loadable, long elapsedRealtimeMs, + public void onLoadCompleted( + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, long loadDurationMs) { manifestEventDispatcher.loadCompleted( loadable.dataSpec, @@ -635,8 +638,12 @@ public final class SsMediaSource extends BaseMediaSource } @Override - public void onLoadCanceled(ParsingLoadable loadable, long elapsedRealtimeMs, - long loadDurationMs, boolean released) { + public void onLoadCanceled( + ParsingLoadable loadable, + long loadTaskId, + long elapsedRealtimeMs, + long loadDurationMs, + boolean released) { manifestEventDispatcher.loadCanceled( loadable.dataSpec, loadable.getUri(), @@ -650,6 +657,7 @@ public final class SsMediaSource extends BaseMediaSource @Override public LoadErrorAction onLoadError( ParsingLoadable loadable, + long loadTaskId, long elapsedRealtimeMs, long loadDurationMs, IOException error,