Drive Android decoding by codec readiness and bounded working frames

This commit is contained in:
evgeny committed 2026-10-14 13:36:38 +02:00
1 parent 223c9a153c
commit f72e2fca94
3 files changed
+262 -72

No files matched your search

@@ -0,0 +1,79 @@
package com.utun.chat.data
import org.junit.Assert.*
import org.junit.Test
import java.util.ArrayDeque
class StreamDecodePumpTest {
private data class Frame(override val pts: Long) : StreamDecodePump.Frame
private class Codec(val depth: Int) {
val queued = ArrayDeque<Frame>()
var released = 0
var blocked = false
var finishing = false
var held = false
var nextIndex = 0
val pump = StreamDecodePump<Frame>(
submit = { if (blocked) false else { queued.addLast(it); true } },
receive = {
if (held || (queued.size < depth && !finishing) || queued.isEmpty()) null
else { held = true; StreamDecodePump.Output(nextIndex++, queued.removeFirst().pts) }
},
release = { _, _ -> check(held); held = false; released++ }
)
}
@Test fun fourAndEightFramePipelinesStartWithFrozenPresentationClock() {
for (depth in listOf(4, 8)) {
val codec = Codec(depth)
repeat(depth) { i ->
assertTrue(codec.pump.wantsInput)
codec.pump.offer(Frame(i * 66666L), i * 66666L)
codec.pump.step(0, i * 66666L)
}
assertEquals(depth.toLong(), codec.pump.inputCount)
assertEquals(1L, codec.pump.outputCount)
assertEquals(1, codec.released)
assertEquals(0L, codec.pump.firstOutputPts)
assertEquals((depth - 1) * 66666L, codec.pump.decoderLeadUs)
}
}
@Test fun futureOutputStopsReadsAndNeverReleasesEarly() {
val codec = Codec(1)
codec.pump.offer(Frame(100000), 0); codec.pump.step(0, 0)
assertFalse(codec.pump.wantsInput); assertNotNull(codec.pump.output); assertEquals(0, codec.released)
codec.pump.step(99999, 15_000_000); codec.pump.checkProgress(15_000_000, false)
assertEquals(0, codec.released)
codec.pump.step(100000, 15_000_001)
assertEquals(1, codec.released); assertTrue(codec.pump.wantsInput)
}
@Test fun blockedCodecKeepsOneInputAndReportsStall() {
val codec = Codec(4); codec.blocked = true
codec.pump.offer(Frame(0), 0); codec.pump.step(0, 0)
assertNotNull(codec.pump.input); assertFalse(codec.pump.wantsInput)
try { codec.pump.checkProgress(10_000_001, false); fail("Stalled decoder must fail") }
catch (expected: IllegalStateException) { assertTrue(expected.message!!.contains("no output progress")) }
}
@Test fun networkPauseKeepsReferencesAndAllowsIncompletePipelineToResume() {
val codec = Codec(4)
repeat(2) { i -> codec.pump.offer(Frame(i * 40000L), i * 40000L); codec.pump.step(0, i * 40000L) }
codec.pump.checkProgress(20_000_000, false)
repeat(2) { i ->
val now = 20_000_000 + i * 40000L
codec.pump.offer(Frame((i + 2) * 40000L), now); codec.pump.step(0, now); codec.pump.checkProgress(now, false)
}
assertEquals(4L, codec.pump.inputCount); assertEquals(1, codec.released)
}
@Test fun finishDrainsShortStreamAndPreservesTheLastPicture() {
val codec = Codec(8)
repeat(3) { i -> codec.pump.offer(Frame(i * 40000L), i * 40000L); codec.pump.step(0, i * 40000L) }
assertEquals(0, codec.released); codec.finishing = true
codec.pump.step(0, 120000); assertEquals(1, codec.released); assertFalse(codec.pump.wantsInput)
codec.pump.step(40000, 160000); assertEquals(2, codec.released)
codec.pump.step(80000, 200000); assertEquals(3, codec.released)
}
}