Johanness 5b2d420e71 feat: add node HTTP control, engine-to-node communication, connect-input/output API
- Node control server now accepts HTTP POST /cmd for command dispatch
  (in addition to existing WebSocket control)
- Added NodeControlClient: engine sends commands to nodes via HTTP POST
- New REST endpoints:
  POST /api/graph/nodes/:id/connect-input  - connect node input to MXL flow
  POST /api/graph/nodes/:id/connect-output - create new MXL flow + connect output
  POST /api/graph/nodes/:id/disconnect-port - remove reader/writer
  POST /api/graph/nodes/:id/command - send raw command to node
- Flow IDs now use proper UUID v4 format (MXL requires standard UUIDs)
- Flow definitions use NMOS format (urn:x-nmos:format:video)
- Engine passes --mxl-domain to node processes
- User-provided node IDs (via 'id' field in POST body)
- End-to-end verified: testsrc → passthrough → new MXL output flow
2026-05-26 21:33:17 +03:00
2026-05-25 22:21:24 +03:00
2026-05-25 22:21:24 +03:00
2026-05-25 22:21:24 +03:00

DMF Studio

Node-based visual production platform for the Dynamic Media Facility architecture.

Prerequisites

  • CMake 3.24+
  • C++20 compiler (GCC 12+, Clang 15+)
  • vcpkg (at ~/vcpkg or set CMAKE_TOOLCHAIN_FILE)
  • GStreamer (for MXL test tools only)

Build

# Configure (from project root)
cmake -B build \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake \
  -DBUILD_SHARED_LIBS=OFF \
  -DBUILD_DOCS=OFF \
  -DBUILD_TESTS=OFF \
  -DBUILD_TOOLS=OFF \
  -DBUILD_UTILS=OFF

# Build all
cmake --build build -j$(nproc)

Binaries end up in:

  • build/engine/dmf-studio-engine
  • build/nodes/passthrough/dmf-node-passthrough

Rebuild after changes

# Full rebuild
cmake --build build -j$(nproc)

# Rebuild only one target (faster)
cmake --build build -j$(nproc) --target dmf-node-passthrough
cmake --build build -j$(nproc) --target dmf-studio-engine

Clean rebuild

rm -rf build
# Then re-run the configure + build steps above

Run unit tests

build/tests/dmf-test-graph

Test with MXL

1. Create MXL domain

mkdir -p /tmp/dmf-mxl

2. Start MXL test source (writes V210 video flow)

Create a flow config file:

cat > /tmp/v210_50p.json << 'EOF'
{
  "id": "a0000001-0000-0000-0000-000000000001",
  "description": "DMF Studio test video flow",
  "format": "urn:x-nmos:format:video",
  "label": "DMF Studio Test Video",
  "tags": {
    "urn:x-nmos:tag:grouphint/v1.0": ["dmf-studio:Video"]
  },
  "media_type": "video/v210",
  "grain_rate": {"numerator": 50, "denominator": 1},
  "frame_width": 1920,
  "frame_height": 1080,
  "interlace_mode": "progressive",
  "colorspace": "BT709",
  "components": [
    {"name": "Y", "width": 1920, "height": 1080, "bit_depth": 10},
    {"name": "Cb", "width": 960, "height": 1080, "bit_depth": 10},
    {"name": "Cr", "width": 960, "height": 1080, "bit_depth": 10}
  ]
}
EOF

Start test source (needs GStreamer + MXL tools built separately):

mxl-gst-testsrc -d /tmp/dmf-mxl -v /tmp/v210_50p.json --pattern smpte &

Check active flows:

mxl-info --domain /tmp/dmf-mxl

3. Start passthrough node

build/nodes/passthrough/dmf-node-passthrough \
  --node-id pass1 \
  --control-port 9100 \
  --mxl-domain /tmp/dmf-mxl

Options:

  • --node-id, -n — Unique node instance ID (required)
  • --control-port, -p — WebSocket control port (required)
  • --mxl-domain, -d — MXL domain path (default: /dev/shm/mxl)
  • --config, -c — Node config as JSON string

4. Start engine

export DMF_STUDIO_BIN_DIR=build/nodes/passthrough
build/engine/dmf-studio-engine --port 8080

5. Control via REST API

# Add nodes
curl -X POST http://localhost:8080/api/graph/nodes \
  -H "Content-Type: application/json" \
  -d '{"type":"passthrough","id":"pass1"}'

curl -X POST http://localhost:8080/api/graph/nodes \
  -H "Content-Type: application/json" \
  -d '{"type":"passthrough","id":"pass2"}'

# Connect nodes (creates MXL flow between them)
curl -X POST http://localhost:8080/api/graph/edges \
  -H "Content-Type: application/json" \
  -d '{"from_node":"pass1","from_port":"video_out","to_node":"pass2","to_port":"video_in"}'

# View graph
curl http://localhost:8080/api/graph

# Start all nodes
curl -X POST http://localhost:8080/api/graph/start

# Stop all nodes
curl -X POST http://localhost:8080/api/graph/stop

# Remove node
curl -X DELETE http://localhost:8080/api/graph/nodes/pass1

# Remove edge
curl -X DELETE http://localhost:8080/api/graph/edges/pass1:video_out->pass2:video_in

6. Control node directly via WebSocket

Connect to ws://localhost:9100 with subprotocol dmf-control:

wscat -c ws://localhost:9100 -s dmf-control

Commands:

{"cmd": "add_reader", "flow_id": "<uuid>", "port_id": "video_in"}
{"cmd": "add_writer", "flow_id": "<uuid>", "port_id": "video_out", "flow_def": {<NMOS flow JSON>}}
{"cmd": "remove_reader", "port_id": "video_in"}
{"cmd": "remove_writer", "port_id": "video_out"}
{"cmd": "configure", "params": {}}
{"cmd": "shutdown"}

Project structure

libs/dmf-node/     — Node skeleton library (interface, WS control, MXL lifecycle)
libs/dmf-engine/   — Engine library (graph model, flow manager, process manager, REST API)
nodes/passthrough/ — Passthrough node (1 MXL in → 1 MXL out, memcpy)
engine/            — Engine binary
tests/             — Unit tests
S
Description
No description provided
Readme 284 KiB
Languages
C++ 92.8%
Shell 3.6%
CMake 3.6%