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<long>(position, bufferSize());
+ while(position > 0) {
+ const long size = std::min<long>(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;
}
{
// 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.
// 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<int>(streamLength * 8.0 / d->bitrate + 0.5);
CPPUNIT_TEST(testEmptyID3v2);
CPPUNIT_TEST(testEmptyID3v1);
CPPUNIT_TEST(testEmptyAPE);
+ CPPUNIT_TEST(testIgnoreGarbage);
CPPUNIT_TEST_SUITE_END();
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());
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());
}
}
+ 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);