From ac339bb61f5f9507567da1831c5c3db0a7fef8be Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 13:27:10 +0200 Subject: [PATCH] Prepare one desktop media response per player request --- tools/chatgui/transport/stream_bridge.cpp | 33 ++++++++++++----------- tools/chatgui/transport/stream_bridge.h | 4 +-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/tools/chatgui/transport/stream_bridge.cpp b/tools/chatgui/transport/stream_bridge.cpp index 22f0f00d..24346786 100644 --- a/tools/chatgui/transport/stream_bridge.cpp +++ b/tools/chatgui/transport/stream_bridge.cpp @@ -21,7 +21,8 @@ struct Bridge { uint64_t watch = 0; bool watchPending = false, opening = false, stopping = false; bool wantRead = false, waiting = false, resync = false; - int64_t position = -1, videoAhead = STREAM_MEDIA_READ_AHEAD_US, audioAhead = STREAM_MEDIA_READ_AHEAD_US; + bool wantVideo = false; + int64_t position = -1, audioAhead = STREAM_MEDIA_READ_AHEAD_US; stream_media_status status{}; uint64_t statusId = 0; Viewer viewer, requested; @@ -93,7 +94,7 @@ void mediaEvent(UTUN_INSTANCE* inst, const stream_event* event, void*) { || event->descriptor.source_node_id != bridge.viewer.source || event->descriptor.source_epoch != bridge.viewer.epoch) return; if (event->type == STREAM_EVENT_CLOSED || !stream_handle_active(bridge.handle)) closeViewer(StreamRecordKind::Closed, event->reason); - else if (event->type == STREAM_EVENT_AVAILABLE) { bridge.wantRead = true; bridge.waiting = false; schedule(); } + else if (event->type == STREAM_EVENT_AVAILABLE) { bridge.waiting = false; schedule(); } } /* Сериализованный actor: старый owner закрывается перед открытием нового выбранного источника. */ @@ -140,7 +141,7 @@ void process(void*) { 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; + bridge.wantRead = false; } if (!bridge.handle) return; if (bridge.resync) { @@ -152,23 +153,21 @@ void process(void*) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop media index failed result=%d", statusResult); closeViewer(StreamRecordKind::Closed, STREAM_CLOSE_FAILED); return; } - unsigned prepared = 0; - for (const auto& record : bridge.output) - if (record.id == bridge.viewer.id && (record.kind == StreamRecordKind::Video || record.kind == StreamRecordKind::Audio)) prepared++; - if (!bridge.wantRead || prepared >= 2 || bridge.bytes + STREAM_MEDIA_MAX_FRAME > 4u * 1024u * 1024u) return; + bool prepared = std::any_of(bridge.output.begin(), bridge.output.end(), [](const StreamRecord& r) { return r.id == bridge.viewer.id; }); + if (!bridge.wantRead || prepared || bridge.bytes + STREAM_MEDIA_MAX_FRAME > 4u * 1024u * 1024u) return; bridge.wantRead = false; stream_media_packet packet{}; - int result = stream_media_read(bridge.handle, bridge.position, bridge.videoAhead, bridge.audioAhead, &packet); - DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "desktop media read id=%llu PTS=%lld ahead=%lld/%lld result=%d kind=%d prepared=%u records=%zu", - (unsigned long long)bridge.viewer.id, (long long)bridge.position, (long long)bridge.videoAhead, - (long long)bridge.audioAhead, result, result == STREAM_OK ? (int)packet.kind : -1, prepared, bridge.output.size()); + int result = stream_media_read(bridge.handle, bridge.position, bridge.wantVideo, bridge.audioAhead, &packet); + DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "desktop media read id=%llu PTS=%lld video=%d audio_ahead=%lld result=%d kind=%d records=%zu", + (unsigned long long)bridge.viewer.id, (long long)bridge.position, bridge.wantVideo, + (long long)bridge.audioAhead, result, result == STREAM_OK ? (int)packet.kind : -1, bridge.output.size()); if (stream_media_get_status(bridge.handle, &bridge.status) < 0) result = STREAM_ERR_FORMAT; if (result == STREAM_OK) { if (packet.kind == STREAM_MEDIA_READ_END) closeViewer(StreamRecordKind::End, 0); else mediaFrame(nullptr, &packet.format, packet.kind == STREAM_MEDIA_READ_VIDEO ? &packet.video : nullptr, packet.kind == STREAM_MEDIA_READ_AUDIO ? &packet.audio : nullptr); } else if (result == STREAM_MORE) { bridge.wantRead = true; schedule(); } - else if (result == STREAM_WAIT) bridge.waiting = true; + else if (result == STREAM_WAIT) bridge.wantRead = bridge.waiting = true; else if (result == STREAM_GAP || result == STREAM_EXPIRED) { DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop playback resync id=%llu result=%d", (unsigned long long)bridge.viewer.id, result); stream_media_resync(bridge.handle); bridge.position = -1; @@ -240,14 +239,16 @@ void gui_stream_stop(uint64_t id) { } } -bool gui_stream_pull(uint64_t id, StreamRecord& record, int64_t position, int64_t videoAhead, int64_t audioAhead) { +bool gui_stream_pull(uint64_t id, StreamRecord& record, int64_t position, bool wantVideo, int64_t audioAhead) { std::lock_guard guard(bridge.lock); auto i = std::find_if(bridge.output.begin(), bridge.output.end(), [id](const StreamRecord& r) { return r.id == id; }); bool found = i != bridge.output.end(); if (found) { bridge.bytes -= static_cast(i->data.size()); record = std::move(*i); bridge.output.erase(i); } - if (bridge.viewer.id == id && bridge.handle && (!bridge.waiting || bridge.position != position - || bridge.videoAhead != videoAhead || bridge.audioAhead != audioAhead || found)) { - bridge.position = position; bridge.videoAhead = videoAhead; bridge.audioAhead = audioAhead; bridge.wantRead = true; schedule(); + if (!found && bridge.viewer.id == id && bridge.handle) { + bool changed = bridge.position != position || bridge.wantVideo != wantVideo || bridge.audioAhead != audioAhead; + bridge.position = position; bridge.wantVideo = wantVideo; bridge.audioAhead = audioAhead; + bridge.wantRead = wantVideo || audioAhead >= 0; + if (bridge.wantRead && (!bridge.waiting || changed)) { bridge.waiting = false; schedule(); } } return found; } diff --git a/tools/chatgui/transport/stream_bridge.h b/tools/chatgui/transport/stream_bridge.h index c1805a8a..d88e37eb 100644 --- a/tools/chatgui/transport/stream_bridge.h +++ b/tools/chatgui/transport/stream_bridge.h @@ -1,6 +1,6 @@ /* Desktop live media: attach/detach в uasync до destroy ядра; остальные вызовы потокобезопасны. * Один общий publisher и один viewer; команды содержат IDs, не borrowed pointers ядра. - * Подготовлено не более двух media records, общий предел RX 4MiB/16 записей. + * Один ответ на запрос плеера; следующий кадр не читается автоматически. Общий предел RX 4MiB/16 записей. * Watch до attach запоминает выбранную группу. Каталог идёт через CHAT_EVT_STREAM_SOURCES, * media pull читает ближайшие кадры из общего RS-кольца; копия нужна только для передачи между потоками. */ #pragma once @@ -27,7 +27,7 @@ bool gui_stream_watch(uint64_t group); bool gui_stream_open(uint64_t id, uint64_t group, uint64_t source, uint64_t epoch); void gui_stream_stop(uint64_t id); bool gui_stream_pull(uint64_t id, StreamRecord& record, int64_t position = -1, - int64_t videoAhead = STREAM_MEDIA_READ_AHEAD_US, int64_t audioAhead = STREAM_MEDIA_READ_AHEAD_US); + bool wantVideo = true, int64_t audioAhead = STREAM_MEDIA_READ_AHEAD_US); bool gui_stream_status(uint64_t id, stream_media_status& status); bool gui_stream_resync(uint64_t id); bool gui_stream_ready();