From a942384fbf4bc939086671a1741ee72244a70f28 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 6 Jan 2010 09:54:09 +0100
Subject: [PATCH] decoder/flac: support streams without STREAMINFO block

---
 NEWS                              |  1 +
 src/decoder/_flac_common.c        | 41 ++++++++++++++++++++++++++++++-
 src/decoder/flac_decoder_plugin.c | 12 +++++++++
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 5d0a736fd..fae16e339 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,7 @@ ver 0.16 (20??/??/??)
   - ffmpeg: convert metadata to generic format
   - sndfile: new decoder plugin based on libsndfile
   - flac: moved CUE sheet support to a playlist plugin
+  - flac: support streams without STREAMINFO block
   - mikmod: sample rate is configurable
   - mpg123: new decoder plugin based on libmpg123
   - sidplay: support sub-tunes
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
index 9a3449096..6e28792ec 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/_flac_common.c
@@ -161,16 +161,55 @@ void flac_error_common_cb(const char *plugin,
 	}
 }
 
+/**
+ * This function attempts to call decoder_initialized() in case there
+ * was no STREAMINFO block.  This is allowed for nonseekable streams,
+ * where the server sends us only a part of the file, without
+ * providing the STREAMINFO block from the beginning of the file
+ * (e.g. when seeking with SqueezeBox Server).
+ */
+static bool
+flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
+{
+	if (data->unsupported)
+		return false;
+
+	GError *error = NULL;
+	if (!audio_format_init_checked(&data->audio_format,
+				       header->sample_rate,
+				       flac_sample_format(header->bits_per_sample),
+				       header->channels, &error)) {
+		g_warning("%s", error->message);
+		g_error_free(error);
+		data->unsupported = true;
+		return false;
+	}
+
+	data->frame_size = audio_format_frame_size(&data->audio_format);
+
+	decoder_initialized(data->decoder, &data->audio_format,
+			    data->input_stream->seekable,
+			    (float)data->total_frames /
+			    (float)data->audio_format.sample_rate);
+
+	data->initialized = true;
+
+	return true;
+}
+
 FLAC__StreamDecoderWriteStatus
 flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
 		  const FLAC__int32 *const buf[],
 		  FLAC__uint64 nbytes)
 {
 	enum decoder_command cmd;
-	size_t buffer_size = frame->header.blocksize * data->frame_size;
 	void *buffer;
 	unsigned bit_rate;
 
+	if (!data->initialized && !flac_got_first_frame(data, &frame->header))
+		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+	size_t buffer_size = frame->header.blocksize * data->frame_size;
 	buffer = pcm_buffer_get(&data->buffer, buffer_size);
 
 	flac_convert(buffer, frame->header.channels,
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/flac_decoder_plugin.c
index d139354b8..022ee7045 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/flac_decoder_plugin.c
@@ -247,6 +247,18 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
 		return false;
 	}
 
+	if (data->initialized)
+		/* done */
+		return true;
+
+	if (data->input_stream->seekable)
+		/* allow the workaround below only for nonseekable
+		   streams*/
+		return false;
+
+	/* no stream_info packet found; try to initialize the decoder
+	   from the first frame header */
+	FLAC__stream_decoder_process_single(sd);
 	return data->initialized;
 }