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

The ILV=1 (line interleaved) path of `ff_jpegls_decode_picture()` always calls 
`ls_decode_line()` with 8 bits per sample. The ILV=0 path picks 8 or 16 bits 
from `s->bits`. JPEG-LS with more than 8 bits is only accepted for a single 
component, and for one component line interleaving is the same as ILV=0. Such a 
scan is accepted, but it is decoded as 8-bit samples into the 16-bit frame. 
Only half of each line is written and the picture is wrong. 8-bit 
single-component scans with ILV=1 already decode the same as with ILV=0.

`repro.sh` encodes testsrc2 as 8-bit and 16-bit gray with our jpegls encoder, 
sets the ILV byte of the SOS header to 1, and compares the decoded MD5s:

```
./repro.sh /path/to/ffmpeg
```

Before (c50d6fca):

```
gray: source MD5=cd06272df6d816052b7c0f27a97b92aa
gray: ILV=0  MD5=cd06272df6d816052b7c0f27a97b92aa
gray: ILV=1  MD5=cd06272df6d816052b7c0f27a97b92aa
gray16le: source MD5=508405c5ba22f58bca3b73e82e6887e8
gray16le: ILV=0  MD5=508405c5ba22f58bca3b73e82e6887e8
gray16le: ILV=1  MD5=784699b6b5149fd02a2c6db76ff00356
```

After:

```
gray16le: ILV=1  MD5=508405c5ba22f58bca3b73e82e6887e8
```

(the 8-bit lines are unchanged)

The fix uses the 16-bit line decoder when there are more than 8 bits, as the 
ILV=0 path does. Because only single-component scans can have more than 8 bits, 
the per-component byte offsets used by the 8-bit path aren't needed there. The 
second commit adds `fate-jpegls-ilv1-16bit`. It generates the file with the 
encoder, patches the ILV byte with `run_with_patched_temp`, and checks the 
decoded CRC (it equals the source's). The test fails on c50d6fca and passes 
with the fix.

Testing: `fate-jpegls` (including the sample-based `ilv0-rst`/`ilv1-rst`), 
`fate-vsynth{1,2,3}-jpegls` and `fate-source` pass.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified 
manually.

🤖 Generated with [Claude Code](https://claude.com/claude-code)



From 250f99770494ca870b288c3a702a88fe843f73c6 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Wed, 21 Oct 2026 18:26:38 +0800
Subject: [PATCH 1/2] avcodec/jpeglsdec: decode line interleaved scans with
 more than 8 bits

The ILV=1 (line interleaved) path always called ls_decode_line() with
8 bits per sample, while the ILV=0 path selects 8 or 16 bits from the
frame. JPEG-LS with more than 8 bits is only accepted for a single
component, for which line interleaving is equivalent to ILV=0, but such
a scan was decoded as 8-bit samples into the 16-bit frame: only half of
each line was written and the output was wrong, e.g. a 16-bit gray image
from our encoder with the ILV byte set to 1 decodes to a different
picture than with ILV=0.

Use the 16-bit line decoder for more than 8 bits, as for ILV=0.

Signed-off-by: Martin Mirchev <[email protected]>
---
 libavcodec/jpeglsdec.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c
index 7633bcceb68..0ab73eb3e42 100644
--- a/libavcodec/jpeglsdec.c
+++ b/libavcodec/jpeglsdec.c
@@ -480,11 +480,18 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s)
                 last = zero;
             }
             for (j = 0; j < stride; j++) {
-                ret = ls_decode_line(state, s, last + j, cur + j,
-                               Rc[j], width, stride, j, 8);
+                if (s->bits <= 8) {
+                    ret = ls_decode_line(state, s, last + j, cur + j,
+                                         Rc[j], width, stride, j, 8);
+                    Rc[j] = last[j];
+                } else {
+                    /* only single component scans can have more than 8 bits */
+                    ret = ls_decode_line(state, s, last, cur,
+                                         Rc[j], width, stride, j, 16);
+                    Rc[j] = *((uint16_t *)last);
+                }
                 if (ret < 0)
                     break;
-                Rc[j] = last[j];
             }
             if (ret < 0)
                 break;
-- 
2.54.0


From 1ae9af603bb1257f1892849f797da403b396e9ac Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Wed, 21 Oct 2026 18:26:38 +0800
Subject: [PATCH 2/2] fate/image: add a test for 16-bit JPEG-LS with line
 interleaving

Encode a 16-bit gray image with the jpegls encoder, set the ILV byte of
the SOS header to 1 and check that it decodes to the source picture.

Signed-off-by: Martin Mirchev <[email protected]>
---
 tests/fate/image.mak             | 13 +++++++++++++
 tests/ref/fate/jpegls-ilv1-16bit |  6 ++++++
 2 files changed, 19 insertions(+)
 create mode 100644 tests/ref/fate/jpegls-ilv1-16bit

diff --git a/tests/fate/image.mak b/tests/fate/image.mak
index c78baa8e086..e7914aa85cc 100644
--- a/tests/fate/image.mak
+++ b/tests/fate/image.mak
@@ -394,6 +394,19 @@ FATE_JPEGLS-$(call DEMDEC, IMAGE2, JPEGLS) += 
$(FATE_JPEGLS)
 FATE_IMAGE_FRAMECRC += $(FATE_JPEGLS-yes)
 fate-jpegls: $(FATE_JPEGLS-yes)
 
+# A single-component scan with line interleaving (ILV=1) decodes like ILV=0.
+# Encode a 16-bit gray image and set the ILV byte of the SOS header (offset 23)
+# to 1; the output must match the source.
+JPEGLS_ILV1_GEN = $(FFMPEG) -nostdin -v error -f lavfi -i 
testsrc2=s=64x64,format=gray16le \
+    -frames:v 1 -c:v jpegls -flags +bitexact -fflags +bitexact -f image2 -y
+JPEGLS_ILV1_DEC = $(FFMPEG) -nostdin -v error -flags +bitexact -fflags 
+bitexact \
+    -f framecrc - -i
+FATE_JPEGLS_ILV1-$(call ENCDEC, JPEGLS, IMAGE2, LAVFI_INDEV TESTSRC2_FILTER 
FORMAT_FILTER \
+                    WRAPPED_AVFRAME_DECODER FRAMECRC_MUXER PIPE_PROTOCOL) += 
fate-jpegls-ilv1-16bit
+fate-jpegls-ilv1-16bit: CMD = run_with_patched_temp "$(JPEGLS_ILV1_GEN)" 
"$(JPEGLS_ILV1_DEC)" jls 23 "\\0001"
+FATE_FFMPEG += $(FATE_JPEGLS_ILV1-yes)
+fate-jpegls: $(FATE_JPEGLS_ILV1-yes)
+
 FATE_IMAGE_FRAMECRC-$(call DEMDEC, IMAGE2, QDRAW) += fate-pict
 fate-pict: CMD = framecrc -i $(TARGET_SAMPLES)/quickdraw/TRU256.PCT -pix_fmt 
rgb24
 
diff --git a/tests/ref/fate/jpegls-ilv1-16bit b/tests/ref/fate/jpegls-ilv1-16bit
new file mode 100644
index 00000000000..ca238f13f32
--- /dev/null
+++ b/tests/ref/fate/jpegls-ilv1-16bit
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 64x64
+#sar 0: 0/1
+0,          0,          0,        1,     8192, 0xb5879ab5
-- 
2.54.0

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

Reply via email to