From 69484aa8936bcc957db429c29fe6c1a0ef584714 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Thu, 1 Aug 2002 06:35:07 +0000 Subject: [PATCH] finish up implementation --- include/FLAC/seekable_stream_encoder.h | 40 +++- src/libFLAC/seekable_stream_encoder.c | 373 +++++++++++++++++---------------- 2 files changed, 230 insertions(+), 183 deletions(-) diff --git a/include/FLAC/seekable_stream_encoder.h b/include/FLAC/seekable_stream_encoder.h index 6d39331..d976ae7 100644 --- a/include/FLAC/seekable_stream_encoder.h +++ b/include/FLAC/seekable_stream_encoder.h @@ -85,6 +85,9 @@ typedef enum { FLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR, /**< Memory allocation failed. */ + FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR, + /**< The write callback returned an error. */ + FLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR, /**< The read callback returned an error. */ @@ -102,6 +105,11 @@ typedef enum { * callbacks being set. */ + FLAC__SEEKABLE_STREAM_ENCODER_INVALID_SEEKTABLE, + /**< An invalid seek table was passed is the metadata to + * FLAC__seekable_stream_encoder_set_metadata(). + */ + FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED /**< The encoder is in the uninitialized state. */ @@ -116,6 +124,26 @@ typedef enum { extern const char * const FLAC__SeekableStreamEncoderStateString[]; +/** Return values for the FLAC__SeekableStreamEncoder seek callback. + */ +typedef enum { + + FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK, + /**< The seek was OK and encoding can continue. */ + + FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_ERROR + /**< An unrecoverable error occurred. The encoder will return from the process call. */ + +} FLAC__SeekableStreamEncoderSeekStatus; + +/** Maps a FLAC__SeekableStreamEncoderSeekStatus to a C string. + * + * Using a FLAC__SeekableStreamEncoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern const char * const FLAC__SeekableStreamEncoderSeekStatusString[]; + + /*********************************************************************** * * class FLAC__SeekableStreamEncoder @@ -382,6 +410,12 @@ FLAC__bool FLAC__seekable_stream_encoder_set_total_samples_estimate(FLAC__Seekab /** This is inherited from FLAC__StreamEncoder; see * FLAC__stream_encoder_set_metadata(). * + * \note + * The decoder instance \b will modify the first \c SEEKTABLE block + * as it transforms the template to a valid seektable while encoding, + * but it is still up to the caller to free all metadata blocks after + * encoding. + * * \default \c NULL, 0 * \param encoder An encoder instance to set. * \param metadata See above. @@ -391,7 +425,7 @@ FLAC__bool FLAC__seekable_stream_encoder_set_total_samples_estimate(FLAC__Seekab * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC__bool FLAC__seekable_stream_encoder_set_metadata(FLAC__SeekableStreamEncoder *encoder, FLAC__FileMetadata **metadata, unsigned num_blocks); +FLAC__bool FLAC__seekable_stream_encoder_set_metadata(FLAC__SeekableStreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks); /** Set the seek callback. * The supplied function will be called when the encoder needs to seek @@ -428,7 +462,7 @@ FLAC__bool FLAC__seekable_stream_encoder_set_seek_callback(FLAC__SeekableStreamE * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC__bool FLAC__stream_encoder_set_write_callback(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderWriteStatus (*value)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)); +FLAC__bool FLAC__seekable_stream_encoder_set_write_callback(FLAC__SeekableStreamEncoder *encoder, FLAC__StreamEncoderWriteStatus (*value)(const FLAC__SeekableStreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)); /** Set the client data to be passed back to callbacks. * This value will be supplied to callbacks in their \a client_data @@ -442,7 +476,7 @@ FLAC__bool FLAC__stream_encoder_set_write_callback(FLAC__StreamEncoder *encoder, * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC__bool FLAC__stream_encoder_set_client_data(FLAC__StreamEncoder *encoder, void *value); +FLAC__bool FLAC__seekable_stream_encoder_set_client_data(FLAC__SeekableStreamEncoder *encoder, void *value); /** Get the current encoder state. * diff --git a/src/libFLAC/seekable_stream_encoder.c b/src/libFLAC/seekable_stream_encoder.c index 50205f4..e0280a9 100644 --- a/src/libFLAC/seekable_stream_encoder.c +++ b/src/libFLAC/seekable_stream_encoder.c @@ -24,6 +24,11 @@ #include "protected/seekable_stream_encoder.h" #include "protected/stream_encoder.h" +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) + /*********************************************************************** * * Private class method prototypes @@ -41,9 +46,15 @@ static void metadata_callback_(const FLAC__StreamEncoder *encoder, const FLAC__S ***********************************************************************/ typedef struct FLAC__SeekableStreamEncoderPrivate { - FILE *file; - char *filename; + FLAC__SeekableStreamEncoderSeekStatus (*seek_callback)(const FLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + FLAC__StreamEncoderWriteStatus (*write_callback)(const FLAC__SeekableStreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data); + void *client_data; FLAC__StreamEncoder *stream_encoder; + FLAC__StreamMetadata_SeekTable *seek_table; + /* internal vars (all the above are class settings) */ + unsigned first_seekpoint_to_check; + FLAC__uint64 stream_offset, seektable_offset; + FLAC__uint64 bytes_written; } FLAC__SeekableStreamEncoderPrivate; /*********************************************************************** @@ -55,18 +66,28 @@ typedef struct FLAC__SeekableStreamEncoderPrivate { const char * const FLAC__SeekableStreamEncoderStateString[] = { "FLAC__SEEKABLE_STREAM_ENCODER_OK", "FLAC__SEEKABLE_STREAM_ENCODER_STREAM_ENCODER_ERROR", - "FLAC__SEEKABLE_STREAM_ENCODER_ERROR_OPENING_FILE", "FLAC__SEEKABLE_STREAM_ENCODER_MEMORY_ALLOCATION_ERROR", + "FLAC__SEEKABLE_STREAM_ENCODER_READ_ERROR", + "FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR", + "FLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR", "FLAC__SEEKABLE_STREAM_ENCODER_ALREADY_INITIALIZED", + "FLAC__SEEKABLE_STREAM_ENCODER_INVALID_CALLBACK", + "FLAC__SEEKABLE_STREAM_ENCODER_INVALID_SEEKTABLE", "FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED" }; +const char * const FLAC__SeekableStreamEncoderSeekStatusString[] = { + "FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK", + "FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_ERROR" +}; + /*********************************************************************** * * Class constructor/destructor * ***********************************************************************/ + FLAC__SeekableStreamEncoder *FLAC__seekable_stream_encoder_new() { FLAC__SeekableStreamEncoder *encoder; @@ -134,13 +155,20 @@ FLAC__SeekableStreamEncoderState FLAC__seekable_stream_encoder_init(FLAC__Seekab if(encoder->protected_->state != FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED) return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_ALREADY_INITIALIZED; - if(0 == encoder->private_->filename) - return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_NO_FILENAME; + if(0 == encoder->private_->seek_callback || 0 == encoder->private_->write_callback) + return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_INVALID_CALLBACK; - encoder->private_->file = fopen(encoder->private_->filename, "wb"); + if(0 != encoder->private_->seek_table && !FLAC__format_seektable_is_legal(encoder->private_->seek_table)) + return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_INVALID_SEEKTABLE; - if(encoder->private_->file == 0) - return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_ERROR_OPENING_FILE; + /* + * This must be done before we init the stream encoder because that + * calls the write_callback, which uses these values. + */ + encoder->private_->first_seekpoint_to_check = 0; + encoder->private_->stream_offset = 0; + encoder->private_->seektable_offset = 0; + encoder->private_->bytes_written = 0; FLAC__stream_encoder_set_write_callback(encoder->private_->stream_encoder, write_callback_); FLAC__stream_encoder_set_metadata_callback(encoder->private_->stream_encoder, metadata_callback_); @@ -149,33 +177,31 @@ FLAC__SeekableStreamEncoderState FLAC__seekable_stream_encoder_init(FLAC__Seekab if(FLAC__stream_encoder_init(encoder->private_->stream_encoder) != FLAC__STREAM_ENCODER_OK) return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_STREAM_ENCODER_ERROR; - return decoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_OK; + /* + * Initializing the stream encoder writes all the metadata, so we + * save the stream offset now. + */ + encoder->private_->stream_offset = encoder->private_->bytes_written; + + return encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_OK; } void FLAC__seekable_stream_encoder_finish(FLAC__SeekableStreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); if(encoder->protected_->state == FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED) return; FLAC__ASSERT(0 != encoder->private_->stream_encoder); - if(0 != encoder->private_->file) { - fclose(encoder->private_->file); - encoder->private_->file = 0; - } - - if(0 != encoder->private_->filename) { - free(encoder->private_->filename); - encoder->private_->filename = 0; - } + FLAC__stream_encoder_finish(encoder->private_->stream_encoder); set_defaults_(encoder); encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED; - - return FLAC__stream_encoder_finish(encoder->private_->stream_encoder); } FLAC__bool FLAC__seekable_stream_encoder_set_streamable_subset(FLAC__SeekableStreamEncoder *encoder, FLAC__bool value) @@ -362,7 +388,16 @@ FLAC__bool FLAC__seekable_stream_encoder_set_metadata(FLAC__SeekableStreamEncode FLAC__ASSERT(0 != encoder->private_->stream_encoder); if(encoder->protected_->state != FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED) return false; - return FLAC__stream_encoder_set_metadata(encoder->private_->stream_encoder, value); + if(0 != metadata && num_blocks > 0) { + unsigned i; + for(i = 0; i < num_blocks; i++) { + if(0 != metadata[i] && metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) { + encoder->private_->seek_table = &metadata[i]->data.seek_table; + break; /* take only the first one */ + } + } + } + return FLAC__stream_encoder_set_metadata(encoder->private_->stream_encoder, metadata, num_blocks); } FLAC__bool FLAC__seekable_stream_encoder_set_seek_callback(FLAC__SeekableStreamEncoder *encoder, FLAC__SeekableStreamEncoderSeekStatus (*value)(const FLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)) @@ -376,7 +411,7 @@ FLAC__bool FLAC__seekable_stream_encoder_set_seek_callback(FLAC__SeekableStreamE return true; } -FLAC__bool FLAC__stream_encoder_set_write_callback(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderWriteStatus (*value)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)) +FLAC__bool FLAC__seekable_stream_encoder_set_write_callback(FLAC__SeekableStreamEncoder *encoder, FLAC__StreamEncoderWriteStatus (*value)(const FLAC__SeekableStreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -387,7 +422,7 @@ FLAC__bool FLAC__stream_encoder_set_write_callback(FLAC__StreamEncoder *encoder, return true; } -FLAC__bool FLAC__stream_encoder_set_client_data(FLAC__StreamEncoder *encoder, void *value) +FLAC__bool FLAC__seekable_stream_encoder_set_client_data(FLAC__SeekableStreamEncoder *encoder, void *value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -544,206 +579,184 @@ void set_defaults_(FLAC__SeekableStreamEncoder *encoder) FLAC__ASSERT(0 != encoder->private_); FLAC__ASSERT(0 != encoder->protected_); - encoder->protected_->filename = 0; + encoder->private_->seek_callback = 0; + encoder->private_->write_callback = 0; + encoder->private_->client_data = 0; + + encoder->private_->seek_table = 0; } FLAC__StreamEncoderWriteStatus write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data) { FLAC__SeekableStreamEncoder *seekable_stream_encoder = (FLAC__SeekableStreamEncoder*)client_data; + FLAC__StreamEncoderWriteStatus status; + + /* + * Watch for the first SEEKTABLE block to go by and store its offset. + */ + if(samples == 0 && (buffer[0] & 0x7f) == FLAC__METADATA_TYPE_SEEKTABLE) + seekable_stream_encoder->private_->seektable_offset = seekable_stream_encoder->private_->bytes_written; - /* mark the current seek point if hit (if stream_offset == 0 that means we're still writing metadata and haven't hit the first frame yet) */ - if(seekable_stream_encoder->private_->stream_offset > 0 && seekable_stream_encoder->private_->seek_table.num_points > 0) { - FLAC__uint64 current_sample = (FLAC__uint64)current_frame * (FLAC__uint64)FLAC__stream_encoder_get_blocksize(encoder), test_sample; + /* + * Mark the current seek point if hit (if stream_offset == 0 that + * means we're still writing metadata and haven't hit the first + * frame yet) + */ + if(seekable_stream_encoder->private_->stream_offset > 0 && seekable_stream_encoder->private_->seek_table->num_points > 0) { + /*@@@ WATCHOUT: assumes the encoder is fixed-blocksize, which will be true indefinitely: */ + const unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + const FLAC__uint64 frame_first_sample = (FLAC__uint64)current_frame * (FLAC__uint64)blocksize; + const FLAC__uint64 frame_last_sample = frame_first_sample + (FLAC__uint64)blocksize - 1; + FLAC__uint64 test_sample; unsigned i; - for(i = seekable_stream_encoder->private_->first_seek_point_to_check; i < seekable_stream_encoder->private_->seek_table.num_points; i++) { - test_sample = seekable_stream_encoder->private_->seek_table.points[i].sample_number; - if(test_sample > current_sample) { + for(i = seekable_stream_encoder->private_->first_seekpoint_to_check; i < seekable_stream_encoder->private_->seek_table->num_points; i++) { + test_sample = seekable_stream_encoder->private_->seek_table->points[i].sample_number; + if(test_sample > frame_last_sample) { break; } - else if(test_sample == current_sample) { - seekable_stream_encoder->private_->seek_table.points[i].stream_offset = seekable_stream_encoder->private_->bytes_written - seekable_stream_encoder->private_->stream_offset; - seekable_stream_encoder->private_->seek_table.points[i].frame_samples = FLAC__stream_encoder_get_blocksize(encoder); - seekable_stream_encoder->private_->first_seek_point_to_check++; - break; + else if(test_sample >= frame_first_sample) { + seekable_stream_encoder->private_->seek_table->points[i].sample_number = frame_first_sample; + seekable_stream_encoder->private_->seek_table->points[i].stream_offset = seekable_stream_encoder->private_->bytes_written - seekable_stream_encoder->private_->stream_offset; + seekable_stream_encoder->private_->seek_table->points[i].frame_samples = blocksize; + seekable_stream_encoder->private_->first_seekpoint_to_check++; + /* DO NOT: "break;" and here's why: + * The seektable template may contain more than one target + * sample for any given frame; we will keep looping, generating + * duplicate seekpoints for them, and we'll clean it up later, + * just before writing the seektable back to the metadata. + */ } else { - seekable_stream_encoder->private_->first_seek_point_to_check++; + seekable_stream_encoder->private_->first_seekpoint_to_check++; } } } - seekable_stream_encoder->private_->bytes_written += bytes; - seekable_stream_encoder->private_->samples_written += samples; - seekable_stream_encoder->private_->current_frame = current_frame; + status = seekable_stream_encoder->private_->write_callback(seekable_stream_encoder, buffer, bytes, samples, current_frame, seekable_stream_encoder->private_->client_data); - if(samples && seekable_stream_encoder->private_->verbose && seekable_stream_encoder->private_->total_samples_to_encode > 0 && !(current_frame & mask)) - print_stats(seekable_stream_encoder->private_); - - if(seekable_stream_encoder->private_->verify) { - seekable_stream_encoder->private_->verify_fifo.encoded_signal = buffer; - seekable_stream_encoder->private_->verify_fifo.encoded_bytes = bytes; - if(seekable_stream_encoder->private_->verify_fifo.into_frames) { - if(!FLAC__stream_decoder_process_one_frame(seekable_stream_encoder->private_->verify_fifo.decoder)) { - seekable_stream_encoder->private_->verify_fifo.result = FLAC__VERIFY_FAILED_IN_FRAME; - return FLAC__STREAM_ENCODER_WRITE_FATAL_ERROR; - } - } - else { - if(!FLAC__stream_decoder_process_metadata(seekable_stream_encoder->private_->verify_fifo.decoder)) { - seekable_stream_encoder->private_->verify_fifo.result = FLAC__VERIFY_FAILED_IN_METADATA; - return FLAC__STREAM_ENCODER_WRITE_FATAL_ERROR; - } - } - } - -#ifdef FLAC__HAS_OGG - if(seekable_stream_encoder->private_->use_ogg) { - ogg_packet op; - - memset(&op, 0, sizeof(op)); - op.packet = (unsigned char *)buffer; - op.granulepos = seekable_stream_encoder->private_->samples_written - 1; - /*@@@ WATCHOUT: - * this depends on the behavior of libFLAC that we will get one - * write_callback first with all the metadata (and 'samples' - * will be 0), then one write_callback for each frame. - */ - op.packetno = (samples == 0? -1 : (int)seekable_stream_encoder->private_->current_frame); - op.bytes = bytes; - - if (seekable_stream_encoder->private_->bytes_written == bytes) - op.b_o_s = 1; - - if (seekable_stream_encoder->private_->total_samples_to_encode == seekable_stream_encoder->private_->samples_written) - op.e_o_s = 1; - - ogg_stream_packetin(&seekable_stream_encoder->private_->ogg.os, &op); - - while(ogg_stream_pageout(&seekable_stream_encoder->private_->ogg.os, &seekable_stream_encoder->private_->ogg.og) != 0) { - int written; - written = fwrite(seekable_stream_encoder->private_->ogg.og.header, 1, seekable_stream_encoder->private_->ogg.og.header_len, seekable_stream_encoder->private_->fout); - if (written != seekable_stream_encoder->private_->ogg.og.header_len) - return FLAC__STREAM_ENCODER_WRITE_FATAL_ERROR; - - written = fwrite(seekable_stream_encoder->private_->ogg.og.body, 1, seekable_stream_encoder->private_->ogg.og.body_len, seekable_stream_encoder->private_->fout); - if (written != seekable_stream_encoder->private_->ogg.og.body_len) - return FLAC__STREAM_ENCODER_WRITE_FATAL_ERROR; - } - - return FLAC__STREAM_ENCODER_WRITE_OK; - } + if(status == FLAC__STREAM_ENCODER_WRITE_STATUS_OK) + seekable_stream_encoder->private_->bytes_written += bytes; else -#endif - { - if(fwrite(buffer, sizeof(FLAC__byte), bytes, seekable_stream_encoder->private_->fout) == bytes) - return FLAC__STREAM_ENCODER_WRITE_OK; - else - return FLAC__STREAM_ENCODER_WRITE_FATAL_ERROR; - } + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR; + + return status; } void metadata_callback_(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data) { - seekable_stream_encoder->private_ *seekable_stream_encoder->private_ = (seekable_stream_encoder->private_ *)client_data; - FLAC__byte b; - FILE *f = seekable_stream_encoder->private_->fout; + FLAC__SeekableStreamEncoder *seekable_stream_encoder = (FLAC__SeekableStreamEncoder*)client_data; + FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; 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; FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); - /* - * If we are writing to an ogg stream, there is no need to go back - * and update the STREAMINFO or SEEKTABLE blocks; the values we would - * update are not necessary with Ogg as the transport. We can't do - * it reliably anyway without knowing the Ogg structure. - */ -#ifdef FLAC__HAS_OGG - if(seekable_stream_encoder->private_->use_ogg) - return; -#endif - - /* - * we get called by the encoder when the encoding process has - * finished so that we can update the STREAMINFO and SEEKTABLE + /* We get called by the stream encoder when the encoding process + * has finished so that we can update the STREAMINFO and SEEKTABLE * blocks. */ (void)encoder; /* silence compiler warning about unused parameter */ - if(f != stdout) { - fclose(seekable_stream_encoder->private_->fout); - if(0 == (f = fopen(seekable_stream_encoder->private_->outfilename, "r+b"))) - return; - } + /*@@@ reopen callback here? */ - /* all this is based on intimate knowledge of the stream header + /* All this is based on intimate knowledge of the stream header * layout, but a change to the header format that would break this * would also break all streams encoded in the previous format. */ - if(-1 == fseek(f, 26, SEEK_SET)) goto end_; - fwrite(metadata->data.stream_info.md5sum, 1, 16, f); + /* + * Write MD5 signature + */ + if(seekable_stream_encoder->private_->seek_callback(seekable_stream_encoder, 26, seekable_stream_encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR; + return; + } + if(seekable_stream_encoder->private_->write_callback(seekable_stream_encoder, metadata->data.stream_info.md5sum, 16, 0, 0, seekable_stream_encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR; + return; + } + + /* + * Write total samples + */ + b[0] = ((FLAC__byte)(bps-1) << 4) | (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(seekable_stream_encoder->private_->seek_callback(seekable_stream_encoder, 21, seekable_stream_encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR; + return; + } + if(seekable_stream_encoder->private_->write_callback(seekable_stream_encoder, b, 5, 0, 0, seekable_stream_encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR; + return; + } + + /* + * Write min/max framesize + */ + 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(seekable_stream_encoder->private_->seek_callback(seekable_stream_encoder, 12, seekable_stream_encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR; + return; + } + if(seekable_stream_encoder->private_->write_callback(seekable_stream_encoder, b, 6, 0, 0, seekable_stream_encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR; + return; + } - /* if we get this far we know we can seek so no need to check the - * return value from fseek() + /* + * Write seektable */ - fseek(f, 21, SEEK_SET); - if(fread(&b, 1, 1, f) != 1) goto framesize_; - fseek(f, 21, SEEK_SET); - b = (b & 0xf0) | (FLAC__byte)((samples >> 32) & 0x0F); - if(fwrite(&b, 1, 1, f) != 1) goto framesize_; - b = (FLAC__byte)((samples >> 24) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto framesize_; - b = (FLAC__byte)((samples >> 16) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto framesize_; - b = (FLAC__byte)((samples >> 8) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto framesize_; - b = (FLAC__byte)(samples & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto framesize_; - -framesize_: - fseek(f, 12, SEEK_SET); - b = (FLAC__byte)((min_framesize >> 16) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto seektable_; - b = (FLAC__byte)((min_framesize >> 8) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto seektable_; - b = (FLAC__byte)(min_framesize & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto seektable_; - b = (FLAC__byte)((max_framesize >> 16) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto seektable_; - b = (FLAC__byte)((max_framesize >> 8) & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto seektable_; - b = (FLAC__byte)(max_framesize & 0xFF); - if(fwrite(&b, 1, 1, f) != 1) goto seektable_; - -seektable_: - if(seekable_stream_encoder->private_->seek_table.num_points > 0) { - long pos; + if(seekable_stream_encoder->private_->seek_table->num_points > 0 && seekable_stream_encoder->private_->seektable_offset > 0) { unsigned i; - /* convert any unused seek points to placeholders */ - for(i = 0; i < seekable_stream_encoder->private_->seek_table.num_points; i++) { - if(seekable_stream_encoder->private_->seek_table.points[i].sample_number == FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) - break; - else if(seekable_stream_encoder->private_->seek_table.points[i].frame_samples == 0) - seekable_stream_encoder->private_->seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + FLAC__format_seektable_sort(seekable_stream_encoder->private_->seek_table); + + FLAC__ASSERT(FLAC__format_seektable_is_legal(seekable_stream_encoder->private_->seek_table)); + + if(seekable_stream_encoder->private_->seek_callback(seekable_stream_encoder, seekable_stream_encoder->private_->seektable_offset, seekable_stream_encoder->private_->client_data) != FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_SEEK_ERROR; + return; } - /* the offset of the seek table data 'pos' should be after then stream sync and STREAMINFO block and SEEKTABLE header */ - pos = (FLAC__STREAM_SYNC_LEN + FLAC__STREAM_METADATA_IS_LAST_LEN + FLAC__STREAM_METADATA_TYPE_LEN + FLAC__STREAM_METADATA_LENGTH_LEN) / 8; - pos += metadata->length; - pos += (FLAC__STREAM_METADATA_IS_LAST_LEN + FLAC__STREAM_METADATA_TYPE_LEN + FLAC__STREAM_METADATA_LENGTH_LEN) / 8; - fseek(f, pos, SEEK_SET); - for(i = 0; i < seekable_stream_encoder->private_->seek_table.num_points; i++) { - if(!write_big_endian_uint64(f, seekable_stream_encoder->private_->seek_table.points[i].sample_number)) goto end_; - if(!write_big_endian_uint64(f, seekable_stream_encoder->private_->seek_table.points[i].stream_offset)) goto end_; - if(!write_big_endian_uint16(f, (FLAC__uint16)seekable_stream_encoder->private_->seek_table.points[i].frame_samples)) goto end_; + for(i = 0; i < seekable_stream_encoder->private_->seek_table->num_points; i++) { + FLAC__uint64 xx; + unsigned x; + xx = seekable_stream_encoder->private_->seek_table->points[i].sample_number; + b[7] = (FLAC__byte)xx; xx >>= 8; + b[6] = (FLAC__byte)xx; xx >>= 8; + b[5] = (FLAC__byte)xx; xx >>= 8; + b[4] = (FLAC__byte)xx; xx >>= 8; + b[3] = (FLAC__byte)xx; xx >>= 8; + b[2] = (FLAC__byte)xx; xx >>= 8; + b[1] = (FLAC__byte)xx; xx >>= 8; + b[0] = (FLAC__byte)xx; xx >>= 8; + xx = seekable_stream_encoder->private_->seek_table->points[i].stream_offset; + b[15] = (FLAC__byte)xx; xx >>= 8; + b[14] = (FLAC__byte)xx; xx >>= 8; + b[13] = (FLAC__byte)xx; xx >>= 8; + b[12] = (FLAC__byte)xx; xx >>= 8; + b[11] = (FLAC__byte)xx; xx >>= 8; + b[10] = (FLAC__byte)xx; xx >>= 8; + b[9] = (FLAC__byte)xx; xx >>= 8; + b[8] = (FLAC__byte)xx; xx >>= 8; + x = seekable_stream_encoder->private_->seek_table->points[i].frame_samples; + b[17] = (FLAC__byte)x; x >>= 8; + b[16] = (FLAC__byte)x; x >>= 8; + if(seekable_stream_encoder->private_->write_callback(seekable_stream_encoder, b, 18, 0, 0, seekable_stream_encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + seekable_stream_encoder->protected_->state = FLAC__SEEKABLE_STREAM_ENCODER_WRITE_ERROR; + return; + } } } - -end_: - fclose(f); - return; } -- 2.7.4