From fc1796f3e8f2df6f7d4c94e43df853d45e9590f6 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 10 Dec 2014 13:05:16 +0100
Subject: [PATCH] decoder/ffmpeg: support ReplayGain and MixRamp

---
 NEWS                                        |  2 +
 src/decoder/plugins/FfmpegDecoderPlugin.cxx | 56 +++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/NEWS b/NEWS
index 78403121e..f26a818d1 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ ver 0.20 (not yet released)
   - report song duration with milliseconds precision
 * tags
   - ape: drop support for non-standard tag "album artist"
+* decoder
+  - ffmpeg: support ReplayGain
 * output
   - pulse: set channel map to WAVE-EX
 * mixer
diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
index 7adbb5899..9010b7402 100644
--- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx
+++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
@@ -26,6 +26,8 @@
 #include "../DecoderAPI.hxx"
 #include "FfmpegMetaData.hxx"
 #include "tag/TagHandler.hxx"
+#include "tag/ReplayGain.hxx"
+#include "tag/MixRamp.hxx"
 #include "input/InputStream.hxx"
 #include "CheckAudioFormat.hxx"
 #include "util/Error.hxx"
@@ -437,6 +439,58 @@ ffmpeg_probe(Decoder *decoder, InputStream &is)
 	return av_probe_input_format(&avpd, true);
 }
 
+static void
+FfmpegParseMetaData(AVDictionary &dict, ReplayGainInfo &rg, MixRampInfo &mr)
+{
+	AVDictionaryEntry *i = nullptr;
+
+	while ((i = av_dict_get(&dict, "", i,
+				AV_DICT_IGNORE_SUFFIX)) != nullptr) {
+		const char *name = i->key;
+		const char *value = i->value;
+
+		if (!ParseReplayGainTag(rg, name, value))
+			ParseMixRampTag(mr, name, value);
+	}
+}
+
+static void
+FfmpegParseMetaData(const AVStream &stream,
+		    ReplayGainInfo &rg, MixRampInfo &mr)
+{
+	FfmpegParseMetaData(*stream.metadata, rg, mr);
+}
+
+static void
+FfmpegParseMetaData(const AVFormatContext &format_context, int audio_stream,
+		    ReplayGainInfo &rg, MixRampInfo &mr)
+{
+	FfmpegParseMetaData(*format_context.metadata, rg, mr);
+
+	if (audio_stream >= 0)
+		FfmpegParseMetaData(*format_context.streams[audio_stream],
+				    rg, mr);
+}
+
+static void
+FfmpegParseMetaData(Decoder &decoder,
+		    const AVFormatContext &format_context, int audio_stream)
+{
+	ReplayGainInfo rg;
+	rg.Clear();
+
+	MixRampInfo mr;
+	mr.Clear();
+
+	FfmpegParseMetaData(format_context, audio_stream, rg, mr);
+
+	if (rg.IsDefined())
+		decoder_replay_gain(decoder, &rg);
+
+	if (mr.IsDefined())
+		decoder_mixramp(decoder, std::move(mr));
+}
+
 static void
 ffmpeg_decode(Decoder &decoder, InputStream &input)
 {
@@ -541,6 +595,8 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
 	decoder_initialized(decoder, audio_format,
 			    input.IsSeekable(), total_time);
 
+	FfmpegParseMetaData(decoder, *format_context, audio_stream);
+
 #if LIBAVUTIL_VERSION_MAJOR >= 53
 	AVFrame *frame = av_frame_alloc();
 #else