Skip to content

Commit

Permalink
Add method to FakeMediaSource to trigger source info refresh.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tonihei authored and ojw28 committed Nov 17, 2017
1 parent 22b9503 commit 5aa053d
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,12 @@
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.source.MediaSource.Listener;
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.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.TimelineAsserts;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException;
import java.util.Arrays;
import junit.framework.TestCase;
import org.mockito.Mockito;
Expand Down Expand Up @@ -216,38 +213,48 @@ public void testPlaylistChangesBeforePreparation() throws InterruptedException {

public void testPlaylistWithLazyMediaSource() throws InterruptedException {
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++) {
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();
mediaSource.addMediaSource(lazySources[0]);
mediaSource.addMediaSource(0, childSources[0]);
mediaSource.addMediaSource(0, fastSources[0]);
mediaSource.removeMediaSource(1);
mediaSource.addMediaSource(1, lazySources[1]);
assertNull(timeline);

// Prepare and assert that the timeline contains all information for normal sources while having
// placeholder information for lazy sources.
prepareAndListenToTimelineUpdates(mediaSource);
waitForTimelineUpdate();
assertNotNull(timeline);
TimelineAsserts.assertPeriodCounts(timeline, 1, 1);
TimelineAsserts.assertWindowIds(timeline, 111, null);
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();
TimelineAsserts.assertPeriodCounts(timeline, 1, 9);
TimelineAsserts.assertWindowIds(timeline, 111, 999);
TimelineAsserts.assertWindowIsDynamic(timeline, false, false);
TimelineAsserts.assertAllPeriodsCanBeCreatedPreparedAndReleased(mediaSource, timeline,
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]);
waitForTimelineUpdate();
mediaSource.addMediaSource(2, childSources[1]);
mediaSource.addMediaSource(2, fastSources[1]);
waitForTimelineUpdate();
mediaSource.addMediaSource(0, lazySources[3]);
waitForTimelineUpdate();
Expand All @@ -257,6 +264,8 @@ public void testPlaylistWithLazyMediaSource() throws InterruptedException {
TimelineAsserts.assertWindowIds(timeline, null, 111, 222, 999);
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);
assertNotNull(lazyPeriod);
final ConditionVariable lazyPeriodPrepared = new ConditionVariable();
Expand All @@ -269,21 +278,29 @@ public void onPrepared(MediaPeriod mediaPeriod) {
public void onContinueLoadingRequested(MediaPeriod source) {}
}, 0);
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);
assertNotNull(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();
TimelineAsserts.assertPeriodCounts(timeline, 8, 1, 2, 9);
TimelineAsserts.assertWindowIds(timeline, 888, 111, 222, 999);
TimelineAsserts.assertWindowIsDynamic(timeline, false, false, false, false);
assertTrue(lazyPeriodPrepared.block(TIMEOUT_MS));
mediaSource.releasePeriod(lazyPeriod);

// Release media source and assert all normal and lazy media sources are fully released as well.
mediaSource.releaseSource();
childSources[0].assertReleased();
childSources[1].assertReleased();
for (FakeMediaSource fastSource : fastSources) {
fastSource.assertReleased();
}
for (FakeMediaSource lazySource : lazySources) {
lazySource.assertReleased();
}
}

public void testEmptyTimelineMediaSource() throws InterruptedException {
Expand Down Expand Up @@ -662,38 +679,6 @@ public DynamicConcatenatingMediaSourceAndHandler(DynamicConcatenatingMediaSource

}

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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.testutil;

import android.support.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Timeline;
Expand All @@ -34,28 +35,34 @@
*/
public class FakeMediaSource implements MediaSource {

protected final Timeline timeline;
private final Object manifest;
private final TrackGroupArray trackGroupArray;
private final ArrayList<FakeMediaPeriod> activeMediaPeriods;
private final ArrayList<MediaPeriodId> createdMediaPeriods;

protected Timeline timeline;
private boolean preparedSource;
private boolean releasedSource;
private Listener listener;

/**
* 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));
}

/**
* 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.manifest = manifest;
this.activeMediaPeriods = new ArrayList<>();
Expand All @@ -67,7 +74,10 @@ public FakeMediaSource(Timeline timeline, Object manifest, TrackGroupArray track
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
Assert.assertFalse(preparedSource);
preparedSource = true;
listener.onSourceInfoRefreshed(this, timeline, manifest);
this.listener = listener;
if (timeline != null) {
listener.onSourceInfoRefreshed(this, timeline, manifest);
}
}

@Override
Expand All @@ -77,9 +87,9 @@ public void maybeThrowSourceInfoRefreshError() throws IOException {

@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount());
Assert.assertTrue(preparedSource);
Assert.assertFalse(releasedSource);
Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount());
FakeMediaPeriod mediaPeriod = createFakeMediaPeriod(id, trackGroupArray, allocator);
activeMediaPeriods.add(mediaPeriod);
createdMediaPeriods.add(id);
Expand All @@ -103,11 +113,23 @@ public void releaseSource() {
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.
*/
public void assertReleased() {
Assert.assertTrue(releasedSource);
Assert.assertTrue(releasedSource || !preparedSource);
}

/**
Expand Down

0 comments on commit 5aa053d

Please sign in to comment.