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
This commit is contained in:
tonihei 2017-07-24 04:16:47 -07:00 committed by Oliver Woodman
parent 0411add91e
commit 3991a6198b
2 changed files with 307 additions and 14 deletions

View File

@ -15,11 +15,20 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; 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.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.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
/** /**
* Base class for actions to perform during playback tests. * 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 player The player to which the action should be applied.
* @param trackSelector The track selector 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 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, public final void doActionAndScheduleNext(SimpleExoPlayer player,
Surface surface) { MappingTrackSelector trackSelector, Surface surface, Handler handler, ActionNode nextAction) {
Log.i(tag, description); Log.i(tag, description);
doActionImpl(player, trackSelector, surface); doActionAndScheduleNextImpl(player, trackSelector, surface, handler, nextAction);
} }
/** /**
* Called by {@link #doAction(SimpleExoPlayer, MappingTrackSelector, Surface)} do perform the * Called by {@link #doActionAndScheduleNext(SimpleExoPlayer, MappingTrackSelector, Surface,
* action. * 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 player The player to which the action should be applied.
* @param trackSelector The track selector 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); Surface surface);
/** /**
* Calls {@link ExoPlayer#seekTo(long)}. * Calls {@link Player#seekTo(long)}.
*/ */
public static final class Seek extends Action { 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 { 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 { 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) {
}
}
} }

View File

@ -18,13 +18,22 @@ package com.google.android.exoplayer2.testutil;
import android.os.Handler; import android.os.Handler;
import android.view.Surface; import android.view.Surface;
import com.google.android.exoplayer2.C; 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.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.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.Seek;
import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady; import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady;
import com.google.android.exoplayer2.testutil.Action.SetRendererDisabled; 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.SetVideoSurface;
import com.google.android.exoplayer2.testutil.Action.Stop; 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; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
/** /**
@ -179,6 +188,63 @@ public final class ActionSchedule {
return apply(new SetVideoSurface(tag)); 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() { public ActionSchedule build() {
return new ActionSchedule(rootNode); 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. * 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 Action action;
private final long delayMs; private final long delayMs;
@ -257,10 +323,7 @@ public final class ActionSchedule {
@Override @Override
public void run() { public void run() {
action.doAction(player, trackSelector, surface); action.doActionAndScheduleNext(player, trackSelector, surface, mainHandler, next);
if (next != null) {
next.schedule(player, trackSelector, surface, mainHandler);
}
if (repeatIntervalMs != C.TIME_UNSET) { if (repeatIntervalMs != C.TIME_UNSET) {
mainHandler.postDelayed(this, repeatIntervalMs); mainHandler.postDelayed(this, repeatIntervalMs);
} }