Browse Source

Prepare one desktop media response per player request

master
evgeny 5 days ago
parent
commit
ac339bb61f
  1. 33
      tools/chatgui/transport/stream_bridge.cpp
  2. 4
      tools/chatgui/transport/stream_bridge.h

33
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<std::mutex> 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<size_t>(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;
}

4
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();

Loading…
Cancel
Save