Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ogg chaining support? #254

Open
jprjr opened this issue Sep 22, 2021 · 6 comments
Open

Ogg chaining support? #254

jprjr opened this issue Sep 22, 2021 · 6 comments

Comments

@jprjr
Copy link

jprjr commented Sep 22, 2021

One feature of Ogg is so-called "chaining" - essentially concatenating multiple Ogg files together, and the player program attempts to play them as a continuous stream.

It seems like libFLAC does not support it - if I use flac --ogg to encapsulate the FLAC streams in Ogg files, then concatenate them together, most programs and tools just ignore everything after the first file. Example, assuming I have a folder of WAV files:

flag --ogg *.wav
cat *.oga > chained.oga
flac -d chained.oga
# the result "chained.wav" only contain's the first WAV file's audio

Most programs seem to support Vorbis chaining and Opus chaining, Ogg FLAC chaining would be a great addition. Are there any plans to support it?

@ziplantil
Copy link

ziplantil commented Oct 29, 2021

I second this. Fundamentally I think it's just about checking the OGG stream serial numbers in FLAC__ogg_decoder_aspect_read_callback_wrapper in ogg_decoder_aspect.c and, if the serial number suddenly changes (which should signal that a new file/stream has begun; maybe checking the granule position could be a better approach in case the serial number does not actually change?), handling that situation appropriately.

It seems that as things are right now, libFLAC (with FLAC__stream_decoder_init_ogg_stream) enters this mode where it doesn't report an error and will happily keep reading the stream, but will never call the write (nor really any other) callbacks, which is hardly an ideal behavior. For files, I'd imagine this behavior equates to only reading the first stream and ignoring the rest until the EOF.

@ktmf01
Copy link
Collaborator

ktmf01 commented Nov 4, 2021

I've never encountered a chained Ogg FLAC file, so could you perhaps tell something on where they're used?

Do the chains in these chained Ogg FLAC files usually have the same parameters such as bit-depth, samplerate and channels? For example, in TV streams, codecs sometimes change from stereo to multichannel from program to program. If this is something chained Ogg FLAC files are used for (fusing FLAC files with a different number of channels for example), extra care should be taken to test libFLAC with this.

edit: also, seeking (backwards) across chains is something to look into in depth, perhaps with extra tests

@ziplantil
Copy link

The use I have tested my PR with in practice has been to play back an Ogg FLAC Icecast stream i.e. online radio. In this circumstance, the PCM audio parameters have stayed constant as the radio decoder always decodes into the same PCM format (with constant bit depth, sample rate and channel count) before encoding it as Ogg FLAC. However, it is conceivable that these parameters could change from one Ogg stream to another.

The use for online streams has also precluded any need to support seeking which could indeed pose an issue if seeking over STREAMINFO blocks.

@jprjr
Copy link
Author

jprjr commented Nov 4, 2021

The main use case I've seen Vorbis and Opus chaining is Icecast internet radio, and similar to the use case from @ziplantil - they're usually a constant bit depth, sample rate, and channel count. It's possible they could change, but most programs I've tested Vorbis/Opus chaining with will do some kind of reset/pause/etc when that happens (it doesn't playback as a smooth, continuous stream).

@ktmf01
Copy link
Collaborator

ktmf01 commented Apr 15, 2022

Perhaps we should discuss this matter a little more.

One question that comes to mind is: which use cases concerning chained ogg files should (lib)FLAC cover?

  • Simple playback
  • Playback with forward seeking
  • Playback with full seeking
  • Transcoding to a non-chained file

I think that simple playback and transcoding should be possible. I haven't reviewed the patch in #261 yet, but from a cursory read it seems like a step in that direction

Adding seeking is much more difficult. Seeking in FLAC is simply guessing and trying some place logical, updating bounds and trying again. This method needs some way to reliably tell where in the stream the decoder has landed, and because each chain resets the frame or sample counter, this isn't possible when crossing a chain boundary. Furthermore, the decoder also doesn't know how long the stream is, because the streaminfo at the beginning of the first chain only tells about the length of the content in first chain.

Perhaps it is wise to add a catch to seeking: as soon as the decoder detects a ogg file is chained, it jumps back to the position from where it started seeking and disables seeking in that stream, throwing a warning or error.

Transcoding to a non-chained file is only possible when the sample rate, number of channels and bit depth stay the same or change exactly at a frame boundary.

P.S.: Concerning #261, I don't think explicitly enabling decoding of chained streams is necessary. I don't think it is desirable to keep the current behaviour, i.e. ignoring anything except the first chain, let alone to leave it default.

@chocolate42
Copy link

It's unfortunate that ungetc only guarantees one byte of pushback, otherwise with a minor change to libflac external programs like flac could implement basic chaining support with libflac relatively easily. I'll detail it anyway just in case there's a way to make it work.

  • Currently the in/out stream is closed in libflac regardless of who opened the stream. Instead, have libflac fclose only when libflac fopened. Seems logical that responsibility of resource management remains with whoever created the resource anyway. Beyond chaining it might also allow some exotic use of libflac that pipes data between processes or something, that maybe is hacky or impossible to do with stdin/stdout
  • When link is fully decoded, ungetc/seek to end of the link if libflac mistakenly read beyond the link

If the first point were implemented flac could easily encode a chain from multiple sources, if the second were also implemented decoding a chain either to multiple files or a single file is possible (but a single file output would have to error if samplerate/bps/channel_count changes, and probably only metadata from the first link could be kept). On decode flac would getc+ungetc to peek the stream to see if there's another link, decoding each link with a fresh decoder instance.

Given that ungetc cannot be relied on to do more than one byte of pushback, overread from unexpected end-of-link on decode can only be reversed by seeking. Unfortunate as chains are mostly used with online streams but better than nothing? Error if there's overread when seeking isn't possible, work otherwise?

Technically the fclose change is a breaking change, programs that deal with hundreds/thousands of files from a single process could run out of file descriptors if they're not updated to fclose themselves (by default they'll be closed on process exit/termination).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants