From 8dfeeacecd2900e7cbd97df7ecb9a09bca340b5a Mon Sep 17 00:00:00 2001 From: JohannesItten Date: Tue, 7 Jul 2026 19:50:09 +0300 Subject: [PATCH] fix ndi->decklinkout slowdown --- nodes/decklinkout/main.cpp | 10 ++++++++-- nodes/ndiin/main.cpp | 13 +++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/nodes/decklinkout/main.cpp b/nodes/decklinkout/main.cpp index 7e02987..e70e629 100644 --- a/nodes/decklinkout/main.cpp +++ b/nodes/decklinkout/main.cpp @@ -114,8 +114,9 @@ class DeckLinkOutNode : public dmf::NodeBase { uint64_t frame_count = 0; uint64_t invalid_count = 0; uint64_t late_count = 0; - auto wall_start = std::chrono::steady_clock::now(); - auto last_log_time = wall_start; + std::chrono::steady_clock::time_point wall_start; + bool timing_started = false; + auto last_log_time = std::chrono::steady_clock::now(); // --- Main loop --- while (dmf::g_running.load(std::memory_order_relaxed)) { @@ -169,6 +170,11 @@ class DeckLinkOutNode : public dmf::NodeBase { video_reader, video_index, &video_grain, &video_buf); if (vst == MXL_STATUS_OK) { + if (!timing_started) { + wall_start = std::chrono::steady_clock::now(); + last_log_time = wall_start; + timing_started = true; + } if (video_grain.flags & MXL_GRAIN_FLAG_INVALID) invalid_count++; sender.submit_frame(video_buf, video_stride); frame_count++; diff --git a/nodes/ndiin/main.cpp b/nodes/ndiin/main.cpp index 8a0138b..71196bf 100644 --- a/nodes/ndiin/main.cpp +++ b/nodes/ndiin/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -96,19 +97,23 @@ class NDIInNode : public dmf::NodeBase { dmf::NDIReceiver::AudioInfo audio_info; while (dmf::g_running.load(std::memory_order_relaxed)) { - // Drain all queued NDI frames before writing one MXL grain. + // Drain available NDI frames before writing one MXL grain. // With audio enabled, many audio packets may be queued ahead of a video frame. - // Calling capture() once per grain window would starve video reception. - // Strategy: blocking first call (5ms), then non-blocking drain of remainder. + // Budget: drain for at most half a frame period so the grain clock stays on time + // even when catching up from a startup audio backlog. try { + const auto drain_deadline = + std::chrono::steady_clock::now() + std::chrono::milliseconds(20); bool first = true; - while (dmf::g_running.load(std::memory_order_relaxed)) { + while (dmf::g_running.load(std::memory_order_relaxed) && + std::chrono::steady_clock::now() < drain_deadline) { auto kind = ndi.capture(latest_video.data(), video_stride, audio_buf, audio_info, has_audio, first ? 5 : 0); first = false; if (kind == dmf::NDIReceiver::FrameKind::Video) { have_video = true; + break; // got video — write grain now, remaining audio deferred to next window } else if (kind == dmf::NDIReceiver::FrameKind::Audio && has_audio) { mxlMutableWrappedMultiBufferSlice slice{}; if (mxlFlowWriterOpenSamples(audio_writer, audio_index,