From 32b608ed3c31ee2078289bcd2180f17067bf925c Mon Sep 17 00:00:00 2001 From: JohannesItten Date: Tue, 7 Jul 2026 19:09:48 +0300 Subject: [PATCH] DeckLinkSender green screen fix --- shared/DeckLinkSender.hpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/shared/DeckLinkSender.hpp b/shared/DeckLinkSender.hpp index ad2d37a..21ab7ca 100644 --- a/shared/DeckLinkSender.hpp +++ b/shared/DeckLinkSender.hpp @@ -44,7 +44,7 @@ public: ~DeckLinkSender() { if (decklink_output) { - decklink_output->StopScheduledPlayback(0, nullptr, 1); + if (playback_started_) decklink_output->StopScheduledPlayback(0, nullptr, 1); decklink_output->DisableVideoOutput(); if (has_audio) decklink_output->DisableAudioOutput(); decklink_output->SetScheduledFrameCompletionCallback(nullptr); @@ -107,33 +107,18 @@ public: r = decklink_output->RowBytesForPixelFormat(bmdFormat10BitYUV, width, &row_bytes); if (r != S_OK) throw std::runtime_error("Could not get row bytes for pixel format"); - // Pre-fill 3 black frames and schedule them to prime the pipeline. - // The card fires ScheduledFrameCompleted once each is done, at which point - // submit_frame() can reclaim and reschedule them with live video. + // Create frame pool. Frames start in free_frames; submit_frame() schedules + // them as live content arrives. StartScheduledPlayback is deferred until + // kPreroll frames have been scheduled so the card has a buffer to draw from. constexpr size_t kPreroll = 3; for (size_t i = 0; i < kPreroll; ++i) { IDeckLinkMutableVideoFrame* vf = nullptr; r = decklink_output->CreateVideoFrame( width, height, row_bytes, bmdFormat10BitYUV, bmdFrameFlagDefault, &vf); if (r != S_OK) throw std::runtime_error("Could not create video frame"); - - IDeckLinkVideoBuffer* buf = nullptr; - vf->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&buf); - buf->StartAccess(bmdBufferAccessWrite); - void* ptr = nullptr; - buf->GetBytes(&ptr); - if (ptr) dmf::v210::fill_black(static_cast(ptr), width, height, row_bytes); - buf->EndAccess(bmdBufferAccessWrite); - buf->Release(); - - decklink_output->ScheduleVideoFrame( - vf, static_cast(i * fps_den), fps_den, fps_num); all_frames.push_back(vf); + free_frames.push_back(vf); } - scheduled_time = static_cast(kPreroll) * fps_den; - - r = decklink_output->StartScheduledPlayback(0, fps_num, 1.0); - if (r != S_OK) throw std::runtime_error("Could not start scheduled playback"); } // Blocks until a frame slot is free (returned by the card via callback), @@ -169,6 +154,11 @@ public: const int64_t t = scheduled_time; scheduled_time += video_info.fps_den; decklink_output->ScheduleVideoFrame(vf, t, video_info.fps_den, video_info.fps_num); + + if (!playback_started_ && ++frames_scheduled_ >= kPreroll) { + playback_started_ = true; + decklink_output->StartScheduledPlayback(0, video_info.fps_num, 1.0); + } } // Converts float32 planar → interleaved int32 and pushes to DeckLink audio buffer. @@ -232,6 +222,8 @@ private: std::vector free_frames; // Scheduling state — only written from submit_frame/submit_audio (single-threaded caller) + bool playback_started_ = false; + size_t frames_scheduled_ = 0; int64_t scheduled_time = 0; // next video frame position (fps_num units) int64_t audio_stream_time = 0; // next audio batch position (sample units) std::vector audio_convert_buf; // reused across submit_audio calls