From c0c4310f08fecc8bd4b21b69e65b65d5021b9847 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 00:06:23 +0200 Subject: [PATCH] Clarify desktop demand reader ownership and preparation bounds --- tools/chatgui/src/streamplayer_window.h | 2 +- tools/chatgui/transport/stream_bridge.cpp | 58 +++++++++++------------ tools/chatgui/transport/stream_bridge.h | 4 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tools/chatgui/src/streamplayer_window.h b/tools/chatgui/src/streamplayer_window.h index ea4b9ed4..033d4281 100644 --- a/tools/chatgui/src/streamplayer_window.h +++ b/tools/chatgui/src/streamplayer_window.h @@ -1,4 +1,4 @@ -/* Просмотр одной live-трансляции: FFmpeg/Opus в worker, ограниченные очереди PCM/кадров. +/* Просмотр одной live-трансляции: запас остаётся в RS-кольце, worker готовит ближайшие FFmpeg/Opus кадры. * GUI владеет output device и окном; audio callback публикует часы физического воспроизведения. * Закрытие останавливает worker и снимает reader. Публикация и общий транспорт не закрываются. */ #pragma once diff --git a/tools/chatgui/transport/stream_bridge.cpp b/tools/chatgui/transport/stream_bridge.cpp index 998e3f83..2b7d9aef 100644 --- a/tools/chatgui/transport/stream_bridge.cpp +++ b/tools/chatgui/transport/stream_bridge.cpp @@ -103,36 +103,36 @@ void process(void*) { bridge.stopping = false; } if (bridge.opening) { - bridge.opening = false; bridge.viewer = bridge.requested; bridge.requested = {}; - bridge.status = {}; bridge.statusId = bridge.viewer.id; bridge.position = -1; bridge.waiting = false; - stream_member_state member{}; - int reason = STREAM_CLOSE_FAILED; - int result = stream_member_load(bridge.inst, bridge.viewer.group, bridge.viewer.source, &member, nullptr); - if (result == STREAM_OK && (!member.published || member.source.source_epoch != bridge.viewer.epoch)) { - DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop selected source changed source=%llx expected=%llx actual=%llx", - (unsigned long long)bridge.viewer.source, (unsigned long long)bridge.viewer.epoch, - (unsigned long long)(member.published ? member.source.source_epoch : 0)); - result = STREAM_ERR_CONFLICT; reason = STREAM_CLOSE_SOURCE_CHANGED; - } - if (result == STREAM_OK) result = stream_subscribe_open(bridge.inst, bridge.viewer.group, bridge.viewer.source, &bridge.handle); - stream_info info{}; - if (result == STREAM_OK) result = stream_get_info(bridge.handle, &info); - if (result == STREAM_OK && info.descriptor.source_epoch != bridge.viewer.epoch) { - DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop reader encountered stale local epoch"); - result = STREAM_ERR_CONFLICT; reason = STREAM_CLOSE_SOURCE_CHANGED; - } - if (result == STREAM_OK) result = stream_media_enable(bridge.handle, STREAM_MEDIA_TRACK_VIDEO | STREAM_MEDIA_TRACK_AUDIO); - if (result != STREAM_OK) { - DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop viewer open failed id=%llu group=%llx source=%llx result=%d", + bridge.opening = false; bridge.viewer = bridge.requested; bridge.requested = {}; + bridge.status = {}; bridge.statusId = bridge.viewer.id; bridge.position = -1; bridge.waiting = false; + stream_member_state member{}; + int reason = STREAM_CLOSE_FAILED; + int result = stream_member_load(bridge.inst, bridge.viewer.group, bridge.viewer.source, &member, nullptr); + if (result == STREAM_OK && (!member.published || member.source.source_epoch != bridge.viewer.epoch)) { + DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop selected source changed source=%llx expected=%llx actual=%llx", + (unsigned long long)bridge.viewer.source, (unsigned long long)bridge.viewer.epoch, + (unsigned long long)(member.published ? member.source.source_epoch : 0)); + result = STREAM_ERR_CONFLICT; reason = STREAM_CLOSE_SOURCE_CHANGED; + } + if (result == STREAM_OK) result = stream_subscribe_open(bridge.inst, bridge.viewer.group, bridge.viewer.source, &bridge.handle); + stream_info info{}; + if (result == STREAM_OK) result = stream_get_info(bridge.handle, &info); + if (result == STREAM_OK && info.descriptor.source_epoch != bridge.viewer.epoch) { + DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop reader encountered stale local epoch"); + result = STREAM_ERR_CONFLICT; reason = STREAM_CLOSE_SOURCE_CHANGED; + } + if (result == STREAM_OK) result = stream_media_enable(bridge.handle, STREAM_MEDIA_TRACK_VIDEO | STREAM_MEDIA_TRACK_AUDIO); + if (result != STREAM_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop viewer open failed id=%llu group=%llx source=%llx result=%d", + (unsigned long long)bridge.viewer.id, (unsigned long long)bridge.viewer.group, + (unsigned long long)bridge.viewer.source, result); + closeViewer(StreamRecordKind::Closed, reason); return; + } + output({bridge.viewer.id, 0, StreamRecordKind::Opened}); + DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop viewer opened id=%llu group=%llx source=%llx epoch=%llx", (unsigned long long)bridge.viewer.id, (unsigned long long)bridge.viewer.group, - (unsigned long long)bridge.viewer.source, result); - closeViewer(StreamRecordKind::Closed, reason); return; - } - output({bridge.viewer.id, 0, StreamRecordKind::Opened}); - DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop viewer opened id=%llu group=%llx source=%llx epoch=%llx", - (unsigned long long)bridge.viewer.id, (unsigned long long)bridge.viewer.group, - (unsigned long long)bridge.viewer.source, (unsigned long long)bridge.viewer.epoch); - bridge.wantRead = true; + (unsigned long long)bridge.viewer.source, (unsigned long long)bridge.viewer.epoch); + bridge.wantRead = true; } if (!bridge.handle) return; if (bridge.resync) { diff --git a/tools/chatgui/transport/stream_bridge.h b/tools/chatgui/transport/stream_bridge.h index 7f0ded42..69bd1115 100644 --- a/tools/chatgui/transport/stream_bridge.h +++ b/tools/chatgui/transport/stream_bridge.h @@ -1,7 +1,7 @@ /* Desktop live viewer: attach/detach в uasync до destroy ядра; остальные вызовы потокобезопасны. - * Один viewer, команды содержат IDs; RX ограничен 4MB/256 записей, borrowed данные копируются. + * Один viewer, команды содержат IDs; подготовлено не более двух media records, общий предел RX 4MiB/16 записей. * Watch до attach запоминает выбранную группу. Каталог идёт через CHAT_EVT_STREAM_SOURCES, - * медиаданные — через отдельную очередь pull. */ + * media pull читает ближайшие кадры из общего RS-кольца; копия нужна только для передачи между потоками. */ #pragma once #include