Skip to content

Commit 218e6a3

Browse files
committed
fix: target exact sabr buffered range
1 parent 228bdb3 commit 218e6a3

2 files changed

Lines changed: 51 additions & 12 deletions

File tree

src/main/kotlin/dev/typetype/server/services/SabrTargetRequestShape.kt

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.typetype.server.services
22

3+
import org.schabi.newpipe.extractor.services.youtube.sabr.SabrBufferedRange
34
import org.schabi.newpipe.extractor.services.youtube.sabr.SabrSegmentRequest
45
import org.schabi.newpipe.extractor.services.youtube.sabr.YoutubeSabrFormat
56

@@ -12,13 +13,11 @@ internal inline fun <T> withTargetedRequestShape(
1213
val state = holder.session.streamState
1314
val targetIsAudio = request.format.isAudio
1415
state.setSelectVideoFormatBeforeAudio(targetIsAudio)
15-
state.setLastOnlyRange(request.format, true)
16-
state.setFullyBuffered(companion, true)
16+
state.setBufferedRangesOverride(listOf(companion.fullRange(), request.targetRange(holder)))
1717
return try {
1818
block()
1919
} finally {
20-
state.setFullyBuffered(companion, false)
21-
state.setLastOnlyRange(request.format, false)
20+
state.setBufferedRangesOverride(null)
2221
state.setActiveTrackTypes(holder.isVideoActive(), holder.isAudioActive())
2322
state.setSelectVideoFormatBeforeAudio(holder.playerTimeMs() > SEEK_FORMAT_ORDER_MS)
2423
}
@@ -27,4 +26,36 @@ internal inline fun <T> withTargetedRequestShape(
2726
private fun SabrSessionHolder.companionFormat(format: YoutubeSabrFormat): YoutubeSabrFormat =
2827
if (format.isAudio) videoFormat else audioFormat
2928

29+
private fun YoutubeSabrFormat.fullRange(): SabrBufferedRange =
30+
SabrBufferedRange(
31+
itag,
32+
lastModified,
33+
xtags,
34+
0L,
35+
MAX_RANGE_DURATION_MS,
36+
MAX_RANGE_INDEX,
37+
MAX_RANGE_INDEX,
38+
TIMESCALE,
39+
)
40+
41+
private fun SabrSegmentRequest.targetRange(holder: SabrSessionHolder): SabrBufferedRange {
42+
val state = holder.session.streamState
43+
val startMs = state.getSegmentStartMs(format, sequenceNumber).coerceAtLeast(0L)
44+
val endMs = state.getSegmentEndMs(format, sequenceNumber)
45+
val durationMs = (endMs - startMs).takeIf { it > 0L } ?: 1L
46+
return SabrBufferedRange(
47+
format.itag,
48+
format.lastModified,
49+
format.xtags,
50+
0L,
51+
durationMs,
52+
sequenceNumber,
53+
sequenceNumber,
54+
TIMESCALE,
55+
)
56+
}
57+
3058
private const val SEEK_FORMAT_ORDER_MS = 1_000L
59+
private const val MAX_RANGE_DURATION_MS = Int.MAX_VALUE.toLong()
60+
private const val MAX_RANGE_INDEX = Int.MAX_VALUE
61+
private const val TIMESCALE = 1_000

src/test/kotlin/dev/typetype/server/services/SabrSessionPumpLoopTest.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@ class SabrSessionPumpLoopTest {
3232
every { session.getCachedSegment(request) } returns null
3333
every { streamState.getMinBufferedEndMs() } returns 329_492L
3434
every { streamState.getSegmentStartMs(audio, 35) } returns 340_000L
35+
every { streamState.getSegmentEndMs(audio, 35) } returns 349_476L
3536
every { streamState.setPlayerTimeMs(any()) } returns Unit
36-
every { streamState.setLastOnlyRange(audio, true) } returns Unit
37-
every { streamState.setLastOnlyRange(audio, false) } returns Unit
38-
every { streamState.setFullyBuffered(video, true) } returns Unit
39-
every { streamState.setFullyBuffered(video, false) } returns Unit
37+
every { streamState.setBufferedRangesOverride(any()) } returns Unit
38+
every { streamState.setBufferedRangesOverride(null) } returns Unit
4039
every { streamState.setSelectVideoFormatBeforeAudio(any()) } returns Unit
4140
every { session.prepareForForwardJump(request) } returns Unit
4241
every { session.pumpOnceStreamingUntilCached(any(), request) } returns 0
@@ -51,11 +50,13 @@ class SabrSessionPumpLoopTest {
5150
verify(exactly = 1) { session.pumpOnceStreamingUntilCached(any(), request) }
5251
verifyOrder {
5352
streamState.setSelectVideoFormatBeforeAudio(true)
54-
streamState.setLastOnlyRange(audio, true)
55-
streamState.setFullyBuffered(video, true)
53+
streamState.setBufferedRangesOverride(match { ranges ->
54+
ranges.size == 2 &&
55+
ranges[0].summarize() == "itag=247:seq=2147483647-2147483647:time=0+2147483647:timescale=1000" &&
56+
ranges[1].summarize() == "itag=140:seq=35-35:time=0+9476:timescale=1000"
57+
})
5658
session.pumpOnceStreamingUntilCached(any(), request)
57-
streamState.setFullyBuffered(video, false)
58-
streamState.setLastOnlyRange(audio, false)
59+
streamState.setBufferedRangesOverride(null)
5960
streamState.setActiveTrackTypes(true, true)
6061
}
6162
} finally {
@@ -82,6 +83,13 @@ class SabrSessionPumpLoopTest {
8283
every { format.itag } returns itag
8384
every { format.isAudio } returns isAudio
8485
every { format.bitrate } returns if (isAudio) 128_000 else 2_000_000
86+
every { format.lastModified } returns if (isAudio) AUDIO_LAST_MODIFIED else VIDEO_LAST_MODIFIED
87+
every { format.xtags } returns null
8588
return format
8689
}
90+
91+
private companion object {
92+
const val AUDIO_LAST_MODIFIED = 1_765_814_035_331_078L
93+
const val VIDEO_LAST_MODIFIED = 1_726_365_891_623_401L
94+
}
8795
}

0 commit comments

Comments
 (0)