|
|
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.delay
|
|
|
|
|
import kotlinx.coroutines.withContext |
|
|
|
|
import org.json.JSONArray |
|
|
|
|
|
|
|
|
|
private data class LiveSource(val id: Long, val name: String, val epoch: String) |
|
|
|
|
private data class LiveSource(val id: Long, val name: String, val epoch: String, val self: Boolean) |
|
|
|
|
|
|
|
|
|
/** Выход с экрана закрывает только reader; foreground publisher останавливает явная кнопка/notification. */ |
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class) |
|
|
|
|
@ -43,6 +43,7 @@ fun StreamScreen(channelId: String, channelName: String, onBack: () -> Unit) {
|
|
|
|
|
val local = state?.takeIf { it.group == group } |
|
|
|
|
var sources by remember(group) { mutableStateOf<List<LiveSource>>(emptyList()) } |
|
|
|
|
var selected by remember(group) { mutableStateOf<LiveSource?>(null) } |
|
|
|
|
var previewedSession by remember(group) { mutableStateOf<Long?>(null) } |
|
|
|
|
var audio by remember { mutableStateOf(true) } |
|
|
|
|
var camera by remember { mutableStateOf("") } |
|
|
|
|
val cameras = remember { |
|
|
|
|
@ -64,15 +65,17 @@ fun StreamScreen(channelId: String, channelName: String, onBack: () -> Unit) {
|
|
|
|
|
val json = JSONArray(NativeLib.streamSources(group)) |
|
|
|
|
(0 until json.length()).map { i -> |
|
|
|
|
val member = json.getJSONObject(i) |
|
|
|
|
LiveSource(member.getString("id").toULong().toLong(), member.getString("name"), member.getString("epoch")) |
|
|
|
|
LiveSource(member.getString("id").toULong().toLong(), member.getString("name"), member.getString("epoch"), member.optInt("self") == 1) |
|
|
|
|
} |
|
|
|
|
} catch (e: Exception) { LogManager.addLog("WARN", "stream", "stream list: ${e.message}"); emptyList() } |
|
|
|
|
} else sources = emptyList() |
|
|
|
|
delay(1000) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
LaunchedEffect(local?.id, local?.phase) { |
|
|
|
|
if (local?.phase == StreamPhase.LIVE) selected = LiveSource(NativeLib.getMyNodeId(), channelName, local.id.toString()) |
|
|
|
|
LaunchedEffect(sources, local?.id, local?.phase) { |
|
|
|
|
if (local?.phase == StreamPhase.LIVE && previewedSession != local.id) sources.firstOrNull { it.self }?.let { |
|
|
|
|
selected = it; previewedSession = local.id |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Scaffold(topBar = { |
|
|
|
|
@ -112,7 +115,7 @@ fun StreamScreen(channelId: String, channelName: String, onBack: () -> Unit) {
|
|
|
|
|
TextButton(onClick = { selected = source }) { Text(stringResource(R.string.stream_watch, source.name.ifEmpty { source.id.toULong().toString() })) } |
|
|
|
|
} |
|
|
|
|
selected?.let { source -> |
|
|
|
|
key(group, source.id, source.epoch) { LiveStreamView(group, source.id) } |
|
|
|
|
key(group, source.id, source.epoch) { LiveStreamView(group, source.id, source.self) } |
|
|
|
|
TextButton(onClick = { selected = null }) { Text(stringResource(R.string.stream_close_view)) } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -120,8 +123,8 @@ fun StreamScreen(channelId: String, channelName: String, onBack: () -> Unit) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Composable |
|
|
|
|
private fun LiveStreamView(group: Long, source: Long) { |
|
|
|
|
val engine = remember(group, source) { StreamPlayerEngine(group, source) } |
|
|
|
|
private fun LiveStreamView(group: Long, source: Long, localPreview: Boolean) { |
|
|
|
|
val engine = remember(group, source, localPreview) { StreamPlayerEngine(group, source, localPreview) } |
|
|
|
|
val aspect by engine.aspect.collectAsState() |
|
|
|
|
val status by engine.status.collectAsState() |
|
|
|
|
var surface by remember { mutableStateOf<Surface?>(null) } |
|
|
|
|
|