Wild Dragon Dev
01211fef7a
fix(framecache): address critical bugs from code review
...
C-Bug 1 (Torn read): fc_client.c zero-copy pointer replaced with consumer-owned
copy buffer + post-copy cursor revalidation to prevent reading torn frames when
the writer laps a slow consumer. New FC_LAPPED return code.
C-Bug 3 (Semaphore busy-spin): fc_client.c drains the semaphore (sem_trywait)
so the count never accumulates, relying entirely on write_cursor diff for
availability. Prevents 100% CPU loops + EOVERFLOW.
C-Bug 4 (GET /slots stack overflow): framecache.c uses heap allocation with
explicit bounds checking for JSON serialization instead of a 64KB stack buffer.
C-Bug 6 (DeckLink race): decklink-bridge uses pthread_mutex_t around fc_writer
calls and reopen_slot to prevent UAF/double-free from concurrent SDK callbacks.
C-Bug 2-net (Resolution resync): net_ingest explicitly scales to target W:H
so ffmpeg always outputs exactly frame_size bytes, ignoring source resolution
changes.
C-Bug 8 (strdup leak): net_ingest uses static caller-owned buffers for ffmpeg
args instead of strdup across listener reconnects.
C-Bug 9 (PROT_READ segfault): removed atomic write to hdr->dropped_frames from
the consumer read loop (which maps shm read-only).
2026-06-03 16:25:34 +00:00
Wild Dragon Dev
b700902200
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 15:32:40 +00:00
Wild Dragon Dev
1573bf8954
feat(framecache): phase 1 — framecache container + consumer library
...
- services/framecache/: new standalone container
- slot.h/slot.c: shm ring buffer (120 frames, FC_MAGIC header, atomic
write_cursor, POSIX semaphore per slot)
- registry.h/registry.c: in-memory slot registry + /dev/shm/framecache/
registry.json persistence
- framecache.c: HTTP API server (libmicrohttpd, port 7435)
POST /slots, GET /slots, GET /slots/:id, DELETE /slots/:id, GET /health
- fc_client.h/fc_client.c: consumer library — fc_consumer_open/read/close
with per-consumer cursor, timeout via sem_timedwait, automatic skip+count
when consumer falls behind writer by > ring_depth frames
- fc_test_consumer.c: dev utility to attach to any slot and print fps/stats
- CMakeLists.txt: framecache server + fc_client static lib + test consumer
- Dockerfile: builder + slim runtime stages
- docker-compose.worker.yml: add framecache service (profile: capture,
ipc: host, shm_size from FC_SHM_SIZE_GB env var, healthcheck)
- .env.example: document FC_SHM_SIZE_GB with per-node guidance
2026-06-03 14:53:51 +00:00