Browse Source

Improve MP3 detection slightly more.

Looking too much result in increase of false-positive and vice-versa. So only look first 128 bytes of the file (after the ID3 tags). Also reject known audio file signature such as WAV, Ogg (Theora/Vorbis), and FLAC.

Fixes #1939
Miku AuahDark 2 years ago
parent
commit
d28b30736f
1 changed files with 16 additions and 18 deletions
  1. 16 18
      src/modules/sound/lullaby/MP3Decoder.cpp

+ 16 - 18
src/modules/sound/lullaby/MP3Decoder.cpp

@@ -30,24 +30,14 @@ namespace sound
 namespace lullaby
 {
 
-// Copied from dr_mp3 function drmp3_hdr_valid()
-static bool isMP3HeaderValid(const uint8 *h)
-{
-	return
-		// Sync bits
-		h[0] == 0xff &&
-		((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
-		// Check layer
-		(DRMP3_HDR_GET_LAYER(h) != 0) &&
-		// Check bitrate
-		(DRMP3_HDR_GET_BITRATE(h) != 15) &&
-		// Check sample rate
-		(DRMP3_HDR_GET_SAMPLE_RATE(h) != 3);
-}
-
+// dr_mp3 looks too far, but it can lead to false-positive. dr_mp3 also doesn't recognize ID3.
+// Implement our own "MP3 detection" heuristics that also help dr_mp3 skip ID3 tags.
 static int64 findFirstValidHeader(Stream* stream)
 {
-	constexpr size_t LOOKUP_SIZE = 16384;
+	// Tweaking this variable has trade-off between false-positive and false-negative. Lesser value
+	// means lesser false-positive and false-negative. Larger value means more false-positive AND
+	// false-negative.
+	constexpr size_t LOOKUP_SIZE = 128;
 
 	std::vector<uint8> data(LOOKUP_SIZE);
 	uint8 header[10];
@@ -58,6 +48,14 @@ static int64 findFirstValidHeader(Stream* stream)
 	if (stream->read(header, 10) < 10)
 		return -1;
 
+	// Test for known audio formats which are definitely not MP3.
+	if (memcmp(header, "RIFF", 4) == 0)
+		return -1;
+	if (memcmp(header, "OggS", 4) == 0)
+		return -1;
+	if (memcmp(header, "fLaC", 4) == 0)
+		return -1;
+
 	if (memcmp(header, "TAG", 3) == 0)
 	{
 		// ID3v1 tag is always 128 bytes long
@@ -93,14 +91,14 @@ static int64 findFirstValidHeader(Stream* stream)
 	// Look for mp3 data
 	for (int i = 0; i < buffer - 4; i++, offset++)
 	{
-		if (isMP3HeaderValid(dataPtr++))
+		if (drmp3_hdr_valid(dataPtr++))
 		{
 			stream->seek(offset, Stream::SEEKORIGIN_BEGIN);
 			return offset;
 		}
 	}
 
-	// No valid MP3 frame found in first 16KB data
+	// No valid MP3 frame found in first few bytes of the data.
 	return -1;
 }