demux/ebml: interpret zero-length elements as their default value

An EBML element is permitted to have a zero-length data payload. It's
then interpreted as the element's RFC declared default value, or zero.

Before this change all zero-length elements were initialized to empty or
rejected in case of uint.

Fixes: #18351
This commit is contained in:
Kacper Michajłow committed 2026-08-14 23:24:50 +02:00
1 parent 52bb166f30
commit e167836802
3 files changed
+77 -37

No files matched your search

+48 -31
View File
@@ -28,13 +28,13 @@ from math import ldexp
elements_ebml = (
"EBML, 1a45dfa3, sub", (
"EBMLVersion, 4286, uint",
"EBMLReadVersion, 42f7, uint",
"EBMLMaxIDLength, 42f2, uint",
"EBMLMaxSizeLength, 42f3, uint",
"EBMLVersion, 4286, uint, default=1",
"EBMLReadVersion, 42f7, uint, default=1",
"EBMLMaxIDLength, 42f2, uint, default=4",
"EBMLMaxSizeLength, 42f3, uint, default=8",
"DocType, 4282, str",
"DocTypeVersion, 4287, uint",
"DocTypeReadVersion, 4285, uint",
"DocTypeVersion, 4287, uint, default=1",
"DocTypeReadVersion, 4285, uint, default=1",
),
"CRC32, bf, binary",
@@ -55,7 +55,7 @@ elements_matroska = (
"SegmentUID, 73a4, binary, length=16",
"PrevUID, 3cb923, binary, length=16",
"NextUID, 3eb923, binary, length=16",
"TimecodeScale, 2ad7b1, uint",
"TimecodeScale, 2ad7b1, uint, default=1000000",
"DateUTC, 4461, sint",
"Title, 7ba9, str",
"MuxingApp, 4d80, str",
@@ -72,7 +72,7 @@ elements_matroska = (
"DiscardPadding, 75A2, sint",
"BlockAdditions, 75A1, sub", (
"BlockMore*, A6, sub", (
"BlockAddID, EE, uint",
"BlockAddID, EE, uint, default=1",
"BlockAdditional, A5, binary",
),
),
@@ -85,34 +85,34 @@ elements_matroska = (
"TrackNumber, d7, uint",
"TrackUID, 73c5, uint",
"TrackType, 83, uint",
"FlagEnabled, b9, uint",
"FlagDefault, 88, uint",
"FlagEnabled, b9, uint, default=1",
"FlagDefault, 88, uint, default=1",
"FlagForced, 55aa, uint",
"FlagHearingImpaired, 55ab, uint",
"FlagVisualImpaired, 55ac, uint",
"FlagOriginal, 55ae, uint",
"FlagCommentary, 55af, uint",
"FlagLacing, 9c, uint",
"FlagLacing, 9c, uint, default=1",
"MinCache, 6de7, uint",
"MaxCache, 6df8, uint",
"DefaultDuration, 23e383, uint",
"TrackTimecodeScale, 23314f, float",
"TrackTimecodeScale, 23314f, float, default=1",
"MaxBlockAdditionID, 55ee, uint",
"Name, 536e, str",
"Language, 22b59c, str",
"Language, 22b59c, str, default=eng",
"LanguageBCP47, 22b59d, str",
"CodecID, 86, str",
"CodecPrivate, 63a2, binary",
"CodecName, 258688, str",
"CodecDecodeAll, aa, uint",
"CodecDecodeAll, aa, uint, default=1",
"CodecDelay, 56aa, uint",
"SeekPreRoll, 56bb, uint",
"Video, e0, sub", (
"FlagInterlaced, 9a, uint",
"PixelWidth, b0, uint",
"PixelHeight, ba, uint",
"DisplayWidth, 54b0, uint",
"DisplayHeight, 54ba, uint",
"DisplayWidth, 54b0, uint, default=context",
"DisplayHeight, 54ba, uint, default=context",
"DisplayUnit, 54b2, uint",
"PixelCropTop, 54bb, uint",
"PixelCropLeft, 54cc, uint",
@@ -122,7 +122,7 @@ elements_matroska = (
"ColourSpace, 2eb524, binary, length=4",
"StereoMode, 53b8, uint",
"Colour, 55b0, sub", (
"MatrixCoefficients, 55B1, uint",
"MatrixCoefficients, 55B1, uint, default=2",
"BitsPerChannel, 55B2, uint",
"ChromaSubsamplingHorz, 55B3, uint",
"ChromaSubsamplingVert, 55B4, uint",
@@ -131,8 +131,8 @@ elements_matroska = (
"ChromaSitingHorz, 55B7, uint",
"ChromaSitingVert, 55B8, uint",
"Range, 55B9, uint",
"TransferCharacteristics, 55BA, uint",
"Primaries, 55BB, uint",
"TransferCharacteristics, 55BA, uint, default=2",
"Primaries, 55BB, uint, default=2",
"MaxCLL, 55BC, uint",
"MaxFALL, 55BD, uint",
"MasteringMetadata, 55D0, sub", (
@@ -157,15 +157,15 @@ elements_matroska = (
),
),
"Audio, e1, sub", (
"SamplingFrequency, b5, float",
"OutputSamplingFrequency, 78b5, float",
"Channels, 9f, uint",
"SamplingFrequency, b5, float, default=8000",
"OutputSamplingFrequency, 78b5, float, default=context",
"Channels, 9f, uint, default=1",
"BitDepth, 6264, uint",
),
"ContentEncodings, 6d80, sub", (
"ContentEncoding*, 6240, sub", (
"ContentEncodingOrder, 5031, uint",
"ContentEncodingScope, 5032, uint",
"ContentEncodingScope, 5032, uint, default=1",
"ContentEncodingType, 5033, uint",
"ContentCompression, 5034, sub", (
"ContentCompAlgo, 4254, uint",
@@ -215,12 +215,12 @@ elements_matroska = (
"ChapterTimeStart, 91, uint",
"ChapterTimeEnd, 92, uint",
"ChapterFlagHidden, 98, uint",
"ChapterFlagEnabled, 4598, uint",
"ChapterFlagEnabled, 4598, uint, default=1",
"ChapterSegmentUID, 6e67, binary, length=16",
"ChapterSegmentEditionUID, 6ebc, uint",
"ChapterDisplay*, 80, sub", (
"ChapString, 85, str",
"ChapLanguage*, 437c, str",
"ChapLanguage*, 437c, str, default=eng",
"ChapLanguageBCP47*, 437d, str",
"ChapCountry*, 437e, str",
),
@@ -230,7 +230,7 @@ elements_matroska = (
"Tags*, 1254c367, sub", (
"Tag*, 7373, sub", (
"Targets, 63c0, sub", (
"TargetTypeValue, 68ca, uint",
"TargetTypeValue, 68ca, uint, default=50",
"TargetType, 63ca, str",
"TargetTrackUID, 63c5, uint",
"TargetEditionUID, 63c9, uint",
@@ -239,10 +239,10 @@ elements_matroska = (
),
"SimpleTag*, 67c8, sub", (
"TagName, 45a3, str",
"TagLanguage, 447a, str",
"TagLanguage, 447a, str, default=und",
"TagLanguageBCP47, 447b, str",
"TagString, 4487, str",
"TagDefault, 4484, uint",
"TagDefault, 4484, uint, default=1",
),
),
),
@@ -266,7 +266,8 @@ def camelcase_to_words(name):
class MatroskaElement:
def __init__(self, name, elid, valtype, namespace, length=None):
def __init__(self, name, elid, valtype, namespace, length=None,
default=None):
self.name = name
self.definename = f"{namespace}_ID_{name.upper()}"
self.fieldname = camelcase_to_words(name)
@@ -274,6 +275,7 @@ class MatroskaElement:
self.elid = elid
self.valtype = valtype
self.length = int(length or "0")
self.default = default
if valtype == "sub":
self.ebmltype = "EBML_TYPE_SUBELEMENTS"
self.valname = "struct " + self.structname
@@ -292,6 +294,21 @@ class MatroskaElement:
self.subelements = subelements
self.subids = {x[0].elid for x in subelements}
def default_initializer(self):
if self.default is None:
return "false, {0}"
if self.default == "context":
return "true, {0}"
if self.valtype == "uint":
return f"false, {{.u = {int(self.default)}}}"
if self.valtype == "sint":
return f"false, {{.i = {int(self.default)}}}"
if self.valtype == "float":
return f"false, {{.f = {float(self.default)}}}"
if self.valtype == "str":
return f'false, {{.s = "{self.default}"}}'
raise SyntaxError("Default not supported for type " + self.valtype)
elementd: dict[str, MatroskaElement] = {}
elementlist: list[MatroskaElement] = []
def parse_elems(elements, namespace):
@@ -302,7 +319,7 @@ def parse_elems(elements, namespace):
attrs = {}
for extra in rest:
key, sep, value = extra.partition("=")
if not sep or key not in ("length",):
if not sep or key not in ("length", "default"):
raise SyntaxError("Invalid element attribute " + extra)
attrs[key] = value
hexid = hexid.lower()
@@ -371,7 +388,7 @@ def generate_c_definitions(out):
printf(out, "#undef N")
else:
printf(out, f'E("{el.name}", {el.fieldname}, {el.ebmltype}, '
f'{el.length})')
f'{el.length}, {el.default_initializer()})')
def read(s, length):
t = s.read(length)
+22 -6
View File
@@ -210,8 +210,11 @@ int ebml_resync_cluster(struct mp_log *log, stream_t *s)
static_assert(sizeof(((struct ebml_elem_desc *)0)->defval) == sizeof(((struct ebml_elem_desc *)0)->defval.u),
"defval.u must cover the entire union for {0} initialization");
#define EVALARGS(F, ...) F(__VA_ARGS__)
#define E(str, N, type, length) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type, length };
#define E(str, N, type, length, context, ...) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type, length, .context_default = context, .defval = __VA_ARGS__ };
#define E_SN(str, count, N) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, EBML_TYPE_SUBELEMENTS, sizeof(struct ebml_ ## N), count, (const struct ebml_field_desc[]){
#define E_S(str, count) EVALARGS(E_SN, str, count, N)
#define FN(id, name, multiple, N) { id, multiple, offsetof(struct ebml_ ## N, name), offsetof(struct ebml_ ## N, n_ ## name), &ebml_##name##_desc},
@@ -510,11 +513,13 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
else \
subelptr = (fieldtype *) fieldptr
GETPTR(uintptr, uint64_t);
if (length < 1 || length > 8) {
if (length > 8) {
MP_ERR(ctx, "uint invalid length %"PRIu64"\n", length);
goto error;
}
*uintptr = ebml_parse_uint(data, length);
if (!length && ed->context_default)
goto skip;
*uintptr = length ? ebml_parse_uint(data, length) : ed->defval.u;
MP_TRACE(ctx, "uint %"PRIu64"\n", *uintptr);
break;
@@ -525,7 +530,9 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
MP_ERR(ctx, "sint invalid length %"PRIu64"\n", length);
goto error;
}
*sintptr = ebml_parse_sint(data, length);
if (!length && ed->context_default)
goto skip;
*sintptr = length ? ebml_parse_sint(data, length) : ed->defval.i;
MP_TRACE(ctx, "sint %"PRId64"\n", *sintptr);
break;
@@ -536,7 +543,9 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
MP_ERR(ctx, "float invalid length %"PRIu64"\n", length);
goto error;
}
*floatptr = ebml_parse_float(data, length);
if (!length && ed->context_default)
goto skip;
*floatptr = length ? ebml_parse_float(data, length) : ed->defval.f;
MP_DBG(ctx, "float %f\n", *floatptr);
break;
@@ -547,7 +556,13 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
}
char **strptr;
GETPTR(strptr, char *);
*strptr = talloc_strndup(ctx->talloc_ctx, data, length);
if (!length && ed->context_default)
goto skip;
if (!length && ed->defval.s) {
*strptr = talloc_strdup(ctx->talloc_ctx, ed->defval.s);
} else {
*strptr = talloc_strndup(ctx->talloc_ctx, data, length);
}
MP_TRACE(ctx, "string \"%s\"\n", *strptr);
break;
@@ -583,6 +598,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
}
*countptr += 1;
error:
skip:
data += length;
}
}
+7
View File
@@ -54,6 +54,13 @@ struct ebml_elem_desc {
int size;
int field_count;
const struct ebml_field_desc *fields;
bool context_default; // default derives from other elements
union {
uint64_t u;
int64_t i;
double f;
const char *s;
} defval; // default value of a zero-length element
};
struct ebml_parse_ctx {