diff --git a/services/capture/deltacast-bridge/main.c b/services/capture/deltacast-bridge/main.c index 1e1f76f..829f4bf 100644 --- a/services/capture/deltacast-bridge/main.c +++ b/services/capture/deltacast-bridge/main.c @@ -297,39 +297,51 @@ static void *audio_thread(void *arg) { static void *video_thread(void *arg) { PortState *ps = (PortState *)arg; - int fd = open(ps->video_fifo, O_WRONLY); - if (fd < 0) { - fprintf(stderr, "[video:%u] open FIFO failed: %s\n", ps->port, strerror(errno)); - atomic_store(&g_port_stop[ps->port], 1); - return NULL; - } - - HANDLE slot = NULL; + /* Outer loop: reopen the FIFO writer each time a reader connects. + * Mirror the audio thread pattern — EPIPE means the ffmpeg sidecar for + * this port died (session stop/restart), NOT a hardware fault. We reopen + * and block until the next recorder start; other ports are unaffected. */ while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) { - ULONG r = VHD_LockSlotHandle(ps->video_stream, &slot); - if (r == VHDERR_NOERROR) { - BYTE *buf = NULL; - ULONG sz = 0; - if (VHD_GetSlotBuffer(slot, VHD_SDI_BT_VIDEO, &buf, &sz) == VHDERR_NOERROR) { - if (write_all(fd, buf, sz) < 0) { - /* EPIPE on video: the capture sidecar for this port died. - * Stop only this port's threads — other ports unaffected. */ - fprintf(stderr, "[video:%u] EPIPE — stopping port\n", ps->port); - atomic_store(&g_port_stop[ps->port], 1); - VHD_UnlockSlotHandle(slot); - break; - } - } - VHD_UnlockSlotHandle(slot); - } else if (r != VHDERR_TIMEOUT) { - fprintf(stderr, "[video:%u] VHD_LockSlotHandle error %lu — stopping port\n", - ps->port, r); - atomic_store(&g_port_stop[ps->port], 1); - break; + + int fd = open(ps->video_fifo, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "[video:%u] open FIFO failed: %s\n", ps->port, strerror(errno)); + struct timespec ts = {0, 200000000L}; + nanosleep(&ts, NULL); + continue; } + fprintf(stderr, "[video:%u] FIFO writer connected\n", ps->port); + + HANDLE slot = NULL; + int fatal = 0; + while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) { + ULONG r = VHD_LockSlotHandle(ps->video_stream, &slot); + if (r == VHDERR_NOERROR) { + BYTE *buf = NULL; + ULONG sz = 0; + if (VHD_GetSlotBuffer(slot, VHD_SDI_BT_VIDEO, &buf, &sz) == VHDERR_NOERROR) { + if (write_all(fd, buf, sz) < 0) { + /* EPIPE: sidecar died (session stop/restart). + * Break to outer loop — reopen for next session. */ + fprintf(stderr, "[video:%u] EPIPE — waiting for next reader\n", ps->port); + VHD_UnlockSlotHandle(slot); + break; + } + } + VHD_UnlockSlotHandle(slot); + } else if (r != VHDERR_TIMEOUT) { + fprintf(stderr, "[video:%u] VHD_LockSlotHandle error %lu — stopping port\n", + ps->port, r); + atomic_store(&g_port_stop[ps->port], 1); + fatal = 1; + break; + } + } + + close(fd); + if (fatal) break; } - close(fd); return NULL; }