📄 seekable_stream_decoder.c
字号:
}OggFLAC_API FLAC__bool OggFLAC__seekable_stream_decoder_seek_absolute(OggFLAC__SeekableStreamDecoder *decoder, FLAC__uint64 sample){ FLAC__uint64 length; FLAC__ASSERT(0 != decoder); FLAC__ASSERT(decoder->protected_->state == OggFLAC__SEEKABLE_STREAM_DECODER_OK || decoder->protected_->state == OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM); decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING; /* turn off md5 checking if a seek is attempted */ decoder->private_->do_md5_checking = false; if(!OggFLAC__stream_decoder_reset(decoder->private_->stream_decoder)) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR; return false; } /* get the file length */ if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } /* rewind */ if(decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) != OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } if(!OggFLAC__stream_decoder_process_until_end_of_metadata(decoder->private_->stream_decoder)) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR; return false; } if(decoder->private_->stream_info.total_samples > 0 && sample >= decoder->private_->stream_info.total_samples) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } return seek_to_absolute_sample_(decoder, length, sample);}/*********************************************************************** * * Private class methods * ***********************************************************************/void set_defaults_(OggFLAC__SeekableStreamDecoder *decoder){ decoder->private_->read_callback = 0; decoder->private_->seek_callback = 0; decoder->private_->tell_callback = 0; decoder->private_->length_callback = 0; decoder->private_->eof_callback = 0; decoder->private_->write_callback = 0; decoder->private_->metadata_callback = 0; decoder->private_->error_callback = 0; decoder->private_->client_data = 0; /* WATCHOUT: these should match the default behavior of OggFLAC__StreamDecoder */ decoder->private_->ignore_stream_info_block = false; decoder->private_->ignore_seek_table_block = true; decoder->protected_->md5_checking = false;}FLAC__StreamDecoderReadStatus read_callback_(const OggFLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data){ OggFLAC__SeekableStreamDecoder *seekable_stream_decoder = (OggFLAC__SeekableStreamDecoder *)client_data; (void)decoder; if(seekable_stream_decoder->private_->eof_callback(seekable_stream_decoder, seekable_stream_decoder->private_->client_data)) { *bytes = 0;#if 0 /*@@@@@@ we used to do this: */ seekable_stream_decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM; /* but it causes a problem because the Ogg decoding layer reads as much as it can to get pages, so the state will get to end-of-stream before the bitbuffer does */#endif return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; } else if(*bytes > 0) { if(seekable_stream_decoder->private_->read_callback(seekable_stream_decoder, buffer, bytes, seekable_stream_decoder->private_->client_data) != OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK) { seekable_stream_decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_READ_ERROR; return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } if(*bytes == 0) { if(seekable_stream_decoder->private_->eof_callback(seekable_stream_decoder, seekable_stream_decoder->private_->client_data)) {#if 0 /*@@@@@@ we used to do this: */ seekable_stream_decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM; /* but it causes a problem because the Ogg decoding layer reads as much as it can to get pages, so the state will get to end-of-stream before the bitbuffer does */#endif return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; } else return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } else { return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } } else return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */}FLAC__StreamDecoderWriteStatus write_callback_(const OggFLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data){ OggFLAC__SeekableStreamDecoder *seekable_stream_decoder = (OggFLAC__SeekableStreamDecoder *)client_data; (void)decoder; if(seekable_stream_decoder->protected_->state == OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING) { FLAC__uint64 this_frame_sample = frame->header.number.sample_number; FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; FLAC__uint64 target_sample = seekable_stream_decoder->private_->target_sample; FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); seekable_stream_decoder->private_->got_a_frame = true; seekable_stream_decoder->private_->last_frame = *frame; /* save the frame */ if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ unsigned delta = (unsigned)(target_sample - this_frame_sample); /* kick out of seek mode */ seekable_stream_decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_OK; /* shift out the samples before target_sample */ if(delta > 0) { unsigned channel; const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; for(channel = 0; channel < frame->header.channels; channel++) newbuffer[channel] = buffer[channel] + delta; seekable_stream_decoder->private_->last_frame.header.blocksize -= delta; seekable_stream_decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; /* write the relevant samples */ return seekable_stream_decoder->private_->write_callback(seekable_stream_decoder, &seekable_stream_decoder->private_->last_frame, newbuffer, seekable_stream_decoder->private_->client_data); } else { /* write the relevant samples */ return seekable_stream_decoder->private_->write_callback(seekable_stream_decoder, frame, buffer, seekable_stream_decoder->private_->client_data); } } else { return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } } else { if(seekable_stream_decoder->private_->do_md5_checking) { if(!FLAC__MD5Accumulate(&seekable_stream_decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } return seekable_stream_decoder->private_->write_callback(seekable_stream_decoder, frame, buffer, seekable_stream_decoder->private_->client_data); }}void metadata_callback_(const OggFLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data){ OggFLAC__SeekableStreamDecoder *seekable_stream_decoder = (OggFLAC__SeekableStreamDecoder *)client_data; (void)decoder; if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { seekable_stream_decoder->private_->stream_info = metadata->data.stream_info; /* save the MD5 signature for comparison later */ memcpy(seekable_stream_decoder->private_->stored_md5sum, metadata->data.stream_info.md5sum, 16); if(0 == memcmp(seekable_stream_decoder->private_->stored_md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) seekable_stream_decoder->private_->do_md5_checking = false; } else if(metadata->type == FLAC__METADATA_TYPE_SEEKTABLE) { seekable_stream_decoder->private_->seek_table = &metadata->data.seek_table; } if(seekable_stream_decoder->protected_->state != OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING) { FLAC__bool ignore_block = false; if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO && seekable_stream_decoder->private_->ignore_stream_info_block) ignore_block = true; else if(metadata->type == FLAC__METADATA_TYPE_SEEKTABLE && seekable_stream_decoder->private_->ignore_seek_table_block) ignore_block = true; if(!ignore_block) seekable_stream_decoder->private_->metadata_callback(seekable_stream_decoder, metadata, seekable_stream_decoder->private_->client_data); }}void error_callback_(const OggFLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data){ OggFLAC__SeekableStreamDecoder *seekable_stream_decoder = (OggFLAC__SeekableStreamDecoder *)client_data; (void)decoder; if(seekable_stream_decoder->protected_->state != OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING) seekable_stream_decoder->private_->error_callback(seekable_stream_decoder, status, seekable_stream_decoder->private_->client_data);}FLAC__bool seek_to_absolute_sample_(OggFLAC__SeekableStreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample){ FLAC__uint64 left_pos = 0, right_pos = stream_length; FLAC__uint64 left_sample = 0, right_sample = decoder->private_->stream_info.total_samples; FLAC__uint64 this_frame_sample = 0; /* only initialized to avoid compiler warning */ FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ FLAC__bool did_a_seek; unsigned iteration = 0; /* In the first iterations, we will calculate the target byte position * by the distance from the target sample to left_sample and * right_sample (let's call it "proportional search"). After that, we * will switch to binary search. */ unsigned BINARY_SEARCH_AFTER_ITERATION = 2; /* We will switch to a linear search once our current sample is less * that this number of samples ahead of the target sample */ static const FLAC__uint64 LINEAR_SEARCH_WITHIN_SAMPLES = FLAC__MAX_BLOCK_SIZE * 2; /* If the total number of samples is unknown, use a large value and * increase 'iteration' to force binary search immediately. */ if(right_sample == 0) { right_sample = (FLAC__uint64)(-1); BINARY_SEARCH_AFTER_ITERATION = 0; } decoder->private_->target_sample = target_sample; for( ; ; iteration++) { if (iteration == 0 || this_frame_sample > target_sample || target_sample - this_frame_sample > LINEAR_SEARCH_WITHIN_SAMPLES) { if (iteration >= BINARY_SEARCH_AFTER_ITERATION) { pos = (right_pos + left_pos) / 2; } else {#ifndef FLAC__INTEGER_ONLY_LIBRARY#if defined _MSC_VER || defined __MINGW32__ /* with MSVC you have to spoon feed it the casting */ pos = (FLAC__uint64)((FLAC__double)(FLAC__int64)(target_sample - left_sample) / (FLAC__double)(FLAC__int64)(right_sample - left_sample) * (FLAC__double)(FLAC__int64)(right_pos - left_pos));#else pos = (FLAC__uint64)((FLAC__double)(target_sample - left_sample) / (FLAC__double)(right_sample - left_sample) * (FLAC__double)(right_pos - left_pos));#endif#else /* a little less accurate: */ if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)); else /* @@@ WATCHOUT, ~2TB limit */ pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16));#endif /* @@@ TODO: might want to limit pos to some distance * before EOF, to make sure we land before the last frame, * thereby getting a this_fram_sample and so having a better * estimate. this would also mostly (or totally if we could * be sure to land before the last frame) avoid the * end-of-stream case we have to check later. */ } /* physical seek */ if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } if(!OggFLAC__stream_decoder_flush(decoder->private_->stream_decoder)) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR; return false; } did_a_seek = true; } else did_a_seek = false; decoder->private_->got_a_frame = false; if(!OggFLAC__stream_decoder_process_single(decoder->private_->stream_decoder)) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } if(!decoder->private_->got_a_frame) { if(did_a_seek) { /* this can happen if we seek to a point after the last frame; we drop * to binary search right away in this case to avoid any wasted * iterations of proportional search. */ right_pos = pos; BINARY_SEARCH_AFTER_ITERATION = 0; } else { /* this can probably only happen if total_samples is unknown and the * target_sample is past the end of the stream */ decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } } /* our write callback will change the state when it gets to the target frame */ else if( decoder->protected_->state != OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM ) { break; } else { 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 (did_a_seek) { if (this_frame_sample <= target_sample) { /* The 'equal' case should not happen, since * OggFLAC__stream_decoder_process_single() * should recognize that it has hit the * target sample and we would exit through * the 'break' above. */ FLAC__ASSERT(this_frame_sample != target_sample); left_sample = this_frame_sample; /* sanity check to avoid infinite loop */ if (left_pos == pos) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } left_pos = pos; } else if(this_frame_sample > target_sample) { right_sample = this_frame_sample; /* sanity check to avoid infinite loop */ if (right_pos == pos) { decoder->protected_->state = OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR; return false; } right_pos = pos; } } } } return true;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -