diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp
index f765befb..0ac87d10 100644
--- a/taglib/mpeg/mpegfile.cpp
+++ b/taglib/mpeg/mpegfile.cpp
@@ -433,9 +433,24 @@ long MPEG::File::firstFrameOffset()
{
long position = 0;
- if(ID3v2Tag())
+ if(ID3v2Tag()) {
position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize();
+ // Skip duplicate ID3v2 tags.
+
+ // Workaround for some faulty files that have duplicate ID3v2 tags.
+ // Combination of EAC and LAME creates such files when configured incorrectly.
+
+ long location;
+ while((location = findID3v2(position)) >= 0) {
+ seek(location);
+ const ID3v2::Header header(readBlock(ID3v2::Header::size()));
+ position = location + header.completeTagSize();
+
+ debug("MPEG::File::firstFrameOffset() - Duplicate ID3v2 tag found.");
+ }
+ }
+
return nextFrameOffset(position);
}
@@ -467,7 +482,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
{
// Look for an ID3v2 tag
- d->ID3v2Location = findID3v2();
+ d->ID3v2Location = findID3v2(0);
if(d->ID3v2Location >= 0) {
@@ -510,7 +525,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
ID3v1Tag(true);
}
-long MPEG::File::findID3v2()
+long MPEG::File::findID3v2(long offset)
{
// This method is based on the contents of TagLib::File::find(), but because
// of some subtlteies -- specifically the need to look for the bit pattern of
@@ -534,9 +549,9 @@ long MPEG::File::findID3v2()
long originalPosition = tell();
- // Start the search at the beginning of the file.
+ // Start the search at the offset.
- seek(0);
+ seek(offset);
// This loop is the crux of the find method. There are three cases that we
// want to account for:
@@ -547,7 +562,7 @@ long MPEG::File::findID3v2()
// (2) The search pattern is wholly contained within the current buffer.
//
// (3) The current buffer ends with a partial match of the pattern. We will
- // note this for use in the next itteration, where we will check for the rest
+ // note this for use in the next iteration, where we will check for the rest
// of the pattern.
for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {
@@ -561,7 +576,7 @@ long MPEG::File::findID3v2()
const int patternOffset = (bufferSize() - previousPartialMatch);
if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
seek(originalPosition);
- return bufferOffset - bufferSize() + previousPartialMatch;
+ return offset + bufferOffset - bufferSize() + previousPartialMatch;
}
}
@@ -570,7 +585,7 @@ long MPEG::File::findID3v2()
long location = buffer.find(ID3v2::Header::fileIdentifier());
if(location >= 0) {
seek(originalPosition);
- return bufferOffset + location;
+ return offset + bufferOffset + location;
}
int firstSynchByte = buffer.find(char(uchar(255)));
diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h
index 3fc01e68..a03887a3 100644
--- a/taglib/mpeg/mpegfile.h
+++ b/taglib/mpeg/mpegfile.h
@@ -242,8 +242,8 @@ namespace TagLib {
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist and returns a valid pointer.
*
- * \note This may return a valid pointer regardless of whether or not the
- * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
+ * \note This may return a valid pointer regardless of whether or not the
+ * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag is still owned by the MPEG::File and should not be
@@ -261,8 +261,8 @@ namespace TagLib {
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist and returns a valid pointer.
*
- * \note This may return a valid pointer regardless of whether or not the
- * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
+ * \note This may return a valid pointer regardless of whether or not the
+ * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag is still owned by the MPEG::File and should not be
@@ -280,8 +280,8 @@ namespace TagLib {
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
- * \note This may return a valid pointer regardless of whether or not the
- * file on disk has an APE tag. Use hasAPETag() to check if the file
+ * \note This may return a valid pointer regardless of whether or not the
+ * file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag is still owned by the MPEG::File and should not be
@@ -370,7 +370,7 @@ namespace TagLib {
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
- long findID3v2();
+ long findID3v2(long offset);
long findID3v1();
void findAPE();
diff --git a/tests/data/duplicate_id3v2.mp3 b/tests/data/duplicate_id3v2.mp3
new file mode 100644
index 00000000..34f4f158
Binary files /dev/null and b/tests/data/duplicate_id3v2.mp3 differ
diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp
index 44925cac..024da6a0 100644
--- a/tests/test_mpeg.cpp
+++ b/tests/test_mpeg.cpp
@@ -16,6 +16,7 @@ class TestMPEG : public CppUnit::TestFixture
CPPUNIT_TEST(testSaveID3v24);
CPPUNIT_TEST(testSaveID3v24WrongParam);
CPPUNIT_TEST(testSaveID3v23);
+ CPPUNIT_TEST(testDuplicateID3v2);
CPPUNIT_TEST_SUITE_END();
public:
@@ -92,6 +93,19 @@ public:
}
}
+ void testDuplicateID3v2()
+ {
+ ScopedFileCopy copy("duplicate_id3v2", ".mp3");
+ string newname = copy.fileName();
+
+ MPEG::File f(newname.c_str());
+
+ // duplicate_id3v2.mp3 has duplicate ID3v2 tags.
+ // Sample rate will be 32000 if can't skip the second tag.
+
+ CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
+ }
+
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG);