mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 19:21:33 +02:00
ffmpeg is spawned with stderr=PIPE and it was only read on the error paths, so for the life of a working stream nobody read that pipe. ffmpeg writes its banner, the input analysis, then a progress line at a steady rate; a 64 KiB pipe fills eventually, ffmpeg blocks writing to it, frames stop, and the stream's own 30s timeout fires -- logged as "RTSP read timeout" with no hint that we starved it ourselves. How long that takes is unmeasured and evidently long: one H2D upstream ran 21m36s without stalling, and an earlier 512 B/s extrapolation of mine was mostly the one-off startup banner. So this is a bounded resource being treated as unbounded, not a fault anyone has reported. _FfmpegStderrTail drains the pipe continuously and keeps a 16 KiB rolling tail. That tail is what the error paths now report, which is better material than before: it holds what ffmpeg said as things went wrong, where the on-demand read returned whatever was printed first -- usually the banner, which the summariser strips anyway. Three readers wanted this one pipe, and asyncio rejects concurrent reads on a StreamReader, so the collector is authoritative: it registers by pid, _read_ffmpeg_stderr returns its tail when present and otherwise reads the pipe unchanged, and _terminate_ffmpeg skips its own stderr drain when the collector owns it (the collector keeps draining through teardown, which is all wait() needs). The generator starts it after the immediate-failure check, which reads the pipe directly because the process is already dead, and releases it after _terminate_ffmpeg. text() goes through _summarize_ffmpeg_stderr like every other stderr log here, so the access code ffmpeg echoes in its input URL stays masked. aclose() awaits the cancelled pump rather than firing and forgetting, so no pending task survives into loop teardown.