@@ -2459,7 +2459,7 @@ public void handleContentTimelineChanged_preRollWithAssetList_resolveAssetListIm
24592459 + "#EXT-X-DATERANGE:"
24602460 + "ID=\" ad1-0\" ,"
24612461 + "CLASS=\" com.apple.hls.interstitial\" ,"
2462- + "START-DATE=\" 2020-01-02T21:00:30 .000Z\" ,"
2462+ + "START-DATE=\" 2020-01-02T21:01:21 .000Z\" ,"
24632463 + "X-ASSET-LIST=\" http://example.com/assetlist-1-0.json\" "
24642464 + "\n " ;
24652465 when (mockPlayer .getContentPosition ()).thenReturn (0L );
@@ -2483,7 +2483,7 @@ public void handleContentTimelineChanged_preRollWithAssetList_resolveAssetListIm
24832483 runMainLooperUntil (assetListLoadingListener ::completed , TIMEOUT_MS , Clock .DEFAULT );
24842484 verify (mockAdsLoaderListener )
24852485 .onAssetListLoadCompleted (eq (contentMediaItem ), eq ("adsId" ), eq (0 ), eq (0 ), any ());
2486- assertThat (midRollPlayerMessage .getPositionMs ()).isEqualTo (3_000L );
2486+ assertThat (midRollPlayerMessage .getPositionMs ()).isEqualTo (54_000L );
24872487 assertThat (midRollPlayerMessage .getPayload ()).isEqualTo (contentMediaItem );
24882488 assertThat (midRollPlayerMessage .getLooper ()).isEqualTo (Looper .myLooper ());
24892489 InOrder inOrder = inOrder (mockPlayer );
@@ -2493,6 +2493,144 @@ public void handleContentTimelineChanged_preRollWithAssetList_resolveAssetListIm
24932493 inOrder .verifyNoMoreInteractions ();
24942494 }
24952495
2496+ @ Test
2497+ public void
2498+ handleContentTimelineChanged_midRollCloserToPreviousAdThan3TimesTargetDuration_schedulesNextPlayerMessageAtEndOfPreviousAdGroup ()
2499+ throws IOException , TimeoutException {
2500+ String playlistString =
2501+ "#EXTM3U\n "
2502+ + "#EXT-X-TARGETDURATION:9\n "
2503+ + "#EXT-X-PROGRAM-DATE-TIME:2020-01-02T21:00:00.000Z\n "
2504+ + "#EXTINF:9,\n "
2505+ + "main0.ts\n "
2506+ + "#EXTINF:81,\n "
2507+ + "main0.ts\n "
2508+ + "#EXT-X-ENDLIST"
2509+ + "\n "
2510+ + "#EXT-X-DATERANGE:"
2511+ // The pre roll ad has a content resume offset equal to its duration of 10.123s.
2512+ // The duration is set by the asset list JSON loaded during the test.
2513+ + "ID=\" ad0-0\" ,"
2514+ + "CLASS=\" com.apple.hls.interstitial\" ,"
2515+ + "START-DATE=\" 2020-01-02T22:00:00.000Z\" ,"
2516+ + "CUE=\" PRE\" ,"
2517+ + "X-ASSET-LIST=\" http://example.com/assetlist-0-0.json\" "
2518+ + "\n "
2519+ + "#EXT-X-DATERANGE:"
2520+ + "ID=\" ad1-0\" ,"
2521+ + "CLASS=\" com.apple.hls.interstitial\" ,"
2522+ // trigger at 00:02.000 (29 - (3 * 9)) is within content resume offset
2523+ + "START-DATE=\" 2020-01-02T21:00:29.000Z\" ,"
2524+ + "X-ASSET-LIST=\" http://example.com/assetlist-1-0.json\" "
2525+ + "\n " ;
2526+ when (mockPlayer .getContentPosition ()).thenReturn (0L );
2527+ PlayerMessage midRollPlayerMessage =
2528+ new PlayerMessage (
2529+ mock (PlayerMessage .Sender .class ),
2530+ mock (PlayerMessage .Target .class ),
2531+ Timeline .EMPTY ,
2532+ /* defaultMediaItemIndex= */ 0 ,
2533+ /* Clock ignored */ null ,
2534+ /* Looper ignored */ null );
2535+ when (mockPlayer .createMessage (any ())).thenReturn (midRollPlayerMessage );
2536+
2537+ callHandleContentTimelineChangedAndCaptureAdPlaybackState (
2538+ playlistString ,
2539+ adsLoader ,
2540+ /* windowIndex= */ 0 ,
2541+ /* windowPositionInPeriodUs= */ 0 ,
2542+ /* windowEndPositionInPeriodUs= */ C .TIME_END_OF_SOURCE );
2543+
2544+ runMainLooperUntil (assetListLoadingListener ::completed , TIMEOUT_MS , Clock .DEFAULT );
2545+ // Assert the message is scheduled at the position right after the pre roll's resume offset.
2546+ assertThat (midRollPlayerMessage .getPositionMs ()).isEqualTo (10_123L );
2547+ }
2548+
2549+ @ Test
2550+ public void
2551+ handleContentTimelineChanged_liveMidRollCloserToPreviousAdThan3TimesTargetDuration_schedulesNextPlayerMessageAtEndOfPreviousAdGroup ()
2552+ throws IOException , TimeoutException {
2553+ when (mockPlayer .getContentPosition ()).thenReturn (0L );
2554+ PlayerMessage midRollPlayerMessage =
2555+ new PlayerMessage (
2556+ mock (PlayerMessage .Sender .class ),
2557+ mock (PlayerMessage .Target .class ),
2558+ Timeline .EMPTY ,
2559+ /* defaultMediaItemIndex= */ 0 ,
2560+ /* Clock ignored */ null ,
2561+ /* Looper ignored */ null );
2562+ when (mockPlayer .createMessage (any ())).thenReturn (midRollPlayerMessage );
2563+ when (mockPlayer .getCurrentMediaItem ()).thenReturn (contentMediaItem );
2564+
2565+ callHandleContentTimelineChangedForLiveAndCaptureAdPlaybackStates (
2566+ adsLoader ,
2567+ /* startAdsLoader= */ true ,
2568+ /* windowOffsetInFirstPeriodUs= */ 0L ,
2569+ "#EXTM3U\n "
2570+ + "#EXT-X-TARGETDURATION:9\n "
2571+ // window.positionInFirstPeriod = 0
2572+ + "#EXT-X-PROGRAM-DATE-TIME:2020-01-02T21:00:00.000Z\n "
2573+ + "#EXTINF:9,\n "
2574+ + "main0.ts\n "
2575+ + "#EXTINF:9,\n "
2576+ + "main0.ts\n "
2577+ + "#EXTINF:9,\n "
2578+ + "main0.ts\n "
2579+ + "\n " ,
2580+ "#EXTM3U\n "
2581+ + "#EXT-X-TARGETDURATION:9\n "
2582+ // window.positionInFirstPeriod = 9_000_000
2583+ + "#EXT-X-PROGRAM-DATE-TIME:2020-01-02T21:00:09.000Z\n "
2584+ + "#EXTINF:9,\n "
2585+ + "main0.ts\n "
2586+ + "#EXTINF:9,\n "
2587+ + "main0.ts"
2588+ + "\n "
2589+ // adGroup.timeUs = 18_000_900 (window positionMs: 9_000; resumes 29_123)
2590+ + "#EXT-X-DATERANGE:"
2591+ + "ID=\" ad0-0\" ,"
2592+ + "CLASS=\" com.apple.hls.interstitial\" ,"
2593+ + "START-DATE=\" 2020-01-02T21:00:18.000Z\" ,"
2594+ + "DURATION=20.123,"
2595+ + "X-ASSET-URI=\" http://example.com/media-0-0.ts\" "
2596+ + "\n " ,
2597+ "#EXTM3U\n "
2598+ + "#EXT-X-TARGETDURATION:9\n "
2599+ // window.positionInFirstPeriod = 18_000_000
2600+ + "#EXT-X-PROGRAM-DATE-TIME:2020-01-02T21:00:18.000Z\n "
2601+ + "#EXTINF:9,\n "
2602+ + "main0.ts"
2603+ + "\n "
2604+ + "#EXT-X-DATERANGE:"
2605+ + "ID=\" ad0-0\" ,"
2606+ + "CLASS=\" com.apple.hls.interstitial\" ,"
2607+ // adGroup.timeUs = 18_000_000 (window positionMs: 0; resumes 20_123)
2608+ + "START-DATE=\" 2020-01-02T21:00:18.000Z\" ,"
2609+ + "DURATION=20.123,"
2610+ + "X-ASSET-URI=\" http://example.com/media-0-0.ts\" "
2611+ + "\n "
2612+ + "#EXTINF:81,\n "
2613+ + "main0.ts"
2614+ + "\n "
2615+ + "#EXT-X-DATERANGE:"
2616+ + "ID=\" ad1-0\" ,"
2617+ + "CLASS=\" com.apple.hls.interstitial\" ,"
2618+ // adGroup.timeUs = 58_000_000 (window positionMs: 40_000)
2619+ // Message trigger at window positionMs: 40_000 - 27_000 = 13_000 which is in the resume
2620+ // offset of the previous ad.
2621+ + "START-DATE=\" 2020-01-02T21:00:58.000Z\" ,"
2622+ + "X-ASSET-LIST=\" http://example.com/assetlist-1-0.json\" "
2623+ + "\n " );
2624+
2625+ runMainLooperUntil (
2626+ () -> midRollPlayerMessage .getPositionMs () != C .TIME_UNSET , TIMEOUT_MS , Clock .DEFAULT );
2627+ // Assert that the message is scheduled at the position right after the mid roll's resume
2628+ // offset. At the moment of scheduling, the most recent timeline/playlist has not yet been
2629+ // published. Hence the message must be scheduled at the window position of the last published
2630+ // timeline, which was created with the previous playlist.
2631+ assertThat (midRollPlayerMessage .getPositionMs ()).isEqualTo (29_123L );
2632+ }
2633+
24962634 @ Test
24972635 public void
24982636 handleContentTimelineChanged_startPositionAfterMidRollTimeUs_resolvesAndSchedulesMidRoll ()
@@ -3501,15 +3639,15 @@ public void positionDiscontinuity_reasonSeek_immediatelyResolvesAndSchedulesNext
35013639 TIMEOUT_MS ,
35023640 Clock .DEFAULT );
35033641 assertThat (midRoll1PlayerMessage .isCanceled ()).isTrue ();
3504- assertThat (midRoll1PlayerMessage .getPositionMs ()).isEqualTo (3_000L );
3642+ assertThat (midRoll1PlayerMessage .getPositionMs ()).isEqualTo (10_123L );
35053643 assertThat (midRoll1PlayerMessage .getPayload ()).isEqualTo (contentMediaItem );
35063644 assertThat (midRoll1PlayerMessage .getLooper ()).isEqualTo (Looper .myLooper ());
35073645 assertThat (midRoll2PlayerMessage .isCanceled ()).isTrue ();
35083646 assertThat (midRoll2PlayerMessage .getPositionMs ()).isEqualTo (27_000L );
35093647 assertThat (midRoll2PlayerMessage .getPayload ()).isEqualTo (contentMediaItem );
35103648 assertThat (midRoll1PlayerMessage .getLooper ()).isEqualTo (Looper .myLooper ());
35113649 assertThat (postRollPlayerMessage .isCanceled ()).isFalse ();
3512- assertThat (postRollPlayerMessage .getPositionMs ()).isEqualTo (63_000L );
3650+ assertThat (postRollPlayerMessage .getPositionMs ()).isEqualTo (64_123L );
35133651 assertThat (postRollPlayerMessage .getPayload ()).isEqualTo (contentMediaItem );
35143652 assertThat (midRoll1PlayerMessage .getLooper ()).isEqualTo (Looper .myLooper ());
35153653 ArgumentCaptor <AssetList > argumentCaptor = ArgumentCaptor .forClass (AssetList .class );
0 commit comments