128 lines
5.0 KiB
C++
128 lines
5.0 KiB
C++
#pragma once
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
namespace dmf {
|
|
|
|
// Generates a minimal but valid NMOS IS-04 flow definition JSON string
|
|
// for a video/v210 flow. MXL uses this to set up the ring buffer geometry.
|
|
inline std::string make_video_flow_def(
|
|
const std::string& flow_id,
|
|
const std::string& label,
|
|
int width, int height,
|
|
int fps_num, int fps_den = 1)
|
|
{
|
|
using json = nlohmann::json;
|
|
return json{
|
|
{"id", flow_id},
|
|
{"format", "urn:x-nmos:format:video"},
|
|
{"label", label},
|
|
{"description", label + " MXL Video Flow"},
|
|
{"media_type", "video/v210"},
|
|
{"parents", json::array()},
|
|
{"grain_rate", {{"numerator", fps_num}, {"denominator", fps_den}}},
|
|
{"frame_width", width},
|
|
{"frame_height", height},
|
|
{"interlace_mode", "progressive"},
|
|
{"colorspace", "BT709"},
|
|
{"tags", {
|
|
{"urn:x-nmos:tag:grouphint/v1.0", json::array({label + ":Video"})}
|
|
}},
|
|
{"components", json::array({
|
|
{{"name","Y"}, {"width",width}, {"height",height}, {"bit_depth",10}},
|
|
{{"name","Cb"}, {"width",width/2}, {"height",height}, {"bit_depth",10}},
|
|
{{"name","Cr"}, {"width",width/2}, {"height",height}, {"bit_depth",10}}
|
|
})}
|
|
}.dump();
|
|
}
|
|
|
|
// Generates a minimal but valid NMOS IS-04 flow definition JSON string
|
|
// for a raw PCM audio flow. grain_rate_num/den sets the grain delivery rate
|
|
// (default 25/1 = one grain per video frame). MXL computes grain size as
|
|
// sample_rate / grain_rate samples per grain.
|
|
inline std::string make_audio_flow_def(
|
|
const std::string& flow_id,
|
|
const std::string& label,
|
|
int sample_rate,
|
|
int channels,
|
|
int bit_depth = 32,
|
|
int grain_rate_num = 25,
|
|
int grain_rate_den = 1)
|
|
{
|
|
using json = nlohmann::json;
|
|
static const char* ch_labels[] = {"L","R","C","LFE","Ls","Rs","Lss","Rss"};
|
|
json ch_arr = json::array();
|
|
for (int i = 0; i < channels; ++i)
|
|
ch_arr.push_back({{"label", i < 8 ? ch_labels[i] : ("Ch" + std::to_string(i + 1))}});
|
|
return json{
|
|
{"id", flow_id},
|
|
{"format", "urn:x-nmos:format:audio"},
|
|
{"label", label},
|
|
{"description", label + " MXL Audio Flow"},
|
|
{"media_type", bit_depth == 32 ? "audio/float32" : "audio/L" + std::to_string(bit_depth)},
|
|
{"parents", json::array()},
|
|
{"grain_rate", {{"numerator", grain_rate_num}, {"denominator", grain_rate_den}}},
|
|
{"sample_rate", {{"numerator", sample_rate}, {"denominator", 1}}},
|
|
{"channels", ch_arr}, // NMOS IS-04 metadata (ignored by MXL)
|
|
{"channel_count", channels}, // MXL uses this for grain buffer geometry
|
|
{"bit_depth", bit_depth}, // must be 32 or 64
|
|
{"tags", {
|
|
{"urn:x-nmos:tag:grouphint/v1.0", json::array({label + ":Audio"})}
|
|
}},
|
|
}.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
|