From d681e264aaebea54b21cbd68c17da357144998df Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 11:18:22 +0000 Subject: [PATCH] Skip played server side inserted ads in a single period window This change makes sure played server side ads are skipped in a single period timeline. It avoids creating an ad-MediaPeriodInfo for played postrolls and creates a content info instead. It also sets the end position for content infos that terminate the stream before the stream is actually finished. This prevents the player from continue playing the remaining media delivered by the MediaPeriod. We also make sure that the discontinuity of played ads are not reported because there is actually no discontinuity. #minor-release PiperOrigin-RevId: 428734387 --- .../exoplayer/ExoPlayerImplInternal.java | 11 +- .../media3/exoplayer/MediaPeriodInfo.java | 7 +- .../media3/exoplayer/MediaPeriodQueue.java | 27 +++- .../media3/exoplayer/ExoPlayerTest.java | 149 ++++++++++++------ .../exoplayer/MediaPeriodQueueTest.java | 2 +- .../ServerSideAdInsertionMediaSourceTest.java | 2 +- .../playbackdumps/mp4/ssai-sample.mp4.dump | 79 ++++++++++ 7 files changed, 218 insertions(+), 59 deletions(-) create mode 100644 libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 14d3f9af17..99896816ae 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.max; import static java.lang.Math.min; @@ -2153,14 +2154,20 @@ import java.util.concurrent.atomic.AtomicBoolean; // If we advance more than one period at a time, notify listeners after each update. maybeNotifyPlaybackInfoChanged(); } - MediaPeriodHolder newPlayingPeriodHolder = queue.advancePlayingPeriod(); + MediaPeriodHolder newPlayingPeriodHolder = checkNotNull(queue.advancePlayingPeriod()); + boolean isCancelledSSAIAdTransition = + playbackInfo.periodId.periodUid.equals(newPlayingPeriodHolder.info.id.periodUid) + && playbackInfo.periodId.adGroupIndex == C.INDEX_UNSET + && newPlayingPeriodHolder.info.id.adGroupIndex == C.INDEX_UNSET + && playbackInfo.periodId.nextAdGroupIndex + != newPlayingPeriodHolder.info.id.nextAdGroupIndex; playbackInfo = handlePositionDiscontinuity( newPlayingPeriodHolder.info.id, newPlayingPeriodHolder.info.startPositionUs, newPlayingPeriodHolder.info.requestedContentPositionUs, /* discontinuityStartPositionUs= */ newPlayingPeriodHolder.info.startPositionUs, - /* reportDiscontinuity= */ true, + /* reportDiscontinuity= */ !isCancelledSSAIAdTransition, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); resetPendingPauseAtEndOfPeriod(); updatePlaybackPositions(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java index 520fd63606..cd50c7e739 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java @@ -39,9 +39,10 @@ import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; public final long requestedContentPositionUs; /** * The end position to which the media period's content is clipped in order to play a following ad - * group, in microseconds, or {@link C#TIME_UNSET} if there is no following ad group or if this - * media period is an ad. The value {@link C#TIME_END_OF_SOURCE} indicates that a postroll ad - * follows at the end of this content media period. + * group or to terminate a server side ad inserted stream before a played postroll, in + * microseconds, or {@link C#TIME_UNSET} if the content is not clipped or if this media period is + * an ad. The value {@link C#TIME_END_OF_SOURCE} indicates that a postroll ad follows at the end + * of this content media period. */ public final long endPositionUs; /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java index 12edf0605c..a0731462bb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java @@ -21,6 +21,7 @@ import static java.lang.Math.max; import android.os.Handler; import android.util.Pair; import androidx.annotation.Nullable; +import androidx.media3.common.AdPlaybackState; import androidx.media3.common.C; import androidx.media3.common.Player.RepeatMode; import androidx.media3.common.Timeline; @@ -801,8 +802,14 @@ import com.google.common.collect.ImmutableList; } else { // Play the next ad group if it's still available. int adIndexInAdGroup = period.getFirstAdIndexToPlay(currentPeriodId.nextAdGroupIndex); - if (adIndexInAdGroup == period.getAdCountInAdGroup(currentPeriodId.nextAdGroupIndex)) { - // The next ad group has no ads left to play. Play content from the end position instead. + boolean isPlayedServerSideInsertedAd = + period.isServerSideInsertedAdGroup(currentPeriodId.nextAdGroupIndex) + && period.getAdState(currentPeriodId.nextAdGroupIndex, adIndexInAdGroup) + == AdPlaybackState.AD_STATE_PLAYED; + if (adIndexInAdGroup == period.getAdCountInAdGroup(currentPeriodId.nextAdGroupIndex) + || isPlayedServerSideInsertedAd) { + // The next ad group has no ads left to play or is a played SSAI ad group. Play content from + // the end position instead. long startPositionUs = getMinStartPositionAfterAdGroupUs( timeline, currentPeriodId.periodUid, currentPeriodId.nextAdGroupIndex); @@ -888,6 +895,20 @@ import com.google.common.collect.ImmutableList; long windowSequenceNumber) { timeline.getPeriodByUid(periodUid, period); int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs); + boolean clipPeriodAtContentDuration = false; + if (nextAdGroupIndex == C.INDEX_UNSET) { + // Clip SSAI streams when at the end of the period. + clipPeriodAtContentDuration = + period.getAdGroupCount() > 0 + && period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount()); + } else if (period.isServerSideInsertedAdGroup(nextAdGroupIndex) + && period.getAdGroupTimeUs(nextAdGroupIndex) == period.durationUs) { + if (period.hasPlayedAdGroup(nextAdGroupIndex)) { + // Clip period before played SSAI post-rolls. + nextAdGroupIndex = C.INDEX_UNSET; + clipPeriodAtContentDuration = true; + } + } MediaPeriodId id = new MediaPeriodId(periodUid, windowSequenceNumber, nextAdGroupIndex); boolean isLastInPeriod = isLastInPeriod(id); boolean isLastInWindow = isLastInWindow(timeline, id); @@ -897,7 +918,7 @@ import com.google.common.collect.ImmutableList; long endPositionUs = nextAdGroupIndex != C.INDEX_UNSET ? period.getAdGroupTimeUs(nextAdGroupIndex) - : C.TIME_UNSET; + : clipPeriodAtContentDuration ? period.durationUs : C.TIME_UNSET; long durationUs = endPositionUs == C.TIME_UNSET || endPositionUs == C.TIME_END_OF_SOURCE ? period.durationUs diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 1ae8722199..67300997f5 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -46,8 +46,11 @@ import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; import static androidx.media3.common.Player.COMMAND_SET_VOLUME; import static androidx.media3.common.Player.COMMAND_STOP; import static androidx.media3.common.Player.STATE_ENDED; +import static androidx.media3.exoplayer.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; import static androidx.media3.test.utils.TestUtil.assertTimelinesSame; import static androidx.media3.test.utils.robolectric.RobolectricUtil.runMainLooperUntil; import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.playUntilPosition; @@ -789,21 +792,8 @@ public final class ExoPlayerTest { .getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true); player.release(); - // There is still one discontinuity from content to content for the failed ad insertion. - PositionInfo positionInfo = - new PositionInfo( - window.uid, - /* mediaItemIndex= */ 0, - window.mediaItem, - period.uid, - /* periodIndex= */ 0, - /* positionMs= */ 5_000, - /* contentPositionMs= */ 5_000, - /* adGroupIndex= */ C.INDEX_UNSET, - /* adIndexInAdGroup= */ C.INDEX_UNSET); - verify(mockListener) - .onPositionDiscontinuity( - positionInfo, positionInfo, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + // Content to content transition is ignored. + verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt()); } @Test @@ -863,24 +853,7 @@ public final class ExoPlayerTest { .getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true); player.release(); - // There is still one discontinuity from content to content for the failed ad insertion and the - // normal ad transition for the successful ad insertion. - PositionInfo positionInfoFailedAd = - new PositionInfo( - window.uid, - /* mediaItemIndex= */ 0, - window.mediaItem, - period.uid, - /* periodIndex= */ 0, - /* positionMs= */ 5_000, - /* contentPositionMs= */ 5_000, - /* adGroupIndex= */ C.INDEX_UNSET, - /* adIndexInAdGroup= */ C.INDEX_UNSET); - verify(mockListener) - .onPositionDiscontinuity( - positionInfoFailedAd, - positionInfoFailedAd, - Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + // There content to content discontinuity after the failed ad is suppressed. PositionInfo positionInfoContentAtSuccessfulAd = new PositionInfo( window.uid, @@ -5029,10 +5002,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); // seek discontinuities assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5121,10 +5093,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 2, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 2, 0, 0, 0).inOrder(); // seek assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5184,10 +5155,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 0, 0, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 0, 0, 0, 0, 0).inOrder(); // seek discontinuity assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(newPositions.get(0).periodIndex).isEqualTo(0); @@ -5252,10 +5222,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); // seek discontinuity assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5334,10 +5303,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1).inOrder(); // seek discontinuity assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5347,7 +5315,8 @@ public final class ExoPlayerTest { } @Test - public void play_playedSSAIPreMidPostRolls_skipsAllAds() throws Exception { + public void play_playedSSAIPreMidPostRollsMultiPeriodWindow_contentPeriodTransitionsOnly() + throws Exception { ArgumentCaptor oldPositionArgumentCaptor = ArgumentCaptor.forClass(PositionInfo.class); ArgumentCaptor newPositionArgumentCaptor = @@ -5371,7 +5340,7 @@ public final class ExoPlayerTest { AtomicReference sourceReference = new AtomicReference<>(); sourceReference.set( new ServerSideAdInsertionMediaSource( - new FakeMediaSource(adTimeline), + new FakeMediaSource(adTimeline, ExoPlayerTestRunner.AUDIO_FORMAT), contentTimeline -> { sourceReference .get() @@ -5385,16 +5354,18 @@ public final class ExoPlayerTest { runUntilPlaybackState(player, Player.STATE_ENDED); player.release(); + ArgumentCaptor playbackStateCaptor = ArgumentCaptor.forClass(Integer.class); + verify(listener, times(3)).onPlaybackStateChanged(playbackStateCaptor.capture()); + assertThat(playbackStateCaptor.getAllValues()).containsExactly(2, 3, 4).inOrder(); verify(listener, times(3)) .onPositionDiscontinuity( oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(0, 0, 0).inOrder(); - // Auto discontinuity from the empty ad period to the first content period. + // Auto discontinuity from the empty pre-roll period to the first content period. assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); assertThat(oldPositions.get(0).positionMs).isEqualTo(0); @@ -5407,7 +5378,7 @@ public final class ExoPlayerTest { assertThat(newPositions.get(1).periodIndex).isEqualTo(4); assertThat(newPositions.get(1).adGroupIndex).isEqualTo(-1); assertThat(newPositions.get(1).positionMs).isEqualTo(1250); - // Auto discontinuity from the second content period to the last frame of the last postroll. + // Auto discontinuity from the second content period to the last frame of the last ad period. assertThat(oldPositions.get(2).periodIndex).isEqualTo(4); assertThat(oldPositions.get(2).adGroupIndex).isEqualTo(-1); assertThat(newPositions.get(2).periodIndex).isEqualTo(7); @@ -5415,6 +5386,86 @@ public final class ExoPlayerTest { assertThat(newPositions.get(2).positionMs).isEqualTo(2500); } + @Test + public void play_playedSSAIPreMidPostRollsSinglePeriodWindow_noDiscontinuities() + throws Exception { + AdPlaybackState adPlaybackState = + addAdGroupToAdPlaybackState( + new AdPlaybackState("adsId"), + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + addAdGroupToAdPlaybackState( + adPlaybackState, + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + (3 * C.MICROS_PER_SECOND), + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + addAdGroupToAdPlaybackState( + adPlaybackState, + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + (5 * C.MICROS_PER_SECOND), + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + addAdGroupToAdPlaybackState( + adPlaybackState, + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + (9 * C.MICROS_PER_SECOND), + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup+ */ 0); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup+ */ 0); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup+ */ 0); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 3, /* adIndexInAdGroup+ */ 0); + FakeTimeline adTimeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + "windowId", + /* isSeekable= */ true, + /* isDynamic= */ false, + /* isLive= */ false, + /* isPlaceholder= */ false, + /* durationUs= */ DEFAULT_WINDOW_DURATION_US, + /* defaultPositionUs= */ 0, + /* windowOffsetInFirstPeriodUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + /* adPlaybackStates= */ ImmutableList.of(adPlaybackState), + MediaItem.EMPTY)); + + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline, ExoPlayerTestRunner.AUDIO_FORMAT), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.prepare(); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + long finalPositionMs = player.getCurrentPosition(); + player.release(); + + assertThat(finalPositionMs).isEqualTo(6000); + verify(listener, never()).onPositionDiscontinuity(any(), any(), anyInt()); + ArgumentCaptor playbackStateCaptor = ArgumentCaptor.forClass(Integer.class); + verify(listener, times(3)).onPlaybackStateChanged(playbackStateCaptor.capture()); + assertThat(playbackStateCaptor.getAllValues()).containsExactly(2, 3, 4).inOrder(); + } + @Test public void becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled() throws Exception { ExoPlayer player = new TestExoPlayerBuilder(context).build(); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index f0c0b42e9d..c649b6a680 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -388,7 +388,7 @@ public final class MediaPeriodQueueTest { /* periodUid= */ firstPeriodUid, /* startPositionUs= */ SECOND_AD_START_TIME_US, /* requestedContentPositionUs= */ SECOND_AD_START_TIME_US, - /* endPositionUs= */ C.TIME_UNSET, + /* endPositionUs= */ CONTENT_DURATION_US, /* durationUs= */ CONTENT_DURATION_US, /* isFollowedByTransitionToSameStream= */ false, /* isLastInPeriod= */ true, diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java index ef8d30a56c..82a5941365 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java @@ -68,7 +68,7 @@ public final class ServerSideAdInsertionMediaSourceTest { ShadowMediaCodecConfig.forAllSupportedMimeTypes(); private static final String TEST_ASSET = "asset:///media/mp4/sample.mp4"; - private static final String TEST_ASSET_DUMP = "playbackdumps/mp4/sample.mp4.dump"; + private static final String TEST_ASSET_DUMP = "playbackdumps/mp4/ssai-sample.mp4.dump"; @Test public void timeline_containsAdsDefinedInAdPlaybackState() throws Exception { diff --git a/libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump b/libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump new file mode 100644 index 0000000000..5417858cbd --- /dev/null +++ b/libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump @@ -0,0 +1,79 @@ +MediaCodecAdapter (exotest.audio.aac): + buffers.length = 44 + buffers[0] = length 23, hash 47DE9131 + buffers[1] = length 6, hash 31EC5206 + buffers[2] = length 148, hash 894A176B + buffers[3] = length 189, hash CEF235A1 + buffers[4] = length 205, hash BBF5F7B0 + buffers[5] = length 210, hash F278B193 + buffers[6] = length 210, hash 82DA1589 + buffers[7] = length 207, hash 5BE231DF + buffers[8] = length 225, hash 18819EE1 + buffers[9] = length 215, hash CA7FA67B + buffers[10] = length 211, hash 581A1C18 + buffers[11] = length 216, hash ADB88187 + buffers[12] = length 229, hash 2E8BA4DC + buffers[13] = length 232, hash 22F0C510 + buffers[14] = length 235, hash 867AD0DC + buffers[15] = length 231, hash 84E823A8 + buffers[16] = length 226, hash 1BEF3A95 + buffers[17] = length 216, hash EAA345AE + buffers[18] = length 229, hash 6957411F + buffers[19] = length 219, hash 41275022 + buffers[20] = length 241, hash 6495DF96 + buffers[21] = length 228, hash 63D95906 + buffers[22] = length 238, hash 34F676F9 + buffers[23] = length 234, hash E5CBC045 + buffers[24] = length 231, hash 5FC43661 + buffers[25] = length 217, hash 682708ED + buffers[26] = length 239, hash D43780FC + buffers[27] = length 243, hash C5E17980 + buffers[28] = length 231, hash AC5837BA + buffers[29] = length 230, hash 169EE895 + buffers[30] = length 238, hash C48FF3F1 + buffers[31] = length 225, hash 531E4599 + buffers[32] = length 232, hash CB3C6B8D + buffers[33] = length 243, hash F8C94C7 + buffers[34] = length 232, hash A646A7D0 + buffers[35] = length 237, hash E8B787A5 + buffers[36] = length 228, hash 3FA7A29F + buffers[37] = length 235, hash B9B33B0A + buffers[38] = length 264, hash 71A4869E + buffers[39] = length 257, hash D049B54C + buffers[40] = length 227, hash 66757231 + buffers[41] = length 227, hash BD374F1B + buffers[42] = length 235, hash 999477F6 + buffers[43] = length 0, hash 1 +MediaCodecAdapter (exotest.video.avc): + buffers.length = 31 + buffers[0] = length 36692, hash D216076E + buffers[1] = length 5312, hash D45D3CA0 + buffers[2] = length 599, hash 1BE7812D + buffers[3] = length 7735, hash 4490F110 + buffers[4] = length 987, hash 560B5036 + buffers[5] = length 673, hash ED7CD8C7 + buffers[6] = length 523, hash 3020DF50 + buffers[7] = length 6061, hash 736C72B2 + buffers[8] = length 992, hash FE132F23 + buffers[9] = length 623, hash 5B2C1816 + buffers[10] = length 421, hash 742E69C1 + buffers[11] = length 4899, hash F72F86A1 + buffers[12] = length 568, hash 519A8E50 + buffers[13] = length 620, hash 3990AA39 + buffers[14] = length 5450, hash F06EC4AA + buffers[15] = length 1051, hash 92DFA63A + buffers[16] = length 874, hash 69587FB4 + buffers[17] = length 781, hash 36BE495B + buffers[18] = length 4725, hash AC0C8CD3 + buffers[19] = length 1022, hash 5D8BFF34 + buffers[20] = length 790, hash 99413A99 + buffers[21] = length 610, hash 5E129290 + buffers[22] = length 2751, hash 769974CB + buffers[23] = length 745, hash B78A477A + buffers[24] = length 621, hash CF741E7A + buffers[25] = length 505, hash 1DB4894E + buffers[26] = length 1268, hash C15348DC + buffers[27] = length 880, hash C2DE85D0 + buffers[28] = length 530, hash C98BC6A8 + buffers[29] = length 568, hash 4FE5C8EA + buffers[30] = length 0, hash 1