dragonflight/services/framecache/CMakeLists.txt

64 lines
2.6 KiB
Text
Raw Normal View History

cmake_minimum_required(VERSION 3.16)
project(framecache C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -O2")
# ── libmicrohttpd ────────────────────────────────────────────────────
find_library(MHD_LIB microhttpd REQUIRED)
find_path(MHD_INCLUDE microhttpd.h REQUIRED)
include_directories(${MHD_INCLUDE})
# ── framecache server ────────────────────────────────────────────────
add_executable(framecache
src/framecache.c
src/slot.c
src/registry.c
)
target_link_libraries(framecache ${MHD_LIB} rt pthread)
# ── fc_client static library (used by bridges + test) ───────────────
add_library(fc_client STATIC
client/fc_client.c
src/slot.c # client needs fc_slot_shm_size / fc_frame_at
)
target_include_directories(fc_client PUBLIC src client)
target_link_libraries(fc_client rt pthread)
feat(framecache): phase 5 — network ingest (RTMP/SRT) via framecache - services/framecache/src/net_ingest.c: new network ingest process - Spawns ffmpeg to decode SRT/RTMP → raw UYVY422 stdout - Reads decoded frames and writes into framecache slot via shm - Registers slot with framecache HTTP API on startup - Deregisters slot on clean exit (SIGTERM) - Reconnect loop for listener mode (stays alive between sessions) - --url, --slot-id, --fc-url, --width, --height, --fps-num/den, --source-type, --listen, --listen-port, --stream-key args - Emits format JSON to stderr on first frame - services/framecache/CMakeLists.txt: add net_ingest target - services/framecache/Dockerfile: copy net_ingest to runtime image - services/node-agent/index.js: - startNetIngest() / stopNetIngest(): lifecycle management per recorder - Spawns net_ingest before sidecar start for srt/rtmp sourceTypes - Injects FC_SLOT_ID=net-<containerId> into sidecar env - Sets IpcMode=host for network sidecars using framecache - Maps temp id → real containerId after container create - stopNetIngest() called on sidecar stop - NET_INGEST_BIN env var (default: docker exec framecache net_ingest) - services/capture/src/capture-manager.js: - _buildInputArgs srt/rtmp: framecache path when FC_SLOT_ID set (spawns fc_pipe, uses pipe:0 rawvideo input — same as SDI path) - Falls back to direct URL when FC_SLOT_ID not set (legacy path) - audioMap: network via framecache uses '0:a:0?' (video-only fc_pipe, no audio FIFO — audio-in-shm is roadmap) - HLS tee: sdiHlsDir covers network-via-framecache; legacy tee gated on !FC_SLOT_ID to avoid duplicate HLS outputs - fc_pipe piped to ffmpeg stdin for network framecache path - docker-compose.worker.yml: FC_URL + NET_INGEST_BIN in node-agent env
2026-06-03 11:37:17 -04:00
# ── net_ingest — network source (RTMP/SRT) → framecache slot ─────────
# Spawned by node-agent when a network recorder starts.
# Decodes the network stream to raw UYVY422 via ffmpeg and writes frames
# into a framecache slot, giving capture-manager the same fc_pipe consumer
# interface as SDI sources.
add_executable(net_ingest
src/net_ingest.c
src/slot.c
)
target_include_directories(net_ingest PRIVATE src)
target_link_libraries(net_ingest rt pthread)
install(TARGETS net_ingest DESTINATION bin)
feat(framecache): phase 4 — capture-manager reads from framecache - services/framecache/client/fc_pipe.c: new slot→stdout pipe adapter - Opens framecache slot as consumer (independent cursor per instance) - Streams raw UYVY422 frames to stdout continuously - SIGPIPE detection via write() return — exits cleanly on ffmpeg exit - SIGTERM/SIGINT clean stop from capture-manager - Periodic stats to stderr (every 300 frames) - Exit codes: 0=clean, 1=slot not found, 2=EPIPE - services/framecache/CMakeLists.txt: add fc_pipe target + install - services/framecache/Dockerfile: copy fc_pipe to runtime image - services/capture/Dockerfile: - New fc-pipe-builder stage (builds fc_pipe from framecache sources) - Copies fc_pipe binary to /usr/local/bin/fc_pipe in runtime image - services/capture/src/capture-manager.js: - _buildInputArgs: new framecache path for deltacast + sdi/blackmagic when FC_SLOT_ID env is set (injected by node-agent from bridge fmt JSON) - Spawns fc_pipe <slot_id> as child process - Uses pipe:0 as ffmpeg rawvideo input 0 - Audio FIFO (unchanged) as ffmpeg input 1 - Falls back to legacy FIFO path when FC_SLOT_ID unset - audioMap: covers blackmagic via framecache (input 1 for audio FIFO) - isInterlacedSource: covers blackmagic interlaced signals - hiresStdio: pipe stdin when bridgeProcess set (fc_pipe stdout→ffmpeg) - Non-growing spawn: pipes fc_pipe.stdout → ffmpeg.stdin - Growing orchestrator spawn: pipes fc_pipe.stdout → bash.stdin - sdiHlsDir: covers blackmagic source type - Session state stores _fcPipeProcess for clean stop - stop(): sends SIGTERM to fc_pipe after ffmpeg SIGINT
2026-06-03 11:32:40 -04:00
# ── fc_pipe — slot → stdout adapter (used by capture-manager.js) ─────
# Spawned by capture-manager as a child process; writes raw UYVY422
# frames from a framecache slot to stdout so ffmpeg reads them as
# rawvideo pipe input. Multiple fc_pipe instances on the same slot
# each get an independent cursor — zero-copy fan-out.
add_executable(fc_pipe
client/fc_pipe.c
)
fix(framecache): single-input streaming AVI muxer in fc_pipe (kills 2-pipe ffmpeg deadlock) fc_pipe now muxes video + that frame's embedded audio into ONE streaming AVI container on stdout, so ffmpeg reads a SINGLE input (-f avi -i pipe:0) instead of a raw video pipe + a separate live audio FIFO. The two-live-pipe design deadlocked ffmpeg (it stalled forever probing input 0); a single interleaved stream removes that failure mode entirely. fc_pipe.c: - New AVI mode (argv[3] == "--avi"/"avi"). Writes RIFF('AVI ') + LIST(hdrl) { avih + strl(vids: strh+BITMAPINFOHEADER UYVY 16bpp) + strl(auds: strh+WAVEFORMATEX PCM s16le 48k 2ch) } + LIST(movi) once, then per ring entry a '00dc' video chunk followed by a '01wb' audio chunk (LE sizes, even-pad). RIFF/movi sizes use the 0x7FFFFFFF streaming sentinel (pipe is unseekable); dwFlags has NO index bits. Frame-coupled by construction: both chunks come from the SAME ring entry in one read-loop iteration. - dwScale/dwRate = fps_den/fps_num (video) and nBlockAlign/nAvgBytesPerSec (audio). If a frame has audio_size 0, emits one frame-interval of silence (round(48000*fps_den/fps_num) samples) so the audio timeline tracks video and ffmpeg never starves on the audio demuxer. - Legacy raw video-only mode retained when no avi flag is given. The old split-stdout/audio-FIFO threaded path is removed (it was the deadlock). fc_client.{h,c}: - Add fc_consumer_info() / fc_stream_info_t to expose the slot header's width/height/fps/audio params to fc_pipe for the AVI header. capture-manager.js (_buildInputArgs deltacast/sdi framecache branch): - Spawn fc_pipe with "--avi" (no audio FIFO). Remove the mkfifo + audio-FIFO creation for this path. - inputArgs: ONE input -thread_queue_size 512 -f avi [AUDIO_OFFSET_MS] -i pipe:0 (was: -f rawvideo -i pipe:0 AND -f s16le -ar 48000 -ac 2 -i <fifo>). - audioInputIndex 0, audioFifo null. Growing VC-3/HEVC builders already map [0:v] and audioMap 0:a:0?; with one AVI input that resolves to 0:v / 0:a. Validated on zampp3 against the LIVE deltacast-0-0 slot: fc_pipe --avi | ffmpeg -f avi -i pipe:0 -> dnxhd/pcm_s24le MXF gives 360 video / 360 audio packets in 6.006s (no stall at 2 frames). A synthetic 1 kHz sine slot through the same path yields mean_volume -9 dB / max -6 dB, proving the muxer carries real audio end-to-end (the live SDI input currently carries no embedded audio, so the bridge's silence fallback reads -91 dB — upstream of the muxer).
2026-06-05 10:06:35 -04:00
target_link_libraries(fc_pipe fc_client pthread)
feat(framecache): phase 4 — capture-manager reads from framecache - services/framecache/client/fc_pipe.c: new slot→stdout pipe adapter - Opens framecache slot as consumer (independent cursor per instance) - Streams raw UYVY422 frames to stdout continuously - SIGPIPE detection via write() return — exits cleanly on ffmpeg exit - SIGTERM/SIGINT clean stop from capture-manager - Periodic stats to stderr (every 300 frames) - Exit codes: 0=clean, 1=slot not found, 2=EPIPE - services/framecache/CMakeLists.txt: add fc_pipe target + install - services/framecache/Dockerfile: copy fc_pipe to runtime image - services/capture/Dockerfile: - New fc-pipe-builder stage (builds fc_pipe from framecache sources) - Copies fc_pipe binary to /usr/local/bin/fc_pipe in runtime image - services/capture/src/capture-manager.js: - _buildInputArgs: new framecache path for deltacast + sdi/blackmagic when FC_SLOT_ID env is set (injected by node-agent from bridge fmt JSON) - Spawns fc_pipe <slot_id> as child process - Uses pipe:0 as ffmpeg rawvideo input 0 - Audio FIFO (unchanged) as ffmpeg input 1 - Falls back to legacy FIFO path when FC_SLOT_ID unset - audioMap: covers blackmagic via framecache (input 1 for audio FIFO) - isInterlacedSource: covers blackmagic interlaced signals - hiresStdio: pipe stdin when bridgeProcess set (fc_pipe stdout→ffmpeg) - Non-growing spawn: pipes fc_pipe.stdout → ffmpeg.stdin - Growing orchestrator spawn: pipes fc_pipe.stdout → bash.stdin - sdiHlsDir: covers blackmagic source type - Session state stores _fcPipeProcess for clean stop - stop(): sends SIGTERM to fc_pipe after ffmpeg SIGINT
2026-06-03 11:32:40 -04:00
target_include_directories(fc_pipe PRIVATE src client)
# ── test consumer (dev utility) ──────────────────────────────────────
if(BUILD_TESTS)
add_executable(fc_test_consumer
client/fc_test_consumer.c
)
target_link_libraries(fc_test_consumer fc_client)
target_include_directories(fc_test_consumer PRIVATE src client)
endif()
feat(framecache): phase 4 — capture-manager reads from framecache - services/framecache/client/fc_pipe.c: new slot→stdout pipe adapter - Opens framecache slot as consumer (independent cursor per instance) - Streams raw UYVY422 frames to stdout continuously - SIGPIPE detection via write() return — exits cleanly on ffmpeg exit - SIGTERM/SIGINT clean stop from capture-manager - Periodic stats to stderr (every 300 frames) - Exit codes: 0=clean, 1=slot not found, 2=EPIPE - services/framecache/CMakeLists.txt: add fc_pipe target + install - services/framecache/Dockerfile: copy fc_pipe to runtime image - services/capture/Dockerfile: - New fc-pipe-builder stage (builds fc_pipe from framecache sources) - Copies fc_pipe binary to /usr/local/bin/fc_pipe in runtime image - services/capture/src/capture-manager.js: - _buildInputArgs: new framecache path for deltacast + sdi/blackmagic when FC_SLOT_ID env is set (injected by node-agent from bridge fmt JSON) - Spawns fc_pipe <slot_id> as child process - Uses pipe:0 as ffmpeg rawvideo input 0 - Audio FIFO (unchanged) as ffmpeg input 1 - Falls back to legacy FIFO path when FC_SLOT_ID unset - audioMap: covers blackmagic via framecache (input 1 for audio FIFO) - isInterlacedSource: covers blackmagic interlaced signals - hiresStdio: pipe stdin when bridgeProcess set (fc_pipe stdout→ffmpeg) - Non-growing spawn: pipes fc_pipe.stdout → ffmpeg.stdin - Growing orchestrator spawn: pipes fc_pipe.stdout → bash.stdin - sdiHlsDir: covers blackmagic source type - Session state stores _fcPipeProcess for clean stop - stop(): sends SIGTERM to fc_pipe after ffmpeg SIGINT
2026-06-03 11:32:40 -04:00
install(TARGETS framecache fc_pipe DESTINATION bin)
install(FILES client/fc_client.h src/slot.h DESTINATION include/framecache)
install(TARGETS fc_client DESTINATION lib)