feat: garbage collection on startup, readable status strings, audio/float32
studio-manager: call mxlGarbageCollectFlows before launching nodes to
clean up stale flow directories left by previous crashed runs.
shared/NodeBase.hpp: add mxl_status_str(mxlStatus) — converts error
codes to readable names (e.g. MXL_ERR_OUT_OF_RANGE_TOO_LATE). All
nodes now log these names instead of raw integers.
shared/FlowDef.hpp: use "audio/float32" as media_type for 32-bit audio
flows, matching the MXL SDK examples. audio/L{n} kept for other depths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ class FakeSinkNode : public dmf::NodeBase {
|
||||
mxlFlowReader reader{};
|
||||
mxlStatus st = mxlCreateFlowReader(instance(), flow_id.c_str(), nullptr, &reader);
|
||||
if (st != MXL_STATUS_OK) {
|
||||
log("mxlCreateFlowReader failed (status=%d)", st);
|
||||
log("mxlCreateFlowReader failed (%s)", dmf::mxl_status_str(st));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class FakeSinkNode : public dmf::NodeBase {
|
||||
index = ri.headIndex;
|
||||
|
||||
} else {
|
||||
log("unexpected status=%d on index=%llu", st, index);
|
||||
log("unexpected status (%s) on index=%llu", dmf::mxl_status_str(st), index);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class NDIInNode : public dmf::NodeBase {
|
||||
instance(),
|
||||
dmf::make_video_flow_def(video_flow_id, node_id(), width, height, fps_num, fps_den).c_str(),
|
||||
nullptr, &video_writer, &video_cfg, &created);
|
||||
if (st != MXL_STATUS_OK) { log("video mxlCreateFlowWriter failed (status=%d)", st); return; }
|
||||
if (st != MXL_STATUS_OK) { log("video mxlCreateFlowWriter failed (%s)", dmf::mxl_status_str(st)); return; }
|
||||
|
||||
const uint32_t video_stride = video_cfg.discrete.sliceSizes[0];
|
||||
log("video stride=%u B/line grain=%u B ring=%u grains",
|
||||
@@ -71,7 +71,7 @@ class NDIInNode : public dmf::NodeBase {
|
||||
fps_num, fps_den).c_str(),
|
||||
nullptr, &audio_writer, &audio_cfg, &created);
|
||||
if (ast != MXL_STATUS_OK) {
|
||||
log("audio mxlCreateFlowWriter failed (status=%d) — continuing without audio", ast);
|
||||
log("audio mxlCreateFlowWriter failed (%s) — continuing without audio", dmf::mxl_status_str(ast));
|
||||
has_audio = false;
|
||||
} else {
|
||||
log("audio channels=%u buffer=%u samples",
|
||||
@@ -146,7 +146,7 @@ class NDIInNode : public dmf::NodeBase {
|
||||
grain.validSlices = grain.totalSlices;
|
||||
mxlFlowWriterCommitGrain(video_writer, &grain);
|
||||
} else {
|
||||
log("video OpenGrain failed (status=%d) at index=%llu", st, video_index);
|
||||
log("video OpenGrain failed (%s) at index=%llu", dmf::mxl_status_str(st), video_index);
|
||||
}
|
||||
video_index = current + 1;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class NDIOutNode : public dmf::NodeBase {
|
||||
|
||||
mxlFlowConfigInfo video_cfg{};
|
||||
mxlStatus vst = mxlCreateFlowReader(instance(), flow_id.c_str(), nullptr, &video_reader);
|
||||
if (vst != MXL_STATUS_OK) { log("video mxlCreateFlowReader failed (status=%d)", vst); return; }
|
||||
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];
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class NDIOutNode : public dmf::NodeBase {
|
||||
|
||||
mxlStatus ast = mxlCreateFlowReader(instance(), audio_flow_id.c_str(), nullptr, &audio_reader);
|
||||
if (ast != MXL_STATUS_OK) {
|
||||
log("audio mxlCreateFlowReader failed (status=%d) — continuing without audio", ast);
|
||||
log("audio mxlCreateFlowReader failed (%s) — continuing without audio", dmf::mxl_status_str(ast));
|
||||
has_audio = false;
|
||||
} else {
|
||||
mxlFlowReaderGetConfigInfo(audio_reader, &audio_cfg);
|
||||
@@ -224,7 +224,7 @@ class NDIOutNode : public dmf::NodeBase {
|
||||
video_index = ri.headIndex;
|
||||
|
||||
} else {
|
||||
log("unexpected video status=%d on index=%llu", vst, video_index);
|
||||
log("unexpected video status (%s) on index=%llu", dmf::mxl_status_str(vst), video_index);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class TestPatternNode : public dmf::NodeBase {
|
||||
instance(),
|
||||
dmf::make_video_flow_def(flow_id, node_id(), width, height, fps_num, fps_den).c_str(),
|
||||
nullptr, &video_writer, &video_cfg, &created);
|
||||
if (vst != MXL_STATUS_OK) { log("video mxlCreateFlowWriter failed (status=%d)", vst); return; }
|
||||
if (vst != MXL_STATUS_OK) { log("video 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",
|
||||
@@ -55,7 +55,7 @@ class TestPatternNode : public dmf::NodeBase {
|
||||
fps_num, fps_den).c_str(),
|
||||
nullptr, &audio_writer, &audio_cfg, &created);
|
||||
if (ast != MXL_STATUS_OK) {
|
||||
log("audio mxlCreateFlowWriter failed (status=%d) — continuing without audio", ast);
|
||||
log("audio mxlCreateFlowWriter failed (%s) — continuing without audio", dmf::mxl_status_str(ast));
|
||||
has_audio = false;
|
||||
} else {
|
||||
log("audio channels=%u buffer=%u samples",
|
||||
@@ -85,7 +85,7 @@ class TestPatternNode : public dmf::NodeBase {
|
||||
uint8_t* video_buf = nullptr;
|
||||
vst = mxlFlowWriterOpenGrain(video_writer, video_index, &video_grain, &video_buf);
|
||||
if (vst != MXL_STATUS_OK) {
|
||||
log("OpenGrain failed (status=%d), skipping index=%llu", vst, video_index);
|
||||
log("OpenGrain failed (%s) at index=%llu", dmf::mxl_status_str(vst), video_index);
|
||||
video_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ inline std::string make_audio_flow_def(
|
||||
{"format", "urn:x-nmos:format:audio"},
|
||||
{"label", label},
|
||||
{"description", label + " MXL Audio Flow"},
|
||||
{"media_type", "audio/L" + std::to_string(bit_depth)},
|
||||
{"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}}},
|
||||
|
||||
@@ -10,6 +10,35 @@
|
||||
|
||||
namespace dmf {
|
||||
|
||||
inline const char* mxl_status_str(mxlStatus s) noexcept {
|
||||
switch (s) {
|
||||
case MXL_STATUS_OK: return "MXL_STATUS_OK";
|
||||
case MXL_ERR_UNKNOWN: return "MXL_ERR_UNKNOWN";
|
||||
case MXL_ERR_FLOW_NOT_FOUND: return "MXL_ERR_FLOW_NOT_FOUND";
|
||||
case MXL_ERR_OUT_OF_RANGE_TOO_LATE: return "MXL_ERR_OUT_OF_RANGE_TOO_LATE";
|
||||
case MXL_ERR_OUT_OF_RANGE_TOO_EARLY: return "MXL_ERR_OUT_OF_RANGE_TOO_EARLY";
|
||||
case MXL_ERR_INVALID_FLOW_READER: return "MXL_ERR_INVALID_FLOW_READER";
|
||||
case MXL_ERR_INVALID_FLOW_WRITER: return "MXL_ERR_INVALID_FLOW_WRITER";
|
||||
case MXL_ERR_TIMEOUT: return "MXL_ERR_TIMEOUT";
|
||||
case MXL_ERR_INVALID_ARG: return "MXL_ERR_INVALID_ARG";
|
||||
case MXL_ERR_CONFLICT: return "MXL_ERR_CONFLICT";
|
||||
case MXL_ERR_PERMISSION_DENIED: return "MXL_ERR_PERMISSION_DENIED";
|
||||
case MXL_ERR_FLOW_INVALID: return "MXL_ERR_FLOW_INVALID";
|
||||
case MXL_ERR_STRLEN: return "MXL_ERR_STRLEN";
|
||||
case MXL_ERR_INTERRUPTED: return "MXL_ERR_INTERRUPTED";
|
||||
case MXL_ERR_NO_FABRIC: return "MXL_ERR_NO_FABRIC";
|
||||
case MXL_ERR_INVALID_STATE: return "MXL_ERR_INVALID_STATE";
|
||||
case MXL_ERR_INTERNAL: return "MXL_ERR_INTERNAL";
|
||||
case MXL_ERR_NOT_READY: return "MXL_ERR_NOT_READY";
|
||||
case MXL_ERR_NOT_FOUND: return "MXL_ERR_NOT_FOUND";
|
||||
case MXL_ERR_EXISTS: return "MXL_ERR_EXISTS";
|
||||
case MXL_ERR_UNSUPPORTED_OPERATION: return "MXL_ERR_UNSUPPORTED_OPERATION";
|
||||
default: return "MXL_ERR_UNRECOGNIZED";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Base class for all DMF node binaries.
|
||||
//
|
||||
// Handles the boilerplate every node needs:
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <mxl/mxl.h>
|
||||
#include <mxl/time.h>
|
||||
#include "Signal.hpp"
|
||||
#include "FlowGraph.hpp"
|
||||
@@ -152,6 +153,16 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
fprintf(stderr, "[studio-manager] domain: %s\n", domain.c_str());
|
||||
|
||||
// Clean up stale flow directories left by any previous crashed run
|
||||
{
|
||||
mxlInstance gc = mxlCreateInstance(domain.c_str(), nullptr);
|
||||
if (gc) {
|
||||
mxlGarbageCollectFlows(gc);
|
||||
mxlDestroyInstance(gc);
|
||||
fprintf(stderr, "[studio-manager] garbage collected stale flows\n");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Build and launch the pipeline graph ---
|
||||
const dmf::FlowGraph graph = build_graph();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user