DeckLinkSender green screen fix

This commit is contained in:
JohannesItten
2026-07-07 19:09:48 +03:00
parent 0b6e9ce46a
commit 32b608ed3c
+12 -20
View File
@@ -44,7 +44,7 @@ public:
~DeckLinkSender() { ~DeckLinkSender() {
if (decklink_output) { if (decklink_output) {
decklink_output->StopScheduledPlayback(0, nullptr, 1); if (playback_started_) decklink_output->StopScheduledPlayback(0, nullptr, 1);
decklink_output->DisableVideoOutput(); decklink_output->DisableVideoOutput();
if (has_audio) decklink_output->DisableAudioOutput(); if (has_audio) decklink_output->DisableAudioOutput();
decklink_output->SetScheduledFrameCompletionCallback(nullptr); decklink_output->SetScheduledFrameCompletionCallback(nullptr);
@@ -107,33 +107,18 @@ public:
r = decklink_output->RowBytesForPixelFormat(bmdFormat10BitYUV, width, &row_bytes); r = decklink_output->RowBytesForPixelFormat(bmdFormat10BitYUV, width, &row_bytes);
if (r != S_OK) throw std::runtime_error("Could not get row bytes for pixel format"); 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. // Create frame pool. Frames start in free_frames; submit_frame() schedules
// The card fires ScheduledFrameCompleted once each is done, at which point // them as live content arrives. StartScheduledPlayback is deferred until
// submit_frame() can reclaim and reschedule them with live video. // kPreroll frames have been scheduled so the card has a buffer to draw from.
constexpr size_t kPreroll = 3; constexpr size_t kPreroll = 3;
for (size_t i = 0; i < kPreroll; ++i) { for (size_t i = 0; i < kPreroll; ++i) {
IDeckLinkMutableVideoFrame* vf = nullptr; IDeckLinkMutableVideoFrame* vf = nullptr;
r = decklink_output->CreateVideoFrame( r = decklink_output->CreateVideoFrame(
width, height, row_bytes, bmdFormat10BitYUV, bmdFrameFlagDefault, &vf); width, height, row_bytes, bmdFormat10BitYUV, bmdFrameFlagDefault, &vf);
if (r != S_OK) throw std::runtime_error("Could not create video frame"); 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<uint8_t*>(ptr), width, height, row_bytes);
buf->EndAccess(bmdBufferAccessWrite);
buf->Release();
decklink_output->ScheduleVideoFrame(
vf, static_cast<BMDTimeValue>(i * fps_den), fps_den, fps_num);
all_frames.push_back(vf); all_frames.push_back(vf);
free_frames.push_back(vf);
} }
scheduled_time = static_cast<int64_t>(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), // Blocks until a frame slot is free (returned by the card via callback),
@@ -169,6 +154,11 @@ public:
const int64_t t = scheduled_time; const int64_t t = scheduled_time;
scheduled_time += video_info.fps_den; scheduled_time += video_info.fps_den;
decklink_output->ScheduleVideoFrame(vf, t, video_info.fps_den, video_info.fps_num); 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. // Converts float32 planar → interleaved int32 and pushes to DeckLink audio buffer.
@@ -232,6 +222,8 @@ private:
std::vector<IDeckLinkMutableVideoFrame*> free_frames; std::vector<IDeckLinkMutableVideoFrame*> free_frames;
// Scheduling state — only written from submit_frame/submit_audio (single-threaded caller) // 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 scheduled_time = 0; // next video frame position (fps_num units)
int64_t audio_stream_time = 0; // next audio batch position (sample units) int64_t audio_stream_time = 0; // next audio batch position (sample units)
std::vector<int32_t> audio_convert_buf; // reused across submit_audio calls std::vector<int32_t> audio_convert_buf; // reused across submit_audio calls