fix: add ffmpeg socket I/O timeout to prevent hung recordings

A stalled RTSP stream (TCP connection alive but no data) made the recorder's ffmpeg processes block forever on read(). The -t 300 flag only bounds by input timestamps, not wall-clock, so it never fired and the recorder loop never started the next segment.

Add -rw_timeout (10s socket I/O timeout) so ffmpeg exits on a stalled stream and the existing retry loop reconnects automatically.
This commit is contained in:
Claus Lohmar 2026-09-12 20:27:48 +01:00
parent 9760bdec33
commit fcc760363f

View file

@ -79,8 +79,9 @@ func (rm *RecorderManager) StopAll() {
// d. On failure: .part file stays (picked up by cleaner), wait 5s, retry
//
// Each segment is exactly 5 minutes. Gaps between segments are ~1-2s
// (FFmpeg restart overhead). The -t 300 flag provides a hard timeout
// preventing hung FFmpeg processes.
// (FFmpeg restart overhead). The -t 300 flag bounds the segment by input
// duration, while -rw_timeout makes ffmpeg exit (rather than hang) when the
// RTSP stream stalls, so the retry loop below can reconnect.
func (rm *RecorderManager) startRecorder(cam CameraConfig) {
rtspURL := cam.RTSPMain
if rtspURL == "" {
@ -125,14 +126,16 @@ func (rm *RecorderManager) startRecorder(cam CameraConfig) {
partFile := filepath.Join(outDir, "rec_"+timeStr+".part.mp4")
finalFile := filepath.Join(outDir, "rec_"+timeStr+".mp4")
// Build FFmpeg command with hard 5-minute timeout.
// Build FFmpeg command: 5-minute segment, with a socket I/O timeout so a
// stalled stream exits ffmpeg instead of hanging the recorder loop.
cmd := exec.Command("ffmpeg",
"-hide_banner", "-loglevel", "error",
"-rtsp_transport", "tcp",
"-rw_timeout", "10000000", // 10s socket I/O timeout — exit on stalled stream
"-i", rtspURL,
"-an", // drop audio (pcm_mulaw not supported in MP4)
"-c:v", "copy", // stream-copy video: zero transcoding, near-zero CPU
"-t", "300", // hard stop after 300 seconds (5 minutes)
"-t", "300", // stop after 300 seconds (5 minutes) of input
"-y", // overwrite output without asking
partFile,
)