summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/OggFLAC++/encoder.h2
-rw-r--r--include/OggFLAC/seekable_stream_encoder.h66
-rw-r--r--src/libOggFLAC++/seekable_stream_encoder.cpp10
-rw-r--r--src/libOggFLAC/Makefile.am1
-rw-r--r--src/libOggFLAC/Makefile.lite1
-rw-r--r--src/libOggFLAC/file_encoder.c20
-rw-r--r--src/libOggFLAC/include/private/Makefile.am3
-rw-r--r--src/libOggFLAC/include/private/all.h1
-rw-r--r--src/libOggFLAC/include/private/ogg_helper.h43
-rw-r--r--src/libOggFLAC/libOggFLAC_dynamic.dsp8
-rw-r--r--src/libOggFLAC/libOggFLAC_static.dsp8
-rw-r--r--src/libOggFLAC/ogg_helper.c189
-rw-r--r--src/libOggFLAC/seekable_stream_encoder.c153
-rw-r--r--src/test_libOggFLAC++/encoders.cpp8
-rw-r--r--src/test_libOggFLAC/encoders.c11
15 files changed, 460 insertions, 64 deletions
diff --git a/include/OggFLAC++/encoder.h b/include/OggFLAC++/encoder.h
index 4d26569b..7123b0d7 100644
--- a/include/OggFLAC++/encoder.h
+++ b/include/OggFLAC++/encoder.h
@@ -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);
diff --git a/include/OggFLAC/seekable_stream_encoder.h b/include/OggFLAC/seekable_stream_encoder.h
index 768b6a05..d72807c1 100644
--- a/include/OggFLAC/seekable_stream_encoder.h
+++ b/include/OggFLAC/seekable_stream_encoder.h
@@ -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
diff --git a/src/libOggFLAC++/seekable_stream_encoder.cpp b/src/libOggFLAC++/seekable_stream_encoder.cpp
index 320b5865..794a0862 100644
--- a/src/libOggFLAC++/seekable_stream_encoder.cpp
+++ b/src/libOggFLAC++/seekable_stream_encoder.cpp
@@ -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;
diff --git a/src/libOggFLAC/Makefile.am b/src/libOggFLAC/Makefile.am
index 23976102..5b447d84 100644
--- a/src/libOggFLAC/Makefile.am
+++ b/src/libOggFLAC/Makefile.am
@@ -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 \
diff --git a/src/libOggFLAC/Makefile.lite b/src/libOggFLAC/Makefile.lite
index 2f4e9a1b..a287398d 100644
--- a/src/libOggFLAC/Makefile.lite
+++ b/src/libOggFLAC/Makefile.lite
@@ -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 \
diff --git a/src/libOggFLAC/file_encoder.c b/src/libOggFLAC/file_encoder.c
index 8c0856bc..dbf66889 100644
--- a/src/libOggFLAC/file_encoder.c
+++ b/src/libOggFLAC/file_encoder.c
@@ -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;
diff --git a/src/libOggFLAC/include/private/Makefile.am b/src/libOggFLAC/include/private/Makefile.am
index 729a0440..fe20b300 100644
--- a/src/libOggFLAC/include/private/Makefile.am
+++ b/src/libOggFLAC/include/private/Makefile.am
@@ -31,4 +31,5 @@
noinst_HEADERS = \
all.h \
ogg_decoder_aspect.h \
- ogg_encoder_aspect.h
+ ogg_encoder_aspect.h \
+ ogg_helper.h
diff --git a/src/libOggFLAC/include/private/all.h b/src/libOggFLAC/include/private/all.h
index 46397e4f..fba11d34 100644
--- a/src/libOggFLAC/include/private/all.h
+++ b/src/libOggFLAC/include/private/all.h
@@ -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
index 00000000..28ed547b
--- /dev/null
+++ b/src/libOggFLAC/include/private/ogg_helper.h
@@ -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
diff --git a/src/libOggFLAC/libOggFLAC_dynamic.dsp b/src/libOggFLAC/libOggFLAC_dynamic.dsp
index d77563c5..0d3a854f 100644
--- a/src/libOggFLAC/libOggFLAC_dynamic.dsp
+++ b/src/libOggFLAC/libOggFLAC_dynamic.dsp
@@ -108,6 +108,10 @@ SOURCE=.\ogg_encoder_aspect.c
# End Source File
# Begin Source File
+SOURCE=.\ogg_helper.c
+# End Source File
+# Begin Source File
+
SOURCE=.\seekable_stream_decoder.c
# End Source File
# Begin Source File
@@ -138,6 +142,10 @@ SOURCE=.\include\private\ogg_decoder_aspect.h
SOURCE=.\include\private\ogg_encoder_aspect.h
# End Source File
+# Begin Source File
+
+SOURCE=.\include\private\ogg_helper.h
+# End Source File
# End Group
# Begin Group "Protected Header Files"
diff --git a/src/libOggFLAC/libOggFLAC_static.dsp b/src/libOggFLAC/libOggFLAC_static.dsp
index db06fc74..8f0afdc9 100644
--- a/src/libOggFLAC/libOggFLAC_static.dsp
+++ b/src/libOggFLAC/libOggFLAC_static.dsp
@@ -101,6 +101,10 @@ SOURCE=.\ogg_encoder_aspect.c
# End Source File
# Begin Source File
+SOURCE=.\ogg_helper.c
+# End Source File
+# Begin Source File
+
SOURCE=.\seekable_stream_decoder.c
# End Source File
# Begin Source File
@@ -131,6 +135,10 @@ SOURCE=.\include\private\ogg_decoder_aspect.h
SOURCE=.\include\private\ogg_encoder_aspect.h
# End Source File
+# Begin Source File
+
+SOURCE=.\include\private\ogg_helper.h
+# End Source File
# End Group
# Begin Group "Protected Header Files"
diff --git a/src/libOggFLAC/ogg_helper.c b/src/libOggFLAC/ogg_helper.c
new file mode 100644
index 00000000..fea73a57
--- /dev/null
+++ b/src/libOggFLAC/ogg_helper.c
@@ -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;
+}
diff --git a/src/libOggFLAC/seekable_stream_encoder.c b/src/libOggFLAC/seekable_stream_encoder.c
index b5acb7c7..87e054ae 100644
--- a/src/libOggFLAC/seekable_stream_encoder.c
+++ b/src/libOggFLAC/seekable_stream_encoder.c
@@ -31,9 +31,11 @@
#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);
}
}
diff --git a/src/test_libOggFLAC++/encoders.cpp b/src/test_libOggFLAC++/encoders.cpp
index b68bd13b..8926c949 100644
--- a/src/test_libOggFLAC++/encoders.cpp
+++ b/src/test_libOggFLAC++/encoders.cpp
@@ -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;
diff --git a/src/test_libOggFLAC/encoders.c b/src/test_libOggFLAC/encoders.c
index d8f45461..c6d0c69a 100644
--- a/src/test_libOggFLAC/encoders.c
+++ b/src/test_libOggFLAC/encoders.c
@@ -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);