diff --git a/src/decoder/mp3_plugin.c b/src/decoder/mp3_plugin.c
index 3a908299f..6fedf3766 100644
--- a/src/decoder/mp3_plugin.c
+++ b/src/decoder/mp3_plugin.c
@@ -202,6 +202,95 @@ mp3_fill_buffer(struct mp3_data *data)
 	return true;
 }
 
+#ifdef HAVE_ID3TAG
+/* Parse mp3 RVA2 frame. Shamelessly stolen from madplay. */
+static int parse_rva2(struct id3_tag * tag, struct replay_gain_info * replay_gain_info)
+{
+	struct id3_frame const * frame;
+
+	id3_latin1_t const *id;
+	id3_byte_t const *data;
+	id3_length_t length;
+	int found;
+
+	enum {
+		CHANNEL_OTHER         = 0x00,
+		CHANNEL_MASTER_VOLUME = 0x01,
+		CHANNEL_FRONT_RIGHT   = 0x02,
+		CHANNEL_FRONT_LEFT    = 0x03,
+		CHANNEL_BACK_RIGHT    = 0x04,
+		CHANNEL_BACK_LEFT     = 0x05,
+		CHANNEL_FRONT_CENTRE  = 0x06,
+		CHANNEL_BACK_CENTRE   = 0x07,
+		CHANNEL_SUBWOOFER     = 0x08
+	};
+
+	found = 0;
+
+	/* relative volume adjustment information */
+
+	frame = id3_tag_findframe(tag, "RVA2", 0);
+	if (!frame) return 0;
+
+	id   = id3_field_getlatin1(id3_frame_field(frame, 0));
+	data = id3_field_getbinarydata(id3_frame_field(frame, 1),
+					&length);
+
+	if (!id || !data) return 0;
+
+	/*
+	 * "The 'identification' string is used to identify the
+	 * situation and/or device where this adjustment should apply.
+	 * The following is then repeated for every channel
+	 *
+	 *   Type of channel         $xx
+	 *   Volume adjustment       $xx xx
+	 *   Bits representing peak  $xx
+	 *   Peak volume             $xx (xx ...)"
+	 */
+
+	while (length >= 4) {
+		unsigned int peak_bytes;
+
+		peak_bytes = (data[3] + 7) / 8;
+		if (4 + peak_bytes > length)
+			break;
+
+		if (data[0] == CHANNEL_MASTER_VOLUME) {
+			signed int voladj_fixed;
+			double voladj_float;
+
+			/*
+			 * "The volume adjustment is encoded as a fixed
+			 * point decibel value, 16 bit signed integer
+			 * representing (adjustment*512), giving +/- 64
+			 * dB with a precision of 0.001953125 dB."
+			 */
+
+			voladj_fixed  = (data[1] << 8) | (data[2] << 0);
+			voladj_fixed |= -(voladj_fixed & 0x8000);
+
+			voladj_float  = (double) voladj_fixed / 512;
+
+			replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak = voladj_float;
+			replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak = voladj_float;
+
+			g_debug("parseRVA2: Relative Volume "
+				"%+.1f dB adjustment (%s)\n",
+				voladj_float, id);
+
+			found = 1;
+			break;
+		}
+
+		data   += 4 + peak_bytes;
+		length -= 4 + peak_bytes;
+	}
+
+	return found;
+}
+#endif
+
 #ifdef HAVE_ID3TAG
 static struct replay_gain_info *
 parse_id3_replay_gain_info(struct id3_tag *tag)
@@ -244,6 +333,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag)
 		free(value);
 	}
 
+	if (!found) {
+		/* fall back on RVA2 if no replaygain tags found */
+		found = parse_rva2(tag, replay_gain_info);
+	}
+
 	if (found)
 		return replay_gain_info;
 	replay_gain_info_free(replay_gain_info);