Commit e6d52b08 authored by Josh Coalson's avatar Josh Coalson
Browse files

add read callback to OggFLAC__SeekableStreamEncoder, necessary for metadata rewriting

parent 8fdb9648
......@@ -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);
......
......@@ -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
......
......@@ -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;
......
......@@ -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 \
......
......@@ -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 \
......
......@@ -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;
......
......@@ -31,4 +31,5 @@
noinst_HEADERS = \
all.h \
ogg_decoder_aspect.h \
ogg_encoder_aspect.h
ogg_encoder_aspect.h \
ogg_helper.h
......@@ -34,5 +34,6 @@
#include "ogg_decoder_aspect.h"
#include "ogg_encoder_aspect.h"
#include "ogg_helper.h"
#endif
/* 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
......@@ -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"
......
......@@ -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"
......
/* 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;
}
......@@ -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);
}
/*