Add EventTimes to AnalyticsListener.onEvents
The EventTime wasn't part of the onEvents callbacks so far because the individual events may have different event times (e.g. if they relate to different media periods). By adding a query method to obtain the EventTime by event type, we can solve this issue. #exofixit PiperOrigin-RevId: 343818819
This commit is contained in:
parent
7d3399764e
commit
e5e903eb8a
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.analytics;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
@ -80,6 +81,7 @@ public class AnalyticsCollector
|
||||
private final Period period;
|
||||
private final Window window;
|
||||
private final MediaPeriodQueueTracker mediaPeriodQueueTracker;
|
||||
private final SparseArray<EventTime> eventTimes;
|
||||
|
||||
private ListenerSet<AnalyticsListener, AnalyticsListener.Events> listeners;
|
||||
private @MonotonicNonNull Player player;
|
||||
@ -100,6 +102,7 @@ public class AnalyticsCollector
|
||||
period = new Period();
|
||||
window = new Window();
|
||||
mediaPeriodQueueTracker = new MediaPeriodQueueTracker(period);
|
||||
eventTimes = new SparseArray<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,7 +136,12 @@ public class AnalyticsCollector
|
||||
this.player == null || mediaPeriodQueueTracker.mediaPeriodQueue.isEmpty());
|
||||
this.player = checkNotNull(player);
|
||||
listeners =
|
||||
listeners.copy(looper, (listener, eventFlags) -> listener.onEvents(player, eventFlags));
|
||||
listeners.copy(
|
||||
looper,
|
||||
(listener, events) -> {
|
||||
events.setEventTimes(eventTimes);
|
||||
listener.onEvents(player, events);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,8 +168,8 @@ public class AnalyticsCollector
|
||||
if (!isSeeking) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
isSeeking = true;
|
||||
listeners.sendEvent(
|
||||
/* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekStarted(eventTime));
|
||||
sendEvent(
|
||||
eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekStarted(eventTime));
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,8 +183,10 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onMetadata(Metadata metadata) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
AnalyticsListener.EVENT_METADATA, listener -> listener.onMetadata(eventTime, metadata));
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_METADATA,
|
||||
listener -> listener.onMetadata(eventTime, metadata));
|
||||
}
|
||||
|
||||
// AudioRendererEventListener implementation.
|
||||
@ -185,7 +195,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onAudioEnabled(DecoderCounters counters) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_ENABLED,
|
||||
listener -> {
|
||||
listener.onAudioEnabled(eventTime, counters);
|
||||
@ -198,7 +209,8 @@ public class AnalyticsCollector
|
||||
public final void onAudioDecoderInitialized(
|
||||
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_DECODER_INITIALIZED,
|
||||
listener -> {
|
||||
listener.onAudioDecoderInitialized(eventTime, decoderName, initializationDurationMs);
|
||||
@ -212,7 +224,8 @@ public class AnalyticsCollector
|
||||
public final void onAudioInputFormatChanged(
|
||||
Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_INPUT_FORMAT_CHANGED,
|
||||
listener -> {
|
||||
listener.onAudioInputFormatChanged(eventTime, format, decoderReuseEvaluation);
|
||||
@ -223,7 +236,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_POSITION_ADVANCING,
|
||||
listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs));
|
||||
}
|
||||
@ -232,7 +246,8 @@ public class AnalyticsCollector
|
||||
public final void onAudioUnderrun(
|
||||
int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_UNDERRUN,
|
||||
listener ->
|
||||
listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs));
|
||||
@ -241,7 +256,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onAudioDecoderReleased(String decoderName) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_DECODER_RELEASED,
|
||||
listener -> listener.onAudioDecoderReleased(eventTime, decoderName));
|
||||
}
|
||||
@ -250,7 +266,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onAudioDisabled(DecoderCounters counters) {
|
||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_DISABLED,
|
||||
listener -> {
|
||||
listener.onAudioDisabled(eventTime, counters);
|
||||
@ -263,7 +280,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onAudioSessionId(int audioSessionId) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_SESSION_ID,
|
||||
listener -> listener.onAudioSessionId(eventTime, audioSessionId));
|
||||
}
|
||||
@ -271,7 +289,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED,
|
||||
listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes));
|
||||
}
|
||||
@ -279,7 +298,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED,
|
||||
listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled));
|
||||
}
|
||||
@ -287,7 +307,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public void onAudioSinkError(Exception audioSinkError) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_AUDIO_SINK_ERROR,
|
||||
listener -> listener.onAudioSinkError(eventTime, audioSinkError));
|
||||
}
|
||||
@ -295,7 +316,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public void onVolumeChanged(float audioVolume) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VOLUME_CHANGED,
|
||||
listener -> listener.onVolumeChanged(eventTime, audioVolume));
|
||||
}
|
||||
@ -306,7 +328,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onVideoEnabled(DecoderCounters counters) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_ENABLED,
|
||||
listener -> {
|
||||
listener.onVideoEnabled(eventTime, counters);
|
||||
@ -319,7 +342,8 @@ public class AnalyticsCollector
|
||||
public final void onVideoDecoderInitialized(
|
||||
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_DECODER_INITIALIZED,
|
||||
listener -> {
|
||||
listener.onVideoDecoderInitialized(eventTime, decoderName, initializationDurationMs);
|
||||
@ -333,7 +357,8 @@ public class AnalyticsCollector
|
||||
public final void onVideoInputFormatChanged(
|
||||
Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_INPUT_FORMAT_CHANGED,
|
||||
listener -> {
|
||||
listener.onVideoInputFormatChanged(eventTime, format, decoderReuseEvaluation);
|
||||
@ -344,7 +369,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onDroppedFrames(int count, long elapsedMs) {
|
||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DROPPED_VIDEO_FRAMES,
|
||||
listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs));
|
||||
}
|
||||
@ -352,7 +378,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onVideoDecoderReleased(String decoderName) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_DECODER_RELEASED,
|
||||
listener -> listener.onVideoDecoderReleased(eventTime, decoderName));
|
||||
}
|
||||
@ -361,7 +388,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onVideoDisabled(DecoderCounters counters) {
|
||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_DISABLED,
|
||||
listener -> {
|
||||
listener.onVideoDisabled(eventTime, counters);
|
||||
@ -372,7 +400,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onRenderedFirstFrame(@Nullable Surface surface) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_RENDERED_FIRST_FRAME,
|
||||
listener -> listener.onRenderedFirstFrame(eventTime, surface));
|
||||
}
|
||||
@ -380,7 +409,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
|
||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_FRAME_PROCESSING_OFFSET,
|
||||
listener ->
|
||||
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount));
|
||||
@ -397,7 +427,8 @@ public class AnalyticsCollector
|
||||
public final void onVideoSizeChanged(
|
||||
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED,
|
||||
listener ->
|
||||
listener.onVideoSizeChanged(
|
||||
@ -407,7 +438,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public void onSurfaceSizeChanged(int width, int height) {
|
||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_SURFACE_SIZE_CHANGED,
|
||||
listener -> listener.onSurfaceSizeChanged(eventTime, width, height));
|
||||
}
|
||||
@ -421,7 +453,8 @@ public class AnalyticsCollector
|
||||
LoadEventInfo loadEventInfo,
|
||||
MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_LOAD_STARTED,
|
||||
listener -> listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData));
|
||||
}
|
||||
@ -433,7 +466,8 @@ public class AnalyticsCollector
|
||||
LoadEventInfo loadEventInfo,
|
||||
MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_LOAD_COMPLETED,
|
||||
listener -> listener.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData));
|
||||
}
|
||||
@ -445,7 +479,8 @@ public class AnalyticsCollector
|
||||
LoadEventInfo loadEventInfo,
|
||||
MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_LOAD_CANCELED,
|
||||
listener -> listener.onLoadCanceled(eventTime, loadEventInfo, mediaLoadData));
|
||||
}
|
||||
@ -459,7 +494,8 @@ public class AnalyticsCollector
|
||||
IOException error,
|
||||
boolean wasCanceled) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_LOAD_ERROR,
|
||||
listener ->
|
||||
listener.onLoadError(eventTime, loadEventInfo, mediaLoadData, error, wasCanceled));
|
||||
@ -469,7 +505,8 @@ public class AnalyticsCollector
|
||||
public final void onUpstreamDiscarded(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_UPSTREAM_DISCARDED,
|
||||
listener -> listener.onUpstreamDiscarded(eventTime, mediaLoadData));
|
||||
}
|
||||
@ -478,7 +515,8 @@ public class AnalyticsCollector
|
||||
public final void onDownstreamFormatChanged(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DOWNSTREAM_FORMAT_CHANGED,
|
||||
listener -> listener.onDownstreamFormatChanged(eventTime, mediaLoadData));
|
||||
}
|
||||
@ -493,7 +531,8 @@ public class AnalyticsCollector
|
||||
public final void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) {
|
||||
mediaPeriodQueueTracker.onTimelineChanged(checkNotNull(player));
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_TIMELINE_CHANGED,
|
||||
listener -> listener.onTimelineChanged(eventTime, reason));
|
||||
}
|
||||
@ -502,7 +541,8 @@ public class AnalyticsCollector
|
||||
public final void onMediaItemTransition(
|
||||
@Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_MEDIA_ITEM_TRANSITION,
|
||||
listener -> listener.onMediaItemTransition(eventTime, mediaItem, reason));
|
||||
}
|
||||
@ -511,7 +551,8 @@ public class AnalyticsCollector
|
||||
public final void onTracksChanged(
|
||||
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_TRACKS_CHANGED,
|
||||
listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections));
|
||||
}
|
||||
@ -519,7 +560,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onStaticMetadataChanged(List<Metadata> metadataList) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_STATIC_METADATA_CHANGED,
|
||||
listener -> listener.onStaticMetadataChanged(eventTime, metadataList));
|
||||
}
|
||||
@ -527,7 +569,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onIsLoadingChanged(boolean isLoading) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_IS_LOADING_CHANGED,
|
||||
listener -> listener.onIsLoadingChanged(eventTime, isLoading));
|
||||
}
|
||||
@ -536,7 +579,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
/* eventFlag= */ C.INDEX_UNSET,
|
||||
listener -> listener.onPlayerStateChanged(eventTime, playWhenReady, playbackState));
|
||||
}
|
||||
@ -544,7 +588,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onPlaybackStateChanged(@Player.State int state) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_PLAYBACK_STATE_CHANGED,
|
||||
listener -> listener.onPlaybackStateChanged(eventTime, state));
|
||||
}
|
||||
@ -553,7 +598,8 @@ public class AnalyticsCollector
|
||||
public final void onPlayWhenReadyChanged(
|
||||
boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_PLAY_WHEN_READY_CHANGED,
|
||||
listener -> listener.onPlayWhenReadyChanged(eventTime, playWhenReady, reason));
|
||||
}
|
||||
@ -562,7 +608,8 @@ public class AnalyticsCollector
|
||||
public void onPlaybackSuppressionReasonChanged(
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED,
|
||||
listener ->
|
||||
listener.onPlaybackSuppressionReasonChanged(eventTime, playbackSuppressionReason));
|
||||
@ -571,7 +618,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public void onIsPlayingChanged(boolean isPlaying) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_IS_PLAYING_CHANGED,
|
||||
listener -> listener.onIsPlayingChanged(eventTime, isPlaying));
|
||||
}
|
||||
@ -579,7 +627,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_REPEAT_MODE_CHANGED,
|
||||
listener -> listener.onRepeatModeChanged(eventTime, repeatMode));
|
||||
}
|
||||
@ -587,7 +636,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_SHUFFLE_MODE_ENABLED_CHANGED,
|
||||
listener -> listener.onShuffleModeChanged(eventTime, shuffleModeEnabled));
|
||||
}
|
||||
@ -598,8 +648,10 @@ public class AnalyticsCollector
|
||||
error.mediaPeriodId != null
|
||||
? generateEventTime(error.mediaPeriodId)
|
||||
: generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
AnalyticsListener.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(eventTime, error));
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_PLAYER_ERROR,
|
||||
listener -> listener.onPlayerError(eventTime, error));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -609,7 +661,8 @@ public class AnalyticsCollector
|
||||
}
|
||||
mediaPeriodQueueTracker.onPositionDiscontinuity(checkNotNull(player));
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_POSITION_DISCONTINUITY,
|
||||
listener -> listener.onPositionDiscontinuity(eventTime, reason));
|
||||
}
|
||||
@ -617,7 +670,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_PLAYBACK_PARAMETERS_CHANGED,
|
||||
listener -> listener.onPlaybackParametersChanged(eventTime, playbackParameters));
|
||||
}
|
||||
@ -626,8 +680,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onSeekProcessed() {
|
||||
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
/* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime));
|
||||
sendEvent(
|
||||
eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime));
|
||||
}
|
||||
|
||||
// BandwidthMeter.Listener implementation.
|
||||
@ -635,7 +689,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
|
||||
EventTime eventTime = generateLoadingMediaPeriodEventTime();
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_BANDWIDTH_ESTIMATE,
|
||||
listener -> listener.onBandwidthEstimate(eventTime, elapsedMs, bytes, bitrate));
|
||||
}
|
||||
@ -645,7 +700,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onDrmSessionAcquired(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DRM_SESSION_ACQUIRED,
|
||||
listener -> listener.onDrmSessionAcquired(eventTime));
|
||||
}
|
||||
@ -653,15 +709,18 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
AnalyticsListener.EVENT_DRM_KEYS_LOADED, listener -> listener.onDrmKeysLoaded(eventTime));
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DRM_KEYS_LOADED,
|
||||
listener -> listener.onDrmKeysLoaded(eventTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onDrmSessionManagerError(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, Exception error) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DRM_SESSION_MANAGER_ERROR,
|
||||
listener -> listener.onDrmSessionManagerError(eventTime, error));
|
||||
}
|
||||
@ -669,7 +728,8 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onDrmKeysRestored(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DRM_KEYS_RESTORED,
|
||||
listener -> listener.onDrmKeysRestored(eventTime));
|
||||
}
|
||||
@ -677,20 +737,31 @@ public class AnalyticsCollector
|
||||
@Override
|
||||
public final void onDrmKeysRemoved(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
AnalyticsListener.EVENT_DRM_KEYS_REMOVED, listener -> listener.onDrmKeysRemoved(eventTime));
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DRM_KEYS_REMOVED,
|
||||
listener -> listener.onDrmKeysRemoved(eventTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId);
|
||||
listeners.sendEvent(
|
||||
sendEvent(
|
||||
eventTime,
|
||||
AnalyticsListener.EVENT_DRM_SESSION_RELEASED,
|
||||
listener -> listener.onDrmSessionReleased(eventTime));
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void sendEvent(
|
||||
EventTime eventTime,
|
||||
@AnalyticsListener.EventFlags int eventFlag,
|
||||
ListenerSet.Event<AnalyticsListener> eventInvocation) {
|
||||
eventTimes.put(eventFlag, eventTime);
|
||||
listeners.sendEvent(eventFlag, eventInvocation);
|
||||
}
|
||||
|
||||
/** Returns a new {@link EventTime} for the specified timeline, window and media period id. */
|
||||
@RequiresNonNull("player")
|
||||
protected EventTime generateEventTime(
|
||||
|
@ -15,7 +15,10 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.analytics;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -63,6 +66,38 @@ public interface AnalyticsListener {
|
||||
|
||||
/** A set of {@link EventFlags}. */
|
||||
final class Events extends MutableFlags {
|
||||
|
||||
private final SparseArray<EventTime> eventTimes;
|
||||
|
||||
/** Creates the set of event flags. */
|
||||
public Events() {
|
||||
eventTimes = new SparseArray<>(/* initialCapacity= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EventTime} for the specified event.
|
||||
*
|
||||
* @param event The {@link EventFlags event}.
|
||||
* @return The {@link EventTime} of this event.
|
||||
*/
|
||||
public EventTime getEventTime(@EventFlags int event) {
|
||||
return checkNotNull(eventTimes.get(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link EventTime} values for events recorded in this set.
|
||||
*
|
||||
* @param eventTimes A map from {@link EventFlags} to {@link EventTime}. Must at least contain
|
||||
* all the events recorded in this set.
|
||||
*/
|
||||
public void setEventTimes(SparseArray<EventTime> eventTimes) {
|
||||
this.eventTimes.clear();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
@EventFlags int eventFlag = get(i);
|
||||
this.eventTimes.append(eventFlag, checkNotNull(eventTimes.get(eventFlag)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given event occurred.
|
||||
*
|
||||
|
@ -8849,7 +8849,7 @@ public final class ExoPlayerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStateChangedFlags_correspondToListenerCalls() throws Exception {
|
||||
public void onEvents_correspondToListenerCalls() throws Exception {
|
||||
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||
EventListener listener = mock(EventListener.class);
|
||||
player.addListener(listener);
|
||||
@ -8897,13 +8897,14 @@ public final class ExoPlayerTest {
|
||||
assertThat(events.contains(Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED)).isTrue();
|
||||
|
||||
// Ensure all other events are called (even though we can't control how exactly they are
|
||||
// combined together in onStateChanged calls).
|
||||
// combined together in onEvents calls).
|
||||
player.prepare();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
|
||||
player.play();
|
||||
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE);
|
||||
ShadowLooper.runMainLooperToNextTask();
|
||||
player.release();
|
||||
|
||||
// Verify that all callbacks have been called at least once.
|
||||
verify(listener, atLeastOnce()).onTimelineChanged(any(), anyInt());
|
||||
@ -8920,7 +8921,7 @@ public final class ExoPlayerTest {
|
||||
verify(listener, atLeastOnce()).onIsPlayingChanged(anyBoolean());
|
||||
verify(listener, atLeastOnce()).onPlayerError(any());
|
||||
|
||||
// Verify all the same events have been recorded with onStateChanged.
|
||||
// Verify all the same events have been recorded with onEvents.
|
||||
verify(listener, atLeastOnce()).onEvents(eq(player), eventCaptor.capture());
|
||||
List<Player.Events> allEvents = eventCaptor.getAllValues();
|
||||
assertThat(containsEvent(allEvents, Player.EVENT_TIMELINE_CHANGED)).isTrue();
|
||||
|
@ -28,11 +28,19 @@ import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DR
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DRM_SESSION_RELEASED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DROPPED_VIDEO_FRAMES;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_IS_LOADING_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_IS_PLAYING_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_CANCELED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_COMPLETED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_ERROR;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_STARTED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_MEDIA_ITEM_TRANSITION;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_PLAYBACK_PARAMETERS_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_PLAYBACK_STATE_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_PLAYER_ERROR;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_PLAY_WHEN_READY_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_POSITION_DISCONTINUITY;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_RENDERED_FIRST_FRAME;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_STATIC_METADATA_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_TIMELINE_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_TRACKS_CHANGED;
|
||||
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_VIDEO_DECODER_INITIALIZED;
|
||||
@ -45,11 +53,18 @@ import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSample
|
||||
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@ -57,6 +72,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
@ -72,6 +88,7 @@ import com.google.android.exoplayer2.drm.ExoMediaDrm;
|
||||
import com.google.android.exoplayer2.drm.MediaDrmCallback;
|
||||
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
|
||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||
import com.google.android.exoplayer2.source.MediaLoadData;
|
||||
@ -89,9 +106,11 @@ import com.google.android.exoplayer2.testutil.FakeRenderer;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
|
||||
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.IOException;
|
||||
@ -103,8 +122,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
|
||||
/** Integration test for {@link AnalyticsCollector}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@ -1601,6 +1622,264 @@ public final class AnalyticsCollectorTest {
|
||||
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onEvents_isReportedWithCorrectEventTimes() throws Exception {
|
||||
SimpleExoPlayer player =
|
||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
|
||||
AnalyticsListener listener = mock(AnalyticsListener.class);
|
||||
Format[] formats =
|
||||
new Format[] {
|
||||
new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_H264).build(),
|
||||
new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).build()
|
||||
};
|
||||
player.addAnalyticsListener(listener);
|
||||
|
||||
// Trigger some simultaneous events.
|
||||
player.setMediaSource(new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), formats));
|
||||
player.seekTo(2_000);
|
||||
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2.0f));
|
||||
ShadowLooper.runMainLooperToNextTask();
|
||||
|
||||
// Move to another item and fail with a third one to trigger events with different EventTimes.
|
||||
player.prepare();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
|
||||
player.addMediaSource(new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), formats));
|
||||
player.play();
|
||||
TestPlayerRunHelper.runUntilPositionDiscontinuity(
|
||||
player, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
|
||||
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE);
|
||||
ShadowLooper.runMainLooperToNextTask();
|
||||
player.release();
|
||||
|
||||
// Verify that expected individual callbacks have been called and capture EventTimes.
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualTimelineChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onTimelineChanged(individualTimelineChangedEventTimes.capture(), anyInt());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualMediaItemTransitionEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onMediaItemTransition(individualMediaItemTransitionEventTimes.capture(), any(), anyInt());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualPositionDiscontinuityEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onPositionDiscontinuity(individualPositionDiscontinuityEventTimes.capture(), anyInt());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualPlaybackStateChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onPlaybackStateChanged(individualPlaybackStateChangedEventTimes.capture(), anyInt());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualIsLoadingChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onIsLoadingChanged(individualIsLoadingChangedEventTimes.capture(), anyBoolean());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualTracksChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onTracksChanged(individualTracksChangedEventTimes.capture(), any(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualPlayWhenReadyChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onPlayWhenReadyChanged(
|
||||
individualPlayWhenReadyChangedEventTimes.capture(), anyBoolean(), anyInt());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualIsPlayingChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onIsPlayingChanged(individualIsPlayingChangedEventTimes.capture(), anyBoolean());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualPlayerErrorEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce()).onPlayerError(individualPlayerErrorEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualPlaybackParametersChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onPlaybackParametersChanged(
|
||||
individualPlaybackParametersChangedEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualLoadStartedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onLoadStarted(individualLoadStartedEventTimes.capture(), any(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualLoadCompletedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onLoadCompleted(individualLoadCompletedEventTimes.capture(), any(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualLoadErrorEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onLoadError(individualLoadErrorEventTimes.capture(), any(), any(), any(), anyBoolean());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoEnabledEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onVideoEnabled(individualVideoEnabledEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualAudioEnabledEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onAudioEnabled(individualAudioEnabledEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualStaticMetadataChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onStaticMetadataChanged(individualStaticMetadataChangedEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualDownstreamFormatChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onDownstreamFormatChanged(individualDownstreamFormatChangedEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoInputFormatChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onVideoInputFormatChanged(
|
||||
individualVideoInputFormatChangedEventTimes.capture(), any(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualAudioInputFormatChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onAudioInputFormatChanged(
|
||||
individualAudioInputFormatChangedEventTimes.capture(), any(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoDecoderInitializedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onVideoDecoderInitialized(
|
||||
individualVideoDecoderInitializedEventTimes.capture(), any(), anyLong());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualAudioDecoderInitializedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onAudioDecoderInitialized(
|
||||
individualAudioDecoderInitializedEventTimes.capture(), any(), anyLong());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoDisabledEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onVideoDisabled(individualVideoDisabledEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualAudioDisabledEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onAudioDisabled(individualAudioDisabledEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualRenderedFirstFrameEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onRenderedFirstFrame(individualRenderedFirstFrameEventTimes.capture(), any());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoSizeChangedEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onVideoSizeChanged(
|
||||
individualVideoSizeChangedEventTimes.capture(),
|
||||
anyInt(),
|
||||
anyInt(),
|
||||
anyInt(),
|
||||
anyFloat());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualAudioPositionAdvancingEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onAudioPositionAdvancing(individualAudioPositionAdvancingEventTimes.capture(), anyLong());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoProcessingOffsetEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onVideoFrameProcessingOffset(
|
||||
individualVideoProcessingOffsetEventTimes.capture(), anyLong(), anyInt());
|
||||
ArgumentCaptor<AnalyticsListener.EventTime> individualDroppedFramesEventTimes =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
|
||||
verify(listener, atLeastOnce())
|
||||
.onDroppedVideoFrames(individualDroppedFramesEventTimes.capture(), anyInt(), anyLong());
|
||||
|
||||
// Verify the EventTimes reported with onEvents are a non-empty subset of the individual
|
||||
// callback EventTimes. We can only assert they are a non-empty subset because there may be
|
||||
// multiple events of the same type arriving in the same message queue iteration.
|
||||
ArgumentCaptor<AnalyticsListener.Events> eventsCaptor =
|
||||
ArgumentCaptor.forClass(AnalyticsListener.Events.class);
|
||||
verify(listener, atLeastOnce()).onEvents(eq(player), eventsCaptor.capture());
|
||||
SparseArray<List<AnalyticsListener.EventTime>> onEventsEventTimes = new SparseArray<>();
|
||||
for (AnalyticsListener.Events events : eventsCaptor.getAllValues()) {
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
@AnalyticsListener.EventFlags int event = events.get(i);
|
||||
if (onEventsEventTimes.get(event) == null) {
|
||||
onEventsEventTimes.put(event, new ArrayList<>());
|
||||
}
|
||||
onEventsEventTimes.get(event).add(events.getEventTime(event));
|
||||
}
|
||||
}
|
||||
// SparseArray.get returns null if the key doesn't exist, thus verifying the sets are non-empty.
|
||||
assertThat(individualTimelineChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_TIMELINE_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualMediaItemTransitionEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_MEDIA_ITEM_TRANSITION))
|
||||
.inOrder();
|
||||
assertThat(individualPositionDiscontinuityEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_POSITION_DISCONTINUITY))
|
||||
.inOrder();
|
||||
assertThat(individualPlaybackStateChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_PLAYBACK_STATE_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualIsLoadingChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_IS_LOADING_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualTracksChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_TRACKS_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualPlayWhenReadyChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_PLAY_WHEN_READY_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualIsPlayingChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_IS_PLAYING_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualPlayerErrorEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_PLAYER_ERROR))
|
||||
.inOrder();
|
||||
assertThat(individualPlaybackParametersChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_PLAYBACK_PARAMETERS_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualLoadStartedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_LOAD_STARTED))
|
||||
.inOrder();
|
||||
assertThat(individualLoadCompletedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_LOAD_COMPLETED))
|
||||
.inOrder();
|
||||
assertThat(individualLoadErrorEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_LOAD_ERROR))
|
||||
.inOrder();
|
||||
assertThat(individualVideoEnabledEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_VIDEO_ENABLED))
|
||||
.inOrder();
|
||||
assertThat(individualAudioEnabledEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_AUDIO_ENABLED))
|
||||
.inOrder();
|
||||
assertThat(individualStaticMetadataChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_STATIC_METADATA_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualDownstreamFormatChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_DOWNSTREAM_FORMAT_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualVideoInputFormatChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_VIDEO_INPUT_FORMAT_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualAudioInputFormatChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualVideoDecoderInitializedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_VIDEO_DECODER_INITIALIZED))
|
||||
.inOrder();
|
||||
assertThat(individualAudioDecoderInitializedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_AUDIO_DECODER_INITIALIZED))
|
||||
.inOrder();
|
||||
assertThat(individualVideoDisabledEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_VIDEO_DISABLED))
|
||||
.inOrder();
|
||||
assertThat(individualAudioDisabledEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_AUDIO_DISABLED))
|
||||
.inOrder();
|
||||
assertThat(individualRenderedFirstFrameEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_RENDERED_FIRST_FRAME))
|
||||
.inOrder();
|
||||
assertThat(individualVideoSizeChangedEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_VIDEO_SIZE_CHANGED))
|
||||
.inOrder();
|
||||
assertThat(individualAudioPositionAdvancingEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_AUDIO_POSITION_ADVANCING))
|
||||
.inOrder();
|
||||
assertThat(individualVideoProcessingOffsetEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
|
||||
.inOrder();
|
||||
assertThat(individualDroppedFramesEventTimes.getAllValues())
|
||||
.containsAtLeastElementsIn(onEventsEventTimes.get(EVENT_DROPPED_VIDEO_FRAMES))
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
private void populateEventIds(Timeline timeline) {
|
||||
period0 =
|
||||
new EventWindowAndPeriodId(
|
||||
|
Loading…
x
Reference in New Issue
Block a user