Commit febf5d20 by tonihei Committed by Oliver Woodman

Use ListenerSet in AnalyticsCollector.

This ensures recursively sent events arrive in the correct order.

Issue: #8048
PiperOrigin-RevId: 337812882
parent 3c682610
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
* Switch Guava dependency from `implementation` to `api` * Switch Guava dependency from `implementation` to `api`
([#7905](https://github.com/google/ExoPlayer/issues/7905), ([#7905](https://github.com/google/ExoPlayer/issues/7905),
([#7993](https://github.com/google/ExoPlayer/issues/7993)). ([#7993](https://github.com/google/ExoPlayer/issues/7993)).
* Fix bug where `AnalyticsListener` callbacks can arrive in the wrong
order ([#8048](https://github.com/google/ExoPlayer/issues/8048)).
* Track selection: * Track selection:
* Add option to specify multiple preferred audio or text languages. * Add option to specify multiple preferred audio or text languages.
* Data sources: * Data sources:
......
...@@ -46,6 +46,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; ...@@ -46,6 +46,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ListenerSet;
import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
import com.google.common.base.Objects; import com.google.common.base.Objects;
...@@ -54,7 +55,6 @@ import com.google.common.collect.ImmutableMap; ...@@ -54,7 +55,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...@@ -73,7 +73,7 @@ public class AnalyticsCollector ...@@ -73,7 +73,7 @@ public class AnalyticsCollector
VideoListener, VideoListener,
AudioListener { AudioListener {
private final CopyOnWriteArraySet<AnalyticsListener> listeners; private final ListenerSet<AnalyticsListener> listeners;
private final Clock clock; private final Clock clock;
private final Period period; private final Period period;
private final Window window; private final Window window;
...@@ -89,7 +89,7 @@ public class AnalyticsCollector ...@@ -89,7 +89,7 @@ public class AnalyticsCollector
*/ */
public AnalyticsCollector(Clock clock) { public AnalyticsCollector(Clock clock) {
this.clock = checkNotNull(clock); this.clock = checkNotNull(clock);
listeners = new CopyOnWriteArraySet<>(); listeners = new ListenerSet<>();
period = new Period(); period = new Period();
window = new Window(); window = new Window();
mediaPeriodQueueTracker = new MediaPeriodQueueTracker(period); mediaPeriodQueueTracker = new MediaPeriodQueueTracker(period);
...@@ -150,9 +150,7 @@ public class AnalyticsCollector ...@@ -150,9 +150,7 @@ public class AnalyticsCollector
if (!isSeeking) { if (!isSeeking) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
isSeeking = true; isSeeking = true;
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onSeekStarted(eventTime));
listener.onSeekStarted(eventTime);
}
} }
} }
...@@ -166,9 +164,7 @@ public class AnalyticsCollector ...@@ -166,9 +164,7 @@ public class AnalyticsCollector
@Override @Override
public final void onMetadata(Metadata metadata) { public final void onMetadata(Metadata metadata) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onMetadata(eventTime, metadata));
listener.onMetadata(eventTime, metadata);
}
} }
// AudioRendererEventListener implementation. // AudioRendererEventListener implementation.
...@@ -177,10 +173,11 @@ public class AnalyticsCollector ...@@ -177,10 +173,11 @@ public class AnalyticsCollector
@Override @Override
public final void onAudioEnabled(DecoderCounters counters) { public final void onAudioEnabled(DecoderCounters counters) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onAudioEnabled(eventTime, counters); listener.onAudioEnabled(eventTime, counters);
listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_AUDIO, counters); listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_AUDIO, counters);
} });
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
...@@ -188,48 +185,50 @@ public class AnalyticsCollector ...@@ -188,48 +185,50 @@ public class AnalyticsCollector
public final void onAudioDecoderInitialized( public final void onAudioDecoderInitialized(
String decoderName, long initializedTimestampMs, long initializationDurationMs) { String decoderName, long initializedTimestampMs, long initializationDurationMs) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onAudioDecoderInitialized(eventTime, decoderName, initializationDurationMs); listener.onAudioDecoderInitialized(eventTime, decoderName, initializationDurationMs);
listener.onDecoderInitialized( listener.onDecoderInitialized(
eventTime, C.TRACK_TYPE_AUDIO, decoderName, initializationDurationMs); eventTime, C.TRACK_TYPE_AUDIO, decoderName, initializationDurationMs);
} });
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public final void onAudioInputFormatChanged(Format format) { public final void onAudioInputFormatChanged(Format format) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onAudioInputFormatChanged(eventTime, format); listener.onAudioInputFormatChanged(eventTime, format);
listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_AUDIO, format); listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_AUDIO, format);
} });
} }
@Override @Override
public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs); listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs));
}
} }
@Override @Override
public final void onAudioUnderrun( public final void onAudioUnderrun(
int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); listener ->
} listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs));
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public final void onAudioDisabled(DecoderCounters counters) { public final void onAudioDisabled(DecoderCounters counters) {
EventTime eventTime = generatePlayingMediaPeriodEventTime(); EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onAudioDisabled(eventTime, counters); listener.onAudioDisabled(eventTime, counters);
listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_AUDIO, counters); listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_AUDIO, counters);
} });
} }
// AudioListener implementation. // AudioListener implementation.
...@@ -237,41 +236,32 @@ public class AnalyticsCollector ...@@ -237,41 +236,32 @@ public class AnalyticsCollector
@Override @Override
public final void onAudioSessionId(int audioSessionId) { public final void onAudioSessionId(int audioSessionId) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onAudioSessionId(eventTime, audioSessionId));
listener.onAudioSessionId(eventTime, audioSessionId);
}
} }
@Override @Override
public void onAudioAttributesChanged(AudioAttributes audioAttributes) { public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes));
listener.onAudioAttributesChanged(eventTime, audioAttributes);
}
} }
@Override @Override
public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled); listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled));
}
} }
@Override @Override
public void onAudioSinkError(Exception audioSinkError) { public void onAudioSinkError(Exception audioSinkError) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onAudioSinkError(eventTime, audioSinkError));
listener.onAudioSinkError(eventTime, audioSinkError);
}
} }
@Override @Override
public void onVolumeChanged(float audioVolume) { public void onVolumeChanged(float audioVolume) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onVolumeChanged(eventTime, audioVolume));
listener.onVolumeChanged(eventTime, audioVolume);
}
} }
// VideoRendererEventListener implementation. // VideoRendererEventListener implementation.
...@@ -280,10 +270,11 @@ public class AnalyticsCollector ...@@ -280,10 +270,11 @@ public class AnalyticsCollector
@Override @Override
public final void onVideoEnabled(DecoderCounters counters) { public final void onVideoEnabled(DecoderCounters counters) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onVideoEnabled(eventTime, counters); listener.onVideoEnabled(eventTime, counters);
listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_VIDEO, counters); listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_VIDEO, counters);
} });
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
...@@ -291,55 +282,54 @@ public class AnalyticsCollector ...@@ -291,55 +282,54 @@ public class AnalyticsCollector
public final void onVideoDecoderInitialized( public final void onVideoDecoderInitialized(
String decoderName, long initializedTimestampMs, long initializationDurationMs) { String decoderName, long initializedTimestampMs, long initializationDurationMs) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onVideoDecoderInitialized(eventTime, decoderName, initializationDurationMs); listener.onVideoDecoderInitialized(eventTime, decoderName, initializationDurationMs);
listener.onDecoderInitialized( listener.onDecoderInitialized(
eventTime, C.TRACK_TYPE_VIDEO, decoderName, initializationDurationMs); eventTime, C.TRACK_TYPE_VIDEO, decoderName, initializationDurationMs);
} });
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public final void onVideoInputFormatChanged(Format format) { public final void onVideoInputFormatChanged(Format format) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onVideoInputFormatChanged(eventTime, format); listener.onVideoInputFormatChanged(eventTime, format);
listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_VIDEO, format); listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_VIDEO, format);
} });
} }
@Override @Override
public final void onDroppedFrames(int count, long elapsedMs) { public final void onDroppedFrames(int count, long elapsedMs) {
EventTime eventTime = generatePlayingMediaPeriodEventTime(); EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs));
listener.onDroppedVideoFrames(eventTime, count, elapsedMs);
}
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public final void onVideoDisabled(DecoderCounters counters) { public final void onVideoDisabled(DecoderCounters counters) {
EventTime eventTime = generatePlayingMediaPeriodEventTime(); EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener -> {
listener.onVideoDisabled(eventTime, counters); listener.onVideoDisabled(eventTime, counters);
listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_VIDEO, counters); listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_VIDEO, counters);
} });
} }
@Override @Override
public final void onRenderedFirstFrame(@Nullable Surface surface) { public final void onRenderedFirstFrame(@Nullable Surface surface) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onRenderedFirstFrame(eventTime, surface));
listener.onRenderedFirstFrame(eventTime, surface);
}
} }
@Override @Override
public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
EventTime eventTime = generatePlayingMediaPeriodEventTime(); EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount); listener ->
} listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount));
} }
// VideoListener implementation. // VideoListener implementation.
...@@ -353,18 +343,16 @@ public class AnalyticsCollector ...@@ -353,18 +343,16 @@ public class AnalyticsCollector
public final void onVideoSizeChanged( public final void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener ->
listener.onVideoSizeChanged( listener.onVideoSizeChanged(
eventTime, width, height, unappliedRotationDegrees, pixelWidthHeightRatio); eventTime, width, height, unappliedRotationDegrees, pixelWidthHeightRatio));
}
} }
@Override @Override
public void onSurfaceSizeChanged(int width, int height) { public void onSurfaceSizeChanged(int width, int height) {
EventTime eventTime = generateReadingMediaPeriodEventTime(); EventTime eventTime = generateReadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onSurfaceSizeChanged(eventTime, width, height));
listener.onSurfaceSizeChanged(eventTime, width, height);
}
} }
// MediaSourceEventListener implementation. // MediaSourceEventListener implementation.
...@@ -376,9 +364,8 @@ public class AnalyticsCollector ...@@ -376,9 +364,8 @@ public class AnalyticsCollector
LoadEventInfo loadEventInfo, LoadEventInfo loadEventInfo,
MediaLoadData mediaLoadData) { MediaLoadData mediaLoadData) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData); listener -> listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData));
}
} }
@Override @Override
...@@ -388,9 +375,8 @@ public class AnalyticsCollector ...@@ -388,9 +375,8 @@ public class AnalyticsCollector
LoadEventInfo loadEventInfo, LoadEventInfo loadEventInfo,
MediaLoadData mediaLoadData) { MediaLoadData mediaLoadData) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData); listener -> listener.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData));
}
} }
@Override @Override
...@@ -400,9 +386,8 @@ public class AnalyticsCollector ...@@ -400,9 +386,8 @@ public class AnalyticsCollector
LoadEventInfo loadEventInfo, LoadEventInfo loadEventInfo,
MediaLoadData mediaLoadData) { MediaLoadData mediaLoadData) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onLoadCanceled(eventTime, loadEventInfo, mediaLoadData); listener -> listener.onLoadCanceled(eventTime, loadEventInfo, mediaLoadData));
}
} }
@Override @Override
...@@ -414,27 +399,23 @@ public class AnalyticsCollector ...@@ -414,27 +399,23 @@ public class AnalyticsCollector
IOException error, IOException error,
boolean wasCanceled) { boolean wasCanceled) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onLoadError(eventTime, loadEventInfo, mediaLoadData, error, wasCanceled); listener ->
} listener.onLoadError(eventTime, loadEventInfo, mediaLoadData, error, wasCanceled));
} }
@Override @Override
public final void onUpstreamDiscarded( public final void onUpstreamDiscarded(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onUpstreamDiscarded(eventTime, mediaLoadData));
listener.onUpstreamDiscarded(eventTime, mediaLoadData);
}
} }
@Override @Override
public final void onDownstreamFormatChanged( public final void onDownstreamFormatChanged(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDownstreamFormatChanged(eventTime, mediaLoadData));
listener.onDownstreamFormatChanged(eventTime, mediaLoadData);
}
} }
// Player.EventListener implementation. // Player.EventListener implementation.
...@@ -447,102 +428,83 @@ public class AnalyticsCollector ...@@ -447,102 +428,83 @@ public class AnalyticsCollector
public final void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) { public final void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) {
mediaPeriodQueueTracker.onTimelineChanged(checkNotNull(player)); mediaPeriodQueueTracker.onTimelineChanged(checkNotNull(player));
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onTimelineChanged(eventTime, reason));
listener.onTimelineChanged(eventTime, reason);
}
} }
@Override @Override
public final void onMediaItemTransition( public final void onMediaItemTransition(
@Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason) { @Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onMediaItemTransition(eventTime, mediaItem, reason));
listener.onMediaItemTransition(eventTime, mediaItem, reason);
}
} }
@Override @Override
public final void onTracksChanged( public final void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onTracksChanged(eventTime, trackGroups, trackSelections); listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections));
}
} }
@Override @Override
public final void onStaticMetadataChanged(List<Metadata> metadataList) { public final void onStaticMetadataChanged(List<Metadata> metadataList) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onStaticMetadataChanged(eventTime, metadataList));
listener.onStaticMetadataChanged(eventTime, metadataList);
}
} }
@Override @Override
public final void onIsLoadingChanged(boolean isLoading) { public final void onIsLoadingChanged(boolean isLoading) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onIsLoadingChanged(eventTime, isLoading));
listener.onIsLoadingChanged(eventTime, isLoading);
}
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public final void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) { public final void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onPlayerStateChanged(eventTime, playWhenReady, playbackState); listener -> listener.onPlayerStateChanged(eventTime, playWhenReady, playbackState));
}
} }
@Override @Override
public final void onPlaybackStateChanged(@Player.State int state) { public final void onPlaybackStateChanged(@Player.State int state) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onPlaybackStateChanged(eventTime, state));
listener.onPlaybackStateChanged(eventTime, state);
}
} }
@Override @Override
public final void onPlayWhenReadyChanged( public final void onPlayWhenReadyChanged(
boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) { boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onPlayWhenReadyChanged(eventTime, playWhenReady, reason); listener -> listener.onPlayWhenReadyChanged(eventTime, playWhenReady, reason));
}
} }
@Override @Override
public void onPlaybackSuppressionReasonChanged( public void onPlaybackSuppressionReasonChanged(
@PlaybackSuppressionReason int playbackSuppressionReason) { @PlaybackSuppressionReason int playbackSuppressionReason) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onPlaybackSuppressionReasonChanged(eventTime, playbackSuppressionReason); listener ->
} listener.onPlaybackSuppressionReasonChanged(eventTime, playbackSuppressionReason));
} }
@Override @Override
public void onIsPlayingChanged(boolean isPlaying) { public void onIsPlayingChanged(boolean isPlaying) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onIsPlayingChanged(eventTime, isPlaying));
listener.onIsPlayingChanged(eventTime, isPlaying);
}
} }
@Override @Override
public final void onRepeatModeChanged(@Player.RepeatMode int repeatMode) { public final void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onRepeatModeChanged(eventTime, repeatMode));
listener.onRepeatModeChanged(eventTime, repeatMode);
}
} }
@Override @Override
public final void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { public final void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onShuffleModeChanged(eventTime, shuffleModeEnabled));
listener.onShuffleModeChanged(eventTime, shuffleModeEnabled);
}
} }
@Override @Override
...@@ -551,9 +513,7 @@ public class AnalyticsCollector ...@@ -551,9 +513,7 @@ public class AnalyticsCollector
error.mediaPeriodId != null error.mediaPeriodId != null
? generateEventTime(error.mediaPeriodId) ? generateEventTime(error.mediaPeriodId)
: generateCurrentPlayerMediaPeriodEventTime(); : generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onPlayerError(eventTime, error));
listener.onPlayerError(eventTime, error);
}
} }
@Override @Override
...@@ -563,26 +523,21 @@ public class AnalyticsCollector ...@@ -563,26 +523,21 @@ public class AnalyticsCollector
} }
mediaPeriodQueueTracker.onPositionDiscontinuity(checkNotNull(player)); mediaPeriodQueueTracker.onPositionDiscontinuity(checkNotNull(player));
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onPositionDiscontinuity(eventTime, reason));
listener.onPositionDiscontinuity(eventTime, reason);
}
} }
@Override @Override
public final void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { public final void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onPlaybackParametersChanged(eventTime, playbackParameters); listener -> listener.onPlaybackParametersChanged(eventTime, playbackParameters));
}
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public final void onSeekProcessed() { public final void onSeekProcessed() {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onSeekProcessed(eventTime));
listener.onSeekProcessed(eventTime);
}
} }
// BandwidthMeter.Listener implementation. // BandwidthMeter.Listener implementation.
...@@ -590,9 +545,8 @@ public class AnalyticsCollector ...@@ -590,9 +545,8 @@ public class AnalyticsCollector
@Override @Override
public final void onBandwidthSample(int elapsedMs, long bytes, long bitrate) { public final void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
EventTime eventTime = generateLoadingMediaPeriodEventTime(); EventTime eventTime = generateLoadingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { listeners.sendEvent(
listener.onBandwidthEstimate(eventTime, elapsedMs, bytes, bitrate); listener -> listener.onBandwidthEstimate(eventTime, elapsedMs, bytes, bitrate));
}
} }
// DefaultDrmSessionManager.EventListener implementation. // DefaultDrmSessionManager.EventListener implementation.
...@@ -600,50 +554,38 @@ public class AnalyticsCollector ...@@ -600,50 +554,38 @@ public class AnalyticsCollector
@Override @Override
public final void onDrmSessionAcquired(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { public final void onDrmSessionAcquired(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDrmSessionAcquired(eventTime));
listener.onDrmSessionAcquired(eventTime);
}
} }
@Override @Override
public final void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { public final void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDrmKeysLoaded(eventTime));
listener.onDrmKeysLoaded(eventTime);
}
} }
@Override @Override
public final void onDrmSessionManagerError( public final void onDrmSessionManagerError(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, Exception error) { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, Exception error) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDrmSessionManagerError(eventTime, error));
listener.onDrmSessionManagerError(eventTime, error);
}
} }
@Override @Override
public final void onDrmKeysRestored(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { public final void onDrmKeysRestored(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDrmKeysRestored(eventTime));
listener.onDrmKeysRestored(eventTime);
}
} }
@Override @Override
public final void onDrmKeysRemoved(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { public final void onDrmKeysRemoved(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDrmKeysRemoved(eventTime));
listener.onDrmKeysRemoved(eventTime);
}
} }
@Override @Override
public final void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { public final void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
for (AnalyticsListener listener : listeners) { listeners.sendEvent(listener -> listener.onDrmSessionReleased(eventTime));
listener.onDrmSessionReleased(eventTime);
}
} }
// Internal methods. // Internal methods.
......
...@@ -18,6 +18,10 @@ package com.google.android.exoplayer2.analytics; ...@@ -18,6 +18,10 @@ package com.google.android.exoplayer2.analytics;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
...@@ -60,6 +64,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinit ...@@ -60,6 +64,7 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinit
import com.google.android.exoplayer2.testutil.FakeVideoRenderer; import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
...@@ -71,6 +76,8 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -71,6 +76,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mockito;
/** Integration test for {@link AnalyticsCollector}. */ /** Integration test for {@link AnalyticsCollector}. */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
...@@ -1646,6 +1653,36 @@ public final class AnalyticsCollectorTest { ...@@ -1646,6 +1653,36 @@ public final class AnalyticsCollectorTest {
} }
} }
@Test
public void recursiveListenerInvocation_arrivesInCorrectOrder() {
AnalyticsCollector analyticsCollector = new AnalyticsCollector(Clock.DEFAULT);
analyticsCollector.setPlayer(
new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build());
AnalyticsListener listener1 = mock(AnalyticsListener.class);
AnalyticsListener listener2 =
spy(
new AnalyticsListener() {
@Override
public void onPlayerError(EventTime eventTime, ExoPlaybackException error) {
analyticsCollector.onSurfaceSizeChanged(/* width= */ 0, /* height= */ 0);
}
});
AnalyticsListener listener3 = mock(AnalyticsListener.class);
analyticsCollector.addListener(listener1);
analyticsCollector.addListener(listener2);
analyticsCollector.addListener(listener3);
analyticsCollector.onPlayerError(ExoPlaybackException.createForSource(new IOException()));
InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
inOrder.verify(listener1).onPlayerError(any(), any());
inOrder.verify(listener2).onPlayerError(any(), any());
inOrder.verify(listener3).onPlayerError(any(), any());
inOrder.verify(listener1).onSurfaceSizeChanged(any(), eq(0), eq(0));
inOrder.verify(listener2).onSurfaceSizeChanged(any(), eq(0), eq(0));
inOrder.verify(listener3).onSurfaceSizeChanged(any(), eq(0), eq(0));
}
private static TestAnalyticsListener runAnalyticsTest(MediaSource mediaSource) throws Exception { private static TestAnalyticsListener runAnalyticsTest(MediaSource mediaSource) throws Exception {
return runAnalyticsTest(mediaSource, /* actionSchedule= */ null); return runAnalyticsTest(mediaSource, /* actionSchedule= */ null);
} }
......
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