MPEG: Scan raw bytes in 64 KB blocks when searching for frames (#842)

findID3v2, nextFrameOffset and previousFrameOffset read raw bytes in
64 KB blocks instead of building a ByteVector per byte from 1 KB reads,
with the sync check in an isFrameSync(b1, b2) overload. Results are
unchanged; junk before or after the audio no longer makes opening slow.
This commit is contained in:
Ryan Francesconi
2026-11-01 07:26:20 +01:00
committed by GitHub
parent d1bd220c26
commit 8875f397d9
3 changed files with 74 additions and 26 deletions
+33
View File
@@ -31,6 +31,7 @@
#include "tstring.h"
#include "tpropertymap.h"
#include "mpegfile.h"
#include "tbytevectorstream.h"
#include "id3v2tag.h"
#include "id3v1tag.h"
#ifdef TAGLIB_WITH_APE
@@ -74,6 +75,7 @@ class TestMPEG : public CppUnit::TestFixture
CPPUNIT_TEST(testEmptyAPE);
#endif
CPPUNIT_TEST(testIgnoreGarbage);
CPPUNIT_TEST(testFrameSearchThroughJunk);
CPPUNIT_TEST(testExtendedHeader);
CPPUNIT_TEST(testReadStyleFast);
CPPUNIT_TEST(testID3v22Properties);
@@ -577,6 +579,37 @@ public:
}
}
void testFrameSearchThroughJunk()
{
// 100 MPEG-1 Layer III frames, 128 kbps at 44.1 kHz: 417 bytes each, no
// padding and no Xing header, so the length comes from the frame search.
ByteVector frames;
for(int i = 0; i < 100; ++i)
frames.append(ByteVector("\xff\xfb\x90\x00", 4) + ByteVector(413, '\0'));
const ByteVector junk(1024 * 1024, '\0');
ByteVectorStream cleanStream(frames);
MPEG::File clean(&cleanStream);
CPPUNIT_ASSERT(clean.isValid());
CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0), clean.firstFrameOffset());
CPPUNIT_ASSERT(clean.audioProperties()->lengthInMilliseconds() > 0);
ByteVectorStream tailStream(frames + junk);
MPEG::File tail(&tailStream);
CPPUNIT_ASSERT_EQUAL(clean.firstFrameOffset(), tail.firstFrameOffset());
CPPUNIT_ASSERT_EQUAL(clean.lastFrameOffset(), tail.lastFrameOffset());
CPPUNIT_ASSERT_EQUAL(clean.audioProperties()->lengthInMilliseconds(),
tail.audioProperties()->lengthInMilliseconds());
ByteVectorStream leadStream(junk + frames);
MPEG::File lead(&leadStream);
const auto shift = static_cast<offset_t>(junk.size());
CPPUNIT_ASSERT_EQUAL(clean.firstFrameOffset() + shift, lead.firstFrameOffset());
CPPUNIT_ASSERT_EQUAL(clean.lastFrameOffset() + shift, lead.lastFrameOffset());
CPPUNIT_ASSERT_EQUAL(clean.audioProperties()->lengthInMilliseconds(),
lead.audioProperties()->lengthInMilliseconds());
}
void testExtendedHeader()
{
const ScopedFileCopy copy("extended-header", ".mp3");