From 7178a84291eaa302428a1e4d8043692508110b73 Mon Sep 17 00:00:00 2001 From: Miguel Freitas Date: Sat, 18 Jan 2003 20:01:59 +0000 Subject: flac demuxer+decoder based on work from John McCutchan play and seeking seem to work. lots of debug messages and maybe bugs. it needs automagic stuff to detect libflac... may someone take care of that? Stephen? CVS patchset: 3961 CVS date: 2003/01/18 20:01:59 --- src/libflac/Makefile.am | 33 +++ src/libflac/decoder_flac.c | 408 ++++++++++++++++++++++++++++++ src/libflac/demux_flac.c | 601 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1042 insertions(+) create mode 100644 src/libflac/Makefile.am create mode 100644 src/libflac/decoder_flac.c create mode 100644 src/libflac/demux_flac.c diff --git a/src/libflac/Makefile.am b/src/libflac/Makefile.am new file mode 100644 index 000000000..9fe261d85 --- /dev/null +++ b/src/libflac/Makefile.am @@ -0,0 +1,33 @@ +# +# the Makefile for flac plugins +# + +LIBTOOL = $(SHELL) $(top_builddir)/libtool-nofpic + +libdir = $(XINE_PLUGINDIR) +XINE_LIB = $(top_builddir)/src/xine-engine/libxine.la + +lib_LTLIBRARIES = xineplug_flac.la + +xineplug_flac_la_SOURCES = demux_flac.c decoder_flac.c +xineplug_flac_la_LDFLAGS = -avoid-version -module -lFLAC @XINE_PLUGIN_MIN_SYMS@ + + +# noinst_HEADERS = + +$(XINE_LIB): + @cd $(top_builddir)/src/xine-engine && $(MAKE) + +debug: + @$(MAKE) CFLAGS="$(DEBUG_CFLAGS)" + +install-debug: debug + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in diff --git a/src/libflac/decoder_flac.c b/src/libflac/decoder_flac.c new file mode 100644 index 000000000..f46d35cbf --- /dev/null +++ b/src/libflac/decoder_flac.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2000-2002 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * John McCutchan 2003 + * FLAC Decoder (http://flac.sf.net) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + + +#include "xine_internal.h" +#include "audio_out.h" +#include "buffer.h" + + +typedef struct { + audio_decoder_class_t decoder_class; +} flac_class_t; + +typedef struct flac_decoder_s { + audio_decoder_t audio_decoder; + + int64_t pts; + + int output_sampling_rate; + int output_open; + int output_mode; + + xine_stream_t *stream; + + FLAC__StreamDecoder *flac_decoder; + + int sample_rate; + int bits_per_sample; + int channels; + + unsigned char *buf; + int buf_size; + int buf_pos; + int min_size; + +} flac_decoder_t; + +/* + * FLAC callback functions + */ + +static FLAC__StreamDecoderReadStatus +flac_read_callback (const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], + unsigned *bytes, + void *client_data) +{ + flac_decoder_t *this = (flac_decoder_t *)client_data; + int number_of_bytes_to_copy; + + printf("FLAC_DEC: flac_read_callback: %d\n", *bytes); + + if (this->buf_pos > *bytes) + number_of_bytes_to_copy = *bytes; + else + number_of_bytes_to_copy = this->buf_pos; + + printf("FLAC_DEC: number_of_bytes_to_copy: %d\n", number_of_bytes_to_copy); + + *bytes = number_of_bytes_to_copy; + + xine_fast_memcpy (buffer, this->buf, number_of_bytes_to_copy); + + this->buf_pos -= number_of_bytes_to_copy; + memmove(this->buf, &this->buf[number_of_bytes_to_copy], this->buf_pos ); + + if(number_of_bytes_to_copy) + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; +} + +static FLAC__StreamDecoderWriteStatus +flac_write_callback (const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], + void *client_data) +{ + flac_decoder_t *this = (flac_decoder_t *)client_data; + audio_buffer_t *audio_buffer = NULL; + int samples_left = frame->header.blocksize; + int bytes_per_sample = (frame->header.bits_per_sample == 8)?1:2; + int buf_samples; + int8_t *data8; + int16_t *data16; + int i,j; + + printf("FLAC_DEC: flac_write_callback\n"); + + while( samples_left ) { + + audio_buffer = this->stream->audio_out->get_buffer(this->stream->audio_out); + + if( audio_buffer->mem_size < samples_left * frame->header.channels * bytes_per_sample ) + buf_samples = audio_buffer->mem_size / (frame->header.channels * bytes_per_sample); + else + buf_samples = samples_left; + + + if( frame->header.bits_per_sample == 8 ) { + data8 = (int8_t *)audio_buffer->mem; + + for( j=0; j < buf_samples; j++ ) + for( i=0; i < frame->header.channels; i++ ) + *data8++ = buffer[i][j]; + + } else { + + data16 = (int16_t *)audio_buffer->mem; + + for( j=0; j < buf_samples; j++ ) + for( i=0; i < frame->header.channels; i++ ) + *data16++ = buffer[i][j]; + } + + audio_buffer->num_frames = buf_samples; + audio_buffer->vpts = this->pts; + this->pts = 0; + this->stream->audio_out->put_buffer (this->stream->audio_out, audio_buffer, this->stream); + + samples_left -= buf_samples; + } + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void +flac_metadata_callback (const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) +{ + flac_decoder_t *this = (flac_decoder_t *)client_data; + + printf("FLAC_DEC: Metadata callback called!\n"); + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + printf("FLAC_DEC: min_blocksize = %d\n", metadata->data.stream_info.min_blocksize); + printf("FLAC_DEC: max_blocksize = %d\n", metadata->data.stream_info.max_blocksize); + printf("FLAC_DEC: min_framesize = %d\n", metadata->data.stream_info.min_framesize); + printf("FLAC_DEC: max_framesize = %d\n", metadata->data.stream_info.max_framesize); + + /* does not work well: + this->min_size = 2 * metadata->data.stream_info.max_blocksize; */ + } + return; +} + +static void +flac_error_callback (const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) +{ + /* This will be called if there is an error in the flac stream */ + printf("FLAC_DEC: flac_error_callback\n"); + + if (status == FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + printf("FLAC_DEC: Decoder lost synchronization.\n"); + else if (status == FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER) + printf("FLAC_DEC: Decoder encounted a corrupted frame header.\n"); + else if (status == FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH) + printf("FLAC_DEC: Frame's data did not match the CRC in the footer.\n"); + else + printf("FLAC_DEC: unknown error.\n"); + + return; +} + + + + +/* + * FLAC plugin decoder + */ + +static void +flac_reset (audio_decoder_t *this_gen) +{ + flac_decoder_t *this = (flac_decoder_t *) this_gen; + + this->buf_pos = 0; + + if( FLAC__stream_decoder_get_state(this->flac_decoder) != + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA ) + FLAC__stream_decoder_flush (this->flac_decoder); +} + +static void +flac_discontinuity (audio_decoder_t *this_gen) +{ + flac_decoder_t *this = (flac_decoder_t *) this_gen; + + this->pts = 0; + printf("FLAC_DEC: Discontinuity!\n"); +} + +static void +flac_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) +{ + flac_decoder_t *this = (flac_decoder_t *) this_gen; + int ret = 1; + + /* We are getting the stream header, open up the audio + * device, and collect information about the stream + */ + if (buf->decoder_flags & BUF_FLAG_HEADER) + { + int mode = AO_CAP_MODE_MONO; + + this->sample_rate = buf->decoder_info[1]; + this->bits_per_sample = buf->decoder_info[2]; + this->channels = buf->decoder_info[3]; + + switch (this->channels) + { + case 1: + mode = AO_CAP_MODE_MONO; + break; + case 2: + mode = AO_CAP_MODE_STEREO; + break; + case 4: + mode = AO_CAP_MODE_4CHANNEL; + break; + case 5: + mode = AO_CAP_MODE_5CHANNEL; + break; + case 6: + mode = AO_CAP_MODE_5_1CHANNEL; + } + + if (!this->output_open) + { + this->output_open = this->stream->audio_out->open ( + this->stream->audio_out, + this->stream, + this->bits_per_sample, + this->sample_rate, + mode); + + + } + this->buf_pos = 0; + } else if (this->output_open) + { + /* This isn't a header frame and we have opened the output device */ + + + /* What we have buffered so far, and what is coming in + * is larger than our buffer + */ + if (this->buf_pos + buf->size > this->buf_size) + { + this->buf_size += 2 * buf->size; + this->buf = realloc (this->buf, this->buf_size); + printf("FLAC_DEC: reallocating buffer to %d\n", this->buf_size); + } + + xine_fast_memcpy (&this->buf[this->buf_pos], buf->content, buf->size); + this->buf_pos += buf->size; + + if (buf->pts) + this->pts = buf->pts; + + /* We have enough to decode a frame */ + while( ret && this->buf_pos > this->min_size ) { + + if( FLAC__stream_decoder_get_state(this->flac_decoder) == + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA ) { + printf("FLAC_DEC: process_until_end_of_metadata\n"); + ret = FLAC__stream_decoder_process_until_end_of_metadata (this->flac_decoder); + } else { + printf("FLAC_DEC: process_single\n"); + ret = FLAC__stream_decoder_process_single (this->flac_decoder); + } + } + } else + return; + + +} + +static void +flac_dispose (audio_decoder_t *this_gen) { + flac_decoder_t *this = (flac_decoder_t *) this_gen; + + FLAC__stream_decoder_finish (this->flac_decoder); + + FLAC__stream_decoder_delete (this->flac_decoder); + + if (this->output_open) + this->stream->audio_out->close (this->stream->audio_out, this->stream); + + free (this_gen); +} + +static audio_decoder_t * +open_plugin (audio_decoder_class_t *class_gen, xine_stream_t *stream) { + flac_decoder_t *this ; + + this = (flac_decoder_t *) malloc (sizeof (flac_decoder_t)); + + this->audio_decoder.decode_data = flac_decode_data; + this->audio_decoder.reset = flac_reset; + this->audio_decoder.discontinuity = flac_discontinuity; + this->audio_decoder.dispose = flac_dispose; + this->stream = stream; + + this->output_open = 0; + this->buf = NULL; + this->buf_size = 0; + this->min_size = 65536; + this->pts = 0; + + this->flac_decoder = FLAC__stream_decoder_new(); + + FLAC__stream_decoder_set_read_callback (this->flac_decoder, + flac_read_callback); + FLAC__stream_decoder_set_write_callback (this->flac_decoder, + flac_write_callback); + FLAC__stream_decoder_set_metadata_callback (this->flac_decoder, + flac_metadata_callback); + FLAC__stream_decoder_set_error_callback (this->flac_decoder, + flac_error_callback); + + FLAC__stream_decoder_set_client_data (this->flac_decoder, this); + + FLAC__stream_decoder_init (this->flac_decoder); + + return (audio_decoder_t *) this; +} + +/* + * flac plugin class + */ + +static char *get_identifier (audio_decoder_class_t *this) { + return "flacdec"; +} + +static char *get_description (audio_decoder_class_t *this) { + return "flac audio decoder plugin"; +} + +static void dispose_class (audio_decoder_class_t *this) { + free (this); +} + +static void * +init_plugin (xine_t *xine, void *data) { + flac_class_t *this; + + this = (flac_class_t *) malloc (sizeof (flac_class_t)); + + this->decoder_class.open_plugin = open_plugin; + this->decoder_class.get_identifier = get_identifier; + this->decoder_class.get_description = get_description; + this->decoder_class.dispose = dispose_class; + + + return this; +} + +static uint32_t audio_types[] = { + BUF_AUDIO_FLAC, 0 + }; + +static decoder_info_t dec_info_audio = { + audio_types, /* supported types */ + 5 /* priority */ +}; + +/* from demux_flac.c */ +void *demux_flac_init_class (xine_t *xine, void *data); + +plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_DEMUX, 20, "flac", XINE_VERSION_CODE, NULL, demux_flac_init_class }, + { PLUGIN_AUDIO_DECODER, 13, "flacdec", XINE_VERSION_CODE, &dec_info_audio, init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/libflac/demux_flac.c b/src/libflac/demux_flac.c new file mode 100644 index 000000000..d0ffa80c0 --- /dev/null +++ b/src/libflac/demux_flac.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2000-2002 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * + * John McCutchan + * FLAC demuxer (http://flac.sf.net) + * + * TODO: Skip id3v2 tags. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "xine_internal.h" +#include "xineutils.h" +#include "../demuxers/demux.h" + +/* FLAC Demuxer plugin */ +typedef struct demux_flac_s { + demux_plugin_t demux_plugin; + + xine_stream_t *stream; + + fifo_buffer_t *audio_fifo; + fifo_buffer_t *video_fifo; + + input_plugin_t *input; + + int status; + + int seek_flag; + + off_t data_start; + off_t data_size; + + /* FLAC Stuff */ + FLAC__SeekableStreamDecoder *flac_decoder; + + uint64_t total_samples; + uint64_t bits_per_sample; + uint64_t channels; + uint64_t sample_rate; + uint64_t length_in_msec; +} demux_flac_t ; + + +/* FLAC Demuxer class */ +typedef struct demux_flac_class_s { + demux_class_t demux_class; + + xine_t *xine; + config_values_t *config; + +} demux_flac_class_t; + +/* FLAC Callbacks */ +static FLAC__SeekableStreamDecoderReadStatus +flac_read_callback (const FLAC__SeekableStreamDecoder *decoder, + FLAC__byte buffer[], + unsigned *bytes, + void *client_data) +{ + demux_flac_t *this = (demux_flac_t *)client_data; + input_plugin_t *input = this->input; + off_t offset = *bytes; + + printf("FLAC_DMXR: flac_read_callback\n"); + + /* This should only be called when flac is reading the metadata + * of the flac stream. + */ + + offset = input->read (input, buffer, offset); + + printf("FLAC_DMXR: Read %lld / %u bytes into buffer\n", offset, *bytes); + + *bytes = offset; + /* This is the way to detect EOF with xine input plugins */ + if ( (offset != *bytes) && (*bytes != 0) ) + { + printf("FLAC_DMXR: Marking EOF\n"); + this->status = DEMUX_FINISHED; + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; + } + else + { + printf("FLAC_DMXR: Read was perfect\n"); + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; + } +} + +static FLAC__SeekableStreamDecoderSeekStatus +flac_seek_callback (const FLAC__SeekableStreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data) +{ + input_plugin_t *input = ((demux_flac_t *)client_data)->input; + off_t offset; + + printf("FLAC_DMXR: flac_seek_callback\n"); + + offset = input->seek (input, absolute_byte_offset, SEEK_SET); + + if (offset == -1) + return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; +} + +static FLAC__SeekableStreamDecoderTellStatus +flac_tell_callback (const FLAC__SeekableStreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data) +{ + input_plugin_t *input = ((demux_flac_t *)client_data)->input; + off_t offset; + + printf("FLAC_DMXR: flac_tell_callback\n"); + offset = input->get_current_pos (input); + + *absolute_byte_offset = offset; + + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; +} + +static FLAC__SeekableStreamDecoderLengthStatus +flac_length_callback (const FLAC__SeekableStreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data) +{ + input_plugin_t *input = ((demux_flac_t *)client_data)->input; + off_t offset; + + printf("FLAC_DMXR: flac_length_callback\n"); + offset = input->get_length (input); + + /* FIXME, can flac handle -1 as offset ? */ + return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; +} + +static FLAC__bool +flac_eof_callback (const FLAC__SeekableStreamDecoder *decoder, + void *client_data) +{ + demux_flac_t *this = (demux_flac_t *)client_data; + printf("FLAC_DMXR: flac_eof_callback\n"); + + if (this->status == DEMUX_FINISHED) + { + printf("FLAC_DMXR: flac_eof_callback: True!\n"); + return true; + } + else + { + printf("FLAC_DMXR: flac_eof_callback: False!\n"); + return false; + } +} + +static FLAC__StreamDecoderWriteStatus +flac_write_callback (const FLAC__SeekableStreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 * const buffer[], + void *client_data) +{ + /* This should never be called, all we use flac for in this demuxer + * is seeking. We do the decoding in the decoder + */ + + printf("FLAC_DMXR: Error: Write callback was called!\n"); + + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void +flac_metadata_callback (const FLAC__SeekableStreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) +{ + demux_flac_t *this = (demux_flac_t *)client_data; + + printf("FLAC_DMXR: IN: Metadata callback\n"); + /* This should be called when we first look at a flac stream, + * We get information about the stream here. + */ + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + printf("FLAC_DMXR: Got METADATA!\n"); + this->total_samples = metadata->data.stream_info.total_samples; + this->bits_per_sample = metadata->data.stream_info.bits_per_sample; + this->channels = metadata->data.stream_info.channels; + this->sample_rate = metadata->data.stream_info.sample_rate; + this->length_in_msec = (this->total_samples * 10 / + (this->sample_rate / 100))/1000; + } + return; +} + +static void +flac_error_callback (const FLAC__SeekableStreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) +{ + /* This will be called if there is an error when flac is seeking + * in the stream. + */ + + printf("FLAC_DMXR: flac_error_callback\n"); + if (status == FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + printf("FLAC_DMXR: Decoder lost synchronization.\n"); + else if (status == FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER) + printf("FLAC_DMXR: Decoder encounted a corrupted frame header.\n"); + else if (status == FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH) + printf("FLAC_DMXR: Frame's data did not match the CRC in the footer.\n"); + else + printf("FLAC_DMXR: unknown error.\n"); + + return; +} + +/* FLAC Demuxer plugin */ +static int +demux_flac_send_chunk (demux_plugin_t *this_gen) { + demux_flac_t *this = (demux_flac_t *) this_gen; + buf_element_t *buf = NULL; + off_t current_file_pos; + int64_t current_pts; + unsigned int remaining_sample_bytes = 0; + + remaining_sample_bytes = 2048; + + current_file_pos = this->input->get_current_pos (this->input) + - this->data_start; + + current_pts = current_file_pos; + current_pts *= 90000; + if (this->sample_rate != 0) + { + current_pts /= this->sample_rate; + } + + if (this->seek_flag) { + xine_demux_control_newpts (this->stream, current_pts, 0); + this->seek_flag = 0; + } + + while (remaining_sample_bytes) + { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_AUDIO_FLAC; + buf->extra_info->input_pos = current_file_pos; + buf->extra_info->input_length = this->data_size; + buf->extra_info->input_time = current_pts / 90; + //buf->pts = current_pts; + + if (remaining_sample_bytes > buf->max_size) + buf->size = buf->max_size; + else + buf->size = remaining_sample_bytes; + + remaining_sample_bytes -= buf->size; + + if (this->input->read (this->input,buf->content,buf->size)!=buf->size) { + printf("FLAC_DMXR: buf->size != input->read()\n"); + buf->free_buffer (buf); + this->status = DEMUX_FINISHED; + break; + } + + /* + if (!remaining_sample_bytes) + { + buf->decoder_flags |= BUF_FLAG_FRAME_END; + }*/ + + this->audio_fifo->put (this->audio_fifo, buf); + } + + return this->status; +} + +static void +demux_flac_send_headers (demux_plugin_t *this_gen) { + demux_flac_t *this = (demux_flac_t *) this_gen; + + buf_element_t *buf; + + printf("FLAC_DMXR: demux_flac_send_headers\n"); + + this->video_fifo = this->stream->video_fifo; + this->audio_fifo = this->stream->audio_fifo; + + this->status = DEMUX_OK; + + this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0; + this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1; + this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = this->channels; + this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = this->sample_rate; + this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = this->bits_per_sample; + + xine_demux_control_start (this->stream); + + if (this->audio_fifo) { + buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); + buf->type = BUF_AUDIO_FLAC; + buf->decoder_flags = BUF_FLAG_HEADER; + buf->decoder_info[0] = 0; + buf->decoder_info[1] = this->sample_rate; + buf->decoder_info[2] = this->bits_per_sample; + buf->decoder_info[3] = this->channels; + buf->size = 0; + this->audio_fifo->put (this->audio_fifo, buf); + } +} + +static void +demux_flac_dispose (demux_plugin_t *this_gen) { + demux_flac_t *this = (demux_flac_t *) this_gen; + + printf("FLAC_DMXR: demux_flac_dispose\n"); + if (this->flac_decoder) + FLAC__seekable_stream_decoder_delete (this->flac_decoder); + + free(this); + return; +} + +static int +demux_flac_get_status (demux_plugin_t *this_gen) { + demux_flac_t *this = (demux_flac_t *) this_gen; + + printf("FLAC_DMXR: demux_flac_get_status\n"); + + return this->status; +} + + +static int +demux_flac_seek (demux_plugin_t *this_gen, off_t start_pos, int start_time) { + demux_flac_t *this = (demux_flac_t *) this_gen; + + printf("FLAC_DMXR: demux_flac_seek\n"); + if (start_pos || !start_time) { + + this->input->seek (this->input, start_pos, SEEK_SET); + printf ("Seek to position: %lld\n", start_pos); + + } else { + + double distance = (double)start_time*1000.0; + uint64_t target_sample = (uint64_t)(distance * this->total_samples); + FLAC__bool s = false; + + if (this->length_in_msec != 0) + { + distance /= (double)this->length_in_msec; + } + + + s = FLAC__seekable_stream_decoder_seek_absolute (this->flac_decoder, + target_sample); + + if (s) + printf ("Seek to: %d successfull!\n", start_time); + else + this->status = DEMUX_FINISHED; + } + + xine_demux_flush_engine (this->stream); + + return this->status; +} + +static int +demux_flac_get_stream_length (demux_plugin_t *this_gen) { + demux_flac_t *this = (demux_flac_t *) this_gen; + + printf("FLAC_DMXR: demux_flac_get_stream_length\n"); + + if (this->flac_decoder) + return this->length_in_msec; + else + return 0; +} + +static uint32_t +demux_flac_get_capabilities (demux_plugin_t *this_gen) { + printf("FLAC_DMXR: demux_flac_get_capabilities\n"); + return DEMUX_CAP_NOCAP; +} + +static int +demux_flac_get_optional_data (demux_plugin_t *this_gen, void *data, int dtype) { + printf("FLAC_DMXR: demux_flac_get_optional_data\n"); + return DEMUX_OPTIONAL_UNSUPPORTED; +} + +static demux_plugin_t * +open_plugin (demux_class_t *class_gen, + xine_stream_t *stream, + input_plugin_t *input) { + demux_flac_t *this; + + printf("FLAC_DMXR: open_plugin\n"); +#if 0 + if ((input->get_capabilities (input) & INPUT_CAP_SEEKABLE) != 0) + { + printf("FLAC_DMXR: Input is not seekable, will not handle.\n"); + return NULL; + } +#endif + + switch (stream->content_detection_method) { + case METHOD_BY_CONTENT: + { + uint8_t buf[4096]; + + /* Seek to the beginning */ + input->seek(input, 0, SEEK_SET); + + + /* FIXME: Skip id3v2 tag */ + if (input->read (input, buf, 4)) { + input->seek(input, 0, SEEK_SET); + /* Look for fLaC tag at the beginning of file */ + if ( (buf[0] != 'f') || (buf[1] != 'L') || + (buf[2] != 'a') || (buf[3] != 'C') ) + return NULL; + } + } + break; + case METHOD_BY_EXTENSION: { + char *ending, *mrl; + + mrl = input->get_mrl (input); + + ending = strrchr (mrl, '.'); + + if (!ending || (strlen (ending) < 5)) + return NULL; + + if (strncasecmp (ending, ".flac", 5)) + return NULL; + } + break; + case METHOD_EXPLICIT: + break; + default: + return NULL; + break; + } + + /* + * if we reach this point, the input has been accepted. + */ + + this = xine_xmalloc (sizeof (demux_flac_t)); + this->stream = stream; + this->input = input; + + this->demux_plugin.send_headers = demux_flac_send_headers; + this->demux_plugin.send_chunk = demux_flac_send_chunk; + this->demux_plugin.seek = demux_flac_seek; + this->demux_plugin.dispose = demux_flac_dispose; + this->demux_plugin.get_status = demux_flac_get_status; + this->demux_plugin.get_stream_length = demux_flac_get_stream_length; + this->demux_plugin.get_video_frame = NULL; + this->demux_plugin.got_video_frame_cb= NULL; + this->demux_plugin.get_capabilities = demux_flac_get_capabilities; + this->demux_plugin.get_optional_data = demux_flac_get_optional_data; + this->demux_plugin.demux_class = class_gen; + + this->seek_flag = 0; + + + /* Get a new FLAC decoder and hook up callbacks */ + this->flac_decoder = FLAC__seekable_stream_decoder_new(); + printf("FLAC_DMXR: this->flac_decoder: %p\n", this->flac_decoder); + FLAC__seekable_stream_decoder_set_md5_checking (this->flac_decoder, false); + FLAC__seekable_stream_decoder_set_read_callback (this->flac_decoder, + flac_read_callback); + FLAC__seekable_stream_decoder_set_seek_callback (this->flac_decoder, + flac_seek_callback); + FLAC__seekable_stream_decoder_set_tell_callback (this->flac_decoder, + flac_tell_callback); + FLAC__seekable_stream_decoder_set_length_callback (this->flac_decoder, + flac_length_callback); + FLAC__seekable_stream_decoder_set_eof_callback (this->flac_decoder, + flac_eof_callback); + FLAC__seekable_stream_decoder_set_metadata_callback (this->flac_decoder, + flac_metadata_callback); + FLAC__seekable_stream_decoder_set_write_callback (this->flac_decoder, + flac_write_callback); + FLAC__seekable_stream_decoder_set_error_callback (this->flac_decoder, + flac_error_callback); + FLAC__seekable_stream_decoder_set_client_data (this->flac_decoder, + this); + + FLAC__seekable_stream_decoder_init (this->flac_decoder); + + /* Get some stream info */ + this->data_size = this->input->get_length (this->input); + this->data_start = this->input->get_current_pos (this->input); + + /* This will cause FLAC to give us the rest of the information on + * this flac stream + */ + this->status = DEMUX_OK; + FLAC__seekable_stream_decoder_process_until_end_of_metadata (this->flac_decoder); + printf("FLAC_DMXR: Processed file until end of metadata\n"); + + return &this->demux_plugin; +} + + +/* FLAC Demuxer class */ + +static char * +get_description (demux_class_t *this_gen) { + return "FLAC demux plugin"; +} + +static char * +get_identifier (demux_class_t *this_gen) { + return "FLAC"; +} + +static char * +get_extensions (demux_class_t *this_gen) { + return "flac"; +} + +static char * +get_mimetypes (demux_class_t *this_gen) { + return "application/x-flac: flac: FLAC Audio;"; +} + +static void +class_dispose (demux_class_t *this_gen) { + demux_flac_class_t *this = (demux_flac_class_t *) this_gen; + + printf("FLAC_DMXR: class_dispose\n"); + free (this); +} + +void * +demux_flac_init_class (xine_t *xine, void *data) { + + demux_flac_class_t *this; + + printf("FLAC_DMXR: demux_flac_init_class\n"); + this = xine_xmalloc (sizeof (demux_flac_class_t)); + this->config = xine->config; + this->xine = xine; + + this->demux_class.open_plugin = open_plugin; + this->demux_class.get_description = get_description; + this->demux_class.get_identifier = get_identifier; + this->demux_class.get_mimetypes = get_mimetypes; + this->demux_class.get_extensions = get_extensions; + this->demux_class.dispose = class_dispose; + + return this; +} + +/* + * exported plugin catalog entry + */ +#if 0 +plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_DEMUX, 20, "flac", XINE_VERSION_CODE, NULL, demux_flac_init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; +#endif -- cgit v1.2.3