Skip to content

Commit f91eb12

Browse files
committed
rwebm: parse DiscardPadding
Matroska DiscardPadding (0x75A2) declares how many nanoseconds of decoded output at the end of a block are not meant to be played -- Opus end trimming. Parse it and expose it per packet; the element may sit before or after its Block within the BlockGroup (ffmpeg writes it after), so a capture of the preceding position is combined with a bounded scan of the remaining group siblings at Block emission. For laced blocks the padding is attached to the last laced frame, which is the block tail it trims.
1 parent 7b6a41f commit f91eb12

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

libretro-common/formats/webm/rwebm.c

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#define ID_SIMPLEBLOCK 0xA3u
4646
#define ID_BLOCKGROUP 0xA0u
4747
#define ID_BLOCK 0xA1u
48+
#define ID_DISCARDPADDING 0x75A2u
4849

4950
#define TRACKTYPE_VIDEO 1
5051
#define TRACKTYPE_AUDIO 2
@@ -119,6 +120,14 @@ static uint64_t be_uint(const uint8_t *p, size_t n)
119120
return v;
120121
}
121122

123+
/* Signed big-endian integer element payload (sign-extended). */
124+
static int64_t ebml_sint(const uint8_t *p, size_t n)
125+
{
126+
uint64_t v = be_uint(p, n);
127+
int shift = (int)(64 - 8 * n);
128+
return ((int64_t)(v << shift)) >> shift;
129+
}
130+
122131
/* Read a big-endian IEEE float/double (Matroska float element). */
123132
static double be_float(const uint8_t *p, size_t n)
124133
{
@@ -156,6 +165,8 @@ struct rwebm
156165
int64_t cluster_ts; /* current cluster timestamp (ticks) */
157166
/* Lacing: a SimpleBlock may carry several frames; we emit them one at a
158167
* time, so remember where we are within a block. */
168+
int64_t pending_discard; /* DiscardPadding for the next Block */
169+
int64_t lace_discard; /* padding owed by the laced block */
159170
const uint8_t *lace_data; /* payload after the block header */
160171
const uint32_t *lace_sizes; /* not used (see note); kept simple */
161172
int lace_count;
@@ -298,6 +309,31 @@ void rwebm_rewind(rwebm_t *webm)
298309
webm->lace_index = 0;
299310
}
300311

312+
/* DiscardPadding for the Block just parsed: the element may sit before
313+
* the Block (already captured in pending_discard) or after it within
314+
* the same BlockGroup, so scan the remaining group siblings. */
315+
static int64_t webm_group_discard(rwebm_t *webm, const uint8_t *p,
316+
const uint8_t *group_end)
317+
{
318+
int64_t v = webm->pending_discard;
319+
ebml_reader s;
320+
webm->pending_discard = 0;
321+
s.p = p;
322+
s.end = group_end;
323+
while (s.p < s.end)
324+
{
325+
int ok;
326+
uint32_t id = ebml_read_id(&s);
327+
uint64_t sz = ebml_read_vint(&s, 1, &ok);
328+
if (!id || !ok || s.p + sz > s.end)
329+
break;
330+
if (id == ID_DISCARDPADDING && sz >= 1 && sz <= 8)
331+
v = ebml_sint(s.p, (size_t)sz);
332+
s.p += sz;
333+
}
334+
return v;
335+
}
336+
301337
int rwebm_read_packet(rwebm_t *webm, rwebm_packet *pkt)
302338
{
303339
ebml_reader r;
@@ -313,6 +349,8 @@ int rwebm_read_packet(rwebm_t *webm, rwebm_packet *pkt)
313349
pkt->track = webm->lace_track;
314350
pkt->timestamp = webm->lace_ts;
315351
pkt->keyframe = webm->lace_keyframe;
352+
pkt->discard_padding = (webm->lace_index == webm->lace_count)
353+
? webm->lace_discard : 0;
316354
return 1;
317355
}
318356

@@ -331,11 +369,23 @@ int rwebm_read_packet(rwebm_t *webm, rwebm_packet *pkt)
331369
if (id == ID_CLUSTER || id == ID_BLOCKGROUP)
332370
{
333371
/* Descend: Cluster holds Timestamp + blocks; BlockGroup wraps a
334-
* single Block (plus duration/refs we ignore). Scan children. */
372+
* single Block (plus DiscardPadding/duration/refs). Scan
373+
* children. */
374+
if (id == ID_BLOCKGROUP)
375+
webm->pending_discard = 0;
335376
r.p = body;
336377
r.end = body + sz;
337378
continue;
338379
}
380+
if (id == ID_DISCARDPADDING)
381+
{
382+
/* Signed big-endian integer, nanoseconds; may precede the
383+
* Block within its BlockGroup. */
384+
if (sz >= 1 && sz <= 8)
385+
webm->pending_discard = ebml_sint(body, (size_t)sz);
386+
r.p = body + sz;
387+
continue;
388+
}
339389
if (id == ID_TIMESTAMP)
340390
{
341391
webm->cluster_ts = (int64_t)be_uint(body, (size_t)sz);
@@ -379,6 +429,8 @@ int rwebm_read_packet(rwebm_t *webm, rwebm_packet *pkt)
379429
* webm->timestamp_scale;
380430
pkt->keyframe = (id == ID_SIMPLEBLOCK)
381431
? ((flags & 0x80) != 0) : 1;
432+
pkt->discard_padding = (id == ID_BLOCK)
433+
? webm_group_discard(webm, body + sz, r.end) : 0;
382434
webm->cur = body + sz;
383435
return 1;
384436
}
@@ -451,6 +503,8 @@ int rwebm_read_packet(rwebm_t *webm, rwebm_packet *pkt)
451503
webm->lace_track = tidx;
452504
webm->lace_ts = (webm->cluster_ts + rel_ts)
453505
* webm->timestamp_scale;
506+
webm->lace_discard = (id == ID_BLOCK)
507+
? webm_group_discard(webm, body + sz, r.end) : 0;
454508
webm->lace_keyframe = (id == ID_SIMPLEBLOCK)
455509
? ((flags & 0x80) != 0) : 1;
456510
for (j = 0; j < nframes; j++)
@@ -468,6 +522,9 @@ int rwebm_read_packet(rwebm_t *webm, rwebm_packet *pkt)
468522
pkt->track = webm->lace_track;
469523
pkt->timestamp = webm->lace_ts;
470524
pkt->keyframe = webm->lace_keyframe;
525+
pkt->discard_padding =
526+
(webm->lace_index == webm->lace_count)
527+
? webm->lace_discard : 0;
471528
return 1;
472529
}
473530
}

libretro-common/include/formats/rwebm.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ typedef struct
6666
int track; /* index into the track array (0-based) */
6767
int64_t timestamp; /* in nanoseconds (scaled by TimestampScale) */
6868
int keyframe;
69+
/* Matroska DiscardPadding in nanoseconds: when positive, this many
70+
* nanoseconds of decoded output at the END of this block are not
71+
* meant to be played (Opus end trimming). 0 when absent. */
72+
int64_t discard_padding;
6973
} rwebm_packet;
7074

7175
typedef struct rwebm rwebm_t;

0 commit comments

Comments
 (0)