Read info about source from source/flow_def

This commit is contained in:
JohannesItten
2026-07-09 12:43:10 +03:00
parent d3ef011319
commit d2095d14d3
8 changed files with 172 additions and 54 deletions
+4 -4
View File
@@ -37,10 +37,10 @@ class DeckLinkInNode : public dmf::NodeBase {
if (!config().contains("video_flow_id")) { log("no video output connected — exiting"); return; }
const auto video_flow_info = config().at("video_flow_id");
const auto video_flow_id = video_flow_info.at("id").get<std::string>();
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);
const int width = vi.width;
const int height = vi.height;
const int fps_num = vi.fps_num;
const int 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 = nullptr;
+30 -17
View File
@@ -5,6 +5,7 @@
#include <mxl/flow.h>
#include <mxl/time.h>
#include "NodeBase.hpp"
#include "FlowDef.hpp"
#include "DeckLinkSender.hpp"
class DeckLinkOutNode : public dmf::NodeBase {
@@ -23,13 +24,7 @@ class DeckLinkOutNode : public dmf::NodeBase {
uint32_t video_stride = 0;
if (has_video) {
const auto flow_info = config().at("video_flow_id");
flow_id = flow_info.at("id").get<std::string>();
width = flow_info.value("width", 1920);
height = flow_info.value("height", 1080);
fps_num = flow_info.value("fps_num", 25);
fps_den = flow_info.value("fps_den", 1);
log("video flow=%s %dx%d @ %d/%d fps", flow_id.c_str(), width, height, fps_num, fps_den);
flow_id = config().at("video_flow_id").at("id").get<std::string>();
log("waiting for flow to become active...");
bool active = false;
@@ -38,7 +33,6 @@ class DeckLinkOutNode : public dmf::NodeBase {
if (!active) mxlSleepForNs(100'000'000);
}
if (!dmf::g_running) return;
log("flow active — starting read");
mxlFlowConfigInfo video_cfg{};
mxlStatus vst = mxlCreateFlowReader(instance(), flow_id.c_str(), "", &video_reader);
@@ -48,6 +42,15 @@ class DeckLinkOutNode : public dmf::NodeBase {
}
mxlFlowReaderGetConfigInfo(video_reader, &video_cfg);
video_stride = video_cfg.discrete.sliceSizes[0];
// Read actual format from what the source wrote — never trust config values here
const auto vfi = dmf::read_video_flow_info(domain(), flow_id);
width = vfi.width;
height = vfi.height;
fps_num = vfi.fps_num;
fps_den = vfi.fps_den;
log("video flow=%s %dx%d @ %d/%d fps stride=%u",
flow_id.c_str(), width, height, fps_num, fps_den, video_stride);
}
// --- audio flow (optional) ---
@@ -59,14 +62,18 @@ class DeckLinkOutNode : public dmf::NodeBase {
std::string audio_flow_id;
if (has_audio) {
const auto audio_flow_info = config().at("audio_flow_id");
audio_flow_id = audio_flow_info.at("id").get<std::string>();
sample_rate = audio_flow_info.value("sample_rate", 48000);
channels = audio_flow_info.value("channels", 2);
samples_per_frame = sample_rate * fps_den / fps_num;
log("audio flow=%s %d Hz %dch %d samples/frame",
audio_flow_id.c_str(), sample_rate, channels, samples_per_frame);
audio_flow_id = config().at("audio_flow_id").at("id").get<std::string>();
log("waiting for audio flow to become active...");
bool active = false;
while (!active && dmf::g_running.load(std::memory_order_relaxed)) {
mxlIsFlowActive(instance(), audio_flow_id.c_str(), &active);
if (!active) mxlSleepForNs(100'000'000);
}
if (!dmf::g_running) { has_audio = false; }
}
if (has_audio) {
mxlStatus ast = mxlCreateFlowReader(instance(), audio_flow_id.c_str(), "", &audio_reader);
if (ast != MXL_STATUS_OK) {
log("audio mxlCreateFlowReader failed (%s) — continuing without audio",
@@ -75,8 +82,14 @@ class DeckLinkOutNode : public dmf::NodeBase {
} else {
mxlFlowConfigInfo audio_cfg{};
mxlFlowReaderGetConfigInfo(audio_reader, &audio_cfg);
log("audio channels=%u buffer=%u samples",
audio_cfg.continuous.channelCount, audio_cfg.continuous.bufferLength);
const auto afi = dmf::read_audio_flow_info(domain(), audio_flow_id);
sample_rate = afi.sample_rate;
channels = afi.channels;
samples_per_frame = afi.samples_per_grain;
log("audio flow=%s %d Hz %dch %d samples/frame",
audio_flow_id.c_str(), sample_rate, channels, samples_per_frame);
log("audio mxl buffer=%u samples",
audio_cfg.continuous.bufferLength);
}
}
+17 -7
View File
@@ -30,10 +30,10 @@ class NDIInNode : public dmf::NodeBase {
if (!config().contains("video_flow_id")) { log("no video output connected — exiting"); return; }
const auto video_flow_info = config().at("video_flow_id");
const auto video_flow_id = video_flow_info.at("id").get<std::string>();
const int width = video_flow_info.value("width", src.width);
const int height = video_flow_info.value("height", src.height);
const int fps_num = video_flow_info.value("fps_num", src.fps_num);
const int fps_den = video_flow_info.value("fps_den", src.fps_den);
const int width = src.width;
const int height = src.height;
const int fps_num = src.fps_num;
const int fps_den = src.fps_den;
log("video flow=%s %dx%d @ %d/%d fps", video_flow_id.c_str(), width, height, fps_num, fps_den);
@@ -61,9 +61,19 @@ class NDIInNode : public dmf::NodeBase {
if (has_audio) {
const auto audio_flow_info = config().at("audio_flow_id");
const auto audio_flow_id = audio_flow_info.at("id").get<std::string>();
sample_rate = audio_flow_info.value("sample_rate", 48000);
channels = audio_flow_info.value("channels", 2);
bit_depth = audio_flow_info.value("bit_depth", 32);
bit_depth = audio_flow_info.value("bit_depth", 32);
log("probing audio format from NDI source...");
try {
const auto ap = ndi.probe_audio();
sample_rate = ap.sample_rate;
channels = ap.channels;
log("detected audio: %d Hz %dch", sample_rate, channels);
} catch (const std::runtime_error& e) {
log("audio probe failed (%s) — using config defaults", e.what());
sample_rate = audio_flow_info.value("sample_rate", 48000);
channels = audio_flow_info.value("channels", 2);
}
log("audio flow=%s %d Hz %dch %d-bit", audio_flow_id.c_str(), sample_rate, channels, bit_depth);
+29 -18
View File
@@ -6,6 +6,7 @@
#include <mxl/flow.h>
#include <mxl/time.h>
#include "NodeBase.hpp"
#include "FlowDef.hpp"
#include <Processing.NDI.Lib.h>
// RAII wrapper: init NDI, create sender, destroy both on scope exit.
@@ -45,14 +46,7 @@ class NDIOutNode : public dmf::NodeBase {
uint32_t video_stride = 0;
if (has_video) {
const auto flow_info = config().at("video_flow_id");
flow_id = flow_info.at("id").get<std::string>();
width = flow_info.value("width", 1920);
height = flow_info.value("height", 1080);
fps_num = flow_info.value("fps_num", 25);
fps_den = flow_info.value("fps_den", 1);
log("video flow=%s %dx%d @ %d/%d fps", flow_id.c_str(), width, height, fps_num, fps_den);
flow_id = config().at("video_flow_id").at("id").get<std::string>();
log("waiting for flow to become active...");
bool active = false;
@@ -61,13 +55,21 @@ class NDIOutNode : public dmf::NodeBase {
if (!active) mxlSleepForNs(100'000'000);
}
if (!dmf::g_running) return;
log("flow active — starting read");
mxlFlowConfigInfo video_cfg{};
mxlStatus vst = mxlCreateFlowReader(instance(), flow_id.c_str(), "", &video_reader);
if (vst != MXL_STATUS_OK) { log("video mxlCreateFlowReader failed (%s)", dmf::mxl_status_str(vst)); return; }
mxlFlowReaderGetConfigInfo(video_reader, &video_cfg);
video_stride = video_cfg.discrete.sliceSizes[0];
// Read actual format from what the source wrote — never trust config values here
const auto vfi = dmf::read_video_flow_info(domain(), flow_id);
width = vfi.width;
height = vfi.height;
fps_num = vfi.fps_num;
fps_den = vfi.fps_den;
log("video flow=%s %dx%d @ %d/%d fps stride=%u",
flow_id.c_str(), width, height, fps_num, fps_den, video_stride);
}
// --- audio flow (optional) ---
@@ -80,23 +82,32 @@ class NDIOutNode : public dmf::NodeBase {
std::string audio_flow_id;
if (has_audio) {
const auto audio_flow_info = config().at("audio_flow_id");
audio_flow_id = audio_flow_info.at("id").get<std::string>();
sample_rate = audio_flow_info.value("sample_rate", 48000);
channels = audio_flow_info.value("channels", 2);
samples_per_frame = sample_rate * fps_den / fps_num;
audio_flow_id = config().at("audio_flow_id").at("id").get<std::string>();
log("audio flow=%s %d Hz %dch %d samples/frame",
audio_flow_id.c_str(), sample_rate, channels, samples_per_frame);
log("waiting for audio flow to become active...");
bool active = false;
while (!active && dmf::g_running.load(std::memory_order_relaxed)) {
mxlIsFlowActive(instance(), audio_flow_id.c_str(), &active);
if (!active) mxlSleepForNs(100'000'000);
}
if (!dmf::g_running) { has_audio = false; }
}
if (has_audio) {
mxlStatus ast = mxlCreateFlowReader(instance(), audio_flow_id.c_str(), "", &audio_reader);
if (ast != MXL_STATUS_OK) {
log("audio mxlCreateFlowReader failed (%s) — continuing without audio", dmf::mxl_status_str(ast));
has_audio = false;
} else {
mxlFlowReaderGetConfigInfo(audio_reader, &audio_cfg);
log("audio channels=%u buffer=%u samples",
audio_cfg.continuous.channelCount, audio_cfg.continuous.bufferLength);
const auto afi = dmf::read_audio_flow_info(domain(), audio_flow_id);
sample_rate = afi.sample_rate;
channels = afi.channels;
samples_per_frame = afi.samples_per_grain;
log("audio flow=%s %d Hz %dch %d samples/frame",
audio_flow_id.c_str(), sample_rate, channels, samples_per_frame);
log("audio mxl buffer=%u samples",
audio_cfg.continuous.bufferLength);
}
}
+4 -4
View File
@@ -30,10 +30,10 @@ class VideoInNode : public dmf::NodeBase {
if (has_video) {
const auto video_flow_info = config().at("video_flow_id");
video_flow_id = video_flow_info.at("id").get<std::string>();
width = video_flow_info.value("width", video_reader.video_info.width);
height = video_flow_info.value("height", video_reader.video_info.height);
fps_num = video_flow_info.value("fps_num", video_reader.video_info.fps_num);
fps_den = video_flow_info.value("fps_den", video_reader.video_info.fps_den);
width = video_reader.video_info.width;
height = video_reader.video_info.height;
fps_num = video_reader.video_info.fps_num;
fps_den = video_reader.video_info.fps_den;
log("video flow=%s %dx%d @ %d/%d fps", video_flow_id.c_str(), width, height, fps_num, fps_den);