diff --git a/taglib/mp4/mp4atom.cpp b/taglib/mp4/mp4atom.cpp index e1a36828..ba07409a 100644 --- a/taglib/mp4/mp4atom.cpp +++ b/taglib/mp4/mp4atom.cpp @@ -47,10 +47,17 @@ MP4::Atom::Atom(File *file) length = header.mid(0, 4).toUInt(); if (length == 1) { - debug("MP4: 64-bit atoms are not supported"); - length = 0; - file->seek(0, File::End); - return; + long long longLength = file->readBlock(8).toLongLong(); + if (longLength >= 8 && longLength <= 0xFFFFFFFF) { + // The atom has a 64-bit length, but it's actually a 32-bit value + length = (long)longLength; + } + else { + debug("MP4: 64-bit atoms are not supported"); + length = 0; + file->seek(0, File::End); + return; + } } if (length < 8) { debug("MP4: Invalid atom size"); diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index 645d4976..3196f101 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -314,9 +314,20 @@ MP4::Tag::updateParents(AtomList &path, long delta, int ignore) { for(unsigned int i = 0; i < path.size() - ignore; i++) { d->file->seek(path[i]->offset); - long size = d->file->readBlock(4).toUInt() + delta; - d->file->seek(path[i]->offset); - d->file->writeBlock(ByteVector::fromUInt(size)); + long size = d->file->readBlock(4).toUInt(); + // 64-bit + if (size == 1) { + d->file->seek(4, File::Current); // Skip name + long long longSize = d->file->readBlock(8).toLongLong(); + // Seek the offset of the 64-bit size + d->file->seek(path[i]->offset + 8); + d->file->writeBlock(ByteVector::fromLongLong(longSize + delta)); + } + // 32-bit + else { + d->file->seek(path[i]->offset); + d->file->writeBlock(ByteVector::fromUInt(size + delta)); + } } } diff --git a/tests/data/64bit.mp4 b/tests/data/64bit.mp4 new file mode 100644 index 00000000..0bd7f9f3 Binary files /dev/null and b/tests/data/64bit.mp4 differ diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index 04ad729a..9732a5bb 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -18,6 +18,7 @@ class TestMP4 : public CppUnit::TestFixture CPPUNIT_TEST(testFreeForm); CPPUNIT_TEST(testUpdateStco); CPPUNIT_TEST(testSaveExisingWhenIlstIsLast); + CPPUNIT_TEST(test64BitAtom); CPPUNIT_TEST_SUITE_END(); public: @@ -118,6 +119,32 @@ public: deleteFile(filename); } + void test64BitAtom() + { + string filename = copyFile("64bit", ".mp4"); + + MP4::File *f = new MP4::File(filename.c_str()); + CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool()); + + MP4::Atoms *atoms = new MP4::Atoms(f); + MP4::Atom *moov = atoms->atoms[0]; + CPPUNIT_ASSERT_EQUAL(long(77), moov->length); + + f->tag()->itemListMap()["pgap"] = 1; + f->save(); + + f = new MP4::File(filename.c_str()); + CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool()); + CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["pgap"].toBool()); + + atoms = new MP4::Atoms(f); + moov = atoms->atoms[0]; + // original size + 'pgap' size + padding + CPPUNIT_ASSERT_EQUAL(long(77 + 25 + 974), moov->length); + + deleteFile(filename); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);