Skip to content

fix(linux): drain ffmpeg/parecord output so encoding never stalls mid-take - #626

Open
laugustyniak wants to merge 2 commits into
llfbandit:mainfrom
laugustyniak:fix/linux-drain-ffmpeg-stderr
Open

laugustyniak wants to merge 2 commits into
llfbandit:mainfrom
laugustyniak:fix/linux-drain-ffmpeg-stderr

Conversation

@laugustyniak

Copy link
Copy Markdown

Problem

On Linux, RecordLinux starts ffmpeg with Process.start in _startFfmpegWithAmplitudeMonitoring and never subscribes to its stdout/stderr. ffmpeg writes a progress line to stderr every 0.5 s for as long as it encodes. Once the kernel pipe buffer is full (64 KiB by default; a single page when the user is past fs.pipe-user-pages-soft), ffmpeg blocks in write(2), stops reading stdin, and the output file silently stops growing in the middle of a take.

stop() then awaits _inputPcmController.close(), which waits for the input pipe to drain — which never happens — so stop() never returns and the file on disk is left without its moov atom.

Observed on a real capture: ffmpeg alive for minutes in anon_pipe_write, stderr pipe at 8102/8192 bytes queued (FIONREAD); reading that pipe made ffmpeg finish the file and exit immediately. Repro without a mic: feed PCM into ffmpeg -f s16le -i - -c:a aac out.m4a with stderr piped to a reader that never reads.

The same file already drains both streams in _callPactl ("Listen to both stdout & stderr to not leak system resources") — this applies the same to the two long-running processes.

Change

  • Drain stdout/stderr of the ffmpeg process right after it starts, and parecord's stderr (its stdout is the PCM stream, already consumed).
  • Executables are injectable through the constructor (@visibleForTesting), defaults unchanged, so a test can stand in a fake ffmpeg that writes 1 MiB to stderr before reading stdin.
  • test/record_linux_test.dart: stop() must complete and the output must hold every input byte. Times out after 10 s without the drain; passes in under a second with it.

No version/CHANGELOG bump — happy to add one if you prefer it in the PR.

ffmpeg writes a progress line to stderr every half second for as long as it
encodes. record_linux never read that pipe, so once the kernel buffer was
full (64 KiB by default, a single page when the user is past
fs.pipe-user-pages-soft) ffmpeg blocked in write(), stopped reading stdin
and the output file silently stopped growing mid-take. stop() then awaited
_inputPcmController.close(), which waits for the input pipe to drain, and
never returned; the file on disk was left without its moov atom.

Drain stdout and stderr of the ffmpeg process right after it starts, and
parecord's stderr for the same reason. The executables are now injectable
through the constructor so the test can stand in a fake ffmpeg that floods
stderr before it reads a byte of input: without the drain stop() hangs,
with it the take is written in full.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant