add read callback to OggFLAC__SeekableStreamEncoder, necessary for metadata rewriting
authorJosh Coalson <jcoalson@users.sourceforce.net>
Sat, 17 Jan 2004 03:52:59 +0000 (03:52 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Sat, 17 Jan 2004 03:52:59 +0000 (03:52 +0000)
15 files changed:
include/OggFLAC++/encoder.h
include/OggFLAC/seekable_stream_encoder.h
src/libOggFLAC++/seekable_stream_encoder.cpp
src/libOggFLAC/Makefile.am
src/libOggFLAC/Makefile.lite
src/libOggFLAC/file_encoder.c
src/libOggFLAC/include/private/Makefile.am
src/libOggFLAC/include/private/all.h
src/libOggFLAC/include/private/ogg_helper.h [new file with mode: 0644]
src/libOggFLAC/libOggFLAC_dynamic.dsp
src/libOggFLAC/libOggFLAC_static.dsp
src/libOggFLAC/ogg_helper.c [new file with mode: 0644]
src/libOggFLAC/seekable_stream_encoder.c
src/test_libOggFLAC++/encoders.cpp
src/test_libOggFLAC/encoders.c

index 4d26569..7123b0d 100644 (file)
@@ -258,6 +258,7 @@ namespace OggFLAC {
                        bool process(const FLAC__int32 * const buffer[], unsigned samples);
                        bool process_interleaved(const FLAC__int32 buffer[], unsigned samples);
                protected:
+                       virtual ::OggFLAC__SeekableStreamEncoderReadStatus read_callback(FLAC__byte buffer[], unsigned *bytes) = 0;
                        virtual ::FLAC__SeekableStreamEncoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset) = 0;
                        virtual ::FLAC__SeekableStreamEncoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset) = 0;
                        virtual ::FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame) = 0;
@@ -268,6 +269,7 @@ namespace OggFLAC {
 #endif
                        ::OggFLAC__SeekableStreamEncoder *encoder_;
                private:
+                       static ::OggFLAC__SeekableStreamEncoderReadStatus read_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
                        static ::FLAC__SeekableStreamEncoderSeekStatus seek_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data);
                        static ::FLAC__SeekableStreamEncoderTellStatus tell_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
                        static ::FLAC__StreamEncoderWriteStatus write_callback_(const OggFLAC__SeekableStreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
index 768b6a0..d72807c 100644 (file)
@@ -131,6 +131,29 @@ typedef enum {
 extern OggFLAC_API const char * const OggFLAC__SeekableStreamEncoderStateString[];
 
 
+/** Return values for the OggFLAC__SeekableStreamEncoder read callback.
+ */
+typedef enum {
+
+       OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE,
+       /**< The read was OK and decoding can continue. */
+
+       OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_END_OF_STREAM,
+       /**< The read was attempted at the end of the stream. */
+
+       OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_ABORT
+       /**< An unrecoverable error occurred. */
+
+} OggFLAC__SeekableStreamEncoderReadStatus;
+
+/** Maps a OggFLAC__SeekableStreamEncoderReadStatus to a C string.
+ *
+ *  Using a OggFLAC__SeekableStreamEncoderReadStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern OggFLAC_API const char * const OggFLAC__SeekableStreamEncoderReadStatusString[];
+
+
 /***********************************************************************
  *
  * class OggFLAC__StreamEncoder
@@ -148,6 +171,25 @@ typedef struct {
        struct OggFLAC__SeekableStreamEncoderPrivate *private_; /* avoid the C++ keyword 'private' */
 } OggFLAC__SeekableStreamEncoder;
 
+/** Signature for the read callback.
+ *  See OggFLAC__seekable_stream_encoder_set_read_callback() for more info.
+ *
+ * \param  encoder  The encoder instance calling the callback.
+ * \param  buffer   A pointer to a location for the callee to store
+ *                  data to be encoded.
+ * \param  bytes    A pointer to the size of the buffer.  On entry
+ *                  to the callback, it contains the maximum number
+ *                  of bytes that may be stored in \a buffer.  The
+ *                  callee must set it to the actual number of bytes
+ *                  stored (0 in case of error or end-of-stream) before
+ *                  returning.
+ * \param  client_data  The callee's client data set through
+ *                      OggFLAC__seekable_stream_encoder_set_client_data().
+ * \retval OggFLAC__SeekableStreamEncoderReadStatus
+ *    The callee's return status.
+ */
+typedef OggFLAC__SeekableStreamEncoderReadStatus (*OggFLAC__SeekableStreamEncoderReadCallback)(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
+
 /** Signature for the seek callback.
  *  See OggFLAC__seekable_stream_encoder_set_seek_callback()
  *  and FLAC__SeekableStreamEncoderSeekCallback for more info.
@@ -454,6 +496,30 @@ OggFLAC_API FLAC__bool OggFLAC__seekable_stream_encoder_set_total_samples_estima
  */
 OggFLAC_API FLAC__bool OggFLAC__seekable_stream_encoder_set_metadata(OggFLAC__SeekableStreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks);
 
+/** Set the read callback.
+ *  The supplied function will be called when the encoder needs to read back
+ *  encoded data.  This happens during the metadata callback, when the encoder
+ *  has to read, modify, and rewrite the metadata (e.g. seekpoints) gathered
+ *  while encoding.  The address of the buffer to be filled is supplied, along
+ *  with the number of bytes the buffer can hold.  The callback may choose to
+ *  supply less data and modify the byte count but must be careful not to
+ *  overflow the buffer.  The callback then returns a status code chosen from
+ *  OggFLAC__SeekableStreamEncoderReadStatus.
+ *
+ * \note
+ * The callback is mandatory and must be set before initialization.
+ *
+ * \default \c NULL
+ * \param  encoder  A encoder instance to set.
+ * \param  value    See above.
+ * \assert
+ *    \code encoder != NULL \endcode
+ *    \code value != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the encoder is already initialized, else \c true.
+ */
+OggFLAC_API FLAC__bool OggFLAC__seekable_stream_encoder_set_read_callback(OggFLAC__SeekableStreamEncoder *encoder, OggFLAC__SeekableStreamEncoderReadCallback value);
+
 /** Set the seek callback.
  *  The supplied function will be called when the encoder needs to seek
  *  the output stream.  The encoder will pass the absolute byte offset
index 320b586..794a086 100644 (file)
@@ -300,6 +300,7 @@ namespace OggFLAC {
                SeekableStream::State SeekableStream::init()
                {
                        FLAC__ASSERT(is_valid());
+                       ::OggFLAC__seekable_stream_encoder_set_read_callback(encoder_, read_callback_);
                        ::OggFLAC__seekable_stream_encoder_set_seek_callback(encoder_, seek_callback_);
                        ::OggFLAC__seekable_stream_encoder_set_tell_callback(encoder_, tell_callback_);
                        ::OggFLAC__seekable_stream_encoder_set_write_callback(encoder_, write_callback_);
@@ -325,6 +326,15 @@ namespace OggFLAC {
                        return (bool)::OggFLAC__seekable_stream_encoder_process_interleaved(encoder_, buffer, samples);
                }
 
+               ::OggFLAC__SeekableStreamEncoderReadStatus SeekableStream::read_callback_(const ::OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+               {
+                       (void)encoder;
+                       FLAC__ASSERT(0 != client_data);
+                       SeekableStream *instance = reinterpret_cast<SeekableStream *>(client_data);
+                       FLAC__ASSERT(0 != instance);
+                       return instance->read_callback(buffer, bytes);
+               }
+
                ::FLAC__SeekableStreamEncoderSeekStatus SeekableStream::seek_callback_(const ::OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)
                {
                        (void)encoder;
index 2397610..5b447d8 100644 (file)
@@ -54,6 +54,7 @@ libOggFLAC_la_SOURCES = \
        file_encoder.c \
        ogg_decoder_aspect.c \
        ogg_encoder_aspect.c \
+       ogg_helper.c \
        seekable_stream_decoder.c \
        seekable_stream_encoder.c \
        stream_decoder.c \
index 2f4e9a1..a287398 100644 (file)
@@ -52,6 +52,7 @@ SRCS_C = \
        file_encoder.c \
        ogg_decoder_aspect.c \
        ogg_encoder_aspect.c \
+       ogg_helper.c \
        seekable_stream_decoder.c \
        seekable_stream_encoder.c \
        stream_decoder.c \
index 8c0856b..dbf6688 100644 (file)
@@ -53,6 +53,7 @@ extern FLAC__bool OggFLAC__seekable_stream_encoder_disable_fixed_subframes(OggFL
 extern FLAC__bool OggFLAC__seekable_stream_encoder_disable_verbatim_subframes(OggFLAC__SeekableStreamEncoder *encoder, FLAC__bool value);
 
 static void set_defaults_(OggFLAC__FileEncoder *encoder);
+static OggFLAC__SeekableStreamEncoderReadStatus read_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
 static FLAC__SeekableStreamEncoderSeekStatus seek_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data);
 static FLAC__SeekableStreamEncoderTellStatus tell_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
 static FLAC__StreamEncoderWriteStatus write_callback_(const OggFLAC__SeekableStreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
@@ -181,6 +182,7 @@ OggFLAC_API OggFLAC__FileEncoderState OggFLAC__file_encoder_init(OggFLAC__FileEn
        encoder->private_->samples_written = 0;
        encoder->private_->frames_written = 0;
 
+       OggFLAC__seekable_stream_encoder_set_read_callback(encoder->private_->seekable_stream_encoder, read_callback_);
        OggFLAC__seekable_stream_encoder_set_seek_callback(encoder->private_->seekable_stream_encoder, seek_callback_);
        OggFLAC__seekable_stream_encoder_set_tell_callback(encoder->private_->seekable_stream_encoder, tell_callback_);
        OggFLAC__seekable_stream_encoder_set_write_callback(encoder->private_->seekable_stream_encoder, write_callback_);
@@ -717,6 +719,24 @@ void set_defaults_(OggFLAC__FileEncoder *encoder)
        encoder->private_->filename = 0;
 }
 
+OggFLAC__SeekableStreamEncoderReadStatus read_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+{
+       OggFLAC__FileEncoder *file_encoder = (OggFLAC__FileEncoder*)client_data;
+
+       (void)encoder;
+
+       FLAC__ASSERT(0 != file_encoder);
+
+       *bytes = (unsigned)fread(buffer, 1, *bytes, file_encoder->private_->file);
+       if (*bytes == 0) {
+               if (feof(file_encoder->private_->file))
+                       return OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_END_OF_STREAM;
+               else if (ferror(file_encoder->private_->file))
+                       return OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_ABORT;
+       }
+       return OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE;
+}
+
 FLAC__SeekableStreamEncoderSeekStatus seek_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)
 {
        OggFLAC__FileEncoder *file_encoder = (OggFLAC__FileEncoder*)client_data;
index 729a044..fe20b30 100644 (file)
@@ -31,4 +31,5 @@
 noinst_HEADERS = \
        all.h \
        ogg_decoder_aspect.h \
-       ogg_encoder_aspect.h
+       ogg_encoder_aspect.h \
+       ogg_helper.h
index 46397e4..fba11d3 100644 (file)
@@ -34,5 +34,6 @@
 
 #include "ogg_decoder_aspect.h"
 #include "ogg_encoder_aspect.h"
+#include "ogg_helper.h"
 
 #endif
diff --git a/src/libOggFLAC/include/private/ogg_helper.h b/src/libOggFLAC/include/private/ogg_helper.h
new file mode 100644 (file)
index 0000000..28ed547
--- /dev/null
@@ -0,0 +1,43 @@
+/* libOggFLAC - Free Lossless Audio Codec + Ogg library
+ * Copyright (C) 2002,2003  Josh Coalson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OggFLAC__PRIVATE__OGG_HELPER_H
+#define OggFLAC__PRIVATE__OGG_HELPER_H
+
+#include <ogg/ogg.h>
+#include "OggFLAC/seekable_stream_encoder.h" /* for OggFLAC__SeekableStreamEncoder */
+
+void simple_ogg_page__init(ogg_page *page);
+void simple_ogg_page__clear(ogg_page *page);
+FLAC__bool simple_ogg_page__get_at(OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, OggFLAC__SeekableStreamEncoderSeekCallback seek_callback, OggFLAC__SeekableStreamEncoderReadCallback read_callback, void *client_data);
+FLAC__bool simple_ogg_page__set_at(OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, OggFLAC__SeekableStreamEncoderSeekCallback seek_callback, OggFLAC__SeekableStreamEncoderWriteCallback write_callback, void *client_data);
+
+#endif
index d77563c..0d3a854 100644 (file)
@@ -108,6 +108,10 @@ SOURCE=.\ogg_encoder_aspect.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\ogg_helper.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\seekable_stream_decoder.c\r
 # End Source File\r
 # Begin Source File\r
@@ -138,6 +142,10 @@ SOURCE=.\include\private\ogg_decoder_aspect.h
 \r
 SOURCE=.\include\private\ogg_encoder_aspect.h\r
 # End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\include\private\ogg_helper.h\r
+# End Source File\r
 # End Group\r
 # Begin Group "Protected Header Files"\r
 \r
index db06fc7..8f0afdc 100644 (file)
@@ -101,6 +101,10 @@ SOURCE=.\ogg_encoder_aspect.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\ogg_helper.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\seekable_stream_decoder.c\r
 # End Source File\r
 # Begin Source File\r
@@ -131,6 +135,10 @@ SOURCE=.\include\private\ogg_decoder_aspect.h
 \r
 SOURCE=.\include\private\ogg_encoder_aspect.h\r
 # End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\include\private\ogg_helper.h\r
+# End Source File\r
 # End Group\r
 # Begin Group "Protected Header Files"\r
 \r
diff --git a/src/libOggFLAC/ogg_helper.c b/src/libOggFLAC/ogg_helper.c
new file mode 100644 (file)
index 0000000..fea73a5
--- /dev/null
@@ -0,0 +1,189 @@
+/* libOggFLAC - Free Lossless Audio Codec + Ogg library
+ * Copyright (C) 2002,2003  Josh Coalson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h> /* for malloc() */
+#include <string.h> /* for memcmp(), memcpy() */
+#include "FLAC/assert.h"
+#include "private/ogg_helper.h"
+#include "protected/seekable_stream_encoder.h"
+
+
+static FLAC__bool full_read_(OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte *buffer, unsigned bytes, OggFLAC__SeekableStreamEncoderReadCallback read_callback, void *client_data)
+{
+       while(bytes > 0) {
+               unsigned bytes_read = bytes;
+               switch(read_callback(encoder, buffer, &bytes_read, client_data)) {
+                       case OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE:
+                               bytes -= bytes_read;
+                               buffer += bytes_read;
+                               break;
+                       case OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_END_OF_STREAM:
+                               if(bytes_read == 0) {
+                                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
+                                       return false;
+                               }
+                               bytes -= bytes_read;
+                               buffer += bytes_read;
+                               break;
+                       case OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_ABORT:
+                               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR;
+                               return false;
+                       default:
+                               /* double protection: */
+                               FLAC__ASSERT(0);
+                               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR;
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+void simple_ogg_page__init(ogg_page *page)
+{
+       page->header = 0;
+       page->header_len = 0;
+       page->body = 0;
+       page->body_len = 0;
+}
+
+void simple_ogg_page__clear(ogg_page *page)
+{
+       if(page->header)
+               free(page->header);
+       if(page->body)
+               free(page->body);
+       simple_ogg_page__init(page);
+}
+
+FLAC__bool simple_ogg_page__get_at(OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, OggFLAC__SeekableStreamEncoderSeekCallback seek_callback, OggFLAC__SeekableStreamEncoderReadCallback read_callback, void *client_data)
+{
+       static const unsigned OGG_HEADER_FIXED_PORTION_LEN = 27;
+       static const unsigned OGG_MAX_HEADER_LEN = 27/*OGG_HEADER_FIXED_PORTION_LEN*/ + 255;
+       FLAC__byte crc[4];
+
+       FLAC__ASSERT(page->header = 0);
+       FLAC__ASSERT(page->header_len = 0);
+       FLAC__ASSERT(page->body = 0);
+       FLAC__ASSERT(page->body_len = 0);
+
+       /* move the stream pointer to the supposed beginning of the page */
+       if(seek_callback(encoder, position, client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
+               return false;
+       }
+
+       /* allocate space for the page header */
+       if(0 == (page->header = malloc(OGG_MAX_HEADER_LEN))) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+               return false;
+       }
+
+       /* read in the fixed part of the page header (up to but not including
+        * the segment table */
+       if(!full_read_(encoder, page->header, OGG_HEADER_FIXED_PORTION_LEN, read_callback, client_data))
+               return false;
+
+       page->header_len = OGG_HEADER_FIXED_PORTION_LEN + page->header[26];
+
+       /* check to see if it's a correct, "simple" page (one packet only) */
+       if(
+               memcmp(page->header, "OggS", 4) ||               /* doesn't start with OggS */
+               (page->header[5] & 0x01) ||                      /* continued packet */
+               memcmp(page->header+6, "\0\0\0\0\0\0\0\0", 8) || /* granulepos is non-zero */
+               page->header[26] == 0                            /* packet is 0-size */
+       ) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
+               return false;
+       }
+
+       /* read in the segment table */
+       if(!full_read_(encoder, page->header + OGG_HEADER_FIXED_PORTION_LEN, page->header[26], read_callback, client_data))
+               return false;
+
+       {
+               unsigned i;
+
+               /* check to see that it specifies a single packet */
+               for(i = 0; i < (unsigned)page->header[26] - 1; i++) {
+                       if(page->header[i + OGG_HEADER_FIXED_PORTION_LEN] != 255) {
+                               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
+                               return false;
+                       }
+               }
+
+               page->body_len = 255 * i + page->header[i];
+       }
+
+       /* allocate space for the page body */
+       if(0 == (page->body = malloc(page->body_len))) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+               return false;
+       }
+
+       /* read in the page body */
+       if(!full_read_(encoder, page->body, page->body_len, read_callback, client_data))
+               return false;
+
+       /* check the CRC */
+       memcpy(crc, page->header+22, 4);
+       ogg_page_checksum_set(page);
+       if(memcmp(crc, page->header+22, 4)) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
+               return false;
+       }
+
+       return true;
+}
+
+FLAC__bool simple_ogg_page__set_at(OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, OggFLAC__SeekableStreamEncoderSeekCallback seek_callback, OggFLAC__SeekableStreamEncoderWriteCallback write_callback, void *client_data)
+{
+       FLAC__ASSERT(page->header != 0);
+       FLAC__ASSERT(page->header_len != 0);
+       FLAC__ASSERT(page->body != 0);
+       FLAC__ASSERT(page->body_len != 0);
+
+       /* move the stream pointer to the supposed beginning of the page */
+       if(seek_callback(encoder, position, client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
+               return false;
+       }
+
+       ogg_page_checksum_set(page);
+
+       /* re-write the page */
+       if(write_callback(encoder, page->header, page->header_len, 0, 0, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+               encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR;
+               return false;
+       }
+
+       return true;
+}
index b5acb7c..87e054a 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h> /* for calloc() */
+#include <string.h> /* for memcpy() */
 #include "FLAC/assert.h"
 #include "OggFLAC/seekable_stream_encoder.h"
 #include "protected/seekable_stream_encoder.h"
+#include "private/ogg_helper.h"
 
 #ifdef max
 #undef max
@@ -63,6 +65,7 @@ static void metadata_callback_(const FLAC__StreamEncoder *encoder, const FLAC__S
  ***********************************************************************/
 
 typedef struct OggFLAC__SeekableStreamEncoderPrivate {
+       OggFLAC__SeekableStreamEncoderReadCallback read_callback;
        OggFLAC__SeekableStreamEncoderSeekCallback seek_callback;
        OggFLAC__SeekableStreamEncoderTellCallback tell_callback;
        OggFLAC__SeekableStreamEncoderWriteCallback write_callback;
@@ -88,6 +91,7 @@ OggFLAC_API const char * const OggFLAC__SeekableStreamEncoderStateString[] = {
        "OggFLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR",
        "OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR",
        "OggFLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR",
+       "OggFLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR",
        "OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR",
        "OggFLAC__SEEKABLE_STREAM_ENCODER_TELL_ERROR",
        "OggFLAC__SEEKABLE_STREAM_ENCODER_ALREADY_INITIALIZED",
@@ -96,6 +100,12 @@ OggFLAC_API const char * const OggFLAC__SeekableStreamEncoderStateString[] = {
        "OggFLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED"
 };
 
+OggFLAC_API const char * const OggFLAC__SeekableStreamEncoderReadStatusString[] = {
+       "OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE",
+       "OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_END_OF_STREAM",
+       "OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_ABORT"
+};
+
 
 /***********************************************************************
  *
@@ -444,6 +454,18 @@ OggFLAC_API FLAC__bool OggFLAC__seekable_stream_encoder_set_metadata(OggFLAC__Se
        return FLAC__stream_encoder_set_metadata(encoder->private_->FLAC_stream_encoder, metadata, num_blocks);
 }
 
+OggFLAC_API FLAC__bool OggFLAC__seekable_stream_encoder_set_read_callback(OggFLAC__SeekableStreamEncoder *encoder, OggFLAC__SeekableStreamEncoderReadCallback value)
+{
+       FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
+       FLAC__ASSERT(0 != value);
+       if(encoder->protected_->state != OggFLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED)
+               return false;
+       encoder->private_->read_callback = value;
+       return true;
+}
+
 OggFLAC_API FLAC__bool OggFLAC__seekable_stream_encoder_set_seek_callback(OggFLAC__SeekableStreamEncoder *encoder, OggFLAC__SeekableStreamEncoderSeekCallback value)
 {
        FLAC__ASSERT(0 != encoder);
@@ -850,7 +872,7 @@ void metadata_callback_(const FLAC__StreamEncoder *unused, const FLAC__StreamMet
        const FLAC__uint64 samples = metadata->data.stream_info.total_samples;
        const unsigned min_framesize = metadata->data.stream_info.min_framesize;
        const unsigned max_framesize = metadata->data.stream_info.max_framesize;
-       const unsigned bps = metadata->data.stream_info.bits_per_sample;
+       ogg_page page;
 
        FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO);
 
@@ -861,7 +883,6 @@ void metadata_callback_(const FLAC__StreamEncoder *unused, const FLAC__StreamMet
 
        (void)unused; /* silence compiler warning about unused parameter */
        FLAC__ASSERT(encoder->private_->FLAC_stream_encoder == unused);
-return;//@@@@@@
 
        /*@@@ reopen callback here?  The docs currently require user to open files in update mode from the start */
 
@@ -871,89 +892,85 @@ return;//@@@@@@
         */
 
        /*
-        * Write MD5 signature
+        * Write STREAMINFO stats
+        */
+       simple_ogg_page__init(&page);
+       if(!simple_ogg_page__get_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data))
+               return; /* state already set */
+       /*
+        * MD5 signature
         */
        {
                const unsigned md5_offset =
-               FLAC__STREAM_METADATA_HEADER_LENGTH +
-               (
-                       FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN
-               ) / 8;
-
-               if(encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + md5_offset, encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
-                       return;
-               }
-               if(encoder->private_->write_callback(encoder, metadata->data.stream_info.md5sum, 16, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR;
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN
+                       ) / 8;
+
+               if(md5_offset + 16 > (unsigned)page.body_len) {
+                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
                        return;
                }
+               memcpy(page.body + md5_offset, metadata->data.stream_info.md5sum, 16);
        }
-
        /*
-        * Write total samples
+        * total samples
         */
        {
                const unsigned total_samples_byte_offset =
-               FLAC__STREAM_METADATA_HEADER_LENGTH +
-               (
-                       FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN
-                       - 4
-               ) / 8;
-
-               b[0] = ((FLAC__byte)(bps-1) << 4) | (FLAC__byte)((samples >> 32) & 0x0F);
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN
+                               - 4
+                       ) / 8;
+
+               if(total_samples_byte_offset + 5 > (unsigned)page.body_len) {
+                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
+                       return;
+               }
+               b[0] = (FLAC__byte)page.body[total_samples_byte_offset] & 0xF0;
+               b[0] |= (FLAC__byte)((samples >> 32) & 0x0F);
                b[1] = (FLAC__byte)((samples >> 24) & 0xFF);
                b[2] = (FLAC__byte)((samples >> 16) & 0xFF);
                b[3] = (FLAC__byte)((samples >> 8) & 0xFF);
                b[4] = (FLAC__byte)(samples & 0xFF);
-               if(encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + total_samples_byte_offset, encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
-                       return;
-               }
-               if(encoder->private_->write_callback(encoder, b, 5, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR;
-                       return;
-               }
+               memcpy(page.body + total_samples_byte_offset, b, 5);
        }
-
        /*
-        * Write min/max framesize
+        * min/max framesize
         */
        {
                const unsigned min_framesize_offset =
-               FLAC__STREAM_METADATA_HEADER_LENGTH +
-               (
-                       FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
-                       FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN
-               ) / 8;
-
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN
+                       ) / 8;
+
+               if(min_framesize_offset + 6 > (unsigned)page.body_len) {
+                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
+                       return;
+               }
                b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF);
                b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF);
                b[2] = (FLAC__byte)(min_framesize & 0xFF);
                b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF);
                b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF);
                b[5] = (FLAC__byte)(max_framesize & 0xFF);
-               if(encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + min_framesize_offset, encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
-                       return;
-               }
-               if(encoder->private_->write_callback(encoder, b, 6, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR;
-                       return;
-               }
+               memcpy(page.body + min_framesize_offset, b, 6);
        }
 
        /*
@@ -961,17 +978,22 @@ return;//@@@@@@
         */
        if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) {
                unsigned i;
+               FLAC__byte *p;
 
                FLAC__format_seektable_sort(encoder->private_->seek_table);
 
                FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table));
 
-               if(encoder->private_->seek_callback(encoder, encoder->protected_->seektable_offset + FLAC__STREAM_METADATA_HEADER_LENGTH, encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) {
-                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR;
+               simple_ogg_page__init(&page);
+               if(!simple_ogg_page__get_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data))
+                       return; /* state already set */
+
+               if(FLAC__STREAM_METADATA_HEADER_LENGTH + (18*encoder->private_->seek_table->num_points) > (unsigned)page.body_len) {
+                       encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_OGG_ERROR;
                        return;
                }
 
-               for(i = 0; i < encoder->private_->seek_table->num_points; i++) {
+               for(i = 0, p = page.body + FLAC__STREAM_METADATA_HEADER_LENGTH; i < encoder->private_->seek_table->num_points; i++, p += 18) {
                        FLAC__uint64 xx;
                        unsigned x;
                        xx = encoder->private_->seek_table->points[i].sample_number;
@@ -999,6 +1021,11 @@ return;//@@@@@@
                                encoder->protected_->state = OggFLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR;
                                return;
                        }
+                       memcpy(p, b, 18);
                }
+
+               if(!simple_ogg_page__set_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data))
+                       return; /* state already set */
+               simple_ogg_page__clear(&page);
        }
 }
index b68bd13..8926c94 100644 (file)
@@ -387,6 +387,7 @@ public:
        ~SeekableStreamEncoder() { }
 
        // from OggFLAC::Encoder::SeekableStream
+       ::OggFLAC__SeekableStreamEncoderReadStatus read_callback(FLAC__byte buffer[], unsigned *bytes);
        ::FLAC__SeekableStreamEncoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset);
        ::FLAC__SeekableStreamEncoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset);
        ::FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame);
@@ -394,6 +395,13 @@ public:
        bool die(const char *msg = 0) const;
 };
 
+::OggFLAC__SeekableStreamEncoderReadStatus SeekableStreamEncoder::read_callback(FLAC__byte buffer[], unsigned *bytes)
+{
+       (void)buffer, (void)bytes;
+
+       return ::OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE;
+}
+
 ::FLAC__SeekableStreamEncoderSeekStatus SeekableStreamEncoder::seek_callback(FLAC__uint64 absolute_byte_offset)
 {
        (void)absolute_byte_offset;
index d8f4546..c6d0c69 100644 (file)
@@ -431,6 +431,12 @@ static FLAC__bool test_stream_encoder()
        return true;
 }
 
+OggFLAC__SeekableStreamEncoderReadStatus seekable_stream_encoder_read_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+{
+       (void)encoder, (void)buffer, (void)bytes, (void)client_data;
+       return OggFLAC__SEEKABLE_STREAM_ENCODER_READ_STATUS_CONTINUE;
+}
+
 FLAC__SeekableStreamEncoderSeekStatus seekable_stream_encoder_seek_callback_(const OggFLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)
 {
        (void)encoder, (void)absolute_byte_offset, (void)client_data;
@@ -567,6 +573,11 @@ static FLAC__bool test_seekable_stream_encoder()
                return die_ss_("returned false", encoder);
        printf("OK\n");
 
+       printf("testing OggFLAC__seekable_stream_encoder_set_read_callback()... ");
+       if(!OggFLAC__seekable_stream_encoder_set_read_callback(encoder, seekable_stream_encoder_read_callback_))
+               return die_ss_("returned false", encoder);
+       printf("OK\n");
+
        printf("testing OggFLAC__seekable_stream_encoder_set_seek_callback()... ");
        if(!OggFLAC__seekable_stream_encoder_set_seek_callback(encoder, seekable_stream_encoder_seek_callback_))
                return die_ss_("returned false", encoder);