From 8d37ddba8aa53b9e7e3bc10b231b86ef11d6b620 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Wed, 5 Jun 2002 06:05:35 +0000 Subject: [PATCH] fix bug where decoding flac file with 0 total_samples yielded 0 chunk size in wave file; add -F to continue decoding through errors --- src/flac/decode.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 12 deletions(-) diff --git a/src/flac/decode.c b/src/flac/decode.c index f56d330..3dcd292 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -57,8 +57,13 @@ typedef struct { unsigned channels; unsigned sample_rate; FLAC__bool verbose; + FLAC__bool continue_through_decode_errors; + struct { + FLAC__bool needs_fixup; + unsigned riff_offset; + unsigned data_offset; + } wave_chunk_size_fixup; FLAC__uint64 skip; - FLAC__bool skip_count_too_high; FLAC__uint64 samples_processed; unsigned frame_counter; #ifdef FLAC__HAS_OGG @@ -79,6 +84,7 @@ static FLAC__bool is_big_endian_host; static FLAC__bool init(const char *infilename, stream_info_struct *stream_info); static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val); static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val); +static FLAC__bool fixup_wave_chunk_size(const char *outfilename, unsigned riff_offset, unsigned data_offset, FLAC__uint32 data_size); #ifdef FLAC__HAS_OGG static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data); #endif @@ -104,8 +110,9 @@ int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool stream_info.test_only = (outfilename == 0); stream_info.is_wave_out = true; stream_info.verbose = options.common.verbose; + stream_info.continue_through_decode_errors = options.common.continue_through_decode_errors; + stream_info.wave_chunk_size_fixup.needs_fixup = false; stream_info.skip = options.common.skip; - stream_info.skip_count_too_high = false; stream_info.samples_processed = 0; stream_info.frame_counter = 0; #ifdef FLAC__HAS_OGG @@ -161,10 +168,8 @@ int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]); goto wav_abort_; } - if(stream_info.skip_count_too_high) { - fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", stream_info.inbasefilename); + if(stream_info.abort_flag) goto wav_abort_; - } if(!FLAC__file_decoder_seek_absolute(stream_info.decoder.file, stream_info.skip)) { fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]); goto wav_abort_; @@ -238,6 +243,9 @@ int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool #endif if(analysis_mode) flac__analyze_finish(aopts); + if(stream_info.wave_chunk_size_fixup.needs_fixup) + if(!fixup_wave_chunk_size(outfilename, stream_info.wave_chunk_size_fixup.riff_offset, stream_info.wave_chunk_size_fixup.data_offset, (FLAC__uint32)stream_info.samples_processed)) + return 1; if(md5_failure) { fprintf(stderr, "\r%s: WARNING, MD5 signature mismatch\n", stream_info.inbasefilename); } @@ -290,8 +298,9 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool stream_info.is_big_endian = options.is_big_endian; stream_info.is_unsigned_samples = options.is_unsigned_samples; stream_info.verbose = options.common.verbose; + stream_info.continue_through_decode_errors = options.common.continue_through_decode_errors; + stream_info.wave_chunk_size_fixup.needs_fixup = false; stream_info.skip = options.common.skip; - stream_info.skip_count_too_high = false; stream_info.samples_processed = 0; stream_info.frame_counter = 0; #ifdef FLAC__HAS_OGG @@ -347,10 +356,8 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]); goto raw_abort_; } - if(stream_info.skip_count_too_high) { - fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", stream_info.inbasefilename); + if(stream_info.abort_flag) goto raw_abort_; - } if(!FLAC__file_decoder_seek_absolute(stream_info.decoder.file, stream_info.skip)) { fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]); goto raw_abort_; @@ -549,6 +556,37 @@ FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val) return fwrite(b, 1, 4, f) == 4; } +FLAC__bool fixup_wave_chunk_size(const char *outfilename, unsigned riff_offset, unsigned data_offset, FLAC__uint32 data_size) +{ + FILE *f = fopen(outfilename, "r+b"); + if(0 == f) { + fprintf(stderr, "ERROR, couldn't open file %s while fixing up WAVE chunk size\n", outfilename); + return false; + } + if(fseek(f, riff_offset, SEEK_SET) < 0) { + fprintf(stderr, "ERROR, couldn't seek in file %s while fixing up WAVE chunk size\n", outfilename); + fclose(f); + return false; + } + if(!write_little_endian_uint32(f, data_size + 36)) { + fprintf(stderr, "ERROR, couldn't write size in file %s while fixing up WAVE chunk size\n", outfilename); + fclose(f); + return false; + } + if(fseek(f, data_offset, SEEK_SET) < 0) { + fprintf(stderr, "ERROR, couldn't seek in file %s while fixing up WAVE chunk size\n", outfilename); + fclose(f); + return false; + } + if(!write_little_endian_uint32(f, data_size)) { + fprintf(stderr, "ERROR, couldn't write size in file %s while fixing up WAVE chunk size\n", outfilename); + fclose(f); + return false; + } + fclose(f); + return true; +} + #ifdef FLAC__HAS_OGG #define OGG_READ_BUFFER_SIZE 4096 FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) @@ -725,8 +763,14 @@ void metadata_callback(const void *decoder, const FLAC__StreamMetaData *metadata if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { /* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */ if(metadata->data.stream_info.total_samples > 0 && stream_info->skip >= metadata->data.stream_info.total_samples) { - stream_info->total_samples = 0; - stream_info->skip_count_too_high = true; + fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", stream_info->inbasefilename); + stream_info->abort_flag = true; + return; + } + else if(metadata->data.stream_info.total_samples == 0 && stream_info->skip > 0) { + fprintf(stderr, "%s: ERROR, can't skip when FLAC metadata has total sample count of 0\n", stream_info->inbasefilename); + stream_info->abort_flag = true; + return; } else stream_info->total_samples = metadata->data.stream_info.total_samples - stream_info->skip; @@ -743,12 +787,24 @@ void metadata_callback(const void *decoder, const FLAC__StreamMetaData *metadata /* write the WAVE headers if necessary */ if(!stream_info->analysis_mode && !stream_info->test_only && stream_info->is_wave_out) { FLAC__uint64 data_size = stream_info->total_samples * stream_info->channels * ((stream_info->bps+7)/8); + if(stream_info->total_samples == 0) { + if(stream_info->fout == stdout) { + fprintf(stderr, "%s: WARNING, don't have accurate sample count available for WAVE header.\n", stream_info->inbasefilename); + fprintf(stderr, " Generated WAVE file will have a data chunk size of 0. Try\n"); + fprintf(stderr, " decoding directly to a file instead.\n"); + } + else { + stream_info->wave_chunk_size_fixup.needs_fixup = true; + } + } if(data_size >= 0xFFFFFFDC) { fprintf(stderr, "%s: ERROR: stream is too big to fit in a single WAVE file chunk\n", stream_info->inbasefilename); stream_info->abort_flag = true; return; } if(fwrite("RIFF", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true; + if(stream_info->wave_chunk_size_fixup.needs_fixup) + stream_info->wave_chunk_size_fixup.riff_offset = ftell(stream_info->fout); if(!write_little_endian_uint32(stream_info->fout, (FLAC__uint32)(data_size+36))) stream_info->abort_flag = true; /* filesize-8 */ if(fwrite("WAVEfmt ", 1, 8, stream_info->fout) != 8) stream_info->abort_flag = true; if(fwrite("\020\000\000\000", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true; /* chunk size = 16 */ @@ -759,6 +815,8 @@ void metadata_callback(const void *decoder, const FLAC__StreamMetaData *metadata if(!write_little_endian_uint16(stream_info->fout, (FLAC__uint16)(stream_info->channels * ((stream_info->bps+7) / 8)))) stream_info->abort_flag = true; /* block align */ if(!write_little_endian_uint16(stream_info->fout, (FLAC__uint16)(stream_info->bps))) stream_info->abort_flag = true; /* bits per sample */ if(fwrite("data", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true; + if(stream_info->wave_chunk_size_fixup.needs_fixup) + stream_info->wave_chunk_size_fixup.data_offset = ftell(stream_info->fout); if(!write_little_endian_uint32(stream_info->fout, (FLAC__uint32)data_size)) stream_info->abort_flag = true; /* data size */ } } @@ -769,7 +827,8 @@ void error_callback(const void *decoder, FLAC__StreamDecoderErrorStatus status, stream_info_struct *stream_info = (stream_info_struct *)client_data; (void)decoder; fprintf(stderr, "%s: *** Got error code %d:%s\n", stream_info->inbasefilename, status, FLAC__StreamDecoderErrorStatusString[status]); - stream_info->abort_flag = true; + if(!stream_info->continue_through_decode_errors) + stream_info->abort_flag = true; } void print_stats(const stream_info_struct *stream_info) @@ -797,3 +856,111 @@ void print_stats(const stream_info_struct *stream_info) } } } +#if 0 +void metadata_callback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetaData *metadata, void *client_data) +{ + encoder_wrapper_struct *encoder_wrapper = (encoder_wrapper_struct *)client_data; + FLAC__byte b; + FILE *f = encoder_wrapper->fout; + 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; + + 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(encoder_wrapper->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 + * blocks. + */ + + (void)encoder; /* silence compiler warning about unused parameter */ + + if(f != stdout) { + fclose(encoder_wrapper->fout); + if(0 == (f = fopen(encoder_wrapper->outfilename, "r+b"))) + return; + } + + /* 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); + +samples_: + /* if we get this far we know we can seek so no need to check the + * return value from fseek() + */ + 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(encoder_wrapper->seek_table.num_points > 0) { + long pos; + unsigned i; + + /* convert any unused seek points to placeholders */ + for(i = 0; i < encoder_wrapper->seek_table.num_points; i++) { + if(encoder_wrapper->seek_table.points[i].sample_number == FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) + break; + else if(encoder_wrapper->seek_table.points[i].frame_samples == 0) + encoder_wrapper->seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + } + + /* 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 < encoder_wrapper->seek_table.num_points; i++) { + if(!write_big_endian_uint64(f, encoder_wrapper->seek_table.points[i].sample_number)) goto end_; + if(!write_big_endian_uint64(f, encoder_wrapper->seek_table.points[i].stream_offset)) goto end_; + if(!write_big_endian_uint16(f, (FLAC__uint16)encoder_wrapper->seek_table.points[i].frame_samples)) goto end_; + } + } + +end_: + fclose(f); + return; +} +#endif -- 2.7.4