Skip to content
Darvi Lab. Logo
Engineering

ffmpeg's Segment Muxer WritesBroken FLAC Chunks

Splitting long audio into FLAC chunks with ffmpeg's segment muxer produces files with zeroed headers and inherited frame numbers. Why it breaks libsndfile, and the fix.

Oct 26, 20265 min read
DarviLabs Team
ffmpeg's Segment Muxer Writes Broken FLAC Chunks
01

The short answer

If you cut a long recording into FLAC chunks with ffmpeg's segment muxer, each chunk is a slice of one continuous FLAC stream, not a standalone FLAC file. The chunks play fine in most players, so the problem is easy to miss.

In our tests with ffmpeg 6.1 and 8.1, the chunks' STREAMINFO header reported zero total samples and a zeroed MD5 checksum, and the frame numbers inside each chunk continued from the parent stream instead of starting at zero. On 8.1, the last chunk instead claimed the totals and checksum of the entire stream.

libsndfile, the library behind Python's soundfile package, could open the chunks but failed to read every chunk except the first. The fix that worked for us: decode each chunk and re-encode it as a fresh, standalone FLAC, then verify it by reading it back with the library you will actually use.

02

What we were doing

We were preparing a speech dataset from very long recordings, each around 24 hours and 2 GB of compressed audio. Loading a file that size into memory is what had been crashing an earlier notebook-based attempt, so the new pipeline streams instead: one ffmpeg process decodes the source and writes fixed-length chunks as it goes, while a worker measures each finished chunk in parallel.

The ffmpeg side was a standard invocation: convert to mono 24 kHz 16-bit audio, encode with -c:a flac, and use -f segment with -segment_time 600 and -reset_timestamps 1 to write ten-minute chunks, with a CSV segment list so the worker knows when each chunk closes.

It is fast. A full day of audio splits in about a minute and a half on a laptop. That speed is exactly why the bug is worth writing about: everything looked fine until the next stage tried to read the files.

03

What is actually wrong in the files

A FLAC file starts with a STREAMINFO block that records, among other things, the total number of samples and an MD5 checksum of the decoded audio. The segment muxer does not rewrite this block per chunk. We found the total and the checksum zeroed on every chunk, and on newer ffmpeg the final chunk carried the totals for the whole stream.

The deeper problem is inside the audio frames. Each FLAC frame header carries a frame number, and readers use those numbers to seek. Because the muxer slices one continuous encode, the numbering never resets. In a minimal reproduction, three ten-second chunks of a test tone started at frame 0, frame 105 and frame 209. A FLAC file written on its own, by contrast, starts at frame 0 and carries correct totals and a real checksum.

Decoders that read sequentially from the start mostly do not care. Readers that seek, or that trust the header to size their buffers, do.

04

Why validation passed anyway

Our first fix patched the STREAMINFO block of each chunk with the correct sample count and checksum. After that, the reference flac -t test passed on every chunk, and soundfile reported the right duration.

Reading the audio still failed on every chunk after the first, because the frame numbering problem was untouched. The header was now correct, but the frames still claimed to start somewhere in the middle of a 24-hour stream.

The lesson is not specific to FLAC. A validator answers the question it was written for. The real test is the read path your pipeline will use, including a seek near the end of the file, not just opening it and checking metadata.

05

The fix

For each chunk ffmpeg finishes, decode it to raw PCM with ffmpeg, which reads the sliced stream without complaint. Write those samples back out as a new FLAC file with libsndfile. Confirm the new file reports the expected sample count, sample rate and channel count, then read the last second via a seek and check it matches the source samples exactly. Only then replace the original chunk, using an atomic rename.

On a ten-minute, 24 kHz mono chunk this costs about 0.2 seconds, the output size stays essentially the same, and it runs in the worker while ffmpeg is still writing the next chunk, so total runtime barely changes.

If you would rather avoid the extra step, run a separate ffmpeg process per chunk using -ss and -t, so each output is its own complete encode with correct headers. You pay some per-process overhead and seeking cost in exchange for simpler files. Whichever route you pick, verify with the same read test.

06

A short checklist for audio pipelines

Never trust that a file is valid because it plays. Read it back with the exact library and call your next stage uses, and seek, do not just open.

Compare decoded sample counts across stages. If the chunk durations do not add up to the source duration, something is lying.

Keep the slow path as the reference. Decoding with ffmpeg and comparing samples is slower than trusting headers, but it is the check that caught this bug.

07

Working with Darvi Labs

Darvi Labs builds data pipelines and AI systems for clients from Kathmandu, and this bug came out of real work on a speech dataset. We test the read path, not just the write path, because that is where data pipelines quietly fail.

If you are building audio or AI data infrastructure and want engineers who check the details, get in touch through our Contact page. More of our engineering writing is on the Blogs page.

Next step

Thinking through a similar decision?

Send us the constraint, the workflow, or the decision that keeps resurfacing. We can help clarify what should change first.

Start a conversation→