Correctly parse MP4 non-full meta atoms (#1041)

There are m4a files with regular (non-full) meta atoms. When such
a meta atom is not correctly parsed, the subsequent atoms are not
recognized and offsets will not be adjusted when atoms are added,
which will corrupt the MP4 file.

This change will look behind the meta atom to check if the next
atom follows directly, i.e. without the four bytes with version
and flags as they exist in full atoms. In such a case, these
four bytes will not be skipped.

Witnesses of this strange format specification are
https://leo-van-stee.github.io/
https://github.com/axiomatic-systems/Bento4/blob/v1.6.0-639/Source/C%2B%2B/Core/Ap4ContainerAtom.cpp#L60
This commit is contained in:
Urs Fleisch 2022-03-03 21:03:48 +01:00
parent e255e749e8
commit 197d2a684b
3 changed files with 41 additions and 1 deletions

View File

@ -85,7 +85,25 @@ MP4::Atom::Atom(File *file)
for(int i = 0; i < numContainers; i++) {
if(name == containers[i]) {
if(name == "meta") {
file->seek(4, File::Current);
long posAfterMeta = file->tell();
ByteVector nextSize = file->readBlock(8).mid(4, 4);
static const char *const metaChildrenNames[] = {
"hdlr", "ilst", "mhdr", "ctry", "lang"
};
bool metaIsFullAtom = true;
for(size_t j = 0;
j < sizeof(metaChildrenNames) / sizeof(metaChildrenNames[0]);
++j) {
if(nextSize == metaChildrenNames[j]) {
// meta is not a full atom (i.e. not followed by version, flags). It
// is followed by the size and type of the first child atom.
metaIsFullAtom = false;
break;
}
}
// Only skip next four bytes, which contain version and flags, if meta
// is a full atom.
file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0));
}
else if(name == "stsd") {
file->seek(8, File::Current);

Binary file not shown.

View File

@ -66,6 +66,7 @@ class TestMP4 : public CppUnit::TestFixture
CPPUNIT_TEST(testWithZeroLengthAtom);
CPPUNIT_TEST(testEmptyValuesRemoveItems);
CPPUNIT_TEST(testRemoveMetadata);
CPPUNIT_TEST(testNonFullMetaAtom);
CPPUNIT_TEST_SUITE_END();
public:
@ -686,6 +687,27 @@ public:
TEST_FILE_PATH_C("no-tags.m4a")));
}
}
void testNonFullMetaAtom()
{
{
MP4::File f(TEST_FILE_PATH_C("non-full-meta.m4a"));
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.hasMP4Tag());
CPPUNIT_ASSERT(f.tag()->contains("covr"));
MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList();
CPPUNIT_ASSERT_EQUAL((unsigned int)2, l.size());
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
CPPUNIT_ASSERT_EQUAL((unsigned int)79, l[0].data().size());
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format());
CPPUNIT_ASSERT_EQUAL((unsigned int)287, l[1].data().size());
PropertyMap properties = f.properties();
CPPUNIT_ASSERT_EQUAL(StringList("Test Artist!!!!"), properties["ARTIST"]);
CPPUNIT_ASSERT_EQUAL(StringList("FAAC 1.24"), properties["ENCODEDBY"]);
}
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);