don't just pass 0 for samples in Ogg/FLAC callback

This is a fix for https://github.com/xiph/flac/issues/661
This commit is contained in:
Jesper Larsson 2024-08-07 08:52:38 +02:00 committed by Martijn van Beurden
parent 8e6498d44d
commit 1880f21066
3 changed files with 21 additions and 8 deletions

View File

@ -538,11 +538,16 @@ typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const F
* callback is being called to write metadata. * callback is being called to write metadata.
* *
* \note * \note
* Unlike when writing to native FLAC, when writing to Ogg FLAC the * Unlike when writing to native FLAC, when writing to Ogg FLAC the write
* write callback will be called twice when writing each audio * callback will be called at least twice when writing each audio frame; once
* frame; once for the page header, and once for the page body. * for the page header and once for the page body, possibly repeating this
* When writing the page header, the \a samples argument to the * pair of calls several times in a batch with the same value of
* write callback will be \c 0. * \a current_frame. When writing the page header, as well as in all but the
* first page body write of the batch, the \a samples argument to the write
* callback will be \c 0. The full number of samples of the batch will be
* passed in the first page body write. Furthermore, it is possible that a few
* of these samples are retained in an internal Ogg encoding buffer and not
* actually encoded until the next batch.
* *
* \note In general, FLAC__StreamEncoder functions which change the * \note In general, FLAC__StreamEncoder functions which change the
* state should not be called on the \a encoder while in the callback. * state should not be called on the \a encoder while in the callback.

View File

@ -49,6 +49,7 @@ typedef struct FLAC__OggEncoderAspect {
FLAC__bool seen_magic; /* true if we've seen the fLaC magic in the write callback yet */ FLAC__bool seen_magic; /* true if we've seen the fLaC magic in the write callback yet */
FLAC__bool is_first_packet; FLAC__bool is_first_packet;
FLAC__uint64 samples_written; FLAC__uint64 samples_written;
uint32_t samples_in_submit_buffer;
} FLAC__OggEncoderAspect; } FLAC__OggEncoderAspect;
void FLAC__ogg_encoder_aspect_set_serial_number(FLAC__OggEncoderAspect *aspect, long value); void FLAC__ogg_encoder_aspect_set_serial_number(FLAC__OggEncoderAspect *aspect, long value);

View File

@ -57,6 +57,7 @@ FLAC__bool FLAC__ogg_encoder_aspect_init(FLAC__OggEncoderAspect *aspect)
aspect->seen_magic = false; aspect->seen_magic = false;
aspect->is_first_packet = true; aspect->is_first_packet = true;
aspect->samples_written = 0; aspect->samples_written = 0;
aspect->samples_in_submit_buffer = 0;
return true; return true;
} }
@ -192,21 +193,27 @@ FLAC__StreamEncoderWriteStatus FLAC__ogg_encoder_aspect_write_callback_wrapper(F
if(ogg_stream_packetin(&aspect->stream_state, &packet) != 0) if(ogg_stream_packetin(&aspect->stream_state, &packet) != 0)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
/*@@@ can't figure out a way to pass a useful number for 'samples' to the write_callback, so we'll just pass 0 */ /* For a batch of write_callback calls associated with the same current_frame, pass the number of samples in the
* first non-metadata page body call, and then set to zero in case there are more iterations of the while loop (so
* as not to give the impression of more samples being processed).
*/
aspect->samples_in_submit_buffer += samples;
if(is_metadata) { if(is_metadata) {
while(ogg_stream_flush(&aspect->stream_state, &aspect->page) != 0) { while(ogg_stream_flush(&aspect->stream_state, &aspect->page) != 0) {
if(write_callback(encoder, aspect->page.header, aspect->page.header_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) if(write_callback(encoder, aspect->page.header, aspect->page.header_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
if(write_callback(encoder, aspect->page.body, aspect->page.body_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) if(write_callback(encoder, aspect->page.body, aspect->page.body_len, aspect->samples_in_submit_buffer, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
aspect->samples_in_submit_buffer = 0;
} }
} }
else { else {
while(ogg_stream_pageout(&aspect->stream_state, &aspect->page) != 0) { while(ogg_stream_pageout(&aspect->stream_state, &aspect->page) != 0) {
if(write_callback(encoder, aspect->page.header, aspect->page.header_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) if(write_callback(encoder, aspect->page.header, aspect->page.header_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
if(write_callback(encoder, aspect->page.body, aspect->page.body_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) if(write_callback(encoder, aspect->page.body, aspect->page.body_len, aspect->samples_in_submit_buffer, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
aspect->samples_in_submit_buffer = 0;
} }
} }
} }