Read info about source from source/flow_def
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -72,4 +73,55 @@ inline std::string make_audio_flow_def(
|
||||
}.dump();
|
||||
}
|
||||
|
||||
// Actual video format as written into the MXL flow_def.json by a source node.
|
||||
// Sink nodes use this to read width/height/fps from MXL instead of trusting config values.
|
||||
struct VideoFlowInfo {
|
||||
int width = 1920;
|
||||
int height = 1080;
|
||||
int fps_num = 25;
|
||||
int fps_den = 1;
|
||||
};
|
||||
|
||||
inline VideoFlowInfo read_video_flow_info(const std::string& domain, const std::string& flow_id) {
|
||||
VideoFlowInfo info;
|
||||
const std::string path = domain + "/" + flow_id + ".mxl-flow/flow_def.json";
|
||||
std::ifstream f(path);
|
||||
if (!f.is_open()) return info;
|
||||
const auto j = nlohmann::json::parse(f, nullptr, /*allow_exceptions=*/false);
|
||||
if (j.is_discarded()) return info;
|
||||
info.width = j.value("frame_width", 1920);
|
||||
info.height = j.value("frame_height", 1080);
|
||||
if (j.contains("grain_rate")) {
|
||||
info.fps_num = j["grain_rate"].value("numerator", 25);
|
||||
info.fps_den = j["grain_rate"].value("denominator", 1);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// Actual audio format as written into the MXL flow_def.json by a source node.
|
||||
struct AudioFlowInfo {
|
||||
int sample_rate = 48000;
|
||||
int channels = 2;
|
||||
int samples_per_grain = 1920; // sample_rate * grain_rate_den / grain_rate_num
|
||||
};
|
||||
|
||||
inline AudioFlowInfo read_audio_flow_info(const std::string& domain, const std::string& flow_id) {
|
||||
AudioFlowInfo info;
|
||||
const std::string path = domain + "/" + flow_id + ".mxl-flow/flow_def.json";
|
||||
std::ifstream f(path);
|
||||
if (!f.is_open()) return info;
|
||||
const auto j = nlohmann::json::parse(f, nullptr, /*allow_exceptions=*/false);
|
||||
if (j.is_discarded()) return info;
|
||||
if (j.contains("sample_rate"))
|
||||
info.sample_rate = j["sample_rate"].value("numerator", 48000);
|
||||
info.channels = j.value("channel_count", 2);
|
||||
if (j.contains("grain_rate") && info.sample_rate > 0) {
|
||||
const int gr_num = j["grain_rate"].value("numerator", 25);
|
||||
const int gr_den = j["grain_rate"].value("denominator", 1);
|
||||
if (gr_num > 0)
|
||||
info.samples_per_grain = info.sample_rate * gr_den / gr_num;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
} // namespace dmf
|
||||
|
||||
@@ -121,6 +121,39 @@ public:
|
||||
int channel_stride = 0; // floats between channel planes (NDI planar layout)
|
||||
};
|
||||
|
||||
// Waits for the first audio packet and returns its format.
|
||||
// Call after probe(). Audio packets are typically already queued at that point.
|
||||
// Discards any video frames encountered while searching. Respects g_running.
|
||||
AudioInfo probe_audio(uint32_t timeout_ms = 5000) {
|
||||
const auto deadline = std::chrono::steady_clock::now()
|
||||
+ std::chrono::milliseconds(timeout_ms);
|
||||
while (dmf::g_running.load(std::memory_order_relaxed) &&
|
||||
std::chrono::steady_clock::now() < deadline) {
|
||||
NDIlib_video_frame_v2_t video_frame{};
|
||||
NDIlib_audio_frame_v3_t audio_frame{};
|
||||
auto type = NDIlib_recv_capture_v3(recv_, &video_frame, &audio_frame, nullptr, 100);
|
||||
if (type == NDIlib_frame_type_error)
|
||||
throw std::runtime_error("NDI source lost during audio probe");
|
||||
if (type == NDIlib_frame_type_video) {
|
||||
NDIlib_recv_free_video_v2(recv_, &video_frame);
|
||||
continue;
|
||||
}
|
||||
if (type == NDIlib_frame_type_audio) {
|
||||
const bool is_fltp = (audio_frame.FourCC == NDIlib_FourCC_audio_type_FLTP);
|
||||
AudioInfo info;
|
||||
if (is_fltp) {
|
||||
info.sample_rate = audio_frame.sample_rate;
|
||||
info.channels = audio_frame.no_channels;
|
||||
info.samples = audio_frame.no_samples;
|
||||
info.channel_stride = audio_frame.channel_stride_in_bytes / sizeof(float);
|
||||
}
|
||||
NDIlib_recv_free_audio_v3(recv_, &audio_frame);
|
||||
if (is_fltp) return info;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Timeout waiting for first audio packet");
|
||||
}
|
||||
|
||||
// Receives one NDI frame. On video: converts to V210 in frame_buffer/frame_stride.
|
||||
// On audio: copies float32 planar samples into audio_out and fills audio_info.
|
||||
// Returns FrameKind::None on timeout or non-A/V frames.
|
||||
|
||||
Reference in New Issue
Block a user