Commit Graph

28 Commits

Author SHA1 Message Date
Johanness 0944f574ad fabric-bridge: remove unnecessary win32 includes 2026-06-16 11:09:10 +03:00
Johanness cca0cd810a feat: fabric-bridge auto-fetch target_info from remote target node
Initiator can now auto-discover target_info by polling the target's
HTTP control server. Just set target_host and target_port in config:
  --config '{"mode":"initiator",...,"target_host":"10.10.100.1","target_port":9100}'

No more manual copy-paste of target_info JSON between hosts.
2026-06-16 11:08:06 +03:00
Johanness da836785df fix: set running_=true before starting fabric-bridge worker thread 2026-06-14 21:16:23 +03:00
Johanness 0a49b31094 fabric-bridge: flush logs on info, log worker thread start 2026-06-14 21:09:50 +03:00
Johanness a6ac2bf1a1 fabric-bridge: thread-safe startup, detailed Fabrics API logging 2026-06-14 16:54:37 +03:00
Johanness 17a8b23d03 fabric-bridge: add detailed logging before each Fabrics API call 2026-06-14 16:49:02 +03:00
Johanness 919f61fc72 fix: fabric-bridge build - use NodeRunner::run, get writer config from node base class 2026-06-14 15:27:47 +03:00
Johanness 1ab41b6740 feat: add fabric-bridge node for RDMA/inter-host MXL flow sharing
- New fabric-bridge node: initiator mode reads local MXL flow and
  transfers via Fabrics (RDMA/TCP); target mode receives remote data
  and commits to local MXL flow writer
- Add set_mxl_instance/get_mxl_instance to Node base class
- CMake option DMF_BUILD_FABRICS=OFF by default (needs libfabric)
- Target publishes target_info in status for out-of-band exchange
- Initiator receives target_info via config parameter
2026-06-14 11:26:00 +03:00
Johanness 4402fb2e12 fix decklink-out: use stream_frame_ counter for DeckLink scheduling instead of TAI-based grains_read_ 2026-05-30 23:26:51 +03:00
Johanness b434c61b86 decklink-out: non-blocking frame pool, skip late frames instead of realign, 5 preroll frames 2026-05-30 23:24:54 +03:00
Johanness bd1f060c73 decklink-out: reuse frames via ScheduledFrameCompleted callback pool 2026-05-30 23:23:15 +03:00
Johanness 26febc41d6 decklink-out: preroll frames before StartScheduledPlayback, detect output format 2026-05-30 23:21:13 +03:00
Johanness a3cd45cc49 decklink-out: add mode support check, limit CreateVideoFrame error spam 2026-05-30 23:18:02 +03:00
Johanness b4f766fb9d add HRESULT logging to CreateVideoFrame for debug 2026-05-30 23:15:00 +03:00
Johanness b5d9c7cd3f fix decklink-out: init grains_read from current time to avoid epoch realignment 2026-05-30 23:13:24 +03:00
Johanness c2e823beda fix realignment 2026-05-30 23:10:35 +03:00
Johanness 003279669e fix output res&frame rate 2026-05-30 23:07:15 +03:00
Johanness 2206fd71b3 some fixes 2026-05-30 22:12:47 +03:00
Johanness 8b5fb3dc36 fix skipped write_index 2026-05-28 23:55:16 +03:00
Johanness 34f72c22f0 hardcoded decklink mode fix 2026-05-28 23:43:26 +03:00
Johanness d02223224f feat: add DeckLink input and output nodes
decklink-in:
- IDeckLinkInputCallback::VideoInputFrameArrived captures V210 frames
- Frame data stored with mutex, copied to MXL grain in process thread
- Uses mxlSleepUntil for TAI-time-based grain pacing
- Configurable: device_index (int), mode (1080i50/1080p50/etc)
- Auto-detect input format via bmdVideoInputEnableFormatDetection
- 1 output port: video_out (V210)

decklink-out:
- Reads V210 grains from MXL, schedules playback via DeckLink output
- Creates IDeckLinkMutableVideoFrame, copies grain data, schedules
- Uses ScheduledFrameCompleted callback for frame completion
- Configurable: device_index (int), mode (1080i50/1080p50/etc)
- 1 input port: video_in (V210)

Both nodes:
- Use DeckLinkAPIDispatch.cpp for CreateDeckLinkIteratorInstance
- Access pixel data via IDeckLinkVideoBuffer (latest SDK API)
- Graceful device open/close on writer/reader add/remove
- Build conditionally via DMF_BUILD_DECKLINK + DECKLINK_SDK_DIR
2026-05-28 22:23:00 +03:00
Johanness 4b50f49172 chore: phase 1 cleanup — proper shutdown, flow resource cleanup, test fix
- node_runner: track all readers/writers in flow_resources vector,
  release them via mxlReleaseFlowWriter/mxlReleaseFlowReader before
  destroying the MXL instance (fixes 'leaked flow writer' warning)
- node_runner: remove_writer/remove_reader commands now also release
  the MXL flow resources, not just reset the node's optional<>
- engine: call process_manager.stop_all(graph) on shutdown to kill
  child node processes (prevents orphaned passthrough processes)
- graph: add get_node_mut() for process_manager to update node state
- process_manager: implement stop_all(Graph&) that SIGTERMs all
  running node processes
- passthrough: reduce logging to first-grain and realign-once only
- tests: update flow format assertion to match NMOS
  (urn:x-nmos:format:video instead of video/v210)
2026-05-26 23:04:16 +03:00
Johanness 5b48d86c6e fix: separate grain processing thread from LWS event loop
The fundamental issue: mxlSleepUntil/mxlSleepForNs blocks the entire
thread, making LWS unresponsive and causing control commands to hang.
Meanwhile, LWS poll(1) adds latency that makes grain timing unreliable.

Solution: run node->process() in a dedicated thread that can freely use
mxlSleepUntil for precise grain timing, while the main thread runs
control_server->poll(10) for LWS event handling.

Passthrough now uses mxlSleepUntil(deadline) matching the mxl-gst-sink
Cursor pattern, with 2-grain read delay for buffering.
2026-05-26 22:55:57 +03:00
Johanness d677b654f0 fix: non-blocking grain timing — use mxlGetNsUntilIndex instead of mxlSleepUntil
mxlSleepUntil blocks the entire thread including LWS poll, causing the
control server to become unresponsive and grains to be missed. Instead,
check mxlGetNsUntilIndex and skip process() if the grain isn't due yet
(>2ms away). The node loop spins with poll(1) keeping LWS responsive,
and only attempts a grain read when it's nearly due.
2026-05-26 22:51:57 +03:00
Johanness 503023f3e5 fix: use mxlSleepUntil delivery deadline pattern for grain timing
Match the mxl-gst-sink pattern: sleep until the next grain's delivery
deadline using mxlSleepUntil, then read with a short 5ms timeout.
Uses 2-grain read delay (40ms at 50fps) for buffering headroom.

Previous approach of mxlSleepForNs + 20ms GetGrain timeout caused the
passthrough to fall behind: each iteration took ~25ms (poll+sleep+read),
missing grains and perpetually chasing headIndex via realign.
2026-05-26 22:46:33 +03:00
Johanness 2c712592dd fix: use TAI-time-based grain index alignment in passthrough
Instead of chasing headIndex from mxlFlowReaderGetRuntimeInfo (which
points to the NEXT grain to be written, causing perpetual TOO_EARLY),
the passthrough now uses mxlTimestampToIndex + mxlGetNsUntilIndex for
proper timing alignment, matching the pattern used by mxl-gst-sink.

Key changes:
- realign() computes read_index from current TAI time minus 1 grain delay
- process() uses mxlSleepForNs to wait until the target grain is due
- on_add_reader fetches grain_rate from mxlFlowConfigInfo
- Removed separate write_index_ (uses grain_info.index for writer)
2026-05-26 22:40:46 +03:00
Johanness 3e1477c56c fix: LWS HTTP connection leak, grain index alignment, and reader head tracking
- control_server: add Connection: close header + always return -1 after
  HTTP response to force-close connection. Without this, lws_service()
  blocks forever after the first POST /cmd, freezing the node process loop.
- passthrough: initialize read_index_ to runtime.headIndex when reader
  is added (prevents TOO_EARLY errors on first read)
- passthrough: handle MXL_ERR_OUT_OF_RANGE_TOO_EARLY in addition to
  TOO_LATE (both jump to head index)
- passthrough: use grain_info.index as writer index instead of separate
  write_index_ counter (MXL writers must use TAI-based grain indices)
- passthrough: reduce grain read timeout from 100ms to 20ms for tighter
  loop with LWS poll
- node_runner: add 'status' command handler that sends node status
  back via control server
2026-05-26 22:22:46 +03:00
Johanness 5b5ffa1308 feat: scaffold project framework with engine, node skeleton, and passthrough node
- CMake build system with vcpkg dependency management
- libdmf-node: Node interface, port definitions, WebSocket control
  server, node runner with MXL lifecycle management
- libdmf-engine: Graph model (nodes/edges CRUD, serialization),
  FlowManager (UUID + V210 NMOS flow definitions), ProcessManager
  (fork/exec node processes), ApiServer (REST API for graph control)
- Passthrough node: reads V210 grains from MXL, copies, writes to MXL
- Unit tests for Graph and FlowManager (6 cases, 21 assertions)
- MXL SDK as git submodule (symlinked from extern/mxl)
2026-05-26 01:32:50 +03:00