mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08: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
@ -447,7 +447,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private void flushCodec() throws ExoPlaybackException {
|
||||
protected void flushCodec() throws ExoPlaybackException {
|
||||
codecHotswapDeadlineMs = -1;
|
||||
inputIndex = -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 ':testutils'
|
||||
include ':demo'
|
||||
include ':playbacktests'
|
||||
include ':extension-opus'
|
||||
include ':extension-vp9'
|
||||
include ':extension-okhttp'
|
||||
|
Loading…
x
Reference in New Issue
Block a user