fix(deltacast-bridge): video EPIPE reopens FIFO instead of stopping port permanently
When a capture sidecar stopped/restarted, the bridge video thread got EPIPE
on the FIFO write, set g_port_stop[port]=1, and the port went dead — requiring
a full bridge restart to recover. Subsequent record attempts on that port would
hang in 'connecting' forever.
Fix: mirror the audio thread pattern — on EPIPE, close the FIFO and loop back
to open() blocking for the next reader. Hardware lock errors (SDK failures)
still stop the port via g_port_stop as before. Only reader-disconnect (EPIPE)
now recovers gracefully.
This was the cause of port 6 (Ghost) failure in the burn test.
🤖 Generated with Claude Code
This commit is contained in:
parent
858c9f7b97
commit
08be8fea77
1 changed files with 41 additions and 29 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue