mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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
This commit is contained in:
parent
bb72d9eb6a
commit
c7c9a1e9e4
@ -295,33 +295,40 @@ public interface MediaSourceEventListener {
|
||||
|
||||
/** Dispatches {@link #onLoadStarted(LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadStarted(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final @Nullable Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final @Nullable Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs) {
|
||||
for (final ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
listenerAndHandler.handler.post(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
@Nullable Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
@Nullable Object trackSelectionData,
|
||||
long mediaStartTimeUs,
|
||||
long mediaEndTimeUs,
|
||||
long elapsedRealtimeMs) {
|
||||
loadStarted(
|
||||
new LoadEventInfo(
|
||||
dataSpec, elapsedRealtimeMs, /* loadDurationMs= */ 0, /* bytesLoaded= */ 0),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onLoadStarted(LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadStarted(final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
Handler handler = listenerAndHandler.handler;
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listenerAndHandler.listener.onLoadStarted(
|
||||
new LoadEventInfo(
|
||||
dataSpec, elapsedRealtimeMs, /* loadDurationMs= */ 0, /* bytesLoaded= */ 0),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
listener.onLoadStarted(loadEventInfo, mediaLoadData);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -350,34 +357,42 @@ public interface MediaSourceEventListener {
|
||||
|
||||
/** Dispatches {@link #onLoadCompleted(LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadCompleted(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final @Nullable Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final @Nullable Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs,
|
||||
final long loadDurationMs,
|
||||
final long bytesLoaded) {
|
||||
for (final ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
listenerAndHandler.handler.post(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
@Nullable Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
@Nullable Object trackSelectionData,
|
||||
long mediaStartTimeUs,
|
||||
long mediaEndTimeUs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded) {
|
||||
loadCompleted(
|
||||
new LoadEventInfo(dataSpec, elapsedRealtimeMs, loadDurationMs, bytesLoaded),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onLoadCompleted(LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadCompleted(
|
||||
final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
Handler handler = listenerAndHandler.handler;
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listenerAndHandler.listener.onLoadCompleted(
|
||||
new LoadEventInfo(dataSpec, elapsedRealtimeMs, loadDurationMs, bytesLoaded),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
listener.onLoadCompleted(loadEventInfo, mediaLoadData);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -406,34 +421,41 @@ public interface MediaSourceEventListener {
|
||||
|
||||
/** Dispatches {@link #onLoadCanceled(LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadCanceled(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final @Nullable Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final @Nullable Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs,
|
||||
final long loadDurationMs,
|
||||
final long bytesLoaded) {
|
||||
for (final ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
listenerAndHandler.handler.post(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
@Nullable Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
@Nullable Object trackSelectionData,
|
||||
long mediaStartTimeUs,
|
||||
long mediaEndTimeUs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded) {
|
||||
loadCanceled(
|
||||
new LoadEventInfo(dataSpec, elapsedRealtimeMs, loadDurationMs, bytesLoaded),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onLoadCanceled(LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadCanceled(final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
Handler handler = listenerAndHandler.handler;
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listenerAndHandler.listener.onLoadCanceled(
|
||||
new LoadEventInfo(dataSpec, elapsedRealtimeMs, loadDurationMs, bytesLoaded),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
listener.onLoadCanceled(loadEventInfo, mediaLoadData);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -466,62 +488,79 @@ public interface MediaSourceEventListener {
|
||||
|
||||
/** Dispatches {@link #onLoadError(LoadEventInfo, MediaLoadData, IOException, boolean)}. */
|
||||
public void loadError(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final @Nullable Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final @Nullable Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs,
|
||||
final long loadDurationMs,
|
||||
final long bytesLoaded,
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
@Nullable Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
@Nullable Object trackSelectionData,
|
||||
long mediaStartTimeUs,
|
||||
long mediaEndTimeUs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded,
|
||||
IOException error,
|
||||
boolean wasCanceled) {
|
||||
loadError(
|
||||
new LoadEventInfo(dataSpec, elapsedRealtimeMs, loadDurationMs, bytesLoaded),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)),
|
||||
error,
|
||||
wasCanceled);
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onLoadError(LoadEventInfo, MediaLoadData, IOException, boolean)}. */
|
||||
public void loadError(
|
||||
final LoadEventInfo loadEventInfo,
|
||||
final MediaLoadData mediaLoadData,
|
||||
final IOException error,
|
||||
final boolean wasCanceled) {
|
||||
for (final ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
listenerAndHandler.handler.post(
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
Handler handler = listenerAndHandler.handler;
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listenerAndHandler.listener.onLoadError(
|
||||
new LoadEventInfo(dataSpec, elapsedRealtimeMs, loadDurationMs, bytesLoaded),
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)),
|
||||
error,
|
||||
wasCanceled);
|
||||
listener.onLoadError(loadEventInfo, mediaLoadData, error, wasCanceled);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onUpstreamDiscarded(MediaLoadData)}. */
|
||||
public void upstreamDiscarded(
|
||||
final int trackType, final long mediaStartTimeUs, final long mediaEndTimeUs) {
|
||||
for (final ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
listenerAndHandler.handler.post(
|
||||
public void upstreamDiscarded(int trackType, long mediaStartTimeUs, long mediaEndTimeUs) {
|
||||
upstreamDiscarded(
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
C.DATA_TYPE_MEDIA,
|
||||
trackType,
|
||||
/* trackFormat= */ null,
|
||||
C.SELECTION_REASON_ADAPTIVE,
|
||||
/* trackSelectionData= */ null,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onUpstreamDiscarded(MediaLoadData)}. */
|
||||
public void upstreamDiscarded(final MediaLoadData mediaLoadData) {
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
Handler handler = listenerAndHandler.handler;
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listenerAndHandler.listener.onUpstreamDiscarded(
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
C.DATA_TYPE_MEDIA,
|
||||
trackType,
|
||||
/* trackFormat= */ null,
|
||||
C.SELECTION_REASON_ADAPTIVE,
|
||||
/* trackSelectionData= */ null,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs)));
|
||||
listener.onUpstreamDiscarded(mediaLoadData);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -529,27 +568,34 @@ public interface MediaSourceEventListener {
|
||||
|
||||
/** Dispatches {@link #onDownstreamFormatChanged(MediaLoadData)}. */
|
||||
public void downstreamFormatChanged(
|
||||
final int trackType,
|
||||
final @Nullable Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final @Nullable Object trackSelectionData,
|
||||
final long mediaTimeUs) {
|
||||
for (final ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
listenerAndHandler.handler.post(
|
||||
int trackType,
|
||||
@Nullable Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
@Nullable Object trackSelectionData,
|
||||
long mediaTimeUs) {
|
||||
downstreamFormatChanged(
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
C.DATA_TYPE_MEDIA,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaTimeUs),
|
||||
/* mediaEndTimeMs= */ C.TIME_UNSET));
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onDownstreamFormatChanged(MediaLoadData)}. */
|
||||
public void downstreamFormatChanged(final MediaLoadData mediaLoadData) {
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
Handler handler = listenerAndHandler.handler;
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listenerAndHandler.listener.onDownstreamFormatChanged(
|
||||
new MediaLoadData(
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
C.DATA_TYPE_MEDIA,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaTimeUs),
|
||||
/* mediaEndTimeMs= */ C.TIME_UNSET));
|
||||
listener.onDownstreamFormatChanged(mediaLoadData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Timeline.Window;
|
||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
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.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||
@ -573,8 +574,11 @@ public final class ExoPlayerTest {
|
||||
new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
|
||||
@Override
|
||||
protected FakeMediaPeriod createFakeMediaPeriod(
|
||||
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray);
|
||||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
EventDispatcher eventDispatcher) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
mediaPeriod.setSeekToUsOffset(10);
|
||||
return mediaPeriod;
|
||||
}
|
||||
@ -604,8 +608,11 @@ public final class ExoPlayerTest {
|
||||
new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
|
||||
@Override
|
||||
protected FakeMediaPeriod createFakeMediaPeriod(
|
||||
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray);
|
||||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
EventDispatcher eventDispatcher) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
mediaPeriod.setDiscontinuityPositionUs(10);
|
||||
return mediaPeriod;
|
||||
}
|
||||
@ -626,8 +633,11 @@ public final class ExoPlayerTest {
|
||||
new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
|
||||
@Override
|
||||
protected FakeMediaPeriod createFakeMediaPeriod(
|
||||
MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray);
|
||||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
EventDispatcher eventDispatcher) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
mediaPeriod.setDiscontinuityPositionUs(0);
|
||||
return mediaPeriod;
|
||||
}
|
||||
@ -878,10 +888,13 @@ public final class ExoPlayerTest {
|
||||
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), null, Builder.VIDEO_FORMAT) {
|
||||
@Override
|
||||
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.
|
||||
fakeMediaPeriodHolder[0] =
|
||||
new FakeMediaPeriod(trackGroupArray, /* deferOnPrepared= */ true);
|
||||
new FakeMediaPeriod(trackGroupArray, eventDispatcher, /* deferOnPrepared= */ true);
|
||||
createPeriodCalledCountDownLatch.countDown();
|
||||
return fakeMediaPeriodHolder[0];
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ public class SimpleDecoderAudioRendererTest {
|
||||
audioRenderer.enable(
|
||||
RendererConfiguration.DEFAULT,
|
||||
new Format[] {FORMAT},
|
||||
new FakeSampleStream(FORMAT, false),
|
||||
new FakeSampleStream(FORMAT, /* eventDispatcher= */ null, /* shouldOutputSample= */ false),
|
||||
0,
|
||||
false,
|
||||
0);
|
||||
|
@ -35,7 +35,6 @@ import java.util.List;
|
||||
public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
||||
implements SequenceableLoader.Callback<ChunkSampleStream<FakeChunkSource>> {
|
||||
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final Allocator allocator;
|
||||
private final FakeChunkSource.Factory chunkSourceFactory;
|
||||
private final long durationUs;
|
||||
@ -50,8 +49,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
||||
Allocator allocator,
|
||||
FakeChunkSource.Factory chunkSourceFactory,
|
||||
long durationUs) {
|
||||
super(trackGroupArray);
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
super(trackGroupArray, eventDispatcher);
|
||||
this.allocator = allocator;
|
||||
this.chunkSourceFactory = chunkSourceFactory;
|
||||
this.durationUs = durationUs;
|
||||
|
@ -20,6 +20,7 @@ import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Period;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
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.upstream.Allocator;
|
||||
|
||||
@ -44,10 +45,12 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FakeMediaPeriod createFakeMediaPeriod(MediaPeriodId id, TrackGroupArray trackGroupArray,
|
||||
Allocator allocator) {
|
||||
protected FakeMediaPeriod createFakeMediaPeriod(
|
||||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
EventDispatcher eventDispatcher) {
|
||||
Period period = timeline.getPeriod(id.periodIndex, new Period());
|
||||
MediaSourceEventListener.EventDispatcher eventDispatcher = createEventDispatcher(id);
|
||||
return new FakeAdaptiveMediaPeriod(
|
||||
trackGroupArray, eventDispatcher, allocator, chunkSourceFactory, period.durationUs);
|
||||
}
|
||||
|
@ -17,24 +17,32 @@ package com.google.android.exoplayer2.testutil;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.SeekParameters;
|
||||
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.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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 static final DataSpec FAKE_DATA_SPEC = new DataSpec(Uri.parse("http://fake.uri"));
|
||||
|
||||
private final TrackGroupArray trackGroupArray;
|
||||
protected final EventDispatcher eventDispatcher;
|
||||
|
||||
@Nullable private Handler playerHandler;
|
||||
@Nullable private Callback prepareCallback;
|
||||
@ -46,19 +54,23 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
|
||||
/**
|
||||
* @param trackGroupArray The track group array.
|
||||
* @param eventDispatcher A dispatcher for media source events.
|
||||
*/
|
||||
public FakeMediaPeriod(TrackGroupArray trackGroupArray) {
|
||||
this(trackGroupArray, false);
|
||||
public FakeMediaPeriod(TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher) {
|
||||
this(trackGroupArray, eventDispatcher, /* deferOnPrepared */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trackGroupArray The track group array.
|
||||
* @param eventDispatcher A dispatcher for media source events.
|
||||
* @param deferOnPrepared Whether {@link MediaPeriod.Callback#onPrepared(MediaPeriod)} should be
|
||||
* called only after {@link #setPreparationComplete()} has been called. If {@code false}
|
||||
* preparation completes immediately.
|
||||
*/
|
||||
public FakeMediaPeriod(TrackGroupArray trackGroupArray, boolean deferOnPrepared) {
|
||||
public FakeMediaPeriod(
|
||||
TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher, boolean deferOnPrepared) {
|
||||
this.trackGroupArray = trackGroupArray;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
this.deferOnPrepared = deferOnPrepared;
|
||||
discontinuityPositionUs = C.TIME_UNSET;
|
||||
}
|
||||
@ -79,13 +91,13 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
public synchronized void setPreparationComplete() {
|
||||
deferOnPrepared = false;
|
||||
if (playerHandler != null && prepareCallback != null) {
|
||||
playerHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
prepared = true;
|
||||
prepareCallback.onPrepared(FakeMediaPeriod.this);
|
||||
}
|
||||
});
|
||||
playerHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finishPreparation();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,12 +116,21 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
playerHandler = new Handler();
|
||||
prepareCallback = callback;
|
||||
} else {
|
||||
prepared = true;
|
||||
callback.onPrepared(this);
|
||||
finishPreparation();
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +217,24 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
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.MediaPeriod;
|
||||
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.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -39,6 +47,9 @@ import java.util.List;
|
||||
*/
|
||||
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 ArrayList<FakeMediaPeriod> activeMediaPeriods;
|
||||
private final ArrayList<MediaPeriodId> createdMediaPeriods;
|
||||
@ -81,7 +92,7 @@ public class FakeMediaSource extends BaseMediaSource {
|
||||
releasedSource = false;
|
||||
sourceInfoRefreshHandler = new Handler();
|
||||
if (timeline != null) {
|
||||
refreshSourceInfo(timeline, manifest);
|
||||
finishSourcePreparation();
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +106,11 @@ public class FakeMediaSource extends BaseMediaSource {
|
||||
assertThat(preparedSource).isTrue();
|
||||
assertThat(releasedSource).isFalse();
|
||||
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);
|
||||
createdMediaPeriods.add(id);
|
||||
return mediaPeriod;
|
||||
@ -135,7 +150,7 @@ public class FakeMediaSource extends BaseMediaSource {
|
||||
assertThat(preparedSource).isTrue();
|
||||
timeline = newTimeline;
|
||||
manifest = newManifest;
|
||||
refreshSourceInfo(timeline, manifest);
|
||||
finishSourcePreparation();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -163,9 +178,51 @@ public class FakeMediaSource extends BaseMediaSource {
|
||||
return createdMediaPeriods;
|
||||
}
|
||||
|
||||
protected FakeMediaPeriod createFakeMediaPeriod(MediaPeriodId id, TrackGroupArray trackGroupArray,
|
||||
Allocator allocator) {
|
||||
return new FakeMediaPeriod(trackGroupArray);
|
||||
/**
|
||||
* Creates a {@link FakeMediaPeriod} for this media source.
|
||||
*
|
||||
* @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) {
|
||||
|
@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.testutil;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@ -29,16 +31,23 @@ import java.io.IOException;
|
||||
public final class FakeSampleStream implements SampleStream {
|
||||
|
||||
private final Format format;
|
||||
private final @Nullable EventDispatcher eventDispatcher;
|
||||
|
||||
private boolean readFormat;
|
||||
private boolean readSample;
|
||||
|
||||
public FakeSampleStream(Format format) {
|
||||
this(format, true);
|
||||
}
|
||||
|
||||
public FakeSampleStream(Format format, boolean shouldOutputSample) {
|
||||
/**
|
||||
* Creates fake sample stream which outputs the given {@link Format}, optionally one sample with
|
||||
* zero bytes, then end of stream.
|
||||
*
|
||||
* @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.eventDispatcher = eventDispatcher;
|
||||
readSample = !shouldOutputSample;
|
||||
}
|
||||
|
||||
@ -60,6 +69,14 @@ public final class FakeSampleStream implements SampleStream {
|
||||
buffer.data.put((byte) 0);
|
||||
buffer.flip();
|
||||
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;
|
||||
} else {
|
||||
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer2.testutil;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.os.ConditionVariable;
|
||||
@ -29,10 +30,18 @@ import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
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.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
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.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -50,6 +59,7 @@ public class MediaSourceTestRunner {
|
||||
private final Allocator allocator;
|
||||
|
||||
private final LinkedBlockingDeque<Timeline> timelines;
|
||||
private final CopyOnWriteArraySet<MediaLoadData> completedLoads;
|
||||
private Timeline timeline;
|
||||
|
||||
/**
|
||||
@ -66,6 +76,8 @@ public class MediaSourceTestRunner {
|
||||
player = new EventHandlingExoPlayer(playbackLooper);
|
||||
mediaSourceListener = new MediaSourceListener();
|
||||
timelines = new LinkedBlockingDeque<>();
|
||||
completedLoads = new CopyOnWriteArraySet<>();
|
||||
mediaSource.addEventListener(playbackHandler, mediaSourceListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,18 +292,99 @@ public class MediaSourceTestRunner {
|
||||
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. */
|
||||
public void release() {
|
||||
playbackThread.quit();
|
||||
}
|
||||
|
||||
private class MediaSourceListener implements MediaSource.SourceInfoRefreshListener {
|
||||
private class MediaSourceListener
|
||||
implements MediaSource.SourceInfoRefreshListener, MediaSourceEventListener {
|
||||
|
||||
// SourceInfoRefreshListener methods.
|
||||
|
||||
@Override
|
||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
|
||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user