|
|
|
|
@ -20,7 +20,8 @@ import kotlin.math.roundToInt
|
|
|
|
|
/** Один worker владеет декодерами и AudioTrack. Hardware audio clock управляет временем показа видео. |
|
|
|
|
* Просроченные P-кадры декодируются без показа, чтобы сохранить references; при переполнении ждём KEY. |
|
|
|
|
* viewerLock сериализует stop/open разных экранов до подтверждения освобождения native owner. */ |
|
|
|
|
class StreamPlayerEngine(private val group: Long, private val source: Long, private val muteAudio: Boolean = false) { |
|
|
|
|
class StreamPlayerEngine(private val group: Long, private val source: Long, private val epoch: Long, |
|
|
|
|
private val muteAudio: Boolean = false) { |
|
|
|
|
companion object { private val viewerLock = ReentrantLock() } |
|
|
|
|
private data class Frame(val pts: Long, val key: Boolean, val width: Int, val height: Int, val rotation: Int, val data: ByteArray) |
|
|
|
|
private val mutableAspect = MutableStateFlow(16f / 9f) |
|
|
|
|
@ -120,7 +121,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
check(NativeLib.streamOpen(id, group, source)) { "Viewer open refused" } |
|
|
|
|
check(NativeLib.streamOpen(id, group, source, epoch = epoch)) { "Viewer open refused" } |
|
|
|
|
val openDeadline = System.nanoTime() + 10_000_000_000 |
|
|
|
|
var opened = false |
|
|
|
|
var prefillUntil = 0L |
|
|
|
|
@ -131,7 +132,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
|
|
|
|
if (raw != null) { |
|
|
|
|
check(raw.size >= 20) |
|
|
|
|
val b = ByteBuffer.wrap(raw).order(ByteOrder.BIG_ENDIAN) |
|
|
|
|
val kind = b.get().toInt(); val key = b.get().toInt() and 1 != 0 |
|
|
|
|
val kind = b.get().toInt(); val flags = b.get().toInt() and 255; val key = flags and 1 != 0 |
|
|
|
|
val width = b.short.toInt() and 65535; val height = b.short.toInt() and 65535 |
|
|
|
|
val rotation = b.short.toInt() and 65535; val pts = b.long; val size = b.int |
|
|
|
|
check(size == b.remaining()) |
|
|
|
|
@ -161,7 +162,17 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
|
|
|
|
3 -> { LogManager.addLog("INFO", "stream", "viewer GAP id=$id"); resetVideo(); resetAudio(); videoBase = -1; prefillUntil = 0 } |
|
|
|
|
4 -> opened = true |
|
|
|
|
5 -> ended = true |
|
|
|
|
6 -> error("Native viewer closed") |
|
|
|
|
6 -> { |
|
|
|
|
mutableStatus.value = when (flags) { |
|
|
|
|
1 -> R.string.stream_group_closed |
|
|
|
|
2 -> R.string.chat_service_stopped |
|
|
|
|
3 -> R.string.stream_source_changed |
|
|
|
|
4 -> R.string.stream_member_removed |
|
|
|
|
else -> R.string.stream_failed |
|
|
|
|
} |
|
|
|
|
LogManager.addLog("WARN", "stream", "native viewer closed id=$id group=$group source=$source epoch=$epoch reason=$flags") |
|
|
|
|
ended = true; running = false |
|
|
|
|
} |
|
|
|
|
else -> error("Unknown media event=$kind") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|