From 640271384e09ba182e09ea74c4d27e429840328b Mon Sep 17 00:00:00 2001 From: JohannesItten Date: Mon, 6 Jul 2026 01:34:01 +0300 Subject: [PATCH] refactor: DeckLinkReceiver cleanup and encapsulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeckLinkReceiver: - Make InputCallback a private nested class — no longer exposed publicly - DeckLinkReceiver owns all shared state (mutex, CVs, frame buffer) - Replace get_input_callback() with clean wait_for_frame() API - Fix device list leak: enumerate_devices stores raw IDeckLink* in raw_devices; destructor releases all of them + selected_device's AddRef - Remove dead members: device_config, device_status, deckLink_notification - Remove dead SourceInfo::stride field; rename SourceInfo → VideoInfo - Remove dead video_source_info public member - Remove printf; use no logging in receiver (caller logs) - Remove commented-out notification code - Fix dead return false after throw in enumerate_devices - Fix dead null check after new InputCallback - Replace IDeckLinkVideoBuffer QueryInterface with simpler GetBytes() - Replace plain bool frame_ready/format_detected with consistent usage under mutex (no longer mixing atomic + CV pattern) - Call StopStreams/DisableVideoInput in destructor - Consistent snake_case naming throughout decklinkin main.cpp: - Rename NodeDeckLinkIn → DeckLinkInNode - Get device_index from config().value("device_index", 0u) - Remove unused includes: , , , - Use clean receiver.wait_for_frame() instead of reaching into callback - Use mxlGetCurrentIndex resync after sleep (consistent with other nodes) - Fix return node.execute() (was node.execute(); return 0) - Fix main() spacing Co-Authored-By: Claude Sonnet 4.6 --- nodes/decklinkin/main.cpp | 128 ++++----- shared/DeckLinkReceiver.hpp | 507 +++++++++++++----------------------- 2 files changed, 220 insertions(+), 415 deletions(-) diff --git a/nodes/decklinkin/main.cpp b/nodes/decklinkin/main.cpp index e04dbc1..956ceb3 100644 --- a/nodes/decklinkin/main.cpp +++ b/nodes/decklinkin/main.cpp @@ -1,123 +1,85 @@ -#include "NodeBase.hpp" -#include "DeckLinkReceiver.hpp" -#include "Signal.hpp" -#include "FlowDef.hpp" -#include +#include #include -#include -#include #include #include -#include -#include +#include "NodeBase.hpp" +#include "FlowDef.hpp" +#include "DeckLinkReceiver.hpp" -class NodeDeckLinkIn: public dmf::NodeBase { +class DeckLinkInNode : public dmf::NodeBase { void run() override { - dmf::DeckLinkReceiver decklink_receiver; - log("Available DeckLink input devices:"); - for (auto device : decklink_receiver.devices_list) { - log("%i) %s", device.index, device.display_name.c_str()); - } - int selected_device = 0; - decklink_receiver.start_capture(selected_device); - log( - "DeckLink feed for device '%s':", - decklink_receiver.devices_list.at(selected_device).display_name.c_str() - ); + dmf::DeckLinkReceiver receiver; - dmf::SourceInfo video_source_info{}; - if (!decklink_receiver.wait_for_format(5000)) { + log("Available DeckLink devices:"); + for (const auto& d : receiver.devices) + log(" %u) %s", d.index, d.name.c_str()); + + const uint32_t device_index = config().value("device_index", 0u); + receiver.start_capture(device_index); + log("Capturing from: %s", receiver.devices[device_index].name.c_str()); + + if (!receiver.wait_for_format(5000)) { log("Timeout waiting for format detection"); return; } - video_source_info = decklink_receiver.get_input_callback()->video_info; - if (video_source_info.width == 0 || video_source_info.fps_num == 0) { - log("Invalid format detected"); - return; - } - + const auto& vi = receiver.video_info; + if (vi.width == 0 || vi.fps_num == 0) { log("Invalid format detected"); return; } + log("Detected: %dx%d @ %d/%d fps", vi.width, vi.height, vi.fps_num, vi.fps_den); const auto video_flow_info = config().at("video_flow_id"); const auto video_flow_id = video_flow_info.at("id").get(); - const int width = video_flow_info.value("width", video_source_info.width); - const int height = video_flow_info.value("height", video_source_info.height); - const int fps_num = video_flow_info.value("fps_num", video_source_info.fps_num); - const int fps_den = video_flow_info.value("fps_den", video_source_info.fps_den); + const int width = video_flow_info.value("width", vi.width); + const int height = video_flow_info.value("height", vi.height); + const int fps_num = video_flow_info.value("fps_num", vi.fps_num); + const int fps_den = video_flow_info.value("fps_den", vi.fps_den); log("video flow=%s %dx%d @ %d/%d fps", video_flow_id.c_str(), width, height, fps_num, fps_den); - mxlFlowWriter video_writer{}; + mxlFlowWriter video_writer = nullptr; mxlFlowConfigInfo video_cfg{}; bool created = false; mxlStatus vst = mxlCreateFlowWriter( instance(), dmf::make_video_flow_def(video_flow_id, node_id(), width, height, fps_num, fps_den).c_str(), "", &video_writer, &video_cfg, &created); - if (vst != MXL_STATUS_OK) { log("video mxlCreateFlowWriter failed (%s)", dmf::mxl_status_str(vst)); return; } + if (vst != MXL_STATUS_OK) { + log("mxlCreateFlowWriter failed (%s)", dmf::mxl_status_str(vst)); + return; + } const uint32_t video_stride = video_cfg.discrete.sliceSizes[0]; log("video stride=%u B/line grain=%u B ring=%u grains", video_stride, video_stride * static_cast(height), video_cfg.discrete.grainCount); - const mxlRational video_rate = {fps_num, fps_den}; - uint64_t video_index = mxlGetCurrentIndex(&video_rate); - log("start video_index=%lu", static_cast(video_index)); + const mxlRational video_rate = {fps_num, fps_den}; + uint64_t video_index = mxlGetCurrentIndex(&video_rate); + log("start video_index=%llu", video_index); - auto* cb = decklink_receiver.get_input_callback(); - const uint32_t dst_row = video_stride; - std::vector local_frame(dst_row * height, 0); + std::vector frame_buf(static_cast(video_stride) * static_cast(height), 0); + + while (dmf::g_running.load(std::memory_order_relaxed)) { + if (!receiver.wait_for_frame(frame_buf.data(), video_stride, width, height)) break; - while(dmf::g_running.load(std::memory_order_relaxed)) { - // Wait for a fresh frame from the callback - uint32_t src_row; - int src_w, src_h; - { - std::unique_lock lk(cb->frame_mutex); - cb->frame_cv.wait(lk, [&] { - return cb->frame_ready.load(std::memory_order_acquire) - || !dmf::g_running.load(std::memory_order_relaxed); - }); - if (!dmf::g_running.load(std::memory_order_relaxed)) break; - src_row = cb->frame_row_bytes ? cb->frame_row_bytes : dst_row; - src_w = cb->frame_width; - src_h = cb->frame_height; - // Row-by-row copy with zero padding for stride difference - const uint8_t* src = cb->frame_buffer.data(); - uint8_t* dst = local_frame.data(); - const uint32_t copy_row = std::min(src_row, dst_row); - const int rows = std::min(src_h, height); - std::memset(local_frame.data(), 0, local_frame.size()); - for (int y = 0; y < rows; ++y) { - std::memcpy(dst, src, copy_row); - src += src_row; - dst += dst_row; - } - cb->frame_ready.store(false, std::memory_order_release); - } - if (src_w != width || src_h != height) { - log("frame dim mismatch: src=%dx%d mxl=%dx%d (skipping)", src_w, src_h, width, height); - continue; - } - // MXL write — no lock held, callback can fill next frame in parallel mxlGrainInfo grain{}; - uint8_t* video_buf = nullptr; + uint8_t* video_buf = nullptr; vst = mxlFlowWriterOpenGrain(video_writer, video_index, &grain, &video_buf); if (vst == MXL_STATUS_OK) { - std::memcpy(video_buf, local_frame.data(), local_frame.size()); - grain.flags = 0; + std::memcpy(video_buf, frame_buf.data(), frame_buf.size()); + grain.flags = 0; grain.validSlices = grain.totalSlices; mxlFlowWriterCommitGrain(video_writer, &grain); const uint64_t ns = mxlGetNsUntilIndex(video_index + 1, &video_rate); if (ns > 0 && ns < 2'000'000'000ULL) mxlSleepForNs(ns); - video_index++; + video_index = mxlGetCurrentIndex(&video_rate); } } + + log("stopped at video_index=%llu", video_index); + mxlReleaseFlowWriter(instance(), video_writer); } }; -int main () -{ - NodeDeckLinkIn node; - node.execute(); - return 0; -} \ No newline at end of file +int main() { + DeckLinkInNode node; + return node.execute(); +} diff --git a/shared/DeckLinkReceiver.hpp b/shared/DeckLinkReceiver.hpp index 92b07b4..5afc2dc 100644 --- a/shared/DeckLinkReceiver.hpp +++ b/shared/DeckLinkReceiver.hpp @@ -1,374 +1,217 @@ #pragma once -#include -#include +#include #include +#include +#include +#include +#include #include +#include #include #include -#include -#include #include - -namespace dmf { class DeckLinkReceiver; } +#include "Signal.hpp" namespace dmf { -struct SourceInfo { - int width = 0; - int height = 0; - int fps_num = 0; - int fps_den = 0; - int stride = 0; -}; +class DeckLinkReceiver { +public: + struct VideoInfo { + int width = 0; + int height = 0; + int fps_num = 0; + int fps_den = 0; + }; -class DeckLinkInputCallback: public IDeckLinkInputCallback - { + struct DeviceInfo { + uint32_t index; + std::string name; + }; + + std::vector devices; + VideoInfo video_info{}; + + DeckLinkReceiver() { enumerate_devices(); } + + ~DeckLinkReceiver() { + if (decklink_input) { + decklink_input->StopStreams(); + decklink_input->DisableVideoInput(); + decklink_input->SetCallback(nullptr); + decklink_input->Release(); + } + delete input_callback; + for (auto* d : raw_devices) d->Release(); + if (selected_device) selected_device->Release(); + } + + void start_capture(uint32_t device_index) { + if (device_index >= raw_devices.size()) + throw std::runtime_error("Device index out of range"); + + selected_device = raw_devices[device_index]; + selected_device->AddRef(); + + HRESULT r = selected_device->QueryInterface(IID_IDeckLinkInput, (void**)&decklink_input); + if (r != S_OK) throw std::runtime_error("Could not obtain IDeckLinkInput"); + + input_callback = new InputCallback(*this); + r = decklink_input->SetCallback(input_callback); + if (r != S_OK) throw std::runtime_error("Could not set input callback"); + + r = decklink_input->EnableVideoInput(bmdModeNTSC, bmdFormat10BitYUV, + bmdVideoInputEnableFormatDetection); + if (r != S_OK) throw std::runtime_error("Could not enable video input"); + + r = decklink_input->StartStreams(); + if (r != S_OK) throw std::runtime_error("Could not start streams"); + } + + bool wait_for_format(uint64_t timeout_ms = 5000) { + std::unique_lock lk(mutex); + return format_cv.wait_for(lk, std::chrono::milliseconds(timeout_ms), + [this] { return format_detected; }); + } + + // Blocks until a fresh frame arrives or g_running goes false. + // Copies frame data into dst with MXL stride. Returns false on shutdown. + bool wait_for_frame(uint8_t* dst, uint32_t dst_stride, int width, int height) { + std::unique_lock lk(mutex); + frame_cv.wait(lk, [this] { + return frame_ready || !dmf::g_running.load(std::memory_order_relaxed); + }); + if (!dmf::g_running.load(std::memory_order_relaxed)) return false; + + const uint32_t src_stride = frame_row_bytes; + const int rows = std::min(frame_height, height); + const uint32_t copy_bytes = std::min(src_stride, dst_stride); + + std::memset(dst, 0, static_cast(dst_stride) * static_cast(height)); + const uint8_t* src = frame_buffer.data(); + uint8_t* d = dst; + for (int y = 0; y < rows; ++y, src += src_stride, d += dst_stride) + std::memcpy(d, src, copy_bytes); + + frame_ready = false; + return true; + } + +private: + class InputCallback : public IDeckLinkInputCallback { public: - bool is_video_info_set() const { return video_info_set.load(std::memory_order_acquire); } - SourceInfo video_info{}; - - std::mutex frame_mutex; - std::condition_variable frame_cv; - std::vector frame_buffer; - std::atomic frame_ready{false}; - size_t frame_size = 0; - uint32_t frame_row_bytes = 0; // actual stride from GetRowBytes() - int frame_width = 0; - int frame_height = 0; - - DeckLinkInputCallback( - dmf::DeckLinkReceiver* receiver, - bool detection_enabled, - std::mutex& mtx, - std::condition_variable& cv - ) - : decklink_receiver(receiver) - , ref_count(1) - , detection_enabled(detection_enabled) - , format_mutex(mtx) - , format_cv(cv) - {} - - void set_decklink_input(IDeckLinkInput* input) { deckLink_input = input; } + explicit InputCallback(DeckLinkReceiver& owner) : owner(owner) {} HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( - BMDVideoInputFormatChangedEvents notification_events, - IDeckLinkDisplayMode *new_display_mode, - BMDDetectedVideoInputFormatFlags detected_signal_flags - ) override + BMDVideoInputFormatChangedEvents events, + IDeckLinkDisplayMode* mode, + BMDDetectedVideoInputFormatFlags /*flags*/) override { - store_format(new_display_mode, nullptr); - long w = new_display_mode->GetWidth(); - long h = new_display_mode->GetHeight(); - BMDTimeValue dur; BMDTimeScale ts; - new_display_mode->GetFrameRate(&dur, &ts); - double fps = (double)ts / (double)dur; - const char* name = nullptr; - new_display_mode->GetName(&name); - printf("Input changed to: %ldx%ld @ %.2f fps (%s)\n", w, h, fps, name ? name : "?"); + if (!(events & bmdVideoInputDisplayModeChanged)) return S_OK; - // Discard any stale frame from the placeholder mode { - std::lock_guard lk(frame_mutex); - frame_ready.store(false, std::memory_order_release); - frame_width = 0; - frame_height = 0; - frame_buffer.clear(); - } - - // Re-enable video input with the detected display mode (per SDK samples) - if (deckLink_input && (notification_events & bmdVideoInputDisplayModeChanged)) { - HRESULT r = deckLink_input->StopStreams(); - if (r == S_OK) { - r = deckLink_input->EnableVideoInput( - new_display_mode->GetDisplayMode(), - bmdFormat10BitYUV, - bmdVideoInputEnableFormatDetection); - if (r == S_OK) { - r = deckLink_input->StartStreams(); - if (r != S_OK) { - printf("StartStreams after reconfig failed (0x%08x)\n", r); - } - } else { - printf("EnableVideoInput after reconfig failed (0x%08x)\n", r); - } - } else { - printf("StopStreams for reconfig failed (0x%08x)\n", r); + std::lock_guard lk(owner.mutex); + owner.video_info.width = static_cast(mode->GetWidth()); + owner.video_info.height = static_cast(mode->GetHeight()); + BMDTimeValue dur = 0; BMDTimeScale ts = 0; + mode->GetFrameRate(&dur, &ts); + if (dur > 0 && ts > 0) { + owner.video_info.fps_num = static_cast(ts); + owner.video_info.fps_den = static_cast(dur); + const int g = std::gcd(owner.video_info.fps_num, owner.video_info.fps_den); + if (g > 1) { owner.video_info.fps_num /= g; owner.video_info.fps_den /= g; } } + owner.frame_ready = false; + owner.frame_buffer.clear(); + owner.format_detected = true; + } + owner.format_cv.notify_one(); + + HRESULT r = owner.decklink_input->StopStreams(); + if (r == S_OK) { + r = owner.decklink_input->EnableVideoInput( + mode->GetDisplayMode(), bmdFormat10BitYUV, + bmdVideoInputEnableFormatDetection); + if (r == S_OK) owner.decklink_input->StartStreams(); } return S_OK; } HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( - IDeckLinkVideoInputFrame* video_frame, - IDeckLinkAudioInputPacket* audio_packet - ) override + IDeckLinkVideoInputFrame* video_frame, + IDeckLinkAudioInputPacket* /*audio_packet*/) override { if (!video_frame) return S_OK; - - // One-time format detection from first frame (only when auto-detection - // is OFF — otherwise VideoInputFormatChanged is authoritative). - if (!detection_enabled - && !video_info_set.load(std::memory_order_relaxed)) - { - BMDTimeValue frame_time = 0, frame_duration = 0; - video_frame->GetStreamTime(&frame_time, &frame_duration, 10000000); - { - std::lock_guard lk(video_info_mutex); - video_info.width = static_cast(video_frame->GetWidth()); - video_info.height = static_cast(video_frame->GetHeight()); - video_info.stride = static_cast(video_frame->GetRowBytes()); - if (frame_duration > 0) { - video_info.fps_num = 10000000; - video_info.fps_den = static_cast(frame_duration); - int g = std::gcd(video_info.fps_num, video_info.fps_den); - if (g > 1) { video_info.fps_num /= g; video_info.fps_den /= g; } - } - if (video_info.width > 0 && video_info.height > 0 && video_info.fps_num > 0) { - video_info_set.store(true, std::memory_order_release); - std::lock_guard flk(format_mutex); - format_cv.notify_one(); - } - } - } - - // Always capture the frame - IDeckLinkVideoBuffer* buf = nullptr; - if (video_frame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&buf) != S_OK) - return S_OK; - - buf->StartAccess(bmdBufferAccessRead); void* src = nullptr; - buf->GetBytes(&src); - if (src) { - uint32_t row_bytes = static_cast(video_frame->GetRowBytes()); - int fw = static_cast(video_frame->GetWidth()); - int fh = static_cast(video_frame->GetHeight()); - size_t sz = static_cast(row_bytes) - * static_cast(fh); - std::lock_guard lk(frame_mutex); - if (frame_buffer.size() < sz) - frame_buffer.resize(sz); - std::memcpy(frame_buffer.data(), src, sz); - frame_size = sz; - frame_row_bytes = row_bytes; - frame_width = fw; - frame_height = fh; - frame_ready.store(true, std::memory_order_release); - frame_cv.notify_one(); + video_frame->GetBytes(&src); + if (!src) return S_OK; + + const uint32_t row_bytes = static_cast(video_frame->GetRowBytes()); + const int fw = static_cast(video_frame->GetWidth()); + const int fh = static_cast(video_frame->GetHeight()); + const size_t sz = static_cast(row_bytes) * static_cast(fh); + + { + std::lock_guard lk(owner.mutex); + if (owner.frame_buffer.size() < sz) owner.frame_buffer.resize(sz); + std::memcpy(owner.frame_buffer.data(), src, sz); + owner.frame_row_bytes = row_bytes; + owner.frame_width = fw; + owner.frame_height = fh; + owner.frame_ready = true; } - buf->EndAccess(bmdBufferAccessRead); - buf->Release(); + owner.frame_cv.notify_one(); return S_OK; } - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) override - { - return E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE AddRef() override - { - return ++ref_count; - } - - ULONG STDMETHODCALLTYPE Release() override - { - return --ref_count; // DeckLinkReceiver destructor owns deletion - } + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) override { return E_NOINTERFACE; } + ULONG STDMETHODCALLTYPE AddRef() override { return ++ref_count; } + ULONG STDMETHODCALLTYPE Release() override { return --ref_count; } private: - dmf::DeckLinkReceiver* decklink_receiver; - IDeckLinkInput* deckLink_input = nullptr; - std::atomic ref_count; - bool detection_enabled = false; - std::mutex& format_mutex; - std::condition_variable& format_cv; + DeckLinkReceiver& owner; + std::atomic ref_count{1}; + }; - std::atomic video_info_set{false}; - std::mutex video_info_mutex; + std::vector raw_devices; + IDeckLink* selected_device = nullptr; + IDeckLinkInput* decklink_input = nullptr; + InputCallback* input_callback = nullptr; - void store_format(IDeckLinkDisplayMode* dm, IDeckLinkVideoInputFrame* /*vf*/) { - if (!dm) return; - std::lock_guard lk(video_info_mutex); - video_info.width = static_cast(dm->GetWidth()); - video_info.height = static_cast(dm->GetHeight()); - video_info.stride = video_info.width * 8 / 3; // V210 - BMDTimeValue dur = 0; BMDTimeScale ts = 0; - dm->GetFrameRate(&dur, &ts); - if (dur > 0 && ts > 0) { - video_info.fps_num = static_cast(ts); - video_info.fps_den = static_cast(dur); - int g = std::gcd(video_info.fps_num, video_info.fps_den); - if (g > 1) { video_info.fps_num /= g; video_info.fps_den /= g; } - } - if (video_info.width > 0 && video_info.height > 0 && video_info.fps_num > 0) { - video_info_set.store(true, std::memory_order_release); - { - std::lock_guard lk(format_mutex); - format_cv.notify_one(); - } - printf("Format from change: %dx%d @ %d/%d fps stride=%d\n", - video_info.width, video_info.height, - video_info.fps_num, video_info.fps_den, video_info.stride); - } - } -}; + std::mutex mutex; + std::condition_variable format_cv; + std::condition_variable frame_cv; + bool format_detected = false; -struct DeviceInfo { - uint32_t index; - IDeckLink* device; - std::string display_name; -}; + std::vector frame_buffer; + uint32_t frame_row_bytes = 0; + int frame_width = 0; + int frame_height = 0; + bool frame_ready = false; -class DeckLinkReceiver -{ - public: - SourceInfo video_source_info{}; - std::vector devices_list; + void enumerate_devices() { + IDeckLinkIterator* it = CreateDeckLinkIteratorInstance(); + if (!it) throw std::runtime_error("DeckLink drivers not installed"); - DeckLinkReceiver() { - selected_device_index = 0; - selected_device = nullptr; - device_config = nullptr; - device_status = nullptr; - deckLink_notification = nullptr; - // notificationCallback = nullptr; - deckLink_input = nullptr; - input_callback = nullptr; - if (!get_input_device_list()) { - throw std::runtime_error("DeckLink devices not found"); - } - } - - ~DeckLinkReceiver() { - if (input_callback) { - if (deckLink_input) deckLink_input->SetCallback(nullptr); - delete input_callback; - input_callback = nullptr; - } - // if (m_notificationCallback) - // { - // m_deckLinkNotification->Unsubscribe(bmdStatusChanged, m_notificationCallback); - // m_notificationCallback->Release(); - // } - if (selected_device) selected_device->Release(); - if (device_config) device_config->Release(); - if (device_status) device_status->Release(); - if (deckLink_input) deckLink_input->Release(); - if (deckLink_notification) deckLink_notification->Release(); - } - - HRESULT start_capture(uint32_t device_index) - { - const bool detection = true; // format-detection mode - setup(device_index, detection); - - HRESULT result = deckLink_input->EnableVideoInput( - bmdModeNTSC, // placeholder; detection will override - bmdFormat10BitYUV, - bmdVideoInputEnableFormatDetection - ); - if (result != S_OK) - { - throw std::runtime_error("Could not enable video input"); - } - - result = deckLink_input->StartStreams(); - if (result != S_OK) - { - throw std::runtime_error("Could not start capture"); - } - return S_OK; - } - - DeckLinkInputCallback* get_input_callback() { - return input_callback; - } - - IDeckLinkInput* get_input() { return deckLink_input; } - - bool wait_for_format(uint64_t timeout_ms = 5000) { - std::unique_lock lk(mutex); - return signal_condition.wait_for(lk, std::chrono::milliseconds(timeout_ms), - [this] { return input_callback && input_callback->is_video_info_set(); }); - } - - private: - uint32_t selected_device_index = 0; - IDeckLink* selected_device = nullptr; - IDeckLinkConfiguration* device_config; - IDeckLinkStatus* device_status; - IDeckLinkNotification* deckLink_notification; - // NotificationCallback* m_notificationCallback; - IDeckLinkInput* deckLink_input; - DeckLinkInputCallback* input_callback; - std::mutex mutex; - std::condition_variable signal_condition; - - bool get_input_device_list() - { - IDeckLinkIterator* decklink_iterator = CreateDeckLinkIteratorInstance(); - if (!decklink_iterator) { - throw std::runtime_error("A DeckLink iterator could not be created. The DeckLink drivers may not be installed"); - return false; - } - - IDeckLink* device = nullptr; - IDeckLinkInput* input = nullptr; - uint32_t index = 0; - while (true) { - device = nullptr; - HRESULT result = decklink_iterator->Next(&device); - if (result != S_OK) break; - input = nullptr; - result = device->QueryInterface(IID_IDeckLinkInput, (void**)&input); - if (input) input->Release(); - if (result != S_OK) { device->Release(); continue; } - const char* display_name = nullptr; - device->GetDisplayName(&display_name); - devices_list.push_back({index, device, std::string(display_name ? display_name : "?")}); + IDeckLink* device = nullptr; + uint32_t index = 0; + while (it->Next(&device) == S_OK) { + IDeckLinkInput* inp = nullptr; + if (device->QueryInterface(IID_IDeckLinkInput, (void**)&inp) == S_OK) { + inp->Release(); + const char* name = nullptr; + device->GetDisplayName(&name); + devices.push_back({index, name ? name : "?"}); + raw_devices.push_back(device); index++; + } else { + device->Release(); } - if (decklink_iterator) decklink_iterator->Release(); - return index > 0; - } - - HRESULT setup(uint32_t device_index, bool detection_enabled) { - selected_device_index = device_index; - selected_device = devices_list.at(selected_device_index).device; - selected_device->AddRef(); - - HRESULT result = selected_device->QueryInterface(IID_IDeckLinkConfiguration, (void**)&device_config); - if (result != S_OK) { - throw std::runtime_error("Could not obtain the IDeckLinkConfiguration interface"); - } - - result = selected_device->QueryInterface(IID_IDeckLinkStatus, (void**)&device_status); - if (result != S_OK) { - throw std::runtime_error("Could not obtain the IDeckLinkStatus interface"); - } - - result = selected_device->QueryInterface(IID_IDeckLinkInput, (void**)&deckLink_input); - if (result != S_OK) { - throw std::runtime_error("Could not obtain the IDeckLinkInput interface - result"); - } - - input_callback = new DeckLinkInputCallback(this, detection_enabled, mutex, signal_condition); - if (!input_callback) { - throw std::runtime_error("Could not create input callback object"); - } - - result = deckLink_input->SetCallback(input_callback); - if (result != S_OK) { - throw std::runtime_error("Could not set input callback - result"); - } - - input_callback->set_decklink_input(deckLink_input); - - return S_OK; } + it->Release(); + if (raw_devices.empty()) + throw std::runtime_error("No DeckLink input devices found"); + } }; -} \ No newline at end of file +} // namespace dmf