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 f48e64c9..262debcb 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 @@ -59,6 +59,8 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv var lastHead = -1L var headChangedNs = 0L var endDrainUntil = 0L + var headEpoch = 0L + var previousRawHead = 0L fun resetVideo() { try { decoder?.stop() } @@ -75,15 +77,28 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv catch (e: Exception) { LogManager.addLog("WARN", "stream", "audio release id=$id: ${e.message}") } track = null if (opus != 0L) NativeLib.streamOpusClose(opus) - opus = 0; audioBase = -1; written = 0; lastHead = -1; audios.clear() + opus = 0; audioBase = -1; written = 0; lastHead = -1; headEpoch = 0; previousRawHead = 0; audios.clear() + } + // PlaybackHeadPosition — unsigned 32 bit: при 48kHz оборачивается примерно через 25 часов. + fun playedFrames(t: AudioTrack): Long { + val raw = t.playbackHeadPosition.toLong() and 0xffffffffL + if (raw < previousRawHead) headEpoch += 0x100000000L + previousRawHead = raw + return headEpoch + raw } fun currentPts(now: Long): Long { val t = track if (t != null && audioBase >= 0) { + val head = playedFrames(t) val played = if (t.getTimestamp(audioTimestamp)) { - (audioTimestamp.framePosition + (now - audioTimestamp.nanoTime).coerceAtLeast(0) * 48000 / 1_000_000_000) + var position = audioTimestamp.framePosition + if (position <= 0xffffffffL) { + position += headEpoch + if (position - head > 0x80000000L) position -= 0x100000000L + if (head - position > 0x80000000L) position += 0x100000000L + } + position + (now - audioTimestamp.nanoTime).coerceAtLeast(0) * 48000 / 1_000_000_000 } else { - val head = t.playbackHeadPosition.toLong() and 0xffffffffL if (head != lastHead) { lastHead = head; headChangedNs = now } head + if (head >= written) (now - headChangedNs) * 48000 / 1_000_000_000 else 0 } @@ -168,7 +183,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv } val t = track if (t != null && audios.isNotEmpty()) { - val head = t.playbackHeadPosition.toLong() and 0xffffffffL + val head = playedFrames(t) if (written - head < 3840) { val f = audios.removeFirst() val missing = ((f.pts - audioBase - written * 1_000_000 / 48000).toDouble() / 20000).roundToInt()