mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add playback tests to V2.
DashTest will be migrated separately, since it's a little more work. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=124722462
This commit is contained in:
parent
0978227a84
commit
5cd7deffe7
13 changed files with 1355 additions and 1 deletions
|
|
@ -447,7 +447,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushCodec() throws ExoPlaybackException {
|
protected void flushCodec() throws ExoPlaybackException {
|
||||||
codecHotswapDeadlineMs = -1;
|
codecHotswapDeadlineMs = -1;
|
||||||
inputIndex = -1;
|
inputIndex = -1;
|
||||||
outputIndex = -1;
|
outputIndex = -1;
|
||||||
|
|
|
||||||
38
playbacktests/build.gradle
Normal file
38
playbacktests/build.gradle
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (C) 2014 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 23
|
||||||
|
buildToolsVersion "23.0.1"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
targetSdkVersion 23
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':library')
|
||||||
|
}
|
||||||
43
playbacktests/src/main/AndroidManifest.xml
Normal file
43
playbacktests/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.google.android.exoplayer.playbacktests"
|
||||||
|
android:versionCode="1508"
|
||||||
|
android:versionName="1.5.8">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||||
|
|
||||||
|
<application android:debuggable="true"
|
||||||
|
android:allowBackup="false"
|
||||||
|
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||||
|
<uses-library android:name="android.test.runner"/>
|
||||||
|
|
||||||
|
<activity android:name="com.google.android.exoplayer.playbacktests.util.HostActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="ExoPlayerTest"/>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<instrumentation
|
||||||
|
android:targetPackage="com.google.android.exoplayer.playbacktests"
|
||||||
|
android:name="android.test.InstrumentationTestRunner"/>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||||
|
import com.google.android.exoplayer.ExoPlayer;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for actions to perform during playback tests.
|
||||||
|
*/
|
||||||
|
public abstract class Action {
|
||||||
|
|
||||||
|
private final String tag;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
* @param description A description to be logged when the action is executed.
|
||||||
|
*/
|
||||||
|
public Action(String tag, String description) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes 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.
|
||||||
|
*/
|
||||||
|
public final void doAction(ExoPlayer player, DefaultTrackSelector trackSelector) {
|
||||||
|
Log.i(tag, description);
|
||||||
|
doActionImpl(player, trackSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@link #doAction(ExoPlayer, DefaultTrackSelector)} do actually 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.
|
||||||
|
*/
|
||||||
|
protected abstract void doActionImpl(ExoPlayer player, DefaultTrackSelector trackSelector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link ExoPlayer#seekTo(long)}.
|
||||||
|
*/
|
||||||
|
public static final class Seek extends Action {
|
||||||
|
|
||||||
|
private final long positionMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
* @param positionMs The seek position.
|
||||||
|
*/
|
||||||
|
public Seek(String tag, long positionMs) {
|
||||||
|
super(tag, "Seek:" + positionMs);
|
||||||
|
this.positionMs = positionMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doActionImpl(ExoPlayer player, DefaultTrackSelector trackSelector) {
|
||||||
|
player.seekTo(positionMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link ExoPlayer#stop()}.
|
||||||
|
*/
|
||||||
|
public static final class Stop extends Action {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
*/
|
||||||
|
public Stop(String tag) {
|
||||||
|
super(tag, "Stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doActionImpl(ExoPlayer player, DefaultTrackSelector trackSelector) {
|
||||||
|
player.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link ExoPlayer#setPlayWhenReady(boolean)}.
|
||||||
|
*/
|
||||||
|
public static final class SetPlayWhenReady extends Action {
|
||||||
|
|
||||||
|
private final boolean playWhenReady;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
* @param playWhenReady The value to pass.
|
||||||
|
*/
|
||||||
|
public SetPlayWhenReady(String tag, boolean playWhenReady) {
|
||||||
|
super(tag, playWhenReady ? "Play" : "Pause");
|
||||||
|
this.playWhenReady = playWhenReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doActionImpl(ExoPlayer player, DefaultTrackSelector trackSelector) {
|
||||||
|
player.setPlayWhenReady(playWhenReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link DefaultTrackSelector#setRendererDisabled(int, boolean)}.
|
||||||
|
*/
|
||||||
|
public static final class SetRendererDisabled extends Action {
|
||||||
|
|
||||||
|
private final int rendererIndex;
|
||||||
|
private final boolean disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
* @param rendererIndex The index of the renderer.
|
||||||
|
* @param disabled Whether the renderer should be disabled.
|
||||||
|
*/
|
||||||
|
public SetRendererDisabled(String tag, int rendererIndex, boolean disabled) {
|
||||||
|
super(tag, "SetRendererDisabled:" + rendererIndex + ":" + disabled);
|
||||||
|
this.rendererIndex = rendererIndex;
|
||||||
|
this.disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doActionImpl(ExoPlayer player, DefaultTrackSelector trackSelector) {
|
||||||
|
trackSelector.setRendererDisabled(rendererIndex, disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||||
|
import com.google.android.exoplayer.ExoPlayer;
|
||||||
|
import com.google.android.exoplayer.playbacktests.util.Action.Seek;
|
||||||
|
import com.google.android.exoplayer.playbacktests.util.Action.SetPlayWhenReady;
|
||||||
|
import com.google.android.exoplayer.playbacktests.util.Action.SetRendererDisabled;
|
||||||
|
import com.google.android.exoplayer.playbacktests.util.Action.Stop;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a sequence of {@link Action}s for execution during a test.
|
||||||
|
*/
|
||||||
|
public final class ActionSchedule {
|
||||||
|
|
||||||
|
private final ActionNode rootNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param rootNode The first node in the sequence.
|
||||||
|
*/
|
||||||
|
private ActionSchedule(ActionNode rootNode) {
|
||||||
|
this.rootNode = rootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts execution of the schedule.
|
||||||
|
*
|
||||||
|
* @param player The player to which actions should be applied.
|
||||||
|
* @param trackSelector The track selector to which actions should be applied.
|
||||||
|
* @param mainHandler A handler associated with the main thread of the host activity.
|
||||||
|
*/
|
||||||
|
/* package */ void start(ExoPlayer player, DefaultTrackSelector trackSelector,
|
||||||
|
Handler mainHandler) {
|
||||||
|
rootNode.schedule(player, trackSelector, mainHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link ActionSchedule} instances.
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private final String tag;
|
||||||
|
private final ActionNode rootNode;
|
||||||
|
private long currentDelayMs;
|
||||||
|
|
||||||
|
private ActionNode previousNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
*/
|
||||||
|
public Builder(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
rootNode = new ActionNode(new RootAction(tag), 0);
|
||||||
|
previousNode = rootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a delay between executing any previous actions and any subsequent ones.
|
||||||
|
*
|
||||||
|
* @param delayMs The delay in milliseconds.
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder delay(long delayMs) {
|
||||||
|
currentDelayMs += delayMs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules an action to be executed.
|
||||||
|
*
|
||||||
|
* @param action The action to schedule.
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder apply(Action action) {
|
||||||
|
ActionNode next = new ActionNode(action, currentDelayMs);
|
||||||
|
previousNode.setNext(next);
|
||||||
|
previousNode = next;
|
||||||
|
currentDelayMs = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a seek action to be executed.
|
||||||
|
*
|
||||||
|
* @param positionMs The seek position.
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder seek(long positionMs) {
|
||||||
|
return apply(new Seek(tag, positionMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a stop action to be executed.
|
||||||
|
*
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder stop() {
|
||||||
|
return apply(new Stop(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a play action to be executed.
|
||||||
|
*
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder play() {
|
||||||
|
return apply(new SetPlayWhenReady(tag, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a pause action to be executed.
|
||||||
|
*
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder pause() {
|
||||||
|
return apply(new SetPlayWhenReady(tag, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a renderer enable action to be executed.
|
||||||
|
*
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder enableRenderer(int index) {
|
||||||
|
return apply(new SetRendererDisabled(tag, index, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a renderer disable action to be executed.
|
||||||
|
*
|
||||||
|
* @return The builder, for convenience.
|
||||||
|
*/
|
||||||
|
public Builder disableRenderer(int index) {
|
||||||
|
return apply(new SetRendererDisabled(tag, index, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionSchedule build() {
|
||||||
|
return new ActionSchedule(rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an {@link Action}, allowing a delay and a next {@link Action} to be specified.
|
||||||
|
*/
|
||||||
|
private static final class ActionNode implements Runnable {
|
||||||
|
|
||||||
|
private final Action action;
|
||||||
|
private final long delayMs;
|
||||||
|
|
||||||
|
private ActionNode next;
|
||||||
|
|
||||||
|
private ExoPlayer player;
|
||||||
|
private DefaultTrackSelector trackSelector;
|
||||||
|
private Handler mainHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param action The wrapped action.
|
||||||
|
* @param delayMs The delay between the node being scheduled and the action being executed.
|
||||||
|
*/
|
||||||
|
public ActionNode(Action action, long delayMs) {
|
||||||
|
this.action = action;
|
||||||
|
this.delayMs = delayMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the next action.
|
||||||
|
*
|
||||||
|
* @param next The next {@link Action}.
|
||||||
|
*/
|
||||||
|
public void setNext(ActionNode next) {
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules {@link #action} to be executed after {@link #delayMs}. The {@link #next} node
|
||||||
|
* will be scheduled immediately after {@link #action} is executed.
|
||||||
|
*
|
||||||
|
* @param player The player to which actions should be applied.
|
||||||
|
* @param trackSelector The track selector to which actions should be applied.
|
||||||
|
* @param mainHandler A handler associated with the main thread of the host activity.
|
||||||
|
*/
|
||||||
|
public void schedule(ExoPlayer player, DefaultTrackSelector trackSelector,
|
||||||
|
Handler mainHandler) {
|
||||||
|
this.player = player;
|
||||||
|
this.trackSelector = trackSelector;
|
||||||
|
this.mainHandler = mainHandler;
|
||||||
|
mainHandler.postDelayed(this, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
action.doAction(player, trackSelector);
|
||||||
|
if (next != null) {
|
||||||
|
next.schedule(player, trackSelector, mainHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A no-op root action.
|
||||||
|
*/
|
||||||
|
private static final class RootAction extends Action {
|
||||||
|
|
||||||
|
public RootAction(String tag) {
|
||||||
|
super(tag, "Root");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doActionImpl(ExoPlayer player, DefaultTrackSelector trackSelector) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.CodecCounters;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assertions for {@link CodecCounters}.
|
||||||
|
*/
|
||||||
|
public final class CodecCountersUtil {
|
||||||
|
|
||||||
|
private CodecCountersUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of the skipped, dropped and rendered buffers.
|
||||||
|
*
|
||||||
|
* @param counters The counters for which the total should be calculated.
|
||||||
|
* @return The sum of the skipped, dropped and rendered buffers.
|
||||||
|
*/
|
||||||
|
public static int getTotalOutputBuffers(CodecCounters counters) {
|
||||||
|
return counters.skippedOutputBufferCount + counters.droppedOutputBufferCount
|
||||||
|
+ counters.renderedOutputBufferCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertSkippedOutputBufferCount(String name, CodecCounters counters,
|
||||||
|
int expected) {
|
||||||
|
counters.ensureUpdated();
|
||||||
|
int actual = counters.skippedOutputBufferCount;
|
||||||
|
TestCase.assertEquals("Codec(" + name + ") skipped " + actual + " buffers. Expected "
|
||||||
|
+ expected + ".", expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertTotalOutputBufferCount(String name, CodecCounters counters,
|
||||||
|
int minCount, int maxCount) {
|
||||||
|
counters.ensureUpdated();
|
||||||
|
int actual = getTotalOutputBuffers(counters);
|
||||||
|
TestCase.assertTrue("Codec(" + name + ") output " + actual + " buffers. Expected in range ["
|
||||||
|
+ minCount + ", " + maxCount + "].", minCount <= actual && actual <= maxCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertDroppedOutputBufferLimit(String name, CodecCounters counters,
|
||||||
|
int limit) {
|
||||||
|
counters.ensureUpdated();
|
||||||
|
int actual = counters.droppedOutputBufferCount;
|
||||||
|
TestCase.assertTrue("Codec(" + name + ") was late decoding: " + actual + " buffers. "
|
||||||
|
+ "Limit: " + limit + ".", actual <= limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertConsecutiveDroppedOutputBufferLimit(String name, CodecCounters counters,
|
||||||
|
int limit) {
|
||||||
|
counters.ensureUpdated();
|
||||||
|
int actual = counters.maxConsecutiveDroppedOutputBufferCount;
|
||||||
|
TestCase.assertTrue("Codec(" + name + ") was late decoding: " + actual
|
||||||
|
+ " buffers consecutively. " + "Limit: " + limit + ".", actual <= limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.DecoderInputBuffer;
|
||||||
|
import com.google.android.exoplayer.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer.MediaCodecSelector;
|
||||||
|
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
||||||
|
import com.google.android.exoplayer.VideoTrackRendererEventListener;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes and renders video using {@link MediaCodecVideoTrackRenderer}. Provides buffer timestamp
|
||||||
|
* assertions.
|
||||||
|
*/
|
||||||
|
@TargetApi(16)
|
||||||
|
public class DebugMediaCodecVideoTrackRenderer extends MediaCodecVideoTrackRenderer {
|
||||||
|
|
||||||
|
private static final int ARRAY_SIZE = 1000;
|
||||||
|
|
||||||
|
private final long[] timestampsList = new long[ARRAY_SIZE];
|
||||||
|
|
||||||
|
private int startIndex;
|
||||||
|
private int queueSize;
|
||||||
|
private int bufferCount;
|
||||||
|
|
||||||
|
public DebugMediaCodecVideoTrackRenderer(Context context, MediaCodecSelector mediaCodecSelector,
|
||||||
|
int videoScalingMode, long allowedJoiningTimeMs, Handler eventHandler,
|
||||||
|
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||||
|
super(context, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, false,
|
||||||
|
eventHandler, eventListener, maxDroppedFrameCountToNotify);
|
||||||
|
startIndex = 0;
|
||||||
|
queueSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void releaseCodec() {
|
||||||
|
super.releaseCodec();
|
||||||
|
clearTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void flushCodec() throws ExoPlaybackException {
|
||||||
|
super.flushCodec();
|
||||||
|
clearTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onQueueInputBuffer(DecoderInputBuffer buffer) {
|
||||||
|
insertTimestamp(buffer.timeUs);
|
||||||
|
maybeShiftTimestampsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProcessedOutputBuffer(long presentationTimeUs) {
|
||||||
|
bufferCount++;
|
||||||
|
long expectedTimestampUs = dequeueTimestamp();
|
||||||
|
if (expectedTimestampUs != presentationTimeUs) {
|
||||||
|
throw new IllegalStateException("Expected to dequeue video buffer with presentation "
|
||||||
|
+ "timestamp: " + expectedTimestampUs + ". Instead got: " + presentationTimeUs
|
||||||
|
+ " (Processed buffers since last flush: " + bufferCount + ").");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearTimestamps() {
|
||||||
|
startIndex = 0;
|
||||||
|
queueSize = 0;
|
||||||
|
bufferCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertTimestamp(long presentationTimeUs) {
|
||||||
|
for (int i = startIndex + queueSize - 1; i >= startIndex; i--) {
|
||||||
|
if (presentationTimeUs >= timestampsList[i]) {
|
||||||
|
timestampsList[i + 1] = presentationTimeUs;
|
||||||
|
queueSize++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timestampsList[i + 1] = timestampsList[i];
|
||||||
|
}
|
||||||
|
timestampsList[startIndex] = presentationTimeUs;
|
||||||
|
queueSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeShiftTimestampsList() {
|
||||||
|
if (startIndex + queueSize == ARRAY_SIZE) {
|
||||||
|
System.arraycopy(timestampsList, startIndex, timestampsList, 0, queueSize);
|
||||||
|
startIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long dequeueTimestamp() {
|
||||||
|
startIndex++;
|
||||||
|
queueSize--;
|
||||||
|
return timestampsList[startIndex - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.CodecCounters;
|
||||||
|
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||||
|
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||||
|
import com.google.android.exoplayer.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer.ExoPlayer;
|
||||||
|
import com.google.android.exoplayer.ExoPlayerFactory;
|
||||||
|
import com.google.android.exoplayer.Format;
|
||||||
|
import com.google.android.exoplayer.SampleSource;
|
||||||
|
import com.google.android.exoplayer.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer.TrackSelectionPolicy;
|
||||||
|
import com.google.android.exoplayer.audio.AudioTrack;
|
||||||
|
import com.google.android.exoplayer.playbacktests.util.HostActivity.HostedTest;
|
||||||
|
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||||
|
import com.google.android.exoplayer.upstream.DataSourceFactory;
|
||||||
|
import com.google.android.exoplayer.upstream.DefaultDataSourceFactory;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Surface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link HostedTest} for {@link ExoPlayer} playback tests.
|
||||||
|
*/
|
||||||
|
public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListener,
|
||||||
|
SimpleExoPlayer.DebugListener {
|
||||||
|
|
||||||
|
static {
|
||||||
|
// ExoPlayer's AudioTrack class is able to work around spurious timestamps reported by the
|
||||||
|
// platform (by ignoring them). Disable this workaround, since we're interested in testing
|
||||||
|
// that the underlying platform is behaving correctly.
|
||||||
|
AudioTrack.failOnSpuriousAudioTimestamp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String tag;
|
||||||
|
private final boolean failOnPlayerError;
|
||||||
|
|
||||||
|
private ActionSchedule pendingSchedule;
|
||||||
|
private Handler actionHandler;
|
||||||
|
private DefaultTrackSelector trackSelector;
|
||||||
|
private SimpleExoPlayer player;
|
||||||
|
private ExoPlaybackException playerError;
|
||||||
|
private boolean playerWasPrepared;
|
||||||
|
private boolean playerFinished;
|
||||||
|
private boolean playing;
|
||||||
|
private long totalPlayingTimeMs;
|
||||||
|
private long lastPlayingStartTimeMs;
|
||||||
|
|
||||||
|
private CodecCounters videoCodecCounters;
|
||||||
|
private CodecCounters audioCodecCounters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a test that fails if a player error occurs.
|
||||||
|
*
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
*/
|
||||||
|
public ExoHostedTest(String tag) {
|
||||||
|
this(tag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
* @param failOnPlayerError True if a player error should be considered a test failure. False
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public ExoHostedTest(String tag, boolean failOnPlayerError) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.failOnPlayerError = failOnPlayerError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a schedule to be applied during the test.
|
||||||
|
*
|
||||||
|
* @param schedule The schedule.
|
||||||
|
*/
|
||||||
|
public final void setSchedule(ActionSchedule schedule) {
|
||||||
|
if (player == null) {
|
||||||
|
pendingSchedule = schedule;
|
||||||
|
} else {
|
||||||
|
schedule.start(player, trackSelector, actionHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostedTest implementation
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onStart(HostActivity host, Surface surface) {
|
||||||
|
// Build the player.
|
||||||
|
TrackSelectionPolicy trackSelectionPolicy = buildTrackSelectionPolicy(host);
|
||||||
|
trackSelector = new DefaultTrackSelector(trackSelectionPolicy, null);
|
||||||
|
player = buildExoPlayer(host, surface, trackSelector);
|
||||||
|
DataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(host, Util
|
||||||
|
.getUserAgent(host, "ExoPlayerPlaybackTests"));
|
||||||
|
player.setSource(buildSource(host, dataSourceFactory, player.getBandwidthMeter()));
|
||||||
|
player.addListener(this);
|
||||||
|
player.setDebugListener(this);
|
||||||
|
player.setPlayWhenReady(true);
|
||||||
|
actionHandler = new Handler();
|
||||||
|
// Schedule any pending actions.
|
||||||
|
if (pendingSchedule != null) {
|
||||||
|
pendingSchedule.start(player, trackSelector, actionHandler);
|
||||||
|
pendingSchedule = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onStop() {
|
||||||
|
actionHandler.removeCallbacksAndMessages(null);
|
||||||
|
player.release();
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isFinished() {
|
||||||
|
return playerFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onFinished() {
|
||||||
|
if (failOnPlayerError && playerError != null) {
|
||||||
|
throw new Error(playerError);
|
||||||
|
}
|
||||||
|
logMetrics();
|
||||||
|
assertPassed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExoPlayer.Listener
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
|
Log.d(tag, "state [" + playWhenReady + ", " + playbackState + "]");
|
||||||
|
playerWasPrepared |= playbackState != ExoPlayer.STATE_IDLE;
|
||||||
|
if (playbackState == ExoPlayer.STATE_ENDED
|
||||||
|
|| (playbackState == ExoPlayer.STATE_IDLE && playerWasPrepared)) {
|
||||||
|
playerFinished = true;
|
||||||
|
}
|
||||||
|
boolean playing = playWhenReady && playbackState == ExoPlayer.STATE_READY;
|
||||||
|
if (!this.playing && playing) {
|
||||||
|
lastPlayingStartTimeMs = SystemClock.elapsedRealtime();
|
||||||
|
} else if (this.playing && !playing) {
|
||||||
|
totalPlayingTimeMs += SystemClock.elapsedRealtime() - lastPlayingStartTimeMs;
|
||||||
|
}
|
||||||
|
this.playing = playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onPlayerError(ExoPlaybackException error) {
|
||||||
|
playerWasPrepared = true;
|
||||||
|
playerError = error;
|
||||||
|
onPlayerErrorInternal(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onPlayWhenReadyCommitted() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleExoPlayer.DebugListener
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioEnabled(CodecCounters counters) {
|
||||||
|
Log.d(tag, "audioEnabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||||
|
long initializationDurationMs) {
|
||||||
|
Log.d(tag, "audioDecoderInitialized [" + decoderName + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioFormatChanged(Format format) {
|
||||||
|
Log.d(tag, "audioFormatChanged [" + format.id + "]");
|
||||||
|
if (format != null) {
|
||||||
|
audioCodecCounters = player.getVideoCodecCounters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioDisabled(CodecCounters counters) {
|
||||||
|
Log.d(tag, "audioDisabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoEnabled(CodecCounters counters) {
|
||||||
|
Log.d(tag, "videoEnabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||||
|
long initializationDurationMs) {
|
||||||
|
Log.d(tag, "videoDecoderInitialized [" + decoderName + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoFormatChanged(Format format) {
|
||||||
|
Log.d(tag, "videoFormatChanged [" + format.id + "]");
|
||||||
|
if (format != null) {
|
||||||
|
videoCodecCounters = player.getVideoCodecCounters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoDisabled(CodecCounters counters) {
|
||||||
|
Log.d(tag, "videoDisabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDroppedFrames(int count, long elapsed) {
|
||||||
|
Log.d(tag, "droppedFrames [" + count + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||||
|
Log.e(tag, "audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", "
|
||||||
|
+ elapsedSinceLastFeedMs + "]", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal logic
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected TrackSelectionPolicy buildTrackSelectionPolicy(HostActivity host) {
|
||||||
|
return new DefaultTrackSelectionPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
|
||||||
|
DefaultTrackSelector trackSelector) {
|
||||||
|
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector);
|
||||||
|
player.setSurface(surface);
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected abstract SampleSource buildSource(HostActivity host,
|
||||||
|
DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter);
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected void onPlayerErrorInternal(ExoPlaybackException error) {
|
||||||
|
// Do nothing. Interested subclasses may override.
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertPassed() {
|
||||||
|
// Do nothing. Subclasses may override to add additional assertions.
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void logMetrics() {
|
||||||
|
// Do nothing. Subclasses may override to log metrics.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility methods and actions for subclasses.
|
||||||
|
|
||||||
|
protected final long getTotalPlayingTimeMs() {
|
||||||
|
return totalPlayingTimeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final ExoPlaybackException getError() {
|
||||||
|
return playerError;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final CodecCounters getLastVideoCodecCounters() {
|
||||||
|
if (videoCodecCounters != null) {
|
||||||
|
videoCodecCounters.ensureUpdated();
|
||||||
|
}
|
||||||
|
return videoCodecCounters;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final CodecCounters getLastAudioCodecCounters() {
|
||||||
|
if (audioCodecCounters != null) {
|
||||||
|
audioCodecCounters.ensureUpdated();
|
||||||
|
}
|
||||||
|
return audioCodecCounters;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.fail;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.playbacktests.R;
|
||||||
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.net.wifi.WifiManager.WifiLock;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ConditionVariable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.os.PowerManager.WakeLock;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.Window;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A host activity for performing playback tests.
|
||||||
|
*/
|
||||||
|
public final class HostActivity extends Activity implements SurfaceHolder.Callback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for tests that run inside of a {@link HostActivity}.
|
||||||
|
*/
|
||||||
|
public interface HostedTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on the main thread when the test is started.
|
||||||
|
* <p>
|
||||||
|
* The test will not be started until the {@link HostActivity} has been resumed and its
|
||||||
|
* {@link Surface} has been created.
|
||||||
|
*
|
||||||
|
* @param host The {@link HostActivity} in which the test is being run.
|
||||||
|
* @param surface The {@link Surface}.
|
||||||
|
*/
|
||||||
|
void onStart(HostActivity host, Surface surface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on the main thread when the test is stopped.
|
||||||
|
* <p>
|
||||||
|
* The test will be stopped if it has finished, if the {@link HostActivity} has been paused, or
|
||||||
|
* if the {@link HostActivity}'s {@link Surface} has been destroyed.
|
||||||
|
*/
|
||||||
|
void onStop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on the main thread to check whether the test has finished.
|
||||||
|
*
|
||||||
|
* @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>
|
||||||
|
* Implementations may use this method to assert that test criteria were met.
|
||||||
|
*/
|
||||||
|
void onFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "HostActivity";
|
||||||
|
|
||||||
|
private WakeLock wakeLock;
|
||||||
|
private WifiLock wifiLock;
|
||||||
|
private SurfaceView surfaceView;
|
||||||
|
private Handler mainHandler;
|
||||||
|
private CheckFinishedRunnable checkFinishedRunnable;
|
||||||
|
|
||||||
|
private HostedTest hostedTest;
|
||||||
|
private ConditionVariable hostedTestStoppedCondition;
|
||||||
|
private boolean hostedTestStarted;
|
||||||
|
private boolean hostedTestFinished;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a {@link HostedTest} inside the host.
|
||||||
|
*
|
||||||
|
* @param hostedTest The test to execute.
|
||||||
|
* @param timeoutMs The number of milliseconds to wait for the test to finish. If the timeout
|
||||||
|
* is exceeded then the test will fail.
|
||||||
|
*/
|
||||||
|
public void runTest(final HostedTest hostedTest, long timeoutMs) {
|
||||||
|
Assertions.checkArgument(timeoutMs > 0);
|
||||||
|
Assertions.checkState(Thread.currentThread() != getMainLooper().getThread());
|
||||||
|
|
||||||
|
Assertions.checkState(this.hostedTest == null);
|
||||||
|
this.hostedTest = Assertions.checkNotNull(hostedTest);
|
||||||
|
hostedTestStoppedCondition = new ConditionVariable();
|
||||||
|
hostedTestStarted = false;
|
||||||
|
hostedTestFinished = false;
|
||||||
|
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
maybeStartHostedTest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hostedTestStoppedCondition.block(timeoutMs)) {
|
||||||
|
if (hostedTestFinished) {
|
||||||
|
Log.d(TAG, "Test finished. Checking pass conditions.");
|
||||||
|
hostedTest.onFinished();
|
||||||
|
Log.d(TAG, "Pass conditions checked.");
|
||||||
|
} else {
|
||||||
|
String message = "Test released before it finished. Activity may have been paused whilst "
|
||||||
|
+ "test was in progress.";
|
||||||
|
Log.e(TAG, message);
|
||||||
|
fail(message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String message = "Test timed out after " + timeoutMs + " ms.";
|
||||||
|
Log.e(TAG, message);
|
||||||
|
fail(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity lifecycle
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
setContentView(R.layout.host_activity);
|
||||||
|
surfaceView = (SurfaceView) findViewById(R.id.surface_view);
|
||||||
|
surfaceView.getHolder().addCallback(this);
|
||||||
|
mainHandler = new Handler();
|
||||||
|
checkFinishedRunnable = new CheckFinishedRunnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
Context appContext = getApplicationContext();
|
||||||
|
WifiManager wifiManager = (WifiManager) appContext.getSystemService(Context.WIFI_SERVICE);
|
||||||
|
wifiLock = wifiManager.createWifiLock(getWifiLockMode(), TAG);
|
||||||
|
wifiLock.acquire();
|
||||||
|
PowerManager powerManager = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
|
||||||
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||||
|
wakeLock.acquire();
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
maybeStartHostedTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
maybeStopHostedTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
wakeLock.release();
|
||||||
|
wakeLock = null;
|
||||||
|
wifiLock.release();
|
||||||
|
wifiLock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SurfaceHolder.Callback
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
maybeStartHostedTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
maybeStopHostedTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal logic
|
||||||
|
|
||||||
|
private void maybeStartHostedTest() {
|
||||||
|
if (hostedTest == null || hostedTestStarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Surface surface = surfaceView.getHolder().getSurface();
|
||||||
|
if (surface != null && surface.isValid()) {
|
||||||
|
hostedTestStarted = true;
|
||||||
|
Log.d(TAG, "Starting test.");
|
||||||
|
hostedTest.onStart(this, surface);
|
||||||
|
checkFinishedRunnable.startChecking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeStopHostedTest() {
|
||||||
|
if (hostedTest != null && hostedTestStarted) {
|
||||||
|
hostedTest.onStop();
|
||||||
|
hostedTest = null;
|
||||||
|
mainHandler.removeCallbacks(checkFinishedRunnable);
|
||||||
|
hostedTestStoppedCondition.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
private static final int getWifiLockMode() {
|
||||||
|
return Util.SDK_INT < 12 ? WifiManager.WIFI_MODE_FULL : WifiManager.WIFI_MODE_FULL_HIGH_PERF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class CheckFinishedRunnable implements Runnable {
|
||||||
|
|
||||||
|
private static final long CHECK_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
|
private void startChecking() {
|
||||||
|
mainHandler.post(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (hostedTest.isFinished()) {
|
||||||
|
hostedTestFinished = true;
|
||||||
|
maybeStopHostedTest();
|
||||||
|
} else {
|
||||||
|
mainHandler.postDelayed(this, CHECK_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link MetricsLogger} that prints the metrics to logcat.
|
||||||
|
*/
|
||||||
|
public final class LogcatMetricsLogger implements MetricsLogger {
|
||||||
|
|
||||||
|
private final String tag;
|
||||||
|
|
||||||
|
public LogcatMetricsLogger(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logMetric(String key, int value) {
|
||||||
|
Log.d(tag, key + ": " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logMetric(String key, double value) {
|
||||||
|
Log.d(tag, key + ": " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logMetric(String key, String value) {
|
||||||
|
Log.d(tag, key + ": " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.playbacktests.util;
|
||||||
|
|
||||||
|
import android.app.Instrumentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metric Logging interface for ExoPlayer playback tests.
|
||||||
|
*/
|
||||||
|
public interface MetricsLogger {
|
||||||
|
|
||||||
|
String KEY_FRAMES_DROPPED_COUNT = "frames_dropped_count";
|
||||||
|
String KEY_FRAMES_RENDERED_COUNT = "frames_rendered_count";
|
||||||
|
String KEY_FRAMES_SKIPPED_COUNT = "frames_skipped_count";
|
||||||
|
String KEY_MAX_CONSECUTIVE_FRAMES_DROPPED_COUNT = "maximum_consecutive_frames_dropped_count";
|
||||||
|
String KEY_TEST_NAME = "test_name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an int metric provided from a test.
|
||||||
|
*
|
||||||
|
* @param key The key of the metric to be logged.
|
||||||
|
* @param value The value of the metric to be logged.
|
||||||
|
*/
|
||||||
|
void logMetric(String key, int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a double metric provided from a test.
|
||||||
|
*
|
||||||
|
* @param key The key of the metric to be logged.
|
||||||
|
* @param value The value of the metric to be logged.
|
||||||
|
*/
|
||||||
|
void logMetric(String key, double value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a string metric provided from a test.
|
||||||
|
*
|
||||||
|
* @param key The key of the metric to be logged.
|
||||||
|
* @param value The value of the metric to be logged.
|
||||||
|
*/
|
||||||
|
void logMetric(String key, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the logger.
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory for instantiating {@link MetricsLogger} instances.
|
||||||
|
*/
|
||||||
|
final class Factory {
|
||||||
|
|
||||||
|
private Factory() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a new instance of {@link MetricsLogger}.
|
||||||
|
*
|
||||||
|
* @param instrumentation The test instrumentation.
|
||||||
|
* @param tag The tag to be used for logcat logs.
|
||||||
|
* @param reportName The name of the report log.
|
||||||
|
* @param streamName The name of the stream of metrics.
|
||||||
|
*/
|
||||||
|
public static MetricsLogger createDefault(Instrumentation instrumentation, String tag,
|
||||||
|
String reportName, String streamName) {
|
||||||
|
return new LogcatMetricsLogger(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
playbacktests/src/main/res/layout/host_activity.xml
Normal file
28
playbacktests/src/main/res/layout/host_activity.xml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/root"
|
||||||
|
android:focusable="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:keepScreenOn="true">
|
||||||
|
|
||||||
|
<SurfaceView android:id="@+id/surface_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
include ':library'
|
include ':library'
|
||||||
include ':testutils'
|
include ':testutils'
|
||||||
include ':demo'
|
include ':demo'
|
||||||
|
include ':playbacktests'
|
||||||
include ':extension-opus'
|
include ':extension-opus'
|
||||||
include ':extension-vp9'
|
include ':extension-vp9'
|
||||||
include ':extension-okhttp'
|
include ':extension-okhttp'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue