[Matroska] Fix unknown size elements (#1377)

In matroska an element data size of a VINT with all bits 1 means the
data size is unknown. Unknown data size can only apply to Master
Elements.

Unknown sized elements are described in
https://datatracker.ietf.org/doc/rfc8794/ section 6.2
It gives the following 5 conditions for detecting the end of an
unknown sized element:

 *  Any EBML Element that is a valid Parent Element of the Unknown-
    Sized Element according to the EBML Schema, Global Elements
    excluded.

 *  Any valid EBML Element according to the EBML Schema, Global
    Elements excluded, that is not a Descendant Element of the
    Unknown-Sized Element but shares a common direct parent, such as a
    Top-Level Element.

 *  Any EBML Element that is a valid Root Element according to the
    EBML Schema, Global Elements excluded.

 *  The end of the Parent Element with a known size has been reached.

 *  The end of the EBML Document, either when reaching the end of the
    file or because a new EBML Header started.

In this patch we use the higher level maxOffset to determine
the maximum data size for the element, which matches the fourth
condition, but is incomplete without the other four methods.

As only Segment and Cluster elements of Matroska files are allowed
to use unknown size length and TagLib does not process Cluster
elements, this should be sufficient.

---------

Signed-off-by: Anthony Brandon <anthony.brandon@gmail.com>
Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
This commit is contained in:
Anthony
2026-07-11 09:02:39 +02:00
committed by GitHub
parent e0d24f7fa7
commit eb698de6e5
3 changed files with 67 additions and 2 deletions

View File

@@ -160,6 +160,7 @@ class TestMatroska : public CppUnit::TestFixture
CPPUNIT_TEST(testSaveTypesBeforeCues);
CPPUNIT_TEST(testSaveTypesNoTrailingVoid);
CPPUNIT_TEST(testSaveTypesReclaimVoid);
CPPUNIT_TEST(testUnknownSizeSegment);
CPPUNIT_TEST_SUITE_END();
public:
@@ -1777,6 +1778,56 @@ public:
}
}
void testUnknownSizeSegment()
{
ScopedFileCopy copy("no-tags", ".mka");
string newname = copy.fileName();
// Modify the copied file (/tmp/taglib-test.mka on Linux) to have elements
// with unknown size length.
{
PlainFile file(newname.c_str());
ByteVector fileData = file.readAll();
CPPUNIT_ASSERT_EQUAL(12390U, fileData.size());
// Size of Segment
for(int i = 0x2d; i <= 0x33; ++i) {
fileData[i] = '\xff';
}
// Size of Cluster
for(int i = 0x2d; i <= 0x33; ++i) {
fileData[i] = '\xff';
}
fileData[0x1482] = '\x7f';
fileData[0x1483] = '\xff';
file.seek(0);
file.writeBlock(fileData);
}
{
Matroska::File f(newname.c_str(), true, AudioProperties::Accurate);
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(444, f.audioProperties()->lengthInMilliseconds());
CPPUNIT_ASSERT_EQUAL(223, f.audioProperties()->bitrate());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT_EQUAL(String("matroska"), f.audioProperties()->docType());
CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->docTypeVersion());
CPPUNIT_ASSERT_EQUAL(String("A_MPEG/L3"), f.audioProperties()->codecName());
CPPUNIT_ASSERT_EQUAL(String(""), f.audioProperties()->title());
CPPUNIT_ASSERT(!f.tag(false));
CPPUNIT_ASSERT(!f.attachments(false));
auto tag = f.tag(true);
CPPUNIT_ASSERT(tag->isEmpty());
tag->setTitle("Unknown size");
CPPUNIT_ASSERT(f.save());
}
{
Matroska::File f(newname.c_str(), true, AudioProperties::Accurate);
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.tag(false));
CPPUNIT_ASSERT_EQUAL(String("Unknown size"), f.tag()->title());
}
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMatroska);