Acknowledge Android frames after RenderThread consumption on all supported APIs
This commit is contained in:
1 parent
b11a1beb44
commit
02e7bbd9af
2 files changed
+21
-4
No files matched your search
@@ -14,7 +14,13 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
private val outputs = ConcurrentLinkedQueue<Output>()
|
||||
private data class Presentation(val surface: Surface, val pts: Long)
|
||||
private val presentations = ConcurrentLinkedQueue<Presentation>()
|
||||
fun presented(surface: Surface, pts: Long) { if (running) presentations.add(Presentation(surface, pts)) }
|
||||
@Volatile private var awaiting: Presentation? = null
|
||||
fun presented(surface: Surface, pts: Long): Boolean {
|
||||
val expected = awaiting ?: return true
|
||||
if (!running || expected.surface !== surface) return true
|
||||
if (expected.pts != pts) return false
|
||||
presentations.add(expected); return true
|
||||
}
|
||||
private val displayed = MutableStateFlow<CallVideoFormat?>(null)
|
||||
val display: StateFlow<CallVideoFormat?> = displayed
|
||||
@Volatile private var verified = emptyMap<Int, CallVideoCodec.Companion.Decoder>()
|
||||
@@ -47,7 +53,7 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
try { decoder?.stop() } catch (e: Exception) { LogManager.addLog("WARN", "call", "video decoder stop: ${e.message}") }
|
||||
try { decoder?.release() } catch (e: Exception) { LogManager.addLog("WARN", "call", "video decoder release: ${e.message}") }
|
||||
decoder = null; format = null; pictures.clear(); inputs.clear()
|
||||
awaitingPts = -1; presentations.clear()
|
||||
awaitingPts = -1; awaiting = null; presentations.clear()
|
||||
draining = false; endSeen = false; lastOutput = -1
|
||||
}
|
||||
try {
|
||||
@@ -64,7 +70,9 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
}
|
||||
while (true) {
|
||||
val consumed = presentations.poll() ?: break
|
||||
if (consumed.surface === surface && consumed.pts == awaitingPts) { awaitingPts = -1; worked = true }
|
||||
if (consumed.surface === surface && consumed.pts == awaitingPts) {
|
||||
awaitingPts = -1; awaiting = null; worked = true
|
||||
}
|
||||
}
|
||||
// Малый output lookahead; основной долг остаётся в encoded FIFO.
|
||||
while (decoder != null && pictures.size < 2 && !endSeen) {
|
||||
@@ -96,6 +104,7 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
|
||||
if (decision > 0) wakeUs = now / 1000 + decision
|
||||
else if (first != null) {
|
||||
val render = decision == 0L
|
||||
if (render) awaiting = Presentation(checkNotNull(surface), first.timing.pts)
|
||||
decoder!!.releaseOutputBuffer(first.index, render); pictures.removeFirst(); worked = true
|
||||
if (render) {
|
||||
awaitingPts = first.timing.pts
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.utun.chat.ui.screens
|
||||
import android.content.Context
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.SurfaceTexture
|
||||
import android.os.Build
|
||||
import android.view.Surface
|
||||
import android.view.TextureView
|
||||
import androidx.compose.foundation.background
|
||||
@@ -123,10 +124,17 @@ private class CallVideoTexture(context: Context, private val video: CallVideoEng
|
||||
override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) { fit() }
|
||||
override fun onSurfaceTextureDestroyed(texture: SurfaceTexture): Boolean { closeOutput(); return false }
|
||||
override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {
|
||||
attached?.let { video.presented(it, texture.timestamp / 1000) }
|
||||
val surface = attached ?: return
|
||||
// TextureView callback предшествует dequeue RenderThread; timestamp читаем после commit.
|
||||
if (Build.VERSION.SDK_INT >= 29) viewTreeObserver.registerFrameCommitCallback { consumed(texture, surface) }
|
||||
else postOnAnimation { consumed(texture, surface) }
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun consumed(texture: SurfaceTexture, surface: Surface) {
|
||||
if (closed || attached !== surface) return
|
||||
if (!video.presented(surface, texture.timestamp / 1000)) postOnAnimation { consumed(texture, surface) }
|
||||
}
|
||||
private fun fit() {
|
||||
val f = format ?: return
|
||||
if (width == 0 || height == 0) return
|
||||
|
||||
Reference in new issue
Block a user