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);