From 9ef18b632473ddb6454680fa8b4976f6efca3c82 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 14:17:35 +0200 Subject: [PATCH] Release Android inflight state at decoder EOS before audio tail --- .../main/java/com/utun/chat/data/StreamDecodePump.kt | 2 +- .../java/com/utun/chat/data/StreamPlayerEngine.kt | 4 +++- .../java/com/utun/chat/data/StreamDecodePumpTest.kt | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamDecodePump.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamDecodePump.kt index 943365a5..1a2fe822 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamDecodePump.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamDecodePump.kt @@ -81,7 +81,7 @@ internal class StreamDecodePump( val render = !next.empty && next.pts >= position - 100000 release(next, render); output = null if (!next.empty) { if (render) shown++ else discarded++ } - if (next.eos) ended = true + if (next.eos) { ended = true; submitted.clear() } } } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt index 9f3822cf..eae13787 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt @@ -263,7 +263,8 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv } } if (playbackStarted && playback != 0L && ready >= 0) { - val lateVideo = video.firstOutputPts == null || (video.oldestSubmittedPts >= 0 && video.oldestSubmittedPts < position - 10000) + val lateVideo = !video.ended && (video.firstOutputPts == null + || (video.oldestSubmittedPts >= 0 && video.oldestSubmittedPts < position - 10000)) if (lateVideo && track != null && !pausedAudio) { track!!.pause(); pausedAudio = true } if (pausedAudio && !lateVideo) { track!!.play(); pausedAudio = false } val head = playedFrames(System.nanoTime()) @@ -282,6 +283,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv MediaCodec.BUFFER_FLAG_END_OF_STREAM); eosQueued = true } } video.step(position, System.nanoTime() / 1000); video.checkProgress(System.nanoTime() / 1000, ended) + check(!video.ended || video.firstOutputPts != null || video.inputCount == 0L) { "Decoder ended without producing an image" } } if (ended && video.input == null && (decoder == null || video.ended) && NativeLib.streamPlaybackEmpty(playback) && playedFrames(System.nanoTime()) >= written) { diff --git a/tools/chatgui-android/app/src/test/java/com/utun/chat/data/StreamDecodePumpTest.kt b/tools/chatgui-android/app/src/test/java/com/utun/chat/data/StreamDecodePumpTest.kt index e5ff64f9..643dc5cd 100644 --- a/tools/chatgui-android/app/src/test/java/com/utun/chat/data/StreamDecodePumpTest.kt +++ b/tools/chatgui-android/app/src/test/java/com/utun/chat/data/StreamDecodePumpTest.kt @@ -95,4 +95,15 @@ class StreamDecodePumpTest { codec.pump.step(120000, 200000) assertEquals(4L, codec.pump.outputCount); assertEquals(120000L, codec.pump.decoderLeadUs) } + + @Test fun emptyEosReleasesTheBufferAndClearsInputsWithoutDisplayPictures() { + var finishing = false; var eof = false; var released = 0 + val pump = StreamDecodePump(submit = { true }, receive = { + if (finishing && !eof) { eof = true; StreamDecodePump.Output(0, 80000, eos = true, empty = true) } else null + }, release = { _, render -> assertFalse(render); released++ }) + repeat(3) { i -> pump.offer(Frame(i * 40000L), i * 40000L); pump.step(0, i * 40000L) } + assertEquals(3, pump.inflight); finishing = true; pump.step(0, 120000) + assertTrue(pump.ended); assertEquals(0, pump.inflight); assertEquals(-1L, pump.oldestSubmittedPts) + assertEquals(1, released); pump.checkProgress(30_000_000, true) + } }