Browse Source

Apply Android call bitrate changes in place on validated keyframes

stream-multiview
evgeny 1 week ago
parent
commit
152d89f0dc
  1. 16
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoCodec.kt
  2. 14
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt

16
tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoCodec.kt

@ -10,7 +10,9 @@ import android.os.Bundle
import java.nio.ByteBuffer
/** Codec принадлежит одному worker. YUV input отделён от камеры: замена на IDR не меняет capture PTS. */
internal class CallVideoCodec(requested: CallVideoFormat, val bitrate: Int, val name: String) : AutoCloseable {
internal class CallVideoCodec(requested: CallVideoFormat, bitrate: Int, val name: String) : AutoCloseable {
var bitrate = bitrate
private set
var format = requested
private set
data class Packet(val pts: Long, val key: Boolean, val bytes: ByteArray)
@ -70,6 +72,18 @@ internal class CallVideoCodec(requested: CallVideoFormat, val bitrate: Int, val
input.close(); codec.queueInputBuffer(index, 0, format.width * format.height * 3 / 2, pts, 0); return true
}
/** Вызывается перед очередным IDR; подтверждение ядру — только после проверки этого IDR. */
fun setBitrate(value: Int): Boolean {
if (value == bitrate) return true
return try {
codec.setParameters(Bundle().apply { putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, value) })
LogManager.addLog("INFO", "call", "video bitrate updated without restart name=$name old=$bitrate target=$value")
bitrate = value; true
} catch (e: Exception) {
LogManager.addLog("WARN", "call", "video live bitrate refused name=$name target=$value: ${e.message}"); false
}
}
fun drain(): List<Packet> {
val packets = mutableListOf<Packet>(); val info = MediaCodec.BufferInfo()
repeat(16) {

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

@ -64,6 +64,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
private var pendingRequest = 0L
private var pendingRate = 0
private var confirmingRequest = 0L
private var confirmingPts = -1L
private var realtime = false
private var sensorOffset: Long? = null
private var openedAt = 0L
@ -235,17 +236,22 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
if (key && pendingRequest != 0L) {
val requestId = pendingRequest; pendingRequest = 0
val old = checkNotNull(encoder); val oldRate = old.bitrate; val name = old.name
old.close(); encoder = null
try {
confirmingPts = pts
if (old.setBitrate(pendingRate)) {
confirmingRequest = requestId; needKey = true
} else {
old.close(); encoder = null
try {
encoder = CallVideoCodec(f, pendingRate, name)
encoder!!.prepare(); check(encoder!!.format == f) { "Bitrate change altered encoder format" }
confirmingRequest = requestId; needKey = true
report("Applying ${pendingRate / 1000} kbit/s on IDR")
} catch (e: Exception) {
} catch (e: Exception) {
LogManager.addLog("WARN", "call", "video bitrate replacement failed: ${e.message}; restoring $oldRate")
encoder?.close(); encoder = CallVideoCodec(f, oldRate, name)
encoder!!.prepare(); check(encoder!!.format == f)
confirmingRequest = requestId; needKey = true
}
}
}
if (encoder?.submit(image, pts, key) == true) lastPts = pts
@ -292,7 +298,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) {
for (p in codec.drain()) {
encodedBytes += p.bytes.size; encodedFrames++
if (p.key) lastKey = p.pts
if (confirmingRequest != 0L && p.key) {
if (confirmingRequest != 0L && p.key && p.pts >= confirmingPts) {
CallVideoNative.applied(id, f.generation, confirmingRequest, codec.bitrate); confirmingRequest = 0
mutable.value = mutable.value.copy(bitrate = codec.bitrate)
LogManager.addLog("INFO", "call", "video bitrate applied id=$id generation=${f.generation} bitrate=${codec.bitrate} pts=${p.pts}")

Loading…
Cancel
Save