Browse Source

Mute local preview and clear Android media queues at detach

master
evgeny 1 week ago
parent
commit
c2c202e716
  1. 3
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt
  2. 11
      tools/chatgui-android/libutun_lite/stream_bridge.c

3
tools/chatgui-android/app/src/main/java/com/utun/chat/data/StreamPlayerEngine.kt

@ -20,7 +20,7 @@ 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) {
class StreamPlayerEngine(private val group: Long, private val source: 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)
@ -116,6 +116,7 @@ class StreamPlayerEngine(private val group: Long, private val source: Long) {
videos.addLast(frame); videoBytes += size
}
} else {
if (muteAudio) return@repeat
if (audios.size >= 16) {
LogManager.addLog("WARN", "stream", "audio queue overflow id=$id; reset timeline")
resetAudio(); resetVideo()

11
tools/chatgui-android/libutun_lite/stream_bridge.c

@ -151,7 +151,7 @@ static void media_event(struct UTUN_INSTANCE *inst, const struct stream_event *e
/* Publisher держит конечное окно выдачи после FIN; abrupt detach ничего не обещает получателям. */
static void media_close(struct android_media_session *s, int kind) {
media_output(s->id, kind, NULL, 0, 0, NULL, 0);
if (kind) media_output(s->id, kind, NULL, 0, 0, NULL, 0);
if (s->handle) stream_close(s->handle);
stream_media_receiver_destroy(s->receiver);
if (s == &publisher) { queue_clear(&video_queue); queue_clear(&audio_queue); }
@ -217,8 +217,8 @@ static void media_list_sources(void) {
struct stream_member_state state;
if (stream_member_load(media_inst, group_id, node, &state, NULL) != STREAM_OK || !state.published) continue;
if (count >= 256) { DEBUG_WARN(DEBUG_CATEGORY_STREAM, "Android stream source list truncated group=%llx", (unsigned long long)group_id); break; }
if (strbuf_addf(&json, "%s{\"id\":\"%llu\",\"epoch\":\"%016llx\",\"name\":", count ? "," : "",
(unsigned long long)node, (unsigned long long)state.source.source_epoch) < 0
if (strbuf_addf(&json, "%s{\"id\":\"%llu\",\"epoch\":\"%016llx\",\"self\":%d,\"name\":", count ? "," : "",
(unsigned long long)node, (unsigned long long)state.source.source_epoch, node == media_inst->node_id) < 0
|| strbuf_add_json_string(&json, (const char *)sqlite3_column_text(statement, 1)) < 0
|| strbuf_addf(&json, "}") < 0) { failed = 1; break; }
count++;
@ -316,8 +316,9 @@ void android_stream_detach(struct UTUN_INSTANCE *inst) {
if (media_timer) uasync_cancel_timeout(inst->ua, media_timer);
media_post = NULL; media_timer = NULL;
stream_set_event_callback(inst, NULL, NULL);
if (publisher.id) media_close(&publisher, 6);
if (viewer.id) media_close(&viewer, 6);
queue_clear(&output_queue);
if (publisher.id) media_close(&publisher, 0);
if (viewer.id) media_close(&viewer, 0);
media_inst = NULL;
u_free(source_list); source_list = NULL; list_group = cached_group = list_updated = 0;
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "Android stream bridge detached; producers rejected");

Loading…
Cancel
Save