diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 30af89dd08..9bcbce834a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2; +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.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertThrows; @@ -617,7 +619,13 @@ public final class ExoPlayerTest { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { - FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher); + FakeMediaPeriod mediaPeriod = + new FakeMediaPeriod( + trackGroupArray, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + eventDispatcher, + drmSessionManager, + /* deferOnPrepared= */ false); mediaPeriod.setSeekToUsOffset(10); return mediaPeriod; } @@ -653,7 +661,11 @@ public final class ExoPlayerTest { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { - FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher); + FakeMediaPeriod mediaPeriod = + new FakeMediaPeriod( + trackGroupArray, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + eventDispatcher); mediaPeriod.setDiscontinuityPositionUs(10); return mediaPeriod; } @@ -680,7 +692,11 @@ public final class ExoPlayerTest { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { - FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher); + FakeMediaPeriod mediaPeriod = + new FakeMediaPeriod( + trackGroupArray, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + eventDispatcher); // Set a discontinuity at the position this period is supposed to start at anyway. mediaPeriod.setDiscontinuityPositionUs( timeline.getWindow(/* windowIndex= */ 0, new Window()).positionInFirstPeriodUs); @@ -929,8 +945,9 @@ public final class ExoPlayerTest { fakeMediaPeriodHolder[0] = new FakeMediaPeriod( trackGroupArray, - drmSessionManager, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, eventDispatcher, + drmSessionManager, /* deferOnPrepared= */ true); createPeriodCalledCountDownLatch.countDown(); return fakeMediaPeriodHolder[0]; @@ -980,8 +997,9 @@ public final class ExoPlayerTest { fakeMediaPeriodHolder[0] = new FakeMediaPeriod( trackGroupArray, - drmSessionManager, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, eventDispatcher, + drmSessionManager, /* deferOnPrepared= */ true); createPeriodCalledCountDownLatch.countDown(); return fakeMediaPeriodHolder[0]; @@ -3708,7 +3726,10 @@ public final class ExoPlayerTest { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { - return new FakeMediaPeriod(trackGroupArray, eventDispatcher) { + return new FakeMediaPeriod( + trackGroupArray, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + eventDispatcher) { @Override public long getBufferedPositionUs() { // Pretend not to have buffered data yet. @@ -6410,7 +6431,10 @@ public final class ExoPlayerTest { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { - return new FakeMediaPeriod(trackGroupArray, eventDispatcher) { + return new FakeMediaPeriod( + trackGroupArray, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + eventDispatcher) { private final List allocations = new ArrayList<>(); @@ -6486,7 +6510,12 @@ public final class ExoPlayerTest { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { - return new FakeMediaPeriod(trackGroupArray, drmSessionManager, eventDispatcher) { + return new FakeMediaPeriod( + trackGroupArray, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + eventDispatcher, + drmSessionManager, + /* deferOnPrepared= */ false) { private Loader loader = new Loader("oomLoader"); @Override @@ -6505,14 +6534,13 @@ public final class ExoPlayerTest { // Create 3 samples without end of stream signal to test that all 3 samples are // still played before the exception is thrown. return new FakeSampleStream( - selection.getSelectedFormat(), drmSessionManager, eventDispatcher, - positionUs, - /* timeUsIncrement= */ 0, - new FakeSampleStream.FakeSampleStreamItem(new byte[] {0}), - new FakeSampleStream.FakeSampleStreamItem(new byte[] {0}), - new FakeSampleStream.FakeSampleStreamItem(new byte[] {0})) { + selection.getSelectedFormat(), + ImmutableList.of( + oneByteSample(positionUs), + oneByteSample(positionUs), + oneByteSample(positionUs))) { @Override public void maybeThrowError() throws IOException { @@ -6676,10 +6704,21 @@ public final class ExoPlayerTest { /* windowOffsetInFirstPeriodUs= */ 1_234_567, AdPlaybackState.NONE)); ExoPlayer player = new TestExoPlayer.Builder(context).setRenderers(renderer).build(); + long firstSampleTimeUs = 4_567_890 + 1_234_567; FakeMediaSource firstMediaSource = - new FakeMediaSource(/* timeline= */ null, ExoPlayerTestRunner.VIDEO_FORMAT); + new FakeMediaSource( + /* timeline= */ null, + DrmSessionManager.DUMMY, + (unusedFormat, unusedMediaPeriodId) -> + ImmutableList.of(oneByteSample(firstSampleTimeUs), END_OF_STREAM_ITEM), + ExoPlayerTestRunner.VIDEO_FORMAT); FakeMediaSource secondMediaSource = - new FakeMediaSource(timelineWithOffsets, ExoPlayerTestRunner.VIDEO_FORMAT); + new FakeMediaSource( + timelineWithOffsets, + DrmSessionManager.DUMMY, + (unusedFormat, unusedMediaPeriodId) -> + ImmutableList.of(oneByteSample(firstSampleTimeUs), END_OF_STREAM_ITEM), + ExoPlayerTestRunner.VIDEO_FORMAT); player.setMediaSources(ImmutableList.of(firstMediaSource, secondMediaSource)); // Start playback and wait until player is idly waiting for an update of the first source. @@ -6697,7 +6736,6 @@ public final class ExoPlayerTest { assertThat(rendererStreamOffsetsUs).hasSize(2); assertThat(firstBufferTimesUsWithOffset).hasSize(2); // Assert that the offsets and buffer times match the expected sample time. - long firstSampleTimeUs = 4_567_890 + 1_234_567; assertThat(firstBufferTimesUsWithOffset.get(0)) .isEqualTo(rendererStreamOffsetsUs.get(0) + firstSampleTimeUs); assertThat(firstBufferTimesUsWithOffset.get(1)) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java index d25cac6806..9916991705 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.analytics; +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.common.truth.Truth.assertThat; import android.view.Surface; @@ -57,6 +59,7 @@ import com.google.android.exoplayer2.testutil.FakeVideoRenderer; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.util.Util; +import com.google.common.collect.ImmutableList; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; @@ -822,10 +825,10 @@ public final class AnalyticsCollectorTest { .containsExactly(window0Period1Seq0, period1Seq0) .inOrder(); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(window0Period1Seq0, window1Period0Seq1, period1Seq0) + .containsExactly(window0Period1Seq0, window1Period0Seq1) .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) - .containsExactly(window0Period1Seq0, window1Period0Seq1, period1Seq0) + .containsExactly(window0Period1Seq0, window1Period0Seq1) .inOrder(); assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET)) .containsExactly(window0Period1Seq0, period1Seq0) @@ -926,14 +929,15 @@ public final class AnalyticsCollectorTest { @Test public void adPlayback() throws Exception { - long contentDurationsUs = 10 * C.MICROS_PER_SECOND; + long contentDurationsUs = 11 * C.MICROS_PER_SECOND; + long windowOffsetInFirstPeriodUs = + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; AtomicReference adPlaybackState = new AtomicReference<>( FakeTimeline.createAdPlaybackState( /* adsPerAdGroup= */ 1, /* adGroupTimesUs...= */ - 0, - TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US - + 5 * C.MICROS_PER_SECOND, + windowOffsetInFirstPeriodUs, + windowOffsetInFirstPeriodUs + 5 * C.MICROS_PER_SECOND, C.TIME_END_OF_SOURCE)); AtomicInteger playedAdCount = new AtomicInteger(0); Timeline adTimeline = @@ -946,7 +950,23 @@ public final class AnalyticsCollectorTest { contentDurationsUs, adPlaybackState.get())); FakeMediaSource fakeMediaSource = - new FakeMediaSource(adTimeline, ExoPlayerTestRunner.VIDEO_FORMAT); + new FakeMediaSource( + adTimeline, + DrmSessionManager.DUMMY, + (unusedFormat, mediaPeriodId) -> { + if (mediaPeriodId.isAd()) { + return ImmutableList.of(oneByteSample(/* timeUs= */ 0), 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), + END_OF_STREAM_ITEM); + } + }, + ExoPlayerTestRunner.VIDEO_FORMAT); ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG) .executeRunnable( @@ -965,7 +985,7 @@ public final class AnalyticsCollectorTest { adPlaybackState .get() .withPlayedAd( - playedAdCount.getAndIncrement(), + /* adGroupIndex= */ playedAdCount.getAndIncrement(), /* adIndexInAdGroup= */ 0)); fakeMediaSource.setNewSourceInfo( new FakeTimeline( @@ -974,7 +994,7 @@ public final class AnalyticsCollectorTest { /* id= */ 0, /* isSeekable= */ true, /* isDynamic= */ false, - /* durationUs =*/ 10 * C.MICROS_PER_SECOND, + contentDurationsUs, adPlaybackState.get())), /* sendManifestLoadEvents= */ false); } @@ -1185,6 +1205,8 @@ public final class AnalyticsCollectorTest { @Test public void seekAfterMidroll() throws Exception { + long windowOffsetInFirstPeriodUs = + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; Timeline adTimeline = new FakeTimeline( new TimelineWindowDefinition( @@ -1195,10 +1217,23 @@ public final class AnalyticsCollectorTest { 10 * C.MICROS_PER_SECOND, FakeTimeline.createAdPlaybackState( /* adsPerAdGroup= */ 1, /* adGroupTimesUs...= */ - TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US - + 5 * C.MICROS_PER_SECOND))); + windowOffsetInFirstPeriodUs + 5 * C.MICROS_PER_SECOND))); FakeMediaSource fakeMediaSource = - new FakeMediaSource(adTimeline, ExoPlayerTestRunner.VIDEO_FORMAT); + new FakeMediaSource( + adTimeline, + DrmSessionManager.DUMMY, + (unusedFormat, mediaPeriodId) -> { + if (mediaPeriodId.isAd()) { + return ImmutableList.of(oneByteSample(/* timeUs= */ 0), 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), + END_OF_STREAM_ITEM); + } + }, + ExoPlayerTestRunner.VIDEO_FORMAT); ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG) .pause() diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java index bfc657aaf4..b141f1ac99 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java @@ -19,6 +19,7 @@ import static com.google.android.exoplayer2.RendererCapabilities.ADAPTIVE_NOT_SE import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED; import static com.google.android.exoplayer2.RendererCapabilities.TUNNELING_NOT_SUPPORTED; import static com.google.android.exoplayer2.RendererCapabilities.TUNNELING_SUPPORTED; +import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -33,9 +34,11 @@ import com.google.android.exoplayer2.decoder.DecoderException; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer; +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.util.MimeTypes; +import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -103,7 +106,11 @@ public class DecoderAudioRendererTest { audioRenderer.enable( RendererConfiguration.DEFAULT, new Format[] {FORMAT}, - new FakeSampleStream(FORMAT, /* eventDispatcher= */ null, /* shouldOutputSample= */ false), + new FakeSampleStream( + DrmSessionManager.DUMMY, + /* eventDispatcher= */ null, + FORMAT, + ImmutableList.of(END_OF_STREAM_ITEM)), /* positionUs= */ 0, /* joining= */ false, /* mayRenderStartOfStream= */ true, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java index 9ba8c9f2d2..21c5086c5a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.audio; +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 org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -33,8 +36,8 @@ 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.util.MimeTypes; +import com.google.common.collect.ImmutableList; import java.util.Collections; import java.util.List; import org.junit.Before; @@ -108,19 +111,18 @@ public class MediaCodecAudioRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ AUDIO_AAC, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(changedFormat), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ AUDIO_AAC, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), + oneByteSample(/* timeUs= */ 50), + oneByteSample(/* timeUs= */ 100), + format(changedFormat), + oneByteSample(/* timeUs= */ 150), + oneByteSample(/* timeUs= */ 200), + oneByteSample(/* timeUs= */ 250), + END_OF_STREAM_ITEM)); mediaCodecAudioRenderer.enable( RendererConfiguration.DEFAULT, @@ -156,19 +158,18 @@ public class MediaCodecAudioRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ AUDIO_AAC, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(changedFormat), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ AUDIO_AAC, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), + oneByteSample(/* timeUs= */ 50), + oneByteSample(/* timeUs= */ 100), + format(changedFormat), + oneByteSample(/* timeUs= */ 150), + oneByteSample(/* timeUs= */ 200), + oneByteSample(/* timeUs= */ 250), + END_OF_STREAM_ITEM)); mediaCodecAudioRenderer.enable( RendererConfiguration.DEFAULT, @@ -224,13 +225,10 @@ public class MediaCodecAudioRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ AUDIO_AAC, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ AUDIO_AAC, + ImmutableList.of(oneByteSample(/* timeUs= */ 0), END_OF_STREAM_ITEM)); exceptionThrowingRenderer.enable( RendererConfiguration.DEFAULT, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java index 1d1cb20b34..d664964888 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java @@ -32,6 +32,7 @@ import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamI import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; +import com.google.common.collect.ImmutableList; import com.google.common.primitives.Bytes; import java.util.ArrayList; import java.util.Collections; @@ -145,13 +146,12 @@ public class MetadataRendererTest { renderer.replaceStream( new Format[] {EMSG_FORMAT}, new FakeSampleStream( - EMSG_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 0, - new FakeSampleStreamItem(input), - FakeSampleStreamItem.END_OF_STREAM_ITEM), + EMSG_FORMAT, + ImmutableList.of( + FakeSampleStreamItem.sample(/* timeUs= */ 0, /* flags= */ 0, input), + FakeSampleStreamItem.END_OF_STREAM_ITEM)), /* offsetUs= */ 0L); renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the format renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the data diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java index b8edff6f60..294236edbb 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadHelperTest.java @@ -503,14 +503,14 @@ public class DownloadHelperTest { int periodIndex = TEST_TIMELINE.getIndexOfPeriod(id.periodUid); return new FakeMediaPeriod( trackGroupArrays[periodIndex], + TEST_TIMELINE.getWindow(0, new Timeline.Window()).positionInFirstPeriodUs, new EventDispatcher() .withParameters(/* windowIndex= */ 0, id, /* mediaTimeOffsetMs= */ 0)) { @Override public List getStreamKeys(List trackSelections) { List result = new ArrayList<>(); for (TrackSelection trackSelection : trackSelections) { - int groupIndex = - trackGroupArrays[periodIndex].indexOf(trackSelection.getTrackGroup()); + int groupIndex = trackGroupArrays[periodIndex].indexOf(trackSelection.getTrackGroup()); for (int i = 0; i < trackSelection.length(); i++) { result.add( new StreamKey(periodIndex, groupIndex, trackSelection.getIndexInTrackGroup(i))); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java index 20df898cb8..a88fbed27f 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source; +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.common.truth.Truth.assertThat; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -22,11 +24,13 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; +import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.testutil.FakeMediaPeriod; import com.google.android.exoplayer2.trackselection.FixedTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.common.collect.ImmutableList; import java.util.concurrent.CountDownLatch; import org.checkerframework.checker.nullness.compatqual.NullableType; import org.junit.Test; @@ -47,8 +51,10 @@ public final class MergingMediaPeriodTest { public void getTrackGroups_returnsAllChildTrackGroups() throws Exception { MergingMediaPeriod mergingMediaPeriod = prepareMergingPeriod( - new MergingPeriodDefinition(/* timeOffsetUs= */ 0, childFormat11, childFormat12), - new MergingPeriodDefinition(/* timeOffsetUs= */ 0, childFormat21, childFormat22)); + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat11, childFormat12), + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat21, childFormat22)); assertThat(mergingMediaPeriod.getTrackGroups().length).isEqualTo(4); assertThat(mergingMediaPeriod.getTrackGroups().get(0).getFormat(0)).isEqualTo(childFormat11); @@ -61,8 +67,10 @@ public final class MergingMediaPeriodTest { public void selectTracks_createsSampleStreamsFromChildPeriods() throws Exception { MergingMediaPeriod mergingMediaPeriod = prepareMergingPeriod( - new MergingPeriodDefinition(/* timeOffsetUs= */ 0, childFormat11, childFormat12), - new MergingPeriodDefinition(/* timeOffsetUs= */ 0, childFormat21, childFormat22)); + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat11, childFormat12), + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat21, childFormat22)); TrackSelection selectionForChild1 = new FixedTrackSelection(mergingMediaPeriod.getTrackGroups().get(1), /* track= */ 0); @@ -97,8 +105,16 @@ public final class MergingMediaPeriodTest { throws Exception { MergingMediaPeriod mergingMediaPeriod = prepareMergingPeriod( - new MergingPeriodDefinition(/* timeOffsetUs= */ 0, childFormat11, childFormat12), - new MergingPeriodDefinition(/* timeOffsetUs= */ -3000, childFormat21, childFormat22)); + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, + /* singleSampleTimeUs= */ 123_000, + childFormat11, + childFormat12), + new MergingPeriodDefinition( + /* timeOffsetUs= */ -3000, + /* singleSampleTimeUs= */ 456_000, + childFormat21, + childFormat22)); TrackSelection selectionForChild1 = new FixedTrackSelection(mergingMediaPeriod.getTrackGroups().get(0), /* track= */ 0); @@ -122,14 +138,14 @@ public final class MergingMediaPeriodTest { assertThat(childMediaPeriod1.selectTracksPositionUs).isEqualTo(0); assertThat(streams[0].readData(formatHolder, inputBuffer, /* formatRequired= */ false)) .isEqualTo(C.RESULT_BUFFER_READ); - assertThat(inputBuffer.timeUs).isEqualTo(0L); + assertThat(inputBuffer.timeUs).isEqualTo(123_000L); FakeMediaPeriodWithSelectTracksPosition childMediaPeriod2 = (FakeMediaPeriodWithSelectTracksPosition) mergingMediaPeriod.getChildPeriod(1); assertThat(childMediaPeriod2.selectTracksPositionUs).isEqualTo(3000L); assertThat(streams[1].readData(formatHolder, inputBuffer, /* formatRequired= */ false)) .isEqualTo(C.RESULT_BUFFER_READ); - assertThat(inputBuffer.timeUs).isEqualTo(0L); + assertThat(inputBuffer.timeUs).isEqualTo(456_000 - 3000); } private MergingMediaPeriod prepareMergingPeriod(MergingPeriodDefinition... definitions) @@ -137,10 +153,11 @@ public final class MergingMediaPeriodTest { MediaPeriod[] mediaPeriods = new MediaPeriod[definitions.length]; long[] timeOffsetsUs = new long[definitions.length]; for (int i = 0; i < definitions.length; i++) { - timeOffsetsUs[i] = definitions[i].timeOffsetUs; - TrackGroup[] trackGroups = new TrackGroup[definitions[i].formats.length]; - for (int j = 0; j < definitions[i].formats.length; j++) { - trackGroups[j] = new TrackGroup(definitions[i].formats[j]); + MergingPeriodDefinition definition = definitions[i]; + timeOffsetsUs[i] = definition.timeOffsetUs; + TrackGroup[] trackGroups = new TrackGroup[definition.formats.length]; + for (int j = 0; j < definition.formats.length; j++) { + trackGroups[j] = new TrackGroup(definition.formats[j]); } mediaPeriods[i] = new FakeMediaPeriodWithSelectTracksPosition( @@ -149,7 +166,10 @@ public final class MergingMediaPeriodTest { .withParameters( /* windowIndex= */ i, new MediaPeriodId(/* periodUid= */ i), - /* mediaTimeOffsetMs= */ 0)); + /* mediaTimeOffsetMs= */ 0), + /* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) -> + ImmutableList.of( + oneByteSample(definition.singleSampleTimeUs), END_OF_STREAM_ITEM)); } MergingMediaPeriod mergingMediaPeriod = new MergingMediaPeriod( @@ -179,8 +199,15 @@ public final class MergingMediaPeriodTest { public long selectTracksPositionUs; public FakeMediaPeriodWithSelectTracksPosition( - TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher) { - super(trackGroupArray, eventDispatcher); + TrackGroupArray trackGroupArray, + EventDispatcher eventDispatcher, + TrackDataFactory trackDataFactory) { + super( + trackGroupArray, + trackDataFactory, + eventDispatcher, + DrmSessionManager.DUMMY, + /* deferOnPrepared= */ false); selectTracksPositionUs = C.TIME_UNSET; } @@ -199,11 +226,13 @@ public final class MergingMediaPeriodTest { private static final class MergingPeriodDefinition { - public long timeOffsetUs; - public Format[] formats; + public final long timeOffsetUs; + public final long singleSampleTimeUs; + public final Format[] formats; - public MergingPeriodDefinition(long timeOffsetUs, Format... formats) { + public MergingPeriodDefinition(long timeOffsetUs, long singleSampleTimeUs, Format... formats) { this.timeOffsetUs = timeOffsetUs; + this.singleSampleTimeUs = singleSampleTimeUs; this.formats = formats; } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java index 9fa06eed57..3d569aba6d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.video; +import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -39,6 +40,7 @@ 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.util.MimeTypes; +import com.google.common.collect.ImmutableList; import java.util.concurrent.Phaser; import org.junit.After; import org.junit.Before; @@ -183,12 +185,10 @@ public final class DecoderVideoRendererTest { public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); renderer.enable( RendererConfiguration.DEFAULT, @@ -212,12 +212,10 @@ public final class DecoderVideoRendererTest { throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); renderer.enable( RendererConfiguration.DEFAULT, @@ -240,12 +238,10 @@ public final class DecoderVideoRendererTest { public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); renderer.enable( RendererConfiguration.DEFAULT, @@ -271,22 +267,18 @@ public final class DecoderVideoRendererTest { public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); renderer.enable( RendererConfiguration.DEFAULT, new Format[] {H264_FORMAT}, @@ -317,22 +309,18 @@ public final class DecoderVideoRendererTest { public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( - /* format= */ H264_FORMAT, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ H264_FORMAT, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); renderer.enable( RendererConfiguration.DEFAULT, new Format[] {H264_FORMAT}, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java index 85b7604e42..840b87e227 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.video; +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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -45,6 +47,7 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryExcep import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem; import com.google.android.exoplayer2.util.MimeTypes; +import com.google.common.collect.ImmutableList; import java.util.Collections; import java.util.List; import org.junit.Before; @@ -126,15 +129,14 @@ public class MediaCodecVideoRendererTest { public void render_dropsLateBuffer() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50_000, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), // First buffer. - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), // Late buffer. - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), // Last buffer. - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), // First buffer. + oneByteSample(/* timeUs= */ 50_000), // Late buffer. + oneByteSample(/* timeUs= */ 100_000), // Last buffer. + FakeSampleStreamItem.END_OF_STREAM_ITEM)); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, new Format[] {VIDEO_H264}, @@ -163,13 +165,11 @@ public class MediaCodecVideoRendererTest { RendererConfiguration.DEFAULT, new Format[] {VIDEO_H264}, new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 0, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM), + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)), /* positionUs= */ 0, /* joining= */ false, /* mayRenderStartOfStream= */ true, @@ -201,12 +201,10 @@ public class MediaCodecVideoRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ pAsp1, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 5000, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ pAsp1, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, @@ -220,16 +218,12 @@ public class MediaCodecVideoRendererTest { mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000); mediaCodecVideoRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000); - fakeSampleStream.addFakeSampleStreamItem(new FakeSampleStreamItem(pAsp2)); - fakeSampleStream.addFakeSampleStreamItem( - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); - fakeSampleStream.addFakeSampleStreamItem( - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); - fakeSampleStream.addFakeSampleStreamItem(new FakeSampleStreamItem(pAsp3)); - fakeSampleStream.addFakeSampleStreamItem( - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); - fakeSampleStream.addFakeSampleStreamItem( - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + 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); mediaCodecVideoRenderer.setCurrentStreamFinal(); @@ -251,12 +245,10 @@ public class MediaCodecVideoRendererTest { throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, new Format[] {VIDEO_H264}, @@ -270,8 +262,7 @@ public class MediaCodecVideoRendererTest { mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000); mediaCodecVideoRenderer.resetPosition(0); mediaCodecVideoRenderer.setCurrentStreamFinal(); - fakeSampleStream.addFakeSampleStreamItem( - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + fakeSampleStream.addFakeSampleStreamItem(oneByteSample(/* timeUs= */ 0)); fakeSampleStream.addFakeSampleStreamItem(FakeSampleStreamItem.END_OF_STREAM_ITEM); int positionUs = 10; do { @@ -286,12 +277,10 @@ public class MediaCodecVideoRendererTest { public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, @@ -313,12 +302,10 @@ public class MediaCodecVideoRendererTest { throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, @@ -339,12 +326,10 @@ public class MediaCodecVideoRendererTest { public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME)); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of(oneByteSample(/* timeUs= */ 0))); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, @@ -366,22 +351,20 @@ public class MediaCodecVideoRendererTest { public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), + FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), + FakeSampleStreamItem.END_OF_STREAM_ITEM)); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, new Format[] {VIDEO_H264}, @@ -410,22 +393,20 @@ public class MediaCodecVideoRendererTest { public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), + FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( - /* format= */ VIDEO_H264, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), + FakeSampleStreamItem.END_OF_STREAM_ITEM)); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, new Format[] {VIDEO_H264}, @@ -458,26 +439,23 @@ public class MediaCodecVideoRendererTest { public void onVideoFrameProcessingOffset_isCalledAfterOutputFormatChanges() throws ExoPlaybackException { Format mp4Uhd = VIDEO_H264.buildUpon().setWidth(3840).setHeight(2160).build(); - byte[] sampleData = new byte[0]; FakeSampleStream fakeSampleStream = new FakeSampleStream( - /* format= */ mp4Uhd, DrmSessionManager.DUMMY, /* eventDispatcher= */ null, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 50, - new FakeSampleStreamItem(mp4Uhd), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(VIDEO_H264), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(mp4Uhd), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - new FakeSampleStreamItem(VIDEO_H264), - new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME), - FakeSampleStreamItem.END_OF_STREAM_ITEM); + /* initialFormat= */ mp4Uhd, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0), + format(VIDEO_H264), + oneByteSample(/* timeUs= */ 50), + oneByteSample(/* timeUs= */ 100), + format(mp4Uhd), + oneByteSample(/* timeUs= */ 150), + oneByteSample(/* timeUs= */ 200), + oneByteSample(/* timeUs= */ 250), + format(VIDEO_H264), + oneByteSample(/* timeUs= */ 300), + FakeSampleStreamItem.END_OF_STREAM_ITEM)); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java index 67b08cbd58..e4e7002d8c 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java @@ -58,7 +58,14 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod FakeChunkSource.Factory chunkSourceFactory, long durationUs, @Nullable TransferListener transferListener) { - super(trackGroupArray, eventDispatcher); + super( + trackGroupArray, + /* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) -> { + throw new RuntimeException("unused track data"); + }, + eventDispatcher, + DrmSessionManager.DUMMY, + /* deferOnPrepared= */ false); this.allocator = allocator; this.chunkSourceFactory = chunkSourceFactory; this.transferListener = transferListener; diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java index 451293746d..216f823f5b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java @@ -38,7 +38,13 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource { Timeline timeline, TrackGroupArray trackGroupArray, FakeChunkSource.Factory chunkSourceFactory) { - super(timeline, DrmSessionManager.DUMMY, trackGroupArray); + super( + timeline, + DrmSessionManager.DUMMY, + /* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) -> { + throw new RuntimeException("Unused TrackDataFactory"); + }, + trackGroupArray); this.chunkSourceFactory = chunkSourceFactory; } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java index e83d924293..a69265fb3b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java @@ -15,6 +15,8 @@ */ 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.common.truth.Truth.assertThat; import android.net.Uri; @@ -22,17 +24,22 @@ import android.os.Handler; import android.os.SystemClock; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaPeriod; +import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem; import com.google.android.exoplayer2.trackselection.TrackSelection; 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 java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -52,6 +59,7 @@ public class FakeMediaPeriod implements MediaPeriod { private final List sampleStreams; private final DrmSessionManager drmSessionManager; private final EventDispatcher eventDispatcher; + private final TrackDataFactory trackDataFactory; private final long fakePreparationLoadTaskId; @Nullable private Handler playerHandler; @@ -64,48 +72,71 @@ public class FakeMediaPeriod implements MediaPeriod { private long discontinuityPositionUs; /** - * Constructs a FakeMediaPeriod. + * Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}. * * @param trackGroupArray The track group array. + * @param singleSampleTimeUs The timestamp to use for the single sample in each track, in + * microseconds. * @param eventDispatcher A dispatcher for media source events. */ - public FakeMediaPeriod(TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher) { - this(trackGroupArray, DrmSessionManager.DUMMY, eventDispatcher, /* deferOnPrepared */ false); + public FakeMediaPeriod( + TrackGroupArray trackGroupArray, long singleSampleTimeUs, EventDispatcher eventDispatcher) { + this( + trackGroupArray, + TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs), + eventDispatcher, + DrmSessionManager.DUMMY, + /* deferOnPrepared= */ false); } /** - * Constructs a FakeMediaPeriod. + * Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}. * * @param trackGroupArray The track group array. + * @param singleSampleTimeUs The timestamp to use for the single sample in each track, in + * microseconds. + * @param eventDispatcher A dispatcher for media source events. * @param drmSessionManager The {@link DrmSessionManager} used for DRM interactions. - * @param eventDispatcher A dispatcher for media source events. + * @param deferOnPrepared Whether {@link Callback#onPrepared(MediaPeriod)} should be called only + * after {@link #setPreparationComplete()} has been called. If {@code false} */ public FakeMediaPeriod( TrackGroupArray trackGroupArray, + long singleSampleTimeUs, + EventDispatcher eventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher) { - this(trackGroupArray, drmSessionManager, eventDispatcher, /* deferOnPrepared */ false); + boolean deferOnPrepared) { + this( + trackGroupArray, + TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs), + eventDispatcher, + drmSessionManager, + deferOnPrepared); } /** * Constructs a FakeMediaPeriod. * * @param trackGroupArray The track group array. - * @param drmSessionManager The DrmSessionManager used for DRM interactions. + * @param trackDataFactory A source for the underlying sample data for each track in {@code + * trackGroupArray}. * @param eventDispatcher A dispatcher for media source events. - * @param deferOnPrepared Whether {@link MediaPeriod.Callback#onPrepared(MediaPeriod)} should be - * called only after {@link #setPreparationComplete()} has been called. If {@code false} - * preparation completes immediately. + * @param drmSessionManager The DrmSessionManager used for DRM interactions. + * @param deferOnPrepared Whether {@link Callback#onPrepared(MediaPeriod)} should be called only + * after {@link #setPreparationComplete()} has been called. If {@code false} preparation + * completes immediately. */ public FakeMediaPeriod( TrackGroupArray trackGroupArray, - DrmSessionManager drmSessionManager, + TrackDataFactory trackDataFactory, EventDispatcher eventDispatcher, + DrmSessionManager drmSessionManager, boolean deferOnPrepared) { this.trackGroupArray = trackGroupArray; this.drmSessionManager = drmSessionManager; this.eventDispatcher = eventDispatcher; this.deferOnPrepared = deferOnPrepared; + this.trackDataFactory = trackDataFactory; discontinuityPositionUs = C.TIME_UNSET; sampleStreams = new ArrayList<>(); fakePreparationLoadTaskId = LoadEventInfo.getNewId(); @@ -282,13 +313,16 @@ public class FakeMediaPeriod implements MediaPeriod { TrackSelection selection, DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher) { - return new FakeSampleStream( - selection.getSelectedFormat(), - drmSessionManager, - eventDispatcher, - positionUs, - /* timeUsIncrement= */ 0, - FakeSampleStream.SINGLE_SAMPLE_THEN_END_OF_STREAM); + FakeSampleStream sampleStream = + new FakeSampleStream( + drmSessionManager, + eventDispatcher, + selection.getSelectedFormat(), + trackDataFactory.create( + selection.getSelectedFormat(), + Assertions.checkNotNull(eventDispatcher.mediaPeriodId))); + sampleStream.seekTo(positionUs); + return sampleStream; } /** @@ -300,8 +334,7 @@ public class FakeMediaPeriod implements MediaPeriod { */ protected void seekSampleStream(SampleStream sampleStream, long positionUs) { // Queue a single sample from the seek position again. - ((FakeSampleStream) sampleStream) - .resetSampleStreamItems(positionUs, FakeSampleStream.SINGLE_SAMPLE_THEN_END_OF_STREAM); + ((FakeSampleStream) sampleStream).seekTo(positionUs); } /** @@ -334,4 +367,27 @@ public class FakeMediaPeriod implements MediaPeriod { /* mediaStartTimeUs= */ 0, /* 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(DrmSessionManager, 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 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); + } + } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java index d4d4e76054..741594686a 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java @@ -36,6 +36,7 @@ import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.testutil.FakeMediaPeriod.TrackDataFactory; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.TransferListener; @@ -46,6 +47,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** * Fake {@link MediaSource} that provides a given timeline. Creating the period will return a {@link @@ -78,6 +80,7 @@ public class FakeMediaSource extends BaseMediaSource { private static final int MANIFEST_LOAD_BYTES = 100; private final TrackGroupArray trackGroupArray; + @Nullable private final FakeMediaPeriod.TrackDataFactory trackDataFactory; private final ArrayList activeMediaPeriods; private final ArrayList createdMediaPeriods; private final DrmSessionManager drmSessionManager; @@ -107,18 +110,35 @@ public class FakeMediaSource extends BaseMediaSource { */ public FakeMediaSource( @Nullable Timeline timeline, DrmSessionManager drmSessionManager, Format... formats) { - this(timeline, drmSessionManager, buildTrackGroupArray(formats)); + this(timeline, drmSessionManager, /* trackDataFactory= */ null, buildTrackGroupArray(formats)); + } + + /** + * Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with a + * {@link TrackGroupArray} using the given {@link Format}s. It passes {@code drmSessionManager} + * and {@code trackDataFactory} into the created periods. The provided {@link Timeline} may be + * null to prevent an immediate source info refresh message when preparing the media source. It + * can be manually set later using {@link #setNewSourceInfo(Timeline)}. + */ + public FakeMediaSource( + @Nullable Timeline timeline, + DrmSessionManager drmSessionManager, + @Nullable FakeMediaPeriod.TrackDataFactory trackDataFactory, + Format... formats) { + this(timeline, drmSessionManager, trackDataFactory, buildTrackGroupArray(formats)); } /** * Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with the - * given {@link TrackGroupArray}. The provided {@link Timeline} may be null to prevent an + * provided {@link TrackGroupArray}, {@link DrmSessionManager} and {@link + * FakeMediaPeriod.TrackDataFactory}. The provided {@link Timeline} may be null to prevent an * immediate source info refresh message when preparing the media source. It can be manually set * later using {@link #setNewSourceInfo(Timeline)}. */ public FakeMediaSource( @Nullable Timeline timeline, DrmSessionManager drmSessionManager, + @Nullable FakeMediaPeriod.TrackDataFactory trackDataFactory, TrackGroupArray trackGroupArray) { if (timeline != null) { this.timeline = timeline; @@ -127,6 +147,7 @@ public class FakeMediaSource extends BaseMediaSource { this.activeMediaPeriods = new ArrayList<>(); this.createdMediaPeriods = new ArrayList<>(); this.drmSessionManager = drmSessionManager; + this.trackDataFactory = trackDataFactory; } @Nullable @@ -292,6 +313,7 @@ public class FakeMediaSource extends BaseMediaSource { * May be null if no listener is available. * @return A new {@link FakeMediaPeriod}. */ + @RequiresNonNull("this.timeline") protected FakeMediaPeriod createFakeMediaPeriod( MediaPeriodId id, TrackGroupArray trackGroupArray, @@ -299,8 +321,17 @@ public class FakeMediaSource extends BaseMediaSource { DrmSessionManager drmSessionManager, EventDispatcher eventDispatcher, @Nullable TransferListener transferListener) { + long positionInWindowUs = + timeline.getPeriodByUid(id.periodUid, new Period()).getPositionInWindowUs(); + long defaultFirstSampleTimeUs = positionInWindowUs >= 0 || id.isAd() ? 0 : -positionInWindowUs; return new FakeMediaPeriod( - trackGroupArray, drmSessionManager, eventDispatcher, /* deferOnPrepared= */ false); + trackGroupArray, + trackDataFactory != null + ? trackDataFactory + : TrackDataFactory.singleSampleWithTimeUs(defaultFirstSampleTimeUs), + eventDispatcher, + drmSessionManager, + /* deferOnPrepared= */ false); } private void finishSourcePreparation(boolean sendManifestLoadEvents) { diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java index 0d84bcb48c..420b9b83ae 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java @@ -15,9 +15,11 @@ */ package com.google.android.exoplayer2.testutil; + 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; @@ -29,9 +31,11 @@ import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; +import com.google.common.collect.Iterables; import java.io.IOException; -import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -40,11 +44,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; */ 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 { - @Nullable Format format; - @Nullable byte[] sampleData; - int flags; /** * Item that designates the end of stream has been reached. @@ -52,118 +65,126 @@ public class FakeSampleStream implements SampleStream { *

When this item is read, readData will repeatedly return end of stream. */ public static final FakeSampleStreamItem END_OF_STREAM_ITEM = - new FakeSampleStreamItem(new byte[] {}, C.BUFFER_FLAG_END_OF_STREAM); + sample( + /* timeUs= */ Long.MAX_VALUE, + C.BUFFER_FLAG_END_OF_STREAM, + /* sampleData= */ new byte[] {}); + + /** Creates an item representing the provided format. */ + public static FakeSampleStreamItem format(Format format) { + return new FakeSampleStreamItem(format, /* sampleInfo= */ null); + } /** - * Item that, when {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} is called, will - * return {@link C#RESULT_FORMAT_READ} with the new format. + * Creates an item representing a sample with the provided timestamp. * - * @param format The format to be returned. + *

The sample will contain a single byte of data. + * + * @param timeUs The timestamp of the sample. */ - public FakeSampleStreamItem(Format format) { + public static FakeSampleStreamItem oneByteSample(long timeUs) { + return oneByteSample(timeUs, /* flags= */ 0); + } + + /** + * Creates an item representing a sample with the provided timestamp and flags. + * + *

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)}. + */ + public static FakeSampleStreamItem oneByteSample(long timeUs, @BufferFlags int flags) { + return sample(timeUs, flags, new byte[] {0}); + } + + /** + * 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 sampleData The sample data. + */ + public static FakeSampleStreamItem sample( + long timeUs, @BufferFlags int flags, byte[] sampleData) { + return new FakeSampleStreamItem( + /* format= */ null, new SampleInfo(sampleData.clone(), flags, timeUs)); + } + + @Nullable private final Format format; + @Nullable private final SampleInfo sampleInfo; + + /** + * Creates an instance. Exactly one of {@code format} or {@code sampleInfo} must be non-null. + */ + private FakeSampleStreamItem(@Nullable Format format, @Nullable SampleInfo sampleInfo) { + Assertions.checkArgument((format == null) != (sampleInfo == null)); this.format = format; - } - - /** - * Item that, when {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} is called, will - * return {@link C#RESULT_BUFFER_READ} with the sample data. - * - * @param sampleData The sample data to be read. - */ - public FakeSampleStreamItem(byte[] sampleData) { - this.sampleData = sampleData.clone(); - } - - /** - * Item that, when {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} is called, will - * return {@link C#RESULT_BUFFER_READ} with the sample data. - * - * @param sampleData The sample data to be read. - * @param flags The buffer flags to be set. - */ - public FakeSampleStreamItem(byte[] sampleData, int flags) { - this.sampleData = sampleData.clone(); - this.flags = flags; + this.sampleInfo = sampleInfo; } } - /** Constant array for use when a single sample is to be output, followed by the end of stream. */ - public static final FakeSampleStreamItem[] SINGLE_SAMPLE_THEN_END_OF_STREAM = - new FakeSampleStreamItem[] { - new FakeSampleStreamItem(new byte[] {0}), FakeSampleStreamItem.END_OF_STREAM_ITEM - }; - private final Format initialFormat; - private final ArrayDeque fakeSampleStreamItems; - private final int timeUsIncrement; + private final List fakeSampleStreamItems; private final DrmSessionManager drmSessionManager; @Nullable private final EventDispatcher eventDispatcher; + private int sampleItemIndex; private @MonotonicNonNull Format downstreamFormat; - private long timeUs; private boolean readEOSBuffer; @Nullable private DrmSession currentDrmSession; /** - * Creates fake sample stream which outputs the given {@link Format}, optionally one sample with - * zero bytes, then end of stream. + * Creates a fake sample stream which outputs the given {@link Format} followed by the provided + * {@link FakeSampleStreamItem items}. * - * @param format The {@link Format} to output. - * @param eventDispatcher An {@link EventDispatcher} to notify of read events. - * @param shouldOutputSample Whether the sample stream should output a sample. - */ - public FakeSampleStream( - Format format, @Nullable EventDispatcher eventDispatcher, boolean shouldOutputSample) { - this( - format, - DrmSessionManager.DUMMY, - eventDispatcher, - /* firstSampleTimeUs= */ 0, - /* timeUsIncrement= */ 0, - shouldOutputSample - ? SINGLE_SAMPLE_THEN_END_OF_STREAM - : new FakeSampleStreamItem[] {FakeSampleStreamItem.END_OF_STREAM_ITEM}); - } - - /** - * Creates a fake sample stream which outputs the given {@link Format}, any amount of {@link - * FakeSampleStreamItem items}, then end of stream. - * - * @param format The {@link Format} to output. * @param drmSessionManager A {@link DrmSessionManager} for DRM interactions. * @param eventDispatcher An {@link EventDispatcher} to notify of read events. - * @param firstSampleTimeUs The time at which samples will start being output, in microseconds. - * @param timeUsIncrement The time each sample should increase by, in microseconds. + * @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)}. Note that once an - * EOS buffer has been read, that will return every time readData is called. + * 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( - Format format, DrmSessionManager drmSessionManager, @Nullable EventDispatcher eventDispatcher, - long firstSampleTimeUs, - int timeUsIncrement, - FakeSampleStreamItem... fakeSampleStreamItems) { - this.initialFormat = format; + Format initialFormat, + List fakeSampleStreamItems) { this.drmSessionManager = drmSessionManager; this.eventDispatcher = eventDispatcher; - this.fakeSampleStreamItems = new ArrayDeque<>(Arrays.asList(fakeSampleStreamItems)); - this.timeUs = firstSampleTimeUs; - this.timeUsIncrement = timeUsIncrement; + this.initialFormat = initialFormat; + this.fakeSampleStreamItems = new ArrayList<>(fakeSampleStreamItems); } /** - * Clears and assigns new samples provided by this sample stream. + * Seeks inside this sample stream. * - * @param timeUs The time at which samples will start being output, in microseconds. - * @param fakeSampleStreamItems The {@link FakeSampleStreamItem items} to provide. + *

Seeks to just before the first sample with {@code sampleTime >= timeUs}, or to the end of + * the stream otherwise. */ - public void resetSampleStreamItems(long timeUs, FakeSampleStreamItem... fakeSampleStreamItems) { - this.fakeSampleStreamItems.clear(); - this.fakeSampleStreamItems.addAll(Arrays.asList(fakeSampleStreamItems)); - this.timeUs = timeUs; - readEOSBuffer = false; + public void seekTo(long timeUs) { + Format applicableFormat = initialFormat; + for (int i = 0; i < fakeSampleStreamItems.size(); i++) { + @Nullable SampleInfo sampleInfo = fakeSampleStreamItems.get(i).sampleInfo; + if (sampleInfo == null) { + applicableFormat = Assertions.checkNotNull(fakeSampleStreamItems.get(i).format); + continue; + } + if (sampleInfo.timeUs >= timeUs) { + sampleItemIndex = i; + readEOSBuffer = false; + if (downstreamFormat != null && !applicableFormat.equals(downstreamFormat)) { + notifyEventDispatcher(applicableFormat); + } + return; + } + } + sampleItemIndex = fakeSampleStreamItems.size(); + readEOSBuffer = true; } /** @@ -177,10 +198,10 @@ public class FakeSampleStream implements SampleStream { @Override public boolean isReady() { - if (fakeSampleStreamItems.isEmpty()) { + if (sampleItemIndex == fakeSampleStreamItems.size()) { return readEOSBuffer || downstreamFormat == null; } - if (fakeSampleStreamItems.peek().format != null) { + if (fakeSampleStreamItems.get(sampleItemIndex).format != null) { // A format can be read. return true; } @@ -200,29 +221,28 @@ public class FakeSampleStream implements SampleStream { return C.RESULT_BUFFER_READ; } - if (!fakeSampleStreamItems.isEmpty()) { - FakeSampleStreamItem fakeSampleStreamItem = fakeSampleStreamItems.remove(); + if (sampleItemIndex < fakeSampleStreamItems.size()) { + FakeSampleStreamItem fakeSampleStreamItem = fakeSampleStreamItems.get(sampleItemIndex); + sampleItemIndex++; if (fakeSampleStreamItem.format != null) { onFormatResult(fakeSampleStreamItem.format, formatHolder); return C.RESULT_FORMAT_READ; } else { - byte[] sampleData = Assertions.checkNotNull(fakeSampleStreamItem.sampleData); - if (fakeSampleStreamItem.flags != 0) { - buffer.setFlags(fakeSampleStreamItem.flags); + 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()) { - // Put the item back so we can consume it next time. - fakeSampleStreamItems.addFirst(fakeSampleStreamItem); + sampleItemIndex--; return C.RESULT_NOTHING_READ; } - buffer.timeUs = timeUs; - timeUs += timeUsIncrement; - buffer.ensureSpaceForWrite(sampleData.length); - buffer.data.put(sampleData); + buffer.timeUs = sampleInfo.timeUs; + buffer.ensureSpaceForWrite(sampleInfo.data.length); + buffer.data.put(sampleInfo.data); return C.RESULT_BUFFER_READ; } } @@ -237,7 +257,7 @@ public class FakeSampleStream implements SampleStream { downstreamFormat = newFormat; @Nullable DrmInitData newDrmInitData = newFormat.drmInitData; outputFormatHolder.drmSession = currentDrmSession; - notifyEventDispatcher(outputFormatHolder); + notifyEventDispatcher(newFormat); if (!isFirstFormat && Util.areEqual(oldDrmInitData, newDrmInitData)) { // Nothing to do. return; @@ -260,11 +280,16 @@ public class FakeSampleStream implements SampleStream { 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 - || (!fakeSampleStreamItems.isEmpty() - && (fakeSampleStreamItems.peek().flags & C.BUFFER_FLAG_ENCRYPTED) == 0 - && drmSession.playClearSamplesWithoutKeys()); + || (nextSampleIsClear && drmSession.playClearSamplesWithoutKeys()); } @Override @@ -276,6 +301,7 @@ public class FakeSampleStream implements SampleStream { @Override public int skipData(long positionUs) { + // TODO: Implement this. return 0; } @@ -287,14 +313,22 @@ public class FakeSampleStream implements SampleStream { } } - private void notifyEventDispatcher(FormatHolder formatHolder) { + private void notifyEventDispatcher(Format format) { if (eventDispatcher != 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; eventDispatcher.downstreamFormatChanged( C.TRACK_TYPE_UNKNOWN, - formatHolder.format, + format, C.SELECTION_REASON_UNKNOWN, /* trackSelectionData= */ null, - /* mediaTimeUs= */ timeUs); + /* mediaTimeUs= */ nextSampleTimeUs); } } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java index 3fc9143fa7..303a727061 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java @@ -37,8 +37,7 @@ public final class FakeTimeline extends Timeline { public static final long DEFAULT_WINDOW_DURATION_US = 10 * C.MICROS_PER_SECOND; /** Default offset of a window in its first period in microseconds. */ - public static final long DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US = - 10_000 * C.MICROS_PER_SECOND; + public static final long DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US = 123 * C.MICROS_PER_SECOND; public final int periodCount; public final Object id; @@ -187,7 +186,7 @@ public final class FakeTimeline extends Timeline { public static final MediaItem FAKE_MEDIA_ITEM = new MediaItem.Builder().setMediaId("FakeTimeline").setUri(Uri.EMPTY).build(); - private static final long AD_DURATION_US = 10 * C.MICROS_PER_SECOND; + private static final long AD_DURATION_US = 5 * C.MICROS_PER_SECOND; private final TimelineWindowDefinition[] windowDefinitions; private final Object[] manifests;