Commit 30ee29df by tonihei Committed by Oliver Woodman

Use real SampleQueue in FakeSampleStream.

This replaces all the duplicated logic previously implemented in
FakeSampleStream and more closely follows the pattern of how
SampleStreams are used from real MediaPeriods.

Some tests needed adjustments because using real the SampleQueue
improved behaviour:
 - Waiting for isLoading is only needed once even across period
   boundaries because the real SampleQueue doesn't have the on/off
   pattern.
 - AnalyticsCollectorTest.playlistOperations() was wrongly asserting
   that some pre-buffering events. The new version is more intuitively
   correct we pre-buffer the second item during the initial loading
   phase (thus period1seq1) and keep the buffer in the queue after
   the removal operation.

PiperOrigin-RevId: 348440255
parent cf0a4e52
...@@ -70,7 +70,6 @@ import com.google.android.exoplayer2.source.MediaPeriod; ...@@ -70,7 +70,6 @@ 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;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SilenceMediaSource; import com.google.android.exoplayer2.source.SilenceMediaSource;
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;
...@@ -90,6 +89,7 @@ import com.google.android.exoplayer2.testutil.FakeMediaPeriod; ...@@ -90,6 +89,7 @@ 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.FakeRenderer; import com.google.android.exoplayer2.testutil.FakeRenderer;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
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;
...@@ -98,7 +98,6 @@ import com.google.android.exoplayer2.testutil.FakeTrackSelector; ...@@ -98,7 +98,6 @@ import com.google.android.exoplayer2.testutil.FakeTrackSelector;
import com.google.android.exoplayer2.testutil.NoUidTimeline; import com.google.android.exoplayer2.testutil.NoUidTimeline;
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder; import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocation; import com.google.android.exoplayer2.upstream.Allocation;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
...@@ -663,6 +662,7 @@ public final class ExoPlayerTest { ...@@ -663,6 +662,7 @@ public final class ExoPlayerTest {
FakeMediaPeriod mediaPeriod = FakeMediaPeriod mediaPeriod =
new FakeMediaPeriod( new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
...@@ -707,6 +707,7 @@ public final class ExoPlayerTest { ...@@ -707,6 +707,7 @@ public final class ExoPlayerTest {
FakeMediaPeriod mediaPeriod = FakeMediaPeriod mediaPeriod =
new FakeMediaPeriod( new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher); mediaSourceEventDispatcher);
mediaPeriod.setDiscontinuityPositionUs(10); mediaPeriod.setDiscontinuityPositionUs(10);
...@@ -739,6 +740,7 @@ public final class ExoPlayerTest { ...@@ -739,6 +740,7 @@ public final class ExoPlayerTest {
FakeMediaPeriod mediaPeriod = FakeMediaPeriod mediaPeriod =
new FakeMediaPeriod( new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher); mediaSourceEventDispatcher);
// Set a discontinuity at the position this period is supposed to start at anyway. // Set a discontinuity at the position this period is supposed to start at anyway.
...@@ -986,6 +988,7 @@ public final class ExoPlayerTest { ...@@ -986,6 +988,7 @@ public final class ExoPlayerTest {
fakeMediaPeriodHolder[0] = fakeMediaPeriodHolder[0] =
new FakeMediaPeriod( new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
...@@ -1039,6 +1042,7 @@ public final class ExoPlayerTest { ...@@ -1039,6 +1042,7 @@ public final class ExoPlayerTest {
fakeMediaPeriodHolder[0] = fakeMediaPeriodHolder[0] =
new FakeMediaPeriod( new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
...@@ -4292,17 +4296,19 @@ public final class ExoPlayerTest { ...@@ -4292,17 +4296,19 @@ public final class ExoPlayerTest {
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
DrmSessionEventListener.EventDispatcher drmEventDispatcher, DrmSessionEventListener.EventDispatcher drmEventDispatcher,
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
FakeMediaPeriod fakeMediaPeriod = return new FakeMediaPeriod(
new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
FakeMediaPeriod.TrackDataFactory.singleSampleWithTimeUs(/* sampleTimeUs= */ 0), allocator,
/* trackDataFactory= */ (format, mediaPeriodId) ->
ImmutableList.of(
oneByteSample(windowOffsetInFirstPeriodUs, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(
windowOffsetInFirstPeriodUs + C.msToUs(maxBufferedPositionMs),
C.BUFFER_FLAG_KEY_FRAME)),
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
drmEventDispatcher, drmEventDispatcher,
/* deferOnPrepared= */ false); /* deferOnPrepared= */ false);
fakeMediaPeriod.setBufferedPositionUs(
windowOffsetInFirstPeriodUs + C.msToUs(maxBufferedPositionMs));
return fakeMediaPeriod;
} }
}; };
} }
...@@ -4336,12 +4342,10 @@ public final class ExoPlayerTest { ...@@ -4336,12 +4342,10 @@ public final class ExoPlayerTest {
boolean[] isPlayingAd = new boolean[3]; boolean[] isPlayingAd = new boolean[3];
ActionSchedule actionSchedule = ActionSchedule actionSchedule =
new ActionSchedule.Builder(TAG) new ActionSchedule.Builder(TAG)
.waitForPlaybackState(Player.STATE_READY) .pause()
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.pause() .waitForPlaybackState(Player.STATE_READY)
.executeRunnable( .executeRunnable(
new PlayerRunnable() { new PlayerRunnable() {
@Override @Override
...@@ -4395,19 +4399,19 @@ public final class ExoPlayerTest { ...@@ -4395,19 +4399,19 @@ public final class ExoPlayerTest {
assertThat(isPlayingAd[0]).isTrue(); assertThat(isPlayingAd[0]).isTrue();
assertThat(positionMs[0]).isAtMost(adDurationMs); assertThat(positionMs[0]).isAtMost(adDurationMs);
assertThat(bufferedPositionMs[0]).isEqualTo(adDurationMs); assertThat(bufferedPositionMs[0]).isEqualTo(adDurationMs);
assertThat(totalBufferedDurationMs[0]).isEqualTo(adDurationMs - positionMs[0]); assertThat(totalBufferedDurationMs[0]).isAtLeast(adDurationMs - positionMs[0]);
assertThat(windowIndex[1]).isEqualTo(0); assertThat(windowIndex[1]).isEqualTo(0);
assertThat(isPlayingAd[1]).isTrue(); assertThat(isPlayingAd[1]).isTrue();
assertThat(positionMs[1]).isAtMost(adDurationMs); assertThat(positionMs[1]).isAtMost(adDurationMs);
assertThat(bufferedPositionMs[1]).isEqualTo(adDurationMs); assertThat(bufferedPositionMs[1]).isEqualTo(adDurationMs);
assertThat(totalBufferedDurationMs[1]).isEqualTo(adDurationMs - positionMs[1]); assertThat(totalBufferedDurationMs[1]).isAtLeast(adDurationMs - positionMs[1]);
assertThat(windowIndex[2]).isEqualTo(0); assertThat(windowIndex[2]).isEqualTo(0);
assertThat(isPlayingAd[2]).isFalse(); assertThat(isPlayingAd[2]).isFalse();
assertThat(positionMs[2]).isGreaterThan(8000); assertThat(positionMs[2]).isGreaterThan(8000);
assertThat(bufferedPositionMs[2]).isEqualTo(contentDurationMs); assertThat(bufferedPositionMs[2]).isEqualTo(contentDurationMs);
assertThat(totalBufferedDurationMs[2]).isEqualTo(contentDurationMs - positionMs[2]); assertThat(totalBufferedDurationMs[2]).isAtLeast(contentDurationMs - positionMs[2]);
} }
@Test @Test
...@@ -4669,6 +4673,7 @@ public final class ExoPlayerTest { ...@@ -4669,6 +4673,7 @@ public final class ExoPlayerTest {
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher) { mediaSourceEventDispatcher) {
@Override @Override
...@@ -7054,8 +7059,6 @@ public final class ExoPlayerTest { ...@@ -7054,8 +7059,6 @@ public final class ExoPlayerTest {
// Wait until fully buffered so that the new renderer can be enabled immediately. // Wait until fully buffered so that the new renderer can be enabled immediately.
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.removeMediaItem(0) .removeMediaItem(0)
.build(); .build();
ExoPlayerTestRunner testRunner = ExoPlayerTestRunner testRunner =
...@@ -7220,6 +7223,7 @@ public final class ExoPlayerTest { ...@@ -7220,6 +7223,7 @@ public final class ExoPlayerTest {
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
trackDataWithoutEos, trackDataWithoutEos,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
...@@ -7284,6 +7288,14 @@ public final class ExoPlayerTest { ...@@ -7284,6 +7288,14 @@ public final class ExoPlayerTest {
@Override @Override
public void cancelLoad() {} public void cancelLoad() {}
}; };
// Create 3 samples without end of stream signal to test that all 3 samples are
// still played before the sample stream exception is thrown.
FakeSampleStreamItem sample =
oneByteSample(
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
C.BUFFER_FLAG_KEY_FRAME);
FakeMediaPeriod.TrackDataFactory threeSamplesWithoutEos =
(format, mediaPeriodId) -> ImmutableList.of(sample, sample, sample);
MediaSource largeBufferAllocatingMediaSource = MediaSource largeBufferAllocatingMediaSource =
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT) { new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT) {
@Override @Override
...@@ -7297,7 +7309,8 @@ public final class ExoPlayerTest { ...@@ -7297,7 +7309,8 @@ public final class ExoPlayerTest {
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, allocator,
threeSamplesWithoutEos,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
drmEventDispatcher, drmEventDispatcher,
...@@ -7306,30 +7319,29 @@ public final class ExoPlayerTest { ...@@ -7306,30 +7319,29 @@ public final class ExoPlayerTest {
@Override @Override
public boolean continueLoading(long positionUs) { public boolean continueLoading(long positionUs) {
super.continueLoading(positionUs);
if (!loader.isLoading()) {
loader.startLoading( loader.startLoading(
loadable, new FakeLoaderCallback(), /* defaultMinRetryCount= */ 1); loadable, new FakeLoaderCallback(), /* defaultMinRetryCount= */ 1);
}
return true; return true;
} }
@Override @Override
protected SampleStream createSampleStream( protected FakeSampleStream createSampleStream(
long positionUs, Allocator allocator,
TrackSelection selection, @Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
DrmSessionEventListener.EventDispatcher drmEventDispatcher) { DrmSessionEventListener.EventDispatcher drmEventDispatcher,
// Create 3 samples without end of stream signal to test that all 3 samples are Format initialFormat,
// still played before the exception is thrown. List<FakeSampleStreamItem> fakeSampleStreamItems) {
return new FakeSampleStream( return new FakeSampleStream(
allocator,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
drmEventDispatcher, drmEventDispatcher,
selection.getSelectedFormat(), initialFormat,
ImmutableList.of( fakeSampleStreamItems) {
oneByteSample(positionUs),
oneByteSample(positionUs),
oneByteSample(positionUs))) {
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
loader.maybeThrowError(); loader.maybeThrowError();
...@@ -7498,14 +7510,16 @@ public final class ExoPlayerTest { ...@@ -7498,14 +7510,16 @@ public final class ExoPlayerTest {
/* timeline= */ null, /* timeline= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
(unusedFormat, unusedMediaPeriodId) -> (unusedFormat, unusedMediaPeriodId) ->
ImmutableList.of(oneByteSample(firstSampleTimeUs), END_OF_STREAM_ITEM), ImmutableList.of(
oneByteSample(firstSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM),
ExoPlayerTestRunner.VIDEO_FORMAT); ExoPlayerTestRunner.VIDEO_FORMAT);
FakeMediaSource secondMediaSource = FakeMediaSource secondMediaSource =
new FakeMediaSource( new FakeMediaSource(
timelineWithOffsets, timelineWithOffsets,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
(unusedFormat, unusedMediaPeriodId) -> (unusedFormat, unusedMediaPeriodId) ->
ImmutableList.of(oneByteSample(firstSampleTimeUs), END_OF_STREAM_ITEM), ImmutableList.of(
oneByteSample(firstSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM),
ExoPlayerTestRunner.VIDEO_FORMAT); ExoPlayerTestRunner.VIDEO_FORMAT);
player.setMediaSources(ImmutableList.of(firstMediaSource, secondMediaSource)); player.setMediaSources(ImmutableList.of(firstMediaSource, secondMediaSource));
...@@ -8033,6 +8047,7 @@ public final class ExoPlayerTest { ...@@ -8033,6 +8047,7 @@ public final class ExoPlayerTest {
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
/* singleSampleTimeUs= */ 0, /* singleSampleTimeUs= */ 0,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
...@@ -8077,20 +8092,28 @@ public final class ExoPlayerTest { ...@@ -8077,20 +8092,28 @@ public final class ExoPlayerTest {
DrmSessionEventListener.EventDispatcher drmEventDispatcher, DrmSessionEventListener.EventDispatcher drmEventDispatcher,
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArray, /* singleSampleTimeUs= */ 0, mediaSourceEventDispatcher) { trackGroupArray,
allocator,
/* trackDataFactory= */ (format, mediaPeriodId) -> ImmutableList.of(),
mediaSourceEventDispatcher,
drmSessionManager,
drmEventDispatcher,
/* deferOnPrepared= */ false) {
@Override @Override
protected SampleStream createSampleStream( protected FakeSampleStream createSampleStream(
long positionUs, Allocator allocator,
TrackSelection selection, @Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
DrmSessionEventListener.EventDispatcher drmEventDispatcher) { DrmSessionEventListener.EventDispatcher drmEventDispatcher,
Format initialFormat,
List<FakeSampleStreamItem> fakeSampleStreamItems) {
return new FakeSampleStream( return new FakeSampleStream(
allocator,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
DrmSessionManager.DUMMY, drmSessionManager,
drmEventDispatcher, drmEventDispatcher,
selection.getSelectedFormat(), initialFormat,
/* fakeSampleStreamItems= */ ImmutableList.of()) { fakeSampleStreamItems) {
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
throw new IOException(); throw new IOException();
......
...@@ -29,7 +29,6 @@ import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DR ...@@ -29,7 +29,6 @@ import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DR
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DROPPED_VIDEO_FRAMES; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DROPPED_VIDEO_FRAMES;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_IS_LOADING_CHANGED; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_IS_LOADING_CHANGED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_IS_PLAYING_CHANGED; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_IS_PLAYING_CHANGED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_CANCELED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_COMPLETED; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_COMPLETED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_ERROR; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_ERROR;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_STARTED; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_LOAD_STARTED;
...@@ -283,7 +282,7 @@ public final class AnalyticsCollectorTest { ...@@ -283,7 +282,7 @@ public final class AnalyticsCollectorTest {
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1); assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly(period0, period0, period0, period0) .containsExactly(period0, period0)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly(period0, period1) .containsExactly(period0, period1)
...@@ -365,7 +364,7 @@ public final class AnalyticsCollectorTest { ...@@ -365,7 +364,7 @@ public final class AnalyticsCollectorTest {
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1); assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly(period0, period0, period0, period0) .containsExactly(period0, period0)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly(period0, period1) .containsExactly(period0, period1)
...@@ -428,8 +427,6 @@ public final class AnalyticsCollectorTest { ...@@ -428,8 +427,6 @@ public final class AnalyticsCollectorTest {
// Wait until second period has fully loaded to assert loading events without flakiness. // Wait until second period has fully loaded to assert loading events without flakiness.
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.seek(/* windowIndex= */ 1, /* positionMs= */ 0) .seek(/* windowIndex= */ 1, /* positionMs= */ 0)
.play() .play()
.build(); .build();
...@@ -453,9 +450,9 @@ public final class AnalyticsCollectorTest { ...@@ -453,9 +450,9 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1); assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period1); assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period1);
List<EventWindowAndPeriodId> loadingEvents = listener.getEvents(EVENT_IS_LOADING_CHANGED); assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
assertThat(loadingEvents).hasSize(4); .containsExactly(period0, period0)
assertThat(loadingEvents).containsAtLeast(period0, period0).inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly(period0, period1, period1) .containsExactly(period0, period1, period1)
.inOrder(); .inOrder();
...@@ -555,7 +552,7 @@ public final class AnalyticsCollectorTest { ...@@ -555,7 +552,7 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly(period0, period0, period0, period0, period0, period0) .containsExactly(period0, period0, period0, period0)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly(period0, period1Seq2) .containsExactly(period0, period1Seq2)
...@@ -851,8 +848,7 @@ public final class AnalyticsCollectorTest { ...@@ -851,8 +848,7 @@ public final class AnalyticsCollectorTest {
period1Seq0 /* SOURCE_UPDATE (child sources in concatenating source moved) */) period1Seq0 /* SOURCE_UPDATE (child sources in concatenating source moved) */)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly( .containsExactly(window0Period1Seq0, window0Period1Seq0, period1Seq0, period1Seq0);
window0Period1Seq0, window0Period1Seq0, window0Period1Seq0, window0Period1Seq0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(window0Period1Seq0); assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(window0Period1Seq0);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
...@@ -960,27 +956,27 @@ public final class AnalyticsCollectorTest { ...@@ -960,27 +956,27 @@ public final class AnalyticsCollectorTest {
.containsExactly(WINDOW_0 /* manifest */, period0Seq0 /* media */, period1Seq1 /* media */) .containsExactly(WINDOW_0 /* manifest */, period0Seq0 /* media */, period1Seq1 /* media */)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(period0Seq0, period0Seq1) .containsExactly(period0Seq0, period1Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(period0Seq0, period0Seq1, period0Seq1) .containsExactly(period0Seq0, period1Seq1, period0Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(period0Seq0, period0Seq1) .containsExactly(period0Seq0, period1Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(period0Seq0, period0Seq1) .containsExactly(period0Seq0, period1Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)) assertThat(listener.getEvents(EVENT_DECODER_DISABLED))
.containsExactly(period0Seq0, period0Seq0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)) assertThat(listener.getEvents(EVENT_VIDEO_ENABLED))
.containsExactly(period0Seq0, period0Seq1, period0Seq1) .containsExactly(period0Seq0, period1Seq1, period0Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)) assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED))
.containsExactly(period0Seq0, period0Seq1) .containsExactly(period0Seq0, period1Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_VIDEO_INPUT_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_VIDEO_INPUT_FORMAT_CHANGED))
.containsExactly(period0Seq0, period0Seq1) .containsExactly(period0Seq0, period1Seq1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0Seq0, period0Seq0); assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0Seq1); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0Seq1);
...@@ -1022,14 +1018,19 @@ public final class AnalyticsCollectorTest { ...@@ -1022,14 +1018,19 @@ public final class AnalyticsCollectorTest {
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
(unusedFormat, mediaPeriodId) -> { (unusedFormat, mediaPeriodId) -> {
if (mediaPeriodId.isAd()) { if (mediaPeriodId.isAd()) {
return ImmutableList.of(oneByteSample(/* timeUs= */ 0), END_OF_STREAM_ITEM); return ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM);
} else { } else {
// Provide a single sample before and after the midroll ad and another after the // Provide a single sample before and after the midroll ad and another after the
// postroll. // postroll.
return ImmutableList.of( return ImmutableList.of(
oneByteSample(windowOffsetInFirstPeriodUs + C.MICROS_PER_SECOND), oneByteSample(
oneByteSample(windowOffsetInFirstPeriodUs + 6 * C.MICROS_PER_SECOND), windowOffsetInFirstPeriodUs + C.MICROS_PER_SECOND, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(windowOffsetInFirstPeriodUs + contentDurationsUs), oneByteSample(
windowOffsetInFirstPeriodUs + 6 * C.MICROS_PER_SECOND,
C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(
windowOffsetInFirstPeriodUs + contentDurationsUs, C.BUFFER_FLAG_KEY_FRAME),
END_OF_STREAM_ITEM); END_OF_STREAM_ITEM);
} }
}, },
...@@ -1073,16 +1074,6 @@ public final class AnalyticsCollectorTest { ...@@ -1073,16 +1074,6 @@ public final class AnalyticsCollectorTest {
// Ensure everything is preloaded. // Ensure everything is preloaded.
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForPlaybackState(Player.STATE_READY) .waitForPlaybackState(Player.STATE_READY)
// Wait in each content part to ensure previously triggered events get a chance to be // Wait in each content part to ensure previously triggered events get a chance to be
// delivered. This prevents flakiness caused by playback progressing too fast. // delivered. This prevents flakiness caused by playback progressing too fast.
...@@ -1161,9 +1152,7 @@ public final class AnalyticsCollectorTest { ...@@ -1161,9 +1152,7 @@ public final class AnalyticsCollectorTest {
contentAfterPreroll, midrollAd, contentAfterMidroll, postrollAd, contentAfterPostroll) contentAfterPreroll, midrollAd, contentAfterMidroll, postrollAd, contentAfterPostroll)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly( .containsExactly(prerollAd, prerollAd)
prerollAd, prerollAd, prerollAd, prerollAd, prerollAd, prerollAd, prerollAd, prerollAd,
prerollAd, prerollAd, prerollAd, prerollAd)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly( .containsExactly(
...@@ -1289,12 +1278,16 @@ public final class AnalyticsCollectorTest { ...@@ -1289,12 +1278,16 @@ public final class AnalyticsCollectorTest {
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
(unusedFormat, mediaPeriodId) -> { (unusedFormat, mediaPeriodId) -> {
if (mediaPeriodId.isAd()) { if (mediaPeriodId.isAd()) {
return ImmutableList.of(oneByteSample(/* timeUs= */ 0), END_OF_STREAM_ITEM); return ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM);
} else { } else {
// Provide a sample before the midroll and another after the seek point below (6s). // Provide a sample before the midroll and another after the seek point below (6s).
return ImmutableList.of( return ImmutableList.of(
oneByteSample(windowOffsetInFirstPeriodUs + C.MICROS_PER_SECOND), oneByteSample(
oneByteSample(windowOffsetInFirstPeriodUs + 7 * C.MICROS_PER_SECOND), windowOffsetInFirstPeriodUs + C.MICROS_PER_SECOND, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(
windowOffsetInFirstPeriodUs + 7 * C.MICROS_PER_SECOND,
C.BUFFER_FLAG_KEY_FRAME),
END_OF_STREAM_ITEM); END_OF_STREAM_ITEM);
} }
}, },
...@@ -1305,10 +1298,6 @@ public final class AnalyticsCollectorTest { ...@@ -1305,10 +1298,6 @@ public final class AnalyticsCollectorTest {
// Ensure everything is preloaded. // Ensure everything is preloaded.
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
// Seek behind the midroll. // Seek behind the midroll.
.seek(6 * C.MICROS_PER_SECOND) .seek(6 * C.MICROS_PER_SECOND)
// Wait until loading started again to assert loading events without flakiness. // Wait until loading started again to assert loading events without flakiness.
...@@ -1361,10 +1350,6 @@ public final class AnalyticsCollectorTest { ...@@ -1361,10 +1350,6 @@ public final class AnalyticsCollectorTest {
.containsExactly( .containsExactly(
contentBeforeMidroll, contentBeforeMidroll,
contentBeforeMidroll, contentBeforeMidroll,
contentBeforeMidroll,
contentBeforeMidroll,
contentBeforeMidroll,
contentBeforeMidroll,
midrollAd, midrollAd,
midrollAd) midrollAd)
.inOrder(); .inOrder();
......
...@@ -38,6 +38,7 @@ import com.google.android.exoplayer2.drm.DrmSessionEventListener; ...@@ -38,6 +38,7 @@ import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.junit.Before; import org.junit.Before;
...@@ -102,15 +103,19 @@ public class DecoderAudioRendererTest { ...@@ -102,15 +103,19 @@ public class DecoderAudioRendererTest {
@Test @Test
public void immediatelyReadEndOfStreamPlaysAudioSinkToEndOfStream() throws Exception { public void immediatelyReadEndOfStreamPlaysAudioSinkToEndOfStream() throws Exception {
audioRenderer.enable( FakeSampleStream fakeSampleStream =
RendererConfiguration.DEFAULT,
new Format[] {FORMAT},
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
FORMAT, FORMAT,
ImmutableList.of(END_OF_STREAM_ITEM)), ImmutableList.of(END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
audioRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {FORMAT},
fakeSampleStream,
/* positionUs= */ 0, /* positionUs= */ 0,
/* joining= */ false, /* joining= */ false,
/* mayRenderStartOfStream= */ true, /* mayRenderStartOfStream= */ true,
......
...@@ -43,6 +43,7 @@ import com.google.android.exoplayer2.drm.DrmSessionManager; ...@@ -43,6 +43,7 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.Collections; import java.util.Collections;
...@@ -118,6 +119,7 @@ public class MediaCodecAudioRendererTest { ...@@ -118,6 +119,7 @@ public class MediaCodecAudioRendererTest {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
...@@ -131,6 +133,7 @@ public class MediaCodecAudioRendererTest { ...@@ -131,6 +133,7 @@ public class MediaCodecAudioRendererTest {
oneByteSample(/* timeUs= */ 200, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 200, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(/* timeUs= */ 250, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 250, C.BUFFER_FLAG_KEY_FRAME),
END_OF_STREAM_ITEM)); END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecAudioRenderer.enable( mediaCodecAudioRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -173,6 +176,7 @@ public class MediaCodecAudioRendererTest { ...@@ -173,6 +176,7 @@ public class MediaCodecAudioRendererTest {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
...@@ -186,6 +190,7 @@ public class MediaCodecAudioRendererTest { ...@@ -186,6 +190,7 @@ public class MediaCodecAudioRendererTest {
oneByteSample(/* timeUs= */ 200, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 200, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(/* timeUs= */ 250, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 250, C.BUFFER_FLAG_KEY_FRAME),
END_OF_STREAM_ITEM)); END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecAudioRenderer.enable( mediaCodecAudioRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -249,12 +254,14 @@ public class MediaCodecAudioRendererTest { ...@@ -249,12 +254,14 @@ public class MediaCodecAudioRendererTest {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ AUDIO_AAC, /* initialFormat= */ AUDIO_AAC,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
exceptionThrowingRenderer.enable( exceptionThrowingRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
......
...@@ -15,11 +15,14 @@ ...@@ -15,11 +15,14 @@
*/ */
package com.google.android.exoplayer2.metadata; package com.google.android.exoplayer2.metadata;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.sample;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionEventListener;
...@@ -29,8 +32,8 @@ import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder; ...@@ -29,8 +32,8 @@ import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer2.metadata.scte35.TimeSignalCommand; import com.google.android.exoplayer2.metadata.scte35.TimeSignalCommand;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
...@@ -144,16 +147,19 @@ public class MetadataRendererTest { ...@@ -144,16 +147,19 @@ public class MetadataRendererTest {
private static List<Metadata> runRenderer(byte[] input) throws ExoPlaybackException { private static List<Metadata> runRenderer(byte[] input) throws ExoPlaybackException {
List<Metadata> metadata = new ArrayList<>(); List<Metadata> metadata = new ArrayList<>();
MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null); MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null);
renderer.replaceStream( FakeSampleStream fakeSampleStream =
new Format[] {EMSG_FORMAT},
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
EMSG_FORMAT, EMSG_FORMAT,
ImmutableList.of( ImmutableList.of(
FakeSampleStreamItem.sample(/* timeUs= */ 0, /* flags= */ 0, input), sample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME, input), END_OF_STREAM_ITEM));
FakeSampleStreamItem.END_OF_STREAM_ITEM)), fakeSampleStream.writeData(/* startPositionUs= */ 0);
renderer.replaceStream(
new Format[] {EMSG_FORMAT},
fakeSampleStream,
/* startPositionUs= */ 0L, /* startPositionUs= */ 0L,
/* offsetUs= */ 0L); /* offsetUs= */ 0L);
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the format renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the format
......
...@@ -493,6 +493,7 @@ public class DownloadHelperTest { ...@@ -493,6 +493,7 @@ public class DownloadHelperTest {
int periodIndex = TEST_TIMELINE.getIndexOfPeriod(id.periodUid); int periodIndex = TEST_TIMELINE.getIndexOfPeriod(id.periodUid);
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArrays[periodIndex], trackGroupArrays[periodIndex],
allocator,
TEST_TIMELINE.getWindow(0, new Timeline.Window()).positionInFirstPeriodUs, TEST_TIMELINE.getWindow(0, new Timeline.Window()).positionInFirstPeriodUs,
new EventDispatcher() new EventDispatcher()
.withParameters(/* windowIndex= */ 0, id, /* mediaTimeOffsetMs= */ 0)) { .withParameters(/* windowIndex= */ 0, id, /* mediaTimeOffsetMs= */ 0)) {
......
...@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat ...@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
import com.google.android.exoplayer2.testutil.FakeMediaPeriod; import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection; import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
...@@ -82,6 +83,7 @@ public final class MergingMediaPeriodTest { ...@@ -82,6 +83,7 @@ public final class MergingMediaPeriodTest {
streams, streams,
/* streamResetFlags= */ new boolean[] {false, false, false, false}, /* streamResetFlags= */ new boolean[] {false, false, false, false},
/* positionUs= */ 0); /* positionUs= */ 0);
mergingMediaPeriod.continueLoading(/* positionUs= */ 0);
assertThat(streams[0]).isNull(); assertThat(streams[0]).isNull();
assertThat(streams[3]).isNull(); assertThat(streams[3]).isNull();
...@@ -126,6 +128,7 @@ public final class MergingMediaPeriodTest { ...@@ -126,6 +128,7 @@ public final class MergingMediaPeriodTest {
streams, streams,
/* streamResetFlags= */ new boolean[] {false, false}, /* streamResetFlags= */ new boolean[] {false, false},
/* positionUs= */ 0); /* positionUs= */ 0);
mergingMediaPeriod.continueLoading(/* positionUs= */ 0);
FormatHolder formatHolder = new FormatHolder(); FormatHolder formatHolder = new FormatHolder();
DecoderInputBuffer inputBuffer = DecoderInputBuffer inputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
...@@ -168,7 +171,8 @@ public final class MergingMediaPeriodTest { ...@@ -168,7 +171,8 @@ public final class MergingMediaPeriodTest {
/* mediaTimeOffsetMs= */ 0), /* mediaTimeOffsetMs= */ 0),
/* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) -> /* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) ->
ImmutableList.of( ImmutableList.of(
oneByteSample(definition.singleSampleTimeUs), END_OF_STREAM_ITEM)); oneByteSample(definition.singleSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME),
END_OF_STREAM_ITEM));
} }
MergingMediaPeriod mergingMediaPeriod = MergingMediaPeriod mergingMediaPeriod =
new MergingMediaPeriod( new MergingMediaPeriod(
...@@ -203,6 +207,7 @@ public final class MergingMediaPeriodTest { ...@@ -203,6 +207,7 @@ public final class MergingMediaPeriodTest {
TrackDataFactory trackDataFactory) { TrackDataFactory trackDataFactory) {
super( super(
trackGroupArray, trackGroupArray,
new DefaultAllocator(/* trimOnReset= */ false, /* individualAllocationSize= */ 1024),
trackDataFactory, trackDataFactory,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.video; package com.google.android.exoplayer2.video;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
...@@ -39,7 +40,7 @@ import com.google.android.exoplayer2.drm.DrmSessionEventListener; ...@@ -39,7 +40,7 @@ import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem; import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.concurrent.Phaser; import java.util.concurrent.Phaser;
...@@ -184,11 +185,13 @@ public final class DecoderVideoRendererTest { ...@@ -184,11 +185,13 @@ public final class DecoderVideoRendererTest {
public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception { public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of(oneByteSample(/* timeUs= */ 0))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
renderer.enable( renderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -213,11 +216,13 @@ public final class DecoderVideoRendererTest { ...@@ -213,11 +216,13 @@ public final class DecoderVideoRendererTest {
throws Exception { throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of(oneByteSample(/* timeUs= */ 0))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
renderer.enable( renderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -241,11 +246,13 @@ public final class DecoderVideoRendererTest { ...@@ -241,11 +246,13 @@ public final class DecoderVideoRendererTest {
public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception { public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of(oneByteSample(/* timeUs= */ 0))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
renderer.enable( renderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -272,20 +279,23 @@ public final class DecoderVideoRendererTest { ...@@ -272,20 +279,23 @@ public final class DecoderVideoRendererTest {
public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception { public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception {
FakeSampleStream fakeSampleStream1 = FakeSampleStream fakeSampleStream1 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
fakeSampleStream1.writeData(/* startPositionUs= */ 0);
FakeSampleStream fakeSampleStream2 = FakeSampleStream fakeSampleStream2 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of( ImmutableList.of(oneByteSample(/* timeUs= */ 0), END_OF_STREAM_ITEM));
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); fakeSampleStream2.writeData(/* startPositionUs= */ 0);
renderer.enable( renderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {H264_FORMAT}, new Format[] {H264_FORMAT},
...@@ -321,20 +331,23 @@ public final class DecoderVideoRendererTest { ...@@ -321,20 +331,23 @@ public final class DecoderVideoRendererTest {
public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception { public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception {
FakeSampleStream fakeSampleStream1 = FakeSampleStream fakeSampleStream1 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
fakeSampleStream1.writeData(/* startPositionUs= */ 0);
FakeSampleStream fakeSampleStream2 = FakeSampleStream fakeSampleStream2 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ H264_FORMAT, /* initialFormat= */ H264_FORMAT,
ImmutableList.of( ImmutableList.of(oneByteSample(/* timeUs= */ 0), END_OF_STREAM_ITEM));
oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); fakeSampleStream2.writeData(/* startPositionUs= */ 0);
renderer.enable( renderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {H264_FORMAT}, new Format[] {H264_FORMAT},
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.video; package com.google.android.exoplayer2.video;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.format; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.format;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
...@@ -47,7 +48,7 @@ import com.google.android.exoplayer2.drm.DrmSessionManager; ...@@ -47,7 +48,7 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem; import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.Collections; import java.util.Collections;
...@@ -125,6 +126,7 @@ public class MediaCodecVideoRendererTest { ...@@ -125,6 +126,7 @@ public class MediaCodecVideoRendererTest {
public void render_dropsLateBuffer() throws Exception { public void render_dropsLateBuffer() throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
...@@ -133,7 +135,8 @@ public class MediaCodecVideoRendererTest { ...@@ -133,7 +135,8 @@ public class MediaCodecVideoRendererTest {
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), // First buffer. oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), // First buffer.
oneByteSample(/* timeUs= */ 50_000), // Late buffer. oneByteSample(/* timeUs= */ 50_000), // Late buffer.
oneByteSample(/* timeUs= */ 100_000), // Last buffer. oneByteSample(/* timeUs= */ 100_000), // Last buffer.
FakeSampleStreamItem.END_OF_STREAM_ITEM)); END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264}, new Format[] {VIDEO_H264},
...@@ -160,17 +163,20 @@ public class MediaCodecVideoRendererTest { ...@@ -160,17 +163,20 @@ public class MediaCodecVideoRendererTest {
@Test @Test
public void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exception { public void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exception {
mediaCodecVideoRenderer.enable( FakeSampleStream fakeSampleStream =
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
FakeSampleStreamItem.END_OF_STREAM_ITEM)), fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream,
/* positionUs= */ 0, /* positionUs= */ 0,
/* joining= */ false, /* joining= */ false,
/* mayRenderStartOfStream= */ true, /* mayRenderStartOfStream= */ true,
...@@ -204,11 +210,13 @@ public class MediaCodecVideoRendererTest { ...@@ -204,11 +210,13 @@ public class MediaCodecVideoRendererTest {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ pAsp1, /* initialFormat= */ pAsp1,
ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -223,13 +231,16 @@ public class MediaCodecVideoRendererTest { ...@@ -223,13 +231,16 @@ public class MediaCodecVideoRendererTest {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000); mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
mediaCodecVideoRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000); mediaCodecVideoRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000);
fakeSampleStream.addFakeSampleStreamItem(format(pAsp2)); fakeSampleStream.append(
fakeSampleStream.addFakeSampleStreamItem(oneByteSample(/* timeUs= */ 5_000)); ImmutableList.of(
fakeSampleStream.addFakeSampleStreamItem(oneByteSample(/* timeUs= */ 10_000)); format(pAsp2),
fakeSampleStream.addFakeSampleStreamItem(format(pAsp3)); oneByteSample(/* timeUs= */ 5_000),
fakeSampleStream.addFakeSampleStreamItem(oneByteSample(/* timeUs= */ 15_000)); oneByteSample(/* timeUs= */ 10_000),
fakeSampleStream.addFakeSampleStreamItem(oneByteSample(/* timeUs= */ 20_000)); format(pAsp3),
fakeSampleStream.addFakeSampleStreamItem(FakeSampleStreamItem.END_OF_STREAM_ITEM); oneByteSample(/* timeUs= */ 15_000),
oneByteSample(/* timeUs= */ 20_000),
END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 5_000);
mediaCodecVideoRenderer.setCurrentStreamFinal(); mediaCodecVideoRenderer.setCurrentStreamFinal();
int pos = 500; int pos = 500;
...@@ -251,11 +262,13 @@ public class MediaCodecVideoRendererTest { ...@@ -251,11 +262,13 @@ public class MediaCodecVideoRendererTest {
throws Exception { throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264}, new Format[] {VIDEO_H264},
...@@ -270,9 +283,10 @@ public class MediaCodecVideoRendererTest { ...@@ -270,9 +283,10 @@ public class MediaCodecVideoRendererTest {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000); mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
mediaCodecVideoRenderer.resetPosition(0); mediaCodecVideoRenderer.resetPosition(0);
mediaCodecVideoRenderer.setCurrentStreamFinal(); mediaCodecVideoRenderer.setCurrentStreamFinal();
fakeSampleStream.addFakeSampleStreamItem( fakeSampleStream.append(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)); ImmutableList.of(
fakeSampleStream.addFakeSampleStreamItem(FakeSampleStreamItem.END_OF_STREAM_ITEM); oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
int positionUs = 10; int positionUs = 10;
do { do {
mediaCodecVideoRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000); mediaCodecVideoRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000);
...@@ -287,11 +301,13 @@ public class MediaCodecVideoRendererTest { ...@@ -287,11 +301,13 @@ public class MediaCodecVideoRendererTest {
public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception { public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -315,11 +331,13 @@ public class MediaCodecVideoRendererTest { ...@@ -315,11 +331,13 @@ public class MediaCodecVideoRendererTest {
throws Exception { throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of(oneByteSample(/* timeUs= */ 0))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -342,11 +360,13 @@ public class MediaCodecVideoRendererTest { ...@@ -342,11 +360,13 @@ public class MediaCodecVideoRendererTest {
public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception { public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception {
FakeSampleStream fakeSampleStream = FakeSampleStream fakeSampleStream =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
...@@ -371,22 +391,25 @@ public class MediaCodecVideoRendererTest { ...@@ -371,22 +391,25 @@ public class MediaCodecVideoRendererTest {
ShadowLooper shadowLooper = shadowOf(testMainLooper); ShadowLooper shadowLooper = shadowOf(testMainLooper);
FakeSampleStream fakeSampleStream1 = FakeSampleStream fakeSampleStream1 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
FakeSampleStreamItem.END_OF_STREAM_ITEM)); fakeSampleStream1.writeData(/* startPositionUs= */ 0);
FakeSampleStream fakeSampleStream2 = FakeSampleStream fakeSampleStream2 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 1_000_000, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 1_000_000, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM)); END_OF_STREAM_ITEM));
fakeSampleStream2.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264}, new Format[] {VIDEO_H264},
...@@ -422,22 +445,24 @@ public class MediaCodecVideoRendererTest { ...@@ -422,22 +445,24 @@ public class MediaCodecVideoRendererTest {
ShadowLooper shadowLooper = shadowOf(testMainLooper); ShadowLooper shadowLooper = shadowOf(testMainLooper);
FakeSampleStream fakeSampleStream1 = FakeSampleStream fakeSampleStream1 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
FakeSampleStreamItem.END_OF_STREAM_ITEM)); fakeSampleStream1.writeData(/* startPositionUs= */ 0);
FakeSampleStream fakeSampleStream2 = FakeSampleStream fakeSampleStream2 =
new FakeSampleStream( new FakeSampleStream(
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
/* mediaSourceEventDispatcher= */ null, /* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(), new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ VIDEO_H264, /* initialFormat= */ VIDEO_H264,
ImmutableList.of( ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
FakeSampleStreamItem.END_OF_STREAM_ITEM)); fakeSampleStream2.writeData(/* startPositionUs= */ 0);
mediaCodecVideoRenderer.enable( mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT, RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264}, new Format[] {VIDEO_H264},
......
...@@ -17,7 +17,9 @@ package com.google.android.exoplayer2.testutil; ...@@ -17,7 +17,9 @@ package com.google.android.exoplayer2.testutil;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample; import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.lang.Math.min;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
...@@ -37,29 +39,51 @@ import com.google.android.exoplayer2.source.TrackGroup; ...@@ -37,29 +39,51 @@ 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.testutil.FakeSampleStream.FakeSampleStreamItem; import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /** Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. */
* Fake {@link MediaPeriod} that provides tracks from the given {@link TrackGroupArray}. Selecting
* 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 static final DataSpec FAKE_DATA_SPEC = new DataSpec(Uri.parse("http://fake.test"));
/** A factory to create the test data for a particular track. */
public interface TrackDataFactory {
/**
* Returns the list of {@link FakeSampleStream.FakeSampleStreamItem}s that will be written the
* sample queue during playback.
*
* @param format The format of the track to provide data for.
* @param mediaPeriodId The {@link MediaPeriodId} to provide data for.
* @return The track data in the form of {@link FakeSampleStream.FakeSampleStreamItem}s.
*/
List<FakeSampleStream.FakeSampleStreamItem> create(Format format, MediaPeriodId mediaPeriodId);
/**
* Returns a factory that always provides a single keyframe sample with {@code
* time=sampleTimeUs} and then end-of-stream.
*/
static TrackDataFactory singleSampleWithTimeUs(long sampleTimeUs) {
return (unusedFormat, unusedMediaPeriodId) ->
ImmutableList.of(
oneByteSample(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM);
}
}
private final TrackGroupArray trackGroupArray; private final TrackGroupArray trackGroupArray;
private final List<SampleStream> sampleStreams; private final Set<FakeSampleStream> sampleStreams;
private final TrackDataFactory trackDataFactory; private final TrackDataFactory trackDataFactory;
private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher;
private final Allocator allocator;
private final DrmSessionManager drmSessionManager; private final DrmSessionManager drmSessionManager;
private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final DrmSessionEventListener.EventDispatcher drmEventDispatcher;
private final long fakePreparationLoadTaskId; private final long fakePreparationLoadTaskId;
...@@ -71,22 +95,25 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -71,22 +95,25 @@ public class FakeMediaPeriod implements MediaPeriod {
private boolean prepared; private boolean prepared;
private long seekOffsetUs; private long seekOffsetUs;
private long discontinuityPositionUs; private long discontinuityPositionUs;
private long bufferedPositionUs; private long lastSeekPositionUs;
/** /**
* Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}. * Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}.
* *
* @param trackGroupArray The track group array. * @param trackGroupArray The track group array.
* @param allocator An {@link Allocator}.
* @param singleSampleTimeUs The timestamp to use for the single sample in each track, in * @param singleSampleTimeUs The timestamp to use for the single sample in each track, in
* microseconds. * microseconds.
* @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events. * @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events.
*/ */
public FakeMediaPeriod( public FakeMediaPeriod(
TrackGroupArray trackGroupArray, TrackGroupArray trackGroupArray,
Allocator allocator,
long singleSampleTimeUs, long singleSampleTimeUs,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher) { MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher) {
this( this(
trackGroupArray, trackGroupArray,
allocator,
TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs), TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs),
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
DrmSessionManager.DUMMY, DrmSessionManager.DUMMY,
...@@ -98,6 +125,7 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -98,6 +125,7 @@ public class FakeMediaPeriod implements MediaPeriod {
* Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}. * Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}.
* *
* @param trackGroupArray The track group array. * @param trackGroupArray The track group array.
* @param allocator An {@link Allocator}.
* @param singleSampleTimeUs The timestamp to use for the single sample in each track, in * @param singleSampleTimeUs The timestamp to use for the single sample in each track, in
* microseconds. * microseconds.
* @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events. * @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events.
...@@ -109,6 +137,7 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -109,6 +137,7 @@ public class FakeMediaPeriod implements MediaPeriod {
*/ */
public FakeMediaPeriod( public FakeMediaPeriod(
TrackGroupArray trackGroupArray, TrackGroupArray trackGroupArray,
Allocator allocator,
long singleSampleTimeUs, long singleSampleTimeUs,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
...@@ -116,6 +145,7 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -116,6 +145,7 @@ public class FakeMediaPeriod implements MediaPeriod {
boolean deferOnPrepared) { boolean deferOnPrepared) {
this( this(
trackGroupArray, trackGroupArray,
allocator,
TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs), TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs),
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
...@@ -127,10 +157,10 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -127,10 +157,10 @@ public class FakeMediaPeriod implements MediaPeriod {
* Constructs a FakeMediaPeriod. * Constructs a FakeMediaPeriod.
* *
* @param trackGroupArray The track group array. * @param trackGroupArray The track group array.
* @param trackDataFactory A source for the underlying sample data for each track in {@code * @param allocator An {@link Allocator}.
* trackGroupArray}. * @param trackDataFactory The {@link TrackDataFactory} creating the data.
* @param mediaSourceEventDispatcher A dispatcher for media source events. * @param mediaSourceEventDispatcher A dispatcher for media source events.
* @param drmSessionManager The DrmSessionManager used for DRM interactions. * @param drmSessionManager The {@link DrmSessionManager} used for DRM interactions.
* @param drmEventDispatcher A dispatcher for {@link DrmSessionEventListener} events. * @param drmEventDispatcher A dispatcher for {@link DrmSessionEventListener} events.
* @param deferOnPrepared Whether {@link Callback#onPrepared(MediaPeriod)} should be called only * @param deferOnPrepared Whether {@link Callback#onPrepared(MediaPeriod)} should be called only
* after {@link #setPreparationComplete()} has been called. If {@code false} preparation * after {@link #setPreparationComplete()} has been called. If {@code false} preparation
...@@ -138,6 +168,7 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -138,6 +168,7 @@ public class FakeMediaPeriod implements MediaPeriod {
*/ */
public FakeMediaPeriod( public FakeMediaPeriod(
TrackGroupArray trackGroupArray, TrackGroupArray trackGroupArray,
Allocator allocator,
TrackDataFactory trackDataFactory, TrackDataFactory trackDataFactory,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
...@@ -145,13 +176,13 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -145,13 +176,13 @@ public class FakeMediaPeriod implements MediaPeriod {
boolean deferOnPrepared) { boolean deferOnPrepared) {
this.trackGroupArray = trackGroupArray; this.trackGroupArray = trackGroupArray;
this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.mediaSourceEventDispatcher = mediaSourceEventDispatcher;
this.drmSessionManager = drmSessionManager;
this.drmEventDispatcher = drmEventDispatcher;
this.deferOnPrepared = deferOnPrepared; this.deferOnPrepared = deferOnPrepared;
this.trackDataFactory = trackDataFactory; this.trackDataFactory = trackDataFactory;
this.bufferedPositionUs = C.TIME_END_OF_SOURCE; this.allocator = allocator;
this.drmSessionManager = drmSessionManager;
this.drmEventDispatcher = drmEventDispatcher;
sampleStreams = Sets.newIdentityHashSet();
discontinuityPositionUs = C.TIME_UNSET; discontinuityPositionUs = C.TIME_UNSET;
sampleStreams = new ArrayList<>();
fakePreparationLoadTaskId = LoadEventInfo.getNewId(); fakePreparationLoadTaskId = LoadEventInfo.getNewId();
} }
...@@ -184,11 +215,13 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -184,11 +215,13 @@ public class FakeMediaPeriod implements MediaPeriod {
this.seekOffsetUs = seekOffsetUs; this.seekOffsetUs = seekOffsetUs;
} }
/** Releases the media period. */
public void release() { public void release() {
prepared = false; prepared = false;
for (int i = 0; i < sampleStreams.size(); i++) { for (FakeSampleStream sampleStream : sampleStreams) {
releaseSampleStream(sampleStreams.get(i)); sampleStream.release();
} }
sampleStreams.clear();
} }
@Override @Override
...@@ -229,10 +262,11 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -229,10 +262,11 @@ public class FakeMediaPeriod implements MediaPeriod {
boolean[] streamResetFlags, boolean[] streamResetFlags,
long positionUs) { long positionUs) {
assertThat(prepared).isTrue(); assertThat(prepared).isTrue();
sampleStreams.clear();
int rendererCount = selections.length; int rendererCount = selections.length;
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) { if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
((FakeSampleStream) streams[i]).release();
sampleStreams.remove(streams[i]);
streams[i] = null; streams[i] = null;
} }
if (streams[i] == null && selections[i] != null) { if (streams[i] == null && selections[i] != null) {
...@@ -243,23 +277,31 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -243,23 +277,31 @@ public class FakeMediaPeriod implements MediaPeriod {
int indexInTrackGroup = selection.getIndexInTrackGroup(selection.getSelectedIndex()); int indexInTrackGroup = selection.getIndexInTrackGroup(selection.getSelectedIndex());
assertThat(indexInTrackGroup).isAtLeast(0); assertThat(indexInTrackGroup).isAtLeast(0);
assertThat(indexInTrackGroup).isLessThan(trackGroup.length); assertThat(indexInTrackGroup).isLessThan(trackGroup.length);
streams[i] = List<FakeSampleStreamItem> sampleStreamItems =
trackDataFactory.create(
selection.getSelectedFormat(),
checkNotNull(mediaSourceEventDispatcher.mediaPeriodId));
FakeSampleStream sampleStream =
createSampleStream( createSampleStream(
positionUs, allocator,
selection,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
drmEventDispatcher); drmEventDispatcher,
sampleStreams.add(streams[i]); selection.getSelectedFormat(),
sampleStreamItems);
sampleStreams.add(sampleStream);
streams[i] = sampleStream;
streamResetFlags[i] = true; streamResetFlags[i] = true;
} }
} }
return positionUs; return seekToUs(positionUs);
} }
@Override @Override
public void discardBuffer(long positionUs, boolean toKeyframe) { public void discardBuffer(long positionUs, boolean toKeyframe) {
// Do nothing. for (FakeSampleStream sampleStream : sampleStreams) {
sampleStream.discardTo(positionUs, toKeyframe);
}
} }
@Override @Override
...@@ -278,22 +320,30 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -278,22 +320,30 @@ public class FakeMediaPeriod implements MediaPeriod {
@Override @Override
public long getBufferedPositionUs() { public long getBufferedPositionUs() {
assertThat(prepared).isTrue(); assertThat(prepared).isTrue();
return bufferedPositionUs; if (isLoadingFinished()) {
return C.TIME_END_OF_SOURCE;
} }
long minBufferedPositionUs = Long.MAX_VALUE;
public void setBufferedPositionUs(long bufferedPositionUs) { for (FakeSampleStream sampleStream : sampleStreams) {
this.bufferedPositionUs = bufferedPositionUs; minBufferedPositionUs =
min(minBufferedPositionUs, sampleStream.getLargestQueuedTimestampUs());
}
return minBufferedPositionUs == Long.MIN_VALUE ? lastSeekPositionUs : minBufferedPositionUs;
} }
@Override @Override
public long seekToUs(long positionUs) { public long seekToUs(long positionUs) {
assertThat(prepared).isTrue(); assertThat(prepared).isTrue();
long seekPositionUs = positionUs + seekOffsetUs; long seekPositionUs = positionUs + seekOffsetUs;
for (SampleStream sampleStream : sampleStreams) { lastSeekPositionUs = seekPositionUs;
seekSampleStream(sampleStream, seekPositionUs); boolean seekedInsideStreams = true;
for (FakeSampleStream sampleStream : sampleStreams) {
seekedInsideStreams &= sampleStream.seekToUs(seekPositionUs);
}
if (!seekedInsideStreams) {
for (FakeSampleStream sampleStream : sampleStreams) {
sampleStream.reset();
} }
if (bufferedPositionUs != C.TIME_END_OF_SOURCE && seekPositionUs > bufferedPositionUs) {
bufferedPositionUs = seekPositionUs;
} }
return seekPositionUs; return seekPositionUs;
} }
...@@ -306,12 +356,15 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -306,12 +356,15 @@ public class FakeMediaPeriod implements MediaPeriod {
@Override @Override
public long getNextLoadPositionUs() { public long getNextLoadPositionUs() {
assertThat(prepared).isTrue(); assertThat(prepared).isTrue();
return C.TIME_END_OF_SOURCE; return getBufferedPositionUs();
} }
@Override @Override
public boolean continueLoading(long positionUs) { public boolean continueLoading(long positionUs) {
return false; for (FakeSampleStream sampleStream : sampleStreams) {
sampleStream.writeData(positionUs);
}
return true;
} }
@Override @Override
...@@ -320,58 +373,31 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -320,58 +373,31 @@ public class FakeMediaPeriod implements MediaPeriod {
} }
/** /**
* Creates a sample stream for the provided selection. * Creates a new {@link FakeSampleStream}.
* *
* @param positionUs The position at which the tracks were selected, in microseconds. * @param mediaSourceEventDispatcher A {@link MediaSourceEventListener.EventDispatcher} to notify
* @param selection A selection of tracks. * of media events.
* @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events that * @param drmSessionManager A {@link DrmSessionManager} for DRM interactions.
* should be used by the sample stream. * @param drmEventDispatcher A {@link DrmSessionEventListener.EventDispatcher} to notify of DRM
* @param drmSessionManager The DRM session manager. * events.
* @param drmEventDispatcher A dispatcher for {@link DrmSessionEventListener} events that should * @param initialFormat The first {@link Format} to output.
* be used by the sample stream. * @param fakeSampleStreamItems The {@link FakeSampleStreamItem items} to output.
* @return A {@link SampleStream} for this selection. * @return A new {@link FakeSampleStream}.
*/ */
protected SampleStream createSampleStream( protected FakeSampleStream createSampleStream(
long positionUs, Allocator allocator,
TrackSelection selection, @Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
DrmSessionEventListener.EventDispatcher drmEventDispatcher) { DrmSessionEventListener.EventDispatcher drmEventDispatcher,
FakeSampleStream sampleStream = Format initialFormat,
new FakeSampleStream( List<FakeSampleStream.FakeSampleStreamItem> fakeSampleStreamItems) {
return new FakeSampleStream(
allocator,
mediaSourceEventDispatcher, mediaSourceEventDispatcher,
drmSessionManager, drmSessionManager,
drmEventDispatcher, drmEventDispatcher,
selection.getSelectedFormat(), initialFormat,
trackDataFactory.create( fakeSampleStreamItems);
selection.getSelectedFormat(),
Assertions.checkNotNull(mediaSourceEventDispatcher.mediaPeriodId)));
sampleStream.seekTo(positionUs);
return sampleStream;
}
/**
* Seeks inside the given sample stream.
*
* @param sampleStream A sample stream that was created by a call to {@link
* #createSampleStream(long, TrackSelection, MediaSourceEventListener.EventDispatcher,
* DrmSessionManager, DrmSessionEventListener.EventDispatcher)}.
* @param positionUs The position to seek to, in microseconds.
*/
protected void seekSampleStream(SampleStream sampleStream, long positionUs) {
// Queue a single sample from the seek position again.
((FakeSampleStream) sampleStream).seekTo(positionUs);
}
/**
* Releases the given sample stream.
*
* @param sampleStream A sample stream that was created by a call to {@link
* #createSampleStream(long, TrackSelection, MediaSourceEventListener.EventDispatcher,
* DrmSessionManager, DrmSessionEventListener.EventDispatcher)}.
*/
protected void releaseSampleStream(SampleStream sampleStream) {
((FakeSampleStream) sampleStream).release();
} }
private void finishPreparation() { private void finishPreparation() {
...@@ -395,27 +421,12 @@ public class FakeMediaPeriod implements MediaPeriod { ...@@ -395,27 +421,12 @@ public class FakeMediaPeriod implements MediaPeriod {
/* mediaEndTimeUs = */ C.TIME_UNSET); /* mediaEndTimeUs = */ C.TIME_UNSET);
} }
/** A factory to create the test data for a particular track. */ private boolean isLoadingFinished() {
public interface TrackDataFactory { for (FakeSampleStream sampleStream : sampleStreams) {
if (!sampleStream.isLoadingFinished()) {
/** return false;
* Returns the list of {@link FakeSampleStreamItem}s that will be passed to {@link }
* FakeSampleStream#FakeSampleStream(MediaSourceEventListener.EventDispatcher,
* DrmSessionManager, DrmSessionEventListener.EventDispatcher, Format, List)}.
*
* @param format The format of the track to provide data for.
* @param mediaPeriodId The {@link MediaPeriodId} to provide data for.
* @return The track data in the form of {@link FakeSampleStreamItem}s.
*/
List<FakeSampleStreamItem> create(Format format, MediaPeriodId mediaPeriodId);
/**
* Returns a factory that always provides a single sample with {@code time=sampleTimeUs} and
* then end-of-stream.
*/
static TrackDataFactory singleSampleWithTimeUs(long sampleTimeUs) {
return (unusedFormat, unusedMediaPeriodId) ->
ImmutableList.of(oneByteSample(sampleTimeUs), END_OF_STREAM_ITEM);
} }
return true;
} }
} }
...@@ -343,6 +343,7 @@ public class FakeMediaSource extends BaseMediaSource { ...@@ -343,6 +343,7 @@ public class FakeMediaSource extends BaseMediaSource {
long defaultFirstSampleTimeUs = positionInWindowUs >= 0 || id.isAd() ? 0 : -positionInWindowUs; long defaultFirstSampleTimeUs = positionInWindowUs >= 0 || id.isAd() ? 0 : -positionInWindowUs;
return new FakeMediaPeriod( return new FakeMediaPeriod(
trackGroupArray, trackGroupArray,
allocator,
trackDataFactory != null trackDataFactory != null
? trackDataFactory ? trackDataFactory
: TrackDataFactory.singleSampleWithTimeUs(defaultFirstSampleTimeUs), : TrackDataFactory.singleSampleWithTimeUs(defaultFirstSampleTimeUs),
......
...@@ -15,55 +15,38 @@ ...@@ -15,55 +15,38 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.BufferFlags;
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.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
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.MimeTypes;
import com.google.common.collect.Iterables; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Fake {@link SampleStream} that outputs a given {@link Format}, any amount of {@link * Fake {@link SampleStream} that outputs a given {@link Format} and any amount of {@link
* FakeSampleStreamItem items}, then end of stream. * FakeSampleStreamItem items}.
*/ */
public class FakeSampleStream implements SampleStream { public class FakeSampleStream implements SampleStream {
private static class SampleInfo {
private final byte[] data;
@C.BufferFlags private final int flags;
private final long timeUs;
private SampleInfo(byte[] data, @C.BufferFlags int flags, long timeUs) {
this.data = Arrays.copyOf(data, data.length);
this.flags = flags;
this.timeUs = timeUs;
}
}
/** Item to customize a return value of {@link FakeSampleStream#readData}. */ /** Item to customize a return value of {@link FakeSampleStream#readData}. */
public static final class FakeSampleStreamItem { public static final class FakeSampleStreamItem {
/** /** Item that designates the end of stream has been reached. */
* Item that designates the end of stream has been reached.
*
* <p>When this item is read, readData will repeatedly return end of stream.
*/
public static final FakeSampleStreamItem END_OF_STREAM_ITEM = public static final FakeSampleStreamItem END_OF_STREAM_ITEM =
sample( sample(
/* timeUs= */ Long.MAX_VALUE, /* timeUs= */ Long.MAX_VALUE,
...@@ -92,10 +75,9 @@ public class FakeSampleStream implements SampleStream { ...@@ -92,10 +75,9 @@ public class FakeSampleStream implements SampleStream {
* <p>The sample will contain a single byte of data. * <p>The sample will contain a single byte of data.
* *
* @param timeUs The timestamp of the sample. * @param timeUs The timestamp of the sample.
* @param flags The buffer flags that will be set when reading this sample through {@link * @param flags The sample {@link C.BufferFlags}.
* FakeSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)}.
*/ */
public static FakeSampleStreamItem oneByteSample(long timeUs, @BufferFlags int flags) { public static FakeSampleStreamItem oneByteSample(long timeUs, @C.BufferFlags int flags) {
return sample(timeUs, flags, new byte[] {0}); return sample(timeUs, flags, new byte[] {0});
} }
...@@ -103,12 +85,11 @@ public class FakeSampleStream implements SampleStream { ...@@ -103,12 +85,11 @@ public class FakeSampleStream implements SampleStream {
* Creates an item representing a sample with the provided timestamp, flags and data. * Creates an item representing a sample with the provided timestamp, flags and data.
* *
* @param timeUs The timestamp of the sample. * @param timeUs The timestamp of the sample.
* @param flags The buffer flags that will be set when reading this sample through {@link * @param flags The sample {@link C.BufferFlags}.
* FakeSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)}.
* @param sampleData The sample data. * @param sampleData The sample data.
*/ */
public static FakeSampleStreamItem sample( public static FakeSampleStreamItem sample(
long timeUs, @BufferFlags int flags, byte[] sampleData) { long timeUs, @C.BufferFlags int flags, byte[] sampleData) {
return new FakeSampleStreamItem( return new FakeSampleStreamItem(
/* format= */ null, new SampleInfo(sampleData.clone(), flags, timeUs)); /* format= */ null, new SampleInfo(sampleData.clone(), flags, timeUs));
} }
...@@ -126,218 +107,205 @@ public class FakeSampleStream implements SampleStream { ...@@ -126,218 +107,205 @@ public class FakeSampleStream implements SampleStream {
} }
} }
private final SampleQueue sampleQueue;
@Nullable private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; @Nullable private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher;
private final Format initialFormat; private final List<FakeSampleStreamItem> sampleStreamItems;
private final List<FakeSampleStreamItem> fakeSampleStreamItems;
private final DrmSessionManager drmSessionManager;
private final DrmSessionEventListener.EventDispatcher drmEventDispatcher;
private int sampleItemIndex; private int sampleStreamItemsWritePosition;
private @MonotonicNonNull Format downstreamFormat; private boolean loadingFinished;
private boolean readEOSBuffer; @Nullable private Format downstreamFormat;
@Nullable private DrmSession currentDrmSession; @Nullable private Format notifiedDownstreamFormat;
/** /**
* Creates a fake sample stream which outputs the given {@link Format} followed by the provided * Creates a fake sample stream which outputs the given {@link Format} followed by the provided
* {@link FakeSampleStreamItem items}. * {@link FakeSampleStreamItem items}.
* *
* @param allocator An {@link Allocator}.
* @param mediaSourceEventDispatcher A {@link MediaSourceEventListener.EventDispatcher} to notify * @param mediaSourceEventDispatcher A {@link MediaSourceEventListener.EventDispatcher} to notify
* of media events. * of media events.
* @param drmSessionManager A {@link DrmSessionManager} for DRM interactions. * @param drmSessionManager A {@link DrmSessionManager} for DRM interactions.
* @param drmEventDispatcher A {@link DrmSessionEventListener.EventDispatcher} to notify of DRM * @param drmEventDispatcher A {@link DrmSessionEventListener.EventDispatcher} to notify of DRM
* events. * events.
* @param initialFormat The first {@link Format} to output. * @param initialFormat The first {@link Format} to output.
* @param fakeSampleStreamItems The {@link FakeSampleStreamItem items} to customize the return * @param fakeSampleStreamItems The {@link FakeSampleStreamItem items} to output.
* values of {@link #readData(FormatHolder, DecoderInputBuffer, boolean)}. This is assumed to
* be in ascending order of sampleTime. Note that once an EOS buffer has been read, that will
* return every time readData is called. This should usually end with {@link
* FakeSampleStreamItem#END_OF_STREAM_ITEM}.
*/ */
public FakeSampleStream( public FakeSampleStream(
Allocator allocator,
@Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, @Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
DrmSessionEventListener.EventDispatcher drmEventDispatcher, DrmSessionEventListener.EventDispatcher drmEventDispatcher,
Format initialFormat, Format initialFormat,
List<FakeSampleStreamItem> fakeSampleStreamItems) { List<FakeSampleStreamItem> fakeSampleStreamItems) {
this.sampleQueue =
SampleQueue.createWithDrm(
allocator,
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
drmSessionManager,
drmEventDispatcher);
this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.mediaSourceEventDispatcher = mediaSourceEventDispatcher;
this.drmSessionManager = drmSessionManager; this.sampleStreamItems = new ArrayList<>();
this.drmEventDispatcher = drmEventDispatcher; sampleStreamItems.add(FakeSampleStreamItem.format(initialFormat));
this.initialFormat = initialFormat; sampleStreamItems.addAll(fakeSampleStreamItems);
this.fakeSampleStreamItems = new ArrayList<>(fakeSampleStreamItems); }
/**
* Appends {@link FakeSampleStreamItem FakeSampleStreamItems} to the list of items that should be
* written to the queue.
*
* <p>Note that this data is only written to the queue once {@link #writeData(long)} is called.
*
* @param items The items to append.
*/
public void append(List<FakeSampleStreamItem> items) {
sampleStreamItems.addAll(items);
} }
/** /**
* Seeks inside this sample stream. * Writes all not yet written {@link FakeSampleStreamItem sample stream items} to the sample queue
* starting at the given position.
* *
* <p>Seeks to just before the first sample with {@code sampleTime >= timeUs}, or to the end of * @param startPositionUs The start position, in microseconds.
* the stream otherwise.
*/ */
public void seekTo(long timeUs) { public void writeData(long startPositionUs) {
Format applicableFormat = initialFormat; if (sampleStreamItemsWritePosition == 0) {
for (int i = 0; i < fakeSampleStreamItems.size(); i++) { sampleQueue.setStartTimeUs(startPositionUs);
@Nullable SampleInfo sampleInfo = fakeSampleStreamItems.get(i).sampleInfo; }
boolean writtenFirstFormat = false;
@Nullable Format pendingFirstFormat = null;
for (int i = 0; i < sampleStreamItems.size(); i++) {
FakeSampleStreamItem fakeSampleStreamItem = sampleStreamItems.get(i);
@Nullable FakeSampleStream.SampleInfo sampleInfo = fakeSampleStreamItem.sampleInfo;
if (sampleInfo == null) { if (sampleInfo == null) {
applicableFormat = Assertions.checkNotNull(fakeSampleStreamItems.get(i).format); if (writtenFirstFormat) {
sampleQueue.format(checkNotNull(fakeSampleStreamItem.format));
} else {
pendingFirstFormat = checkNotNull(fakeSampleStreamItem.format);
}
continue; continue;
} }
if (sampleInfo.timeUs >= timeUs) { if ((sampleInfo.flags & C.BUFFER_FLAG_END_OF_STREAM) != 0) {
sampleItemIndex = i; loadingFinished = true;
readEOSBuffer = false; break;
if (downstreamFormat != null && !applicableFormat.equals(downstreamFormat)) {
notifyEventDispatcher(applicableFormat);
} }
return; if (sampleInfo.timeUs >= startPositionUs && i >= sampleStreamItemsWritePosition) {
if (!writtenFirstFormat) {
sampleQueue.format(checkNotNull(pendingFirstFormat));
writtenFirstFormat = true;
} }
sampleQueue.sampleData(new ParsableByteArray(sampleInfo.data), sampleInfo.data.length);
sampleQueue.sampleMetadata(
sampleInfo.timeUs,
sampleInfo.flags,
sampleInfo.data.length,
/* offset= */ 0,
/* cryptoData= */ null);
} }
sampleItemIndex = fakeSampleStreamItems.size(); }
@Nullable sampleStreamItemsWritePosition = sampleStreamItems.size();
FakeSampleStreamItem lastItem =
Iterables.getLast(fakeSampleStreamItems, /* defaultValue= */ null);
readEOSBuffer =
lastItem != null
&& lastItem.sampleInfo != null
&& ((lastItem.sampleInfo.flags & C.BUFFER_FLAG_END_OF_STREAM) != 0);
} }
/** /**
* Adds an item to the end of the queue of {@link FakeSampleStreamItem items}. * Seeks the stream to a new position using already available data in the queue.
* *
* @param item The item to add. * @param positionUs The new position, in microseconds.
* @return Whether seeking inside the available data was possible.
*/ */
public void addFakeSampleStreamItem(FakeSampleStreamItem item) { public boolean seekToUs(long positionUs) {
this.fakeSampleStreamItems.add(item); return sampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */ false);
} }
@Override /**
public boolean isReady() { * Resets the sample queue.
if (sampleItemIndex == fakeSampleStreamItems.size()) { *
return readEOSBuffer || downstreamFormat == null; * <p>A new call to {@link #writeData(long)} is required to fill the queue again.
} */
if (fakeSampleStreamItems.get(sampleItemIndex).format != null) { public void reset() {
// A format can be read. sampleQueue.reset();
return true; sampleStreamItemsWritePosition = 0;
} loadingFinished = false;
return mayReadSample();
} }
@Override /** Returns whether data has been written to the sample queue until the end of stream signal. */
public int readData( public boolean isLoadingFinished() {
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { return loadingFinished;
if (downstreamFormat == null || formatRequired) {
onFormatResult(downstreamFormat == null ? initialFormat : downstreamFormat, formatHolder);
return C.RESULT_FORMAT_READ;
}
// Once an EOS buffer has been read, send EOS every time.
if (readEOSBuffer) {
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
} }
if (sampleItemIndex < fakeSampleStreamItems.size()) { /**
FakeSampleStreamItem fakeSampleStreamItem = fakeSampleStreamItems.get(sampleItemIndex); * Returns the timestamp of the largest queued sample in the queue, or {@link Long#MIN_VALUE} if
sampleItemIndex++; * no samples are queued.
if (fakeSampleStreamItem.format != null) { */
onFormatResult(fakeSampleStreamItem.format, formatHolder); public long getLargestQueuedTimestampUs() {
return C.RESULT_FORMAT_READ; return sampleQueue.getLargestQueuedTimestampUs();
} else {
SampleInfo sampleInfo = Assertions.checkNotNull(fakeSampleStreamItem.sampleInfo);
if (sampleInfo.flags != 0) {
buffer.setFlags(sampleInfo.flags);
if (buffer.isEndOfStream()) {
readEOSBuffer = true;
return C.RESULT_BUFFER_READ;
}
}
if (!mayReadSample()) {
sampleItemIndex--;
return C.RESULT_NOTHING_READ;
}
buffer.timeUs = sampleInfo.timeUs;
buffer.ensureSpaceForWrite(sampleInfo.data.length);
buffer.data.put(sampleInfo.data);
return C.RESULT_BUFFER_READ;
}
}
return C.RESULT_NOTHING_READ;
} }
private void onFormatResult(Format newFormat, FormatHolder outputFormatHolder) { /**
outputFormatHolder.format = newFormat; * Discards data from the queue.
@Nullable *
DrmInitData oldDrmInitData = downstreamFormat == null ? null : downstreamFormat.drmInitData; * @param positionUs The position to discard to, in microseconds.
boolean isFirstFormat = downstreamFormat == null; * @param toKeyframe Whether to discard to keyframes only.
downstreamFormat = newFormat; */
@Nullable DrmInitData newDrmInitData = newFormat.drmInitData; public void discardTo(long positionUs, boolean toKeyframe) {
outputFormatHolder.drmSession = currentDrmSession; sampleQueue.discardTo(positionUs, toKeyframe, /* stopAtReadPosition= */ true);
notifyEventDispatcher(newFormat);
if (!isFirstFormat && Util.areEqual(oldDrmInitData, newDrmInitData)) {
// Nothing to do.
return;
}
// Ensure we acquire the new session before releasing the previous one in case the same session
// is being used for both DrmInitData.
@Nullable DrmSession previousSession = currentDrmSession;
Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper());
currentDrmSession =
drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newFormat);
outputFormatHolder.drmSession = currentDrmSession;
if (previousSession != null) {
previousSession.release(drmEventDispatcher);
} }
/** Release the stream and its underlying sample queue. */
public void release() {
sampleQueue.release();
} }
private boolean mayReadSample() { @Override
@Nullable DrmSession drmSession = this.currentDrmSession; public boolean isReady() {
@Nullable return sampleQueue.isReady(loadingFinished);
FakeSampleStreamItem nextSample =
Iterables.get(fakeSampleStreamItems, sampleItemIndex, /* defaultValue= */ null);
boolean nextSampleIsClear =
nextSample != null
&& nextSample.sampleInfo != null
&& (nextSample.sampleInfo.flags & C.BUFFER_FLAG_ENCRYPTED) == 0;
return drmSession == null
|| drmSession.getState() == DrmSession.STATE_OPENED_WITH_KEYS
|| (nextSampleIsClear && drmSession.playClearSamplesWithoutKeys());
} }
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
if (currentDrmSession != null && currentDrmSession.getState() == DrmSession.STATE_ERROR) { sampleQueue.maybeThrowError();
throw Assertions.checkNotNull(currentDrmSession.getError());
}
} }
@Override @Override
public int skipData(long positionUs) { public int readData(
// TODO: Implement this. FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
return 0; int result = sampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished);
if (result == C.RESULT_FORMAT_READ) {
downstreamFormat = checkNotNull(formatHolder.format);
} }
if (result == C.RESULT_BUFFER_READ && !buffer.isFlagsOnly()) {
/** Release this SampleStream and all underlying resources. */ maybeNotifyDownstreamFormat(buffer.timeUs);
public void release() {
if (currentDrmSession != null) {
currentDrmSession.release(drmEventDispatcher);
currentDrmSession = null;
} }
return result;
} }
private void notifyEventDispatcher(Format format) { @Override
if (mediaSourceEventDispatcher != null) { public int skipData(long positionUs) {
@Nullable SampleInfo sampleInfo = null; int skipCount = sampleQueue.getSkipCount(positionUs, loadingFinished);
for (int i = sampleItemIndex; i < fakeSampleStreamItems.size(); i++) { sampleQueue.skip(skipCount);
sampleInfo = fakeSampleStreamItems.get(i).sampleInfo; return skipCount;
if (sampleInfo != null) {
break;
}
} }
long nextSampleTimeUs = sampleInfo != null ? sampleInfo.timeUs : C.TIME_END_OF_SOURCE;
private void maybeNotifyDownstreamFormat(long timeUs) {
if (mediaSourceEventDispatcher != null
&& downstreamFormat != null
&& !downstreamFormat.equals(notifiedDownstreamFormat)) {
mediaSourceEventDispatcher.downstreamFormatChanged( mediaSourceEventDispatcher.downstreamFormatChanged(
C.TRACK_TYPE_UNKNOWN, MimeTypes.getTrackType(downstreamFormat.sampleMimeType),
format, downstreamFormat,
C.SELECTION_REASON_UNKNOWN, C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null, /* trackSelectionData= */ null,
/* mediaTimeUs= */ nextSampleTimeUs); timeUs);
notifiedDownstreamFormat = downstreamFormat;
}
}
private static class SampleInfo {
public final byte[] data;
@C.BufferFlags public final int flags;
public final long timeUs;
public SampleInfo(byte[] data, @C.BufferFlags int flags, long timeUs) {
this.data = Arrays.copyOf(data, data.length);
this.flags = flags;
this.timeUs = timeUs;
} }
} }
} }
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