$NetBSD: patch-ac,v 1.1 2007/01/09 23:19:26 wiz Exp $

--- src/inputPlugins/flac_plugin.c.orig	2006-10-10 23:56:56.000000000 +0000
+++ src/inputPlugins/flac_plugin.c
@@ -16,12 +16,10 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include "../inputPlugin.h"
+#include "_flac_common.h"
 
 #ifdef HAVE_FLAC
 
-#include "_flac_common.h"
-
 #include "../utils.h"
 #include "../log.h"
 #include "../pcm_utils.h"
@@ -33,166 +31,14 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <FLAC/seekable_stream_decoder.h>
-#include <FLAC/metadata.h>
+#include <assert.h>
 
-/* this code is based on flac123, from flac-tools */
+/* this code was based on flac123, from flac-tools */
 
-static void flacError(const FLAC__SeekableStreamDecoder *,
-		      FLAC__StreamDecoderErrorStatus, void *);
-static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state);
-static void flacMetadata(const FLAC__SeekableStreamDecoder *,
-			 const FLAC__StreamMetadata *, void *);
-static FLAC__StreamDecoderWriteStatus flacWrite(const
-						FLAC__SeekableStreamDecoder *,
-						const FLAC__Frame *,
-						const FLAC__int32 * const buf[],
-						void *);
-static FLAC__SeekableStreamDecoderReadStatus flacRead(const
-						      FLAC__SeekableStreamDecoder
-						      *, FLAC__byte buf[],
-						      unsigned *, void *);
-static FLAC__SeekableStreamDecoderSeekStatus flacSeek(const
-						      FLAC__SeekableStreamDecoder
-						      *, FLAC__uint64, void *);
-static FLAC__SeekableStreamDecoderTellStatus flacTell(const
-						      FLAC__SeekableStreamDecoder
-						      *, FLAC__uint64 *,
-						      void *);
-static FLAC__SeekableStreamDecoderLengthStatus flacLength(const
-							  FLAC__SeekableStreamDecoder
-							  *, FLAC__uint64 *,
-							  void *);
-static FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder *, void *);
-
-static int flac_decode(OutputBuffer * cb, DecoderControl * dc,
-		       InputStream * inStream)
-{
-	FLAC__SeekableStreamDecoder *flacDec = NULL;
-	FlacData data;
-	int status = 1;
-	int ret = 0;
-
-	init_FlacData(&data, cb, dc, inStream);
-
-	if (!(flacDec = FLAC__seekable_stream_decoder_new())) {
-		ret = -1;
-		goto fail;
-	}
-	/*status&=FLAC__file_decoder_set_md5_checking(flacDec,1); */
-	status &= FLAC__seekable_stream_decoder_set_read_callback(flacDec,
-								  flacRead);
-	status &= FLAC__seekable_stream_decoder_set_seek_callback(flacDec,
-								  flacSeek);
-	status &= FLAC__seekable_stream_decoder_set_tell_callback(flacDec,
-								  flacTell);
-	status &= FLAC__seekable_stream_decoder_set_length_callback(flacDec,
-								    flacLength);
-	status &=
-	    FLAC__seekable_stream_decoder_set_eof_callback(flacDec, flacEOF);
-	status &=
-	    FLAC__seekable_stream_decoder_set_write_callback(flacDec,
-							     flacWrite);
-	status &=
-	    FLAC__seekable_stream_decoder_set_metadata_callback(flacDec,
-								flacMetadata);
-	status &=
-	    FLAC__seekable_stream_decoder_set_metadata_respond(flacDec,
-							       FLAC__METADATA_TYPE_VORBIS_COMMENT);
-	status &=
-	    FLAC__seekable_stream_decoder_set_error_callback(flacDec,
-							     flacError);
-	status &=
-	    FLAC__seekable_stream_decoder_set_client_data(flacDec,
-							  (void *)&data);
-	if (!status) {
-		ERROR("flac problem before init()\n");
-		flacPrintErroredState(FLAC__seekable_stream_decoder_get_state
-				      (flacDec));
-		ret = -1;
-		goto fail;
-	}
-
-	if (FLAC__seekable_stream_decoder_init(flacDec) !=
-	    FLAC__SEEKABLE_STREAM_DECODER_OK) {
-		ERROR("flac problem doing init()\n");
-		flacPrintErroredState(FLAC__seekable_stream_decoder_get_state
-				      (flacDec));
-		ret = -1;
-		goto fail;
-	}
-
-	if (!FLAC__seekable_stream_decoder_process_until_end_of_metadata
-	    (flacDec)) {
-		ERROR("flac problem reading metadata\n");
-		flacPrintErroredState(FLAC__seekable_stream_decoder_get_state
-				      (flacDec));
-		ret = -1;
-		goto fail;
-	}
-
-	dc->state = DECODE_STATE_DECODE;
-
-	while (1) {
-		FLAC__seekable_stream_decoder_process_single(flacDec);
-		if (FLAC__seekable_stream_decoder_get_state(flacDec) !=
-		    FLAC__SEEKABLE_STREAM_DECODER_OK) {
-			break;
-		}
-		if (dc->seek) {
-			FLAC__uint64 sampleToSeek = dc->seekWhere *
-			    dc->audioFormat.sampleRate + 0.5;
-			if (FLAC__seekable_stream_decoder_seek_absolute(flacDec,
-									sampleToSeek))
-			{
-				clearOutputBuffer(cb);
-				data.time = ((float)sampleToSeek) /
-				    dc->audioFormat.sampleRate;
-				data.position = 0;
-			} else
-				dc->seekError = 1;
-			dc->seek = 0;
-		}
-	}
-	/* I don't think we need this bit here! -shank */
-	/*FLAC__file_decoder_process_until_end_of_file(flacDec); */
-	if (!dc->stop) {
-		flacPrintErroredState(FLAC__seekable_stream_decoder_get_state
-				      (flacDec));
-		FLAC__seekable_stream_decoder_finish(flacDec);
-	}
-	/* send last little bit */
-	if (data.chunk_length > 0 && !dc->stop) {
-		flacSendChunk(&data);
-		flushOutputBuffer(data.cb);
-	}
-
-	/*if(dc->seek) {
-	   dc->seekError = 1;
-	   dc->seek = 0;
-	   } */
-
-	dc->state = DECODE_STATE_STOP;
-	dc->stop = 0;
-
-fail:
-	if (data.replayGainInfo)
-		freeReplayGainInfo(data.replayGainInfo);
-
-	if (flacDec)
-		FLAC__seekable_stream_decoder_delete(flacDec);
-
-	closeInputStream(inStream);
-
-	return ret;
-}
-
-static FLAC__SeekableStreamDecoderReadStatus flacRead(const
-						      FLAC__SeekableStreamDecoder
-						      * flacDec,
-						      FLAC__byte buf[],
-						      unsigned *bytes,
-						      void *fdata)
+static flac_read_status flacRead(const flac_decoder * flacDec,
+                                  FLAC__byte buf[],
+				  unsigned *bytes,
+				  void *fdata)
 {
 	FlacData *data = (FlacData *) fdata;
 	size_t r;
@@ -207,55 +53,51 @@ static FLAC__SeekableStreamDecoderReadSt
 	}
 	*bytes = r;
 
-	if (*bytes == 0 && !inputStreamAtEOF(data->inStream) && !data->dc->stop)
-		return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
-
-	return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
+	if (r == 0 && !data->dc->stop) {
+		if (inputStreamAtEOF(data->inStream))
+			return flac_read_status_eof;
+		else
+			return flac_read_status_abort;
+	}
+	return flac_read_status_continue;
 }
 
-static FLAC__SeekableStreamDecoderSeekStatus flacSeek(const
-						      FLAC__SeekableStreamDecoder
-						      * flacDec,
-						      FLAC__uint64 offset,
-						      void *fdata)
+static flac_seek_status flacSeek(const flac_decoder * flacDec,
+				 FLAC__uint64 offset,
+				 void *fdata)
 {
 	FlacData *data = (FlacData *) fdata;
 
 	if (seekInputStream(data->inStream, offset, SEEK_SET) < 0) {
-		return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
+		return flac_seek_status_error;
 	}
 
-	return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
+	return flac_tell_status_ok;
 }
 
-static FLAC__SeekableStreamDecoderTellStatus flacTell(const
-						      FLAC__SeekableStreamDecoder
-						      * flacDec,
-						      FLAC__uint64 * offset,
-						      void *fdata)
+static flac_tell_status flacTell(const flac_decoder * flacDec,
+				 FLAC__uint64 * offset,
+				 void *fdata)
 {
 	FlacData *data = (FlacData *) fdata;
 
 	*offset = (long)(data->inStream->offset);
 
-	return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
+	return flac_tell_status_ok;
 }
 
-static FLAC__SeekableStreamDecoderLengthStatus flacLength(const
-							  FLAC__SeekableStreamDecoder
-							  * flacDec,
-							  FLAC__uint64 * length,
-							  void *fdata)
+static flac_length_status flacLength(const flac_decoder * flacDec,
+				     FLAC__uint64 * length,
+				     void *fdata)
 {
 	FlacData *data = (FlacData *) fdata;
 
 	*length = (size_t) (data->inStream->size);
 
-	return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
+	return flac_length_status_ok;
 }
 
-static FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder * flacDec,
-			  void *fdata)
+static FLAC__bool flacEOF(const flac_decoder * flacDec, void *fdata)
 {
 	FlacData *data = (FlacData *) fdata;
 
@@ -264,52 +106,112 @@ static FLAC__bool flacEOF(const FLAC__Se
 	return false;
 }
 
-static void flacError(const FLAC__SeekableStreamDecoder * dec,
+static void flacError(const flac_decoder *dec,
 		      FLAC__StreamDecoderErrorStatus status, void *fdata)
 {
 	flac_error_common_cb("flac", status, (FlacData *) fdata);
 }
 
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
 static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
 {
+	const char *str = ""; /* "" to silence compiler warning */
 	switch (state) {
+	case FLAC__SEEKABLE_STREAM_DECODER_OK:
+	case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
+	case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
+		return;
 	case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
-		ERROR("flac allocation error\n");
+		str = "allocation error";
 		break;
 	case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
-		ERROR("flac read error\n");
+		str = "read error";
 		break;
 	case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
-		ERROR("flac seek error\n");
+		str = "seek error";
 		break;
 	case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
-		ERROR("flac seekable stream error\n");
+		str = "seekable stream error";
 		break;
 	case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
-		ERROR("flac decoder already initialized\n");
+		str = "decoder already initialized";
 		break;
 	case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
-		ERROR("invalid flac callback\n");
+		str = "invalid callback";
 		break;
 	case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
-		ERROR("flac decoder uninitialized\n");
+		str = "decoder uninitialized";
+	}
+	ERROR("flac %s\n", str);
+}
+
+static int flac_init(FLAC__SeekableStreamDecoder *dec,
+                     FLAC__SeekableStreamDecoderReadCallback read_cb,
+                     FLAC__SeekableStreamDecoderSeekCallback seek_cb,
+                     FLAC__SeekableStreamDecoderTellCallback tell_cb,
+                     FLAC__SeekableStreamDecoderLengthCallback length_cb,
+                     FLAC__SeekableStreamDecoderEofCallback eof_cb,
+                     FLAC__SeekableStreamDecoderWriteCallback write_cb,
+                     FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
+                     FLAC__SeekableStreamDecoderErrorCallback error_cb,
+                     void *data)
+{
+	int s = 1;
+	s &= FLAC__seekable_stream_decoder_set_read_callback(dec, read_cb);
+	s &= FLAC__seekable_stream_decoder_set_seek_callback(dec, seek_cb);
+	s &= FLAC__seekable_stream_decoder_set_tell_callback(dec, tell_cb);
+	s &= FLAC__seekable_stream_decoder_set_length_callback(dec, length_cb);
+	s &= FLAC__seekable_stream_decoder_set_eof_callback(dec, eof_cb);
+	s &= FLAC__seekable_stream_decoder_set_write_callback(dec, write_cb);
+	s &= FLAC__seekable_stream_decoder_set_metadata_callback(dec,
+	                                                         metadata_cb);
+	s &= FLAC__seekable_stream_decoder_set_metadata_respond(dec,
+	                                  FLAC__METADATA_TYPE_VORBIS_COMMENT);
+	s &= FLAC__seekable_stream_decoder_set_error_callback(dec, error_cb);
+	s &= FLAC__seekable_stream_decoder_set_client_data(dec, data);
+	if (!s || (FLAC__seekable_stream_decoder_init(dec) !=
+	           FLAC__SEEKABLE_STREAM_DECODER_OK))
+		return 0;
+	return 1;
+}
+#else /* FLAC_API_VERSION_CURRENT >= 7 */
+static void flacPrintErroredState(FLAC__StreamDecoderState state)
+{
+	const char *str = ""; /* "" to silence compiler warning */
+	switch (state) {
+	case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
+	case FLAC__STREAM_DECODER_READ_METADATA:
+	case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
+	case FLAC__STREAM_DECODER_READ_FRAME:
+	case FLAC__STREAM_DECODER_END_OF_STREAM:
+		return;
+	case FLAC__STREAM_DECODER_OGG_ERROR:
+		str = "error in the Ogg layer";
 		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_OK:
-	case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
-	case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
+	case FLAC__STREAM_DECODER_SEEK_ERROR:
+		str = "seek error";
+		break;
+	case FLAC__STREAM_DECODER_ABORTED:
+		str = "decoder aborted by read";
+		break;
+	case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
+		str = "allocation error";
 		break;
+	case FLAC__STREAM_DECODER_UNINITIALIZED:
+		str = "decoder uninitialized";
 	}
+	ERROR("flac %s\n", str);
 }
+#endif /* FLAC_API_VERSION_CURRENT >= 7 */
 
-static void flacMetadata(const FLAC__SeekableStreamDecoder * dec,
+static void flacMetadata(const flac_decoder * dec,
 			 const FLAC__StreamMetadata * block, void *vdata)
 {
 	flac_metadata_common_cb(block, (FlacData *) vdata);
 }
 
-static FLAC__StreamDecoderWriteStatus flacWrite(const
-						FLAC__SeekableStreamDecoder *
-						dec, const FLAC__Frame * frame,
+static FLAC__StreamDecoderWriteStatus flacWrite(const flac_decoder *dec,
+                                                const FLAC__Frame * frame,
 						const FLAC__int32 * const buf[],
 						void *vdata)
 {
@@ -325,7 +227,7 @@ static FLAC__StreamDecoderWriteStatus fl
 	timeChange = ((float)samples) / frame->header.sample_rate;
 	data->time += timeChange;
 
-	FLAC__seekable_stream_decoder_get_decode_position(dec, &newPosition);
+	flac_get_decode_position(dec, &newPosition);
 	if (data->position) {
 		data->bitRate =
 		    ((newPosition - data->position) * 8.0 / timeChange)
@@ -438,12 +340,174 @@ static MpdTag *flacTagDup(char *file)
 	return ret;
 }
 
+static int flac_decode_internal(OutputBuffer * cb, DecoderControl * dc,
+                               InputStream * inStream, int is_ogg)
+{
+	flac_decoder *flacDec;
+	FlacData data;
+	const char *err = NULL;
+
+	if (!(flacDec = flac_new()))
+		return -1;
+	init_FlacData(&data, cb, dc, inStream);
+	if (is_ogg) {
+		if (!flac_ogg_init(flacDec, flacRead, flacSeek, flacTell,
+		                   flacLength, flacEOF, flacWrite, flacMetadata,
+			           flacError, (void *)&data)) {
+			err = "doing Ogg init()";
+			goto fail;
+		}
+	} else {
+		if (!flac_init(flacDec, flacRead, flacSeek, flacTell,
+		               flacLength, flacEOF, flacWrite, flacMetadata,
+			       flacError, (void *)&data)) {
+			err = "doing init()";
+			goto fail;
+		}
+		if (!flac_process_metadata(flacDec)) {
+			err = "problem reading metadata";
+			goto fail;
+		}
+	}
+
+	dc->state = DECODE_STATE_DECODE;
+
+	while (1) {
+		if (!flac_process_single(flacDec))
+			break;
+		if (flac_get_state(flacDec) == flac_decoder_eof)
+			break;
+		if (dc->seek) {
+			FLAC__uint64 sampleToSeek = dc->seekWhere *
+			    dc->audioFormat.sampleRate + 0.5;
+			if (flac_seek_absolute(flacDec, sampleToSeek)) {
+				clearOutputBuffer(cb);
+				data.time = ((float)sampleToSeek) /
+				    dc->audioFormat.sampleRate;
+				data.position = 0;
+			} else
+				dc->seekError = 1;
+			dc->seek = 0;
+		}
+	}
+	if (!dc->stop) {
+		flacPrintErroredState(flac_get_state(flacDec));
+		flac_finish(flacDec);
+	}
+	/* send last little bit */
+	if (data.chunk_length > 0 && !dc->stop) {
+		flacSendChunk(&data);
+		flushOutputBuffer(data.cb);
+	}
+
+	/*if(dc->seek) {
+	   dc->seekError = 1;
+	   dc->seek = 0;
+	   } */
+
+	dc->state = DECODE_STATE_STOP;
+	dc->stop = 0;
+
+fail:
+	if (data.replayGainInfo)
+		freeReplayGainInfo(data.replayGainInfo);
+
+	if (flacDec)
+		flac_delete(flacDec);
+
+	closeInputStream(inStream);
+
+	if (err) {
+		ERROR("flac %s\n", err);
+		return -1;
+	}
+	return 0;
+}
+
+static int flac_decode(OutputBuffer * cb, DecoderControl * dc,
+                       InputStream * inStream)
+{
+	return flac_decode_internal(cb, dc, inStream, 0);
+}
+
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
+#  define flac_plugin_init NULL
+#else /* FLAC_API_VERSION_CURRENT >= 7 */
+/* some of this stuff is duplicated from oggflac_plugin.c */
+extern InputPlugin oggflacPlugin;
+
+static MpdTag *oggflac_tag_dup(char *file)
+{
+	MpdTag *ret = NULL;
+	FLAC__Metadata_Iterator *it;
+	FLAC__StreamMetadata *block;
+	FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
+
+	if (!(FLAC__metadata_chain_read_ogg(chain, file)))
+		goto out;
+	it = FLAC__metadata_iterator_new();
+	FLAC__metadata_iterator_init(it, chain);
+	do {
+		if (!(block = FLAC__metadata_iterator_get_block(it)))
+			break;
+		if (block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+			ret = copyVorbisCommentBlockToMpdTag(block, ret);
+		} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
+			if (!ret)
+				ret = newMpdTag();
+			ret->time = ((float)block->data.stream_info.
+				     total_samples) /
+			    block->data.stream_info.sample_rate + 0.5;
+		}
+	} while (FLAC__metadata_iterator_next(it));
+	FLAC__metadata_iterator_delete(it);
+out:
+	FLAC__metadata_chain_delete(chain);
+	return ret;
+}
+
+static int oggflac_decode(OutputBuffer * cb, DecoderControl * dc,
+		          InputStream * inStream)
+{
+	return flac_decode_internal(cb, dc, inStream, 1);
+}
+
+static unsigned int oggflac_try_decode(InputStream * inStream)
+{
+	return (ogg_stream_type_detect(inStream) == FLAC) ? 1 : 0;
+}
+
+static char *oggflac_suffixes[] = { "ogg", NULL };
+static char *oggflac_mime_types[] = { "application/ogg", NULL };
+
+static int flac_plugin_init(void)
+{
+	if (!FLAC_API_SUPPORTS_OGG_FLAC) {
+		DEBUG("libFLAC does not support OggFLAC\n");
+		return 1;
+	}
+	DEBUG("libFLAC supports OggFLAC, initializing OggFLAC support\n");
+	assert(oggflacPlugin.name == NULL);
+	oggflacPlugin.name = "oggflac";
+	oggflacPlugin.tryDecodeFunc = oggflac_try_decode;
+	oggflacPlugin.streamDecodeFunc = oggflac_decode;
+	oggflacPlugin.tagDupFunc = oggflac_tag_dup;
+	oggflacPlugin.streamTypes = INPUT_PLUGIN_STREAM_URL |
+	                            INPUT_PLUGIN_STREAM_FILE;
+	oggflacPlugin.suffixes = oggflac_suffixes;
+	oggflacPlugin.mimeTypes = oggflac_mime_types;
+	loadInputPlugin(&oggflacPlugin);
+	return 1;
+}
+
+#endif /* FLAC_API_VERSION_CURRENT >= 7 */
+
 static char *flacSuffixes[] = { "flac", NULL };
 static char *flac_mime_types[] = { "application/x-flac", NULL };
 
 InputPlugin flacPlugin = {
 	"flac",
-	NULL,
+	flac_plugin_init,
 	NULL,
 	NULL,
 	flac_decode,
