Browse Source

Ignore stale Android call surfaces during activity replacement

master
evgeny 4 days ago
parent
commit
b1390a9542
  1. 1
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt
  2. 6
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoPlayer.kt
  3. 6
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CallVideoPanel.kt

1
tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt

@ -105,6 +105,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
}
fun surface(surface: Surface?, release: () -> Unit = {}) = player.surface(surface, release)
fun detachSurface(surface: Surface) = player.detach(surface)
fun select(settings: CallVideoSettings) { handler.post {
if (!stopped) { wanted = settings; excluded = emptySet(); mutable.value = mutable.value.copy(settings = settings) }
} }

6
tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoPlayer.kt

@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentLinkedQueue
/** Один RX worker; удерживаем output buffer до фактического audio PTS. Новое поколение ждёт drain старого. */
internal class CallVideoPlayer(private val id: Long, private val failure: (String) -> Unit) {
@Volatile private var running = true
private data class Output(val surface: Surface?, val release: () -> Unit)
private data class Output(val surface: Surface?, val release: () -> Unit, val detach: Surface? = null)
private val outputs = ConcurrentLinkedQueue<Output>()
private val displayed = MutableStateFlow<CallVideoFormat?>(null)
val display: StateFlow<CallVideoFormat?> = displayed
@ -18,6 +18,9 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
@Synchronized fun surface(surface: Surface?, release: () -> Unit = {}) {
if (running) outputs.add(Output(surface, release)) else release()
}
@Synchronized fun detach(surface: Surface) {
if (running) outputs.add(Output(null, {}, surface))
}
fun decoders(value: Map<Int, CallVideoCodec.Companion.Decoder>) { verified = value }
fun stop() { synchronized(this) { running = false }; worker.join() }
@ -39,6 +42,7 @@ internal class CallVideoPlayer(private val id: Long, private val failure: (Strin
val now = System.nanoTime()
while (true) {
val next = outputs.poll() ?: break
if (next.detach != null && output?.surface !== next.detach) continue
close(); output?.release?.invoke(); output = next; surface = next.surface
pending = null; displayed.value = null; CallVideoNative.requestKey(id)
}

6
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CallVideoPanel.kt

@ -91,7 +91,7 @@ internal fun CallVideoPreview(video: CallVideoEngine, modifier: Modifier = Modif
}
private class CallVideoTexture(context: Context, private val video: CallVideoEngine) : TextureView(context) {
private var attached = false
private var attached: Surface? = null
private var closed = false
var format: CallVideoFormat? = null
set(value) { field = value; fit() }
@ -100,7 +100,7 @@ private class CallVideoTexture(context: Context, private val video: CallVideoEng
surfaceTextureListener = object : SurfaceTextureListener {
override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
if (closed) return
val surface = Surface(texture); attached = true
val surface = Surface(texture); attached = surface
// RX worker освобождает Surface/texture только после остановки MediaCodec.
video.surface(surface) { surface.release(); texture.release() }; fit()
}
@ -117,7 +117,7 @@ private class CallVideoTexture(context: Context, private val video: CallVideoEng
val scale = minOf(width.toFloat() / w, height.toFloat() / h)
setTransform(Matrix().apply { setScale(w * scale / width, h * scale / height, width / 2f, height / 2f) })
}
private fun closeOutput() { if (attached) { attached = false; video.surface(null) } }
private fun closeOutput() { attached?.let(video::detachSurface); attached = null }
fun close() { closed = true; closeOutput() }
}

Loading…
Cancel
Save