Files
DMF-Studio/nodes/decklink-out/src/decklink_out_node.hpp
T
Johanness d02223224f feat: add DeckLink input and output nodes
decklink-in:
- IDeckLinkInputCallback::VideoInputFrameArrived captures V210 frames
- Frame data stored with mutex, copied to MXL grain in process thread
- Uses mxlSleepUntil for TAI-time-based grain pacing
- Configurable: device_index (int), mode (1080i50/1080p50/etc)
- Auto-detect input format via bmdVideoInputEnableFormatDetection
- 1 output port: video_out (V210)

decklink-out:
- Reads V210 grains from MXL, schedules playback via DeckLink output
- Creates IDeckLinkMutableVideoFrame, copies grain data, schedules
- Uses ScheduledFrameCompleted callback for frame completion
- Configurable: device_index (int), mode (1080i50/1080p50/etc)
- 1 input port: video_in (V210)

Both nodes:
- Use DeckLinkAPIDispatch.cpp for CreateDeckLinkIteratorInstance
- Access pixel data via IDeckLinkVideoBuffer (latest SDK API)
- Graceful device open/close on writer/reader add/remove
- Build conditionally via DMF_BUILD_DECKLINK + DECKLINK_SDK_DIR
2026-05-28 22:23:00 +03:00

79 lines
2.3 KiB
C++

#pragma once
#include <dmf-node/node.hpp>
#include <mxl/flow.h>
#include <mxl/time.h>
#include <DeckLinkAPI.h>
#include <atomic>
#include <mutex>
#include <optional>
#include <thread>
namespace dmf_node {
class DeckLinkOutNode : public Node {
public:
DeckLinkOutNode() = default;
~DeckLinkOutNode();
std::string type() const override { return "decklink-out"; }
std::vector<PortDef> input_ports() const override {
return {{"video_in", PortDirection::Input, MediaType::VideoV210}};
}
std::vector<PortDef> output_ports() const override { return {}; }
void configure(const nlohmann::json& params) override;
void on_add_writer(const std::string& port_id, mxlFlowWriter writer) override {}
void on_add_reader(const std::string& port_id, mxlFlowReader reader) override;
void on_remove_writer(const std::string& port_id) override {}
void on_remove_reader(const std::string& port_id) override;
void process() override;
nlohmann::json status() const override;
private:
bool open_device();
void close_device();
void schedule_frame(void* mxl_payload, long width, long height, long row_bytes);
std::optional<mxlFlowReader> reader_;
mxlRational grain_rate_{50, 1};
uint64_t read_index_ = 0;
uint64_t grains_read_ = 0;
int device_index_ = 0;
BMDDisplayMode display_mode_ = bmdModeHD1080i50;
IDeckLink* decklink_ = nullptr;
IDeckLinkOutput* output_ = nullptr;
BMDTimeValue frame_duration_ = 1000;
BMDTimeScale time_scale_ = 50000;
std::atomic<bool> playing_{false};
class OutputCallback : public IDeckLinkVideoOutputCallback {
public:
OutputCallback(DeckLinkOutNode& owner) : owner_(owner) {}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**) override { return E_NOINTERFACE; }
ULONG STDMETHODCALLTYPE AddRef() override { return 1; }
ULONG STDMETHODCALLTYPE Release() override { return 1; }
HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result) override;
HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped() override;
private:
DeckLinkOutNode& owner_;
};
std::unique_ptr<OutputCallback> callback_;
IDeckLinkMutableVideoFrame* scheduled_frame_ = nullptr;
};
} // namespace dmf_node