Ok, the patch from 2003 about improving seeking still didn't make it to CVS, so here is another try.
I made some benchmarking with the test_seeking utility from flac sources to show how bad the current seeking is, especially without seektable. Track used for the experiment had about 50 minutes. In the following table is average number of seeks and number of decoded frames required for one complete absolute seek. I tried several files encoded with fixed/variable block size, seektable with seekpoints every 10s, 100s and no seektable. unpatched patched block size seek decoded decoded points seeks frames seeks frames fixed 10s 1.22 2.04 1.16 1.83 100s 6.37 12.26 2.22 3.21 - 16.80 341.61 3.84 5.23 variable 10s 1.18 2.93 1.42 2.57 100s 8.80 16.65 2.58 3.67 - 31.35 445.08 4.28 5.67 And another table shows the worst seek for the same files. block size seek decoded decoded points seeks frames seeks frames fixed 10s 49 49 3 6 100s 55 55 5 8 - 1 1038 6 9 variable 10s 53 53 9 9 100s 87 9 8 8 - 1 1620 10 13 Attached is the new patch. -- Miroslav Lichvar
Index: stream_decoder.c =================================================================== RCS file: /cvsroot/flac/flac/src/libFLAC/stream_decoder.c,v retrieving revision 1.117 diff -u -r1.117 stream_decoder.c --- stream_decoder.c 16 Oct 2006 15:49:18 -0000 1.117 +++ stream_decoder.c 28 Oct 2006 17:12:19 -0000 @@ -2923,18 +2923,18 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) { - FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound; + FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample; FLAC__int64 pos = -1, last_pos = -1; - int i, lower_seek_point = -1, upper_seek_point = -1; + int i; unsigned approx_bytes_per_frame; - FLAC__uint64 last_frame_sample = FLAC__U64L(0xffffffffffffffff); + FLAC__uint64 last_frame_sample = FLAC__U64L(0xffffffffffffffff), this_frame_sample; FLAC__bool needs_seek; const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); const unsigned min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; const unsigned max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; const unsigned max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; - const unsigned channels = FLAC__stream_decoder_get_channels(decoder); - const unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + const unsigned channels = decoder->private_->stream_info.data.stream_info.channels; + const unsigned bps = decoder->private_->stream_info.data.stream_info.bits_per_sample; const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; /* we are just guessing here, but we want to guess high, not low */ @@ -2961,12 +2961,14 @@ * the first and last frames. */ lower_bound = first_frame_offset; + lower_bound_sample = 0; /* calc the upper_bound, beyond which we never want to seek */ if(max_framesize > 0) upper_bound = stream_length - (max_framesize + 128 + 2); /* 128 for a possible ID3V1 tag, 2 for indexing differences */ else upper_bound = stream_length - ((channels * bps * FLAC__MAX_BLOCK_SIZE) / 8 + 128 + 2); + upper_bound_sample = total_samples > 0 ? total_samples : target_sample; /* * Now we refine the bounds if we have a seektable with @@ -2981,7 +2983,7 @@ } if(i >= 0) { /* i.e. we found a suitable seek point... */ lower_bound = first_frame_offset + seek_table->points[i].stream_offset; - lower_seek_point = i; + lower_bound_sample = seek_table->points[i].sample_number; } /* find the closest seek point > target_sample, if it exists */ @@ -2991,98 +2993,32 @@ } if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */ upper_bound = first_frame_offset + seek_table->points[i].stream_offset; - upper_seek_point = i; + upper_bound_sample = seek_table->points[i].sample_number; } } + decoder->private_->target_sample = target_sample; - /* - * Now guess at where within those bounds our target - * sample will be. - */ - if(seek_table && lower_seek_point >= 0) { - /* first see if our sample is within a few frames of the lower seekpoint */ - if(seek_table->points[lower_seek_point].sample_number <= target_sample && target_sample < seek_table->points[lower_seek_point].sample_number + (seek_table->points[lower_seek_point].frame_samples * 4)) { - pos = (FLAC__int64)lower_bound; - } - else if(upper_seek_point >= 0) { - const FLAC__uint64 target_offset = target_sample - seek_table->points[lower_seek_point].sample_number; - const FLAC__uint64 range_samples = seek_table->points[upper_seek_point].sample_number - seek_table->points[lower_seek_point].sample_number; - const FLAC__uint64 range_bytes = (upper_bound>lower_bound? upper_bound - lower_bound - 1 : 0); + needs_seek = true; + while(1) { + if(needs_seek) { #ifndef FLAC__INTEGER_ONLY_LIBRARY #if defined _MSC_VER || defined __MINGW32__ - /* with MSVC you have to spoon feed it the casting */ - pos = (FLAC__int64)lower_bound + (FLAC__int64)(((FLAC__double)(FLAC__int64)target_offset / (FLAC__double)(FLAC__int64)range_samples) * (FLAC__double)(FLAC__int64)(range_bytes-1)) - approx_bytes_per_frame; + /* with VC++ you have to spoon feed it the casting */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(FLAC__int64)(target_sample - lower_bound_sample) / (FLAC__double)(FLAC__int64)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(FLAC__int64)(upper_bound - lower_bound)) - approx_bytes_per_frame; #else - pos = (FLAC__int64)lower_bound + (FLAC__int64)(((FLAC__double)target_offset / (FLAC__double)range_samples) * (FLAC__double)range_bytes) - approx_bytes_per_frame; + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(target_sample - lower_bound_sample) / (FLAC__double)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(upper_bound - lower_bound)) - approx_bytes_per_frame; #endif #else /* a little less accurate: */ - if (range_bytes <= 0xffffffff) - pos = (FLAC__int64)lower_bound + (FLAC__int64)((target_offset * range_bytes) / range_samples) - approx_bytes_per_frame; + if (upper_bound - lower_bound < 0xffffffff) + pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; else /* @@@ WATCHOUT, ~2TB limit */ - pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_offset>>8) * (range_bytes>>8)) / (range_samples>>16)) - approx_bytes_per_frame; -#endif - } - } - - /* - * If there's no seek table, we need to use the metadata (if we - * have it) and the filelength to estimate the position of the - * frame with the correct sample. - */ - if(pos < 0 && total_samples > 0) { - /* - * For max accuracy we should be using - * (stream_length-first_frame_offset-1) in the divisor, but the - * difference is trivial and (stream_length-first_frame_offset) - * has no chance of underflow. - */ -#ifndef FLAC__INTEGER_ONLY_LIBRARY -#if defined _MSC_VER || defined __MINGW32__ - /* with VC++ you have to spoon feed it the casting */ - pos = (FLAC__int64)first_frame_offset + (FLAC__int64)(((FLAC__double)(FLAC__int64)target_sample / (FLAC__double)(FLAC__int64)total_samples) * (FLAC__double)(FLAC__int64)(stream_length-first_frame_offset)) - approx_bytes_per_frame; -#else - pos = (FLAC__int64)first_frame_offset + (FLAC__int64)(((FLAC__double)target_sample / (FLAC__double)total_samples) * (FLAC__double)(stream_length-first_frame_offset)) - approx_bytes_per_frame; -#endif -#else - /* a little less accurate: */ - if (stream_length < 0xffffffff) - pos = (FLAC__int64)first_frame_offset + (FLAC__int64)((target_sample * (stream_length-first_frame_offset)) / total_samples) - approx_bytes_per_frame; - else /* @@@ WATCHOUT, ~2TB limit */ - pos = (FLAC__int64)first_frame_offset + (FLAC__int64)(((target_sample>>8) * ((stream_length-first_frame_offset)>>8)) / (total_samples>>16)) - approx_bytes_per_frame; + pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame; #endif - } - - /* - * If there's no seek table and total_samples is unknown, we - * don't even bother trying to figure out a target, we just use - * our current position. - */ - if(pos < 0) { - FLAC__uint64 upos; - if(decoder->private_->tell_callback(decoder, &upos, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) { - decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; - return false; - } - pos = (FLAC__int64)upos; - needs_seek = false; - } - else - needs_seek = true; - - /* clip the position to the bounds, lower bound takes precedence */ - if(pos >= (FLAC__int64)upper_bound) { - pos = (FLAC__int64)upper_bound-1; - needs_seek = true; - } - if(pos < (FLAC__int64)lower_bound) { - pos = (FLAC__int64)lower_bound; - needs_seek = true; - } - - decoder->private_->target_sample = target_sample; - while(1) { - if(needs_seek) { + if(pos >= (FLAC__int64)upper_bound) + pos = (FLAC__int64)upper_bound - 1; + if(pos < (FLAC__int64)lower_bound) + pos = (FLAC__int64)lower_bound; if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; return false; @@ -3113,45 +3049,43 @@ if(!decoder->private_->is_seeking) { break; } - else { /* we need to narrow the search */ - const FLAC__uint64 this_frame_sample = decoder->private_->last_frame.header.number.sample_number; - FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); - if(this_frame_sample == last_frame_sample && pos < last_pos) { - /* our last move backwards wasn't big enough, double it */ - pos -= (last_pos - pos); - needs_seek = true; + /* we need to narrow the search */ + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + + approx_bytes_per_frame = decoder->private_->last_frame.header.blocksize * channels * bps/8 + 64; + + if(target_sample < this_frame_sample) { + if(this_frame_sample == last_frame_sample) { + /* our last move backwards wasn't big enough */ + upper_bound -= approx_bytes_per_frame; } else { - if(target_sample < this_frame_sample) { - last_pos = pos; - approx_bytes_per_frame = decoder->private_->last_frame.header.blocksize * channels * bps/8 + 64; - pos -= approx_bytes_per_frame; - needs_seek = true; - } - else { /* target_sample >= this_frame_sample + this frame's blocksize */ - FLAC__uint64 upos; - if(decoder->private_->tell_callback(decoder, &upos, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) { - decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; - return false; - } - last_pos = pos; - pos = (FLAC__int64)upos; - pos -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder); - needs_seek = false; - /* - * if we haven't hit the target frame yet and our position hasn't changed, - * it means we're at the end of the stream and the seek target does not exist. - */ - if(last_pos == pos) { - decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; - return false; - } - } + upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } } - if(pos < (FLAC__int64)lower_bound) - pos = (FLAC__int64)lower_bound; - last_frame_sample = this_frame_sample; } + else { + /* target_sample >= this_frame_sample + this frame's blocksize */ + + if(target_sample < this_frame_sample + 4 * decoder->private_->last_frame.header.blocksize) + needs_seek = false; + + lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(last_pos == (FLAC__int64)lower_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + last_pos = lower_bound; + } + last_frame_sample = this_frame_sample; } return true;
_______________________________________________ Flac-dev mailing list Flac-dev@xiph.org http://lists.xiph.org/mailman/listinfo/flac-dev