Add method to FakeMediaSource to trigger source info refresh.

This allows to remove the LazyMediaSource used within
DynamicConcatenatingMediaSourceTest and also allows to write test which
simulates dynamic timeline or manifest updates.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=175680371
This commit is contained in:
tonihei 2017-11-14 08:06:50 -08:00 committed by Oliver Woodman
parent 22b95032b3
commit 5aa053d91d
2 changed files with 61 additions and 54 deletions

View File

@ -33,15 +33,12 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.source.MediaSource.Listener; import com.google.android.exoplayer2.source.MediaSource.Listener;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.testutil.TimelineAsserts;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -216,19 +213,26 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
public void testPlaylistWithLazyMediaSource() throws InterruptedException { public void testPlaylistWithLazyMediaSource() throws InterruptedException {
timeline = null; timeline = null;
FakeMediaSource[] childSources = createMediaSources(2);
LazyMediaSource[] lazySources = new LazyMediaSource[4]; // Create some normal (immediately preparing) sources and some lazy sources whose timeline
// updates need to be triggered.
FakeMediaSource[] fastSources = createMediaSources(2);
FakeMediaSource[] lazySources = new FakeMediaSource[4];
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
lazySources[i] = new LazyMediaSource(); lazySources[i] = new FakeMediaSource(null, null);
} }
//Add lazy sources before preparation // Add lazy sources and normal sources before preparation. Also remove one lazy source again
// before preparation to check it doesn't throw or change the result.
DynamicConcatenatingMediaSource mediaSource = new DynamicConcatenatingMediaSource(); DynamicConcatenatingMediaSource mediaSource = new DynamicConcatenatingMediaSource();
mediaSource.addMediaSource(lazySources[0]); mediaSource.addMediaSource(lazySources[0]);
mediaSource.addMediaSource(0, childSources[0]); mediaSource.addMediaSource(0, fastSources[0]);
mediaSource.removeMediaSource(1); mediaSource.removeMediaSource(1);
mediaSource.addMediaSource(1, lazySources[1]); mediaSource.addMediaSource(1, lazySources[1]);
assertNull(timeline); assertNull(timeline);
// Prepare and assert that the timeline contains all information for normal sources while having
// placeholder information for lazy sources.
prepareAndListenToTimelineUpdates(mediaSource); prepareAndListenToTimelineUpdates(mediaSource);
waitForTimelineUpdate(); waitForTimelineUpdate();
assertNotNull(timeline); assertNotNull(timeline);
@ -236,7 +240,9 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
TimelineAsserts.assertWindowIds(timeline, 111, null); TimelineAsserts.assertWindowIds(timeline, 111, null);
TimelineAsserts.assertWindowIsDynamic(timeline, false, true); TimelineAsserts.assertWindowIsDynamic(timeline, false, true);
lazySources[1].triggerTimelineUpdate(createFakeTimeline(8)); // Trigger source info refresh for lazy source and check that the timeline now contains all
// information for all windows.
lazySources[1].setNewSourceInfo(createFakeTimeline(8), null);
waitForTimelineUpdate(); waitForTimelineUpdate();
TimelineAsserts.assertPeriodCounts(timeline, 1, 9); TimelineAsserts.assertPeriodCounts(timeline, 1, 9);
TimelineAsserts.assertWindowIds(timeline, 111, 999); TimelineAsserts.assertWindowIds(timeline, 111, 999);
@ -244,10 +250,11 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
TimelineAsserts.assertAllPeriodsCanBeCreatedPreparedAndReleased(mediaSource, timeline, TimelineAsserts.assertAllPeriodsCanBeCreatedPreparedAndReleased(mediaSource, timeline,
TIMEOUT_MS); TIMEOUT_MS);
//Add lazy sources after preparation (and also try to prepare media period from lazy source). // Add further lazy and normal sources after preparation. Also remove one lazy source again to
// check it doesn't throw or change the result.
mediaSource.addMediaSource(1, lazySources[2]); mediaSource.addMediaSource(1, lazySources[2]);
waitForTimelineUpdate(); waitForTimelineUpdate();
mediaSource.addMediaSource(2, childSources[1]); mediaSource.addMediaSource(2, fastSources[1]);
waitForTimelineUpdate(); waitForTimelineUpdate();
mediaSource.addMediaSource(0, lazySources[3]); mediaSource.addMediaSource(0, lazySources[3]);
waitForTimelineUpdate(); waitForTimelineUpdate();
@ -257,6 +264,8 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
TimelineAsserts.assertWindowIds(timeline, null, 111, 222, 999); TimelineAsserts.assertWindowIds(timeline, null, 111, 222, 999);
TimelineAsserts.assertWindowIsDynamic(timeline, true, false, false, false); TimelineAsserts.assertWindowIsDynamic(timeline, true, false, false, false);
// Create a period from an unprepared lazy media source and assert Callback.onPrepared is not
// called yet.
MediaPeriod lazyPeriod = mediaSource.createPeriod(new MediaPeriodId(0), null); MediaPeriod lazyPeriod = mediaSource.createPeriod(new MediaPeriodId(0), null);
assertNotNull(lazyPeriod); assertNotNull(lazyPeriod);
final ConditionVariable lazyPeriodPrepared = new ConditionVariable(); final ConditionVariable lazyPeriodPrepared = new ConditionVariable();
@ -269,11 +278,14 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
public void onContinueLoadingRequested(MediaPeriod source) {} public void onContinueLoadingRequested(MediaPeriod source) {}
}, 0); }, 0);
assertFalse(lazyPeriodPrepared.block(1)); assertFalse(lazyPeriodPrepared.block(1));
// Assert that a second period can also be created and released without problems.
MediaPeriod secondLazyPeriod = mediaSource.createPeriod(new MediaPeriodId(0), null); MediaPeriod secondLazyPeriod = mediaSource.createPeriod(new MediaPeriodId(0), null);
assertNotNull(secondLazyPeriod); assertNotNull(secondLazyPeriod);
mediaSource.releasePeriod(secondLazyPeriod); mediaSource.releasePeriod(secondLazyPeriod);
lazySources[3].triggerTimelineUpdate(createFakeTimeline(7)); // Trigger source info refresh for lazy media source. Assert that now all information is
// available again and the previously created period now also finished preparing.
lazySources[3].setNewSourceInfo(createFakeTimeline(7), null);
waitForTimelineUpdate(); waitForTimelineUpdate();
TimelineAsserts.assertPeriodCounts(timeline, 8, 1, 2, 9); TimelineAsserts.assertPeriodCounts(timeline, 8, 1, 2, 9);
TimelineAsserts.assertWindowIds(timeline, 888, 111, 222, 999); TimelineAsserts.assertWindowIds(timeline, 888, 111, 222, 999);
@ -281,9 +293,14 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
assertTrue(lazyPeriodPrepared.block(TIMEOUT_MS)); assertTrue(lazyPeriodPrepared.block(TIMEOUT_MS));
mediaSource.releasePeriod(lazyPeriod); mediaSource.releasePeriod(lazyPeriod);
// Release media source and assert all normal and lazy media sources are fully released as well.
mediaSource.releaseSource(); mediaSource.releaseSource();
childSources[0].assertReleased(); for (FakeMediaSource fastSource : fastSources) {
childSources[1].assertReleased(); fastSource.assertReleased();
}
for (FakeMediaSource lazySource : lazySources) {
lazySource.assertReleased();
}
} }
public void testEmptyTimelineMediaSource() throws InterruptedException { public void testEmptyTimelineMediaSource() throws InterruptedException {
@ -662,38 +679,6 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
} }
private static class LazyMediaSource implements MediaSource {
private Listener listener;
public void triggerTimelineUpdate(Timeline timeline) {
listener.onSourceInfoRefreshed(this, timeline, null);
}
@Override
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
this.listener = listener;
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
}
@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
return new FakeMediaPeriod(TrackGroupArray.EMPTY);
}
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
}
@Override
public void releaseSource() {
}
}
/** /**
* Stub ExoPlayer which only accepts custom messages and runs them on a separate handler thread. * Stub ExoPlayer which only accepts custom messages and runs them on a separate handler thread.
*/ */

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
@ -34,28 +35,34 @@ import junit.framework.Assert;
*/ */
public class FakeMediaSource implements MediaSource { public class FakeMediaSource implements MediaSource {
protected final Timeline timeline;
private final Object manifest; private final Object manifest;
private final TrackGroupArray trackGroupArray; private final TrackGroupArray trackGroupArray;
private final ArrayList<FakeMediaPeriod> activeMediaPeriods; private final ArrayList<FakeMediaPeriod> activeMediaPeriods;
private final ArrayList<MediaPeriodId> createdMediaPeriods; private final ArrayList<MediaPeriodId> createdMediaPeriods;
protected Timeline timeline;
private boolean preparedSource; private boolean preparedSource;
private boolean releasedSource; private boolean releasedSource;
private Listener listener;
/** /**
* Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with a * Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with a
* {@link TrackGroupArray} using the given {@link Format}s. * {@link TrackGroupArray} using the given {@link Format}s. The provided {@link Timeline} may be
* null to prevent an immediate source info refresh message when preparing the media source. It
* can be manually set later using {@link #setNewSourceInfo(Timeline, Object)}.
*/ */
public FakeMediaSource(Timeline timeline, Object manifest, Format... formats) { public FakeMediaSource(@Nullable Timeline timeline, Object manifest, Format... formats) {
this(timeline, manifest, buildTrackGroupArray(formats)); this(timeline, manifest, buildTrackGroupArray(formats));
} }
/** /**
* Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with the * Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with the
* given {@link TrackGroupArray}. * given {@link TrackGroupArray}. The provided {@link Timeline} may be null to prevent an
* immediate source info refresh message when preparing the media source. It can be manually set
* later using {@link #setNewSourceInfo(Timeline, Object)}.
*/ */
public FakeMediaSource(Timeline timeline, Object manifest, TrackGroupArray trackGroupArray) { public FakeMediaSource(@Nullable Timeline timeline, Object manifest,
TrackGroupArray trackGroupArray) {
this.timeline = timeline; this.timeline = timeline;
this.manifest = manifest; this.manifest = manifest;
this.activeMediaPeriods = new ArrayList<>(); this.activeMediaPeriods = new ArrayList<>();
@ -67,7 +74,10 @@ public class FakeMediaSource implements MediaSource {
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) { public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
Assert.assertFalse(preparedSource); Assert.assertFalse(preparedSource);
preparedSource = true; preparedSource = true;
listener.onSourceInfoRefreshed(this, timeline, manifest); this.listener = listener;
if (timeline != null) {
listener.onSourceInfoRefreshed(this, timeline, manifest);
}
} }
@Override @Override
@ -77,9 +87,9 @@ public class FakeMediaSource implements MediaSource {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount());
Assert.assertTrue(preparedSource); Assert.assertTrue(preparedSource);
Assert.assertFalse(releasedSource); Assert.assertFalse(releasedSource);
Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount());
FakeMediaPeriod mediaPeriod = createFakeMediaPeriod(id, trackGroupArray, allocator); FakeMediaPeriod mediaPeriod = createFakeMediaPeriod(id, trackGroupArray, allocator);
activeMediaPeriods.add(mediaPeriod); activeMediaPeriods.add(mediaPeriod);
createdMediaPeriods.add(id); createdMediaPeriods.add(id);
@ -103,11 +113,23 @@ public class FakeMediaSource implements MediaSource {
releasedSource = true; releasedSource = true;
} }
/**
* Sets a new timeline and manifest. If the source is already prepared, this triggers a source
* info refresh message being sent to the listener.
*/
public void setNewSourceInfo(Timeline newTimeline, Object manifest) {
Assert.assertFalse(releasedSource);
this.timeline = newTimeline;
if (preparedSource) {
listener.onSourceInfoRefreshed(this, timeline, manifest);
}
}
/** /**
* Assert that the source and all periods have been released. * Assert that the source and all periods have been released.
*/ */
public void assertReleased() { public void assertReleased() {
Assert.assertTrue(releasedSource); Assert.assertTrue(releasedSource || !preparedSource);
} }
/** /**