mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Clean up test runner for ExoPlayer tests.
Currently the ExoPlayerWrapper is used to run tests using an ExoPlayer implementation. Some properties of the test are set in the constructor, some are set by overloading the class, others are hard-coded in the ExoPlayerWrapper class, and a mechanism similar to the existing ActionSchedule is missing. This change does the following: 1. Renames ExoPlayerWrapper to ExoPlayerTestRunner as it better reflects its purpose. 2. Adds an internal Builder to easily set-up the test in a coherent way. This allows to only set necessary test components while using defaults for the rest. 3. Integrate ActionSchedule. 4. Apply the new structure to the existing tests currently using ExoPlayerWrapper. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163186578
This commit is contained in:
parent
cdb71a80aa
commit
b631755b63
@ -15,20 +15,18 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.testutil.ExoPlayerWrapper;
|
||||
import com.google.android.exoplayer2.testutil.ActionSchedule;
|
||||
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder;
|
||||
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
|
||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeRenderer;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
@ -43,67 +41,59 @@ public final class ExoPlayerTest extends TestCase {
|
||||
*/
|
||||
private static final int TIMEOUT_MS = 10000;
|
||||
|
||||
private static final Format TEST_VIDEO_FORMAT = Format.createVideoSampleFormat(null,
|
||||
MimeTypes.VIDEO_H264, null, Format.NO_VALUE, Format.NO_VALUE, 1280, 720, Format.NO_VALUE,
|
||||
null, null);
|
||||
private static final Format TEST_AUDIO_FORMAT = Format.createAudioSampleFormat(null,
|
||||
MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null);
|
||||
|
||||
/**
|
||||
* Tests playback of a source that exposes an empty timeline. Playback is expected to end without
|
||||
* error.
|
||||
*/
|
||||
public void testPlayEmptyTimeline() throws Exception {
|
||||
ExoPlayerWrapper playerWrapper = new ExoPlayerWrapper();
|
||||
Timeline timeline = Timeline.EMPTY;
|
||||
MediaSource mediaSource = new FakeMediaSource(timeline, null);
|
||||
FakeRenderer renderer = new FakeRenderer();
|
||||
playerWrapper.setup(mediaSource, renderer);
|
||||
playerWrapper.blockUntilEnded(TIMEOUT_MS);
|
||||
assertEquals(0, playerWrapper.positionDiscontinuityCount);
|
||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||
.setTimeline(timeline).setRenderers(renderer)
|
||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertPositionDiscontinuityCount(0);
|
||||
testRunner.assertTimelinesEqual(timeline);
|
||||
assertEquals(0, renderer.formatReadCount);
|
||||
assertEquals(0, renderer.bufferReadCount);
|
||||
assertFalse(renderer.isEnded);
|
||||
playerWrapper.assertSourceInfosEquals(Pair.create(timeline, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests playback of a source that exposes a single period.
|
||||
*/
|
||||
public void testPlaySinglePeriodTimeline() throws Exception {
|
||||
ExoPlayerWrapper playerWrapper = new ExoPlayerWrapper();
|
||||
Timeline timeline = new FakeTimeline(new TimelineWindowDefinition(false, false, 0));
|
||||
Object manifest = new Object();
|
||||
MediaSource mediaSource = new FakeMediaSource(timeline, manifest, TEST_VIDEO_FORMAT);
|
||||
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
|
||||
playerWrapper.setup(mediaSource, renderer);
|
||||
playerWrapper.blockUntilEnded(TIMEOUT_MS);
|
||||
assertEquals(0, playerWrapper.positionDiscontinuityCount);
|
||||
FakeRenderer renderer = new FakeRenderer(Builder.VIDEO_FORMAT);
|
||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||
.setTimeline(timeline).setManifest(manifest).setRenderers(renderer)
|
||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertPositionDiscontinuityCount(0);
|
||||
testRunner.assertTimelinesEqual(timeline);
|
||||
testRunner.assertManifestsEqual(manifest);
|
||||
testRunner.assertTrackGroupsEqual(new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT)));
|
||||
assertEquals(1, renderer.formatReadCount);
|
||||
assertEquals(1, renderer.bufferReadCount);
|
||||
assertTrue(renderer.isEnded);
|
||||
assertEquals(new TrackGroupArray(new TrackGroup(TEST_VIDEO_FORMAT)), playerWrapper.trackGroups);
|
||||
playerWrapper.assertSourceInfosEquals(Pair.create(timeline, manifest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests playback of a source that exposes three periods.
|
||||
*/
|
||||
public void testPlayMultiPeriodTimeline() throws Exception {
|
||||
ExoPlayerWrapper playerWrapper = new ExoPlayerWrapper();
|
||||
Timeline timeline = new FakeTimeline(
|
||||
new TimelineWindowDefinition(false, false, 0),
|
||||
new TimelineWindowDefinition(false, false, 0),
|
||||
new TimelineWindowDefinition(false, false, 0));
|
||||
MediaSource mediaSource = new FakeMediaSource(timeline, null, TEST_VIDEO_FORMAT);
|
||||
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
|
||||
playerWrapper.setup(mediaSource, renderer);
|
||||
playerWrapper.blockUntilEnded(TIMEOUT_MS);
|
||||
assertEquals(2, playerWrapper.positionDiscontinuityCount);
|
||||
FakeRenderer renderer = new FakeRenderer(Builder.VIDEO_FORMAT);
|
||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||
.setTimeline(timeline).setRenderers(renderer)
|
||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertPositionDiscontinuityCount(2);
|
||||
testRunner.assertTimelinesEqual(timeline);
|
||||
assertEquals(3, renderer.formatReadCount);
|
||||
assertEquals(1, renderer.bufferReadCount);
|
||||
assertTrue(renderer.isEnded);
|
||||
playerWrapper.assertSourceInfosEquals(Pair.create(timeline, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,16 +101,12 @@ public final class ExoPlayerTest extends TestCase {
|
||||
* source.
|
||||
*/
|
||||
public void testReadAheadToEndDoesNotResetRenderer() throws Exception {
|
||||
final ExoPlayerWrapper playerWrapper = new ExoPlayerWrapper();
|
||||
Timeline timeline = new FakeTimeline(
|
||||
new TimelineWindowDefinition(false, false, 10),
|
||||
new TimelineWindowDefinition(false, false, 10),
|
||||
new TimelineWindowDefinition(false, false, 10));
|
||||
MediaSource mediaSource = new FakeMediaSource(timeline, null, TEST_VIDEO_FORMAT,
|
||||
TEST_AUDIO_FORMAT);
|
||||
|
||||
FakeRenderer videoRenderer = new FakeRenderer(TEST_VIDEO_FORMAT);
|
||||
FakeMediaClockRenderer audioRenderer = new FakeMediaClockRenderer(TEST_AUDIO_FORMAT) {
|
||||
final FakeRenderer videoRenderer = new FakeRenderer(Builder.VIDEO_FORMAT);
|
||||
FakeMediaClockRenderer audioRenderer = new FakeMediaClockRenderer(Builder.AUDIO_FORMAT) {
|
||||
|
||||
@Override
|
||||
public long getPositionUs() {
|
||||
@ -143,35 +129,30 @@ public final class ExoPlayerTest extends TestCase {
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
// Allow playback to end once the final period is playing.
|
||||
return playerWrapper.positionDiscontinuityCount == 2;
|
||||
return videoRenderer.isEnded();
|
||||
}
|
||||
|
||||
};
|
||||
playerWrapper.setup(mediaSource, videoRenderer, audioRenderer);
|
||||
playerWrapper.blockUntilEnded(TIMEOUT_MS);
|
||||
assertEquals(2, playerWrapper.positionDiscontinuityCount);
|
||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||
.setTimeline(timeline).setRenderers(videoRenderer, audioRenderer)
|
||||
.setSupportedFormats(Builder.VIDEO_FORMAT, Builder.AUDIO_FORMAT)
|
||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertPositionDiscontinuityCount(2);
|
||||
testRunner.assertTimelinesEqual(timeline);
|
||||
assertEquals(1, audioRenderer.positionResetCount);
|
||||
assertTrue(videoRenderer.isEnded);
|
||||
assertTrue(audioRenderer.isEnded);
|
||||
playerWrapper.assertSourceInfosEquals(Pair.create(timeline, null));
|
||||
}
|
||||
|
||||
public void testRepreparationGivesFreshSourceInfo() throws Exception {
|
||||
ExoPlayerWrapper playerWrapper = new ExoPlayerWrapper();
|
||||
Timeline timeline = new FakeTimeline(new TimelineWindowDefinition(false, false, 0));
|
||||
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
|
||||
|
||||
// Prepare the player with a source with the first manifest and a non-empty timeline
|
||||
FakeRenderer renderer = new FakeRenderer(Builder.VIDEO_FORMAT);
|
||||
Object firstSourceManifest = new Object();
|
||||
playerWrapper.setup(new FakeMediaSource(timeline, firstSourceManifest, TEST_VIDEO_FORMAT),
|
||||
renderer);
|
||||
playerWrapper.blockUntilSourceInfoRefreshed(TIMEOUT_MS);
|
||||
|
||||
// Prepare the player again with a source and a new manifest, which will never be exposed.
|
||||
MediaSource firstSource = new FakeMediaSource(timeline, firstSourceManifest,
|
||||
Builder.VIDEO_FORMAT);
|
||||
final CountDownLatch queuedSourceInfoCountDownLatch = new CountDownLatch(1);
|
||||
final CountDownLatch completePreparationCountDownLatch = new CountDownLatch(1);
|
||||
playerWrapper.prepare(new FakeMediaSource(timeline, new Object(), TEST_VIDEO_FORMAT) {
|
||||
MediaSource secondSource = new FakeMediaSource(timeline, new Object(), Builder.VIDEO_FORMAT) {
|
||||
@Override
|
||||
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||
super.prepareSource(player, isTopLevelSource, listener);
|
||||
@ -185,29 +166,49 @@ public final class ExoPlayerTest extends TestCase {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prepare the player again with a third source.
|
||||
queuedSourceInfoCountDownLatch.await();
|
||||
};
|
||||
Object thirdSourceManifest = new Object();
|
||||
playerWrapper.prepare(new FakeMediaSource(timeline, thirdSourceManifest, TEST_VIDEO_FORMAT));
|
||||
completePreparationCountDownLatch.countDown();
|
||||
|
||||
// Wait for playback to complete.
|
||||
playerWrapper.blockUntilEnded(TIMEOUT_MS);
|
||||
assertEquals(0, playerWrapper.positionDiscontinuityCount);
|
||||
assertEquals(1, renderer.formatReadCount);
|
||||
assertEquals(1, renderer.bufferReadCount);
|
||||
assertTrue(renderer.isEnded);
|
||||
assertEquals(new TrackGroupArray(new TrackGroup(TEST_VIDEO_FORMAT)), playerWrapper.trackGroups);
|
||||
MediaSource thirdSource = new FakeMediaSource(timeline, thirdSourceManifest,
|
||||
Builder.VIDEO_FORMAT);
|
||||
|
||||
// Prepare the player with a source with the first manifest and a non-empty timeline. Prepare
|
||||
// the player again with a source and a new manifest, which will never be exposed. Allow the
|
||||
// test thread to prepare the player with a third source, and block the playback thread until
|
||||
// the test thread's call to prepare() has returned.
|
||||
ActionSchedule actionSchedule = new ActionSchedule.Builder("testRepreparation")
|
||||
.waitForTimelineChanged(timeline)
|
||||
.prepareSource(secondSource)
|
||||
.executeRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
queuedSourceInfoCountDownLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
})
|
||||
.prepareSource(thirdSource)
|
||||
.executeRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
completePreparationCountDownLatch.countDown();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||
.setMediaSource(firstSource).setRenderers(renderer).setActionSchedule(actionSchedule)
|
||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertPositionDiscontinuityCount(0);
|
||||
// The first source's preparation completed with a non-empty timeline. When the player was
|
||||
// re-prepared with the second source, it immediately exposed an empty timeline, but the source
|
||||
// info refresh from the second source was suppressed as we re-prepared with the third source.
|
||||
playerWrapper.assertSourceInfosEquals(
|
||||
Pair.create(timeline, firstSourceManifest),
|
||||
Pair.create(Timeline.EMPTY, null),
|
||||
Pair.create(timeline, thirdSourceManifest));
|
||||
testRunner.assertTimelinesEqual(timeline, Timeline.EMPTY, timeline);
|
||||
testRunner.assertManifestsEqual(firstSourceManifest, null, thirdSourceManifest);
|
||||
testRunner.assertTrackGroupsEqual(new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT)));
|
||||
assertEquals(1, renderer.formatReadCount);
|
||||
assertEquals(1, renderer.bufferReadCount);
|
||||
assertTrue(renderer.isEnded);
|
||||
}
|
||||
|
||||
public void testRepeatModeChanges() throws Exception {
|
||||
@ -215,49 +216,22 @@ public final class ExoPlayerTest extends TestCase {
|
||||
new TimelineWindowDefinition(true, false, 100000),
|
||||
new TimelineWindowDefinition(true, false, 100000),
|
||||
new TimelineWindowDefinition(true, false, 100000));
|
||||
final int[] actionSchedule = { // 0 -> 1
|
||||
Player.REPEAT_MODE_ONE, // 1 -> 1
|
||||
Player.REPEAT_MODE_OFF, // 1 -> 2
|
||||
Player.REPEAT_MODE_ONE, // 2 -> 2
|
||||
Player.REPEAT_MODE_ALL, // 2 -> 0
|
||||
Player.REPEAT_MODE_ONE, // 0 -> 0
|
||||
-1, // 0 -> 0
|
||||
Player.REPEAT_MODE_OFF, // 0 -> 1
|
||||
-1, // 1 -> 2
|
||||
-1 // 2 -> ended
|
||||
};
|
||||
int[] expectedWindowIndices = {1, 1, 2, 2, 0, 0, 0, 1, 2};
|
||||
final LinkedList<Integer> windowIndices = new LinkedList<>();
|
||||
final CountDownLatch actionCounter = new CountDownLatch(actionSchedule.length);
|
||||
ExoPlayerWrapper playerWrapper = new ExoPlayerWrapper() {
|
||||
@Override
|
||||
@SuppressWarnings("ResourceType")
|
||||
public void onPositionDiscontinuity() {
|
||||
super.onPositionDiscontinuity();
|
||||
int actionIndex = actionSchedule.length - (int) actionCounter.getCount();
|
||||
if (actionSchedule[actionIndex] != -1) {
|
||||
player.setRepeatMode(actionSchedule[actionIndex]);
|
||||
}
|
||||
windowIndices.add(player.getCurrentWindowIndex());
|
||||
actionCounter.countDown();
|
||||
}
|
||||
};
|
||||
MediaSource mediaSource = new FakeMediaSource(timeline, null, TEST_VIDEO_FORMAT);
|
||||
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
|
||||
playerWrapper.setup(mediaSource, renderer);
|
||||
boolean finished = actionCounter.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
playerWrapper.release();
|
||||
assertTrue("Test playback timed out waiting for action schedule to end.", finished);
|
||||
if (playerWrapper.exception != null) {
|
||||
throw playerWrapper.exception;
|
||||
}
|
||||
assertEquals(expectedWindowIndices.length, windowIndices.size());
|
||||
for (int i = 0; i < expectedWindowIndices.length; i++) {
|
||||
assertEquals(expectedWindowIndices[i], windowIndices.get(i).intValue());
|
||||
}
|
||||
assertEquals(9, playerWrapper.positionDiscontinuityCount);
|
||||
FakeRenderer renderer = new FakeRenderer(Builder.VIDEO_FORMAT);
|
||||
ActionSchedule actionSchedule = new ActionSchedule.Builder("testRepeatMode") // 0 -> 1
|
||||
.waitForPositionDiscontinuity().setRepeatMode(Player.REPEAT_MODE_ONE) // 1 -> 1
|
||||
.waitForPositionDiscontinuity().setRepeatMode(Player.REPEAT_MODE_OFF) // 1 -> 2
|
||||
.waitForPositionDiscontinuity().setRepeatMode(Player.REPEAT_MODE_ONE) // 2 -> 2
|
||||
.waitForPositionDiscontinuity().setRepeatMode(Player.REPEAT_MODE_ALL) // 2 -> 0
|
||||
.waitForPositionDiscontinuity().setRepeatMode(Player.REPEAT_MODE_ONE) // 0 -> 0
|
||||
.waitForPositionDiscontinuity() // 0 -> 0
|
||||
.waitForPositionDiscontinuity().setRepeatMode(Player.REPEAT_MODE_OFF) // 0 -> end
|
||||
.build();
|
||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||
.setTimeline(timeline).setRenderers(renderer).setActionSchedule(actionSchedule)
|
||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertPlayedPeriodIndices(0, 1, 1, 2, 2, 0, 0, 0, 1, 2);
|
||||
testRunner.assertTimelinesEqual(timeline);
|
||||
assertTrue(renderer.isEnded);
|
||||
playerWrapper.assertSourceInfosEquals(Pair.create(timeline, null));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.exoplayer2.testutil;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.LoadControl;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.RenderersFactory;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder.PlayerFactory;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.text.TextRenderer.Output;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.video.VideoRendererEventListener;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import junit.framework.Assert;
|
||||
|
||||
/**
|
||||
* Helper class to run an ExoPlayer test.
|
||||
*/
|
||||
public final class ExoPlayerTestRunner implements Player.EventListener {
|
||||
|
||||
/**
|
||||
* Builder to set-up a {@link ExoPlayerTestRunner}. Default fake implementations will be used for
|
||||
* unset test properties.
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
/**
|
||||
* Factory to create an {@link SimpleExoPlayer} instance. The player will be created on its own
|
||||
* {@link HandlerThread}.
|
||||
*/
|
||||
public interface PlayerFactory {
|
||||
|
||||
SimpleExoPlayer createExoPlayer(RenderersFactory renderersFactory,
|
||||
MappingTrackSelector trackSelector, LoadControl loadControl);
|
||||
|
||||
}
|
||||
|
||||
public static final Format VIDEO_FORMAT = Format.createVideoSampleFormat(null,
|
||||
MimeTypes.VIDEO_H264, null, Format.NO_VALUE, Format.NO_VALUE, 1280, 720, Format.NO_VALUE,
|
||||
null, null);
|
||||
public static final Format AUDIO_FORMAT = Format.createAudioSampleFormat(null,
|
||||
MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null);
|
||||
|
||||
private PlayerFactory playerFactory;
|
||||
private Timeline timeline;
|
||||
private Object manifest;
|
||||
private MediaSource mediaSource;
|
||||
private MappingTrackSelector trackSelector;
|
||||
private LoadControl loadControl;
|
||||
private Format[] supportedFormats;
|
||||
private Renderer[] renderers;
|
||||
private RenderersFactory renderersFactory;
|
||||
private ActionSchedule actionSchedule;
|
||||
private Player.EventListener eventListener;
|
||||
|
||||
public Builder setTimeline(Timeline timeline) {
|
||||
Assert.assertNull(mediaSource);
|
||||
this.timeline = timeline;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setManifest(Object manifest) {
|
||||
Assert.assertNull(mediaSource);
|
||||
this.manifest = manifest;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replaces {@link #setTimeline(Timeline)} and {@link #setManifest(Object)}. */
|
||||
public Builder setMediaSource(MediaSource mediaSource) {
|
||||
Assert.assertNull(timeline);
|
||||
Assert.assertNull(manifest);
|
||||
this.mediaSource = mediaSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTrackSelector(MappingTrackSelector trackSelector) {
|
||||
this.trackSelector = trackSelector;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLoadControl(LoadControl loadControl) {
|
||||
this.loadControl = loadControl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSupportedFormats(Format... supportedFormats) {
|
||||
this.supportedFormats = supportedFormats;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenderers(Renderer... renderers) {
|
||||
Assert.assertNull(renderersFactory);
|
||||
this.renderers = renderers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replaces {@link #setRenderers(Renderer...)}. */
|
||||
public Builder setRenderersFactory(RenderersFactory renderersFactory) {
|
||||
Assert.assertNull(renderers);
|
||||
this.renderersFactory = renderersFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setExoPlayer(PlayerFactory playerFactory) {
|
||||
this.playerFactory = playerFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setActionSchedule(ActionSchedule actionSchedule) {
|
||||
this.actionSchedule = actionSchedule;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEventListener(Player.EventListener eventListener) {
|
||||
this.eventListener = eventListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExoPlayerTestRunner build() {
|
||||
if (supportedFormats == null) {
|
||||
supportedFormats = new Format[] { VIDEO_FORMAT };
|
||||
}
|
||||
if (trackSelector == null) {
|
||||
trackSelector = new DefaultTrackSelector();
|
||||
}
|
||||
if (renderersFactory == null) {
|
||||
if (renderers == null) {
|
||||
renderers = new Renderer[] { new FakeRenderer(supportedFormats) };
|
||||
}
|
||||
renderersFactory = new RenderersFactory() {
|
||||
@Override
|
||||
public Renderer[] createRenderers(Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener, Output textRendererOutput,
|
||||
MetadataRenderer.Output metadataRendererOutput) {
|
||||
return renderers;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (loadControl == null) {
|
||||
loadControl = new DefaultLoadControl();
|
||||
}
|
||||
if (playerFactory == null) {
|
||||
playerFactory = new PlayerFactory() {
|
||||
@Override
|
||||
public SimpleExoPlayer createExoPlayer(RenderersFactory renderersFactory,
|
||||
MappingTrackSelector trackSelector, LoadControl loadControl) {
|
||||
return ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (mediaSource == null) {
|
||||
if (timeline == null) {
|
||||
timeline = new FakeTimeline(new TimelineWindowDefinition(false, false, 0));
|
||||
}
|
||||
mediaSource = new FakeMediaSource(timeline, manifest, supportedFormats);
|
||||
}
|
||||
return new ExoPlayerTestRunner(playerFactory, mediaSource, renderersFactory, trackSelector,
|
||||
loadControl, actionSchedule, eventListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final PlayerFactory playerFactory;
|
||||
private final MediaSource mediaSource;
|
||||
private final RenderersFactory renderersFactory;
|
||||
private final MappingTrackSelector trackSelector;
|
||||
private final LoadControl loadControl;
|
||||
private final ActionSchedule actionSchedule;
|
||||
private final Player.EventListener eventListener;
|
||||
|
||||
private final HandlerThread playerThread;
|
||||
private final Handler handler;
|
||||
private final CountDownLatch endedCountDownLatch;
|
||||
private final LinkedList<Timeline> timelines;
|
||||
private final LinkedList<Object> manifests;
|
||||
private final LinkedList<Integer> periodIndices;
|
||||
|
||||
private SimpleExoPlayer player;
|
||||
private Exception exception;
|
||||
private TrackGroupArray trackGroups;
|
||||
private int positionDiscontinuityCount;
|
||||
|
||||
private ExoPlayerTestRunner(PlayerFactory playerFactory, MediaSource mediaSource,
|
||||
RenderersFactory renderersFactory, MappingTrackSelector trackSelector,
|
||||
LoadControl loadControl, ActionSchedule actionSchedule, Player.EventListener eventListener) {
|
||||
this.playerFactory = playerFactory;
|
||||
this.mediaSource = mediaSource;
|
||||
this.renderersFactory = renderersFactory;
|
||||
this.trackSelector = trackSelector;
|
||||
this.loadControl = loadControl;
|
||||
this.actionSchedule = actionSchedule;
|
||||
this.eventListener = eventListener;
|
||||
this.timelines = new LinkedList<>();
|
||||
this.manifests = new LinkedList<>();
|
||||
this.periodIndices = new LinkedList<>();
|
||||
this.endedCountDownLatch = new CountDownLatch(1);
|
||||
this.playerThread = new HandlerThread("ExoPlayerTest thread");
|
||||
playerThread.start();
|
||||
this.handler = new Handler(playerThread.getLooper());
|
||||
}
|
||||
|
||||
// Called on the test thread to run the test.
|
||||
|
||||
public ExoPlayerTestRunner start() {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
player = playerFactory.createExoPlayer(renderersFactory, trackSelector, loadControl);
|
||||
player.addListener(ExoPlayerTestRunner.this);
|
||||
if (eventListener != null) {
|
||||
player.addListener(eventListener);
|
||||
}
|
||||
player.setPlayWhenReady(true);
|
||||
if (actionSchedule != null) {
|
||||
actionSchedule.start(player, trackSelector, null, handler);
|
||||
}
|
||||
player.prepare(mediaSource);
|
||||
} catch (Exception e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExoPlayerTestRunner blockUntilEnded(long timeoutMs) throws Exception {
|
||||
if (!endedCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
|
||||
exception = new TimeoutException("Test playback timed out waiting for playback to end.");
|
||||
}
|
||||
release();
|
||||
// Throw any pending exception (from playback, timing out or releasing).
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// Assertions called on the test thread after test finished.
|
||||
|
||||
public void assertTimelinesEqual(Timeline... timelines) {
|
||||
Assert.assertEquals(timelines.length, this.timelines.size());
|
||||
for (Timeline timeline : timelines) {
|
||||
Assert.assertEquals(timeline, this.timelines.remove());
|
||||
}
|
||||
}
|
||||
|
||||
public void assertManifestsEqual(Object... manifests) {
|
||||
Assert.assertEquals(manifests.length, this.manifests.size());
|
||||
for (Object manifest : manifests) {
|
||||
Assert.assertEquals(manifest, this.manifests.remove());
|
||||
}
|
||||
}
|
||||
|
||||
public void assertTrackGroupsEqual(TrackGroupArray trackGroupArray) {
|
||||
Assert.assertEquals(trackGroupArray, this.trackGroups);
|
||||
}
|
||||
|
||||
public void assertPositionDiscontinuityCount(int expectedCount) {
|
||||
Assert.assertEquals(expectedCount, positionDiscontinuityCount);
|
||||
}
|
||||
|
||||
public void assertPlayedPeriodIndices(int... periodIndices) {
|
||||
Assert.assertEquals(periodIndices.length, this.periodIndices.size());
|
||||
for (int periodIndex : periodIndices) {
|
||||
Assert.assertEquals(periodIndex, (int) this.periodIndices.remove());
|
||||
}
|
||||
}
|
||||
|
||||
// Private implementation details.
|
||||
|
||||
private void release() throws InterruptedException {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (player != null) {
|
||||
player.release();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException(e);
|
||||
} finally {
|
||||
playerThread.quit();
|
||||
}
|
||||
}
|
||||
});
|
||||
playerThread.join();
|
||||
}
|
||||
|
||||
private void handleException(Exception exception) {
|
||||
if (this.exception == null) {
|
||||
this.exception = exception;
|
||||
}
|
||||
endedCountDownLatch.countDown();
|
||||
}
|
||||
|
||||
// Player.EventListener
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
timelines.add(timeline);
|
||||
manifests.add(manifest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
this.trackGroups = trackGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (periodIndices.isEmpty() && playbackState == Player.STATE_READY) {
|
||||
periodIndices.add(player.getCurrentPeriodIndex());
|
||||
}
|
||||
if (playbackState == Player.STATE_ENDED) {
|
||||
endedCountDownLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
handleException(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity() {
|
||||
positionDiscontinuityCount++;
|
||||
periodIndices.add(player.getCurrentPeriodIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.exoplayer2.testutil;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import junit.framework.Assert;
|
||||
|
||||
/**
|
||||
* Wraps a player with its own handler thread.
|
||||
*/
|
||||
public class ExoPlayerWrapper implements Player.EventListener {
|
||||
|
||||
private final CountDownLatch sourceInfoCountDownLatch;
|
||||
private final CountDownLatch endedCountDownLatch;
|
||||
private final HandlerThread playerThread;
|
||||
private final Handler handler;
|
||||
private final LinkedList<Pair<Timeline, Object>> sourceInfos;
|
||||
|
||||
public ExoPlayer player;
|
||||
public TrackGroupArray trackGroups;
|
||||
public Exception exception;
|
||||
|
||||
// Written only on the main thread.
|
||||
public volatile int positionDiscontinuityCount;
|
||||
|
||||
public ExoPlayerWrapper() {
|
||||
sourceInfoCountDownLatch = new CountDownLatch(1);
|
||||
endedCountDownLatch = new CountDownLatch(1);
|
||||
playerThread = new HandlerThread("ExoPlayerTest thread");
|
||||
playerThread.start();
|
||||
handler = new Handler(playerThread.getLooper());
|
||||
sourceInfos = new LinkedList<>();
|
||||
}
|
||||
|
||||
// Called on the test thread.
|
||||
|
||||
public void blockUntilEnded(long timeoutMs) throws Exception {
|
||||
if (!endedCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
|
||||
exception = new TimeoutException("Test playback timed out waiting for playback to end.");
|
||||
}
|
||||
release();
|
||||
// Throw any pending exception (from playback, timing out or releasing).
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public void blockUntilSourceInfoRefreshed(long timeoutMs) throws Exception {
|
||||
if (!sourceInfoCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
|
||||
throw new TimeoutException("Test playback timed out waiting for source info.");
|
||||
}
|
||||
}
|
||||
|
||||
public void setup(final MediaSource mediaSource, final Renderer... renderers) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
player = ExoPlayerFactory.newInstance(renderers, new DefaultTrackSelector());
|
||||
player.addListener(ExoPlayerWrapper.this);
|
||||
player.setPlayWhenReady(true);
|
||||
player.prepare(mediaSource);
|
||||
} catch (Exception e) {
|
||||
handleError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void prepare(final MediaSource mediaSource) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
player.prepare(mediaSource);
|
||||
} catch (Exception e) {
|
||||
handleError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void release() throws InterruptedException {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (player != null) {
|
||||
player.release();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleError(e);
|
||||
} finally {
|
||||
playerThread.quit();
|
||||
}
|
||||
}
|
||||
});
|
||||
playerThread.join();
|
||||
}
|
||||
|
||||
private void handleError(Exception exception) {
|
||||
if (this.exception == null) {
|
||||
this.exception = exception;
|
||||
}
|
||||
endedCountDownLatch.countDown();
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void assertSourceInfosEquals(Pair<Timeline, Object>... sourceInfos) {
|
||||
Assert.assertEquals(sourceInfos.length, this.sourceInfos.size());
|
||||
for (Pair<Timeline, Object> sourceInfo : sourceInfos) {
|
||||
Assert.assertEquals(sourceInfo, this.sourceInfos.remove());
|
||||
}
|
||||
}
|
||||
|
||||
// Player.EventListener implementation.
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (playbackState == Player.STATE_ENDED) {
|
||||
endedCountDownLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int repeatMode) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
sourceInfos.add(Pair.create(timeline, manifest));
|
||||
sourceInfoCountDownLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
this.trackGroups = trackGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException exception) {
|
||||
handleError(exception);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||
@Override
|
||||
public void onPositionDiscontinuity() {
|
||||
positionDiscontinuityCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user