PR #21496 opened by Ramiro Polla (ramiro) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21496 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21496.patch
This PR improves unescaping of SOS fields using restart markers: - the SOS header is no longer incorrectly unescaped; - `ff_mjpeg_find_marker()` should no longer have to needlessly skip over data which has already been consumed; - the behaviour for stuffed zero bytes (and bits for jpegls) is more in line with IJG's reference implementation; - fix decoding of jpegls files with restart markers. This paves the way to implement slice-based multi-threading in mjpeg by using restart markers. >From 693e49ce231b5b4feef31d76bad357123ac79069 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 10 Sep 2025 16:04:06 +0200 Subject: [PATCH 01/21] avcodec/mjpegdec: remove unnecessary else --- libavcodec/mjpegdec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 3af4b05551..076c0dc2fa 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2405,9 +2405,10 @@ redo_for_pal8: &unescaped_buf_ptr, &unescaped_buf_size); /* EOF */ - if (start_code < 0) { + if (start_code < 0) break; - } else if (unescaped_buf_size > INT_MAX / 8) { + + if (unescaped_buf_size > INT_MAX / 8) { av_log(avctx, AV_LOG_ERROR, "MJPEG packet 0x%x too big (%d/%d), corrupt data?\n", start_code, unescaped_buf_size, buf_size); -- 2.52.0 >From 996e6a1f850e0fb6058fed82dcd0a399f95afe44 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 11 Sep 2025 17:45:47 +0200 Subject: [PATCH 02/21] avcodec/mjpegdec: remove start_code field from MJpegDecodeContext Instead, pass it as a parameter to the only function that uses it. --- libavcodec/mjpegbdec.c | 4 ---- libavcodec/mjpegdec.c | 14 ++++++-------- libavcodec/mjpegdec.h | 1 - 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/libavcodec/mjpegbdec.c b/libavcodec/mjpegbdec.c index 79eed6eaa6..5a21ea47fc 100644 --- a/libavcodec/mjpegbdec.c +++ b/libavcodec/mjpegbdec.c @@ -85,7 +85,6 @@ read_header: av_log(avctx, AV_LOG_DEBUG, "dqt offs: 0x%"PRIx32"\n", dqt_offs); if (dqt_offs) { bytestream2_init(&s->gB, buf_ptr+dqt_offs, buf_end - (buf_ptr+dqt_offs)); - s->start_code = DQT; ret = ff_mjpeg_decode_dqt(s); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; @@ -95,7 +94,6 @@ read_header: av_log(avctx, AV_LOG_DEBUG, "dht offs: 0x%"PRIx32"\n", dht_offs); if (dht_offs) { bytestream2_init(&s->gB, buf_ptr+dht_offs, buf_end - (buf_ptr+dht_offs)); - s->start_code = DHT; ff_mjpeg_decode_dht(s); } @@ -103,7 +101,6 @@ read_header: av_log(avctx, AV_LOG_DEBUG, "sof offs: 0x%"PRIx32"\n", sof_offs); if (sof_offs) { bytestream2_init(&s->gB, buf_ptr+sof_offs, buf_end - (buf_ptr+sof_offs)); - s->start_code = SOF0; if ((ret = ff_mjpeg_decode_sof(s)) < 0) return ret; } @@ -116,7 +113,6 @@ read_header: bytestream2_init(&s->gB, buf_ptr+sos_offs, FFMIN(field_size, buf_end - buf_ptr - sos_offs)); s->mjpb_skiptosod = (sod_offs - sos_offs - bytestream2_peek_be16(&s->gB)); - s->start_code = SOS; if (avctx->skip_frame == AVDISCARD_ALL) { bytestream2_skipu(&s->gB, bytestream2_get_bytes_left(&s->gB)); } else { diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 076c0dc2fa..313feaa7a8 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -134,7 +134,6 @@ av_cold int ff_mjpeg_decode_init(AVCodecContext *avctx) init_idct(avctx); s->buffer_size = 0; s->buffer = NULL; - s->start_code = -1; s->first_picture = 1; s->got_picture = 0; s->orig_height = avctx->coded_height; @@ -1866,7 +1865,7 @@ static int mjpeg_decode_dri(MJpegDecodeContext *s) return 0; } -static int mjpeg_decode_app(MJpegDecodeContext *s) +static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) { int len, id, i; @@ -2013,7 +2012,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s) } /* JPS extension by VRex */ - if (s->start_code == APP3 && id == AV_RB32("_JPS") && len >= 10) { + if (start_code == APP3 && id == AV_RB32("_JPS") && len >= 10) { int flags, layout, type; if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_INFO, "_JPSJPS_\n"); @@ -2053,7 +2052,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s) } /* EXIF metadata */ - if (s->start_code == APP1 && id == AV_RB32("Exif") && len >= 2) { + if (start_code == APP1 && id == AV_RB32("Exif") && len >= 2) { int ret; bytestream2_skipu(&s->gB, 2); // skip padding @@ -2072,7 +2071,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s) } /* Apple MJPEG-A */ - if ((s->start_code == APP1) && (len > (0x28 - 8))) { + if ((start_code == APP1) && (len > (0x28 - 8))) { id = bytestream2_get_be32u(&s->gB); len -= 4; /* Apple MJPEG-A */ @@ -2092,7 +2091,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s) } } - if (s->start_code == APP2 && id == AV_RB32("ICC_") && len >= 10) { + if (start_code == APP2 && id == AV_RB32("ICC_") && len >= 10) { int id2; unsigned seqno; unsigned nummarkers; @@ -2419,7 +2418,6 @@ redo_for_pal8: bytestream2_init(&s->gB, unescaped_buf_ptr, unescaped_buf_size); - s->start_code = start_code; if (avctx->debug & FF_DEBUG_STARTCODE) av_log(avctx, AV_LOG_DEBUG, "startcode: %X\n", start_code); @@ -2429,7 +2427,7 @@ redo_for_pal8: "restart marker: %d\n", start_code & 0x0f); /* APP fields */ } else if (start_code >= APP0 && start_code <= APP15) { - if ((ret = mjpeg_decode_app(s)) < 0) + if ((ret = mjpeg_decode_app(s, start_code)) < 0) av_log(avctx, AV_LOG_ERROR, "unable to decode APP fields: %s\n", av_err2str(ret)); /* Comment */ diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index c2f83ac949..0feb398a22 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -60,7 +60,6 @@ typedef struct MJpegDecodeContext { GetByteContext gB; int buf_size; - int start_code; /* current start code */ int buffer_size; uint8_t *buffer; -- 2.52.0 >From 8ef12113561fd7604a63d962fec7f2564a16d91f Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 18 Sep 2025 16:37:03 +0200 Subject: [PATCH 03/21] avcodec/mjpegdec: remove commented out code --- libavcodec/mjpegdec.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 313feaa7a8..788a6a854a 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1796,9 +1796,6 @@ next_field: } else if (s->lossless) { av_assert0(s->picture_ptr == s->picture); if (CONFIG_JPEGLS_DECODER && s->ls) { -// for () { -// reset_ls_coding_parameters(s, 0); - if ((ret = ff_jpegls_decode_picture(s, predictor, point_transform, ilv)) < 0) return ret; -- 2.52.0 >From 764a1a291d9fdacee112e7c5dffd1204abb3eb2d Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 23 Sep 2025 16:33:26 +0200 Subject: [PATCH 04/21] avcodec/mjpegdec: improve debug message in find_marker() Use pointer arithmetic instead of an extra variable to keep track of skipped bytes. --- libavcodec/mjpegdec.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 788a6a854a..07e9e17dad 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2210,7 +2210,6 @@ static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) { const uint8_t *buf_ptr; int val; - int skipped = 0; buf_ptr = *pbuf_ptr; while ((buf_ptr = memchr(buf_ptr, 0xff, buf_end - buf_ptr))) { @@ -2223,12 +2222,12 @@ static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) break; } } - skipped++; } buf_ptr = buf_end; val = -1; found: - ff_dlog(NULL, "find_marker skipped %d bytes\n", skipped); + ff_dlog(NULL, "find_marker skipped %td bytes\n", + (buf_ptr - *pbuf_ptr) - (val < 0 ? 0 : 2)); *pbuf_ptr = buf_ptr; return val; } -- 2.52.0 >From e06c411ef64a3fd5ecde5f1bd50c4a287936a2d1 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 24 Sep 2025 23:30:57 +0200 Subject: [PATCH 05/21] avcodec/mjpegdec: move initialization of last_dc field to mjpeg_decode_scan() The last_dc field is only used in sequential JPEG and the DC coefficients for progressive JPEG. --- libavcodec/mjpegdec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 07e9e17dad..ca5cdc823b 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1503,6 +1503,9 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, s->coefs_finished[c] |= 1; } + for (i = 0; i < nb_components; i++) + s->last_dc[i] = (4 << s->bits); + for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { const int copy_mb = mb_bitmask && !get_bits1(&mb_bitmask_gb); @@ -1779,9 +1782,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, return ret; next_field: - for (i = 0; i < nb_components; i++) - s->last_dc[i] = (4 << s->bits); - if (s->avctx->hwaccel) { int bytes_to_start = bytestream2_tell(&s->gB); av_assert0(bytes_to_start >= 0 && -- 2.52.0 >From 0631497e3e274a55335dc83ea5e21b0e50fbc773 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 24 Sep 2025 23:33:50 +0200 Subject: [PATCH 06/21] avcodec/mjpegdec: move handling of AVRn interlaced picture to mjpeg_decode_scan() AVRn interlaced files are only present in sequential JPEG. --- libavcodec/mjpegdec.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index ca5cdc823b..8f36c9465e 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1503,6 +1503,7 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, s->coefs_finished[c] |= 1; } +next_field: for (i = 0; i < nb_components; i++) s->last_dc[i] = (4 << s->bits); @@ -1589,6 +1590,22 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, handle_rstn(s, nb_components); } } + + if (s->interlaced && + get_bits_left(&s->gb) > 32 && + show_bits(&s->gb, 8) == 0xFF) { + GetBitContext bak = s->gb; + align_get_bits(&bak); + if (show_bits(&bak, 16) == 0xFFD1) { + av_log(s->avctx, AV_LOG_DEBUG, "AVRn interlaced picture marker found\n"); + s->gb = bak; + skip_bits(&s->gb, 16); + s->bottom_field ^= 1; + + goto next_field; + } + } + return 0; } @@ -1781,7 +1798,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, if (ret < 0) return ret; -next_field: if (s->avctx->hwaccel) { int bytes_to_start = bytestream2_tell(&s->gB); av_assert0(bytes_to_start >= 0 && @@ -1825,21 +1841,6 @@ next_field: } } - if (s->interlaced && - get_bits_left(&s->gb) > 32 && - show_bits(&s->gb, 8) == 0xFF) { - GetBitContext bak = s->gb; - align_get_bits(&bak); - if (show_bits(&bak, 16) == 0xFFD1) { - av_log(s->avctx, AV_LOG_DEBUG, "AVRn interlaced picture marker found\n"); - s->gb = bak; - skip_bits(&s->gb, 16); - s->bottom_field ^= 1; - - goto next_field; - } - } - /* Add the amount of bits read from the unescaped image data buffer * into the GetByteContext. */ bytestream2_skipu(&s->gB, (get_bits_count(&s->gb) + 7) / 8); -- 2.52.0 >From 4f447d86644a679223ff9fbb819a05f173198681 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 11 Sep 2025 22:14:43 +0200 Subject: [PATCH 07/21] avcodec/mjpegdec: improve ff_mjpeg_find_marker() for non-SOS and for THP There is no need to unescape the buffer for non-SOS fields and for THP. --- libavcodec/mjpegdec.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 8f36c9465e..01f099026f 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2241,12 +2241,19 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, int start_code; start_code = find_marker(buf_ptr, buf_end); + if (start_code != SOS || + s->avctx->codec_id == AV_CODEC_ID_THP) { + *unescaped_buf_ptr = *buf_ptr; + *unescaped_buf_size = buf_end - *buf_ptr; + return start_code; + } + av_fast_padded_malloc(&s->buffer, &s->buffer_size, buf_end - *buf_ptr); if (!s->buffer) return AVERROR(ENOMEM); /* unescape buffer of SOS, use special treatment for JPEG-LS */ - if (start_code == SOS && !s->ls) { + if (!s->ls) { const uint8_t *src = *buf_ptr; const uint8_t *ptr = src; uint8_t *dst = s->buffer; @@ -2260,10 +2267,6 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, } \ } while (0) - if (s->avctx->codec_id == AV_CODEC_ID_THP) { - ptr = buf_end; - copy_data_segment(0); - } else { while (ptr < buf_end) { uint8_t x = *(ptr++); @@ -2293,7 +2296,6 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, } if (src < ptr) copy_data_segment(0); - } #undef copy_data_segment *unescaped_buf_ptr = s->buffer; @@ -2303,7 +2305,7 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %td bytes\n", (buf_end - *buf_ptr) - (dst - s->buffer)); - } else if (start_code == SOS && s->ls) { + } else { const uint8_t *src = *buf_ptr; uint8_t *dst = s->buffer; int bit_count = 0; @@ -2345,9 +2347,6 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, *unescaped_buf_size = (bit_count + 7) >> 3; memset(s->buffer + *unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - } else { - *unescaped_buf_ptr = *buf_ptr; - *unescaped_buf_size = buf_end - *buf_ptr; } return start_code; -- 2.52.0 >From f339f32f45cb30545a700999c9aeb21f686e85fa Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 23 Sep 2025 17:40:10 +0200 Subject: [PATCH 08/21] avcodec/mjpegdec: split mjpeg_unescape_sos() out of ff_mjpeg_find_marker() Now only the image data is unescaped (and not the SOS header). As a side-effect, this also provides a proper fix for decoding CJPG files (issue #133). --- libavcodec/mjpegdec.c | 62 +++++++++++++++++++++++-------------------- libavcodec/mjpegdec.h | 4 +-- libavcodec/mxpegdec.c | 13 ++++----- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 01f099026f..c072802fab 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -55,6 +55,11 @@ #include "put_bits.h" +static int mjpeg_unescape_sos(MJpegDecodeContext *s, + const uint8_t *buf_ptr, const uint8_t *buf_end, + const uint8_t **unescaped_buf_ptr, + int *unescaped_buf_size); + static int init_default_huffman_tables(MJpegDecodeContext *s) { static const struct { @@ -1762,12 +1767,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, predictor = bytestream2_get_byteu(&s->gB); /* JPEG Ss / lossless JPEG predictor / JPEG-LS NEAR */ ilv = bytestream2_get_byteu(&s->gB); /* JPEG Se / JPEG-LS ILV */ - if(s->avctx->codec_tag != AV_RL32("CJPG")){ uint8_t b = bytestream2_get_byteu(&s->gB); prev_shift = b >> 4; /* Ah */ point_transform = b & 0x0F; /* Al */ - }else - prev_shift = point_transform = 0; if (nb_components > 1) { /* interleaved stream */ @@ -1794,7 +1796,14 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, if (s->mjpb_skiptosod) bytestream2_skip(&s->gB, s->mjpb_skiptosod); - ret = init_get_bits8(&s->gb, s->gB.buffer, bytestream2_get_bytes_left(&s->gB)); + const uint8_t *unescaped_buf_ptr; + int unescaped_buf_size; + ret = mjpeg_unescape_sos(s, s->gB.buffer, s->gB.buffer + bytestream2_get_bytes_left(&s->gB), + &unescaped_buf_ptr, &unescaped_buf_size); + if (ret < 0) + return ret; + + ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); if (ret < 0) return ret; @@ -2207,7 +2216,7 @@ static int mjpeg_decode_com(MJpegDecodeContext *s) /* return the 8 bit start code value and update the search state. Return -1 if no start code found */ -static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) +int ff_mjpeg_find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) { const uint8_t *buf_ptr; int val; @@ -2233,28 +2242,26 @@ found: return val; } -int ff_mjpeg_find_marker(MJpegDecodeContext *s, - const uint8_t **buf_ptr, const uint8_t *buf_end, - const uint8_t **unescaped_buf_ptr, - int *unescaped_buf_size) +static int mjpeg_unescape_sos(MJpegDecodeContext *s, + const uint8_t *buf_ptr, const uint8_t *buf_end, + const uint8_t **unescaped_buf_ptr, + int *unescaped_buf_size) { - int start_code; - start_code = find_marker(buf_ptr, buf_end); - - if (start_code != SOS || + if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || + s->avctx->codec_id == AV_CODEC_ID_MJPEGB || s->avctx->codec_id == AV_CODEC_ID_THP) { - *unescaped_buf_ptr = *buf_ptr; - *unescaped_buf_size = buf_end - *buf_ptr; - return start_code; + *unescaped_buf_ptr = buf_ptr; + *unescaped_buf_size = buf_end - buf_ptr; + return 0; } - av_fast_padded_malloc(&s->buffer, &s->buffer_size, buf_end - *buf_ptr); + av_fast_padded_malloc(&s->buffer, &s->buffer_size, buf_end - buf_ptr); if (!s->buffer) return AVERROR(ENOMEM); /* unescape buffer of SOS, use special treatment for JPEG-LS */ if (!s->ls) { - const uint8_t *src = *buf_ptr; + const uint8_t *src = buf_ptr; const uint8_t *ptr = src; uint8_t *dst = s->buffer; @@ -2304,9 +2311,9 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, AV_INPUT_BUFFER_PADDING_SIZE); av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %td bytes\n", - (buf_end - *buf_ptr) - (dst - s->buffer)); + (buf_end - buf_ptr) - (dst - s->buffer)); } else { - const uint8_t *src = *buf_ptr; + const uint8_t *src = buf_ptr; uint8_t *dst = s->buffer; int bit_count = 0; int t = 0, b = 0; @@ -2349,7 +2356,7 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, AV_INPUT_BUFFER_PADDING_SIZE); } - return start_code; + return 0; } static void reset_icc_profile(MJpegDecodeContext *s) @@ -2372,9 +2379,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, { MJpegDecodeContext *s = avctx->priv_data; const uint8_t *buf_end, *buf_ptr; - const uint8_t *unescaped_buf_ptr; int hshift, vshift; - int unescaped_buf_size; int start_code; int index; int ret = 0; @@ -2396,23 +2401,22 @@ redo_for_pal8: buf_end = buf + buf_size; while (buf_ptr < buf_end) { /* find start next marker */ - start_code = ff_mjpeg_find_marker(s, &buf_ptr, buf_end, - &unescaped_buf_ptr, - &unescaped_buf_size); + start_code = ff_mjpeg_find_marker(&buf_ptr, buf_end); /* EOF */ if (start_code < 0) break; - if (unescaped_buf_size > INT_MAX / 8) { + int bytes_left = buf_end - buf_ptr; + if (bytes_left > INT_MAX / 8) { av_log(avctx, AV_LOG_ERROR, "MJPEG packet 0x%x too big (%d/%d), corrupt data?\n", - start_code, unescaped_buf_size, buf_size); + start_code, bytes_left, buf_size); return AVERROR_INVALIDDATA; } av_log(avctx, AV_LOG_DEBUG, "marker=%x avail_size_in_buf=%td\n", start_code, buf_end - buf_ptr); - bytestream2_init(&s->gB, unescaped_buf_ptr, unescaped_buf_size); + bytestream2_init(&s->gB, buf_ptr, bytes_left); if (avctx->debug & FF_DEBUG_STARTCODE) av_log(avctx, AV_LOG_DEBUG, "startcode: %X\n", start_code); diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index 0feb398a22..c3acd126d3 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -184,8 +184,6 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s); int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask,int mb_bitmask_size, const AVFrame *reference); -int ff_mjpeg_find_marker(MJpegDecodeContext *s, - const uint8_t **buf_ptr, const uint8_t *buf_end, - const uint8_t **unescaped_buf_ptr, int *unescaped_buf_size); +int ff_mjpeg_find_marker(const uint8_t **buf_ptr, const uint8_t *buf_end); #endif /* AVCODEC_MJPEGDEC_H */ diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index b66bff56c9..de374a8904 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -198,8 +198,6 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, MXpegDecodeContext *s = avctx->priv_data; MJpegDecodeContext *jpg = &s->jpg; const uint8_t *buf_end, *buf_ptr; - const uint8_t *unescaped_buf_ptr; - int unescaped_buf_size; int start_code; int ret; @@ -212,15 +210,15 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, s->got_mxm_bitmask = 0; s->got_sof_data = !!s->got_sof_data; while (buf_ptr < buf_end) { - start_code = ff_mjpeg_find_marker(jpg, &buf_ptr, buf_end, - &unescaped_buf_ptr, &unescaped_buf_size); + start_code = ff_mjpeg_find_marker(&buf_ptr, buf_end); if (start_code < 0) goto the_end; - bytestream2_init(&jpg->gB, unescaped_buf_ptr, unescaped_buf_size); + int bytes_left = buf_end - buf_ptr; + bytestream2_init(&jpg->gB, buf_ptr, bytes_left); if (start_code >= APP0 && start_code <= APP15) { - mxpeg_decode_app(s, unescaped_buf_ptr, unescaped_buf_size); + mxpeg_decode_app(s, buf_ptr, bytes_left); } switch (start_code) { @@ -247,8 +245,7 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } break; case COM: - ret = mxpeg_decode_com(s, unescaped_buf_ptr, - unescaped_buf_size); + ret = mxpeg_decode_com(s, buf_ptr, bytes_left); if (ret < 0) return ret; break; -- 2.52.0 >From 09da5e3529dcf6ce7940ea85a6f7ee70256a13ee Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 24 Sep 2025 00:17:02 +0200 Subject: [PATCH 09/21] avcodec/mjpegdec: simplify away mjpeg_unescape_sos()'s parameters The input is always obtained from s->gB and the output is always used to initialize s->gb, so we can move that inside the function itself. --- libavcodec/mjpegdec.c | 45 ++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index c072802fab..f8e81ebac1 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -55,10 +55,7 @@ #include "put_bits.h" -static int mjpeg_unescape_sos(MJpegDecodeContext *s, - const uint8_t *buf_ptr, const uint8_t *buf_end, - const uint8_t **unescaped_buf_ptr, - int *unescaped_buf_size); +static int mjpeg_unescape_sos(MJpegDecodeContext *s); static int init_default_huffman_tables(MJpegDecodeContext *s) { @@ -1796,14 +1793,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, if (s->mjpb_skiptosod) bytestream2_skip(&s->gB, s->mjpb_skiptosod); - const uint8_t *unescaped_buf_ptr; - int unescaped_buf_size; - ret = mjpeg_unescape_sos(s, s->gB.buffer, s->gB.buffer + bytestream2_get_bytes_left(&s->gB), - &unescaped_buf_ptr, &unescaped_buf_size); - if (ret < 0) - return ret; - - ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); + ret = mjpeg_unescape_sos(s); if (ret < 0) return ret; @@ -2242,17 +2232,19 @@ found: return val; } -static int mjpeg_unescape_sos(MJpegDecodeContext *s, - const uint8_t *buf_ptr, const uint8_t *buf_end, - const uint8_t **unescaped_buf_ptr, - int *unescaped_buf_size) +static int mjpeg_unescape_sos(MJpegDecodeContext *s) { + const uint8_t *buf_ptr = s->gB.buffer; + const uint8_t *buf_end = buf_ptr + bytestream2_get_bytes_left(&s->gB); + const uint8_t *unescaped_buf_ptr; + int unescaped_buf_size; + if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || s->avctx->codec_id == AV_CODEC_ID_MJPEGB || s->avctx->codec_id == AV_CODEC_ID_THP) { - *unescaped_buf_ptr = buf_ptr; - *unescaped_buf_size = buf_end - buf_ptr; - return 0; + unescaped_buf_ptr = buf_ptr; + unescaped_buf_size = buf_end - buf_ptr; + goto the_end; } av_fast_padded_malloc(&s->buffer, &s->buffer_size, buf_end - buf_ptr); @@ -2305,9 +2297,9 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s, copy_data_segment(0); #undef copy_data_segment - *unescaped_buf_ptr = s->buffer; - *unescaped_buf_size = dst - s->buffer; - memset(s->buffer + *unescaped_buf_size, 0, + unescaped_buf_ptr = s->buffer; + unescaped_buf_size = dst - s->buffer; + memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %td bytes\n", @@ -2350,13 +2342,14 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s, } flush_put_bits(&pb); - *unescaped_buf_ptr = dst; - *unescaped_buf_size = (bit_count + 7) >> 3; - memset(s->buffer + *unescaped_buf_size, 0, + unescaped_buf_ptr = dst; + unescaped_buf_size = (bit_count + 7) >> 3; + memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } - return 0; +the_end: + return init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); } static void reset_icc_profile(MJpegDecodeContext *s) -- 2.52.0 >From f5aeca6c37c961969eca084b11ecc701459caf2e Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Fri, 19 Sep 2025 16:53:49 +0200 Subject: [PATCH 10/21] avcodec/mjpegdec: improve unescaping of SOS fields For non-jpegls: Changes the behaviour to be more in line with IJG's reference implementation: - optional 0xFF fill bytes in a stuffed zero byte sequence (which is an invalid pattern according to the standard) are now discarded: "FF (FF)? 00" => "FF" instead of "FF 00" - sequences with optional 0xFF fill bytes and a marker are no longer copied: "FF (FF)? XX" => "" instead of "FF XX" - a trailing 0xFF byte is no longer issued when a valid "0xFF 0xXX" marker is found: "FF XX" => "" instead of "FF" For jpegls: Changes the behaviour to be more in line with IJG's (non-jpegls) reference implementation, similar to the changes above: - optional 0xFF fill bytes in a stuffed zero bit sequence (which is an invalid pattern according to the standard) are now discarded: "FF (FF)? 0b0xxxxxxx" => "FF 0bxxxxxxx" instead of "FF 7F XX" - sequences with optional 0xFF fill bytes and a marker are no longer copied: "FF (FF)? 0b1xxxxxxx" => "" instead of "FF 7F" Unescaping for jpegls is now done in one pass instead of two. The first pass used to detect the length of the buffer, while the second pass would copy up to the detected length. Note that jpegls restart markers are still not supported. There is also a speed up with the new implementations, mostly due to the usage of memchr() as suggested by Andreas Rheinhardt <[email protected]> --- libavcodec/mjpegdec.c | 125 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index f8e81ebac1..3e63949587 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2256,94 +2256,91 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) const uint8_t *src = buf_ptr; const uint8_t *ptr = src; uint8_t *dst = s->buffer; + PutByteContext pb; - #define copy_data_segment(skip) do { \ - ptrdiff_t length = (ptr - src) - (skip); \ - if (length > 0) { \ - memcpy(dst, src, length); \ - dst += length; \ - src = ptr; \ - } \ - } while (0) + bytestream2_init_writer(&pb, dst, buf_end - src); - while (ptr < buf_end) { - uint8_t x = *(ptr++); + while ((ptr = memchr(ptr, 0xff, buf_end - ptr))) { + ptr++; + if (ptr < buf_end) { + /* Copy verbatim data. */ + int length = (ptr - 1) - src; + if (length > 0) + bytestream2_put_bufferu(&pb, src, length); - if (x == 0xff) { - ptrdiff_t skip = 0; - while (ptr < buf_end && x == 0xff) { - x = *(ptr++); - skip++; - } + uint8_t x = *ptr++; + /* Discard multiple optional 0xFF fill bytes. */ + while (x == 0xff && ptr < buf_end) + x = *ptr++; - /* 0xFF, 0xFF, ... */ - if (skip > 1) { - copy_data_segment(skip); - - /* decrement src as it is equal to ptr after the - * copy_data_segment macro and we might want to - * copy the current value of x later on */ - src--; - } - - if (x < RST0 || x > RST7) { - copy_data_segment(1); - if (x) - break; - } + src = ptr; + if (x == 0) { + /* Stuffed zero byte */ + bytestream2_put_byteu(&pb, 0xff); + } else if (x >= RST0 && x <= RST7) { + /* Restart marker */ + bytestream2_put_be16u(&pb, 0xff00 | x); + } else { + /* Non-restart marker */ + goto found; } } - if (src < ptr) - copy_data_segment(0); - #undef copy_data_segment + } + /* Copy remaining verbatim data. */ + ptr = buf_end; + int length = ptr - src; + if (length > 0) + bytestream2_put_bufferu(&pb, src, length); +found: unescaped_buf_ptr = s->buffer; - unescaped_buf_size = dst - s->buffer; + unescaped_buf_size = bytestream2_tell_p(&pb); memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %td bytes\n", - (buf_end - buf_ptr) - (dst - s->buffer)); + (buf_end - buf_ptr) - (unescaped_buf_size)); } else { const uint8_t *src = buf_ptr; + const uint8_t *ptr = src; uint8_t *dst = s->buffer; - int bit_count = 0; - int t = 0, b = 0; PutBitContext pb; - /* find marker */ - while (src + t < buf_end) { - uint8_t x = src[t++]; - if (x == 0xff) { - while ((src + t < buf_end) && x == 0xff) - x = src[t++]; - if (x & 0x80) { - t -= FFMIN(2, t); - break; - } - } - } - bit_count = t * 8; - init_put_bits(&pb, dst, t); + init_put_bits(&pb, dst, buf_end - src); - /* unescape bitstream */ - while (b < t) { - uint8_t x = src[b++]; - put_bits(&pb, 8, x); - if (x == 0xFF && b < t) { - x = src[b++]; - if (x & 0x80) { - av_log(s->avctx, AV_LOG_WARNING, "Invalid escape sequence\n"); - x &= 0x7f; + while ((ptr = memchr(ptr, 0xff, buf_end - ptr))) { + ptr++; + if (ptr < buf_end) { + /* Copy verbatim data. */ + int length = (ptr - 1) - src; + if (length > 0) + ff_copy_bits(&pb, src, length * 8); + + uint8_t x = *ptr++; + /* Discard multiple optional 0xFF fill bytes. */ + while (x == 0xff && ptr < buf_end) + x = *ptr++; + + src = ptr; + if (!(x & 0x80)) { + /* Stuffed zero bit */ + put_bits(&pb, 15, 0x7f80 | x); + } else { + goto found_ls; } - put_bits(&pb, 7, x); - bit_count--; } } + /* Copy remaining verbatim data. */ + ptr = buf_end; + int length = ptr - src; + if (length > 0) + ff_copy_bits(&pb, src, length * 8); + +found_ls: flush_put_bits(&pb); unescaped_buf_ptr = dst; - unescaped_buf_size = (bit_count + 7) >> 3; + unescaped_buf_size = put_bytes_output(&pb); memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } -- 2.52.0 >From 96d78f721d05882b2fe49cc091620981d3c2314b Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 24 Sep 2025 20:40:53 +0200 Subject: [PATCH 11/21] avcodec/mjpegdec: find correct sizes for SOS fields For hwaccel, find_marker() was being used to skip over the image data, which could include multiple restart markers. For MJPEG-B and THP, the field size was already correct since the image data was already unescaped. For the rest (mjpeg and jpegls), the buffer was being incremented by the unescaped_buf_size, which could be smaller than the actual buffer size. Now the buffer is correctly incremented in all cases. --- libavcodec/mjpegdec.c | 49 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 3e63949587..ecf8fb0862 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1798,13 +1798,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, return ret; if (s->avctx->hwaccel) { - int bytes_to_start = bytestream2_tell(&s->gB); - av_assert0(bytes_to_start >= 0 && - s->raw_scan_buffer_size >= bytes_to_start); - ret = FF_HW_CALL(s->avctx, decode_slice, - s->raw_scan_buffer + bytes_to_start, - s->raw_scan_buffer_size - bytes_to_start); + s->raw_scan_buffer, + s->raw_scan_buffer_size); if (ret < 0) return ret; @@ -1840,9 +1836,13 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, } } + if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || + s->avctx->codec_id == AV_CODEC_ID_MJPEGB || + s->avctx->codec_id == AV_CODEC_ID_THP) { /* Add the amount of bits read from the unescaped image data buffer * into the GetByteContext. */ bytestream2_skipu(&s->gB, (get_bits_count(&s->gb) + 7) / 8); + } return 0; out_of_range: @@ -2239,9 +2239,37 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) const uint8_t *unescaped_buf_ptr; int unescaped_buf_size; + if (s->avctx->hwaccel) { + /* Find size of image data buffer (including restart markers). + * No unescaping is performed. */ + const uint8_t *ptr = buf_ptr; + while ((ptr = memchr(ptr, 0xff, buf_end - ptr))) { + ptr++; + if (ptr < buf_end) { + uint8_t x = *ptr++; + /* Discard multiple optional 0xFF fill bytes. */ + while (x == 0xff && ptr < buf_end) + x = *ptr++; + if (x && (x < RST0 || x > RST7)) { + /* Non-restart marker */ + ptr -= 2; + goto found_hw; + } + } + } + ptr = buf_end; +found_hw: + s->raw_scan_buffer = buf_ptr; + s->raw_scan_buffer_size = ptr - buf_ptr; + bytestream2_skipu(&s->gB, s->raw_scan_buffer_size); + return 0; + } + if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || s->avctx->codec_id == AV_CODEC_ID_MJPEGB || s->avctx->codec_id == AV_CODEC_ID_THP) { + /* The image data buffer is already unescaped. The only way to + * find the size of the buffer is by fully decoding it. */ unescaped_buf_ptr = buf_ptr; unescaped_buf_size = buf_end - buf_ptr; goto the_end; @@ -2282,6 +2310,7 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) bytestream2_put_be16u(&pb, 0xff00 | x); } else { /* Non-restart marker */ + ptr -= 2; goto found; } } @@ -2298,6 +2327,8 @@ found: memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + bytestream2_skipu(&s->gB, ptr - buf_ptr); + av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %td bytes\n", (buf_end - buf_ptr) - (unescaped_buf_size)); } else { @@ -2326,6 +2357,7 @@ found: /* Stuffed zero bit */ put_bits(&pb, 15, 0x7f80 | x); } else { + ptr -= 2; goto found_ls; } } @@ -2343,6 +2375,8 @@ found_ls: unescaped_buf_size = put_bytes_output(&pb); memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + bytestream2_skipu(&s->gB, ptr - buf_ptr); } the_end: @@ -2560,9 +2594,6 @@ eoi_parser: goto the_end; case SOS: - s->raw_scan_buffer = buf_ptr; - s->raw_scan_buffer_size = buf_end - buf_ptr; - s->cur_scan++; if ((ret = ff_mjpeg_decode_sos(s, NULL, 0, NULL)) < 0 && -- 2.52.0 >From 749443e962bf1205cd39fc309d9101c35d36805b Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 25 Sep 2025 17:49:36 +0200 Subject: [PATCH 12/21] avcodec/mjpegdec: move SOS header fields to MJpegDecodeContext Use naming for SOS header fields from ISO/IEC 10918-1's non-lossless mode of operation in ff_mjpeg_decode_sos() instead of mixing JPEG-LS and lossless names. Each decode function still keeps its correct name for each field. --- libavcodec/jpeglsdec.c | 6 ++- libavcodec/jpeglsdec.h | 3 +- libavcodec/mjpegdec.c | 113 +++++++++++++++++++++-------------------- libavcodec/mjpegdec.h | 7 +++ 4 files changed, 70 insertions(+), 59 deletions(-) diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index 3c899568ec..d481221f2b 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -352,9 +352,11 @@ static inline int ls_decode_line(JLSState *state, MJpegDecodeContext *s, return 0; } -int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, - int point_transform, int ilv) +int ff_jpegls_decode_picture(MJpegDecodeContext *s) { + int near = s->Ss; + int point_transform = s->Al; + int ilv = s->Se; int i, t = 0; uint8_t *zero, *last, *cur; JLSState *state = s->jls_state; diff --git a/libavcodec/jpeglsdec.h b/libavcodec/jpeglsdec.h index 0cafaba7a4..0b5ee2d488 100644 --- a/libavcodec/jpeglsdec.h +++ b/libavcodec/jpeglsdec.h @@ -36,7 +36,6 @@ */ int ff_jpegls_decode_lse(MJpegDecodeContext *s); -int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, - int point_transform, int ilv); +int ff_jpegls_decode_picture(MJpegDecodeContext *s); #endif /* AVCODEC_JPEGLSDEC_H */ diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index ecf8fb0862..8f98505cfb 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -914,7 +914,7 @@ static int decode_dc_progressive(MJpegDecodeContext *s, int16_t *block, static int decode_block_progressive(MJpegDecodeContext *s, int16_t *block, uint8_t *last_nnz, int ac_index, uint16_t *quant_matrix, - int ss, int se, int Al, int *EOBRUN) + int Ss, int Se, int Al, int *EOBRUN) { int code, i, j, val, run; unsigned level; @@ -926,7 +926,7 @@ static int decode_block_progressive(MJpegDecodeContext *s, int16_t *block, { OPEN_READER(re, &s->gb); - for (i = ss; ; i++) { + for (i = Ss; ; i++) { UPDATE_CACHE(re, &s->gb); GET_VLC(code, re, &s->gb, s->vlcs[2][ac_index].table, 9, 2); @@ -943,10 +943,10 @@ static int decode_block_progressive(MJpegDecodeContext *s, int16_t *block, LAST_SKIP_BITS(re, &s->gb, code); - if (i >= se) { - if (i == se) { - j = s->permutated_scantable[se]; - block[j] = level * (quant_matrix[se] << Al); + if (i >= Se) { + if (i == Se) { + j = s->permutated_scantable[Se]; + block[j] = level * (quant_matrix[Se] << Al); break; } av_log(s->avctx, AV_LOG_ERROR, "error count: %d\n", i); @@ -957,7 +957,7 @@ static int decode_block_progressive(MJpegDecodeContext *s, int16_t *block, } else { if (run == 0xF) {// ZRL - skip 15 coefficients i += 15; - if (i >= se) { + if (i >= Se) { av_log(s->avctx, AV_LOG_ERROR, "ZRL overflow: %d\n", i); return AVERROR_INVALIDDATA; } @@ -995,7 +995,7 @@ static int decode_block_progressive(MJpegDecodeContext *s, int16_t *block, for (; ; i++) { \ if (i > last) { \ i += run; \ - if (i > se) { \ + if (i > Se) { \ av_log(s->avctx, AV_LOG_ERROR, "error count: %d\n", i); \ return -1; \ } \ @@ -1012,10 +1012,10 @@ for (; ; i++) { \ static int decode_block_refinement(MJpegDecodeContext *s, int16_t *block, uint8_t *last_nnz, int ac_index, uint16_t *quant_matrix, - int ss, int se, int Al, int *EOBRUN) + int Ss, int Se, int Al, int *EOBRUN) { - int code, i = ss, j, sign, val, run; - int last = FFMIN(se, *last_nnz); + int code, i = Ss, j, sign, val, run; + int last = FFMIN(Se, *last_nnz); OPEN_READER(re, &s->gb); if (*EOBRUN) { @@ -1033,7 +1033,7 @@ static int decode_block_refinement(MJpegDecodeContext *s, int16_t *block, j = s->permutated_scantable[i]; val--; block[j] = ((quant_matrix[i] << Al) ^ val) - val; - if (i == se) { + if (i == Se) { if (i > *last_nnz) *last_nnz = i; CLOSE_READER(re, &s->gb); @@ -1109,8 +1109,11 @@ static int handle_rstn(MJpegDecodeContext *s, int nb_components) } /* Handles 1 to 4 components */ -static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int predictor, int point_transform) +static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) { + int nb_components = s->nb_components_sos; + int predictor = s->Ss; + int point_transform = s->Al; int i, mb_x, mb_y; unsigned width; uint16_t (*buffer)[4]; @@ -1282,9 +1285,11 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int p return 0; } -static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s, int predictor, - int point_transform, int nb_components) +static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) { + int predictor = s->Ss; + int point_transform = s->Al; + int nb_components = s->nb_components_sos; int i, mb_x, mb_y, mask; int bits= (s->bits+7)&~7; int resync_mb_y = 0; @@ -1470,11 +1475,14 @@ static void shift_output(MJpegDecodeContext *s, uint8_t *ptr, int linesize) } } -static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, - int Al, const uint8_t *mb_bitmask, +static int mjpeg_decode_scan(MJpegDecodeContext *s, + const uint8_t *mb_bitmask, int mb_bitmask_size, const AVFrame *reference) { + int nb_components = s->nb_components_sos; + int Ah = s->Ah; + int Al = s->Al; int i, mb_x, mb_y, chroma_h_shift, chroma_v_shift, chroma_width, chroma_height; uint8_t *data[MAX_COMPONENTS]; const uint8_t *reference_data[MAX_COMPONENTS]; @@ -1611,23 +1619,26 @@ next_field: return 0; } -static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s, int ss, - int se, int Ah, int Al) +static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s) { + int Ss = s->Ss; + int Se = s->Se; + int Ah = s->Ah; + int Al = s->Al; int mb_x, mb_y; int EOBRUN = 0; int c = s->comp_index[0]; uint16_t *quant_matrix = s->quant_matrixes[s->quant_sindex[0]]; - av_assert0(ss>=0 && Ah>=0 && Al>=0); - if (se < ss || se > 63) { - av_log(s->avctx, AV_LOG_ERROR, "SS/SE %d/%d is invalid\n", ss, se); + av_assert0(Ss>=0 && Ah>=0 && Al>=0); + if (Se < Ss || Se > 63) { + av_log(s->avctx, AV_LOG_ERROR, "SS/SE %d/%d is invalid\n", Ss, Se); return AVERROR_INVALIDDATA; } // s->coefs_finished is a bitmask for coefficients coded - // ss and se are parameters telling start and end coefficients - s->coefs_finished[c] |= (2ULL << se) - (1ULL << ss); + // Ss and Se are parameters telling start and end coefficients + s->coefs_finished[c] |= (2ULL << Se) - (1ULL << Ss); s->restart_count = 0; @@ -1642,10 +1653,10 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s, int ss, if (Ah) ret = decode_block_refinement(s, *block, last_nnz, s->ac_index[0], - quant_matrix, ss, se, Al, &EOBRUN); + quant_matrix, Ss, Se, Al, &EOBRUN); else ret = decode_block_progressive(s, *block, last_nnz, s->ac_index[0], - quant_matrix, ss, se, Al, &EOBRUN); + quant_matrix, Ss, Se, Al, &EOBRUN); if (ret >= 0 && get_bits_left(&s->gb) < 0) ret = AVERROR_INVALIDDATA; @@ -1700,10 +1711,9 @@ static void mjpeg_idct_scan_progressive_ac(MJpegDecodeContext *s) int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, int mb_bitmask_size, const AVFrame *reference) { - int len, nb_components, i, h, v, predictor, point_transform; + int len, i, h, v; int index, id, ret; const int block_size = s->lossless ? 1 : 8; - int ilv, prev_shift; if (!s->got_picture) { av_log(s->avctx, AV_LOG_WARNING, @@ -1716,18 +1726,18 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, return ret; if (len < 1) return AVERROR_INVALIDDATA; - nb_components = bytestream2_get_byteu(&s->gB); - if (nb_components == 0 || nb_components > MAX_COMPONENTS) { + s->nb_components_sos = bytestream2_get_byteu(&s->gB); + if (s->nb_components_sos == 0 || s->nb_components_sos > MAX_COMPONENTS) { avpriv_report_missing_feature(s->avctx, "decode_sos: nb_components (%d)", - nb_components); + s->nb_components_sos); return AVERROR_PATCHWELCOME; } - if (len != 4 + 2 * nb_components) { - av_log(s->avctx, AV_LOG_ERROR, "decode_sos: len(%d) mismatch %d components\n", len, nb_components); + if (len != 4 + 2 * s->nb_components_sos) { + av_log(s->avctx, AV_LOG_ERROR, "decode_sos: len(%d) mismatch %d components\n", len, s->nb_components_sos); return AVERROR_INVALIDDATA; } - for (i = 0; i < nb_components; i++) { + for (i = 0; i < s->nb_components_sos; i++) { id = bytestream2_get_byteu(&s->gB); av_log(s->avctx, AV_LOG_DEBUG, "component: %d\n", id); /* find component index */ @@ -1741,7 +1751,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, } /* Metasoft MJPEG codec has Cb and Cr swapped */ if (s->avctx->codec_tag == MKTAG('M', 'T', 'S', 'J') - && nb_components == 3 && s->nb_components == 3 && i) + && s->nb_components_sos == 3 && s->nb_components == 3 && i) index = 3 - i; s->quant_sindex[i] = s->quant_index[index]; @@ -1762,13 +1772,13 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, goto out_of_range; } - predictor = bytestream2_get_byteu(&s->gB); /* JPEG Ss / lossless JPEG predictor / JPEG-LS NEAR */ - ilv = bytestream2_get_byteu(&s->gB); /* JPEG Se / JPEG-LS ILV */ + s->Ss = bytestream2_get_byteu(&s->gB); /* JPEG Ss / lossless JPEG predictor / JPEG-LS NEAR */ + s->Se = bytestream2_get_byteu(&s->gB); /* JPEG Se / JPEG-LS ILV */ uint8_t b = bytestream2_get_byteu(&s->gB); - prev_shift = b >> 4; /* Ah */ - point_transform = b & 0x0F; /* Al */ + s->Ah = b >> 4; /* Ah */ + s->Al = b & 0x0F; /* Al */ - if (nb_components > 1) { + if (s->nb_components_sos > 1) { /* interleaved stream */ s->mb_width = (s->width + s->h_max * block_size - 1) / (s->h_max * block_size); s->mb_height = (s->height + s->v_max * block_size - 1) / (s->v_max * block_size); @@ -1785,8 +1795,8 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_DEBUG, "%s %s p:%d >>:%d ilv:%d bits:%d skip:%d %s comp:%d\n", s->lossless ? "lossless" : "sequential DCT", s->rgb ? "RGB" : "", - predictor, point_transform, ilv, s->bits, s->mjpb_skiptosod, - s->pegasus_rct ? "PRCT" : (s->rct ? "RCT" : ""), nb_components); + s->Ss, s->Al, s->Se, s->bits, s->mjpb_skiptosod, + s->pegasus_rct ? "PRCT" : (s->rct ? "RCT" : ""), s->nb_components_sos); /* mjpeg-b can have padding bytes between sos and image data, skip them */ @@ -1807,31 +1817,24 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, } else if (s->lossless) { av_assert0(s->picture_ptr == s->picture); if (CONFIG_JPEGLS_DECODER && s->ls) { - if ((ret = ff_jpegls_decode_picture(s, predictor, - point_transform, ilv)) < 0) + if ((ret = ff_jpegls_decode_picture(s)) < 0) return ret; } else { if (s->rgb || s->bayer) { - if ((ret = ljpeg_decode_rgb_scan(s, nb_components, predictor, point_transform)) < 0) + if ((ret = ljpeg_decode_rgb_scan(s)) < 0) return ret; } else { - if ((ret = ljpeg_decode_yuv_scan(s, predictor, - point_transform, - nb_components)) < 0) + if ((ret = ljpeg_decode_yuv_scan(s)) < 0) return ret; } } } else { - if (s->progressive && predictor) { + if (s->progressive && s->Ss) { av_assert0(s->picture_ptr == s->picture); - if ((ret = mjpeg_decode_scan_progressive_ac(s, predictor, - ilv, prev_shift, - point_transform)) < 0) + if ((ret = mjpeg_decode_scan_progressive_ac(s)) < 0) return ret; } else { - if ((ret = mjpeg_decode_scan(s, nb_components, - prev_shift, point_transform, - mb_bitmask, mb_bitmask_size, reference)) < 0) + if ((ret = mjpeg_decode_scan(s, mb_bitmask, mb_bitmask_size, reference)) < 0) return ret; } } diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index c3acd126d3..afe2263c2f 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -85,6 +85,13 @@ typedef struct MJpegDecodeContext { int xfrm; int adobe_transform; + /* SOS fields */ + int nb_components_sos; + int Ss; + int Se; + int Ah; + int Al; + int maxval; int near; ///< near lossless bound (si 0 for lossless) int t1,t2,t3; -- 2.52.0 >From 2251f69dbed95d76205f8d4638e2bbeb7ec108ff Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 15 Jan 2026 18:15:19 +0100 Subject: [PATCH 13/21] avcodec/mjpegdec: move MxPEG parameters from mjpeg_decode_scan() to MJpegDecodeContext --- libavcodec/mjpegbdec.c | 2 +- libavcodec/mjpegdec.c | 21 ++++++++++++--------- libavcodec/mjpegdec.h | 8 +++++--- libavcodec/mxpegdec.c | 9 +++++++-- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libavcodec/mjpegbdec.c b/libavcodec/mjpegbdec.c index 5a21ea47fc..2c1747f502 100644 --- a/libavcodec/mjpegbdec.c +++ b/libavcodec/mjpegbdec.c @@ -116,7 +116,7 @@ read_header: if (avctx->skip_frame == AVDISCARD_ALL) { bytestream2_skipu(&s->gB, bytestream2_get_bytes_left(&s->gB)); } else { - ret = ff_mjpeg_decode_sos(s, NULL, 0, NULL); + ret = ff_mjpeg_decode_sos(s); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; } diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 8f98505cfb..14bb70061b 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1475,14 +1475,13 @@ static void shift_output(MJpegDecodeContext *s, uint8_t *ptr, int linesize) } } -static int mjpeg_decode_scan(MJpegDecodeContext *s, - const uint8_t *mb_bitmask, - int mb_bitmask_size, - const AVFrame *reference) +static int mjpeg_decode_scan(MJpegDecodeContext *s) { int nb_components = s->nb_components_sos; int Ah = s->Ah; int Al = s->Al; + const uint8_t *mb_bitmask = NULL; + const AVFrame *reference = NULL; int i, mb_x, mb_y, chroma_h_shift, chroma_v_shift, chroma_width, chroma_height; uint8_t *data[MAX_COMPONENTS]; const uint8_t *reference_data[MAX_COMPONENTS]; @@ -1490,8 +1489,13 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, GetBitContext mb_bitmask_gb = {0}; // initialize to silence gcc warning int bytes_per_pixel = 1 + (s->bits > 8); + if (s->avctx->codec_id == AV_CODEC_ID_MXPEG) { + mb_bitmask = s->mb_bitmask; + reference = s->reference; + } + if (mb_bitmask) { - if (mb_bitmask_size != (s->mb_width * s->mb_height + 7)>>3) { + if (s->mb_bitmask_size != (s->mb_width * s->mb_height + 7)>>3) { av_log(s->avctx, AV_LOG_ERROR, "mb_bitmask_size mismatches\n"); return AVERROR_INVALIDDATA; } @@ -1708,8 +1712,7 @@ static void mjpeg_idct_scan_progressive_ac(MJpegDecodeContext *s) } } -int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, - int mb_bitmask_size, const AVFrame *reference) +int ff_mjpeg_decode_sos(MJpegDecodeContext *s) { int len, i, h, v; int index, id, ret; @@ -1834,7 +1837,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, if ((ret = mjpeg_decode_scan_progressive_ac(s)) < 0) return ret; } else { - if ((ret = mjpeg_decode_scan(s, mb_bitmask, mb_bitmask_size, reference)) < 0) + if ((ret = mjpeg_decode_scan(s)) < 0) return ret; } } @@ -2599,7 +2602,7 @@ eoi_parser: case SOS: s->cur_scan++; - if ((ret = ff_mjpeg_decode_sos(s, NULL, 0, NULL)) < 0 && + if ((ret = ff_mjpeg_decode_sos(s)) < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) goto fail; break; diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index afe2263c2f..4ca67d06c6 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -173,6 +173,10 @@ typedef struct MJpegDecodeContext { enum AVPixelFormat hwaccel_pix_fmt; void *hwaccel_picture_private; struct JLSState *jls_state; + + const uint8_t *mb_bitmask; + int mb_bitmask_size; + const AVFrame *reference; } MJpegDecodeContext; int ff_mjpeg_build_vlc(VLC *vlc, const uint8_t *bits_table, @@ -188,9 +192,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, int ff_mjpeg_decode_dqt(MJpegDecodeContext *s); int ff_mjpeg_decode_dht(MJpegDecodeContext *s); int ff_mjpeg_decode_sof(MJpegDecodeContext *s); -int ff_mjpeg_decode_sos(MJpegDecodeContext *s, - const uint8_t *mb_bitmask,int mb_bitmask_size, - const AVFrame *reference); +int ff_mjpeg_decode_sos(MJpegDecodeContext *s); int ff_mjpeg_find_marker(const uint8_t **buf_ptr, const uint8_t *buf_end); #endif /* AVCODEC_MJPEGDEC_H */ diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index de374a8904..2a8e583988 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -311,11 +311,16 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; - ret = ff_mjpeg_decode_sos(jpg, s->mxm_bitmask, s->bitmask_size, reference_ptr); + jpg->mb_bitmask = s->mxm_bitmask; + jpg->mb_bitmask_size = s->bitmask_size; + jpg->reference = reference_ptr; + ret = ff_mjpeg_decode_sos(jpg); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; } else { - ret = ff_mjpeg_decode_sos(jpg, NULL, 0, NULL); + jpg->mb_bitmask = NULL; + jpg->reference = NULL; + ret = ff_mjpeg_decode_sos(jpg); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; } -- 2.52.0 >From e5419169828bc1f9cb36edce4b8906ac2e692e93 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 15 Jan 2026 18:30:09 +0100 Subject: [PATCH 14/21] avcodec/mjpegdec: simplify decode_scan codepaths in ff_mjpeg_decode_sos() This will be helpful for the next commit. --- libavcodec/mjpegdec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 14bb70061b..a1d3958bf4 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1817,7 +1817,8 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) if (ret < 0) return ret; - } else if (s->lossless) { + } else { + if (s->lossless) { av_assert0(s->picture_ptr == s->picture); if (CONFIG_JPEGLS_DECODER && s->ls) { if ((ret = ff_jpegls_decode_picture(s)) < 0) @@ -1841,6 +1842,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) return ret; } } + } if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || s->avctx->codec_id == AV_CODEC_ID_MJPEGB || -- 2.52.0 >From 943d2a4ee76a91e5823b8557c9b3117aca24b3a8 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 15 Jan 2026 18:30:59 +0100 Subject: [PATCH 15/21] avcodec/mjpegdec: split mjpeg_find_raw_scan_data() out of mjpeg_unescape_sos() --- libavcodec/mjpegdec.c | 40 ++++++++++++++++++++++++---------------- libavcodec/mjpegdec.h | 2 -- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index a1d3958bf4..d2c3bb2230 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -55,6 +55,8 @@ #include "put_bits.h" +static void mjpeg_find_raw_scan_data(MJpegDecodeContext *s, + const uint8_t **pbuf_ptr, size_t *pbuf_size); static int mjpeg_unescape_sos(MJpegDecodeContext *s); static int init_default_huffman_tables(MJpegDecodeContext *s) @@ -1806,18 +1808,20 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) if (s->mjpb_skiptosod) bytestream2_skip(&s->gB, s->mjpb_skiptosod); - ret = mjpeg_unescape_sos(s); - if (ret < 0) - return ret; - if (s->avctx->hwaccel) { - ret = FF_HW_CALL(s->avctx, decode_slice, - s->raw_scan_buffer, - s->raw_scan_buffer_size); + const uint8_t *buf_ptr; + size_t buf_size; + + mjpeg_find_raw_scan_data(s, &buf_ptr, &buf_size); + + ret = FF_HW_CALL(s->avctx, decode_slice, buf_ptr, buf_size); if (ret < 0) return ret; } else { + ret = mjpeg_unescape_sos(s); + if (ret < 0) + return ret; if (s->lossless) { av_assert0(s->picture_ptr == s->picture); if (CONFIG_JPEGLS_DECODER && s->ls) { @@ -2240,14 +2244,12 @@ found: return val; } -static int mjpeg_unescape_sos(MJpegDecodeContext *s) +static void mjpeg_find_raw_scan_data(MJpegDecodeContext *s, + const uint8_t **pbuf_ptr, size_t *pbuf_size) { const uint8_t *buf_ptr = s->gB.buffer; const uint8_t *buf_end = buf_ptr + bytestream2_get_bytes_left(&s->gB); - const uint8_t *unescaped_buf_ptr; - int unescaped_buf_size; - if (s->avctx->hwaccel) { /* Find size of image data buffer (including restart markers). * No unescaping is performed. */ const uint8_t *ptr = buf_ptr; @@ -2267,11 +2269,17 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) } ptr = buf_end; found_hw: - s->raw_scan_buffer = buf_ptr; - s->raw_scan_buffer_size = ptr - buf_ptr; - bytestream2_skipu(&s->gB, s->raw_scan_buffer_size); - return 0; - } + *pbuf_ptr = buf_ptr; + *pbuf_size = ptr - buf_ptr; + bytestream2_skipu(&s->gB, *pbuf_size); +} + +static int mjpeg_unescape_sos(MJpegDecodeContext *s) +{ + const uint8_t *buf_ptr = s->gB.buffer; + const uint8_t *buf_end = buf_ptr + bytestream2_get_bytes_left(&s->gB); + const uint8_t *unescaped_buf_ptr; + int unescaped_buf_size; if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || s->avctx->codec_id == AV_CODEC_ID_MJPEGB || diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index 4ca67d06c6..47164d74af 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -163,8 +163,6 @@ typedef struct MJpegDecodeContext { // Raw stream data for hwaccel use. const uint8_t *raw_image_buffer; size_t raw_image_buffer_size; - const uint8_t *raw_scan_buffer; - size_t raw_scan_buffer_size; uint8_t raw_huffman_lengths[2][4][16]; uint8_t raw_huffman_values[2][4][256]; -- 2.52.0 >From 122a1cb54f3a42f580ff560d7fcc2a2948aa93ea Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Mon, 29 Dec 2025 19:50:54 +0100 Subject: [PATCH 16/21] avcodec/jpegls: clear more JLSState fields inside ff_jpegls_init_state() --- libavcodec/jpegls.c | 4 ++++ libavcodec/jpeglsdec.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libavcodec/jpegls.c b/libavcodec/jpegls.c index 7b4bc30e46..ab80763681 100644 --- a/libavcodec/jpegls.c +++ b/libavcodec/jpegls.c @@ -49,6 +49,10 @@ void ff_jpegls_init_state(JLSState *state) state->A[i] = FFMAX(state->range + 32 >> 6, 2); state->N[i] = 1; } + + memset(state->B, 0, sizeof(state->B)); + memset(state->C, 0, sizeof(state->C)); + memset(state->run_index, 0, sizeof(state->run_index)); } /** diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index d481221f2b..8f10f5a985 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -376,7 +376,6 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s) cur = s->picture_ptr->data[0]; /* initialize JPEG-LS state from JPEG parameters */ - memset(state, 0, sizeof(*state)); state->near = near; state->bpp = (s->bits < 2) ? 2 : s->bits; state->maxval = s->maxval; -- 2.52.0 >From 3f013320f7c173a445ac5f52467c65f3182ec926 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 23 Sep 2025 23:28:49 +0200 Subject: [PATCH 17/21] avcodec/mjpegdec: move vpred initialization out of loop in ljpeg_decode_rgb_scan() The initialization code was only being run when mb_y was 0, so it could just as well be moved out of the loop. I haven't been able to find a bayer sample that has restart markers to check whether vpred should be reinitialized at every restart. It would seem logical that it should, but I have left this out until we find a sample that does have restart markers. --- libavcodec/mjpegdec.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index d2c3bb2230..e3907b3554 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1143,8 +1143,8 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) s->restart_count = s->restart_interval; - if (s->restart_interval == 0) - s->restart_interval = INT_MAX; + for (i = 0; i < 6; i++) + vpred[i] = 1 << (s->bits-1); if (s->bayer) width = s->mb_width / nb_components; /* Interleaved, width stored is the total so need to divide */ @@ -1169,11 +1169,6 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) for (i = 0; i < 4; i++) top[i] = left[i] = topleft[i] = buffer[0][i]; - if ((mb_y * s->width) % s->restart_interval == 0) { - for (i = 0; i < 6; i++) - vpred[i] = 1 << (s->bits-1); - } - for (mb_x = 0; mb_x < width; mb_x++) { int modified_predictor = predictor; -- 2.52.0 >From ec5e73468657aba79cc3f6f6d517f9e1249cae4d Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Thu, 9 Oct 2025 16:55:18 +0200 Subject: [PATCH 18/21] avcodec/mjpegdec: move get_bits_left() checks after handling of restart count This commit doesn't really change much on its own, but it's helpful in preparation for the following commit. --- libavcodec/mjpegdec.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index e3907b3554..0abccb20b6 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1172,11 +1172,6 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) for (mb_x = 0; mb_x < width; mb_x++) { int modified_predictor = predictor; - if (get_bits_left(&s->gb) < 1) { - av_log(s->avctx, AV_LOG_ERROR, "bitstream end in rgb_scan\n"); - return AVERROR_INVALIDDATA; - } - if (s->restart_interval && !s->restart_count){ s->restart_count = s->restart_interval; resync_mb_x = mb_x; @@ -1184,6 +1179,12 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) for(i=0; i<4; i++) top[i] = left[i]= topleft[i]= 1 << (s->bits - 1); } + + if (get_bits_left(&s->gb) < 1) { + av_log(s->avctx, AV_LOG_ERROR, "bitstream end in rgb_scan\n"); + return AVERROR_INVALIDDATA; + } + if (mb_y == resync_mb_y || mb_y == resync_mb_y+1 && mb_x < resync_mb_x || !mb_x) modified_predictor = 1; @@ -1300,16 +1301,17 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { - if (get_bits_left(&s->gb) < 1) { - av_log(s->avctx, AV_LOG_ERROR, "bitstream end in yuv_scan\n"); - return AVERROR_INVALIDDATA; - } if (s->restart_interval && !s->restart_count){ s->restart_count = s->restart_interval; resync_mb_x = mb_x; resync_mb_y = mb_y; } + if (get_bits_left(&s->gb) < 1) { + av_log(s->avctx, AV_LOG_ERROR, "bitstream end in yuv_scan\n"); + return AVERROR_INVALIDDATA; + } + if(!mb_x || mb_y == resync_mb_y || mb_y == resync_mb_y+1 && mb_x < resync_mb_x || s->interlaced){ int toprow = mb_y == resync_mb_y || mb_y == resync_mb_y+1 && mb_x < resync_mb_x; int leftcol = !mb_x || mb_y == resync_mb_y && mb_x == resync_mb_x; -- 2.52.0 >From d46517e1b3658ffb1e089d29ddd36d1baab2d3ea Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 8 Oct 2025 19:48:49 +0200 Subject: [PATCH 19/21] avcodec/mjpegdec: unescape data for each restart marker individually Instead of unescaping the entire image data buffer in advance, and then having to perform heuristics to skip over where the restart markers would have been, unescape the image data for each restart marker individually. --- libavcodec/mjpegbdec.c | 1 - libavcodec/mjpegdec.c | 130 ++++++++++++++++------------------------- libavcodec/mjpegdec.h | 30 ++++++++++ 3 files changed, 81 insertions(+), 80 deletions(-) diff --git a/libavcodec/mjpegbdec.c b/libavcodec/mjpegbdec.c index 2c1747f502..e627ab810a 100644 --- a/libavcodec/mjpegbdec.c +++ b/libavcodec/mjpegbdec.c @@ -61,7 +61,6 @@ static int mjpegb_decode_frame(AVCodecContext *avctx, AVFrame *rframe, read_header: /* reset on every SOI */ s->restart_interval = 0; - s->restart_count = 0; s->mjpb_skiptosod = 0; if ((ret = init_get_bits8(&hgb, buf_ptr, /*buf_size*/(buf_end - buf_ptr))) < 0) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 0abccb20b6..40558da136 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -57,7 +57,6 @@ static void mjpeg_find_raw_scan_data(MJpegDecodeContext *s, const uint8_t **pbuf_ptr, size_t *pbuf_size); -static int mjpeg_unescape_sos(MJpegDecodeContext *s); static int init_default_huffman_tables(MJpegDecodeContext *s) { @@ -1076,40 +1075,6 @@ static int decode_block_refinement(MJpegDecodeContext *s, int16_t *block, #undef REFINE_BIT #undef ZERO_RUN -static int handle_rstn(MJpegDecodeContext *s, int nb_components) -{ - int i; - int reset = 0; - - if (s->restart_interval) { - s->restart_count--; - if(s->restart_count == 0 && s->avctx->codec_id == AV_CODEC_ID_THP){ - align_get_bits(&s->gb); - for (i = 0; i < nb_components; i++) /* reset dc */ - s->last_dc[i] = (4 << s->bits); - } - - i = 8 + ((-get_bits_count(&s->gb)) & 7); - /* skip RSTn */ - if (s->restart_count == 0) { - if( show_bits(&s->gb, i) == (1 << i) - 1 - || show_bits(&s->gb, i) == 0xFF) { - int pos = get_bits_count(&s->gb); - align_get_bits(&s->gb); - while (get_bits_left(&s->gb) >= 8 && show_bits(&s->gb, 8) == 0xFF) - skip_bits(&s->gb, 8); - if (get_bits_left(&s->gb) >= 8 && (get_bits(&s->gb, 8) & 0xF8) == 0xD0) { - for (i = 0; i < nb_components; i++) /* reset dc */ - s->last_dc[i] = (4 << s->bits); - reset = 1; - } else - skip_bits_long(&s->gb, pos - get_bits_count(&s->gb)); - } - } - } - return reset; -} - /* Handles 1 to 4 components */ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) { @@ -1141,8 +1106,6 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) } - s->restart_count = s->restart_interval; - for (i = 0; i < 6; i++) vpred[i] = 1 << (s->bits-1); @@ -1160,6 +1123,8 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) for (i = 0; i < 4; i++) buffer[0][i] = 1 << (s->bits - 1); + s->restart_count = -1; + for (mb_y = 0; mb_y < s->mb_height; mb_y++) { uint8_t *ptr = s->picture_ptr->data[0] + (linesize * mb_y); @@ -1171,9 +1136,12 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) for (mb_x = 0; mb_x < width; mb_x++) { int modified_predictor = predictor; + int restart; - if (s->restart_interval && !s->restart_count){ - s->restart_count = s->restart_interval; + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + return ret; + if (restart) { resync_mb_x = mb_x; resync_mb_y = mb_y; for(i=0; i<4; i++) @@ -1210,11 +1178,6 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) left[i] = buffer[mb_x][i] = mask & (pred + (unsigned)(dc * (1 << point_transform))); } - - if (s->restart_interval && !--s->restart_count) { - align_get_bits(&s->gb); - skip_bits(&s->gb, 16); /* skip RSTn */ - } } if (s->rct && s->nb_components == 4) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { @@ -1299,10 +1262,15 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) av_assert0(nb_components>=1 && nb_components<=4); + s->restart_count = -1; + for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { - if (s->restart_interval && !s->restart_count){ - s->restart_count = s->restart_interval; + int restart; + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + return ret; + if (restart) { resync_mb_x = mb_x; resync_mb_y = mb_y; } @@ -1434,10 +1402,6 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) } } } - if (s->restart_interval && !--s->restart_count) { - align_get_bits(&s->gb); - skip_bits(&s->gb, 16); /* skip RSTn */ - } } } return 0; @@ -1487,6 +1451,7 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s) int linesize[MAX_COMPONENTS]; GetBitContext mb_bitmask_gb = {0}; // initialize to silence gcc warning int bytes_per_pixel = 1 + (s->bits > 8); + int ret; if (s->avctx->codec_id == AV_CODEC_ID_MXPEG) { mb_bitmask = s->mb_bitmask; @@ -1501,8 +1466,6 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s) init_get_bits(&mb_bitmask_gb, mb_bitmask, s->mb_width * s->mb_height); } - s->restart_count = 0; - av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt, &chroma_h_shift, &chroma_v_shift); chroma_width = AV_CEIL_RSHIFT(s->width, chroma_h_shift); @@ -1517,15 +1480,31 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s) } next_field: - for (i = 0; i < nb_components; i++) - s->last_dc[i] = (4 << s->bits); + s->restart_count = -1; for (mb_y = 0; mb_y < s->mb_height; mb_y++) { for (mb_x = 0; mb_x < s->mb_width; mb_x++) { const int copy_mb = mb_bitmask && !get_bits1(&mb_bitmask_gb); + int restart; - if (s->restart_interval && !s->restart_count) - s->restart_count = s->restart_interval; + if (s->avctx->codec_id == AV_CODEC_ID_THP) { + if (s->restart_count < 0) { + ret = ff_mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + } + restart = ff_mjpeg_should_restart(s); + if (restart) + align_get_bits(&s->gb); + } else { + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + return ret; + } + if (restart) { + for (i = 0; i < nb_components; i++) + s->last_dc[i] = (4 << s->bits); + } if (get_bits_left(&s->gb) < 0) { av_log(s->avctx, AV_LOG_ERROR, "overread %d\n", @@ -1599,24 +1578,18 @@ next_field: } } } - - handle_rstn(s, nb_components); } } if (s->interlaced && - get_bits_left(&s->gb) > 32 && - show_bits(&s->gb, 8) == 0xFF) { - GetBitContext bak = s->gb; - align_get_bits(&bak); - if (show_bits(&bak, 16) == 0xFFD1) { + bytestream2_get_bytes_left(&s->gB) > 2 && + bytestream2_tell(&s->gB) > 2 && + s->gB.buffer[-2] == 0xFF && + s->gB.buffer[-1] == 0xD1) { av_log(s->avctx, AV_LOG_DEBUG, "AVRn interlaced picture marker found\n"); - s->gb = bak; - skip_bits(&s->gb, 16); s->bottom_field ^= 1; goto next_field; - } } return 0; @@ -1643,7 +1616,7 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s) // Ss and Se are parameters telling start and end coefficients s->coefs_finished[c] |= (2ULL << Se) - (1ULL << Ss); - s->restart_count = 0; + s->restart_count = -1; for (mb_y = 0; mb_y < s->mb_height; mb_y++) { int block_idx = mb_y * s->block_stride[c]; @@ -1651,8 +1624,12 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s) uint8_t *last_nnz = &s->last_nnz[c][block_idx]; for (mb_x = 0; mb_x < s->mb_width; mb_x++, block++, last_nnz++) { int ret; - if (s->restart_interval && !s->restart_count) - s->restart_count = s->restart_interval; + int restart; + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + return ret; + if (restart) + EOBRUN = 0; if (Ah) ret = decode_block_refinement(s, *block, last_nnz, s->ac_index[0], @@ -1668,9 +1645,6 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s) "error y=%d x=%d\n", mb_y, mb_x); return AVERROR_INVALIDDATA; } - - if (handle_rstn(s, 0)) - EOBRUN = 0; } } return 0; @@ -1816,12 +1790,12 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) return ret; } else { - ret = mjpeg_unescape_sos(s); - if (ret < 0) - return ret; if (s->lossless) { av_assert0(s->picture_ptr == s->picture); if (CONFIG_JPEGLS_DECODER && s->ls) { + ret = ff_mjpeg_unescape_sos(s); + if (ret < 0) + return ret; if ((ret = ff_jpegls_decode_picture(s)) < 0) return ret; } else { @@ -1864,7 +1838,6 @@ static int mjpeg_decode_dri(MJpegDecodeContext *s) if (bytestream2_get_be16u(&s->gB) != 4) return AVERROR_INVALIDDATA; s->restart_interval = bytestream2_get_be16u(&s->gB); - s->restart_count = 0; av_log(s->avctx, AV_LOG_DEBUG, "restart interval: %d\n", s->restart_interval); @@ -2271,7 +2244,7 @@ found_hw: bytestream2_skipu(&s->gB, *pbuf_size); } -static int mjpeg_unescape_sos(MJpegDecodeContext *s) +int ff_mjpeg_unescape_sos(MJpegDecodeContext *s) { const uint8_t *buf_ptr = s->gB.buffer; const uint8_t *buf_end = buf_ptr + bytestream2_get_bytes_left(&s->gB); @@ -2320,7 +2293,7 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) bytestream2_put_byteu(&pb, 0xff); } else if (x >= RST0 && x <= RST7) { /* Restart marker */ - bytestream2_put_be16u(&pb, 0xff00 | x); + goto found; } else { /* Non-restart marker */ ptr -= 2; @@ -2502,7 +2475,6 @@ redo_for_pal8: switch (start_code) { case SOI: s->restart_interval = 0; - s->restart_count = 0; s->raw_image_buffer = buf_ptr; s->raw_image_buffer_size = buf_end - buf_ptr; /* nothing to do on SOI */ diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index 47164d74af..95f69049db 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -192,5 +192,35 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s); int ff_mjpeg_decode_sof(MJpegDecodeContext *s); int ff_mjpeg_decode_sos(MJpegDecodeContext *s); int ff_mjpeg_find_marker(const uint8_t **buf_ptr, const uint8_t *buf_end); +int ff_mjpeg_unescape_sos(MJpegDecodeContext *s); + +static inline int ff_mjpeg_should_restart(MJpegDecodeContext *s) +{ + int restart = 0; + if (s->restart_interval) { + if (s->restart_count <= 0) { + s->restart_count = s->restart_interval; + restart = 1; + } + s->restart_count--; + } else { + if (s->restart_count < 0) { + s->restart_count = 0; + restart = 1; + } + } + return restart; +} + +static inline int ff_mjpeg_handle_restart(MJpegDecodeContext *s, int *restart) +{ + *restart = ff_mjpeg_should_restart(s); + if (*restart) { + int ret = ff_mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + } + return 0; +} #endif /* AVCODEC_MJPEGDEC_H */ -- 2.52.0 >From 768f99636b1ebe9b5294748805c9adf6dd0da8d3 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Wed, 10 Dec 2025 23:53:28 +0100 Subject: [PATCH 20/21] avcodec/jpeglsdec: fix decoding of jpegls files with restart markers --- libavcodec/jpeglsdec.c | 33 +++++++++++++++++++++------------ libavcodec/mjpegdec.c | 7 ++++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index 8f10f5a985..4b07f1f8d4 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -397,8 +397,6 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s) goto end; } - ff_jpegls_init_state(state); - if (s->bits <= 8) shift = point_transform + (8 - s->bits); else @@ -419,6 +417,9 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s) av_log(s->avctx, AV_LOG_DEBUG, "JPEG params: ILV=%i Pt=%i BPP=%i, scan = %i\n", ilv, point_transform, s->bits, s->cur_scan); } + + s->restart_count = -1; + if (ilv == 0) { /* separate planes */ if (s->cur_scan > s->nb_components) { ret = AVERROR_INVALIDDATA; @@ -429,6 +430,15 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s) width = s->width * stride; cur += off; for (i = 0; i < s->height; i++) { + int restart; + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + goto end; + if (restart) { + ff_jpegls_init_state(state); + t = 0; + last = zero; + } if (s->bits <= 8) { ret = ls_decode_line(state, s, last, cur, t, width, stride, off, 8); t = last[0]; @@ -440,11 +450,6 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s) break; last = cur; cur += s->picture_ptr->linesize[0]; - - if (s->restart_interval && !--s->restart_count) { - align_get_bits(&s->gb); - skip_bits(&s->gb, 16); /* skip RSTn */ - } } decoded_height = i; } else if (ilv == 1) { /* line interleaving */ @@ -454,17 +459,21 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s) memset(cur, 0, s->picture_ptr->linesize[0]); width = s->width * stride; for (i = 0; i < s->height; i++) { + int restart; + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + goto end; + if (restart) { + ff_jpegls_init_state(state); + memset(Rc, 0, sizeof(Rc)); + 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 (ret < 0) break; Rc[j] = last[j]; - - if (s->restart_interval && !--s->restart_count) { - align_get_bits(&s->gb); - skip_bits(&s->gb, 16); /* skip RSTn */ - } } if (ret < 0) break; diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 40558da136..38def305ab 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1793,9 +1793,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) if (s->lossless) { av_assert0(s->picture_ptr == s->picture); if (CONFIG_JPEGLS_DECODER && s->ls) { - ret = ff_mjpeg_unescape_sos(s); - if (ret < 0) - return ret; if ((ret = ff_jpegls_decode_picture(s)) < 0) return ret; } else { @@ -2342,7 +2339,11 @@ found: if (!(x & 0x80)) { /* Stuffed zero bit */ put_bits(&pb, 15, 0x7f80 | x); + } else if (x >= RST0 && x <= RST7) { + /* Restart marker */ + goto found_ls; } else { + /* Non-restart marker */ ptr -= 2; goto found_ls; } -- 2.52.0 >From ab3b7aa7a06e90a94d16300af5e99c33e4980309 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Fri, 16 Jan 2026 20:50:38 +0100 Subject: [PATCH 21/21] avcodec/mjpegdec: fix indentation and some white spaces --- libavcodec/mjpegdec.c | 244 +++++++++++++++++++++--------------------- 1 file changed, 123 insertions(+), 121 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 38def305ab..2df87feef6 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -337,7 +337,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (bits == 9 && !s->pegasus_rct) s->rct = 1; // FIXME ugly - if(s->lossless && s->avctx->lowres){ + if (s->lossless && s->avctx->lowres) { av_log(s->avctx, AV_LOG_ERROR, "lowres is not possible with lossless jpeg\n"); return AVERROR(ENOSYS); } @@ -623,7 +623,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) case 0x41000000: case 0x43000000: case 0x44000000: - if(s->bits <= 8) + if (s->bits <= 8) s->avctx->pix_fmt = s->force_pal8 ? AV_PIX_FMT_PAL8 : AV_PIX_FMT_GRAY8; else s->avctx->pix_fmt = AV_PIX_FMT_GRAY16; @@ -863,34 +863,36 @@ static int decode_block(MJpegDecodeContext *s, int16_t *block, int component, block[0] = av_clip_int16(val); /* AC coefs */ i = 0; - {OPEN_READER(re, &s->gb); - do { - UPDATE_CACHE(re, &s->gb); - GET_VLC(code, re, &s->gb, s->vlcs[1][ac_index].table, 9, 2); + { + OPEN_READER(re, &s->gb); + do { + UPDATE_CACHE(re, &s->gb); + GET_VLC(code, re, &s->gb, s->vlcs[1][ac_index].table, 9, 2); - i += ((unsigned)code) >> 4; + i += ((unsigned)code) >> 4; code &= 0xf; - if (code) { - // GET_VLC updates the cache if parsing reaches the second stage. - // So we have at least MIN_CACHE_BITS - 9 > 15 bits left here - // and don't need to refill the cache. - { - int cache = GET_CACHE(re, &s->gb); - int sign = (~cache) >> 31; - level = (NEG_USR32(sign ^ cache,code) ^ sign) - sign; - } + if (code) { + // GET_VLC updates the cache if parsing reaches the second stage. + // So we have at least MIN_CACHE_BITS - 9 > 15 bits left here + // and don't need to refill the cache. + { + int cache = GET_CACHE(re, &s->gb); + int sign = (~cache) >> 31; + level = (NEG_USR32(sign ^ cache,code) ^ sign) - sign; + } - LAST_SKIP_BITS(re, &s->gb, code); + LAST_SKIP_BITS(re, &s->gb, code); - if (i > 63) { - av_log(s->avctx, AV_LOG_ERROR, "error count: %d\n", i); - return AVERROR_INVALIDDATA; + if (i > 63) { + av_log(s->avctx, AV_LOG_ERROR, "error count: %d\n", i); + return AVERROR_INVALIDDATA; + } + j = s->permutated_scantable[i]; + block[j] = level * quant_matrix[i]; } - j = s->permutated_scantable[i]; - block[j] = level * quant_matrix[i]; - } - } while (i < 63); - CLOSE_READER(re, &s->gb);} + } while (i < 63); + CLOSE_READER(re, &s->gb); + } return 0; } @@ -1144,7 +1146,7 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) if (restart) { resync_mb_x = mb_x; resync_mb_y = mb_y; - for(i=0; i<4; i++) + for (i=0; i<4; i++) top[i] = left[i]= topleft[i]= 1 << (s->bits - 1); } @@ -1187,16 +1189,16 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) ptr[4*mb_x + 0] = buffer[mb_x][3]; } } else if (s->nb_components == 4) { - for(i=0; i<nb_components; i++) { + for (i=0; i<nb_components; i++) { int c= s->comp_index[i]; if (s->bits <= 8) { - for(mb_x = 0; mb_x < s->mb_width; mb_x++) { + for (mb_x = 0; mb_x < s->mb_width; mb_x++) { ptr[4*mb_x+3-c] = buffer[mb_x][i]; } - } else if(s->bits == 9) { + } else if (s->bits == 9) { return AVERROR_PATCHWELCOME; } else { - for(mb_x = 0; mb_x < s->mb_width; mb_x++) { + for (mb_x = 0; mb_x < s->mb_width; mb_x++) { ((uint16_t*)ptr)[4*mb_x+c] = buffer[mb_x][i]; } } @@ -1227,16 +1229,16 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s) } } } else { - for(i=0; i<nb_components; i++) { + for (i=0; i<nb_components; i++) { int c= s->comp_index[i]; if (s->bits <= 8) { - for(mb_x = 0; mb_x < s->mb_width; mb_x++) { + for (mb_x = 0; mb_x < s->mb_width; mb_x++) { ptr[3*mb_x+2-c] = buffer[mb_x][i]; } - } else if(s->bits == 9) { + } else if (s->bits == 9) { return AVERROR_PATCHWELCOME; } else { - for(mb_x = 0; mb_x < s->mb_width; mb_x++) { + for (mb_x = 0; mb_x < s->mb_width; mb_x++) { ((uint16_t*)ptr)[3*mb_x+2-c] = buffer[mb_x][i]; } } @@ -1280,7 +1282,7 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) return AVERROR_INVALIDDATA; } - if(!mb_x || mb_y == resync_mb_y || mb_y == resync_mb_y+1 && mb_x < resync_mb_x || s->interlaced){ + if (!mb_x || mb_y == resync_mb_y || mb_y == resync_mb_y+1 && mb_x < resync_mb_x || s->interlaced) { int toprow = mb_y == resync_mb_y || mb_y == resync_mb_y+1 && mb_x < resync_mb_x; int leftcol = !mb_x || mb_y == resync_mb_y && mb_x == resync_mb_x; for (i = 0; i < nb_components; i++) { @@ -1295,9 +1297,9 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) y = 0; linesize= s->linesize[c]; - if(bits>8) linesize /= 2; + if (bits>8) linesize /= 2; - for(j=0; j<n; j++) { + for (j=0; j<n; j++) { int pred, dc; ret = mjpeg_decode_dc(s, s->dc_index[i], &dc); @@ -1308,17 +1310,17 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) || v * mb_y + y >= s->height) { // Nothing to do } else if (bits<=8) { - ptr = s->picture_ptr->data[c] + (linesize * (v * mb_y + y)) + (h * mb_x + x); //FIXME optimize this crap - if(y==0 && toprow){ - if(x==0 && leftcol){ + ptr = s->picture_ptr->data[c] + (linesize * (v * mb_y + y)) + (h * mb_x + x); // FIXME optimize this crap + if (y==0 && toprow) { + if (x==0 && leftcol) { pred= 1 << (bits - 1); - }else{ + } else { pred= ptr[-1]; } - }else{ - if(x==0 && leftcol){ + } else { + if (x==0 && leftcol) { pred= ptr[-linesize]; - }else{ + } else { PREDICT(pred, ptr[-linesize-1], ptr[-linesize], ptr[-1], predictor); } } @@ -1327,18 +1329,18 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) ptr += linesize >> 1; pred &= mask; *ptr= pred + ((unsigned)dc << point_transform); - }else{ - ptr16 = (uint16_t*)(s->picture_ptr->data[c] + 2*(linesize * (v * mb_y + y)) + 2*(h * mb_x + x)); //FIXME optimize this crap - if(y==0 && toprow){ - if(x==0 && leftcol){ + } else { + ptr16 = (uint16_t*)(s->picture_ptr->data[c] + 2*(linesize * (v * mb_y + y)) + 2*(h * mb_x + x)); // FIXME optimize this crap + if (y==0 && toprow) { + if (x==0 && leftcol) { pred= 1 << (bits - 1); - }else{ + } else { pred= ptr16[-1]; } - }else{ - if(x==0 && leftcol){ + } else { + if (x==0 && leftcol) { pred= ptr16[-linesize]; - }else{ + } else { PREDICT(pred, ptr16[-linesize-1], ptr16[-linesize], ptr16[-1], predictor); } } @@ -1367,7 +1369,7 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) y = 0; linesize = s->linesize[c]; - if(bits>8) linesize /= 2; + if (bits>8) linesize /= 2; for (j = 0; j < n; j++) { int pred; @@ -1382,13 +1384,13 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s) } else if (bits<=8) { ptr = s->picture_ptr->data[c] + (linesize * (v * mb_y + y)) + - (h * mb_x + x); //FIXME optimize this crap + (h * mb_x + x); // FIXME optimize this crap PREDICT(pred, ptr[-linesize-1], ptr[-linesize], ptr[-1], predictor); pred &= mask; *ptr = pred + ((unsigned)dc << point_transform); - }else{ - ptr16 = (uint16_t*)(s->picture_ptr->data[c] + 2*(linesize * (v * mb_y + y)) + 2*(h * mb_x + x)); //FIXME optimize this crap + } else { + ptr16 = (uint16_t*)(s->picture_ptr->data[c] + 2*(linesize * (v * mb_y + y)) + 2*(h * mb_x + x)); // FIXME optimize this crap PREDICT(pred, ptr16[-linesize-1], ptr16[-linesize], ptr16[-1], predictor); pred &= mask; @@ -1586,10 +1588,10 @@ next_field: bytestream2_tell(&s->gB) > 2 && s->gB.buffer[-2] == 0xFF && s->gB.buffer[-1] == 0xD1) { - av_log(s->avctx, AV_LOG_DEBUG, "AVRn interlaced picture marker found\n"); - s->bottom_field ^= 1; + av_log(s->avctx, AV_LOG_DEBUG, "AVRn interlaced picture marker found\n"); + s->bottom_field ^= 1; - goto next_field; + goto next_field; } return 0; @@ -1623,28 +1625,28 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s) int16_t (*block)[64] = &s->blocks[c][block_idx]; uint8_t *last_nnz = &s->last_nnz[c][block_idx]; for (mb_x = 0; mb_x < s->mb_width; mb_x++, block++, last_nnz++) { - int ret; - int restart; - ret = ff_mjpeg_handle_restart(s, &restart); - if (ret < 0) - return ret; - if (restart) - EOBRUN = 0; + int ret; + int restart; + ret = ff_mjpeg_handle_restart(s, &restart); + if (ret < 0) + return ret; + if (restart) + EOBRUN = 0; - if (Ah) - ret = decode_block_refinement(s, *block, last_nnz, s->ac_index[0], - quant_matrix, Ss, Se, Al, &EOBRUN); - else - ret = decode_block_progressive(s, *block, last_nnz, s->ac_index[0], - quant_matrix, Ss, Se, Al, &EOBRUN); + if (Ah) + ret = decode_block_refinement(s, *block, last_nnz, s->ac_index[0], + quant_matrix, Ss, Se, Al, &EOBRUN); + else + ret = decode_block_progressive(s, *block, last_nnz, s->ac_index[0], + quant_matrix, Ss, Se, Al, &EOBRUN); - if (ret >= 0 && get_bits_left(&s->gb) < 0) - ret = AVERROR_INVALIDDATA; - if (ret < 0) { - av_log(s->avctx, AV_LOG_ERROR, - "error y=%d x=%d\n", mb_y, mb_x); - return AVERROR_INVALIDDATA; - } + if (ret >= 0 && get_bits_left(&s->gb) < 0) + ret = AVERROR_INVALIDDATA; + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "error y=%d x=%d\n", mb_y, mb_x); + return AVERROR_INVALIDDATA; + } } } return 0; @@ -1750,9 +1752,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) s->Ss = bytestream2_get_byteu(&s->gB); /* JPEG Ss / lossless JPEG predictor / JPEG-LS NEAR */ s->Se = bytestream2_get_byteu(&s->gB); /* JPEG Se / JPEG-LS ILV */ - uint8_t b = bytestream2_get_byteu(&s->gB); - s->Ah = b >> 4; /* Ah */ - s->Al = b & 0x0F; /* Al */ + uint8_t b = bytestream2_get_byteu(&s->gB); + s->Ah = b >> 4; /* Ah */ + s->Al = b & 0x0F; /* Al */ if (s->nb_components_sos > 1) { /* interleaved stream */ @@ -1790,38 +1792,38 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s) return ret; } else { - if (s->lossless) { - av_assert0(s->picture_ptr == s->picture); - if (CONFIG_JPEGLS_DECODER && s->ls) { - if ((ret = ff_jpegls_decode_picture(s)) < 0) - return ret; - } else { - if (s->rgb || s->bayer) { - if ((ret = ljpeg_decode_rgb_scan(s)) < 0) + if (s->lossless) { + av_assert0(s->picture_ptr == s->picture); + if (CONFIG_JPEGLS_DECODER && s->ls) { + if ((ret = ff_jpegls_decode_picture(s)) < 0) return ret; } else { - if ((ret = ljpeg_decode_yuv_scan(s)) < 0) + if (s->rgb || s->bayer) { + if ((ret = ljpeg_decode_rgb_scan(s)) < 0) + return ret; + } else { + if ((ret = ljpeg_decode_yuv_scan(s)) < 0) + return ret; + } + } + } else { + if (s->progressive && s->Ss) { + av_assert0(s->picture_ptr == s->picture); + if ((ret = mjpeg_decode_scan_progressive_ac(s)) < 0) + return ret; + } else { + if ((ret = mjpeg_decode_scan(s)) < 0) return ret; } } - } else { - if (s->progressive && s->Ss) { - av_assert0(s->picture_ptr == s->picture); - if ((ret = mjpeg_decode_scan_progressive_ac(s)) < 0) - return ret; - } else { - if ((ret = mjpeg_decode_scan(s)) < 0) - return ret; - } - } } if (s->avctx->codec_id == AV_CODEC_ID_MEDIA100 || s->avctx->codec_id == AV_CODEC_ID_MJPEGB || s->avctx->codec_id == AV_CODEC_ID_THP) { - /* Add the amount of bits read from the unescaped image data buffer - * into the GetByteContext. */ - bytestream2_skipu(&s->gB, (get_bits_count(&s->gb) + 7) / 8); + /* Add the amount of bits read from the unescaped image data buffer + * into the GetByteContext. */ + bytestream2_skipu(&s->gB, (get_bits_count(&s->gb) + 7) / 8); } return 0; @@ -2217,24 +2219,24 @@ static void mjpeg_find_raw_scan_data(MJpegDecodeContext *s, const uint8_t *buf_ptr = s->gB.buffer; const uint8_t *buf_end = buf_ptr + bytestream2_get_bytes_left(&s->gB); - /* Find size of image data buffer (including restart markers). - * No unescaping is performed. */ - const uint8_t *ptr = buf_ptr; - while ((ptr = memchr(ptr, 0xff, buf_end - ptr))) { - ptr++; - if (ptr < buf_end) { - uint8_t x = *ptr++; - /* Discard multiple optional 0xFF fill bytes. */ - while (x == 0xff && ptr < buf_end) - x = *ptr++; - if (x && (x < RST0 || x > RST7)) { - /* Non-restart marker */ - ptr -= 2; - goto found_hw; - } + /* Find size of image data buffer (including restart markers). + * No unescaping is performed. */ + const uint8_t *ptr = buf_ptr; + while ((ptr = memchr(ptr, 0xff, buf_end - ptr))) { + ptr++; + if (ptr < buf_end) { + uint8_t x = *ptr++; + /* Discard multiple optional 0xFF fill bytes. */ + while (x == 0xff && ptr < buf_end) + x = *ptr++; + if (x && (x < RST0 || x > RST7)) { + /* Non-restart marker */ + ptr -= 2; + goto found_hw; } } - ptr = buf_end; + } + ptr = buf_end; found_hw: *pbuf_ptr = buf_ptr; *pbuf_size = ptr - buf_ptr; @@ -2695,7 +2697,7 @@ the_end: for (index = w - 3; index > 0; index--) { line[index] = (line[index / 3] + line[(index + 1) / 3] + line[(index + 2) / 3] + 1) / 3; } - } else if (s->upscale_h[p] == 4){ + } else if (s->upscale_h[p] == 4) { if (is16bit) { uint16_t *line16 = (uint16_t *) line; line16[w - 1] = line16[(w - 1) >> 2]; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
