Commit c7c9a1e9 by tonihei Committed by Oliver Woodman

Send media source events for fake media sources.

This allows to test sending events when using fake media sources.
The FakeMediaSource now simulates a manifest load when being prepared.
And the FakeMediaPeriod simulates a media load when being prepared.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=189205285
parent bb72d9eb
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Timeline.Window; ...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdPlaybackState; import com.google.android.exoplayer2.source.ads.AdPlaybackState;
...@@ -573,8 +574,11 @@ public final class ExoPlayerTest { ...@@ -573,8 +574,11 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) { new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
@Override @Override
protected FakeMediaPeriod createFakeMediaPeriod( protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) { MediaPeriodId id,
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray); TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher) {
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
mediaPeriod.setSeekToUsOffset(10); mediaPeriod.setSeekToUsOffset(10);
return mediaPeriod; return mediaPeriod;
} }
...@@ -604,8 +608,11 @@ public final class ExoPlayerTest { ...@@ -604,8 +608,11 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) { new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
@Override @Override
protected FakeMediaPeriod createFakeMediaPeriod( protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) { MediaPeriodId id,
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray); TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher) {
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
mediaPeriod.setDiscontinuityPositionUs(10); mediaPeriod.setDiscontinuityPositionUs(10);
return mediaPeriod; return mediaPeriod;
} }
...@@ -626,8 +633,11 @@ public final class ExoPlayerTest { ...@@ -626,8 +633,11 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) { new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
@Override @Override
protected FakeMediaPeriod createFakeMediaPeriod( protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) { MediaPeriodId id,
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray); TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher) {
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
mediaPeriod.setDiscontinuityPositionUs(0); mediaPeriod.setDiscontinuityPositionUs(0);
return mediaPeriod; return mediaPeriod;
} }
...@@ -878,10 +888,13 @@ public final class ExoPlayerTest { ...@@ -878,10 +888,13 @@ public final class ExoPlayerTest {
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), null, Builder.VIDEO_FORMAT) { new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), null, Builder.VIDEO_FORMAT) {
@Override @Override
protected FakeMediaPeriod createFakeMediaPeriod( protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) { MediaPeriodId id,
TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher) {
// Defer completing preparation of the period until playback parameters have been set. // Defer completing preparation of the period until playback parameters have been set.
fakeMediaPeriodHolder[0] = fakeMediaPeriodHolder[0] =
new FakeMediaPeriod(trackGroupArray, /* deferOnPrepared= */ true); new FakeMediaPeriod(trackGroupArray, eventDispatcher, /* deferOnPrepared= */ true);
createPeriodCalledCountDownLatch.countDown(); createPeriodCalledCountDownLatch.countDown();
return fakeMediaPeriodHolder[0]; return fakeMediaPeriodHolder[0];
} }
......
...@@ -92,7 +92,7 @@ public class SimpleDecoderAudioRendererTest { ...@@ -92,7 +92,7 @@ public class SimpleDecoderAudioRendererTest {
audioRenderer.enable( audioRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {FORMAT}, new Format[] {FORMAT},
new FakeSampleStream(FORMAT, false), new FakeSampleStream(FORMAT, /* eventDispatcher= */ null, /* shouldOutputSample= */ false),
0, 0,
false, false,
0); 0);
......
...@@ -35,7 +35,6 @@ import java.util.List; ...@@ -35,7 +35,6 @@ import java.util.List;
public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
implements SequenceableLoader.Callback<ChunkSampleStream<FakeChunkSource>> { implements SequenceableLoader.Callback<ChunkSampleStream<FakeChunkSource>> {
private final EventDispatcher eventDispatcher;
private final Allocator allocator; private final Allocator allocator;
private final FakeChunkSource.Factory chunkSourceFactory; private final FakeChunkSource.Factory chunkSourceFactory;
private final long durationUs; private final long durationUs;
...@@ -50,8 +49,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod ...@@ -50,8 +49,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
Allocator allocator, Allocator allocator,
FakeChunkSource.Factory chunkSourceFactory, FakeChunkSource.Factory chunkSourceFactory,
long durationUs) { long durationUs) {
super(trackGroupArray); super(trackGroupArray, eventDispatcher);
this.eventDispatcher = eventDispatcher;
this.allocator = allocator; this.allocator = allocator;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.durationUs = durationUs; this.durationUs = durationUs;
......
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.Timeline; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
...@@ -44,10 +45,12 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource { ...@@ -44,10 +45,12 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource {
} }
@Override @Override
protected FakeMediaPeriod createFakeMediaPeriod(MediaPeriodId id, TrackGroupArray trackGroupArray, protected FakeMediaPeriod createFakeMediaPeriod(
Allocator allocator) { MediaPeriodId id,
TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher) {
Period period = timeline.getPeriod(id.periodIndex, new Period()); Period period = timeline.getPeriod(id.periodIndex, new Period());
MediaSourceEventListener.EventDispatcher eventDispatcher = createEventDispatcher(id);
return new FakeAdaptiveMediaPeriod( return new FakeAdaptiveMediaPeriod(
trackGroupArray, eventDispatcher, allocator, chunkSourceFactory, period.durationUs); trackGroupArray, eventDispatcher, allocator, chunkSourceFactory, period.durationUs);
} }
......
...@@ -17,24 +17,32 @@ package com.google.android.exoplayer2.testutil; ...@@ -17,24 +17,32 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DataSpec;
import java.io.IOException; import java.io.IOException;
/** /**
* Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting * Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting
* tracks will give the player {@link FakeSampleStream}s. * tracks will give the player {@link FakeSampleStream}s. Loading data completes immediately after
* the period has finished preparing.
*/ */
public class FakeMediaPeriod implements MediaPeriod { public class FakeMediaPeriod implements MediaPeriod {
public static final DataSpec FAKE_DATA_SPEC = new DataSpec(Uri.parse("http://fake.uri"));
private final TrackGroupArray trackGroupArray; private final TrackGroupArray trackGroupArray;
protected final EventDispatcher eventDispatcher;
@Nullable private Handler playerHandler; @Nullable private Handler playerHandler;
@Nullable private Callback prepareCallback; @Nullable private Callback prepareCallback;
...@@ -46,19 +54,23 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -46,19 +54,23 @@ public class FakeMediaPeriod implements MediaPeriod {
/** /**
* @param trackGroupArray The track group array. * @param trackGroupArray The track group array.
* @param eventDispatcher A dispatcher for media source events.
*/ */
public FakeMediaPeriod(TrackGroupArray trackGroupArray) { public FakeMediaPeriod(TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher) {
this(trackGroupArray, false); this(trackGroupArray, eventDispatcher, /* deferOnPrepared */ false);
} }
/** /**
* @param trackGroupArray The track group array. * @param trackGroupArray The track group array.
* @param eventDispatcher A dispatcher for media source events.
* @param deferOnPrepared Whether {@link MediaPeriod.Callback#onPrepared(MediaPeriod)} should be * @param deferOnPrepared Whether {@link MediaPeriod.Callback#onPrepared(MediaPeriod)} should be
* called only after {@link #setPreparationComplete()} has been called. If {@code false} * called only after {@link #setPreparationComplete()} has been called. If {@code false}
* preparation completes immediately. * preparation completes immediately.
*/ */
public FakeMediaPeriod(TrackGroupArray trackGroupArray, boolean deferOnPrepared) { public FakeMediaPeriod(
TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher, boolean deferOnPrepared) {
this.trackGroupArray = trackGroupArray; this.trackGroupArray = trackGroupArray;
this.eventDispatcher = eventDispatcher;
this.deferOnPrepared = deferOnPrepared; this.deferOnPrepared = deferOnPrepared;
discontinuityPositionUs = C.TIME_UNSET; discontinuityPositionUs = C.TIME_UNSET;
} }
...@@ -79,11 +91,11 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -79,11 +91,11 @@ public class FakeMediaPeriod implements MediaPeriod {
public synchronized void setPreparationComplete() { public synchronized void setPreparationComplete() {
deferOnPrepared = false; deferOnPrepared = false;
if (playerHandler != null && prepareCallback != null) { if (playerHandler != null && prepareCallback != null) {
playerHandler.post(new Runnable() { playerHandler.post(
new Runnable() {
@Override @Override
public void run() { public void run() {
prepared = true; finishPreparation();
prepareCallback.onPrepared(FakeMediaPeriod.this);
} }
}); });
} }
...@@ -104,12 +116,21 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -104,12 +116,21 @@ public class FakeMediaPeriod implements MediaPeriod {
@Override @Override
public synchronized void prepare(Callback callback, long positionUs) { public synchronized void prepare(Callback callback, long positionUs) {
eventDispatcher.loadStarted(
FAKE_DATA_SPEC,
C.DATA_TYPE_MEDIA,
C.TRACK_TYPE_UNKNOWN,
/* trackFormat= */ null,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
/* mediaStartTimeUs= */ 0,
/* mediaEndTimeUs = */ C.TIME_UNSET,
SystemClock.elapsedRealtime());
prepareCallback = callback;
if (deferOnPrepared) { if (deferOnPrepared) {
playerHandler = new Handler(); playerHandler = new Handler();
prepareCallback = callback;
} else { } else {
prepared = true; finishPreparation();
callback.onPrepared(this);
} }
} }
...@@ -196,7 +217,24 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -196,7 +217,24 @@ public class FakeMediaPeriod implements MediaPeriod {
} }
protected SampleStream createSampleStream(TrackSelection selection) { protected SampleStream createSampleStream(TrackSelection selection) {
return new FakeSampleStream(selection.getSelectedFormat()); return new FakeSampleStream(
selection.getSelectedFormat(), eventDispatcher, /* shouldOutputSample= */ true);
} }
private void finishPreparation() {
prepared = true;
prepareCallback.onPrepared(this);
eventDispatcher.loadCompleted(
FAKE_DATA_SPEC,
C.DATA_TYPE_MEDIA,
C.TRACK_TYPE_UNKNOWN,
/* trackFormat= */ null,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
/* mediaStartTimeUs= */ 0,
/* mediaEndTimeUs = */ C.TIME_UNSET,
SystemClock.elapsedRealtime(),
/* loadDurationMs= */ 0,
/* bytesLoaded= */ 100);
}
} }
...@@ -17,17 +17,25 @@ package com.google.android.exoplayer2.testutil; ...@@ -17,17 +17,25 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
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;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.source.BaseMediaSource; import com.google.android.exoplayer2.source.BaseMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.MediaSourceEventListener.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaSourceEventListener.MediaLoadData;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -39,6 +47,9 @@ import java.util.List; ...@@ -39,6 +47,9 @@ import java.util.List;
*/ */
public class FakeMediaSource extends BaseMediaSource { public class FakeMediaSource extends BaseMediaSource {
private static final DataSpec FAKE_DATA_SPEC = new DataSpec(Uri.parse("http://manifest.uri"));
private static final int MANIFEST_LOAD_BYTES = 100;
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;
...@@ -81,7 +92,7 @@ public class FakeMediaSource extends BaseMediaSource { ...@@ -81,7 +92,7 @@ public class FakeMediaSource extends BaseMediaSource {
releasedSource = false; releasedSource = false;
sourceInfoRefreshHandler = new Handler(); sourceInfoRefreshHandler = new Handler();
if (timeline != null) { if (timeline != null) {
refreshSourceInfo(timeline, manifest); finishSourcePreparation();
} }
} }
...@@ -95,7 +106,11 @@ public class FakeMediaSource extends BaseMediaSource { ...@@ -95,7 +106,11 @@ public class FakeMediaSource extends BaseMediaSource {
assertThat(preparedSource).isTrue(); assertThat(preparedSource).isTrue();
assertThat(releasedSource).isFalse(); assertThat(releasedSource).isFalse();
Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount()); Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount());
FakeMediaPeriod mediaPeriod = createFakeMediaPeriod(id, trackGroupArray, allocator); Period period = timeline.getPeriod(id.periodIndex, new Period());
EventDispatcher eventDispatcher =
createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs());
FakeMediaPeriod mediaPeriod =
createFakeMediaPeriod(id, trackGroupArray, allocator, eventDispatcher);
activeMediaPeriods.add(mediaPeriod); activeMediaPeriods.add(mediaPeriod);
createdMediaPeriods.add(id); createdMediaPeriods.add(id);
return mediaPeriod; return mediaPeriod;
...@@ -135,7 +150,7 @@ public class FakeMediaSource extends BaseMediaSource { ...@@ -135,7 +150,7 @@ public class FakeMediaSource extends BaseMediaSource {
assertThat(preparedSource).isTrue(); assertThat(preparedSource).isTrue();
timeline = newTimeline; timeline = newTimeline;
manifest = newManifest; manifest = newManifest;
refreshSourceInfo(timeline, manifest); finishSourcePreparation();
} }
}); });
} else { } else {
...@@ -163,9 +178,51 @@ public class FakeMediaSource extends BaseMediaSource { ...@@ -163,9 +178,51 @@ public class FakeMediaSource extends BaseMediaSource {
return createdMediaPeriods; return createdMediaPeriods;
} }
protected FakeMediaPeriod createFakeMediaPeriod(MediaPeriodId id, TrackGroupArray trackGroupArray, /**
Allocator allocator) { * Creates a {@link FakeMediaPeriod} for this media source.
return new FakeMediaPeriod(trackGroupArray); *
* @param id The identifier of the period.
* @param trackGroupArray The {@link TrackGroupArray} supported by the media period.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param eventDispatcher An {@link EventDispatcher} to dispatch media source events.
* @return A new {@link FakeMediaPeriod}.
*/
protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id,
TrackGroupArray trackGroupArray,
Allocator allocator,
EventDispatcher eventDispatcher) {
return new FakeMediaPeriod(trackGroupArray, eventDispatcher);
}
private void finishSourcePreparation() {
refreshSourceInfo(timeline, manifest);
if (!timeline.isEmpty()) {
MediaLoadData mediaLoadData =
new MediaLoadData(
/* windowIndex= */ 0,
/* mediaPeriodId= */ null,
C.DATA_TYPE_MANIFEST,
C.TRACK_TYPE_UNKNOWN,
/* trackFormat= */ null,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
/* mediaStartTimeMs= */ C.TIME_UNSET,
/* mediaEndTimeMs = */ C.TIME_UNSET);
long elapsedRealTimeMs = SystemClock.elapsedRealtime();
EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
eventDispatcher.loadStarted(
new LoadEventInfo(
FAKE_DATA_SPEC, elapsedRealTimeMs, /* loadDurationMs= */ 0, /* bytesLoaded= */ 0),
mediaLoadData);
eventDispatcher.loadCompleted(
new LoadEventInfo(
FAKE_DATA_SPEC,
elapsedRealTimeMs,
/* loadDurationMs= */ 0,
/* bytesLoaded= */ MANIFEST_LOAD_BYTES),
mediaLoadData);
}
} }
private static TrackGroupArray buildTrackGroupArray(Format... formats) { private static TrackGroupArray buildTrackGroupArray(Format... formats) {
......
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import java.io.IOException; import java.io.IOException;
...@@ -29,16 +31,23 @@ import java.io.IOException; ...@@ -29,16 +31,23 @@ import java.io.IOException;
public final class FakeSampleStream implements SampleStream { public final class FakeSampleStream implements SampleStream {
private final Format format; private final Format format;
private final @Nullable EventDispatcher eventDispatcher;
private boolean readFormat; private boolean readFormat;
private boolean readSample; private boolean readSample;
public FakeSampleStream(Format format) { /**
this(format, true); * Creates fake sample stream which outputs the given {@link Format}, optionally one sample with
} * zero bytes, then end of stream.
*
public FakeSampleStream(Format format, boolean shouldOutputSample) { * @param format The {@link Format} to output.
* @param eventDispatcher An {@link EventDispatcher} to notify of read events.
* @param shouldOutputSample Whether the sample stream should output a sample.
*/
public FakeSampleStream(
Format format, @Nullable EventDispatcher eventDispatcher, boolean shouldOutputSample) {
this.format = format; this.format = format;
this.eventDispatcher = eventDispatcher;
readSample = !shouldOutputSample; readSample = !shouldOutputSample;
} }
...@@ -60,6 +69,14 @@ public final class FakeSampleStream implements SampleStream { ...@@ -60,6 +69,14 @@ public final class FakeSampleStream implements SampleStream {
buffer.data.put((byte) 0); buffer.data.put((byte) 0);
buffer.flip(); buffer.flip();
readSample = true; readSample = true;
if (eventDispatcher != null) {
eventDispatcher.downstreamFormatChanged(
C.TRACK_TYPE_UNKNOWN,
format,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
/* mediaTimeUs= */ 0);
}
return C.RESULT_BUFFER_READ; return C.RESULT_BUFFER_READ;
} else { } else {
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import android.os.ConditionVariable; import android.os.ConditionVariable;
...@@ -29,10 +30,18 @@ import com.google.android.exoplayer2.Timeline; ...@@ -29,10 +30,18 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.MediaSourceEventListener.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaSourceEventListener.MediaLoadData;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -50,6 +59,7 @@ public class MediaSourceTestRunner { ...@@ -50,6 +59,7 @@ public class MediaSourceTestRunner {
private final Allocator allocator; private final Allocator allocator;
private final LinkedBlockingDeque<Timeline> timelines; private final LinkedBlockingDeque<Timeline> timelines;
private final CopyOnWriteArraySet<MediaLoadData> completedLoads;
private Timeline timeline; private Timeline timeline;
/** /**
...@@ -66,6 +76,8 @@ public class MediaSourceTestRunner { ...@@ -66,6 +76,8 @@ public class MediaSourceTestRunner {
player = new EventHandlingExoPlayer(playbackLooper); player = new EventHandlingExoPlayer(playbackLooper);
mediaSourceListener = new MediaSourceListener(); mediaSourceListener = new MediaSourceListener();
timelines = new LinkedBlockingDeque<>(); timelines = new LinkedBlockingDeque<>();
completedLoads = new CopyOnWriteArraySet<>();
mediaSource.addEventListener(playbackHandler, mediaSourceListener);
} }
/** /**
...@@ -280,18 +292,99 @@ public class MediaSourceTestRunner { ...@@ -280,18 +292,99 @@ public class MediaSourceTestRunner {
releasePeriod(secondMediaPeriod); releasePeriod(secondMediaPeriod);
} }
/**
* Asserts that the media source reported completed loads via {@link
* MediaSourceEventListener#onLoadCompleted(LoadEventInfo, MediaLoadData)} for each specified
* window index and a null period id. Also asserts that no other loads with media period id null
* are reported.
*/
public void assertCompletedManifestLoads(Integer... windowIndices) {
List<Integer> expectedWindowIndices = new ArrayList<>(Arrays.asList(windowIndices));
for (MediaLoadData mediaLoadData : completedLoads) {
if (mediaLoadData.mediaPeriodId == null) {
boolean loadExpected = expectedWindowIndices.remove((Integer) mediaLoadData.windowIndex);
assertThat(loadExpected).isTrue();
}
}
assertWithMessage("Not all expected media source loads have been completed.")
.that(expectedWindowIndices)
.isEmpty();
}
/**
* Asserts that the media source reported completed loads via {@link
* MediaSourceEventListener#onLoadCompleted(LoadEventInfo, MediaLoadData)} for each specified
* media period id, and asserts that the associated window index matches the one in the last known
* timeline returned from {@link #prepareSource()}, {@link #assertTimelineChange()} or {@link
* #assertTimelineChangeBlocking()}.
*/
public void assertCompletedMediaPeriodLoads(MediaPeriodId... mediaPeriodIds) {
Timeline.Period period = new Timeline.Period();
HashSet<MediaPeriodId> expectedLoads = new HashSet<>(Arrays.asList(mediaPeriodIds));
for (MediaLoadData mediaLoadData : completedLoads) {
if (expectedLoads.remove(mediaLoadData.mediaPeriodId)) {
assertThat(mediaLoadData.windowIndex)
.isEqualTo(
timeline.getPeriod(mediaLoadData.mediaPeriodId.periodIndex, period).windowIndex);
}
}
assertWithMessage("Not all expected media source loads have been completed.")
.that(expectedLoads)
.isEmpty();
}
/** Releases the runner. Should be called when the runner is no longer required. */ /** Releases the runner. Should be called when the runner is no longer required. */
public void release() { public void release() {
playbackThread.quit(); playbackThread.quit();
} }
private class MediaSourceListener implements MediaSource.SourceInfoRefreshListener { private class MediaSourceListener
implements MediaSource.SourceInfoRefreshListener, MediaSourceEventListener {
// SourceInfoRefreshListener methods.
@Override @Override
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) { public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper()); Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
timelines.addLast(timeline); timelines.addLast(timeline);
} }
// MediaSourceEventListener methods.
@Override
public void onLoadStarted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
}
@Override
public void onLoadCompleted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
completedLoads.add(mediaLoadData);
}
@Override
public void onLoadCanceled(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
}
@Override
public void onLoadError(
LoadEventInfo loadEventInfo,
MediaLoadData mediaLoadData,
IOException error,
boolean wasCanceled) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
}
@Override
public void onUpstreamDiscarded(MediaLoadData mediaLoadData) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
}
@Override
public void onDownstreamFormatChanged(MediaLoadData mediaLoadData) {
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
}
} }
private static class EventHandlingExoPlayer extends StubExoPlayer private static class EventHandlingExoPlayer extends StubExoPlayer
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment