Commit 36ef3328 by tonihei Committed by Ian Baker

Add missing events to AnalyticsListener.

And also add a test that all Player.Listener events are forwarded
to AnalyticsListener.

The AnalyticsCollector also needlessly implemented
Video/AudioRendererEventListener, which is not needed because all of
the equivalent methods are called directly and never through the
interface.

#minor-release

PiperOrigin-RevId: 427478000
parent fd1e89df
...@@ -107,12 +107,12 @@ public class ForwardingPlayerTest { ...@@ -107,12 +107,12 @@ public class ForwardingPlayerTest {
public void forwardingPlayer_overridesAllPlayerMethods() throws Exception { public void forwardingPlayer_overridesAllPlayerMethods() throws Exception {
// Check with reflection that ForwardingPlayer overrides all Player methods. // Check with reflection that ForwardingPlayer overrides all Player methods.
List<Method> methods = getPublicMethods(Player.class); List<Method> methods = getPublicMethods(Player.class);
for (int i = 0; i < methods.size(); i++) { for (Method method : methods) {
Method method = methods.get(i);
assertThat( assertThat(
ForwardingPlayer.class.getDeclaredMethod( ForwardingPlayer.class
method.getName(), method.getParameterTypes())) .getDeclaredMethod(method.getName(), method.getParameterTypes())
.isNotNull(); .getDeclaringClass())
.isEqualTo(ForwardingPlayer.class);
} }
} }
...@@ -121,10 +121,12 @@ public class ForwardingPlayerTest { ...@@ -121,10 +121,12 @@ public class ForwardingPlayerTest {
// Check with reflection that ForwardingListener overrides all Listener methods. // Check with reflection that ForwardingListener overrides all Listener methods.
Class<?> forwardingListenerClass = getInnerClass("ForwardingListener"); Class<?> forwardingListenerClass = getInnerClass("ForwardingListener");
List<Method> methods = getPublicMethods(Player.Listener.class); List<Method> methods = getPublicMethods(Player.Listener.class);
for (int i = 0; i < methods.size(); i++) { for (Method method : methods) {
Method method = methods.get(i); assertThat(
assertThat(forwardingListenerClass.getMethod(method.getName(), method.getParameterTypes())) forwardingListenerClass
.isNotNull(); .getMethod(method.getName(), method.getParameterTypes())
.getDeclaringClass())
.isEqualTo(forwardingListenerClass);
} }
} }
......
...@@ -31,6 +31,7 @@ import android.view.Surface; ...@@ -31,6 +31,7 @@ import android.view.Surface;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DeviceInfo;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.MediaMetadata;
...@@ -54,8 +55,10 @@ import com.google.android.exoplayer2.source.LoadEventInfo; ...@@ -54,8 +55,10 @@ import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
import com.google.android.exoplayer2.util.FlagSet; import com.google.android.exoplayer2.util.FlagSet;
import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer; import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer;
import com.google.android.exoplayer2.video.VideoSize; import com.google.android.exoplayer2.video.VideoSize;
...@@ -65,6 +68,7 @@ import java.lang.annotation.Documented; ...@@ -65,6 +68,7 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.List;
/** /**
* A listener for analytics events. * A listener for analytics events.
...@@ -182,6 +186,10 @@ public interface AnalyticsListener { ...@@ -182,6 +186,10 @@ public interface AnalyticsListener {
EVENT_PLAYLIST_METADATA_CHANGED, EVENT_PLAYLIST_METADATA_CHANGED,
EVENT_SEEK_BACK_INCREMENT_CHANGED, EVENT_SEEK_BACK_INCREMENT_CHANGED,
EVENT_SEEK_FORWARD_INCREMENT_CHANGED, EVENT_SEEK_FORWARD_INCREMENT_CHANGED,
EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED,
EVENT_TRACK_SELECTION_PARAMETERS_CHANGED,
EVENT_DEVICE_INFO_CHANGED,
EVENT_DEVICE_VOLUME_CHANGED,
EVENT_LOAD_STARTED, EVENT_LOAD_STARTED,
EVENT_LOAD_COMPLETED, EVENT_LOAD_COMPLETED,
EVENT_LOAD_CANCELED, EVENT_LOAD_CANCELED,
...@@ -190,6 +198,7 @@ public interface AnalyticsListener { ...@@ -190,6 +198,7 @@ public interface AnalyticsListener {
EVENT_UPSTREAM_DISCARDED, EVENT_UPSTREAM_DISCARDED,
EVENT_BANDWIDTH_ESTIMATE, EVENT_BANDWIDTH_ESTIMATE,
EVENT_METADATA, EVENT_METADATA,
EVENT_CUES,
EVENT_AUDIO_ENABLED, EVENT_AUDIO_ENABLED,
EVENT_AUDIO_DECODER_INITIALIZED, EVENT_AUDIO_DECODER_INITIALIZED,
EVENT_AUDIO_INPUT_FORMAT_CHANGED, EVENT_AUDIO_INPUT_FORMAT_CHANGED,
...@@ -270,6 +279,8 @@ public interface AnalyticsListener { ...@@ -270,6 +279,8 @@ public interface AnalyticsListener {
/** {@link Player#getMaxSeekToPreviousPosition()} changed. */ /** {@link Player#getMaxSeekToPreviousPosition()} changed. */
int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED = int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED =
Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED; Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED;
/** {@link Player#getTrackSelectionParameters()} changed. */
int EVENT_TRACK_SELECTION_PARAMETERS_CHANGED = Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED;
/** Audio attributes changed. */ /** Audio attributes changed. */
int EVENT_AUDIO_ATTRIBUTES_CHANGED = Player.EVENT_AUDIO_ATTRIBUTES_CHANGED; int EVENT_AUDIO_ATTRIBUTES_CHANGED = Player.EVENT_AUDIO_ATTRIBUTES_CHANGED;
/** An audio session id was set. */ /** An audio session id was set. */
...@@ -289,9 +300,12 @@ public interface AnalyticsListener { ...@@ -289,9 +300,12 @@ public interface AnalyticsListener {
int EVENT_RENDERED_FIRST_FRAME = Player.EVENT_RENDERED_FIRST_FRAME; int EVENT_RENDERED_FIRST_FRAME = Player.EVENT_RENDERED_FIRST_FRAME;
/** Metadata associated with the current playback time was reported. */ /** Metadata associated with the current playback time was reported. */
int EVENT_METADATA = Player.EVENT_METADATA; int EVENT_METADATA = Player.EVENT_METADATA;
/** {@link Player#getCurrentCues()} changed. */
// TODO: Forward EVENT_CUES, EVENT_DEVICE_INFO_CHANGED and EVENT_DEVICE_VOLUME_CHANGED. int EVENT_CUES = Player.EVENT_CUES;
/** {@link Player#getDeviceInfo()} changed. */
int EVENT_DEVICE_INFO_CHANGED = Player.EVENT_DEVICE_INFO_CHANGED;
/** {@link Player#getDeviceVolume()} changed. */
int EVENT_DEVICE_VOLUME_CHANGED = Player.EVENT_DEVICE_VOLUME_CHANGED;
/** A source started loading data. */ /** A source started loading data. */
int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events
/** A source started completed loading data. */ /** A source started completed loading data. */
...@@ -682,6 +696,17 @@ public interface AnalyticsListener { ...@@ -682,6 +696,17 @@ public interface AnalyticsListener {
default void onPlayerError(EventTime eventTime, PlaybackException error) {} default void onPlayerError(EventTime eventTime, PlaybackException error) {}
/** /**
* Called when the {@link PlaybackException} returned by {@link Player#getPlayerError()} changes.
*
* <p>Implementations of Player may pass an instance of a subclass of {@link PlaybackException} to
* this method in order to include more information about the error.
*
* @param eventTime The event time.
* @param error The new error, or null if the error is being cleared.
*/
default void onPlayerErrorChanged(EventTime eventTime, @Nullable PlaybackException error) {}
/**
* Called when the available or selected tracks for the renderers changed. * Called when the available or selected tracks for the renderers changed.
* *
* @param eventTime The event time. * @param eventTime The event time.
...@@ -702,6 +727,15 @@ public interface AnalyticsListener { ...@@ -702,6 +727,15 @@ public interface AnalyticsListener {
default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {} default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {}
/** /**
* Called when track selection parameters change.
*
* @param eventTime The event time.
* @param trackSelectionParameters The new {@link TrackSelectionParameters}.
*/
default void onTrackSelectionParametersChanged(
EventTime eventTime, TrackSelectionParameters trackSelectionParameters) {}
/**
* Called when the combined {@link MediaMetadata} changes. * Called when the combined {@link MediaMetadata} changes.
* *
* <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} * <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata}
...@@ -810,6 +844,17 @@ public interface AnalyticsListener { ...@@ -810,6 +844,17 @@ public interface AnalyticsListener {
*/ */
default void onMetadata(EventTime eventTime, Metadata metadata) {} default void onMetadata(EventTime eventTime, Metadata metadata) {}
/**
* Called when there is a change in the {@link Cue Cues}.
*
* <p>{@code cues} is in ascending order of priority. If any of the cue boxes overlap when
* displayed, the {@link Cue} nearer the end of the list should be shown on top.
*
* @param eventTime The event time.
* @param cues The {@link Cue Cues}. May be empty.
*/
default void onCues(EventTime eventTime, List<Cue> cues) {}
/** @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. */ /** @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. */
@Deprecated @Deprecated
default void onDecoderEnabled( default void onDecoderEnabled(
...@@ -988,6 +1033,23 @@ public interface AnalyticsListener { ...@@ -988,6 +1033,23 @@ public interface AnalyticsListener {
default void onVolumeChanged(EventTime eventTime, float volume) {} default void onVolumeChanged(EventTime eventTime, float volume) {}
/** /**
* Called when the device information changes
*
* @param eventTime The event time.
* @param deviceInfo The new {@link DeviceInfo}.
*/
default void onDeviceInfoChanged(EventTime eventTime, DeviceInfo deviceInfo) {}
/**
* Called when the device volume or mute state changes.
*
* @param eventTime The event time.
* @param volume The new device volume, with 0 being silence and 1 being unity gain.
* @param muted Whether the device is muted.
*/
default void onDeviceVolumeChanged(EventTime eventTime, int volume, boolean muted) {}
/**
* Called when a video renderer is enabled. * Called when a video renderer is enabled.
* *
* @param eventTime The event time. * @param eventTime The event time.
......
...@@ -130,7 +130,7 @@ public interface AudioRendererEventListener { ...@@ -130,7 +130,7 @@ public interface AudioRendererEventListener {
/** /**
* Called when {@link AudioSink} has encountered an error. * Called when {@link AudioSink} has encountered an error.
* *
* <p>If the sink writes to a platform {@link AudioTrack}, this will called for all {@link * <p>If the sink writes to a platform {@link AudioTrack}, this will be called for all {@link
* AudioTrack} errors. * AudioTrack} errors.
* *
* <p>This method being called does not indicate that playback has failed, or that it will fail. * <p>This method being called does not indicate that playback has failed, or that it will fail.
......
...@@ -121,6 +121,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -121,6 +121,7 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoSize; import com.google.android.exoplayer2.video.VideoSize;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
...@@ -192,6 +193,18 @@ public final class AnalyticsCollectorTest { ...@@ -192,6 +193,18 @@ public final class AnalyticsCollectorTest {
private EventWindowAndPeriodId window1Period0Seq1; private EventWindowAndPeriodId window1Period0Seq1;
@Test @Test
public void analyticsCollector_overridesAllPlayerListenerMethods() throws Exception {
// Verify that AnalyticsCollector forwards all Player.Listener methods to AnalyticsListener.
for (Method method : Player.Listener.class.getDeclaredMethods()) {
assertThat(
AnalyticsCollector.class
.getMethod(method.getName(), method.getParameterTypes())
.getDeclaringClass())
.isEqualTo(AnalyticsCollector.class);
}
}
@Test
public void emptyTimeline() throws Exception { public void emptyTimeline() throws Exception {
FakeMediaSource mediaSource = FakeMediaSource mediaSource =
new FakeMediaSource( new FakeMediaSource(
...@@ -434,7 +447,7 @@ public final class AnalyticsCollectorTest { ...@@ -434,7 +447,7 @@ public final class AnalyticsCollectorTest {
// Wait until second period has fully loaded to assert loading events without flakiness. // Wait until second period has fully loaded to assert loading events without flakiness.
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.seek(/* windowIndex= */ 1, /* positionMs= */ 0) .seek(/* mediaItemIndex= */ 1, /* positionMs= */ 0)
.play() .play()
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
...@@ -527,7 +540,7 @@ public final class AnalyticsCollectorTest { ...@@ -527,7 +540,7 @@ public final class AnalyticsCollectorTest {
new ActionSchedule.Builder(TAG) new ActionSchedule.Builder(TAG)
.pause() .pause()
.waitForPlaybackState(Player.STATE_READY) .waitForPlaybackState(Player.STATE_READY)
.playUntilPosition(/* windowIndex= */ 0, periodDurationMs) .playUntilPosition(/* mediaItemIndex= */ 0, periodDurationMs)
.seekAndWait(/* positionMs= */ 0) .seekAndWait(/* positionMs= */ 0)
.play() .play()
.build(); .build();
...@@ -821,7 +834,7 @@ public final class AnalyticsCollectorTest { ...@@ -821,7 +834,7 @@ public final class AnalyticsCollectorTest {
.pause() .pause()
.waitForPlaybackState(Player.STATE_READY) .waitForPlaybackState(Player.STATE_READY)
// Ensure second period is already being read from. // Ensure second period is already being read from.
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ periodDurationMs) .playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ periodDurationMs)
.executeRunnable( .executeRunnable(
() -> () ->
concatenatedMediaSource.moveMediaSource( concatenatedMediaSource.moveMediaSource(
...@@ -1086,9 +1099,9 @@ public final class AnalyticsCollectorTest { ...@@ -1086,9 +1099,9 @@ public final class AnalyticsCollectorTest {
.waitForPlaybackState(Player.STATE_READY) .waitForPlaybackState(Player.STATE_READY)
// Wait in each content part to ensure previously triggered events get a chance to be // Wait in each content part to ensure previously triggered events get a chance to be
// delivered. This prevents flakiness caused by playback progressing too fast. // delivered. This prevents flakiness caused by playback progressing too fast.
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 3_000) .playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 3_000)
.waitForPendingPlayerCommands() .waitForPendingPlayerCommands()
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 8_000) .playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 8_000)
.waitForPendingPlayerCommands() .waitForPendingPlayerCommands()
.play() .play()
.waitForPlaybackState(Player.STATE_ENDED) .waitForPlaybackState(Player.STATE_ENDED)
......
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