mirror of https://github.com/xiph/flac
Improve decoding of chained streams in flac command line tool
This commit is contained in:
parent
60e9234c6f
commit
08cf8a194a
|
@ -38,7 +38,6 @@ typedef struct {
|
|||
FLAC__bool use_first_serial_number;
|
||||
long serial_number;
|
||||
FLAC__bool decode_chained_stream;
|
||||
uint32_t stream_counter;
|
||||
#endif
|
||||
|
||||
FileFormat format;
|
||||
|
@ -87,6 +86,8 @@ typedef struct {
|
|||
uint32_t sample_rate;
|
||||
FLAC__uint32 channel_mask;
|
||||
|
||||
int stream_counter;
|
||||
|
||||
/* these are used only in analyze mode */
|
||||
FLAC__uint64 decode_position;
|
||||
FLAC__bool decode_position_valid;
|
||||
|
@ -113,6 +114,7 @@ static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg,
|
|||
static void DecoderSession_destroy(DecoderSession *d, FLAC__bool error_occurred);
|
||||
static FLAC__bool DecoderSession_init_decoder(DecoderSession *d, const char *infilename);
|
||||
static FLAC__bool DecoderSession_process(DecoderSession *d);
|
||||
static FLAC__bool verify_streaminfo(DecoderSession *d, FLAC__bool md5_failure);
|
||||
static int DecoderSession_finish_ok(DecoderSession *d);
|
||||
static int DecoderSession_finish_error(DecoderSession *d);
|
||||
static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, uint32_t sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
|
||||
|
@ -188,7 +190,7 @@ int flac__decode_file(const char *infilename, const char *outfilename, FLAC__boo
|
|||
)
|
||||
return 1;
|
||||
|
||||
stats_new_file();
|
||||
stats_new_line();
|
||||
if(!DecoderSession_init_decoder(&decoder_session, infilename))
|
||||
return DecoderSession_finish_error(&decoder_session);
|
||||
|
||||
|
@ -205,7 +207,6 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__
|
|||
d->use_first_serial_number = use_first_serial_number;
|
||||
d->serial_number = serial_number;
|
||||
d->decode_chained_stream = decode_chained_stream;
|
||||
d->stream_counter = 0;
|
||||
#else
|
||||
(void)is_ogg;
|
||||
(void)use_first_serial_number;
|
||||
|
@ -246,6 +247,8 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__
|
|||
else
|
||||
d->warn_user_about_foreign_metadata = true;
|
||||
|
||||
d->stream_counter = -1;
|
||||
|
||||
d->iff_headers_need_fixup = false;
|
||||
|
||||
d->total_samples = 0;
|
||||
|
@ -381,7 +384,7 @@ FLAC__bool DecoderSession_init_decoder(DecoderSession *decoder_session, const ch
|
|||
if(decoder_session->is_ogg) {
|
||||
if(!decoder_session->use_first_serial_number)
|
||||
FLAC__stream_decoder_set_ogg_serial_number(decoder_session->decoder, decoder_session->serial_number);
|
||||
FLAC__stream_decoder_set_decode_chained_ogg_stream(decoder_session->decoder, decoder_session->decode_chained_stream);
|
||||
FLAC__stream_decoder_set_decode_chained_stream(decoder_session->decoder, decoder_session->decode_chained_stream);
|
||||
init_status = FLAC__stream_decoder_init_ogg_file(decoder_session->decoder, strcmp(infilename, "-")? infilename : 0, write_callback, metadata_callback, error_callback, /*client_data=*/decoder_session);
|
||||
}
|
||||
else
|
||||
|
@ -491,12 +494,40 @@ FLAC__bool DecoderSession_process(DecoderSession *d)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if(!FLAC__stream_decoder_process_until_end_of_stream(d->decoder) && !d->aborting_due_to_until) {
|
||||
flac__utils_printf(stderr, 2, "\n");
|
||||
print_error_with_state(d, "ERROR while decoding data");
|
||||
if(!d->continue_through_decode_errors)
|
||||
return false;
|
||||
#if FLAC__HAS_OGG
|
||||
if(!d->decode_chained_stream) {
|
||||
#endif
|
||||
if(!FLAC__stream_decoder_process_until_end_of_stream(d->decoder) && !d->aborting_due_to_until) {
|
||||
flac__utils_printf(stderr, 2, "\n");
|
||||
print_error_with_state(d, "ERROR while decoding data");
|
||||
if(!d->continue_through_decode_errors)
|
||||
return false;
|
||||
}
|
||||
#if FLAC__HAS_OGG
|
||||
}
|
||||
else {
|
||||
FLAC__ASSERT(!d->continue_through_decode_errors);
|
||||
while(1) {
|
||||
FLAC__bool md5_failure;
|
||||
d->stream_counter++;
|
||||
if(!FLAC__stream_decoder_process_until_end_of_link(d->decoder) && !d->aborting_due_to_until) {
|
||||
flac__utils_printf(stderr, 2, "\n");
|
||||
print_error_with_state(d, "ERROR while decoding data");
|
||||
return false;
|
||||
}
|
||||
if(FLAC__stream_decoder_get_state(d->decoder) == FLAC__STREAM_DECODER_END_OF_STREAM)
|
||||
break;
|
||||
md5_failure = !FLAC__stream_decoder_finish_link(d->decoder) && !d->aborting_due_to_until;
|
||||
if(!verify_streaminfo(d, md5_failure))
|
||||
return false;
|
||||
/* Reset some stuff for next link */
|
||||
stats_new_line();
|
||||
memset(&(d->prev_frameheader), 0, sizeof(FLAC__FrameHeader));
|
||||
d->samples_processed = 0;
|
||||
d->got_stream_info = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(
|
||||
(d->abort_flag && !(d->aborting_due_to_until || d->continue_through_decode_errors)) ||
|
||||
(FLAC__stream_decoder_get_state(d->decoder) > FLAC__STREAM_DECODER_END_OF_STREAM && !d->aborting_due_to_until)
|
||||
|
@ -534,9 +565,45 @@ FLAC__bool DecoderSession_process(DecoderSession *d)
|
|||
return true;
|
||||
}
|
||||
|
||||
FLAC__bool verify_streaminfo(DecoderSession *d, FLAC__bool md5_failure)
|
||||
{
|
||||
FLAC__bool ok = true;
|
||||
|
||||
if(md5_failure) {
|
||||
stats_print_name_and_stream_number(1, d->inbasefilename, d->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "ERROR, MD5 signature mismatch\n");
|
||||
ok = d->continue_through_decode_errors;
|
||||
}
|
||||
else if(d->got_stream_info && d->total_samples && (d->total_samples > d->samples_processed)){
|
||||
stats_print_name_and_stream_number(1, d->inbasefilename, d->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "ERROR, decoded number of samples is smaller than the total number of samples set in the STREAMINFO\n");
|
||||
ok = d->continue_through_decode_errors;
|
||||
}
|
||||
else {
|
||||
if(!d->got_stream_info) {
|
||||
stats_print_name_and_stream_number(1, d->inbasefilename, d->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "WARNING, cannot check MD5 signature since there was no STREAMINFO\n");
|
||||
ok = !d->treat_warnings_as_errors;
|
||||
}
|
||||
else if(!d->has_md5sum) {
|
||||
stats_print_name_and_stream_number(1, d->inbasefilename, d->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "WARNING, cannot check MD5 signature since it was unset in the STREAMINFO\n");
|
||||
ok = !d->treat_warnings_as_errors;
|
||||
}
|
||||
else if(!d->total_samples) {
|
||||
stats_print_name_and_stream_number(1, d->inbasefilename, d->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "WARNING, cannot check total number of samples since it was unset in the STREAMINFO\n");
|
||||
ok = !d->treat_warnings_as_errors;
|
||||
}
|
||||
stats_print_name_and_stream_number(2, d->inbasefilename, d->stream_counter);
|
||||
flac__utils_printf(stderr, 2, "%s \n", d->test_only? "ok ":d->analysis_mode?"done ":"done");
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int DecoderSession_finish_ok(DecoderSession *d)
|
||||
{
|
||||
FLAC__bool ok = true, md5_failure = false;
|
||||
FLAC__bool ok, md5_failure = false;
|
||||
|
||||
if(d->decoder) {
|
||||
md5_failure = !FLAC__stream_decoder_finish(d->decoder) && !d->aborting_due_to_until;
|
||||
|
@ -545,41 +612,7 @@ int DecoderSession_finish_ok(DecoderSession *d)
|
|||
}
|
||||
if(d->analysis_mode)
|
||||
flac__analyze_finish(d->aopts);
|
||||
if(md5_failure) {
|
||||
stats_print_name(1, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 1, "ERROR, MD5 signature mismatch\n");
|
||||
ok = d->continue_through_decode_errors;
|
||||
}
|
||||
else if(d->got_stream_info && d->total_samples && (d->total_samples > d->samples_processed)){
|
||||
stats_print_name(1, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 1, "ERROR, decoded number of samples is smaller than the total number of samples set in the STREAMINFO\n");
|
||||
ok = d->continue_through_decode_errors;
|
||||
}
|
||||
else {
|
||||
if(!d->got_stream_info) {
|
||||
stats_print_name(1, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 1, "WARNING, cannot check MD5 signature since there was no STREAMINFO\n");
|
||||
ok = !d->treat_warnings_as_errors;
|
||||
}
|
||||
else if(!d->has_md5sum) {
|
||||
stats_print_name(1, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 1, "WARNING, cannot check MD5 signature since it was unset in the STREAMINFO\n");
|
||||
ok = !d->treat_warnings_as_errors;
|
||||
}
|
||||
else if(!d->total_samples) {
|
||||
stats_print_name(1, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 1, "WARNING, cannot check total number of samples since it was unset in the STREAMINFO\n");
|
||||
ok = !d->treat_warnings_as_errors;
|
||||
}
|
||||
#if FLAC__HAS_OGG
|
||||
if (d->decode_chained_stream) {
|
||||
stats_print_name(2, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 2, "%d stream(s) found\n", d->stream_counter);
|
||||
}
|
||||
#endif
|
||||
stats_print_name(2, d->inbasefilename);
|
||||
flac__utils_printf(stderr, 2, "%s \n", d->test_only? "ok ":d->analysis_mode?"done ":"done");
|
||||
}
|
||||
ok = verify_streaminfo(d, md5_failure);
|
||||
DecoderSession_destroy(d, /*error_occurred=*/!ok);
|
||||
if(!d->analysis_mode && !d->test_only && d->format != FORMAT_RAW) {
|
||||
if(d->iff_headers_need_fixup || (!d->got_stream_info && strcmp(d->outfilename, "-"))) {
|
||||
|
@ -1279,7 +1312,9 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
|
|||
if(decoder_session->prev_frameheader.number.sample_number +
|
||||
decoder_session->prev_frameheader.blocksize !=
|
||||
frame->header.number.sample_number) {
|
||||
flac__utils_printf_clear_stats(stderr, 1, "%s: WARNING: sample or frame number does not increase correctly (%" PRIu64 " samples have been decoded), file might not be seekable\n", decoder_session->inbasefilename, decoder_session->samples_processed);
|
||||
stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "WARNING: sample or frame number does not increase correctly (%" PRIu64 " samples have been decoded), file might not be seekable\n", decoder_session->samples_processed);
|
||||
stats_new_line();
|
||||
if(decoder_session->treat_warnings_as_errors) {
|
||||
decoder_session->abort_flag = true;
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
|
@ -1512,16 +1547,6 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet
|
|||
|
||||
(void)decoder;
|
||||
|
||||
#if FLAC__HAS_OGG
|
||||
if (decoder_session->frame_counter && decoder_session->is_ogg && decoder_session->decode_chained_stream) {
|
||||
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
|
||||
decoder_session->stream_counter++;
|
||||
decoder_session->total_samples += metadata->data.stream_info.total_samples;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
|
||||
FLAC__uint64 skip, until;
|
||||
|
||||
|
@ -1535,18 +1560,44 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet
|
|||
|
||||
decoder_session->got_stream_info = true;
|
||||
decoder_session->has_md5sum = memcmp(metadata->data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0;
|
||||
decoder_session->bps = metadata->data.stream_info.bits_per_sample;
|
||||
decoder_session->channels = metadata->data.stream_info.channels;
|
||||
decoder_session->sample_rate = metadata->data.stream_info.sample_rate;
|
||||
decoder_session->stream_counter = 1;
|
||||
|
||||
if(!flac__utils_canonicalize_skip_until_specification(decoder_session->skip_specification, decoder_session->sample_rate)) {
|
||||
flac__utils_printf(stderr, 1, "%s: ERROR, value of --skip is too large\n", decoder_session->inbasefilename);
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
if(decoder_session->stream_counter > 0) {
|
||||
/* This is not the first link in the chain, so check whether parameters are the same */
|
||||
if(decoder_session->bps != metadata->data.stream_info.bits_per_sample) {
|
||||
stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "ERROR, bits-per-sample is %u in this link's STREAMINFO but was %u in previous one\n", metadata->data.stream_info.bits_per_sample, decoder_session->bps);
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
}
|
||||
if(decoder_session->channels != metadata->data.stream_info.channels) {
|
||||
stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "ERROR, channels is %u in this link's STREAMINFO but was %u in previous one\n", metadata->data.stream_info.channels, decoder_session->channels);
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
}
|
||||
if(decoder_session->sample_rate != metadata->data.stream_info.sample_rate) {
|
||||
stats_print_name_and_stream_number(1, decoder_session->inbasefilename, decoder_session->stream_counter);
|
||||
flac__utils_printf(stderr, 1, "ERROR, sample rate is %u in this link's STREAMINFO but was %u in previous one\n", metadata->data.stream_info.sample_rate, decoder_session->sample_rate);
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
decoder_session->bps = metadata->data.stream_info.bits_per_sample;
|
||||
decoder_session->channels = metadata->data.stream_info.channels;
|
||||
decoder_session->sample_rate = metadata->data.stream_info.sample_rate;
|
||||
}
|
||||
if(decoder_session->stream_counter < 0) {
|
||||
if(!flac__utils_canonicalize_skip_until_specification(decoder_session->skip_specification, decoder_session->sample_rate)) {
|
||||
flac__utils_printf(stderr, 1, "%s: ERROR, value of --skip is too large\n", decoder_session->inbasefilename);
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
}
|
||||
FLAC__ASSERT(decoder_session->skip_specification->value.samples >= 0);
|
||||
skip = (FLAC__uint64)decoder_session->skip_specification->value.samples;
|
||||
}
|
||||
else
|
||||
skip = 0;
|
||||
|
||||
/* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */
|
||||
if(metadata->data.stream_info.total_samples > 0 && skip >= metadata->data.stream_info.total_samples) {
|
||||
|
@ -1563,12 +1614,16 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet
|
|||
decoder_session->total_samples = metadata->data.stream_info.total_samples - skip;
|
||||
|
||||
/* note that we use metadata->data.stream_info.total_samples instead of decoder_session->total_samples */
|
||||
if(!canonicalize_until_specification(decoder_session->until_specification, decoder_session->inbasefilename, decoder_session->sample_rate, skip, metadata->data.stream_info.total_samples)) {
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
if(decoder_session->stream_counter < 0) {
|
||||
if(!canonicalize_until_specification(decoder_session->until_specification, decoder_session->inbasefilename, decoder_session->sample_rate, skip, metadata->data.stream_info.total_samples)) {
|
||||
decoder_session->abort_flag = true;
|
||||
return;
|
||||
}
|
||||
FLAC__ASSERT(decoder_session->until_specification->value.samples >= 0);
|
||||
until = (FLAC__uint64)decoder_session->until_specification->value.samples;
|
||||
}
|
||||
FLAC__ASSERT(decoder_session->until_specification->value.samples >= 0);
|
||||
until = (FLAC__uint64)decoder_session->until_specification->value.samples;
|
||||
else
|
||||
until = 0;
|
||||
|
||||
if(until > 0) {
|
||||
FLAC__ASSERT(decoder_session->total_samples != 0);
|
||||
|
@ -1741,14 +1796,14 @@ void print_stats(const DecoderSession *decoder_session)
|
|||
if ((uint32_t)floor(progress + 0.5) == 100)
|
||||
return;
|
||||
|
||||
stats_print_name(2, decoder_session->inbasefilename);
|
||||
stats_print_name_and_stream_number(2, decoder_session->inbasefilename, decoder_session->stream_counter);
|
||||
stats_print_info(2, "%s%u%% complete",
|
||||
decoder_session->test_only? "testing, " : decoder_session->analysis_mode? "analyzing, " : "",
|
||||
(uint32_t)floor(progress + 0.5)
|
||||
);
|
||||
}
|
||||
else {
|
||||
stats_print_name(2, decoder_session->inbasefilename);
|
||||
stats_print_name_and_stream_number(2, decoder_session->inbasefilename, decoder_session->stream_counter);
|
||||
stats_print_info(2, "%s %" PRIu64 " samples",
|
||||
decoder_session->test_only? "tested" : decoder_session->analysis_mode? "analyzed" : "wrote",
|
||||
decoder_session->samples_processed
|
||||
|
|
|
@ -1119,7 +1119,7 @@ int flac__encode_file(FILE *infile, FLAC__off_t infilesize, const char *infilena
|
|||
memset(encoder_session.md5sum_input,0,16);
|
||||
}
|
||||
|
||||
stats_new_file();
|
||||
stats_new_line();
|
||||
/* init the encoder */
|
||||
if(!EncoderSession_init_encoder(&encoder_session, options))
|
||||
return EncoderSession_finish_error(&encoder_session);
|
||||
|
|
|
@ -375,7 +375,9 @@ int do_it(void)
|
|||
*/
|
||||
if(!option_values.mode_decode) {
|
||||
if(0 != option_values.cue_specification)
|
||||
return usage_error("ERROR: --cue is not allowed in test mode\n");
|
||||
return usage_error("ERROR: --cue must be used together with -d\n");
|
||||
if(0 != option_values.decode_chained_stream)
|
||||
return usage_error("ERROR: --decode-chained-streams must be used together with -d, -t or -a\n");
|
||||
}
|
||||
else {
|
||||
if(option_values.test_only) {
|
||||
|
@ -475,6 +477,18 @@ int do_it(void)
|
|||
return usage_error("ERROR: --keep-foreign-metadata is not allowed in analyis mode\n");
|
||||
flac__utils_printf(stderr, 1, "NOTE: --keep-foreign-metadata is a new feature; make sure to test the output file before deleting the original.\n");
|
||||
}
|
||||
if(0 != option_values.decode_chained_stream) {
|
||||
if(0 != option_values.skip_specification)
|
||||
return usage_error("ERROR: --skip is not supported when decoding chained streams\n");
|
||||
if(0 != option_values.until_specification)
|
||||
return usage_error("ERROR: --until is not supported when decoding chained streams\n");
|
||||
if(0 != option_values.cue_specification)
|
||||
return usage_error("ERROR: --cue is not supported when decoding chained streams\n");
|
||||
if(option_values.continue_through_decode_errors)
|
||||
return usage_error("ERROR: decoding through errors is not supported when decoding chained streams\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
flac__utils_printf(stderr, 2, "\n");
|
||||
|
|
|
@ -227,7 +227,7 @@ size_t strlen_console(const char *text)
|
|||
#endif
|
||||
}
|
||||
|
||||
void stats_new_file(void)
|
||||
void stats_new_line(void)
|
||||
{
|
||||
is_name_printed = false;
|
||||
stats_char_count = 0;
|
||||
|
@ -240,6 +240,11 @@ void stats_clear(void)
|
|||
}
|
||||
|
||||
void stats_print_name(int level, const char *name)
|
||||
{
|
||||
stats_print_name_and_stream_number(level, name, -1);
|
||||
}
|
||||
|
||||
void stats_print_name_and_stream_number(int level, const char *name, int stream_number)
|
||||
{
|
||||
int len;
|
||||
|
||||
|
@ -249,8 +254,13 @@ void stats_print_name(int level, const char *name)
|
|||
|
||||
console_width = get_console_width();
|
||||
len = strlen_console(name)+2;
|
||||
if(stream_number >= 0)
|
||||
len += 10 + floor(log10(stream_number));
|
||||
console_chars_left = console_width - (len % console_width);
|
||||
flac_fprintf(stderr, "%s: ", name);
|
||||
if(stream_number < 0)
|
||||
flac_fprintf(stderr, "%s: ", name);
|
||||
else
|
||||
flac_fprintf(stderr, "%s, stream %d: ", name, stream_number);
|
||||
is_name_printed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,10 @@ void flac__utils_printf(FILE *stream, int level, const char *format, ...);
|
|||
|
||||
int get_console_width(void);
|
||||
size_t strlen_console(const char *text);
|
||||
void stats_new_file(void);
|
||||
void stats_new_line(void);
|
||||
void stats_clear(void);
|
||||
void stats_print_name(int level, const char *name);
|
||||
void stats_print_name_and_stream_number(int level, const char *name, int stream_number);
|
||||
void stats_print_info(int level, const char *format, ...);
|
||||
void flac__utils_printf_clear_stats(FILE *stream, int level, const char *format, ...);
|
||||
|
||||
|
|
Loading…
Reference in New Issue