summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/demuxers/Makefile.am2
-rw-r--r--src/demuxers/demux_flac.c475
-rw-r--r--src/demuxers/group_audio.c7
-rw-r--r--src/demuxers/group_audio.h3
4 files changed, 484 insertions, 3 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am
index 82686fd9d..96643b6c9 100644
--- a/src/demuxers/Makefile.am
+++ b/src/demuxers/Makefile.am
@@ -121,7 +121,7 @@ xineplug_dmx_audio_la_SOURCES = group_audio.c demux_aud.c demux_aiff.c \
demux_cdda.c demux_mpgaudio.c demux_nsf.c \
demux_realaudio.c demux_snd.c demux_voc.c \
demux_vox.c demux_wav.c demux_ac3.c id3.c \
- demux_aac.c demux_mod.c
+ demux_aac.c demux_mod.c demux_flac.c
xineplug_dmx_audio_la_LIBADD = $(XINE_LIB) $(LIBMODPLUG_LIBS)
xineplug_dmx_audio_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/demuxers/demux_flac.c b/src/demuxers/demux_flac.c
new file mode 100644
index 000000000..e29133c32
--- /dev/null
+++ b/src/demuxers/demux_flac.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2000-2004 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
+ */
+
+/*
+ * FLAC File Demuxer by Mike Melanson (melanson@pcisys.net)
+ * For more information on the FLAC file format, visit:
+ * http://flac.sourceforge.net/
+ *
+ * $Id: demux_flac.c,v 1.1 2004/06/11 01:29:49 tmmm Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define LOG_MODULE "demux_flac"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "compat.h"
+#include "demux.h"
+#include "bswap.h"
+#include "group_audio.h"
+
+typedef struct {
+ off_t offset;
+ int64_t sample_number;
+ int64_t pts;
+ int size;
+} flac_seekpoint_t;
+
+#define FLAC_SIGNATURE_SIZE 4
+#define FLAC_STREAMINFO_SIZE 34
+#define FLAC_SEEKPOINT_SIZE 18
+
+typedef struct {
+ demux_plugin_t demux_plugin;
+
+ xine_stream_t *stream;
+ fifo_buffer_t *audio_fifo;
+ input_plugin_t *input;
+ int status;
+
+ int sample_rate;
+ int bits_per_sample;
+ int channels;
+ int64_t total_samples;
+ off_t data_start;
+ off_t data_size;
+
+ unsigned char streaminfo[sizeof(xine_waveformatex) + FLAC_STREAMINFO_SIZE];
+ flac_seekpoint_t *seekpoints;
+ int seekpoint_count;
+
+} demux_flac_t;
+
+typedef struct {
+ demux_class_t demux_class;
+} demux_flac_class_t;
+
+/* Open a flac file
+ * This function is called from the _open() function of this demuxer.
+ * It returns 1 if flac file was opened successfully. */
+static int open_flac_file(demux_flac_t *flac) {
+
+ unsigned char preamble[4];
+ unsigned int block_length;
+ unsigned char buffer[16];
+ unsigned char *streaminfo = flac->streaminfo + sizeof(xine_waveformatex);
+ int i;
+
+ flac->seekpoints = NULL;
+
+ /* fetch the file signature */
+ if (_x_demux_read_header(flac->input, preamble, 4) != 4)
+ return 0;
+
+ /* validate signature */
+ if ((preamble[0] != 'f') ||
+ (preamble[1] != 'L') ||
+ (preamble[2] != 'a') ||
+ (preamble[3] != 'C'))
+ return 0;
+
+ /* file is qualified; skip over the signature bytes in the stream */
+ flac->input->seek(flac->input, 4, SEEK_SET);
+
+ /* loop through the metadata blocks; use a do-while construct since there
+ * will always be 1 metadata block */
+ do {
+
+ if (flac->input->read(flac->input, preamble, FLAC_SIGNATURE_SIZE) !=
+ FLAC_SIGNATURE_SIZE)
+ return 0;
+
+ block_length = (preamble[1] << 16) |
+ (preamble[2] << 8) |
+ (preamble[3] << 0);
+
+ switch (preamble[0] & 0x7F) {
+
+ /* STREAMINFO */
+ case 0:
+ lprintf ("STREAMINFO metadata\n");
+ if (block_length != FLAC_STREAMINFO_SIZE) {
+ lprintf ("expected STREAMINFO chunk of %d bytes\n",
+ FLAC_STREAMINFO_SIZE);
+ return 0;
+ }
+ if (flac->input->read(flac->input,
+ flac->streaminfo + sizeof(xine_waveformatex),
+ FLAC_STREAMINFO_SIZE) != FLAC_STREAMINFO_SIZE)
+ return 0;
+ flac->sample_rate = BE_32(&streaminfo[10]);
+ flac->channels = ((flac->sample_rate >> 9) & 0x07) + 1;
+ flac->bits_per_sample = ((flac->sample_rate >> 4) & 0x1F) + 1;
+ flac->sample_rate >>= 12;
+ flac->total_samples = BE_64(&streaminfo[10]) & 0x0FFFFFFFFF; /* 36 bits */
+ lprintf ("%d Hz, %d bits, %d channels, %lld total samples\n",
+ flac->sample_rate, flac->bits_per_sample,
+ flac->channels, flac->total_samples);
+ break;
+
+ /* PADDING */
+ case 1:
+ lprintf ("PADDING metadata\n");
+ flac->input->seek(flac->input, block_length, SEEK_CUR);
+ break;
+
+ /* APPLICATION */
+ case 2:
+ lprintf ("APPLICATION metadata\n");
+ flac->input->seek(flac->input, block_length, SEEK_CUR);
+ break;
+
+ /* SEEKTABLE */
+ case 3:
+ lprintf ("SEEKTABLE metadata, %d bytes\n", block_length);
+ flac->seekpoint_count = block_length / FLAC_SEEKPOINT_SIZE;
+ flac->seekpoints = xine_xmalloc(flac->seekpoint_count *
+ sizeof(flac_seekpoint_t));
+ for (i = 0; i < flac->seekpoint_count; i++) {
+ if (flac->input->read(flac->input, buffer, FLAC_SEEKPOINT_SIZE) !=
+ FLAC_SEEKPOINT_SIZE)
+ return 0;
+ flac->seekpoints[i].sample_number = BE_64(&buffer[0]);
+ lprintf (" %d: sample %lld, ", i, flac->seekpoints[i].sample_number);
+ flac->seekpoints[i].offset = BE_64(&buffer[8]);
+ flac->seekpoints[i].size = BE_16(&buffer[16]);
+ lprintf ("@ 0x%llX, size = %d bytes, ",
+ flac->seekpoints[i].offset, flac->seekpoints[i].size);
+ flac->seekpoints[i].pts = flac->seekpoints[i].sample_number;
+ flac->seekpoints[i].pts *= 90000;
+ flac->seekpoints[i].pts /= flac->sample_rate;
+ lprintf ("pts = %lld\n", flac->seekpoints[i].pts);
+ }
+ break;
+
+ /* VORBIS_COMMENT */
+ case 4:
+ lprintf ("VORBIS_COMMENT metadata\n");
+ flac->input->seek(flac->input, block_length, SEEK_CUR);
+ break;
+
+ /* CUESHEET */
+ case 5:
+ lprintf ("CUESHEET metadata\n");
+ flac->input->seek(flac->input, block_length, SEEK_CUR);
+ break;
+
+ /* 6-127 are presently reserved */
+ default:
+ lprintf ("unknown metadata chunk: %d\n", preamble[0] & 0x7F);
+ flac->input->seek(flac->input, block_length, SEEK_CUR);
+ break;
+
+ }
+
+ } while ((preamble[0] & 0x80) == 0);
+
+ flac->data_start = flac->input->get_current_pos(flac->input);
+ flac->data_size = flac->input->get_length(flac->input) - flac->data_start;
+
+ /* now at the beginning of the audio, adjust the seekpoint offsets */
+ for (i = 0; i < flac->seekpoint_count; i++) {
+ flac->seekpoints[i].offset += flac->data_start;
+ }
+
+ return 1;
+}
+
+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;
+ int64_t input_time_guess;
+
+ /* just send a buffer-sized chunk; let the decoder figure out the
+ * boundaries and let the engine figure out the pts */
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = BUF_AUDIO_FLAC;
+ buf->extra_info->input_pos = this->input->get_current_pos(this->input) -
+ this->data_start;
+ buf->extra_info->input_length = this->data_size;
+ buf->pts = 0;
+ buf->size = buf->max_size;
+
+ /*
+ * Estimate the input_time field based on file position:
+ *
+ * current_pos input time
+ * ----------- = ----------
+ * total_pos total time
+ *
+ * total time = total samples / sample rate * 1000
+ */
+
+ /* do this one step at a time to make sure all the numbers stay safe */
+ input_time_guess = this->total_samples;
+ input_time_guess /= this->sample_rate;
+ input_time_guess *= 1000;
+ input_time_guess *= buf->extra_info->input_pos;
+ input_time_guess /= buf->extra_info->input_length;
+ buf->extra_info->input_time = input_time_guess;
+
+ if (this->input->read(this->input, buf->content, buf->size) !=
+ buf->size) {
+ buf->free_buffer(buf);
+ this->status = DEMUX_FINISHED;
+ return this->status;
+ }
+ 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;
+ xine_waveformatex wave;
+
+ this->audio_fifo = this->stream->audio_fifo;
+
+ /* send start buffers */
+ _x_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_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
+ 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;
+ /* copy the faux WAV header */
+ buf->size = sizeof(xine_waveformatex) + FLAC_STREAMINFO_SIZE;
+ memcpy(buf->content, this->streaminfo, buf->size);
+ /* forge a WAV header with the proper length */
+ wave.cbSize = FLAC_STREAMINFO_SIZE;
+ memcpy(buf->content, &wave, sizeof(xine_waveformatex));
+ this->audio_fifo->put (this->audio_fifo, buf);
+ }
+
+ this->status = DEMUX_OK;
+}
+
+static int demux_flac_seek (demux_plugin_t *this_gen,
+ off_t start_pos, int start_time, int playing) {
+ demux_flac_t *this = (demux_flac_t *) this_gen;
+ int seekpoint_index = 0;
+ int64_t start_pts;
+
+ /* if thread is not running, initialize demuxer */
+ if( !playing ) {
+
+ /* send new pts */
+ _x_demux_control_newpts(this->stream, 0, 0);
+
+ this->status = DEMUX_OK;
+ } else {
+
+ /* do a lazy, linear seek based on the assumption that there are not
+ * that many seek points */
+ if (start_pos) {
+ /* offset-based seek */
+ if (start_pos < this->seekpoints[0].offset)
+ seekpoint_index = 0;
+ else {
+ for (seekpoint_index = 0; seekpoint_index < this->seekpoint_count - 1;
+ seekpoint_index++) {
+ if (start_pos < this->seekpoints[seekpoint_index + 1].offset) {
+ break;
+ }
+ }
+ }
+ } else {
+ /* time-based seek */
+ start_pts = start_time;
+ start_pts *= 90;
+ if (start_pts < this->seekpoints[0].pts)
+ seekpoint_index = 0;
+ else {
+ for (seekpoint_index = 0; seekpoint_index < this->seekpoint_count - 1;
+ seekpoint_index++) {
+ if (start_pts < this->seekpoints[seekpoint_index + 1].pts) {
+ break;
+ }
+ }
+ }
+ }
+
+ _x_demux_flush_engine(this->stream);
+ this->input->seek(this->input, this->seekpoints[seekpoint_index].offset,
+ SEEK_SET);
+ _x_demux_control_newpts(this->stream,
+ this->seekpoints[seekpoint_index].pts, BUF_FLAG_SEEK);
+ }
+
+ return this->status;
+}
+
+static void demux_flac_dispose (demux_plugin_t *this_gen) {
+ demux_flac_t *this = (demux_flac_t *) this_gen;
+
+ free(this->seekpoints);
+}
+
+static int demux_flac_get_status (demux_plugin_t *this_gen) {
+ demux_flac_t *this = (demux_flac_t *) this_gen;
+
+ return this->status;
+}
+
+static int demux_flac_get_stream_length (demux_plugin_t *this_gen) {
+ demux_flac_t *this = (demux_flac_t *) this_gen;
+ int64_t length = this->total_samples;
+
+ length *= 1000;
+ length /= this->sample_rate;
+
+ return length;
+}
+
+static uint32_t demux_flac_get_capabilities(demux_plugin_t *this_gen) {
+ return DEMUX_CAP_NOCAP;
+}
+
+static int demux_flac_get_optional_data(demux_plugin_t *this_gen,
+ void *data, int data_type) {
+ 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;
+
+ /* this should change eventually... */
+ if (!INPUT_IS_SEEKABLE(input)) {
+ xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "input not seekable, can not handle!\n");
+ return NULL;
+ }
+
+ 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_capabilities = demux_flac_get_capabilities;
+ this->demux_plugin.get_optional_data = demux_flac_get_optional_data;
+ this->demux_plugin.demux_class = class_gen;
+
+ this->status = DEMUX_FINISHED;
+
+ switch (stream->content_detection_method) {
+
+ case METHOD_BY_EXTENSION: {
+ char *extensions, *mrl;
+
+ mrl = input->get_mrl (input);
+ extensions = class_gen->get_extensions (class_gen);
+
+ if (!_x_demux_check_extension (mrl, extensions)) {
+ free (this);
+ return NULL;
+ }
+ }
+ /* falling through is intended */
+
+ case METHOD_BY_CONTENT:
+ case METHOD_EXPLICIT:
+
+ if (!open_flac_file(this)) {
+ free (this);
+ return NULL;
+ }
+
+ break;
+
+ default:
+ free (this);
+ return NULL;
+ }
+
+ return &this->demux_plugin;
+}
+
+static char *get_description (demux_class_t *this_gen) {
+ return "Free Lossless Audio Codec (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 NULL;
+}
+
+static void class_dispose (demux_class_t *this_gen) {
+ demux_flac_class_t *this = (demux_flac_class_t *) this_gen;
+
+ free (this);
+}
+
+void *demux_flac_init_plugin (xine_t *xine, void *data) {
+ demux_flac_class_t *this;
+
+ this = xine_xmalloc (sizeof (demux_flac_class_t));
+
+ 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;
+}
diff --git a/src/demuxers/group_audio.c b/src/demuxers/group_audio.c
index 9e4fc60e4..eef80d21a 100644
--- a/src/demuxers/group_audio.c
+++ b/src/demuxers/group_audio.c
@@ -19,7 +19,7 @@
*
* This file contains plugin entries for several demuxers used in games
*
- * $Id: group_audio.c,v 1.13 2004/05/16 18:01:44 tmattern Exp $
+ * $Id: group_audio.c,v 1.14 2004/06/11 01:29:49 tmmm Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -55,6 +55,10 @@ demuxer_info_t demux_info_cdda = {
10 /* priority */
};
+demuxer_info_t demux_info_flac = {
+ 10 /* priority */
+};
+
demuxer_info_t demux_info_mpgaudio = {
0 /* priority */
};
@@ -96,6 +100,7 @@ plugin_info_t xine_plugin_info[] = {
{ PLUGIN_DEMUX, 24, "aud", XINE_VERSION_CODE, &demux_info_aud, demux_aud_init_plugin },
{ PLUGIN_DEMUX, 24, "aiff", XINE_VERSION_CODE, &demux_info_aiff, demux_aiff_init_plugin },
{ PLUGIN_DEMUX, 24, "cdda", XINE_VERSION_CODE, &demux_info_cdda, demux_cdda_init_plugin },
+ { PLUGIN_DEMUX, 24, "flac", XINE_VERSION_CODE, &demux_info_flac, demux_flac_init_plugin },
{ PLUGIN_DEMUX, 24, "mp3", XINE_VERSION_CODE, &demux_info_mpgaudio, demux_mpgaudio_init_class },
{ PLUGIN_DEMUX, 24, "nsf", XINE_VERSION_CODE, &demux_info_nsf, demux_nsf_init_plugin },
{ PLUGIN_DEMUX, 24, "realaudio", XINE_VERSION_CODE, &demux_info_realaudio, demux_realaudio_init_plugin },
diff --git a/src/demuxers/group_audio.h b/src/demuxers/group_audio.h
index 847f4857f..ced34d682 100644
--- a/src/demuxers/group_audio.h
+++ b/src/demuxers/group_audio.h
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: group_audio.h,v 1.4 2004/02/11 20:40:00 tmattern Exp $
+ * $Id: group_audio.h,v 1.5 2004/06/11 01:29:49 tmmm Exp $
*/
#ifndef HAVE_GROUP_AUDIO_H
@@ -30,6 +30,7 @@ void *demux_ac3_init_plugin (xine_t *xine, void *data);
void *demux_aud_init_plugin (xine_t *xine, void *data);
void *demux_aiff_init_plugin (xine_t *xine, void *data);
void *demux_cdda_init_plugin (xine_t *xine, void *data);
+void *demux_flac_init_plugin (xine_t *xine, void *data);
void *demux_mpgaudio_init_class (xine_t *xine, void *data);
void *demux_nsf_init_plugin (xine_t *xine, void *data);
void *demux_realaudio_init_plugin (xine_t *xine, void *data);