From 6bb92c34fac889f0ca9667ed0d1c6cfda23a6938 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 20 Jan 2017 22:38:25 +0900 Subject: [PATCH] Ignore fake MPEG frame headers when seeking them. --- taglib/mpeg/mpegfile.cpp | 57 ++++++++++++++++----------------- taglib/mpeg/mpegproperties.cpp | 29 +++-------------- tests/data/garbage.mp3 | Bin 0 -> 8190 bytes tests/test_mpeg.cpp | 33 ++++++++++++++----- 4 files changed, 56 insertions(+), 63 deletions(-) create mode 100644 tests/data/garbage.mp3 diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index c634eeb8..6860053b 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -346,55 +346,52 @@ void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) long MPEG::File::nextFrameOffset(long position) { - bool foundLastSyncPattern = false; - - ByteVector buffer; + char frameSyncBytes[2] = {}; while(true) { seek(position); - buffer = readBlock(bufferSize()); - - if(buffer.size() <= 0) + const ByteVector buffer = readBlock(bufferSize()); + if(buffer.size() == 0) return -1; - if(foundLastSyncPattern && secondSynchByte(buffer[0])) - return position - 1; - - for(unsigned int i = 0; i < buffer.size() - 1; i++) { - if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) - return position + i; + for(unsigned int i = 0; i < buffer.size(); ++i) { + frameSyncBytes[0] = frameSyncBytes[1]; + frameSyncBytes[1] = buffer[i]; + if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + Header header(this, position + i - 1, true); + if(header.isValid()) + return position + i - 1; + } } - foundLastSyncPattern = firstSyncByte(buffer[buffer.size() - 1]); - position += buffer.size(); + position += bufferSize(); } } long MPEG::File::previousFrameOffset(long position) { - bool foundFirstSyncPattern = false; - ByteVector buffer; + char frameSyncBytes[2] = {}; - while (position > 0) { - long size = std::min(position, bufferSize()); + while(position > 0) { + const long size = std::min(position, bufferSize()); position -= size; seek(position); - buffer = readBlock(size); - - if(buffer.size() <= 0) - break; - - if(foundFirstSyncPattern && firstSyncByte(buffer[buffer.size() - 1])) - return position + buffer.size() - 1; + const ByteVector buffer = readBlock(bufferSize()); + if(buffer.size() == 0) + return -1; - for(int i = buffer.size() - 2; i >= 0; i--) { - if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) - return position + i; + for(int i = buffer.size() - 1; i >= 0; i--) { + frameSyncBytes[1] = frameSyncBytes[0]; + frameSyncBytes[0] = buffer[i]; + if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + Header header(this, position + i, true); + if(header.isValid()) + return position + i + header.frameLength(); + } } - - foundFirstSyncPattern = secondSynchByte(buffer[0]); } + return -1; } diff --git a/taglib/mpeg/mpegproperties.cpp b/taglib/mpeg/mpegproperties.cpp index 6e7bb823..d8b7bd50 100644 --- a/taglib/mpeg/mpegproperties.cpp +++ b/taglib/mpeg/mpegproperties.cpp @@ -157,23 +157,13 @@ void MPEG::Properties::read(File *file) { // Only the first valid frame is required if we have a VBR header. - long firstFrameOffset = file->firstFrameOffset(); + const long firstFrameOffset = file->firstFrameOffset(); if(firstFrameOffset < 0) { debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); return; } - Header firstHeader(file, firstFrameOffset); - - while(!firstHeader.isValid()) { - firstFrameOffset = file->nextFrameOffset(firstFrameOffset + 1); - if(firstFrameOffset < 0) { - debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream."); - return; - } - - firstHeader = Header(file, firstFrameOffset); - } + const Header firstHeader(file, firstFrameOffset, false); // Check for a VBR header that will help us in gathering information about a // VBR stream. @@ -207,24 +197,13 @@ void MPEG::Properties::read(File *file) // Look for the last MPEG audio frame to calculate the stream length. - long lastFrameOffset = file->lastFrameOffset(); + const long lastFrameOffset = file->lastFrameOffset(); if(lastFrameOffset < 0) { debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); return; } - Header lastHeader(file, lastFrameOffset, false); - - while(!lastHeader.isValid()) { - lastFrameOffset = file->previousFrameOffset(lastFrameOffset); - if(lastFrameOffset < 0) { - debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream."); - return; - } - - lastHeader = Header(file, lastFrameOffset, false); - } - + const Header lastHeader(file, lastFrameOffset, false); const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); if(streamLength > 0) d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); diff --git a/tests/data/garbage.mp3 b/tests/data/garbage.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..730b74e7c2babe736acc8b5922e0a0fbc1f6f33f GIT binary patch literal 8190 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 YdjCZ_?Wr7v#yEfT4BEkpaZzjfUmgnumH+?% literal 0 HcmV?d00001 diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index c1179ee5..5a990580 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -64,6 +64,7 @@ class TestMPEG : public CppUnit::TestFixture CPPUNIT_TEST(testEmptyID3v2); CPPUNIT_TEST(testEmptyID3v1); CPPUNIT_TEST(testEmptyAPE); + CPPUNIT_TEST(testIgnoreGarbage); CPPUNIT_TEST_SUITE_END(); public: @@ -119,13 +120,8 @@ public: CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - long last = f.lastFrameOffset(); - MPEG::Header lastHeader(&f, last, false); - - while(!lastHeader.isValid()) { - last = f.previousFrameOffset(last); - lastHeader = MPEG::Header(&f, last, false); - } + const long last = f.lastFrameOffset(); + const MPEG::Header lastHeader(&f, last, false); CPPUNIT_ASSERT_EQUAL(28213L, last); CPPUNIT_ASSERT_EQUAL(209, lastHeader.frameLength()); @@ -163,7 +159,7 @@ public: CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->length()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(176, f.audioProperties()->lengthInMilliseconds()); + CPPUNIT_ASSERT_EQUAL(183, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(320, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); @@ -421,6 +417,27 @@ public: } } + void testIgnoreGarbage() + { + const ScopedFileCopy copy("garbage", ".mp3"); + { + MPEG::File f(copy.fileName().c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT_EQUAL(2255L, f.firstFrameOffset()); + CPPUNIT_ASSERT_EQUAL(6015L, f.lastFrameOffset()); + CPPUNIT_ASSERT_EQUAL(String("Title A"), f.ID3v2Tag()->title()); + f.ID3v2Tag()->setTitle("Title B"); + f.save(); + } + { + MPEG::File f(copy.fileName().c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT_EQUAL(String("Title B"), f.ID3v2Tag()->title()); + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG); -- 2.40.0