fix FLAC->Ogg mapping to merge fLaC magic with the STREAMINFO in the first Ogg packet

This commit is contained in:
Josh Coalson 2004-08-20 22:09:02 +00:00
parent 9ea8a53de2
commit 55ddee58f0
2 changed files with 88 additions and 41 deletions

View File

@ -44,6 +44,7 @@ typedef struct OggFLAC__OggEncoderAspect {
/* these are for internal state related to Ogg encoding */
ogg_stream_state stream_state;
ogg_page page;
FLAC__bool seen_magic; /* true if we've seen the fLaC magic in the write callback yet */
FLAC__bool is_first_packet;
FLAC__uint64 samples_written;
} OggFLAC__OggEncoderAspect;

View File

@ -45,6 +45,7 @@ FLAC__bool OggFLAC__ogg_encoder_aspect_init(OggFLAC__OggEncoderAspect *aspect)
if(ogg_stream_init(&aspect->stream_state, aspect->serial_number) != 0)
return false;
aspect->seen_magic = false;
aspect->is_first_packet = true;
aspect->samples_written = 0;
@ -67,6 +68,23 @@ void OggFLAC__ogg_encoder_aspect_set_defaults(OggFLAC__OggEncoderAspect *aspect)
aspect->serial_number = 0;
}
/*
* The basic FLAC -> Ogg mapping goes like this:
*
* - 'fLaC' magic and STREAMINFO block get combined into the first
* packet
* - the first packet is flushed to the first page
* - each subsequent metadata block goes into its own packet
* - each metadata packet is flushed to page (this is not required,
* the mapping only requires that a flush must occur after all
* metadata is written)
* - each subsequent FLAC audio frame goes into its own packet.
*
* WATCHOUT:
* This depends on the behavior of FLAC__StreamEncoder that we get a
* separate write callback for the fLaC magic, and then separate write
* callbacks for each metadata block and audio frame.
*/
FLAC__StreamEncoderWriteStatus OggFLAC__ogg_encoder_aspect_write_callback_wrapper(OggFLAC__OggEncoderAspect *aspect, const FLAC__uint64 total_samples_estimate, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, OggFLAC__OggEncoderAspectWriteCallbackProxy write_callback, void *encoder, void *client_data)
{
/* WATCHOUT:
@ -74,49 +92,77 @@ FLAC__StreamEncoderWriteStatus OggFLAC__ogg_encoder_aspect_write_callback_wrappe
* will be 0 for metadata writes.
*/
const FLAC__bool is_metadata = (samples == 0);
ogg_packet packet;
/*
* Treat fLaC magic packet specially. We will note when we see it, then
* wait until we get the STREAMINFO and prepend it in that packet
*/
if(aspect->seen_magic) {
ogg_packet packet;
memset(&packet, 0, sizeof(packet));
packet.granulepos = aspect->samples_written + samples;
if(aspect->is_first_packet) {
FLAC__byte newbuffer[FLAC__STREAM_SYNC_LENGTH + FLAC__STREAM_METADATA_HEADER_LENGTH + FLAC__STREAM_METADATA_STREAMINFO_LENGTH];
if(bytes != FLAC__STREAM_METADATA_HEADER_LENGTH + FLAC__STREAM_METADATA_STREAMINFO_LENGTH) {
/*
* If we get here, our assumption about the way write callbacks happen
* explained above is wrong
*/
FLAC__ASSERT(0);
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
memcpy(newbuffer, FLAC__STREAM_SYNC_STRING, FLAC__STREAM_SYNC_LENGTH);
memcpy(newbuffer + FLAC__STREAM_SYNC_LENGTH, buffer, bytes);
packet.packet = (unsigned char *)newbuffer;
packet.bytes = sizeof(newbuffer);
packet.b_o_s = 1;
aspect->is_first_packet = false;
}
else {
packet.packet = (unsigned char *)buffer;
packet.bytes = bytes;
}
if(total_samples_estimate > 0 && total_samples_estimate == aspect->samples_written + samples)
packet.e_o_s = 1;
if(ogg_stream_packetin(&aspect->stream_state, &packet) != 0)
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 */
if(is_metadata) {
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)
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)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
}
else {
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)
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)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
}
}
else if(is_metadata && current_frame == 0 && samples == 0 && bytes == 4 && 0 == memcmp(buffer, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING))) {
aspect->seen_magic = true;
}
else {
/*
* If we get here, our assumption about the way write callbacks happen
* explained above is wrong
*/
FLAC__ASSERT(0);
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
aspect->samples_written += samples;
memset(&packet, 0, sizeof(packet));
packet.packet = (unsigned char *)buffer;
packet.granulepos = aspect->samples_written;
packet.bytes = bytes;
if(aspect->is_first_packet) {
packet.b_o_s = 1;
aspect->is_first_packet = false;
}
if(total_samples_estimate > 0 && total_samples_estimate == aspect->samples_written)
packet.e_o_s = 1;
if(ogg_stream_packetin(&aspect->stream_state, &packet) != 0)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
/*
* For the initial fLaC header and metadata blocks, we will try and
* force them to all be on their own page.
*
* For audio frames, we let Ogg do the paging.
*/
/*@@@@@@ can't figure out a way to pass a useful number for 'samples' to the write_callback, so we'll just pass 0 */
if (is_metadata) {
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)
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)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
}
else {
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)
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)
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
}
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}