From e6d52b08ba7986b9a51adf64209d050f7b6b9768 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Sat, 17 Jan 2004 03:52:59 +0000 Subject: [PATCH] add read callback to OggFLAC__SeekableStreamEncoder, necessary for metadata rewriting --- include/OggFLAC++/encoder.h | 2 + include/OggFLAC/seekable_stream_encoder.h | 66 ++++++++++ src/libOggFLAC++/seekable_stream_encoder.cpp | 10 ++ src/libOggFLAC/Makefile.am | 1 + src/libOggFLAC/Makefile.lite | 1 + src/libOggFLAC/file_encoder.c | 20 +++ src/libOggFLAC/include/private/Makefile.am | 3 +- src/libOggFLAC/include/private/all.h | 1 + src/libOggFLAC/include/private/ogg_helper.h | 43 ++++++ src/libOggFLAC/libOggFLAC_dynamic.dsp | 8 ++ src/libOggFLAC/libOggFLAC_static.dsp | 8 ++ src/libOggFLAC/ogg_helper.c | 189 +++++++++++++++++++++++++++ src/libOggFLAC/seekable_stream_encoder.c | 153 +++++++++++++--------- src/test_libOggFLAC++/encoders.cpp | 8 ++ src/test_libOggFLAC/encoders.c | 11 ++ 15 files changed, 460 insertions(+), 64 deletions(-) create mode 100644 src/libOggFLAC/include/private/ogg_helper.h create mode 100644 src/libOggFLAC/ogg_helper.c diff --git a/include/OggFLAC++/encoder.h b/include/OggFLAC++/encoder.h index 4d26569..7123b0d 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 768b6a0..d72807c 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 320b586..794a086 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(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 2397610..5b447d8 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 2f4e9a1..a287398 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 8c0856b..dbf6688 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 729a044..fe20b30 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 46397e4..fba11d3 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 0000000..28ed547 --- /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 +#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 d77563c..0d3a854 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 db06fc7..8f0afdc 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 0000000..fea73a5 --- /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 /* for malloc() */ +#include /* 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 b5acb7c..87e054a 100644 --- a/src/libOggFLAC/seekable_stream_encoder.c +++ b/src/libOggFLAC/seekable_stream_encoder.c @@ -31,9 +31,11 @@ #include #include /* for calloc() */ +#include /* 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 b68bd13..8926c94 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 d8f4546..c6d0c69 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); -- 2.7.4