Skip to content

Fix FLAC playback auto-stop issues and reset frame buffer on seek#910

Open
axel10 wants to merge 2 commits into
sbooth:mainfrom
axel10:fix-flac
Open

Fix FLAC playback auto-stop issues and reset frame buffer on seek#910
axel10 wants to merge 2 commits into
sbooth:mainfrom
axel10:fix-flac

Conversation

@axel10
Copy link
Copy Markdown

@axel10 axel10 commented May 22, 2026

Description

This pull request addresses two distinct issues in SFBFLACDecoder:

  1. FLAC playback not automatically stopping at the end of stream:
    For certain FLAC files (e.g., those containing trailing metadata, ID3v2 tags, or padding blocks after the audio frames), the underlying decoder continues to process non-audio blocks via FLAC__stream_decoder_process_single() successfully. Because FLAC__stream_decoder_get_state() does not yet return FLAC__STREAM_DECODER_END_OF_STREAM during these calls, the decode loop in decodeIntoBuffer:frameLength:error: can get stuck in a spin or fail to immediately signal EOF (returning a buffer frame length of 0).
    • Fix: If _streamInfo.total_samples is known, we clamp the requested frameLength to the remaining samples. If _framePosition is already at or past the total sample count, we return early, signaling EOF.
  2. Stale audio frame buffer after seeking:
    When seekToFrame:error: is called, the internal push-to-pull buffer _frameBuffer may still contain leftover decoded frames from the pre-seek position. If these are not cleared, the next decodeIntoBuffer: call will read these stale frames first, resulting in brief audio artifacts or incorrect position tracking immediately after a seek.
    • Fix: Reset _frameBuffer.frameLength to 0 upon a successful seek to ensure the next read pulls fresh data from the seek target.

Changes

  • Sources/CSFBAudioEngine/Decoders/SFBFLACDecoder.mm:
    • Bound check _framePosition against _streamInfo.total_samples and clamp frameLength in decodeIntoBuffer:frameLength:error:.
    • Reset _frameBuffer.frameLength to 0 in seekToFrame:error:.

This is the test file: https://drive.google.com/file/d/1Jk7PvfMK9YmMoQaWv29iQbxch0d9dtm8/view?usp=sharing

@sbooth
Copy link
Copy Markdown
Owner

sbooth commented May 22, 2026

Thanks for the PR!

I am not sure whether the sample file conforms to the FLAC specification. Running it through flac gives an error:

/tmp % flac -d AcDc\ -\ Shoot\ To\ Thrill.flac 

flac 1.5.0
Copyright (C) 2000-2009  Josh Coalson, 2011-2025  Xiph.Org Foundation
flac comes with ABSOLUTELY NO WARRANTY.  This is free software, and you are
welcome to redistribute it under certain conditions.  Type `flac' for details.

AcDc - Shoot To Thrill.flac: *** Got error code 0:FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC after processing 14078484 samples


AcDc - Shoot To Thrill.flac: ERROR while decoding data
                             state = FLAC__STREAM_DECODER_ABORTED

and metaflac doesn't like it either:

/tmp % metaflac --list AcDc\ -\ Shoot\ To\ Thrill.flac
AcDc - Shoot To Thrill.flac: ERROR: reading metadata, status = "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR"

I will take a closer look at the file structure to see what is going on. Do you know where this file came from (which encoder/editor)?

Clearing _frameBuffer on seek is a good fix.

@axel10
Copy link
Copy Markdown
Author

axel10 commented May 23, 2026

I've forgotten where this file came from; it was probably downloaded from some forum or obscure website.

This song plays fine with Rust's Rodio, but mainstream players do exhibit inconsistent behavior when playing it. foobar2000 or IINA loop the last few seconds of the 10-second song. Android's Poweramp, however, freezes at the end.

I have a music player project that uses rust-ffmpeg to call ffmpeg for audio decoding on Windows and Linux, which can be used for testing. https://github.com/axel10/vibe_flow
The release build for this project can be downloaded directly.

@axel10
Copy link
Copy Markdown
Author

axel10 commented May 23, 2026

Since the total duration of a FLAC file is calculated using STREAMINFO, the total_samples in STREAMINFO should be considered complete after playback, ignoring any remaining data. As a mandatory metadata block, STREAMINFO is included in most FLAC files to record the actual song length, so a total_samples value less than the actual length is almost never found.

Because the FLAC specification is somewhat lenient, some software may overload the song with metadata. Using total_samples to limit sampling is a robust approach, in my opinion.

@sbooth
Copy link
Copy Markdown
Owner

sbooth commented May 25, 2026

I took a closer look at the file's structure and the second metadata block (at file offset 42) has an illegal header. The first bit is 0 indicating the metadata block is not the last one and the remaining 7 bits encode the block type which is 127- an illegal value. I believe metaflac fails because of this block although the stream decoder happily skips it so this isn't the cause of the decoding issue.

I haven't worked through the FLAC frames to see what causes the loss of sync after 14078484 frames. According to the stream info block the file consists of 14078484 frames so perhaps there is trailing garbage at the end of the file.

Your suggestion to limit decoding to the number of samples contained in the stream info block (if non-zero) is an interesting one. Another option I'm investigating is whether errors reported through calls to handleFLACError should cause decoding to stop.

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.

2 participants