fix FLAC->Ogg mapping to merge fLaC magic with the STREAMINFO in the first Ogg packet
authorJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 20 Aug 2004 22:09:02 +0000 (22:09 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 20 Aug 2004 22:09:02 +0000 (22:09 +0000)
src/libOggFLAC/include/private/ogg_encoder_aspect.h
src/libOggFLAC/ogg_encoder_aspect.c

index 53946df..0bf5390 100644 (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;
index 86b3653..38efc1e 100644 (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;
-
-       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.
+        * 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
         */
-       /*@@@@@@ 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)
+       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 {
-               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;
-               }
+               /*
+                * 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;
+
        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
 }