PR #25831 opened by romainhedouin
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25831
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25831.patch

The encoder wrote h / 2 lines for both the top and the bottom half of a bitmap. 
For an odd height the last line belongs to the top half, so it was never 
written and came back empty after decoding. A 1-line bitmap was lost entirely.

The top half now gets (h + 1) / 2 lines. Even heights give the same bytes as 
before.

Encoding then decoding the PGS and VobSub samples now gives back every pixel, 
and the dvbsub FATE tests are unchanged.

For a height of 1, our decoder now logs an "Invalid object location" error once 
for that object. The spec says such an object isn't valid, and before this 
change the bitmap was lost.

Testing: I tested this change against a corpus of thousands of random subtitle 
sets, even heights (all byte-identical before/after), I tested the output with 
the VLC decoder, I tested extreme sizes

Found during a code review sweep of ffmpeg using an LLM.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From b49916f1fcdaa68cd8ffecb7805ea0d800ae2c04 Mon Sep 17 00:00:00 2001
From: romainhedouin <[email protected]>
Date: Sat, 7 Nov 2026 13:16:41 +0100
Subject: [PATCH] avcodec/dvbsubenc: encode the last line of odd-height bitmaps

An object is coded as two interlaced fields: the top field holds lines
0, 2, 4, ... and the bottom field lines 1, 3, 5, ... (ETSI EN 300 743,
7.2.5), which is also how the decoder places them. Both fields were
coded with h >> 1 lines, so for an odd height the last line, which
belongs to the top field, was never written; our decoder left it at
the region background, pixel code 0. A height of 1 lost the whole
bitmap.

Bitmaps of odd height are common, for example when converting PGS
subtitles; the PGS sample of the FATE suite has two rects of heights 67
and 63, and an encode/decode round trip with our own decoder now gives
back every pixel of them. Even heights produce the same bytes as before.

Code (h + 1) >> 1 lines in the top field. Each line is still checked
against the worst case line size in the RLE coders, so the output buffer
checks hold for the extra line.

For a height of 1 the bottom field is now empty, which the specification
defines as repeating the top field; our decoder then logs that the
repeated line lies outside the region and keeps the line it decoded.

Found during a code review sweep of ffmpeg using an LLM.

Signed-off-by: romainhedouin <[email protected]>
---
 libavcodec/dvbsubenc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/dvbsubenc.c b/libavcodec/dvbsubenc.c
index a7507723568..4ba9d954b7e 100644
--- a/libavcodec/dvbsubenc.c
+++ b/libavcodec/dvbsubenc.c
@@ -526,7 +526,7 @@ static int dvbsub_encode(AVCodecContext *avctx, uint8_t 
*outbuf, int buf_size,
                                      h->rects[object_id]->data[0],
                                      h->rects[object_id]->w * 2,
                                      h->rects[object_id]->w,
-                                     h->rects[object_id]->h >> 1);
+                                     (h->rects[object_id]->h + 1) >> 1);
                 if (ret < 0)
                     return ret;
                 buf_size -= ret;
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to