PR #23851 opened by superuser404
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23851
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23851.patch
`parse_presentation_segment()` flushes the palette and object caches for every
`composition_state != 0`. Composition state 3 (epoch continue) however
continues the previous epoch: when decoding linearly, the retained palettes and
objects stay valid, and streams carry bare PCS + WDS + END display sets at
connection points that reference them without re-supplying the data.
Flushing on epoch continue drops exactly the data such a display set
references. Decoding then fails with `Invalid palette id` / `Invalid object id`
and the subtitle is missing on screen. I have seen this in the wild on
BD-sourced PGS streams whose authoring splits at connection points.
With this patch only acquisition point (1) and epoch start (2) release the
cache.
### Reproducer
The generator below writes a minimal .sup: display set 1 (epoch start) defines
a palette and an 8x8 object and presents it, display set 2 (epoch continue) is
a bare PCS + WDS + END referencing the retained data.
Before:
```
$ ffprobe -select_streams s:0 -show_frames -show_entries frame=pts_time
epoch_continue.sup
pts_time=1.000000
[pgssub @ ...] Invalid palette id 0
```
After:
```
pts_time=1.000000
pts_time=3.000000
```
<details>
<summary>gen_pgs_epoch_continue.py</summary>
```python
#!/usr/bin/env python3
import struct
import sys
def seg(pts_90k, seg_type, payload):
return b"PG" + struct.pack(">IIBH", pts_90k, 0, seg_type, len(payload)) +
payload
def pcs(comp_num, state, num_objs):
p = struct.pack(">HHBHBBBB", 720, 480, 0x10, comp_num, state, 0x00, 0,
num_objs)
if num_objs:
p += struct.pack(">HBBHH", 0, 0, 0x00, 100, 100) # obj 0 in window 0
at (100,100)
return p
def wds():
return struct.pack(">BBHHHH", 1, 0, 100, 100, 8, 8)
def pds():
entries = struct.pack(">BBBBB", 0, 0x00, 0x80, 0x80, 0x00) # entry 0:
transparent
entries += struct.pack(">BBBBB", 1, 0xEB, 0x80, 0x80, 0xFF) # entry 1:
opaque white
return struct.pack(">BB", 0, 0) + entries
def ods():
rle = (b"\x01" * 8 + b"\x00\x00") * 8 # 8x8 solid color 1, one run per line
data_len = 4 + len(rle) # width + height fields + RLE payload
return struct.pack(">HBB", 0, 0, 0xC0) + data_len.to_bytes(3, "big") + \
struct.pack(">HH", 8, 8) + rle
out = b""
t1 = 90000 # display set 1: epoch start at 1s
out += seg(t1, 0x16, pcs(0, 0x80, 1))
out += seg(t1, 0x17, wds())
out += seg(t1, 0x14, pds())
out += seg(t1, 0x15, ods())
out += seg(t1, 0x80, b"")
t2 = 270000 # display set 2: epoch continue at 3s, bare PCS+WDS+END
out += seg(t2, 0x16, pcs(1, 0xC0, 1))
out += seg(t2, 0x17, wds())
out += seg(t2, 0x80, b"")
with open(sys.argv[1] if len(sys.argv) > 1 else "epoch_continue.sup", "wb") as
f:
f.write(out)
```
</details>
Note: this touches the same hunk as the pending decoder rework in #21109, which
keeps the flush-on-any-nonzero-state behavior. The two changes are independent;
I am happy to rebase this on top of #21109 if that lands first.
>From 1fa66ef502288d300ab44ab16287b34fd9e86280 Mon Sep 17 00:00:00 2001
From: Vincent Herbst <[email protected]>
Date: Sun, 19 Jul 2026 20:10:22 +0200
Subject: [PATCH] avcodec/pgssubdec: keep cached palettes and objects on epoch
continue
parse_presentation_segment() flushes the palette and object caches for
every composition_state != 0. composition_state 3 (epoch continue)
however continues the previous epoch: when decoding linearly, the
retained palettes and objects stay valid, and streams carry bare
PCS + WDS + END display sets at connection points that reference them
without re-supplying the data.
Flushing on epoch continue drops exactly the data such a display set
references, decoding fails with "Invalid palette id" / "Invalid object
id", and the subtitle is missing on screen. Seen in the wild on
BD-sourced PGS streams whose authoring splits at connection points.
Only acquisition point (1) and epoch start (2) release the cache now.
A minimal reproducer is an epoch start display set defining one object
followed by an epoch continue display set with a bare PCS + WDS + END
referencing it.
---
libavcodec/pgssubdec.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c
index 20583c9afa..079808ac4a 100644
--- a/libavcodec/pgssubdec.c
+++ b/libavcodec/pgssubdec.c
@@ -416,12 +416,15 @@ static int parse_presentation_segment(AVCodecContext
*avctx,
* 00 - Normal, previously defined objects and palettes are still valid
* 01 - Acquisition point, previous objects and palettes can be released
* 10 - Epoch start, previous objects and palettes can be released
- * 11 - Epoch continue, previous objects and palettes can be released
+ * 11 - Epoch continue, the epoch continues across a connection point;
+ * when decoding linearly the previously defined objects and
+ * palettes remain valid and display sets may reference them
+ * without re-supplying the data
*
* reserved 6 bits discarded
*/
state = bytestream_get_byte(&buf) >> 6;
- if (state != 0) {
+ if (state != 0 && state != 3) {
flush_cache(avctx);
}
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]