+ return false;
+ }
+ /* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around
+ * for Ogg FLAC. This is because the ogg decoder aspect can lose sync
+ * and at the same time hit the end of the stream (for example, seeking
+ * to a point that is after the beginning of the last Ogg page). There
+ * is no way to report an Ogg sync loss through the callbacks (see note
+ * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0.
+ * So to keep the decoder from stopping at this point we gate the call
+ * to the eof_callback and let the Ogg decoder aspect set the
+ * end-of-stream state when it is needed.
+ */
+}
+
+#if FLAC__HAS_OGG
+FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes)
+{
+ switch(FLAC__ogg_decoder_aspect_read_callback_wrapper(&decoder->protected_->ogg_decoder_aspect, buffer, bytes, read_callback_proxy_, decoder, decoder->private_->client_data)) {
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK:
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ /* we don't really have a way to handle lost sync via read
+ * callback so we'll let it pass and let the underlying
+ * FLAC decoder catch the error
+ */
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC:
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM:
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC:
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION:
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT:
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR:
+ case FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR:
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ default:
+ FLAC__ASSERT(0);
+ /* double protection */
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+}
+
+FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder*)void_decoder;
+
+ switch(decoder->private_->read_callback(decoder, buffer, bytes, client_data)) {
+ case FLAC__STREAM_DECODER_READ_STATUS_CONTINUE:
+ return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK;
+ case FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM:
+ return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM;
+ case FLAC__STREAM_DECODER_READ_STATUS_ABORT:
+ return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT;
+ default:
+ /* double protection: */
+ FLAC__ASSERT(0);
+ return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT;
+ }
+}
+#endif
+
+FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+ if(decoder->private_->is_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 = decoder->private_->target_sample;
+
+ FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
+
+#if FLAC__HAS_OGG
+ decoder->private_->got_a_frame = true;
+#endif
+ 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 */
+ decoder->private_->is_seeking = false;
+ /* 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;
+ decoder->private_->last_frame.header.blocksize -= delta;
+ decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta;
+ /* write the relevant samples */
+ return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data);
+ }
+ else {
+ /* write the relevant samples */
+ return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data);
+ }
+ }
+ else {
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ }
+ }
+ else {
+ /*
+ * If we never got STREAMINFO, turn off MD5 checking to save
+ * cycles since we don't have a sum to compare to anyway
+ */
+ if(!decoder->private_->has_stream_info)
+ decoder->private_->do_md5_checking = false;
+ if(decoder->private_->do_md5_checking) {
+ if(!FLAC__MD5Accumulate(&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 decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data);
+ }
+}
+
+void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status)
+{
+ if(!decoder->private_->is_seeking)
+ decoder->private_->error_callback(decoder, status, decoder->private_->client_data);
+ else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM)
+ decoder->private_->unparseable_frame_count++;
+}
+
+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, lower_bound_sample, upper_bound_sample, this_frame_sample;
+ FLAC__int64 pos = -1;
+ int i;
+ unsigned approx_bytes_per_frame;
+ FLAC__bool first_seek = true;
+ 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 min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize;
+ /* take these from the current frame in case they've changed mid-stream */
+ unsigned channels = FLAC__stream_decoder_get_channels(decoder);
+ unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder);
+ const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0;
+
+ /* use values from stream info if we didn't decode a frame */
+ if(channels == 0)
+ channels = decoder->private_->stream_info.data.stream_info.channels;
+ if(bps == 0)
+ bps = decoder->private_->stream_info.data.stream_info.bits_per_sample;
+
+ /* we are just guessing here */
+ if(max_framesize > 0)
+ approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1;
+ /*
+ * Check if it's a known fixed-blocksize stream. Note that though
+ * the spec doesn't allow zeroes in the STREAMINFO block, we may
+ * never get a STREAMINFO block when decoding so the value of
+ * min_blocksize might be zero.
+ */
+ else if(min_blocksize == max_blocksize && min_blocksize > 0) {
+ /* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */
+ approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64;
+ }
+ else
+ approx_bytes_per_frame = 4096 * channels * bps/8 + 64;
+
+ /*
+ * First, we set an upper and lower bound on where in the
+ * stream we will search. For now we assume the worst case
+ * scenario, which is our best guess at the beginning of
+ * the first frame and end of the stream.
+ */
+ lower_bound = first_frame_offset;
+ lower_bound_sample = 0;
+ upper_bound = stream_length;
+ upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/;
+
+ /*
+ * Now we refine the bounds if we have a seektable with
+ * suitable points. Note that according to the spec they
+ * must be ordered by ascending sample number.
+ *
+ * Note: to protect against invalid seek tables we will ignore points
+ * that have frame_samples==0 or sample_number>=total_samples
+ */
+ if(seek_table) {
+ FLAC__uint64 new_lower_bound = lower_bound;
+ FLAC__uint64 new_upper_bound = upper_bound;
+ FLAC__uint64 new_lower_bound_sample = lower_bound_sample;
+ FLAC__uint64 new_upper_bound_sample = upper_bound_sample;
+
+ /* find the closest seek point <= target_sample, if it exists */
+ for(i = (int)seek_table->num_points - 1; i >= 0; i--) {
+ if(
+ seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER &&
+ seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */
+ (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */
+ seek_table->points[i].sample_number <= target_sample
+ )
+ break;
+ }
+ if(i >= 0) { /* i.e. we found a suitable seek point... */
+ new_lower_bound = first_frame_offset + seek_table->points[i].stream_offset;
+ new_lower_bound_sample = seek_table->points[i].sample_number;
+ }
+
+ /* find the closest seek point > target_sample, if it exists */
+ for(i = 0; i < (int)seek_table->num_points; i++) {
+ if(
+ seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER &&
+ seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */
+ (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */
+ seek_table->points[i].sample_number > target_sample
+ )
+ break;
+ }
+ if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */
+ new_upper_bound = first_frame_offset + seek_table->points[i].stream_offset;
+ new_upper_bound_sample = seek_table->points[i].sample_number;
+ }
+ /* final protection against unsorted seek tables; keep original values if bogus */
+ if(new_upper_bound >= new_lower_bound) {
+ lower_bound = new_lower_bound;
+ upper_bound = new_upper_bound;
+ lower_bound_sample = new_lower_bound_sample;
+ upper_bound_sample = new_upper_bound_sample;
+ }
+ }
+
+ FLAC__ASSERT(upper_bound_sample >= lower_bound_sample);
+ /* there are 2 insidious ways that the following equality occurs, which
+ * we need to fix:
+ * 1) total_samples is 0 (unknown) and target_sample is 0
+ * 2) total_samples is 0 (unknown) and target_sample happens to be
+ * exactly equal to the last seek point in the seek table; this
+ * means there is no seek point above it, and upper_bound_samples
+ * remains equal to the estimate (of target_samples) we made above
+ * in either case it does not hurt to move upper_bound_sample up by 1
+ */
+ if(upper_bound_sample == lower_bound_sample)
+ upper_bound_sample++;
+
+ decoder->private_->target_sample = target_sample;
+ while(1) {
+ /* check if the bounds are still ok */
+ if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) {
+ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+#ifndef FLAC__INTEGER_ONLY_LIBRARY
+#if defined _MSC_VER || defined __MINGW32__
+ /* 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_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(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_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame;
+#endif
+ 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;
+ }
+ if(!FLAC__stream_decoder_flush(decoder)) {
+ /* above call sets the state for us */
+ return false;
+ }
+ /* Now we need to get a frame. First we need to reset our
+ * unparseable_frame_count; if we get too many unparseable
+ * frames in a row, the read callback will return
+ * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing
+ * FLAC__stream_decoder_process_single() to return false.
+ */
+ decoder->private_->unparseable_frame_count = 0;
+ if(!FLAC__stream_decoder_process_single(decoder)) {
+ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+ /* our write callback will change the state when it gets to the target frame */
+ /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */
+#if 0
+ /*@@@@@@ used to be the following; not clear if the check for end of stream is needed anymore */
+ if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM)
+ break;
+#endif
+ if(!decoder->private_->is_seeking)
+ break;
+
+ FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
+ this_frame_sample = decoder->private_->last_frame.header.number.sample_number;
+
+ if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) {
+ if (pos == (FLAC__int64)lower_bound) {
+ /* can't move back any more than the first frame, something is fatally wrong */
+ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+ /* our last move backwards wasn't big enough, try again */
+ approx_bytes_per_frame = approx_bytes_per_frame? approx_bytes_per_frame * 2 : 16;
+ continue;
+ }
+ /* allow one seek over upper bound, so we can get a correct upper_bound_sample for streams with unknown total_samples */
+ first_seek = false;
+
+ /* make sure we are not seeking in corrupted stream */
+ if (this_frame_sample < lower_bound_sample) {
+ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+
+ /* we need to narrow the search */
+ if(target_sample < this_frame_sample) {
+ upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize;
+/*@@@@@@ what will decode position be if at end of stream? */
+ if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) {
+ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+ approx_bytes_per_frame = (unsigned)(2 * (upper_bound - pos) / 3 + 16);
+ }
+ else { /* target_sample >= this_frame_sample + this frame's blocksize */
+ 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;
+ }
+ approx_bytes_per_frame = (unsigned)(2 * (lower_bound - pos) / 3 + 16);
+ }
+ }
+
+ return true;
+}
+
+#if FLAC__HAS_OGG
+FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *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 = FLAC__stream_decoder_get_total_samples(decoder);
+ FLAC__uint64 this_frame_sample = (FLAC__uint64)0 - 1;
+ 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
+ * than 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
+ * 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_frame_sample and so having a better
+ * estimate.
+ */
+ }
+
+ /* physical seek */
+ if(decoder->private_->seek_callback((FLAC__StreamDecoder*)decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) {
+ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+ if(!FLAC__stream_decoder_flush(decoder)) {
+ /* above call sets the state for us */
+ return false;
+ }
+ did_a_seek = true;
+ }
+ else
+ did_a_seek = false;
+
+ decoder->private_->got_a_frame = false;
+ if(!FLAC__stream_decoder_process_single(decoder)) {
+ decoder->protected_->state = FLAC__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 = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+ }
+ /* our write callback will change the state when it gets to the target frame */
+ else if(!decoder->private_->is_seeking) {
+ 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
+ * FLAC__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 = FLAC__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 = FLAC__STREAM_DECODER_SEEK_ERROR;
+ return false;
+ }
+ right_pos = pos;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+#endif
+
+FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ (void)client_data;
+
+ if(*bytes > 0) {
+ *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file);
+ if(ferror(decoder->private_->file))
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ else if(*bytes == 0)
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
+}
+
+FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ (void)client_data;
+
+ if(decoder->private_->file == stdin)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+ else if(fseeko(decoder->private_->file, (FLAC__off_t)absolute_byte_offset, SEEK_SET) < 0)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+ else
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ FLAC__off_t pos;
+ (void)client_data;
+
+ if(decoder->private_->file == stdin)
+ return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+ else if((pos = ftello(decoder->private_->file)) < 0)
+ return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+ else {
+ *absolute_byte_offset = (FLAC__uint64)pos;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+ }
+}
+
+FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+ struct flac_stat_s filestats;
+ (void)client_data;
+
+ if(decoder->private_->file == stdin)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+ else if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+ else {
+ *stream_length = (FLAC__uint64)filestats.st_size;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ }
+}
+
+FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data)
+{
+ (void)client_data;
+
+ return feof(decoder->private_->file)? true : false;