diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b3d72a23b0..32ee83129e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -88,6 +88,9 @@ `CacheDataSink.Factory` and `CacheDataSource.Factory` respectively. * Enable the configuration of `SilenceSkippingAudioProcessor` ([#6705](https://github.com/google/ExoPlayer/issues/6705)). + * Extend `EventTime` with more details about the current player state for + easier access + ([#7332](https://github.com/google/ExoPlayer/issues/7332)). * Video: Pass frame rate hint to `Surface.setFrameRate` on Android R devices. * Text: * Parse `` and `` tags in WebVTT subtitles (rendering is coming diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java index fceaa14b73..de30f66e6c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java @@ -659,12 +659,16 @@ public class AnalyticsCollector eventPositionMs = timeline.isEmpty() ? 0 : timeline.getWindow(windowIndex, window).getDefaultPositionMs(); } + @Nullable MediaPeriodInfo currentInfo = mediaPeriodQueueTracker.getCurrentPlayerMediaPeriod(); return new EventTime( realtimeMs, timeline, windowIndex, mediaPeriodId, eventPositionMs, + player.getCurrentTimeline(), + player.getCurrentWindowIndex(), + currentInfo == null ? null : currentInfo.mediaPeriodId, player.getCurrentPosition(), player.getTotalBufferedDuration()); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java index 0b841ab543..d798702f43 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java @@ -56,7 +56,7 @@ public interface AnalyticsListener { */ public final long realtimeMs; - /** Timeline at the time of the event. */ + /** Most recent {@link Timeline} that contains the event position. */ public final Timeline timeline; /** @@ -66,8 +66,8 @@ public interface AnalyticsListener { public final int windowIndex; /** - * Media period identifier for the media period this event belongs to, or {@code null} if the - * event is not associated with a specific media period. + * {@link MediaPeriodId Media period identifier} for the media period this event belongs to, or + * {@code null} if the event is not associated with a specific media period. */ @Nullable public final MediaPeriodId mediaPeriodId; @@ -77,8 +77,27 @@ public interface AnalyticsListener { public final long eventPlaybackPositionMs; /** - * Position in the current timeline window ({@link Player#getCurrentWindowIndex()}) or the - * currently playing ad at the time of the event, in milliseconds. + * The current {@link Timeline} at the time of the event (equivalent to {@link + * Player#getCurrentTimeline()}). + */ + public final Timeline currentTimeline; + + /** + * The current window index in {@link #currentTimeline} at the time of the event, or the + * prospective window index if the timeline is not yet known and empty (equivalent to {@link + * Player#getCurrentWindowIndex()}). + */ + public final int currentWindowIndex; + + /** + * {@link MediaPeriodId Media period identifier} for the currently playing media period at the + * time of the event, or {@code null} if no current media period identifier is available. + */ + @Nullable public final MediaPeriodId currentMediaPeriodId; + + /** + * Position in the {@link #currentWindowIndex current timeline window} or the currently playing + * ad at the time of the event, in milliseconds. */ public final long currentPlaybackPositionMs; @@ -91,19 +110,27 @@ public interface AnalyticsListener { /** * @param realtimeMs Elapsed real-time as returned by {@code SystemClock.elapsedRealtime()} at * the time of the event, in milliseconds. - * @param timeline Timeline at the time of the event. - * @param windowIndex Window index in the {@link #timeline} this event belongs to, or the + * @param timeline Most recent {@link Timeline} that contains the event position. + * @param windowIndex Window index in the {@code timeline} this event belongs to, or the * prospective window index if the timeline is not yet known and empty. - * @param mediaPeriodId Media period identifier for the media period this event belongs to, or - * {@code null} if the event is not associated with a specific media period. + * @param mediaPeriodId {@link MediaPeriodId Media period identifier} for the media period this + * event belongs to, or {@code null} if the event is not associated with a specific media + * period. * @param eventPlaybackPositionMs Position in the window or ad this event belongs to at the time * of the event, in milliseconds. - * @param currentPlaybackPositionMs Position in the current timeline window ({@link - * Player#getCurrentWindowIndex()}) or the currently playing ad at the time of the event, in - * milliseconds. - * @param totalBufferedDurationMs Total buffered duration from {@link - * #currentPlaybackPositionMs} at the time of the event, in milliseconds. This includes - * pre-buffered data for subsequent ads and windows. + * @param currentTimeline The current {@link Timeline} at the time of the event (equivalent to + * {@link Player#getCurrentTimeline()}). + * @param currentWindowIndex The current window index in {@code currentTimeline} at the time of + * the event, or the prospective window index if the timeline is not yet known and empty + * (equivalent to {@link Player#getCurrentWindowIndex()}). + * @param currentMediaPeriodId {@link MediaPeriodId Media period identifier} for the currently + * playing media period at the time of the event, or {@code null} if no current media period + * identifier is available. + * @param currentPlaybackPositionMs Position in the current timeline window or the currently + * playing ad at the time of the event, in milliseconds. + * @param totalBufferedDurationMs Total buffered duration from {@code currentPlaybackPositionMs} + * at the time of the event, in milliseconds. This includes pre-buffered data for subsequent + * ads and windows. */ public EventTime( long realtimeMs, @@ -111,6 +138,9 @@ public interface AnalyticsListener { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long eventPlaybackPositionMs, + Timeline currentTimeline, + int currentWindowIndex, + @Nullable MediaPeriodId currentMediaPeriodId, long currentPlaybackPositionMs, long totalBufferedDurationMs) { this.realtimeMs = realtimeMs; @@ -118,6 +148,9 @@ public interface AnalyticsListener { this.windowIndex = windowIndex; this.mediaPeriodId = mediaPeriodId; this.eventPlaybackPositionMs = eventPlaybackPositionMs; + this.currentTimeline = currentTimeline; + this.currentWindowIndex = currentWindowIndex; + this.currentMediaPeriodId = currentMediaPeriodId; this.currentPlaybackPositionMs = currentPlaybackPositionMs; this.totalBufferedDurationMs = totalBufferedDurationMs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/PlaybackStatsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/PlaybackStatsListener.java index 0524f4d3b1..cefe143b73 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/PlaybackStatsListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/PlaybackStatsListener.java @@ -157,6 +157,9 @@ public final class PlaybackStatsListener /* windowIndex= */ 0, /* mediaPeriodId= */ null, /* eventPlaybackPositionMs= */ 0, + Timeline.EMPTY, + /* currentWindowIndex= */ 0, + /* currentMediaPeriodId= */ null, /* currentPlaybackPositionMs= */ 0, /* totalBufferedDurationMs= */ 0); sessionManager.finishAllSessions(dummyEventTime); @@ -210,6 +213,9 @@ public final class PlaybackStatsListener eventTime.mediaPeriodId.windowSequenceNumber, eventTime.mediaPeriodId.adGroupIndex), /* eventPlaybackPositionMs= */ C.usToMs(contentWindowPositionUs), + eventTime.timeline, + eventTime.currentWindowIndex, + eventTime.currentMediaPeriodId, eventTime.currentPlaybackPositionMs, eventTime.totalBufferedDurationMs); Assertions.checkNotNull(playbackStatsTrackers.get(contentSession)) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java index b24135152e..a5a021c80a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java @@ -1092,6 +1092,9 @@ public final class DefaultPlaybackSessionManagerTest { windowIndex, mediaPeriodId, /* eventPlaybackPositionMs= */ 0, + timeline, + windowIndex, + mediaPeriodId, /* currentPlaybackPositionMs= */ 0, /* totalBufferedDurationMs= */ 0); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/analytics/PlaybackStatsListenerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/analytics/PlaybackStatsListenerTest.java index c6d4a597ed..8a1f8807ea 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/analytics/PlaybackStatsListenerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/analytics/PlaybackStatsListenerTest.java @@ -44,20 +44,27 @@ public final class PlaybackStatsListenerTest { /* windowIndex= */ 0, /* mediaPeriodId= */ null, /* eventPlaybackPositionMs= */ 0, + /* currentTimeline= */ Timeline.EMPTY, + /* currentWindowIndex= */ 0, + /* currentMediaPeriodId= */ null, /* currentPlaybackPositionMs= */ 0, /* totalBufferedDurationMs= */ 0); private static final Timeline TEST_TIMELINE = new FakeTimeline(/* windowCount= */ 1); + private static final MediaSource.MediaPeriodId TEST_MEDIA_PERIOD_ID = + new MediaSource.MediaPeriodId( + TEST_TIMELINE.getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true) + .uid, + /* windowSequenceNumber= */ 42); private static final AnalyticsListener.EventTime TEST_EVENT_TIME = new AnalyticsListener.EventTime( /* realtimeMs= */ 500, TEST_TIMELINE, /* windowIndex= */ 0, - new MediaSource.MediaPeriodId( - TEST_TIMELINE.getPeriod( - /* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true) - .uid, - /* windowSequenceNumber= */ 42), + TEST_MEDIA_PERIOD_ID, /* eventPlaybackPositionMs= */ 123, + TEST_TIMELINE, + /* currentWindowIndex= */ 0, + TEST_MEDIA_PERIOD_ID, /* currentPlaybackPositionMs= */ 123, /* totalBufferedDurationMs= */ 456); @@ -151,6 +158,9 @@ public final class PlaybackStatsListenerTest { /* windowIndex= */ 0, /* mediaPeriodId= */ null, /* eventPlaybackPositionMs= */ 0, + Timeline.EMPTY, + /* currentWindowIndex= */ 0, + /* currentMediaPeriodId= */ null, /* currentPlaybackPositionMs= */ 0, /* totalBufferedDurationMs= */ 0); AnalyticsListener.EventTime eventTimeWindow1 = @@ -160,6 +170,9 @@ public final class PlaybackStatsListenerTest { /* windowIndex= */ 1, /* mediaPeriodId= */ null, /* eventPlaybackPositionMs= */ 0, + Timeline.EMPTY, + /* currentWindowIndex= */ 1, + /* currentMediaPeriodId= */ null, /* currentPlaybackPositionMs= */ 0, /* totalBufferedDurationMs= */ 0); PlaybackStatsListener.Callback callback = mock(PlaybackStatsListener.Callback.class);