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