@@ -21,6 +21,34 @@ import org.schabi.newpipe.extractor.services.youtube.sabr.YoutubeSabrStreamState
2121import java.time.Instant
2222
2323class SabrProgressivePlaybackWindowTest {
24+ @Test
25+ fun `vod audio starts at the playhead rather than the preceding video keyframe` () = runTest {
26+ val audio = format(140 , isAudio = true )
27+ val video = format(137 , isAudio = false )
28+ val state = mockk<YoutubeSabrStreamState >(relaxed = true )
29+ every { state.getSegmentNumberAtOrAfterTimeMs(video, 100_000L ) } returns 16
30+ every { state.getSegmentNumberAtOrAfterTimeMs(audio, 100_000L ) } returns 11
31+ val session = mockk<YoutubeSabrSession >(relaxed = true )
32+ every { session.streamState } returns state
33+ every { session.getReadableSegment(match { it.format == video && it.sequenceNumber == 16 }) } returns
34+ readableSegment(video, sequence = 16 , startMs = 95_000L , durationMs = 7_000L )
35+ every { session.getReadableSegment(match { it.format == audio && it.sequenceNumber == 11 }) } returns
36+ readableSegment(audio, sequence = 11 , startMs = 99_845L , durationMs = 9_985L )
37+ every { session.getCachedSegment(any()) } returns null
38+ val holder = holder(session, audio, video)
39+ val store = mockk<SabrSessionStore >()
40+ coEvery { store.cachedSegment(holder, any()) } returns null
41+
42+ val result = SabrPlaybackWindowBuilder (store).build(
43+ holder,
44+ SabrPlaybackWindowRequest (0L , 100_000L , video.itag, audio.itag, bufferGoalMs = 1_000L ),
45+ )
46+
47+ assertTrue(result.isReady)
48+ assertEquals(95_000L , requireNotNull(result.response.video).segments.single().startMs)
49+ assertEquals(99_845L , result.response.audio.segments.single().startMs)
50+ }
51+
2452 @Test
2553 fun `vod window exposes timeline segments before their payload completes` () = runTest {
2654 val audio = format(140 , isAudio = true )
@@ -53,12 +81,17 @@ class SabrProgressivePlaybackWindowTest {
5381 assertEquals(9_985L , result.response.audio.segments.single().durationMs)
5482 }
5583
56- private fun readableSegment (format : YoutubeSabrFormat , durationMs : Long ): SabrMediaSegment {
84+ private fun readableSegment (
85+ format : YoutubeSabrFormat ,
86+ sequence : Int = 1,
87+ startMs : Long = 0L,
88+ durationMs : Long ,
89+ ): SabrMediaSegment {
5790 val header = mockk<SabrMediaHeader >()
5891 every { header.itag } returns format.itag
5992 every { header.isInitSegment } returns false
60- every { header.sequenceNumber } returns 1
61- every { header.startMs } returns 0L
93+ every { header.sequenceNumber } returns sequence
94+ every { header.startMs } returns startMs
6295 every { header.durationMs } returns durationMs
6396 return mockk<SabrMediaSegment >().also {
6497 every { it.header } returns header
0 commit comments