Keep stream catalogue labels current and preserve viewer errors

This commit is contained in:
evgeny committed 2026-10-29 13:34:25 +01:00
1 parent 6f0bc4307f
commit b3f5c49c4d
3 files changed
+10 -6

No files matched your search

@@ -36,7 +36,7 @@ import java.text.NumberFormat
internal enum class CaptureIconShape { CAMERA, CHIP, GAUGE, SCREEN }
/** Короткое локализованное значение битрейта для шкалы в записи и трансляции. */
/** Положительный bitrate в бит/с отображается в десятичных k/M; дробь до 10 M и разделитель берутся из locale. */
@Composable
internal fun captureBitrateLabel(bitrate: Int): String {
val mega = bitrate >= 1_000_000
@@ -157,7 +157,8 @@ internal fun CaptureOptionItem(label: String, active: Boolean, onClick: () -> Un
trailingIcon = { if (active) Icon(Icons.Filled.Check, contentDescription = null) }, modifier = Modifier.semantics { selected = active })
}
/** Общий выбор ориентации фото, видеосообщений и трансляции; маркер показывает верх записываемого кадра. */
/** Общий выбор AUTO/0/90/270° для фото, видеосообщений и трансляции; маркер показывает верх записываемого кадра.
* displayDegrees — 0/90/180/270°; front меняет направление компенсации. Изменение применяет вызывающий владелец. */
@Composable
internal fun CaptureOrientationControl(orientation: StreamOrientation?, displayDegrees: Int, front: Boolean,
enabled: Boolean, expanded: Boolean, properties: PopupProperties,
@@ -40,7 +40,8 @@ import com.utun.chat.data.streamGridShape
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
private class GridStream(val source: StreamSource) {
private class GridStream(source: StreamSource) {
var source by mutableStateOf(source)
var status by mutableStateOf(R.string.stream_connecting)
var attempt by mutableStateOf(0)
var removeAt: Long? = null
@@ -84,7 +85,7 @@ internal fun StreamGridScreen(group: Long, title: String, sources: List<StreamSo
val current = sources.associateBy { it.id }
tiles.removeAll { tile -> current[tile.source.id]?.let { it.epoch != tile.source.epoch } == true }
for (tile in tiles) {
if (tile.source.id in current) tile.removeAt = null
if (tile.source.id in current) { tile.source = current.getValue(tile.source.id); tile.removeAt = null }
else if (tile.removeAt == null) {
tile.removeAt = now + if (tile.source.self) 0 else (tile.engine?.drainTimeoutMs ?: 2000)
LogManager.addLog("INFO", "stream", "multiview drains source=${tile.source.id} deadline=${tile.removeAt}")
@@ -130,8 +130,10 @@ internal fun StreamViewerPlayback(group: Long, source: Long, epoch: Long, onAspe
modifier: Modifier, onEngine: (StreamPlayerEngine) -> Unit = {},
onTelemetry: (StreamPlaybackTelemetry) -> Unit = {}) {
var engine by remember(group, source, epoch) { mutableStateOf<StreamPlayerEngine?>(null) }
var startupError by remember(group, source, epoch) { mutableStateOf(false) }
val aspect = engine?.aspect?.collectAsState()?.value ?: 0f
val status = engine?.status?.collectAsState()?.value ?: R.string.stream_connecting
val engineStatus = engine?.status?.collectAsState()?.value ?: R.string.stream_connecting
val status = if (startupError) R.string.stream_failed else engineStatus
val telemetry = engine?.telemetry?.collectAsState()?.value ?: StreamPlaybackTelemetry()
val resources = remember(group, source, epoch) { StreamViewerSurface() }
fun releaseSurface(texture: SurfaceTexture? = null) {
@@ -161,7 +163,7 @@ internal fun StreamViewerPlayback(group: Long, source: Long, epoch: Long, onAspe
engine = player
try { onEngine(player); player.start(target.surface) } catch (e: Exception) {
LogManager.addLog("ERROR", "stream", "viewer UI start failed source=$source epoch=$epoch: $e")
releaseSurface(); onStatus(R.string.stream_failed)
startupError = true; releaseSurface()
}
}
override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) {