Misc fixes for playback tests.

- AllowedVideoJoiningTimeMs must be set to 0 for tests so
  that tests which disable/enable video renderers don't
  register a large number of dropped frames.
- Fixed a threading issue that could cause occassional test
  failure.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=124978843
This commit is contained in:
olly 2016-06-15 12:19:43 -07:00 committed by Oliver Woodman
parent aab551e907
commit 4fe6e5d755
4 changed files with 69 additions and 37 deletions

View File

@ -38,6 +38,12 @@ public final class ExoPlayerFactory {
*/ */
public static final int DEFAULT_MIN_REBUFFER_MS = 5000; public static final int DEFAULT_MIN_REBUFFER_MS = 5000;
/**
* The default maximum duration for which a video renderer can attempt to seamlessly join an
* ongoing playback.
*/
public static final long DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS = 5000;
private ExoPlayerFactory() {} private ExoPlayerFactory() {}
/** /**
@ -49,7 +55,22 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) { public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) {
return newSimpleInstance(context, trackSelector, null, false); return newSimpleInstance(context, trackSelector, null);
}
/**
* Obtains a {@link SimpleExoPlayer} instance.
* <p>
* Must be called from a thread that has an associated {@link Looper}.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
*/
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager) {
return newSimpleInstance(context, trackSelector, drmSessionManager, false);
} }
/** /**
@ -68,7 +89,7 @@ public final class ExoPlayerFactory {
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders) { DrmSessionManager drmSessionManager, boolean preferExtensionDecoders) {
return newSimpleInstance(context, trackSelector, drmSessionManager, preferExtensionDecoders, return newSimpleInstance(context, trackSelector, drmSessionManager, preferExtensionDecoders,
DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS); DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
} }
/** /**
@ -88,12 +109,14 @@ public final class ExoPlayerFactory {
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume * @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
* after a player-invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and * after a player-invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking). * not due to a user action such as starting playback or seeking).
* @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to
* seamlessly join an ongoing playback.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs, DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs,
int minRebufferMs) { int minRebufferMs, long allowedVideoJoiningTimeMs) {
return new SimpleExoPlayer(context, trackSelector, drmSessionManager, preferExtensionDecoders, return new SimpleExoPlayer(context, trackSelector, drmSessionManager, preferExtensionDecoders,
minBufferMs, minRebufferMs); minBufferMs, minRebufferMs, allowedVideoJoiningTimeMs);
} }
/** /**

View File

@ -90,7 +90,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
} }
private static final String TAG = "SimpleExoPlayer"; private static final String TAG = "SimpleExoPlayer";
private static final long ALLOWED_VIDEO_JOINING_TIME_MS = 5000;
private static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY = 50; private static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY = 50;
private final ExoPlayer player; private final ExoPlayer player;
@ -113,7 +112,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs, DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs,
int minRebufferMs) { int minRebufferMs, long allowedVideoJoiningTimeMs) {
mainHandler = new Handler(); mainHandler = new Handler();
bandwidthMeter = new DefaultBandwidthMeter(); bandwidthMeter = new DefaultBandwidthMeter();
componentListener = new ComponentListener(); componentListener = new ComponentListener();
@ -121,11 +120,11 @@ public final class SimpleExoPlayer implements ExoPlayer {
// Build the renderers. // Build the renderers.
ArrayList<TrackRenderer> renderersList = new ArrayList<>(); ArrayList<TrackRenderer> renderersList = new ArrayList<>();
if (preferExtensionDecoders) { if (preferExtensionDecoders) {
buildExtensionRenderers(renderersList); buildExtensionRenderers(renderersList, allowedVideoJoiningTimeMs);
buildRenderers(context, drmSessionManager, renderersList); buildRenderers(context, drmSessionManager, renderersList, allowedVideoJoiningTimeMs);
} else { } else {
buildRenderers(context, drmSessionManager, renderersList); buildRenderers(context, drmSessionManager, renderersList, allowedVideoJoiningTimeMs);
buildExtensionRenderers(renderersList); buildExtensionRenderers(renderersList, allowedVideoJoiningTimeMs);
} }
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]); renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
@ -387,10 +386,10 @@ public final class SimpleExoPlayer implements ExoPlayer {
// Internal methods. // Internal methods.
private void buildRenderers(Context context, DrmSessionManager drmSessionManager, private void buildRenderers(Context context, DrmSessionManager drmSessionManager,
ArrayList<TrackRenderer> renderersList) { ArrayList<TrackRenderer> renderersList, long allowedVideoJoiningTimeMs) {
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
ALLOWED_VIDEO_JOINING_TIME_MS, drmSessionManager, false, mainHandler, componentListener, allowedVideoJoiningTimeMs, drmSessionManager, false, mainHandler, componentListener,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY); MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
renderersList.add(videoRenderer); renderersList.add(videoRenderer);
@ -407,7 +406,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
renderersList.add(id3Renderer); renderersList.add(id3Renderer);
} }
private void buildExtensionRenderers(ArrayList<TrackRenderer> renderersList) { private void buildExtensionRenderers(ArrayList<TrackRenderer> renderersList,
long allowedVideoJoiningTimeMs) {
// Load extension renderers using reflection so that demo app doesn't depend on them. // Load extension renderers using reflection so that demo app doesn't depend on them.
// Class.forName(<class name>) appears for each renderer so that automated tools like proguard // Class.forName(<class name>) appears for each renderer so that automated tools like proguard
// can detect the use of reflection (see http://proguard.sourceforge.net/FAQ.html#forname). // can detect the use of reflection (see http://proguard.sourceforge.net/FAQ.html#forname).
@ -416,7 +416,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer"); Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class, Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class,
VideoTrackRendererEventListener.class, int.class); VideoTrackRendererEventListener.class, int.class);
renderersList.add((TrackRenderer) constructor.newInstance(true, ALLOWED_VIDEO_JOINING_TIME_MS, renderersList.add((TrackRenderer) constructor.newInstance(true, allowedVideoJoiningTimeMs,
mainHandler, componentListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); mainHandler, componentListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
Log.i(TAG, "Loaded LibvpxVideoTrackRenderer."); Log.i(TAG, "Loaded LibvpxVideoTrackRenderer.");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {

View File

@ -140,6 +140,11 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
} }
} }
@Override
public final boolean canStop() {
return playerFinished;
}
@Override @Override
public final void onStop() { public final void onStop() {
actionHandler.removeCallbacksAndMessages(null); actionHandler.removeCallbacksAndMessages(null);
@ -148,11 +153,6 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
player = null; player = null;
} }
@Override
public final boolean isFinished() {
return playerFinished;
}
@Override @Override
public final void onFinished() { public final void onFinished() {
if (failOnPlayerError && playerError != null) { if (failOnPlayerError && playerError != null) {
@ -271,7 +271,8 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface, protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
DefaultTrackSelector trackSelector) { DefaultTrackSelector trackSelector) {
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector); SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector, null, false,
ExoPlayerFactory.DEFAULT_MIN_BUFFER_MS, ExoPlayerFactory.DEFAULT_MIN_REBUFFER_MS, 0);
player.setSurface(surface); player.setSurface(surface);
return player; return player;
} }

View File

@ -58,23 +58,23 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
*/ */
void onStart(HostActivity host, Surface surface); void onStart(HostActivity host, Surface surface);
/**
* Called on the main thread to check whether the test is ready to be stopped.
*
* @return True if the test is ready to be stopped. False otherwise.
*/
boolean canStop();
/** /**
* Called on the main thread when the test is stopped. * Called on the main thread when the test is stopped.
* <p> * <p>
* The test will be stopped if it has finished, if the {@link HostActivity} has been paused, or * The test will be stopped if {@link #canStop()} returns true, if the {@link HostActivity} has
* if the {@link HostActivity}'s {@link Surface} has been destroyed. * been paused, or if the {@link HostActivity}'s {@link Surface} has been destroyed.
*/ */
void onStop(); void onStop();
/** /**
* Called on the main thread to check whether the test has finished. * Called on the test thread after the test has finished and been stopped.
*
* @return True if the test has finished. False otherwise.
*/
boolean isFinished();
/**
* Called on the main thread after the test has finished and been stopped.
* <p> * <p>
* Implementations may use this method to assert that test criteria were met. * Implementations may use this method to assert that test criteria were met.
*/ */
@ -88,7 +88,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
private WifiLock wifiLock; private WifiLock wifiLock;
private SurfaceView surfaceView; private SurfaceView surfaceView;
private Handler mainHandler; private Handler mainHandler;
private CheckFinishedRunnable checkFinishedRunnable; private CheckCanStopRunnable checkCanStopRunnable;
private HostedTest hostedTest; private HostedTest hostedTest;
private ConditionVariable hostedTestStoppedCondition; private ConditionVariable hostedTestStoppedCondition;
@ -147,7 +147,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
surfaceView = (SurfaceView) findViewById(R.id.surface_view); surfaceView = (SurfaceView) findViewById(R.id.surface_view);
surfaceView.getHolder().addCallback(this); surfaceView.getHolder().addCallback(this);
mainHandler = new Handler(); mainHandler = new Handler();
checkFinishedRunnable = new CheckFinishedRunnable(); checkCanStopRunnable = new CheckCanStopRunnable();
} }
@Override @Override
@ -211,7 +211,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
hostedTestStarted = true; hostedTestStarted = true;
Log.d(TAG, "Starting test."); Log.d(TAG, "Starting test.");
hostedTest.onStart(this, surface); hostedTest.onStart(this, surface);
checkFinishedRunnable.startChecking(); checkCanStopRunnable.startChecking();
} }
} }
@ -219,8 +219,16 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
if (hostedTest != null && hostedTestStarted) { if (hostedTest != null && hostedTestStarted) {
hostedTest.onStop(); hostedTest.onStop();
hostedTest = null; hostedTest = null;
mainHandler.removeCallbacks(checkFinishedRunnable); mainHandler.removeCallbacks(checkCanStopRunnable);
hostedTestStoppedCondition.open(); // We post opening of the stopped condition so that any events posted to the main thread as a
// result of hostedTest.onStop() are guaranteed to be handled before hostedTest.onFinished()
// is invoked from runTest.
mainHandler.post(new Runnable() {
@Override
public void run() {
hostedTestStoppedCondition.open();
}
});
} }
} }
@ -229,7 +237,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
return Util.SDK_INT < 12 ? WifiManager.WIFI_MODE_FULL : WifiManager.WIFI_MODE_FULL_HIGH_PERF; return Util.SDK_INT < 12 ? WifiManager.WIFI_MODE_FULL : WifiManager.WIFI_MODE_FULL_HIGH_PERF;
} }
private final class CheckFinishedRunnable implements Runnable { private final class CheckCanStopRunnable implements Runnable {
private static final long CHECK_INTERVAL_MS = 1000; private static final long CHECK_INTERVAL_MS = 1000;
@ -239,7 +247,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
@Override @Override
public void run() { public void run() {
if (hostedTest.isFinished()) { if (hostedTest.canStop()) {
hostedTestFinished = true; hostedTestFinished = true;
maybeStopHostedTest(); maybeStopHostedTest();
} else { } else {