From 3991a6198bf9d51ea06e4189f4aad3dfae3a9de4 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 24 Jul 2017 04:16:47 -0700 Subject: [PATCH] Extend ActionSchedule with new actions. The new actions are: prepare source, set repeat mode, wait for timeline change, wait for position discontinuity, execute Runnable. Moreover, this change removes the restriction of using a SimpleExoPlayer to allow ActionSchedule to be used in other scenarios. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=162918554 --- .../android/exoplayer2/testutil/Action.java | 248 +++++++++++++++++- .../exoplayer2/testutil/ActionSchedule.java | 73 +++++- 2 files changed, 307 insertions(+), 14 deletions(-) diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java index b1c6f081cf..bbb694d6d6 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java @@ -15,11 +15,20 @@ */ package com.google.android.exoplayer2.testutil; +import android.os.Handler; import android.util.Log; import android.view.Surface; +import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.testutil.ActionSchedule.ActionNode; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; /** * Base class for actions to perform during playback tests. @@ -39,21 +48,41 @@ public abstract class Action { } /** - * Executes the action. + * Executes the action and schedules the next. * * @param player The player to which the action should be applied. * @param trackSelector The track selector to which the action should be applied. * @param surface The surface to use when applying actions. + * @param handler The handler to use to pass to the next action. + * @param nextAction The next action to schedule immediately after this action finished. */ - public final void doAction(SimpleExoPlayer player, MappingTrackSelector trackSelector, - Surface surface) { + public final void doActionAndScheduleNext(SimpleExoPlayer player, + MappingTrackSelector trackSelector, Surface surface, Handler handler, ActionNode nextAction) { Log.i(tag, description); - doActionImpl(player, trackSelector, surface); + doActionAndScheduleNextImpl(player, trackSelector, surface, handler, nextAction); } /** - * Called by {@link #doAction(SimpleExoPlayer, MappingTrackSelector, Surface)} do perform the - * action. + * Called by {@link #doActionAndScheduleNext(SimpleExoPlayer, MappingTrackSelector, Surface, + * Handler, ActionNode)} to perform the action and to schedule the next action node. + * + * @param player The player to which the action should be applied. + * @param trackSelector The track selector to which the action should be applied. + * @param surface The surface to use when applying actions. + * @param handler The handler to use to pass to the next action. + * @param nextAction The next action to schedule immediately after this action finished. + */ + protected void doActionAndScheduleNextImpl(SimpleExoPlayer player, + MappingTrackSelector trackSelector, Surface surface, Handler handler, ActionNode nextAction) { + doActionImpl(player, trackSelector, surface); + if (nextAction != null) { + nextAction.schedule(player, trackSelector, surface, handler); + } + } + + /** + * Called by {@link #doActionAndScheduleNextImpl(SimpleExoPlayer, MappingTrackSelector, Surface, + * Handler, ActionNode)} to perform the action. * * @param player The player to which the action should be applied. * @param trackSelector The track selector to which the action should be applied. @@ -63,7 +92,7 @@ public abstract class Action { Surface surface); /** - * Calls {@link ExoPlayer#seekTo(long)}. + * Calls {@link Player#seekTo(long)}. */ public static final class Seek extends Action { @@ -87,7 +116,7 @@ public abstract class Action { } /** - * Calls {@link ExoPlayer#stop()}. + * Calls {@link Player#stop()}. */ public static final class Stop extends Action { @@ -107,7 +136,7 @@ public abstract class Action { } /** - * Calls {@link ExoPlayer#setPlayWhenReady(boolean)}. + * Calls {@link Player#setPlayWhenReady(boolean)}. */ public static final class SetPlayWhenReady extends Action { @@ -197,5 +226,206 @@ public abstract class Action { } + /** + * Calls {@link ExoPlayer#prepare(MediaSource)}. + */ + public static final class PrepareSource extends Action { + + private final MediaSource mediaSource; + private final boolean resetPosition; + private final boolean resetState; + + /** + * @param tag A tag to use for logging. + */ + public PrepareSource(String tag, MediaSource mediaSource) { + this(tag, mediaSource, true, true); + } + + /** + * @param tag A tag to use for logging. + */ + public PrepareSource(String tag, MediaSource mediaSource, boolean resetPosition, + boolean resetState) { + super(tag, "PrepareSource"); + this.mediaSource = mediaSource; + this.resetPosition = resetPosition; + this.resetState = resetState; + } + + @Override + protected void doActionImpl(SimpleExoPlayer player, MappingTrackSelector trackSelector, + Surface surface) { + player.prepare(mediaSource, resetPosition, resetState); + } + + } + + /** + * Calls {@link Player#setRepeatMode(int)}. + */ + public static final class SetRepeatMode extends Action { + + private final @Player.RepeatMode int repeatMode; + + /** + * @param tag A tag to use for logging. + */ + public SetRepeatMode(String tag, @Player.RepeatMode int repeatMode) { + super(tag, "SetRepeatMode:" + repeatMode); + this.repeatMode = repeatMode; + } + + @Override + protected void doActionImpl(SimpleExoPlayer player, MappingTrackSelector trackSelector, + Surface surface) { + player.setRepeatMode(repeatMode); + } + + } + + /** + * Waits for {@link Player.EventListener#onTimelineChanged(Timeline, Object)}. + */ + public static final class WaitForTimelineChanged extends Action { + + private final Timeline expectedTimeline; + + /** + * @param tag A tag to use for logging. + */ + public WaitForTimelineChanged(String tag, Timeline expectedTimeline) { + super(tag, "WaitForTimelineChanged"); + this.expectedTimeline = expectedTimeline; + } + + @Override + protected void doActionAndScheduleNextImpl(final SimpleExoPlayer player, + final MappingTrackSelector trackSelector, final Surface surface, final Handler handler, + final ActionNode nextAction) { + PlayerListener listener = new PlayerListener() { + @Override + public void onTimelineChanged(Timeline timeline, Object manifest) { + if (timeline.equals(expectedTimeline)) { + player.removeListener(this); + nextAction.schedule(player, trackSelector, surface, handler); + } + } + }; + player.addListener(listener); + if (player.getCurrentTimeline().equals(expectedTimeline)) { + player.removeListener(listener); + nextAction.schedule(player, trackSelector, surface, handler); + } + } + + @Override + protected void doActionImpl(SimpleExoPlayer player, MappingTrackSelector trackSelector, + Surface surface) { + // Not triggered. + } + + } + + /** + * Waits for {@link Player.EventListener#onPositionDiscontinuity()}. + */ + public static final class WaitForPositionDiscontinuity extends Action { + + /** + * @param tag A tag to use for logging. + */ + public WaitForPositionDiscontinuity(String tag) { + super(tag, "WaitForPositionDiscontinuity"); + } + + @Override + protected void doActionAndScheduleNextImpl(final SimpleExoPlayer player, + final MappingTrackSelector trackSelector, final Surface surface, final Handler handler, + final ActionNode nextAction) { + player.addListener(new PlayerListener() { + @Override + public void onPositionDiscontinuity() { + player.removeListener(this); + nextAction.schedule(player, trackSelector, surface, handler); + } + }); + } + + @Override + protected void doActionImpl(SimpleExoPlayer player, MappingTrackSelector trackSelector, + Surface surface) { + // Not triggered. + } + + } + + /** + * Calls {@link Runnable#run()}. + */ + public static final class ExecuteRunnable extends Action { + + private final Runnable runnable; + + /** + * @param tag A tag to use for logging. + */ + public ExecuteRunnable(String tag, Runnable runnable) { + super(tag, "ExecuteRunnable"); + this.runnable = runnable; + } + + @Override + protected void doActionImpl(SimpleExoPlayer player, MappingTrackSelector trackSelector, + Surface surface) { + runnable.run(); + } + + } + + /** Listener implementation used for overriding. Does nothing. */ + private static class PlayerListener implements Player.EventListener { + + @Override + public void onTimelineChanged(Timeline timeline, Object manifest) { + + } + + @Override + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + + } + + @Override + public void onLoadingChanged(boolean isLoading) { + + } + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + + } + + @Override + public void onRepeatModeChanged(@ExoPlayer.RepeatMode int repeatMode) { + + } + + @Override + public void onPlayerError(ExoPlaybackException error) { + + } + + @Override + public void onPositionDiscontinuity() { + + } + + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + + } + + } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java index 66f7ebca95..4392dd9d3f 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ActionSchedule.java @@ -18,13 +18,22 @@ package com.google.android.exoplayer2.testutil; import android.os.Handler; import android.view.Surface; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.testutil.Action.ClearVideoSurface; +import com.google.android.exoplayer2.testutil.Action.ExecuteRunnable; +import com.google.android.exoplayer2.testutil.Action.PrepareSource; import com.google.android.exoplayer2.testutil.Action.Seek; import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady; import com.google.android.exoplayer2.testutil.Action.SetRendererDisabled; +import com.google.android.exoplayer2.testutil.Action.SetRepeatMode; import com.google.android.exoplayer2.testutil.Action.SetVideoSurface; import com.google.android.exoplayer2.testutil.Action.Stop; +import com.google.android.exoplayer2.testutil.Action.WaitForPositionDiscontinuity; +import com.google.android.exoplayer2.testutil.Action.WaitForTimelineChanged; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; /** @@ -179,6 +188,63 @@ public final class ActionSchedule { return apply(new SetVideoSurface(tag)); } + /** + * Schedules a new source preparation action to be executed. + * + * @return The builder, for convenience. + */ + public Builder prepareSource(MediaSource mediaSource) { + return apply(new PrepareSource(tag, mediaSource)); + } + + /** + * Schedules a new source preparation action to be executed. + * @see ExoPlayer#prepare(MediaSource, boolean, boolean). + * + * @return The builder, for convenience. + */ + public Builder prepareSource(MediaSource mediaSource, boolean resetPosition, + boolean resetState) { + return apply(new PrepareSource(tag, mediaSource, resetPosition, resetState)); + } + + /** + * Schedules a repeat mode setting action to be executed. + * + * @return The builder, for convenience. + */ + public Builder setRepeatMode(@Player.RepeatMode int repeatMode) { + return apply(new SetRepeatMode(tag, repeatMode)); + } + + /** + * Schedules a delay until the timeline changed to a specified expected timeline. + * + * @param expectedTimeline The expected timeline to wait for. + * @return The builder, for convenience. + */ + public Builder waitForTimelineChanged(Timeline expectedTimeline) { + return apply(new WaitForTimelineChanged(tag, expectedTimeline)); + } + + /** + * Schedules a delay until the next position discontinuity. + * + * @return The builder, for convenience. + */ + public Builder waitForPositionDiscontinuity() { + return apply(new WaitForPositionDiscontinuity(tag)); + } + + /** + * Schedules a {@link Runnable} to be executed. + * + * @return The builder, for convenience. + */ + public Builder executeRunnable(Runnable runnable) { + return apply(new ExecuteRunnable(tag, runnable)); + } + public ActionSchedule build() { return new ActionSchedule(rootNode); } @@ -195,7 +261,7 @@ public final class ActionSchedule { /** * Wraps an {@link Action}, allowing a delay and a next {@link Action} to be specified. */ - private static final class ActionNode implements Runnable { + /* package */ static final class ActionNode implements Runnable { private final Action action; private final long delayMs; @@ -257,10 +323,7 @@ public final class ActionSchedule { @Override public void run() { - action.doAction(player, trackSelector, surface); - if (next != null) { - next.schedule(player, trackSelector, surface, mainHandler); - } + action.doActionAndScheduleNext(player, trackSelector, surface, mainHandler, next); if (repeatIntervalMs != C.TIME_UNSET) { mainHandler.postDelayed(this, repeatIntervalMs); }