diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 33056df142..55c7a50d4c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,6 +11,8 @@ `TrackSelectionParameters.Builder.setPreferredVideoLanguage(s)`. * Add `selectedAudioLanguage` parameter to `DefaultTrackSelector.selectVideoTrack()` method. + * Add `retryCount` parameter to `MediaSourceEventListener.onLoadStarted` + and corresponding `MediaSourceEventListener.EventDispatcher` methods. * Transformer: * Update parameters of `VideoFrameProcessor.registerInputStream` and `VideoFrameProcessor.Listener.onInputStreamRegistered` to use `Format`. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaSourceList.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaSourceList.java index 64c20835ab..0d534b1fe7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaSourceList.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaSourceList.java @@ -564,7 +564,8 @@ import java.util.Set; int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, LoadEventInfo loadEventData, - MediaLoadData mediaLoadData) { + MediaLoadData mediaLoadData, + int retryCount) { @Nullable Pair eventParameters = getEventParameters(windowIndex, mediaPeriodId); @@ -572,7 +573,11 @@ import java.util.Set; eventHandler.post( () -> eventListener.onLoadStarted( - eventParameters.first, eventParameters.second, loadEventData, mediaLoadData)); + eventParameters.first, + eventParameters.second, + loadEventData, + mediaLoadData, + retryCount)); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 4418a683a6..e885057918 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -843,16 +843,30 @@ public interface AnalyticsListener { @UnstableApi default void onPlaylistMetadataChanged(EventTime eventTime, MediaMetadata playlistMetadata) {} + /** + * @deprecated Implement {@link #onLoadStarted(EventTime, LoadEventInfo, MediaLoadData, int)} + * instead, and check for {@code retryCount == 0} for equivalent behavior. + */ + @UnstableApi + @Deprecated + default void onLoadStarted( + EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {} + /** * Called when a media source started loading data. * * @param eventTime The event time. * @param loadEventInfo The {@link LoadEventInfo} defining the load event. * @param mediaLoadData The {@link MediaLoadData} defining the data being loaded. + * @param retryCount The number of failed attempts since {@link #onLoadStarted} was called (this + * is zero for the first load attempt). */ @UnstableApi default void onLoadStarted( - EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {} + EventTime eventTime, + LoadEventInfo loadEventInfo, + MediaLoadData mediaLoadData, + int retryCount) {} /** * Called when a media source completed loading data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java index e9c2b7b9a5..c12e798f54 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java @@ -397,17 +397,23 @@ public class DefaultAnalyticsCollector implements AnalyticsCollector { // MediaSourceEventListener implementation. + // Deliberately calling deprecated listener method for backwards compatibility. + @SuppressWarnings("deprecation") @Override public final void onLoadStarted( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { + MediaLoadData mediaLoadData, + int retryCount) { EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); sendEvent( eventTime, AnalyticsListener.EVENT_LOAD_STARTED, - listener -> listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData)); + listener -> { + listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData); + listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData, retryCount); + }); } @Override diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/CompositeMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/CompositeMediaSource.java index 3b58c177ee..dcbe3aa436 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/CompositeMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/CompositeMediaSource.java @@ -239,10 +239,11 @@ public abstract class CompositeMediaSource extends BaseMediaSource { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, LoadEventInfo loadEventData, - MediaLoadData mediaLoadData) { + MediaLoadData mediaLoadData, + int retryCount) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { mediaSourceEventDispatcher.loadStarted( - loadEventData, maybeUpdateMediaLoadData(mediaLoadData, mediaPeriodId)); + loadEventData, maybeUpdateMediaLoadData(mediaLoadData, mediaPeriodId), retryCount); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceEventListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceEventListener.java index 440625ed19..4a15005e0a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceEventListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceEventListener.java @@ -46,12 +46,15 @@ public interface MediaSourceEventListener { * LoadEventInfo#uri} won't reflect potential redirection yet and {@link * LoadEventInfo#responseHeaders} will be empty. * @param mediaLoadData The {@link MediaLoadData} defining the data being loaded. + * @param retryCount The number of failed attempts since this method was first called (this is + * zero for the first load attempt). */ default void onLoadStarted( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) {} + MediaLoadData mediaLoadData, + int retryCount) {} /** * Called when a load ends. @@ -61,8 +64,7 @@ public interface MediaSourceEventListener { * belong to a specific media period. * @param loadEventInfo The {@link LoadEventInfo} corresponding to the event. The values of {@link * LoadEventInfo#elapsedRealtimeMs} and {@link LoadEventInfo#bytesLoaded} are relative to the - * corresponding {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)} - * event. + * corresponding {@link #onLoadStarted} event. * @param mediaLoadData The {@link MediaLoadData} defining the data being loaded. */ default void onLoadCompleted( @@ -79,8 +81,7 @@ public interface MediaSourceEventListener { * belong to a specific media period. * @param loadEventInfo The {@link LoadEventInfo} corresponding to the event. The values of {@link * LoadEventInfo#elapsedRealtimeMs} and {@link LoadEventInfo#bytesLoaded} are relative to the - * corresponding {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)} - * event. + * corresponding {@link #onLoadStarted} event. * @param mediaLoadData The {@link MediaLoadData} defining the data being loaded. */ default void onLoadCanceled( @@ -108,8 +109,7 @@ public interface MediaSourceEventListener { * belong to a specific media period. * @param loadEventInfo The {@link LoadEventInfo} corresponding to the event. The values of {@link * LoadEventInfo#elapsedRealtimeMs} and {@link LoadEventInfo#bytesLoaded} are relative to the - * corresponding {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)} - * event. + * corresponding {@link #onLoadStarted} event. * @param mediaLoadData The {@link MediaLoadData} defining the data being loaded. * @param error The load error. * @param wasCanceled Whether the load was canceled as a result of the error. @@ -223,8 +223,17 @@ public interface MediaSourceEventListener { } } - /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ + /** + * @deprecated Use {link {@link #loadStarted(LoadEventInfo, int, int)} instead to pass {@code + * retryCount}. + */ + @Deprecated public void loadStarted(LoadEventInfo loadEventInfo, @DataType int dataType) { + loadStarted(loadEventInfo, dataType, /* retryCount= */ 0); + } + + /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData, int)}. */ + public void loadStarted(LoadEventInfo loadEventInfo, @DataType int dataType, int retryCount) { loadStarted( loadEventInfo, dataType, @@ -233,10 +242,15 @@ public interface MediaSourceEventListener { /* trackSelectionReason= */ C.SELECTION_REASON_UNKNOWN, /* trackSelectionData= */ null, /* mediaStartTimeUs= */ C.TIME_UNSET, - /* mediaEndTimeUs= */ C.TIME_UNSET); + /* mediaEndTimeUs= */ C.TIME_UNSET, + retryCount); } - /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ + /** + * @deprecated Use {link {@link #loadStarted(LoadEventInfo, int, int, Format, int, Object, long, + * long, int)} )} instead to pass {@code retryCount}. + */ + @Deprecated public void loadStarted( LoadEventInfo loadEventInfo, @DataType int dataType, @@ -258,11 +272,46 @@ public interface MediaSourceEventListener { usToMs(mediaEndTimeUs))); } - /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ + /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData, int)}. */ + public void loadStarted( + LoadEventInfo loadEventInfo, + @DataType int dataType, + @C.TrackType int trackType, + @Nullable Format trackFormat, + @C.SelectionReason int trackSelectionReason, + @Nullable Object trackSelectionData, + long mediaStartTimeUs, + long mediaEndTimeUs, + int retryCount) { + loadStarted( + loadEventInfo, + new MediaLoadData( + dataType, + trackType, + trackFormat, + trackSelectionReason, + trackSelectionData, + usToMs(mediaStartTimeUs), + usToMs(mediaEndTimeUs)), + retryCount); + } + + /** + * @deprecated Use {link {@link #loadStarted(LoadEventInfo, MediaLoadData, int)} instead to pass + * {@code retryCount}. + */ + @Deprecated public void loadStarted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { + loadStarted(loadEventInfo, mediaLoadData, /* retryCount= */ 0); + } + + /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData, int)}. */ + public void loadStarted( + LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData, int retryCount) { dispatchEvent( (listener) -> - listener.onLoadStarted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData)); + listener.onLoadStarted( + windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData, retryCount)); } /** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java index 59685136a1..5c525f9798 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java @@ -597,6 +597,31 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Loader.Callback implementation. + @Override + public void onLoadStarted( + ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, int retryCount) { + StatsDataSource dataSource = loadable.dataSource; + LoadEventInfo loadEventInfo = + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + dataSource.getLastOpenedUri(), + dataSource.getLastResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + dataSource.getBytesRead()); + mediaSourceEventDispatcher.loadStarted( + loadEventInfo, + C.DATA_TYPE_MEDIA, + C.TRACK_TYPE_UNKNOWN, + /* trackFormat= */ null, + C.SELECTION_REASON_UNKNOWN, + /* trackSelectionData= */ null, + /* mediaStartTimeUs= */ loadable.seekTimeUs, + durationUs, + retryCount); + } + @Override public void onLoadCompleted( ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { @@ -881,19 +906,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; pendingResetPositionUs = C.TIME_UNSET; } extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount(); - long elapsedRealtimeMs = - loader.startLoading( - loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(dataType)); - DataSpec dataSpec = loadable.dataSpec; - mediaSourceEventDispatcher.loadStarted( - new LoadEventInfo(loadable.loadTaskId, dataSpec, elapsedRealtimeMs), - C.DATA_TYPE_MEDIA, - C.TRACK_TYPE_UNKNOWN, - /* trackFormat= */ null, - C.SELECTION_REASON_UNKNOWN, - /* trackSelectionData= */ null, - /* mediaStartTimeUs= */ loadable.seekTimeUs, - durationUs); + loader.startLoading( + loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(dataType)); } /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SingleSampleMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SingleSampleMediaPeriod.java index 9348819a8c..651e296078 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SingleSampleMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SingleSampleMediaPeriod.java @@ -155,20 +155,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; dataSource.addTransferListener(transferListener); } SourceLoadable loadable = new SourceLoadable(dataSpec, dataSource); - long elapsedRealtimeMs = - loader.startLoading( - loadable, - /* callback= */ this, - loadErrorHandlingPolicy.getMinimumLoadableRetryCount(C.DATA_TYPE_MEDIA)); - eventDispatcher.loadStarted( - new LoadEventInfo(loadable.loadTaskId, dataSpec, elapsedRealtimeMs), - C.DATA_TYPE_MEDIA, - C.TRACK_TYPE_UNKNOWN, - format, - C.SELECTION_REASON_UNKNOWN, - /* trackSelectionData= */ null, - /* mediaStartTimeUs= */ 0, - durationUs); + loader.startLoading( + loadable, + /* callback= */ this, + loadErrorHandlingPolicy.getMinimumLoadableRetryCount(C.DATA_TYPE_MEDIA)); return true; } @@ -207,6 +197,31 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Loader.Callback implementation. + @Override + public void onLoadStarted( + SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, int retryCount) { + StatsDataSource dataSource = loadable.dataSource; + LoadEventInfo loadEventInfo = + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + dataSource.getLastOpenedUri(), + dataSource.getLastResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + dataSource.getBytesRead()); + eventDispatcher.loadStarted( + loadEventInfo, + C.DATA_TYPE_MEDIA, + C.TRACK_TYPE_UNKNOWN, + format, + C.SELECTION_REASON_UNKNOWN, + /* trackSelectionData= */ null, + /* mediaStartTimeUs= */ 0, + durationUs, + retryCount); + } + @Override public void onLoadCompleted( SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java index 3ed590e3ea..f37a86bdfa 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java @@ -427,20 +427,26 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource int windowIndex, @Nullable MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { - @Nullable - MediaPeriodImpl mediaPeriod = - getMediaPeriodForEvent(mediaPeriodId, mediaLoadData, /* useLoadingPeriod= */ true); - if (mediaPeriod == null) { - mediaSourceEventDispatcherWithoutId.loadStarted(loadEventInfo, mediaLoadData); - } else { - mediaPeriod.sharedPeriod.onLoadStarted(loadEventInfo, mediaLoadData); - mediaPeriod.mediaSourceEventDispatcher.loadStarted( - loadEventInfo, - correctMediaLoadData( - mediaPeriod, - mediaLoadData, - checkNotNull(adPlaybackStates.get(mediaPeriod.mediaPeriodId.periodUid)))); + MediaLoadData mediaLoadData, + int retryCount) { + // TODO: b/375408535 - Update this to support non-zero retry counts. + if (retryCount == 0) { + @Nullable + MediaPeriodImpl mediaPeriod = + getMediaPeriodForEvent(mediaPeriodId, mediaLoadData, /* useLoadingPeriod= */ true); + if (mediaPeriod == null) { + mediaSourceEventDispatcherWithoutId.loadStarted( + loadEventInfo, mediaLoadData, /* retryCount= */ 0); + } else { + mediaPeriod.sharedPeriod.onLoadStarted(loadEventInfo, mediaLoadData); + mediaPeriod.mediaSourceEventDispatcher.loadStarted( + loadEventInfo, + correctMediaLoadData( + mediaPeriod, + mediaLoadData, + checkNotNull(adPlaybackStates.get(mediaPeriod.mediaPeriodId.periodUid))), + /* retryCount= */ 0); + } } } @@ -750,7 +756,9 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource loadData.first, correctMediaLoadData(loadingPeriod, loadData.second, adPlaybackState)); mediaPeriod.mediaSourceEventDispatcher.loadStarted( - loadData.first, correctMediaLoadData(mediaPeriod, loadData.second, adPlaybackState)); + loadData.first, + correctMediaLoadData(mediaPeriod, loadData.second, adPlaybackState), + /* retryCount= */ 0); } } this.loadingPeriod = mediaPeriod; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java index e2ee724462..006f63911b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkSampleStream.java @@ -429,6 +429,28 @@ public class ChunkSampleStream // Loader.Callback implementation. + @Override + public void onLoadStarted( + Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, int retryCount) { + mediaSourceEventDispatcher.loadStarted( + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + loadable.getUri(), + loadable.getResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + loadable.dataSource.getBytesRead()), + loadable.type, + primaryTrackType, + loadable.trackFormat, + loadable.trackSelectionReason, + loadable.trackSelectionData, + loadable.startTimeUs, + loadable.endTimeUs, + retryCount); + } + @Override public void onLoadCompleted(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs) { loadingChunk = null; @@ -636,18 +658,8 @@ public class ChunkSampleStream } else if (loadable instanceof InitializationChunk) { ((InitializationChunk) loadable).init(chunkOutput); } - long elapsedRealtimeMs = - loader.startLoading( - loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); - mediaSourceEventDispatcher.loadStarted( - new LoadEventInfo(loadable.loadTaskId, loadable.dataSpec, elapsedRealtimeMs), - loadable.type, - primaryTrackType, - loadable.trackFormat, - loadable.trackSelectionReason, - loadable.trackSelectionData, - loadable.startTimeUs, - loadable.endTimeUs); + loader.startLoading( + loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); return true; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java index d5a65b1c0f..916bb1421e 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java @@ -146,8 +146,9 @@ import org.mockito.InOrder; public final class DefaultAnalyticsCollectorTest { // Deprecated event constants. - private static final long EVENT_PLAYER_STATE_CHANGED = 1L << 63; - private static final long EVENT_SEEK_STARTED = 1L << 62; + private static final long EVENT_PLAYER_STATE_CHANGED = Long.MIN_VALUE; + private static final long EVENT_SEEK_STARTED = Long.MIN_VALUE + 1; + private static final long DEPRECATED_EVENT_LOAD_STARTED = Long.MIN_VALUE + 2; private static final UUID DRM_SCHEME_UUID = UUID.nameUUIDFromBytes(TestUtil.createByteArray(7, 8, 9)); @@ -264,6 +265,9 @@ public final class DefaultAnalyticsCollectorTest { assertThat(listener.getEvents(EVENT_LOAD_STARTED)) .containsExactly(WINDOW_0 /* manifest */, period0 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly(WINDOW_0 /* manifest */, period0 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly(WINDOW_0 /* manifest */, period0 /* media */) .inOrder(); @@ -333,6 +337,13 @@ public final class DefaultAnalyticsCollectorTest { period0 /* media */, period1 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, + WINDOW_1 /* manifest */, + period0 /* media */, + period1 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, @@ -411,6 +422,13 @@ public final class DefaultAnalyticsCollectorTest { period0 /* media */, period1 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, + WINDOW_1 /* manifest */, + period0 /* media */, + period1 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, @@ -493,6 +511,13 @@ public final class DefaultAnalyticsCollectorTest { period0 /* media */, period1 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, + WINDOW_1 /* manifest */, + period0 /* media */, + period1 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, @@ -590,6 +615,14 @@ public final class DefaultAnalyticsCollectorTest { period1Seq1 /* media */, period1Seq2 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, + WINDOW_1 /* manifest */, + period0 /* media */, + period1Seq1 /* media */, + period1Seq2 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, @@ -709,6 +742,13 @@ public final class DefaultAnalyticsCollectorTest { WINDOW_0 /* manifest */, period0Seq1 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, + period0Seq0 /* media */, + WINDOW_0 /* manifest */, + period0Seq1 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, @@ -803,6 +843,13 @@ public final class DefaultAnalyticsCollectorTest { WINDOW_0 /* manifest */, period0Seq0 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, + period0Seq0 /* media */, + WINDOW_0 /* manifest */, + period0Seq0 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, @@ -875,6 +922,10 @@ public final class DefaultAnalyticsCollectorTest { .containsExactly( WINDOW_0 /* manifest */, window0Period1Seq0 /* media */, window1Period0Seq1 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* manifest */, window0Period1Seq0 /* media */, window1Period0Seq1 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* manifest */, window0Period1Seq0 /* media */, window1Period0Seq1 /* media */) @@ -973,6 +1024,9 @@ public final class DefaultAnalyticsCollectorTest { assertThat(listener.getEvents(EVENT_LOAD_STARTED)) .containsExactly(WINDOW_0 /* manifest */, period0Seq0 /* media */, period1Seq1 /* media */) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly(WINDOW_0 /* manifest */, period0Seq0 /* media */, period1Seq1 /* media */) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly(WINDOW_0 /* manifest */, period0Seq0 /* media */, period1Seq1 /* media */) .inOrder(); @@ -1178,6 +1232,16 @@ public final class DefaultAnalyticsCollectorTest { postrollAd, contentAfterPostroll) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* content manifest */, + prerollAd, + contentAfterPreroll, + midrollAd, + contentAfterMidroll, + postrollAd, + contentAfterPostroll) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* content manifest */, @@ -1337,6 +1401,14 @@ public final class DefaultAnalyticsCollectorTest { contentAfterMidroll, contentAfterMidroll) .inOrder(); + assertThat(listener.getEvents(DEPRECATED_EVENT_LOAD_STARTED)) + .containsExactly( + WINDOW_0 /* content manifest */, + contentBeforeMidroll, + midrollAd, + contentAfterMidroll, + contentAfterMidroll) + .inOrder(); assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) .containsExactly( WINDOW_0 /* content manifest */, @@ -1715,7 +1787,7 @@ public final class DefaultAnalyticsCollectorTest { ArgumentCaptor individualLoadStartedEventTimes = ArgumentCaptor.forClass(AnalyticsListener.EventTime.class); verify(listener, atLeastOnce()) - .onLoadStarted(individualLoadStartedEventTimes.capture(), any(), any()); + .onLoadStarted(individualLoadStartedEventTimes.capture(), any(), any(), anyInt()); ArgumentCaptor individualLoadCompletedEventTimes = ArgumentCaptor.forClass(AnalyticsListener.EventTime.class); verify(listener, atLeastOnce()) @@ -2196,6 +2268,15 @@ public final class DefaultAnalyticsCollectorTest { @Override public void onLoadStarted( EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { + reportedEvents.add(new ReportedEvent(DEPRECATED_EVENT_LOAD_STARTED, eventTime)); + } + + @Override + public void onLoadStarted( + EventTime eventTime, + LoadEventInfo loadEventInfo, + MediaLoadData mediaLoadData, + int retryCount) { reportedEvents.add(new ReportedEvent(EVENT_LOAD_STARTED, eventTime)); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ConcatenatingMediaSource2Test.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ConcatenatingMediaSource2Test.java index 56a6729215..951fc3c0a9 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ConcatenatingMediaSource2Test.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ConcatenatingMediaSource2Test.java @@ -20,6 +20,7 @@ import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUnti import static com.google.common.truth.Truth.assertThat; import static java.lang.Math.max; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; @@ -625,7 +626,8 @@ public final class ConcatenatingMediaSource2Test { /* windowIndex= */ eq(0), /* mediaPeriodId= */ eq(mediaPeriodId), /* loadEventInfo= */ any(), - /* mediaLoadData= */ any()); + /* mediaLoadData= */ any(), + /* retryCount */ anyInt()); verify(eventListener) .onLoadCompleted( /* windowIndex= */ eq(0), diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaSourceTest.java index b39328c399..5630e97945 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaSourceTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaSourceTest.java @@ -139,7 +139,8 @@ public class MergingMediaSourceTest { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { + MediaLoadData mediaLoadData, + int retryCount) { if (mediaPeriodId != null) { onLoadStartedMediaPeriodUids.add(mediaPeriodId.periodUid); } diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java index 417f2ac40e..3bbbf9929c 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java @@ -629,6 +629,24 @@ public final class DashMediaSource extends BaseMediaSource { // Loadable callbacks. + /* package */ void onManifestLoadStarted( + ParsingLoadable loadable, + long elapsedRealtimeMs, + long loadDurationMs, + int retryCount) { + manifestEventDispatcher.loadStarted( + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + loadable.getUri(), + loadable.getResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + loadable.bytesLoaded()), + loadable.type, + retryCount); + } + /* package */ void onManifestLoadCompleted( ParsingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { LoadEventInfo loadEventInfo = @@ -1094,10 +1112,7 @@ public final class DashMediaSource extends BaseMediaSource { ParsingLoadable loadable, Loader.Callback> callback, int minRetryCount) { - long elapsedRealtimeMs = loader.startLoading(loadable, callback, minRetryCount); - manifestEventDispatcher.loadStarted( - new LoadEventInfo(loadable.loadTaskId, loadable.dataSpec, elapsedRealtimeMs), - loadable.type); + loader.startLoading(loadable, callback, minRetryCount); } private static long getIntervalUntilNextManifestRefreshMs( @@ -1389,6 +1404,15 @@ public final class DashMediaSource extends BaseMediaSource { private final class ManifestCallback implements Loader.Callback> { + @Override + public void onLoadStarted( + ParsingLoadable loadable, + long elapsedRealtimeMs, + long loadDurationMs, + int retryCount) { + onManifestLoadStarted(loadable, elapsedRealtimeMs, loadDurationMs, retryCount); + } + @Override public void onLoadCompleted( ParsingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java index 8779c83b40..60b0333983 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsSampleStreamWrapper.java @@ -808,18 +808,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; initMediaChunkLoad((HlsMediaChunk) loadable); } loadingChunk = loadable; - long elapsedRealtimeMs = - loader.startLoading( - loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); - mediaSourceEventDispatcher.loadStarted( - new LoadEventInfo(loadable.loadTaskId, loadable.dataSpec, elapsedRealtimeMs), - loadable.type, - trackType, - loadable.trackFormat, - loadable.trackSelectionReason, - loadable.trackSelectionData, - loadable.startTimeUs, - loadable.endTimeUs); + loader.startLoading( + loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); return true; } @@ -860,6 +850,28 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // Loader.Callback implementation. + @Override + public void onLoadStarted( + Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, int retryCount) { + mediaSourceEventDispatcher.loadStarted( + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + loadable.getUri(), + loadable.getResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + loadable.bytesLoaded()), + loadable.type, + trackType, + loadable.trackFormat, + loadable.trackSelectionReason, + loadable.trackSelectionData, + loadable.startTimeUs, + loadable.endTimeUs, + retryCount); + } + @Override public void onLoadCompleted(Chunk loadable, long elapsedRealtimeMs, long loadDurationMs) { loadingChunk = null; diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java index 9e2d786d18..a242d81b8b 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/DefaultHlsPlaylistTracker.java @@ -141,18 +141,10 @@ public final class DefaultHlsPlaylistTracker playlistParserFactory.createPlaylistParser()); Assertions.checkState(initialPlaylistLoader == null); initialPlaylistLoader = new Loader("DefaultHlsPlaylistTracker:MultivariantPlaylist"); - long elapsedRealtime = - initialPlaylistLoader.startLoading( - multivariantPlaylistLoadable, - this, - loadErrorHandlingPolicy.getMinimumLoadableRetryCount( - multivariantPlaylistLoadable.type)); - eventDispatcher.loadStarted( - new LoadEventInfo( - multivariantPlaylistLoadable.loadTaskId, - multivariantPlaylistLoadable.dataSpec, - elapsedRealtime), - multivariantPlaylistLoadable.type); + initialPlaylistLoader.startLoading( + multivariantPlaylistLoadable, + this, + loadErrorHandlingPolicy.getMinimumLoadableRetryCount(multivariantPlaylistLoadable.type)); } @Override @@ -254,6 +246,25 @@ public final class DefaultHlsPlaylistTracker // Loader.Callback implementation. + @Override + public void onLoadStarted( + ParsingLoadable loadable, + long elapsedRealtimeMs, + long loadDurationMs, + int retryCount) { + eventDispatcher.loadStarted( + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + loadable.getUri(), + loadable.getResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + loadable.bytesLoaded()), + loadable.type, + retryCount); + } + @Override public void onLoadCompleted( ParsingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { @@ -736,15 +747,10 @@ public final class DefaultHlsPlaylistTracker playlistRequestUri, C.DATA_TYPE_MANIFEST, mediaPlaylistParser); - long elapsedRealtime = - mediaPlaylistLoader.startLoading( - mediaPlaylistLoadable, - /* callback= */ this, - loadErrorHandlingPolicy.getMinimumLoadableRetryCount(mediaPlaylistLoadable.type)); - eventDispatcher.loadStarted( - new LoadEventInfo( - mediaPlaylistLoadable.loadTaskId, mediaPlaylistLoadable.dataSpec, elapsedRealtime), - mediaPlaylistLoadable.type); + mediaPlaylistLoader.startLoading( + mediaPlaylistLoadable, + /* callback= */ this, + loadErrorHandlingPolicy.getMinimumLoadableRetryCount(mediaPlaylistLoadable.type)); } private void processLoadedPlaylist( diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java index 7086deeb9f..62a581c7d8 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java @@ -497,6 +497,25 @@ public final class SsMediaSource extends BaseMediaSource // Loader.Callback implementation + @Override + public void onLoadStarted( + ParsingLoadable loadable, + long elapsedRealtimeMs, + long loadDurationMs, + int retryCount) { + manifestEventDispatcher.loadStarted( + new LoadEventInfo( + loadable.loadTaskId, + loadable.dataSpec, + loadable.getUri(), + loadable.getResponseHeaders(), + elapsedRealtimeMs, + loadDurationMs, + loadable.bytesLoaded()), + loadable.type, + retryCount); + } + @Override public void onLoadCompleted( ParsingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { @@ -659,11 +678,7 @@ public final class SsMediaSource extends BaseMediaSource ParsingLoadable loadable = new ParsingLoadable<>( manifestDataSource, manifestUri, C.DATA_TYPE_MANIFEST, manifestParser); - long elapsedRealtimeMs = - manifestLoader.startLoading( - loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); - manifestEventDispatcher.loadStarted( - new LoadEventInfo(loadable.loadTaskId, loadable.dataSpec, elapsedRealtimeMs), - loadable.type); + manifestLoader.startLoading( + loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); } } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeAdaptiveMediaPeriod.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeAdaptiveMediaPeriod.java index 87ad35b0ce..5a6b53c7b8 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeAdaptiveMediaPeriod.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeAdaptiveMediaPeriod.java @@ -116,7 +116,8 @@ public class FakeAdaptiveMediaPeriod C.SELECTION_REASON_UNKNOWN, /* trackSelectionData= */ null, /* mediaStartTimeUs= */ 0, - /* mediaEndTimeUs= */ C.TIME_UNSET); + /* mediaEndTimeUs= */ C.TIME_UNSET, + /* retryCount= */ 0); this.callback = callback; prepared = true; Util.castNonNull(this.callback).onPrepared(this); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaPeriod.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaPeriod.java index a8b9c06b5f..a8740dd63d 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaPeriod.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaPeriod.java @@ -235,7 +235,8 @@ public class FakeMediaPeriod implements MediaPeriod { C.SELECTION_REASON_UNKNOWN, /* trackSelectionData= */ null, /* mediaStartTimeUs= */ 0, - /* mediaEndTimeUs= */ C.TIME_UNSET); + /* mediaEndTimeUs= */ C.TIME_UNSET, + /* retryCount= */ 0); prepareCallback = callback; if (deferOnPrepared) { playerHandler = Util.createHandlerForCurrentLooper(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSource.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSource.java index 4ec1f61bc5..f1fd428482 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSource.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSource.java @@ -455,7 +455,8 @@ public class FakeMediaSource extends BaseMediaSource { elapsedRealTimeMs, /* loadDurationMs= */ 0, /* bytesLoaded= */ 0), - mediaLoadData); + mediaLoadData, + /* retryCount= */ 0); eventDispatcher.loadCompleted( new LoadEventInfo( loadTaskId, diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaSourceTestRunner.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaSourceTestRunner.java index 07ccf80f28..ac0c393c71 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaSourceTestRunner.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaSourceTestRunner.java @@ -364,7 +364,8 @@ public class MediaSourceTestRunner { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { + MediaLoadData mediaLoadData, + int retryCount) { Assertions.checkState(Looper.myLooper() == playbackThread.getLooper()); }