From ac96022c1de5573b9c4ff03990ac07c4bba8e3f9 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 3 Nov 2008 18:24:01 +0100
Subject: decoder_api: automatically send stream tag

If an input stream provides tags (e.g. from an icecast server), send
them in the decoder_data() and decoder_tag() methods.  Removed the
according code from the mp3 and oggvorbis plugins - decoders shouldn't
have to care about stream tags.

This patch also adds the missing decoder_tag() invocation to the mp3
plugin.
---
 src/decoder/mp3_plugin.c       | 46 +++++--------------------------------
 src/decoder/oggvorbis_plugin.c | 10 ---------
 src/decoder_api.c              | 51 ++++++++++++++++++++++++++++++++++++++++++
 src/decoder_internal.h         |  3 +++
 src/decoder_thread.c           |  1 +
 5 files changed, 60 insertions(+), 51 deletions(-)

(limited to 'src')

diff --git a/src/decoder/mp3_plugin.c b/src/decoder/mp3_plugin.c
index 7f742361d..f3edf9d20 100644
--- a/src/decoder/mp3_plugin.c
+++ b/src/decoder/mp3_plugin.c
@@ -942,19 +942,6 @@ mp3_synth_and_send(struct mp3_data *data, ReplayGainInfo *replay_gain_info)
 		return DECODE_COMMAND_STOP;
 	}
 
-	if (data->input_stream->meta_title) {
-		struct tag *tag = tag_new();
-		if (data->input_stream->meta_name) {
-			tag_add_item(tag, TAG_ITEM_NAME,
-				     data->input_stream->meta_name);
-		}
-		tag_add_item(tag, TAG_ITEM_TITLE,
-			     data->input_stream->meta_title);
-		free(data->input_stream->meta_title);
-		data->input_stream->meta_title = NULL;
-		tag_free(tag);
-	}
-
 	if (!data->decoded_first_frame) {
 		i = data->drop_start_samples;
 		data->decoded_first_frame = true;
@@ -1079,37 +1066,14 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
 
 	mp3_audio_format(&data, &audio_format);
 
-	if (input_stream->meta_title) {
-		if (tag)
-			tag_free(tag);
-		tag = tag_new();
-		tag_add_item(tag, TAG_ITEM_TITLE, input_stream->meta_title);
-		free(input_stream->meta_title);
-		input_stream->meta_title = NULL;
-		if (input_stream->meta_name) {
-			tag_add_item(tag, TAG_ITEM_NAME,
-				     input_stream->meta_name);
-		}
-		tag_free(tag);
-	} else if (tag) {
-		if (input_stream->meta_name) {
-			tag_clear_items_by_type(tag, TAG_ITEM_NAME);
-			tag_add_item(tag, TAG_ITEM_NAME,
-				     input_stream->meta_name);
-		}
-		tag_free(tag);
-	} else if (input_stream->meta_name) {
-		tag = tag_new();
-		if (input_stream->meta_name) {
-			tag_add_item(tag, TAG_ITEM_NAME,
-				     input_stream->meta_name);
-		}
-		tag_free(tag);
-	}
-
 	decoder_initialized(decoder, &audio_format,
 			    data.input_stream->seekable, data.total_time);
 
+	if (tag != NULL) {
+		decoder_tag(decoder, input_stream, tag);
+		tag_free(tag);
+	}
+
 	while (mp3_read(&data, &replay_gain_info)) ;
 
 	if (replay_gain_info)
diff --git a/src/decoder/oggvorbis_plugin.c b/src/decoder/oggvorbis_plugin.c
index d5402aa66..f86bf3fc4 100644
--- a/src/decoder/oggvorbis_plugin.c
+++ b/src/decoder/oggvorbis_plugin.c
@@ -181,23 +181,14 @@ static struct tag *oggCommentsParse(char **comments)
 
 static void putOggCommentsIntoOutputBuffer(struct decoder *decoder,
 					   struct input_stream *is,
-					   char *streamName,
 					   char **comments)
 {
 	struct tag *tag;
 
 	tag = oggCommentsParse(comments);
-	if (!tag && streamName) {
-		tag = tag_new();
-	}
 	if (!tag)
 		return;
 
-	if (streamName) {
-		tag_clear_items_by_type(tag, TAG_ITEM_NAME);
-		tag_add_item(tag, TAG_ITEM_NAME, streamName);
-	}
-
 	decoder_tag(decoder, is, tag);
 	tag_free(tag);
 }
@@ -288,7 +279,6 @@ oggvorbis_decode(struct decoder *decoder, struct input_stream *inStream)
 			}
 			comments = ov_comment(&vf, -1)->user_comments;
 			putOggCommentsIntoOutputBuffer(decoder, inStream,
-						       inStream->meta_name,
 						       comments);
 			ogg_getReplayGainInfo(comments, &replayGainInfo);
 		}
diff --git a/src/decoder_api.c b/src/decoder_api.c
index e0dccdd01..6d577ec79 100644
--- a/src/decoder_api.c
+++ b/src/decoder_api.c
@@ -117,6 +117,33 @@ size_t decoder_read(struct decoder *decoder,
 	}
 }
 
+/**
+ * Add the tag items from the input stream (meta_name, meta_title) to
+ * a duplicate of the specified tag.  The return value has to be freed
+ * with tag_free().  If this function returns NULL, then there are no
+ * tags provided by the stream.
+ */
+static struct tag *
+tag_add_stream_tags(const struct tag *src_tag, const struct input_stream *is)
+{
+	struct tag *tag;
+
+	assert(src_tag != NULL);
+	assert(is != NULL);
+
+	if ((is->meta_name == NULL || tag_has_type(src_tag, TAG_ITEM_NAME)) &&
+	    (is->meta_title == NULL || tag_has_type(src_tag, TAG_ITEM_TITLE)))
+	    return NULL;
+
+	tag = tag_dup(src_tag);
+	if (is->meta_name != NULL && !tag_has_type(src_tag, TAG_ITEM_NAME))
+		tag_add_item(tag, TAG_ITEM_NAME, is->meta_name);
+	if (is->meta_title != NULL && !tag_has_type(src_tag, TAG_ITEM_TITLE))
+		tag_add_item(tag, TAG_ITEM_TITLE, is->meta_title);
+
+	return tag;
+}
+
 /**
  * All chunks are full of decoded data; wait for the player to free
  * one.
@@ -148,6 +175,20 @@ decoder_data(struct decoder *decoder,
 	size_t nbytes;
 	char *data;
 
+	if (is != NULL && !decoder->stream_tag_sent) {
+		struct tag *tag1 = tag_new(), *tag2;
+
+		tag2 = tag_add_stream_tags(tag1, is);
+		tag_free(tag1);
+
+		if (tag2 != NULL) {
+			music_pipe_tag(tag2);
+			tag_free(tag2);
+		}
+
+		decoder->stream_tag_sent = true;
+	}
+
 	if (audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) {
 		data = _data;
 	} else {
@@ -193,11 +234,21 @@ enum decoder_command
 decoder_tag(mpd_unused struct decoder *decoder, struct input_stream *is,
 	    const struct tag *tag)
 {
+	struct tag *tag2 = is != NULL ? tag_add_stream_tags(tag, is) : NULL;
+
+	if (tag2 != NULL)
+		tag = tag2;
+
 	while (!music_pipe_tag(tag)) {
 		enum decoder_command cmd = need_chunks(is);
 		if (cmd != DECODE_COMMAND_NONE)
 			return cmd;
 	}
 
+	if (tag2 != NULL)
+		tag_free(tag2);
+
+	decoder->stream_tag_sent = true;
+
 	return DECODE_COMMAND_NONE;
 }
diff --git a/src/decoder_internal.h b/src/decoder_internal.h
index 7a36e011c..9d3d6abea 100644
--- a/src/decoder_internal.h
+++ b/src/decoder_internal.h
@@ -28,6 +28,9 @@ struct decoder {
 	struct pcm_convert_state conv_state;
 
 	bool seeking;
+
+	/** has the tag from the input stream been sent yet? */
+	bool stream_tag_sent;
 };
 
 #endif
diff --git a/src/decoder_thread.c b/src/decoder_thread.c
index d66094cd2..040e34f84 100644
--- a/src/decoder_thread.c
+++ b/src/decoder_thread.c
@@ -72,6 +72,7 @@ static void decodeStart(void)
 	}
 
 	decoder.seeking = false;
+	decoder.stream_tag_sent = false;
 
 	dc.state = DECODE_STATE_START;
 	dc.command = DECODE_COMMAND_NONE;
-- 
cgit v1.2.3