diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamController.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamController.kt index c59d95bd..2995a0a6 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamController.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamController.kt @@ -24,6 +24,12 @@ object StreamController { private val main = Handler(Looper.getMainLooper()) private val mutable = MutableStateFlow(null) val state: StateFlow = mutable + private val mutableTx = MutableStateFlow(null) + val txStats: StateFlow = mutableTx + fun updateTx(id: Long, data: LongArray) { + check(Looper.myLooper() == Looper.getMainLooper()) + if (mutable.value?.id == id && mutable.value?.ongoing == true) mutableTx.value = StreamTxStats.fromNative(id, data) + } @Volatile var capturing = false private set private var service: StreamService? = null @@ -38,6 +44,7 @@ object StreamController { mutable.value = StreamState(id, group, StreamPhase.FAILED, R.string.stream_resources_busy); return } capturing = true; mutable.value = StreamState(id, group, StreamPhase.STARTING, pendingConfig = config) + mutableTx.value = null try { ContextCompat.startForegroundService(ChatApplication.instance, Intent(ChatApplication.instance, StreamService::class.java) .putExtra("id", id).putExtra("group", group).putExtra("audio", audio)) @@ -99,6 +106,7 @@ object StreamController { val st = mutable.value ?: return if (st.id != id || !st.ongoing) return capturing = false + mutableTx.value = null mutable.value = st.copy(phase = if (st.error != null) StreamPhase.FAILED else StreamPhase.ENDED, pendingConfig = null) LogManager.addLog("INFO", "stream", "publisher released id=$id") } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamService.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamService.kt index a8c89ecd..e6512931 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamService.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamService.kt @@ -76,7 +76,13 @@ class StreamService : Service() { poll = scope.launch { val deadline = System.nanoTime() + 10_000_000_000 var opened = false + var nextStats = 0L while (true) { + val now = System.nanoTime() + if (now >= nextStats) { + nextStats = now + 100_000_000 + NativeLib.streamTxStats(id)?.let { StreamController.updateTx(id, it) } + } val event = NativeLib.streamPull(id) if (event != null) when (event[0].toInt()) { 4 -> { opened = true; StreamController.opened(id) } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamTxStats.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamTxStats.kt new file mode 100644 index 00000000..22a12164 --- /dev/null +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamTxStats.kt @@ -0,0 +1,13 @@ +package com.utun.chat.data + +/** Все поля в байтах; ACK — десять завершённых интервалов по 100ms, для всех соединений ядра. */ +data class StreamTxStats(val id: Long, val sampleUs: Long, val videoBytes: Long, val audioBytes: Long, + val streamBytes: Long, val transportBytes: Long, val inflightBytes: Long, + val acked1s: Long, val totalBytes: Long, val connections: Long) { + companion object { + fun fromNative(id: Long, data: LongArray): StreamTxStats { + require(data.size == 9 && data.all { it >= 0 }) + return StreamTxStats(id, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]) + } + } +}