|
|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
|