From: Ramiro Polla via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: Ramiro Polla <code@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH] Various avcodec/mjpegdec improvements (PR #20626) Date: Mon, 29 Sep 2025 13:31:56 -0000 Message-ID: <175915271680.25.18314306363754935782@bf249f23a2c8> (raw) PR #20626 opened by Ramiro Polla (ramiro) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20626 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20626.patch This patchset is in preparation to add error recovery and multi-threaded decoding using restart markers. The most important changes are: - conversion from GetBitContext to GetByteContext since mjpeg is mostly byte-aligned. - improvements to unescaping and detecting the size of image data. - some code cleanup. >From f5072a4facf37b771c297ff50c30c2e15609c0e4 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 10 Sep 2025 16:04:06 +0200 Subject: [PATCH 01/18] 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 b829ab682b..92c994c717 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2382,9 +2382,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.49.1 >From 24b627e359ee1f1d2e1ecda293963109c2253017 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Thu, 11 Sep 2025 17:45:47 +0200 Subject: [PATCH 02/18] 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 4db1d9a89d..833521f0cd 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) { init_get_bits(&s->gb, buf_ptr+dqt_offs, (buf_end - (buf_ptr+dqt_offs))*8); - 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) { init_get_bits(&s->gb, buf_ptr+dht_offs, (buf_end - (buf_ptr+dht_offs))*8); - 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) { init_get_bits(&s->gb, buf_ptr+sof_offs, (buf_end - (buf_ptr+sof_offs))*8); - s->start_code = SOF0; if ((ret = ff_mjpeg_decode_sof(s)) < 0) return ret; } @@ -116,7 +113,6 @@ read_header: init_get_bits(&s->gb, buf_ptr + sos_offs, 8 * FFMIN(field_size, buf_end - buf_ptr - sos_offs)); s->mjpb_skiptosod = (sod_offs - sos_offs - show_bits(&s->gb, 16)); - s->start_code = SOS; if (avctx->skip_frame == AVDISCARD_ALL) { skip_bits(&s->gb, get_bits_left(&s->gb)); } else { diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 92c994c717..214e7dffd1 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -136,7 +136,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; @@ -1842,7 +1841,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; @@ -1991,7 +1990,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"); @@ -2031,7 +2030,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; const uint8_t *aligned; @@ -2054,7 +2053,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 = get_bits_long(&s->gb, 32); len -= 4; /* Apple MJPEG-A */ @@ -2074,7 +2073,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; @@ -2401,7 +2400,6 @@ redo_for_pal8: goto fail; } - s->start_code = start_code; if (avctx->debug & FF_DEBUG_STARTCODE) av_log(avctx, AV_LOG_DEBUG, "startcode: %X\n", start_code); @@ -2411,7 +2409,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 ce688482cf..0f9400ef31 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -58,7 +58,6 @@ typedef struct MJpegDecodeContext { GetBitContext gb; int buf_size; - int start_code; /* current start code */ int buffer_size; uint8_t *buffer; -- 2.49.1 >From 3be313d4b5443347dc6fe9a204d23109edd248dc Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Thu, 18 Sep 2025 16:37:03 +0200 Subject: [PATCH 03/18] 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 214e7dffd1..386322474d 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1775,9 +1775,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.49.1 >From 41cf511d736d97b4c6ddf87ca9e6444043c1d3f2 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Fri, 12 Sep 2025 12:04:23 +0200 Subject: [PATCH 04/18] avcodec/mjpegdec: fix some error return codes --- libavcodec/mjpegdec.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 386322474d..9cab3b583e 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -210,7 +210,7 @@ int ff_mjpeg_decode_dqt(MJpegDecodeContext *s) } index = get_bits(&s->gb, 4); if (index >= 4) - return -1; + return AVERROR_INVALIDDATA; av_log(s->avctx, AV_LOG_DEBUG, "index=%d\n", index); /* read quant table */ for (i = 0; i < 64; i++) { @@ -326,7 +326,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if(s->lossless && s->avctx->lowres){ av_log(s->avctx, AV_LOG_ERROR, "lowres is not possible with lossless jpeg\n"); - return -1; + return AVERROR_INVALIDDATA; } height = get_bits(&s->gb, 16); @@ -345,7 +345,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) nb_components = get_bits(&s->gb, 8); if (nb_components <= 0 || nb_components > MAX_COMPONENTS) - return -1; + return AVERROR_INVALIDDATA; if (s->interlaced && (s->bottom_field == !s->interlace_polarity)) { if (nb_components != s->nb_components) { av_log(s->avctx, AV_LOG_ERROR, @@ -753,8 +753,9 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } av_frame_unref(s->picture_ptr); - if (ff_get_buffer(s->avctx, s->picture_ptr, AV_GET_BUFFER_FLAG_REF) < 0) - return -1; + ret = ff_get_buffer(s->avctx, s->picture_ptr, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return ret; s->picture_ptr->pict_type = AV_PICTURE_TYPE_I; s->picture_ptr->flags |= AV_FRAME_FLAG_KEY; s->got_picture = 1; @@ -1674,7 +1675,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, if (!s->got_picture) { av_log(s->avctx, AV_LOG_WARNING, "Can not process SOS before SOF, skipping\n"); - return -1; + return AVERROR_INVALIDDATA; } /* XXX: verify len field validity */ -- 2.49.1 >From ea5af022de2e3ab2c182bcb80d3109cfd69074b7 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Fri, 12 Sep 2025 17:15:41 +0200 Subject: [PATCH 05/18] avcodec/mjpegdec: add mjpeg_parse_len() helper And check for length field in a consistent manner. --- libavcodec/mjpegdec.c | 69 ++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 9cab3b583e..78d7751fad 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -102,12 +102,12 @@ static int init_default_huffman_tables(MJpegDecodeContext *s) static void parse_avid(MJpegDecodeContext *s, uint8_t *buf, int len) { s->buggy_avid = 1; - if (len > 14 && buf[12] == 1) /* 1 - NTSC */ + if (len > 12 && buf[12] == 1) /* 1 - NTSC */ s->interlace_polarity = 1; - if (len > 14 && buf[12] == 2) /* 2 - PAL */ + if (len > 12 && buf[12] == 2) /* 2 - PAL */ s->interlace_polarity = 0; if (s->avctx->debug & FF_DEBUG_PICT_INFO) - av_log(s->avctx, AV_LOG_INFO, "AVID: len:%d %d\n", len, len > 14 ? buf[12] : -1); + av_log(s->avctx, AV_LOG_INFO, "AVID: len:%d %d\n", len, len > 12 ? buf[12] : -1); } static void init_idct(AVCodecContext *avctx) @@ -190,17 +190,26 @@ av_cold int ff_mjpeg_decode_init(AVCodecContext *avctx) } +static int mjpeg_parse_len(MJpegDecodeContext *s, const char *name) +{ + if (get_bits_left(&s->gb) < 16) + return -1; + int len = get_bits(&s->gb, 16) - 2; + if (8 * len > get_bits_left(&s->gb)) { + av_log(s->avctx, AV_LOG_ERROR, "%s: len %d is too large\n", name, len); + return -1; + } + return len; +} + /* quantize tables */ int ff_mjpeg_decode_dqt(MJpegDecodeContext *s) { int len, index, i; - len = get_bits(&s->gb, 16) - 2; - - if (8*len > get_bits_left(&s->gb)) { - av_log(s->avctx, AV_LOG_ERROR, "dqt: len %d is too large\n", len); + len = mjpeg_parse_len(s, "dqt"); + if (len < 0) return AVERROR_INVALIDDATA; - } while (len >= 65) { int pr = get_bits(&s->gb, 4); @@ -241,12 +250,9 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s) uint8_t val_table[256]; int ret = 0; - len = get_bits(&s->gb, 16) - 2; - - if (8*len > get_bits_left(&s->gb)) { - av_log(s->avctx, AV_LOG_ERROR, "dht: len %d is too large\n", len); + len = mjpeg_parse_len(s, "dht"); + if (len < 0) return AVERROR_INVALIDDATA; - } while (len > 0) { if (len < 17) @@ -306,7 +312,9 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) memset(s->upscale_h, 0, sizeof(s->upscale_h)); memset(s->upscale_v, 0, sizeof(s->upscale_v)); - len = get_bits(&s->gb, 16); + len = mjpeg_parse_len(s, "sof"); + if (len < 6) + return AVERROR_INVALIDDATA; bits = get_bits(&s->gb, 8); if (bits > 16 || bits < 1) { @@ -359,7 +367,8 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) "bits/component or 16-bit gray"); return AVERROR_PATCHWELCOME; } - if (len != 8 + 3 * nb_components) { + len -= 6; + if (len != 3 * nb_components) { av_log(s->avctx, AV_LOG_ERROR, "decode_sof0: error, len(%d) mismatch %d components\n", len, nb_components); return AVERROR_INVALIDDATA; } @@ -1678,8 +1687,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, return AVERROR_INVALIDDATA; } - /* XXX: verify len field validity */ - len = get_bits(&s->gb, 16); + len = mjpeg_parse_len(s, "sos"); + if (len < 1) + return AVERROR_INVALIDDATA; nb_components = get_bits(&s->gb, 8); if (nb_components == 0 || nb_components > MAX_COMPONENTS) { avpriv_report_missing_feature(s->avctx, @@ -1687,8 +1697,8 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, nb_components); return AVERROR_PATCHWELCOME; } - if (len != 6 + 2 * nb_components) { - av_log(s->avctx, AV_LOG_ERROR, "decode_sos: invalid len (%d)\n", len); + if (len != 4 + 2 * nb_components) { + av_log(s->avctx, AV_LOG_ERROR, "decode_sos: len(%d) mismatch %d components\n", len, nb_components); return AVERROR_INVALIDDATA; } for (i = 0; i < nb_components; i++) { @@ -1843,10 +1853,9 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) { int len, id, i; - len = get_bits(&s->gb, 16); - if (len < 2) + len = mjpeg_parse_len(s, "app"); + if (len < 0) return AVERROR_INVALIDDATA; - len -= 2; if (len < 4) { if (s->avctx->err_recognition & AV_EF_EXPLODE) @@ -1855,9 +1864,6 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) goto out; } - if (8 * len > get_bits_left(&s->gb)) - return AVERROR_INVALIDDATA; - id = get_bits_long(&s->gb, 32); len -= 4; @@ -2148,16 +2154,18 @@ out: static int mjpeg_decode_com(MJpegDecodeContext *s) { - int len = get_bits(&s->gb, 16); - if (len >= 2 && 8 * len - 16 <= get_bits_left(&s->gb)) { + int len = mjpeg_parse_len(s, "com"); + if (len <= 0) + return 0; + int i; - char *cbuf = av_malloc(len - 1); + char *cbuf = av_malloc(len + 1); if (!cbuf) return AVERROR(ENOMEM); - for (i = 0; i < len - 2; i++) + for (i = 0; i < len; i++) cbuf[i] = get_bits(&s->gb, 8); - if (i > 0 && cbuf[i - 1] == '\n') + if (cbuf[i - 1] == '\n') cbuf[i - 1] = 0; else cbuf[i] = 0; @@ -2179,7 +2187,6 @@ static int mjpeg_decode_com(MJpegDecodeContext *s) } av_free(cbuf); - } return 0; } -- 2.49.1 >From 9af6c5d812ccb27b253f80346f0a9284e7357bef Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Tue, 23 Sep 2025 16:33:26 +0200 Subject: [PATCH 06/18] 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 78d7751fad..b763461669 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2198,7 +2198,6 @@ static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) const uint8_t *buf_ptr; unsigned int v, v2; int val; - int skipped = 0; buf_ptr = *pbuf_ptr; while (buf_end - buf_ptr > 1) { @@ -2208,12 +2207,12 @@ static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) val = *buf_ptr++; goto found; } - skipped++; } buf_ptr = buf_end; val = -1; found: - ff_dlog(NULL, "find_marker skipped %d bytes\n", skipped); + ff_dlog(NULL, "find_marker skipped %" PTRDIFF_SPECIFIER " bytes\n", + (buf_ptr - *pbuf_ptr) - (val < 0 ? 0 : 2)); *pbuf_ptr = buf_ptr; return val; } -- 2.49.1 >From 6af4b9c4748bca40f721665bead6312f9db7a7e3 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Tue, 23 Sep 2025 17:11:40 +0200 Subject: [PATCH 07/18] avcodec/mjpegdec: speed up find_marker() Minimize number of reads and simplify conditionals. Speed up of ~35% (tested using START_TIMER/STOP_TIMER). --- libavcodec/mjpegdec.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index b763461669..ce4581613b 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2196,16 +2196,19 @@ static int mjpeg_decode_com(MJpegDecodeContext *s) static int find_marker(const uint8_t **pbuf_ptr, const uint8_t *buf_end) { const uint8_t *buf_ptr; - unsigned int v, v2; int val; buf_ptr = *pbuf_ptr; - while (buf_end - buf_ptr > 1) { - v = *buf_ptr++; - v2 = *buf_ptr; - if ((v == 0xff) && (v2 >= SOF0) && (v2 <= COM) && buf_ptr < buf_end) { - val = *buf_ptr++; - goto found; + while (buf_ptr < buf_end) { + if (*buf_ptr++ == 0xff) { + while (buf_ptr < buf_end) { + val = *buf_ptr++; + if (val != 0xff) { + if ((val >= SOF0) && (val <= COM)) + goto found; + break; + } + } } } buf_ptr = buf_end; -- 2.49.1 >From 3f64bc4eaec84fd5aa65bf9a14c8e2de79e498aa Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 24 Sep 2025 23:30:57 +0200 Subject: [PATCH 08/18] avcodec/mjpegdec: move initialization of last_dc field to mjpeg_decode_scan() The last_dc field is only used in sequential JPEG. --- libavcodec/mjpegdec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index ce4581613b..e8d54579e9 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1497,6 +1497,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); @@ -1769,9 +1772,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, skip_bits(&s->gb, 8); next_field: - for (i = 0; i < nb_components; i++) - s->last_dc[i] = (4 << s->bits); - if (s->avctx->hwaccel) { int bytes_to_start = get_bits_count(&s->gb) / 8; av_assert0(bytes_to_start >= 0 && -- 2.49.1 >From b36ec08323ee6eb16589876a6f63a49c39865812 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 24 Sep 2025 23:33:50 +0200 Subject: [PATCH 09/18] avcodec/mjpegdec: move handling of AVRn interlaced picture to mjpeg_decode_scan() --- libavcodec/mjpegdec.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index e8d54579e9..b30583fe19 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1497,6 +1497,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); @@ -1583,6 +1584,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; } @@ -1771,7 +1788,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, for (i = s->mjpb_skiptosod; i > 0; i--) skip_bits(&s->gb, 8); -next_field: if (s->avctx->hwaccel) { int bytes_to_start = get_bits_count(&s->gb) / 8; av_assert0(bytes_to_start >= 0 && @@ -1815,21 +1831,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; - } - } - emms_c(); return 0; out_of_range: -- 2.49.1 >From a8d1ba17d2ec14a69ea40ab5dde4f33f94ccc52f Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 10 Sep 2025 16:01:43 +0200 Subject: [PATCH 10/18] avcodec/mxpegdec: remove unnecessary scope and indentation --- libavcodec/mxpegdec.c | 216 +++++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 109 deletions(-) diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index 8861a3969d..9526189f66 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -216,119 +216,117 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, &unescaped_buf_ptr, &unescaped_buf_size); if (start_code < 0) goto the_end; - { - init_get_bits(&jpg->gb, unescaped_buf_ptr, unescaped_buf_size*8); - if (start_code >= APP0 && start_code <= APP15) { - mxpeg_decode_app(s, unescaped_buf_ptr, unescaped_buf_size); - } + init_get_bits(&jpg->gb, unescaped_buf_ptr, unescaped_buf_size*8); - switch (start_code) { - case SOI: - if (jpg->got_picture) //emulating EOI - goto the_end; - break; - case EOI: - goto the_end; - case DQT: - ret = ff_mjpeg_decode_dqt(jpg); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, - "quantization table decode error\n"); - return ret; - } - break; - case DHT: - ret = ff_mjpeg_decode_dht(jpg); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, - "huffman table decode error\n"); - return ret; - } - break; - case COM: - ret = mxpeg_decode_com(s, unescaped_buf_ptr, - unescaped_buf_size); - if (ret < 0) - return ret; - break; - case SOF0: - if (s->got_sof_data > 1) { - av_log(avctx, AV_LOG_ERROR, - "Multiple SOF in a frame\n"); - return AVERROR_INVALIDDATA; - } - ret = ff_mjpeg_decode_sof(jpg); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, - "SOF data decode error\n"); - s->got_sof_data = 0; - return ret; - } - if (jpg->interlaced) { - av_log(avctx, AV_LOG_ERROR, - "Interlaced mode not supported in MxPEG\n"); - s->got_sof_data = 0; - return AVERROR(EINVAL); - } - s->got_sof_data ++; - break; - case SOS: - if (!s->got_sof_data) { - av_log(avctx, AV_LOG_WARNING, - "Can not process SOS without SOF data, skipping\n"); - break; - } - if (!jpg->got_picture) { - if (jpg->first_picture) { - av_log(avctx, AV_LOG_WARNING, - "First picture has no SOF, skipping\n"); - break; - } - if (!s->got_mxm_bitmask){ - av_log(avctx, AV_LOG_WARNING, - "Non-key frame has no MXM, skipping\n"); - break; - } - /* use stored SOF data to allocate current picture */ - av_frame_unref(jpg->picture_ptr); - if ((ret = ff_get_buffer(avctx, jpg->picture_ptr, - AV_GET_BUFFER_FLAG_REF)) < 0) - return ret; - jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_P; - jpg->picture_ptr->flags &= ~AV_FRAME_FLAG_KEY; - jpg->got_picture = 1; - } else { - jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - jpg->picture_ptr->flags |= AV_FRAME_FLAG_KEY; - } - - if (s->got_mxm_bitmask) { - AVFrame *reference_ptr = s->picture[s->picture_index ^ 1]; - if (mxpeg_check_dimensions(s, jpg, reference_ptr) < 0) - break; - - /* allocate dummy reference picture if needed */ - if (!reference_ptr->data[0] && - (ret = ff_get_buffer(avctx, reference_ptr, - AV_GET_BUFFER_FLAG_REF)) < 0) - return ret; - - ret = ff_mjpeg_decode_sos(jpg, s->mxm_bitmask, s->bitmask_size, reference_ptr); - if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) - return ret; - } else { - ret = ff_mjpeg_decode_sos(jpg, NULL, 0, NULL); - if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) - return ret; - } - - break; - } - - buf_ptr += (get_bits_count(&jpg->gb)+7) >> 3; + if (start_code >= APP0 && start_code <= APP15) { + mxpeg_decode_app(s, unescaped_buf_ptr, unescaped_buf_size); } + switch (start_code) { + case SOI: + if (jpg->got_picture) //emulating EOI + goto the_end; + break; + case EOI: + goto the_end; + case DQT: + ret = ff_mjpeg_decode_dqt(jpg); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "quantization table decode error\n"); + return ret; + } + break; + case DHT: + ret = ff_mjpeg_decode_dht(jpg); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "huffman table decode error\n"); + return ret; + } + break; + case COM: + ret = mxpeg_decode_com(s, unescaped_buf_ptr, + unescaped_buf_size); + if (ret < 0) + return ret; + break; + case SOF0: + if (s->got_sof_data > 1) { + av_log(avctx, AV_LOG_ERROR, + "Multiple SOF in a frame\n"); + return AVERROR_INVALIDDATA; + } + ret = ff_mjpeg_decode_sof(jpg); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "SOF data decode error\n"); + s->got_sof_data = 0; + return ret; + } + if (jpg->interlaced) { + av_log(avctx, AV_LOG_ERROR, + "Interlaced mode not supported in MxPEG\n"); + s->got_sof_data = 0; + return AVERROR(EINVAL); + } + s->got_sof_data ++; + break; + case SOS: + if (!s->got_sof_data) { + av_log(avctx, AV_LOG_WARNING, + "Can not process SOS without SOF data, skipping\n"); + break; + } + if (!jpg->got_picture) { + if (jpg->first_picture) { + av_log(avctx, AV_LOG_WARNING, + "First picture has no SOF, skipping\n"); + break; + } + if (!s->got_mxm_bitmask){ + av_log(avctx, AV_LOG_WARNING, + "Non-key frame has no MXM, skipping\n"); + break; + } + /* use stored SOF data to allocate current picture */ + av_frame_unref(jpg->picture_ptr); + if ((ret = ff_get_buffer(avctx, jpg->picture_ptr, + AV_GET_BUFFER_FLAG_REF)) < 0) + return ret; + jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_P; + jpg->picture_ptr->flags &= ~AV_FRAME_FLAG_KEY; + jpg->got_picture = 1; + } else { + jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_I; + jpg->picture_ptr->flags |= AV_FRAME_FLAG_KEY; + } + + if (s->got_mxm_bitmask) { + AVFrame *reference_ptr = s->picture[s->picture_index ^ 1]; + if (mxpeg_check_dimensions(s, jpg, reference_ptr) < 0) + break; + + /* allocate dummy reference picture if needed */ + if (!reference_ptr->data[0] && + (ret = ff_get_buffer(avctx, reference_ptr, + AV_GET_BUFFER_FLAG_REF)) < 0) + return ret; + + ret = ff_mjpeg_decode_sos(jpg, s->mxm_bitmask, s->bitmask_size, reference_ptr); + if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) + return ret; + } else { + ret = ff_mjpeg_decode_sos(jpg, NULL, 0, NULL); + if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) + return ret; + } + + break; + } + + buf_ptr += (get_bits_count(&jpg->gb)+7) >> 3; } the_end: -- 2.49.1 >From 0bcb6b9160fe9f775c96a15fa5eb6a639aed3b28 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Fri, 12 Sep 2025 19:01:42 +0200 Subject: [PATCH 11/18] avcodec/mjpegdec: use GetByteContext instead of GetBitContext where possible JPEG is mostly byte-aligned. We only really need GetBitContext while decoding the image data. Suggested-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com> --- libavcodec/jpeglsdec.c | 20 ++--- libavcodec/mjpegbdec.c | 14 ++-- libavcodec/mjpegdec.c | 177 +++++++++++++++++++++-------------------- libavcodec/mjpegdec.h | 2 + libavcodec/mxpegdec.c | 8 +- 5 files changed, 112 insertions(+), 109 deletions(-) diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index 5f463c9660..e506851b57 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -53,19 +53,19 @@ int ff_jpegls_decode_lse(MJpegDecodeContext *s) int id; int tid, wt, maxtab, i, j; - int len = get_bits(&s->gb, 16); - id = get_bits(&s->gb, 8); + int len = bytestream2_get_be16(&s->gB); + id = bytestream2_get_byte(&s->gB); switch (id) { case 1: if (len < 13) return AVERROR_INVALIDDATA; - s->maxval = get_bits(&s->gb, 16); - s->t1 = get_bits(&s->gb, 16); - s->t2 = get_bits(&s->gb, 16); - s->t3 = get_bits(&s->gb, 16); - s->reset = get_bits(&s->gb, 16); + s->maxval = bytestream2_get_be16u(&s->gB); + s->t1 = bytestream2_get_be16u(&s->gB); + s->t2 = bytestream2_get_be16u(&s->gB); + s->t3 = bytestream2_get_be16u(&s->gB); + s->reset = bytestream2_get_be16u(&s->gB); if (s->avctx->debug & FF_DEBUG_PICT_INFO) { av_log(s->avctx, AV_LOG_DEBUG, "Coding parameters maxval:%d T1:%d T2:%d T3:%d reset:%d\n", @@ -78,8 +78,8 @@ int ff_jpegls_decode_lse(MJpegDecodeContext *s) case 2: s->palette_index = 0; case 3: - tid= get_bits(&s->gb, 8); - wt = get_bits(&s->gb, 8); + tid= bytestream2_get_byte(&s->gB); + wt = bytestream2_get_byte(&s->gB); if (len < 5) return AVERROR_INVALIDDATA; @@ -129,7 +129,7 @@ int ff_jpegls_decode_lse(MJpegDecodeContext *s) uint8_t k = i << shift; pal[k] = wt < 4 ? 0xFF000000 : 0; for (j=0; j<wt; j++) { - pal[k] |= get_bits(&s->gb, 8) << (8*(wt-j-1)); + pal[k] |= bytestream2_get_byte(&s->gB) << (8*(wt-j-1)); } } s->palette_index = i; diff --git a/libavcodec/mjpegbdec.c b/libavcodec/mjpegbdec.c index 833521f0cd..5a21ea47fc 100644 --- a/libavcodec/mjpegbdec.c +++ b/libavcodec/mjpegbdec.c @@ -84,7 +84,7 @@ read_header: dqt_offs = read_offs(avctx, &hgb, buf_end - buf_ptr, "dqt is %d and size is %d\n"); av_log(avctx, AV_LOG_DEBUG, "dqt offs: 0x%"PRIx32"\n", dqt_offs); if (dqt_offs) { - init_get_bits(&s->gb, buf_ptr+dqt_offs, (buf_end - (buf_ptr+dqt_offs))*8); + bytestream2_init(&s->gB, buf_ptr+dqt_offs, buf_end - (buf_ptr+dqt_offs)); ret = ff_mjpeg_decode_dqt(s); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; @@ -93,14 +93,14 @@ read_header: dht_offs = read_offs(avctx, &hgb, buf_end - buf_ptr, "dht is %d and size is %d\n"); av_log(avctx, AV_LOG_DEBUG, "dht offs: 0x%"PRIx32"\n", dht_offs); if (dht_offs) { - init_get_bits(&s->gb, buf_ptr+dht_offs, (buf_end - (buf_ptr+dht_offs))*8); + bytestream2_init(&s->gB, buf_ptr+dht_offs, buf_end - (buf_ptr+dht_offs)); ff_mjpeg_decode_dht(s); } sof_offs = read_offs(avctx, &hgb, buf_end - buf_ptr, "sof is %d and size is %d\n"); av_log(avctx, AV_LOG_DEBUG, "sof offs: 0x%"PRIx32"\n", sof_offs); if (sof_offs) { - init_get_bits(&s->gb, buf_ptr+sof_offs, (buf_end - (buf_ptr+sof_offs))*8); + bytestream2_init(&s->gB, buf_ptr+sof_offs, buf_end - (buf_ptr+sof_offs)); if ((ret = ff_mjpeg_decode_sof(s)) < 0) return ret; } @@ -110,11 +110,11 @@ read_header: sod_offs = read_offs(avctx, &hgb, buf_end - buf_ptr, "sof is %d and size is %d\n"); av_log(avctx, AV_LOG_DEBUG, "sod offs: 0x%"PRIx32"\n", sod_offs); if (sos_offs) { - init_get_bits(&s->gb, buf_ptr + sos_offs, - 8 * FFMIN(field_size, buf_end - buf_ptr - sos_offs)); - s->mjpb_skiptosod = (sod_offs - sos_offs - show_bits(&s->gb, 16)); + 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)); if (avctx->skip_frame == AVDISCARD_ALL) { - skip_bits(&s->gb, get_bits_left(&s->gb)); + bytestream2_skipu(&s->gB, bytestream2_get_bytes_left(&s->gB)); } else { ret = ff_mjpeg_decode_sos(s, NULL, 0, NULL); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index b30583fe19..de06b8334c 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -148,8 +148,7 @@ av_cold int ff_mjpeg_decode_init(AVCodecContext *avctx) if (s->extern_huff) { av_log(avctx, AV_LOG_INFO, "using external huffman table\n"); - if ((ret = init_get_bits(&s->gb, avctx->extradata, avctx->extradata_size * 8)) < 0) - return ret; + bytestream2_init(&s->gB, avctx->extradata, avctx->extradata_size); if (ff_mjpeg_decode_dht(s)) { av_log(avctx, AV_LOG_ERROR, "error using external huffman table, switching back to internal\n"); @@ -192,10 +191,10 @@ av_cold int ff_mjpeg_decode_init(AVCodecContext *avctx) static int mjpeg_parse_len(MJpegDecodeContext *s, const char *name) { - if (get_bits_left(&s->gb) < 16) + if (bytestream2_get_bytes_left(&s->gB) < 2) return -1; - int len = get_bits(&s->gb, 16) - 2; - if (8 * len > get_bits_left(&s->gb)) { + int len = bytestream2_get_be16u(&s->gB) - 2; + if (len > bytestream2_get_bytes_left(&s->gB)) { av_log(s->avctx, AV_LOG_ERROR, "%s: len %d is too large\n", name, len); return -1; } @@ -212,18 +211,19 @@ int ff_mjpeg_decode_dqt(MJpegDecodeContext *s) return AVERROR_INVALIDDATA; while (len >= 65) { - int pr = get_bits(&s->gb, 4); + uint8_t b = bytestream2_get_byteu(&s->gB); + int pr = b >> 4; if (pr > 1) { av_log(s->avctx, AV_LOG_ERROR, "dqt: invalid precision\n"); return AVERROR_INVALIDDATA; } - index = get_bits(&s->gb, 4); + index = b & 0x0F; if (index >= 4) return AVERROR_INVALIDDATA; av_log(s->avctx, AV_LOG_DEBUG, "index=%d\n", index); /* read quant table */ for (i = 0; i < 64; i++) { - s->quant_matrixes[index][i] = get_bits(&s->gb, pr ? 16 : 8); + s->quant_matrixes[index][i] = pr ? bytestream2_get_be16u(&s->gB) : bytestream2_get_byteu(&s->gB); if (s->quant_matrixes[index][i] == 0) { int log_level = s->avctx->err_recognition & AV_EF_EXPLODE ? AV_LOG_ERROR : AV_LOG_WARNING; av_log(s->avctx, log_level, "dqt: 0 quant value\n"); @@ -257,15 +257,16 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s) while (len > 0) { if (len < 17) return AVERROR_INVALIDDATA; - class = get_bits(&s->gb, 4); + uint8_t b = bytestream2_get_byteu(&s->gB); + class = b >> 4; if (class >= 2) return AVERROR_INVALIDDATA; - index = get_bits(&s->gb, 4); + index = b & 0x0F; if (index >= 4) return AVERROR_INVALIDDATA; n = 0; for (i = 1; i <= 16; i++) { - bits_table[i] = get_bits(&s->gb, 8); + bits_table[i] = bytestream2_get_byteu(&s->gB); n += bits_table[i]; } len -= 17; @@ -273,7 +274,7 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s) return AVERROR_INVALIDDATA; for (i = 0; i < n; i++) { - v = get_bits(&s->gb, 8); + v = bytestream2_get_byteu(&s->gB); val_table[i] = v; } len -= n; @@ -315,7 +316,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) len = mjpeg_parse_len(s, "sof"); if (len < 6) return AVERROR_INVALIDDATA; - bits = get_bits(&s->gb, 8); + bits = bytestream2_get_byteu(&s->gB); if (bits > 16 || bits < 1) { av_log(s->avctx, AV_LOG_ERROR, "bits %d is invalid\n", bits); @@ -337,8 +338,8 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) return AVERROR_INVALIDDATA; } - height = get_bits(&s->gb, 16); - width = get_bits(&s->gb, 16); + height = bytestream2_get_be16u(&s->gB); + width = bytestream2_get_be16u(&s->gB); // HACK for odd_height.mov if (s->interlaced && s->width == width && s->height == height + 1) @@ -350,7 +351,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->buf_size && (width + 7) / 8 * ((height + 7) / 8) > s->buf_size * 4LL) return AVERROR_INVALIDDATA; - nb_components = get_bits(&s->gb, 8); + nb_components = bytestream2_get_byteu(&s->gB); if (nb_components <= 0 || nb_components > MAX_COMPONENTS) return AVERROR_INVALIDDATA; @@ -378,15 +379,16 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) s->v_max = 1; for (i = 0; i < nb_components; i++) { /* component id */ - s->component_id[i] = get_bits(&s->gb, 8); - h_count[i] = get_bits(&s->gb, 4); - v_count[i] = get_bits(&s->gb, 4); + s->component_id[i] = bytestream2_get_byteu(&s->gB); + uint8_t b = bytestream2_get_byteu(&s->gB); + h_count[i] = b >> 4; + v_count[i] = b & 0x0F; /* compute hmax and vmax (only used in interleaved case) */ if (h_count[i] > s->h_max) s->h_max = h_count[i]; if (v_count[i] > s->v_max) s->v_max = v_count[i]; - s->quant_index[i] = get_bits(&s->gb, 8); + s->quant_index[i] = bytestream2_get_byteu(&s->gB); if (s->quant_index[i] >= 4) { av_log(s->avctx, AV_LOG_ERROR, "quant_index is invalid\n"); return AVERROR_INVALIDDATA; @@ -1710,7 +1712,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, len = mjpeg_parse_len(s, "sos"); if (len < 1) return AVERROR_INVALIDDATA; - nb_components = get_bits(&s->gb, 8); + nb_components = bytestream2_get_byteu(&s->gB); if (nb_components == 0 || nb_components > MAX_COMPONENTS) { avpriv_report_missing_feature(s->avctx, "decode_sos: nb_components (%d)", @@ -1722,7 +1724,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, return AVERROR_INVALIDDATA; } for (i = 0; i < nb_components; i++) { - id = get_bits(&s->gb, 8); + id = bytestream2_get_byteu(&s->gB); av_log(s->avctx, AV_LOG_DEBUG, "component: %d\n", id); /* find component index */ for (index = 0; index < s->nb_components; index++) @@ -1745,8 +1747,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, s->comp_index[i] = index; - s->dc_index[i] = get_bits(&s->gb, 4); - s->ac_index[i] = get_bits(&s->gb, 4); + uint8_t b = bytestream2_get_byteu(&s->gB); + s->dc_index[i] = b >> 4; + s->ac_index[i] = b & 0x0F; if (s->dc_index[i] < 0 || s->ac_index[i] < 0 || s->dc_index[i] >= 4 || s->ac_index[i] >= 4) @@ -1755,11 +1758,12 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, goto out_of_range; } - predictor = get_bits(&s->gb, 8); /* JPEG Ss / lossless JPEG predictor /JPEG-LS NEAR */ - ilv = get_bits(&s->gb, 8); /* JPEG Se / JPEG-LS ILV */ + 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")){ - prev_shift = get_bits(&s->gb, 4); /* Ah */ - point_transform = get_bits(&s->gb, 4); /* Al */ + uint8_t b = bytestream2_get_byteu(&s->gB); + prev_shift = b >> 4; /* Ah */ + point_transform = b & 0x0F; /* Al */ }else prev_shift = point_transform = 0; @@ -1785,11 +1789,15 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, /* mjpeg-b can have padding bytes between sos and image data, skip them */ - for (i = s->mjpb_skiptosod; i > 0; i--) - skip_bits(&s->gb, 8); + 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)); + if (ret < 0) + return ret; if (s->avctx->hwaccel) { - int bytes_to_start = get_bits_count(&s->gb) / 8; + int bytes_to_start = bytestream2_tell(&s->gB); av_assert0(bytes_to_start >= 0 && s->raw_scan_buffer_size >= bytes_to_start); @@ -1831,6 +1839,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, } } + /* 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); emms_c(); return 0; out_of_range: @@ -1840,9 +1851,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, static int mjpeg_decode_dri(MJpegDecodeContext *s) { - if (get_bits(&s->gb, 16) != 4) + if (bytestream2_get_be16u(&s->gB) != 4) return AVERROR_INVALIDDATA; - s->restart_interval = get_bits(&s->gb, 16); + 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); @@ -1865,7 +1876,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) goto out; } - id = get_bits_long(&s->gb, 32); + id = bytestream2_get_be32u(&s->gB); len -= 4; if (s->avctx->debug & FF_DEBUG_STARTCODE) @@ -1884,7 +1895,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) 4bytes field_size_less_padding */ s->buggy_avid = 1; - i = get_bits(&s->gb, 8); len--; + i = bytestream2_get_byteu(&s->gB); len--; av_log(s->avctx, AV_LOG_DEBUG, "polarity %d\n", i); goto out; } @@ -1893,13 +1904,13 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) int t_w, t_h, v1, v2; if (len < 8) goto out; - skip_bits(&s->gb, 8); /* the trailing zero-byte */ - v1 = get_bits(&s->gb, 8); - v2 = get_bits(&s->gb, 8); - skip_bits(&s->gb, 8); + bytestream2_skipu(&s->gB, 1); /* the trailing zero-byte */ + v1 = bytestream2_get_byteu(&s->gB); + v2 = bytestream2_get_byteu(&s->gB); + bytestream2_skipu(&s->gB, 1); - s->avctx->sample_aspect_ratio.num = get_bits(&s->gb, 16); - s->avctx->sample_aspect_ratio.den = get_bits(&s->gb, 16); + s->avctx->sample_aspect_ratio.num = bytestream2_get_be16u(&s->gB); + s->avctx->sample_aspect_ratio.den = bytestream2_get_be16u(&s->gB); if ( s->avctx->sample_aspect_ratio.num <= 0 || s->avctx->sample_aspect_ratio.den <= 0) { s->avctx->sample_aspect_ratio.num = 0; @@ -1915,8 +1926,8 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) len -= 8; if (len >= 2) { - t_w = get_bits(&s->gb, 8); - t_h = get_bits(&s->gb, 8); + t_w = bytestream2_get_byteu(&s->gB); + t_h = bytestream2_get_byteu(&s->gB); if (t_w && t_h) { /* skip thumbnail */ if (len -10 - (t_w * t_h * 3) > 0) @@ -1929,13 +1940,13 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) if ( id == AV_RB32("Adob") && len >= 8 - && show_bits(&s->gb, 8) == 'e' - && show_bits_long(&s->gb, 32) != AV_RB32("e_CM")) { - skip_bits(&s->gb, 8); /* 'e' */ - skip_bits(&s->gb, 16); /* version */ - skip_bits(&s->gb, 16); /* flags0 */ - skip_bits(&s->gb, 16); /* flags1 */ - s->adobe_transform = get_bits(&s->gb, 8); + && bytestream2_peek_byteu(&s->gB) == 'e' + && bytestream2_peek_be32u(&s->gB) != AV_RB32("e_CM")) { + bytestream2_skipu(&s->gB, 1); /* 'e' */ + bytestream2_skipu(&s->gB, 2); /* version */ + bytestream2_skipu(&s->gB, 2); /* flags0 */ + bytestream2_skipu(&s->gB, 2); /* flags1 */ + s->adobe_transform = bytestream2_get_byteu(&s->gB); if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_INFO, "mjpeg: Adobe header found, transform=%d\n", s->adobe_transform); len -= 8; @@ -1948,11 +1959,11 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_INFO, "Pegasus lossless jpeg header found\n"); - skip_bits(&s->gb, 16); /* version ? */ - skip_bits(&s->gb, 16); /* unknown always 0? */ - skip_bits(&s->gb, 16); /* unknown always 0? */ - skip_bits(&s->gb, 16); /* unknown always 0? */ - switch (i=get_bits(&s->gb, 8)) { + bytestream2_skipu(&s->gB, 2); /* version ? */ + bytestream2_skipu(&s->gB, 2); /* unknown always 0? */ + bytestream2_skipu(&s->gB, 2); /* unknown always 0? */ + bytestream2_skipu(&s->gB, 2); /* unknown always 0? */ + switch (i=bytestream2_get_byteu(&s->gB)) { case 1: rgb = 1; pegasus_rct = 0; @@ -1980,14 +1991,14 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) goto out; } if (id == AV_RL32("colr") && len > 0) { - s->colr = get_bits(&s->gb, 8); + s->colr = bytestream2_get_byteu(&s->gB); if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_INFO, "COLR %d\n", s->colr); len --; goto out; } if (id == AV_RL32("xfrm") && len > 0) { - s->xfrm = get_bits(&s->gb, 8); + s->xfrm = bytestream2_get_byteu(&s->gB); if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_INFO, "XFRM %d\n", s->xfrm); len --; @@ -2000,12 +2011,12 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_INFO, "_JPSJPS_\n"); - skip_bits(&s->gb, 32); len -= 4; /* JPS_ */ - skip_bits(&s->gb, 16); len -= 2; /* block length */ - skip_bits(&s->gb, 8); /* reserved */ - flags = get_bits(&s->gb, 8); - layout = get_bits(&s->gb, 8); - type = get_bits(&s->gb, 8); + bytestream2_skipu(&s->gB, 4); len -= 4; /* JPS_ */ + bytestream2_skipu(&s->gB, 2); len -= 2; /* block length */ + bytestream2_skipu(&s->gB, 1); /* reserved */ + flags = bytestream2_get_byteu(&s->gB); + layout = bytestream2_get_byteu(&s->gB); + type = bytestream2_get_byteu(&s->gB); len -= 4; av_freep(&s->stereo3d); @@ -2037,21 +2048,17 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) /* EXIF metadata */ if (start_code == APP1 && id == AV_RB32("Exif") && len >= 2) { int ret; - const uint8_t *aligned; - skip_bits(&s->gb, 16); // skip padding + bytestream2_skipu(&s->gB, 2); // skip padding len -= 2; - // init byte wise reading - aligned = align_get_bits(&s->gb); - - ret = av_exif_parse_buffer(s->avctx, aligned, len, &s->exif_metadata, AV_EXIF_TIFF_HEADER); + ret = av_exif_parse_buffer(s->avctx, s->gB.buffer, len, &s->exif_metadata, AV_EXIF_TIFF_HEADER); if (ret < 0) { av_log(s->avctx, AV_LOG_WARNING, "unable to parse EXIF buffer\n"); goto out; } - skip_bits(&s->gb, ret << 3); + bytestream2_skipu(&s->gB, ret); len -= ret; goto out; @@ -2059,7 +2066,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) /* Apple MJPEG-A */ if ((start_code == APP1) && (len > (0x28 - 8))) { - id = get_bits_long(&s->gb, 32); + id = bytestream2_get_be32u(&s->gB); len -= 4; /* Apple MJPEG-A */ if (id == AV_RB32("mjpg")) { @@ -2083,23 +2090,23 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) unsigned seqno; unsigned nummarkers; - id = get_bits_long(&s->gb, 32); - id2 = get_bits(&s->gb, 24); + id = bytestream2_get_be32u(&s->gB); + id2 = bytestream2_get_be24u(&s->gB); len -= 7; if (id != AV_RB32("PROF") || id2 != AV_RB24("ILE")) { av_log(s->avctx, AV_LOG_WARNING, "Invalid ICC_PROFILE header in APP2\n"); goto out; } - skip_bits(&s->gb, 8); - seqno = get_bits(&s->gb, 8); + bytestream2_skipu(&s->gB, 1); + seqno = bytestream2_get_byteu(&s->gB); len -= 2; if (seqno == 0) { av_log(s->avctx, AV_LOG_WARNING, "Invalid sequence number in APP2\n"); goto out; } - nummarkers = get_bits(&s->gb, 8); + nummarkers = bytestream2_get_byteu(&s->gB); len -= 1; if (nummarkers == 0) { av_log(s->avctx, AV_LOG_WARNING, "Invalid number of markers coded in APP2\n"); @@ -2133,8 +2140,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) return AVERROR(ENOMEM); } - memcpy(s->iccentries[seqno - 1].data, align_get_bits(&s->gb), len); - skip_bits(&s->gb, len << 3); + bytestream2_get_bufferu(&s->gB, s->iccentries[seqno - 1].data, len); len = 0; s->iccread++; @@ -2147,8 +2153,8 @@ out: if (len < 0) av_log(s->avctx, AV_LOG_ERROR, "mjpeg: error, decode_app parser read over the end\n"); - while (len-- > 0) - skip_bits(&s->gb, 8); + if (len) + bytestream2_skipu(&s->gB, len); return 0; } @@ -2165,7 +2171,7 @@ static int mjpeg_decode_com(MJpegDecodeContext *s) return AVERROR(ENOMEM); for (i = 0; i < len; i++) - cbuf[i] = get_bits(&s->gb, 8); + cbuf[i] = bytestream2_get_byteu(&s->gB); if (cbuf[i - 1] == '\n') cbuf[i - 1] = 0; else @@ -2401,12 +2407,7 @@ redo_for_pal8: av_log(avctx, AV_LOG_DEBUG, "marker=%x avail_size_in_buf=%"PTRDIFF_SPECIFIER"\n", start_code, buf_end - buf_ptr); - ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); - - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "invalid buffer\n"); - goto fail; - } + bytestream2_init(&s->gB, unescaped_buf_ptr, unescaped_buf_size); if (avctx->debug & FF_DEBUG_STARTCODE) av_log(avctx, AV_LOG_DEBUG, "startcode: %X\n", start_code); @@ -2602,7 +2603,7 @@ eoi_parser: skip: /* eof process start code */ - buf_ptr += (get_bits_count(&s->gb) + 7) / 8; + buf_ptr += bytestream2_tell(&s->gB); av_log(avctx, AV_LOG_DEBUG, "marker parser used %d bytes (%d bits)\n", (get_bits_count(&s->gb) + 7) / 8, get_bits_count(&s->gb)); diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index 0f9400ef31..4d391ff9f9 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -36,6 +36,7 @@ #include "avcodec.h" #include "blockdsp.h" +#include "bytestream.h" #include "exif.h" #include "get_bits.h" #include "hpeldsp.h" @@ -56,6 +57,7 @@ typedef struct MJpegDecodeContext { AVClass *class; AVCodecContext *avctx; GetBitContext gb; + GetByteContext gB; int buf_size; int buffer_size; diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index 9526189f66..b66bff56c9 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -86,7 +86,7 @@ static int mxpeg_decode_app(MXpegDecodeContext *s, if (buf_size < 2) return 0; len = AV_RB16(buf_ptr); - skip_bits(&s->jpg.gb, 8*FFMIN(len,buf_size)); + bytestream2_skipu(&s->jpg.gB, FFMIN(len, buf_size)); return 0; } @@ -154,7 +154,7 @@ static int mxpeg_decode_com(MXpegDecodeContext *s, if (len > 14 && len <= buf_size && !strncmp(buf_ptr + 2, "MXM", 3)) { ret = mxpeg_decode_mxm(s, buf_ptr + 2, len - 2); } - skip_bits(&s->jpg.gb, 8*FFMIN(len,buf_size)); + bytestream2_skipu(&s->jpg.gB, FFMIN(len, buf_size)); return ret; } @@ -217,7 +217,7 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (start_code < 0) goto the_end; - init_get_bits(&jpg->gb, unescaped_buf_ptr, unescaped_buf_size*8); + bytestream2_init(&jpg->gB, unescaped_buf_ptr, unescaped_buf_size); if (start_code >= APP0 && start_code <= APP15) { mxpeg_decode_app(s, unescaped_buf_ptr, unescaped_buf_size); @@ -326,7 +326,7 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, break; } - buf_ptr += (get_bits_count(&jpg->gb)+7) >> 3; + buf_ptr += bytestream2_tell(&jpg->gB); } the_end: -- 2.49.1 >From b83f15b00ffc4ae26b13953744005892278347d1 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Thu, 11 Sep 2025 22:14:43 +0200 Subject: [PATCH 12/18] 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 de06b8334c..ed7175b022 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2235,12 +2235,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; @@ -2254,10 +2261,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++); @@ -2287,7 +2290,6 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, } if (src < ptr) copy_data_segment(0); - } #undef copy_data_segment *unescaped_buf_ptr = s->buffer; @@ -2297,7 +2299,7 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %"PTRDIFF_SPECIFIER" 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; @@ -2339,9 +2341,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.49.1 >From 48f7e8d7ad42da02cd0154717b226bfc8a9a8a47 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Tue, 23 Sep 2025 17:40:10 +0200 Subject: [PATCH 13/18] avcodec/mjpegdec: split mjpeg_unescape_sos() out of ff_mjpeg_find_marker() --- libavcodec/mjpegdec.c | 59 ++++++++++++++++++++++++------------------- libavcodec/mjpegdec.h | 4 +-- libavcodec/mxpegdec.c | 13 ++++------ 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index ed7175b022..de50df8ce0 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -56,6 +56,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 { @@ -1792,7 +1797,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 = s->gB.buffer; + int unescaped_buf_size = bytestream2_get_bytes_left(&s->gB); + ret = mjpeg_unescape_sos(s, unescaped_buf_ptr, unescaped_buf_ptr + unescaped_buf_size, + &unescaped_buf_ptr, &unescaped_buf_size); + if (ret < 0) + goto the_end; + + ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); if (ret < 0) return ret; @@ -2200,7 +2212,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; @@ -2227,28 +2239,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; @@ -2298,9 +2308,9 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s, AV_INPUT_BUFFER_PADDING_SIZE); av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %"PTRDIFF_SPECIFIER" 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; @@ -2343,7 +2353,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) @@ -2366,9 +2376,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; @@ -2390,23 +2398,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=%"PTRDIFF_SPECIFIER"\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 4d391ff9f9..2584c0dd2d 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -185,8 +185,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.49.1 >From 75679c090c6b4f2cc8ab324c0d25d96a6ec687dd Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 24 Sep 2025 00:17:02 +0200 Subject: [PATCH 14/18] 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 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 de50df8ce0..c7f584707c 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -56,10 +56,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) { @@ -1797,14 +1794,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 = s->gB.buffer; - int unescaped_buf_size = bytestream2_get_bytes_left(&s->gB); - ret = mjpeg_unescape_sos(s, unescaped_buf_ptr, unescaped_buf_ptr + unescaped_buf_size, - &unescaped_buf_ptr, &unescaped_buf_size); - if (ret < 0) - goto the_end; - - ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); + ret = mjpeg_unescape_sos(s); if (ret < 0) return ret; @@ -2239,17 +2229,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); @@ -2302,9 +2294,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 %"PTRDIFF_SPECIFIER" bytes\n", @@ -2347,13 +2339,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.49.1 >From be5f5d2412edd7bbcf74b6bc0aecc4c13685d9ba Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Fri, 19 Sep 2025 16:53:49 +0200 Subject: [PATCH 15/18] 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" There is also a speed up with the new implementation: ~5% faster for data with restart markers ~2% faster for data without restart markers 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. --- libavcodec/mjpegdec.c | 135 +++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 73 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index c7f584707c..f8967deb5b 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -2253,94 +2253,83 @@ 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 < buf_end) { + if (*ptr++ == 0xff && 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; - } - } - } - if (src < ptr) - 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, - AV_INPUT_BUFFER_PADDING_SIZE); - - av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %"PTRDIFF_SPECIFIER" bytes\n", - (buf_end - buf_ptr) - (dst - s->buffer)); - } else { - const uint8_t *src = buf_ptr; - 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); + 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 */ break; } } } - bit_count = t * 8; - init_put_bits(&pb, dst, t); + /* Copy remaining verbatim data. */ + int length = ptr - src; + if (length > 0) + bytestream2_put_bufferu(&pb, src, length); - /* 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; + unescaped_buf_ptr = 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 %"PTRDIFF_SPECIFIER" bytes\n", + (buf_end - buf_ptr) - (unescaped_buf_size)); + } else { + const uint8_t *src = buf_ptr; + const uint8_t *ptr = src; + uint8_t *dst = s->buffer; + PutBitContext pb; + + init_put_bits(&pb, dst, (buf_end - src) * 8); + + while (ptr < buf_end) { + if (*ptr++ == 0xff && ptr < buf_end) { + /* Copy verbatim data. */ + while ((ptr - 1) > src) + put_bits(&pb, 8, *src++); + + 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 { + break; } - put_bits(&pb, 7, x); - bit_count--; } } + /* Copy remaining verbatim data. */ + while (ptr > src) + put_bits(&pb, 8, *src++); + flush_put_bits(&pb); unescaped_buf_ptr = dst; - unescaped_buf_size = (bit_count + 7) >> 3; + unescaped_buf_size = put_bits_count(&pb) >> 3; memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } -- 2.49.1 >From b42c0528822a5d34d37f300dc3e53001c15a77b3 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 24 Sep 2025 20:40:53 +0200 Subject: [PATCH 16/18] 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 | 46 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index f8967deb5b..f7e926a55d 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1799,13 +1799,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; @@ -1841,9 +1837,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); + } emms_c(); return 0; out_of_range: @@ -2236,9 +2236,34 @@ 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 < buf_end) { + if (*ptr++ == 0xff && ptr < buf_end) { + uint8_t x = *ptr++; + /* Discard multiple optional 0xFF fill bytes. */ + while (x == 0xff && ptr < buf_end) + x = *ptr++; + if ((x != 0) && (x < RST0 || x > RST7)) { + /* Non-restart marker */ + ptr -= 2; + break; + } + } + } + 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; @@ -2278,6 +2303,7 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) bytestream2_put_be16u(&pb, 0xff00 | x); } else { /* Non-restart marker */ + ptr -= 2; break; } } @@ -2292,6 +2318,8 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) 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 %"PTRDIFF_SPECIFIER" bytes\n", (buf_end - buf_ptr) - (unescaped_buf_size)); } else { @@ -2318,6 +2346,7 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) /* Stuffed zero bit */ put_bits(&pb, 15, 0x7f80 | x); } else { + ptr -= 2; break; } } @@ -2332,6 +2361,8 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) unescaped_buf_size = put_bits_count(&pb) >> 3; memset(s->buffer + unescaped_buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + bytestream2_skipu(&s->gB, ptr - buf_ptr); } the_end: @@ -2549,9 +2580,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.49.1 >From a3835f91da4bcb10e22d50fbca079aa46f65453c Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Wed, 24 Sep 2025 23:49:35 +0200 Subject: [PATCH 17/18] avcodec/mjpegdec: split jpegls codepath out of mjpeg_unescape_sos() --- libavcodec/jpeglsdec.c | 62 ++++++++++++++++++++++++++++++++++++ libavcodec/mjpegdec.c | 72 ++++++++++++++---------------------------- 2 files changed, 85 insertions(+), 49 deletions(-) diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index e506851b57..314bf5958b 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -34,6 +34,7 @@ #include "mjpegdec.h" #include "jpegls.h" #include "jpeglsdec.h" +#include "put_bits.h" /* * Uncomment this to significantly speed up decoding of broken JPEG-LS @@ -352,6 +353,62 @@ static inline int ls_decode_line(JLSState *state, MJpegDecodeContext *s, return 0; } +static int jpegls_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; + + 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 */ + const uint8_t *src = buf_ptr; + const uint8_t *ptr = src; + uint8_t *dst = s->buffer; + PutBitContext pb; + + init_put_bits(&pb, dst, (buf_end - src) * 8); + + while (ptr < buf_end) { + if (*ptr++ == 0xff && ptr < buf_end) { + /* Copy verbatim data. */ + while ((ptr - 1) > src) + put_bits(&pb, 8, *src++); + + 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 { + ptr -= 2; + break; + } + } + } + /* Copy remaining verbatim data. */ + while (ptr > src) + put_bits(&pb, 8, *src++); + + flush_put_bits(&pb); + + unescaped_buf_ptr = dst; + unescaped_buf_size = put_bits_count(&pb) >> 3; + memset(s->buffer + unescaped_buf_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + + bytestream2_skipu(&s->gB, ptr - buf_ptr); + + return init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); +} + int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, int point_transform, int ilv) { @@ -418,6 +475,11 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, 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); } + + ret = jpegls_unescape_sos(s); + if (ret < 0) + return ret; + if (get_bits_left(&s->gb) < s->height) { ret = AVERROR_INVALIDDATA; goto end; diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index f7e926a55d..5c034f7ccf 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -53,7 +53,6 @@ #include "mjpegdec.h" #include "jpeglsdec.h" #include "profiles.h" -#include "put_bits.h" static int mjpeg_unescape_sos(MJpegDecodeContext *s); @@ -1151,6 +1150,10 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int p for (i = 0; i < 4; i++) buffer[0][i] = 1 << (s->bits - 1); + ret = mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + for (mb_y = 0; mb_y < s->mb_height; mb_y++) { uint8_t *ptr = s->picture_ptr->data[0] + (linesize * mb_y); @@ -1292,6 +1295,10 @@ static int ljpeg_decode_yuv_scan(MJpegDecodeContext *s, int predictor, av_assert0(nb_components>=1 && nb_components<=4); + ret = mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + 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) { @@ -1477,6 +1484,7 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, 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 (mb_bitmask) { if (mb_bitmask_size != (s->mb_width * s->mb_height + 7)>>3) { @@ -1501,6 +1509,10 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, s->coefs_finished[c] |= 1; } + ret = mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + next_field: for (i = 0; i < nb_components; i++) s->last_dc[i] = (4 << s->bits); @@ -1614,6 +1626,7 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s, int ss, int EOBRUN = 0; int c = s->comp_index[0]; uint16_t *quant_matrix = s->quant_matrixes[s->quant_sindex[0]]; + int ret; av_assert0(ss>=0 && Ah>=0 && Al>=0); if (se < ss || se > 63) { @@ -1627,6 +1640,10 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s, int ss, s->restart_count = 0; + ret = mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + for (mb_y = 0; mb_y < s->mb_height; mb_y++) { int block_idx = mb_y * s->block_stride[c]; int16_t (*block)[64] = &s->blocks[c][block_idx]; @@ -1794,11 +1811,11 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, 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 = mjpeg_unescape_sos(s); + if (ret < 0) + return ret; + ret = FF_HW_CALL(s->avctx, decode_slice, s->raw_scan_buffer, s->raw_scan_buffer_size); @@ -2273,8 +2290,7 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) if (!s->buffer) return AVERROR(ENOMEM); - /* unescape buffer of SOS, use special treatment for JPEG-LS */ - if (!s->ls) { + /* unescape buffer of SOS */ const uint8_t *src = buf_ptr; const uint8_t *ptr = src; uint8_t *dst = s->buffer; @@ -2322,48 +2338,6 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %"PTRDIFF_SPECIFIER" bytes\n", (buf_end - buf_ptr) - (unescaped_buf_size)); - } else { - const uint8_t *src = buf_ptr; - const uint8_t *ptr = src; - uint8_t *dst = s->buffer; - PutBitContext pb; - - init_put_bits(&pb, dst, (buf_end - src) * 8); - - while (ptr < buf_end) { - if (*ptr++ == 0xff && ptr < buf_end) { - /* Copy verbatim data. */ - while ((ptr - 1) > src) - put_bits(&pb, 8, *src++); - - 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 { - ptr -= 2; - break; - } - } - } - /* Copy remaining verbatim data. */ - while (ptr > src) - put_bits(&pb, 8, *src++); - - flush_put_bits(&pb); - - unescaped_buf_ptr = dst; - unescaped_buf_size = put_bits_count(&pb) >> 3; - memset(s->buffer + unescaped_buf_size, 0, - AV_INPUT_BUFFER_PADDING_SIZE); - - bytestream2_skipu(&s->gB, ptr - buf_ptr); - } the_end: return init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); -- 2.49.1 >From fe772c8736cdde4cda4ed297e79f18b72276337b Mon Sep 17 00:00:00 2001 From: Ramiro Polla <ramiro.polla@gmail.com> Date: Mon, 29 Sep 2025 15:13:01 +0200 Subject: [PATCH 18/18] avcodec/mjpegdec: fix indentation --- libavcodec/mjpegdec.c | 134 +++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 5c034f7ccf..22748e22ae 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -1857,9 +1857,9 @@ 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); + /* 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); } emms_c(); return 0; @@ -1913,7 +1913,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s, int start_code) 4bytes field_size 4bytes field_size_less_padding */ - s->buggy_avid = 1; + s->buggy_avid = 1; i = bytestream2_get_byteu(&s->gB); len--; av_log(s->avctx, AV_LOG_DEBUG, "polarity %d\n", i); goto out; @@ -2184,35 +2184,35 @@ static int mjpeg_decode_com(MJpegDecodeContext *s) if (len <= 0) return 0; - int i; - char *cbuf = av_malloc(len + 1); - if (!cbuf) - return AVERROR(ENOMEM); + int i; + char *cbuf = av_malloc(len + 1); + if (!cbuf) + return AVERROR(ENOMEM); - for (i = 0; i < len; i++) - cbuf[i] = bytestream2_get_byteu(&s->gB); - if (cbuf[i - 1] == '\n') - cbuf[i - 1] = 0; - else - cbuf[i] = 0; + for (i = 0; i < len; i++) + cbuf[i] = bytestream2_get_byteu(&s->gB); + if (cbuf[i - 1] == '\n') + cbuf[i - 1] = 0; + else + cbuf[i] = 0; - if (s->avctx->debug & FF_DEBUG_PICT_INFO) - av_log(s->avctx, AV_LOG_INFO, "comment: '%s'\n", cbuf); + if (s->avctx->debug & FF_DEBUG_PICT_INFO) + av_log(s->avctx, AV_LOG_INFO, "comment: '%s'\n", cbuf); - /* buggy avid, it puts EOI only at every 10th frame */ - if (!strncmp(cbuf, "AVID", 4)) { - parse_avid(s, cbuf, len); - } else if (!strcmp(cbuf, "CS=ITU601")) - s->cs_itu601 = 1; - else if ((!strncmp(cbuf, "Intel(R) JPEG Library, version 1", 32) && s->avctx->codec_tag) || - (!strncmp(cbuf, "Metasoft MJPEG Codec", 20))) - s->flipped = 1; - else if (!strcmp(cbuf, "MULTISCOPE II")) { - s->avctx->sample_aspect_ratio = (AVRational) { 1, 2 }; - s->multiscope = 2; - } + /* buggy avid, it puts EOI only at every 10th frame */ + if (!strncmp(cbuf, "AVID", 4)) { + parse_avid(s, cbuf, len); + } else if (!strcmp(cbuf, "CS=ITU601")) + s->cs_itu601 = 1; + else if ((!strncmp(cbuf, "Intel(R) JPEG Library, version 1", 32) && s->avctx->codec_tag) || + (!strncmp(cbuf, "Metasoft MJPEG Codec", 20))) + s->flipped = 1; + else if (!strcmp(cbuf, "MULTISCOPE II")) { + s->avctx->sample_aspect_ratio = (AVRational) { 1, 2 }; + s->multiscope = 2; + } - av_free(cbuf); + av_free(cbuf); return 0; } @@ -2291,53 +2291,53 @@ static int mjpeg_unescape_sos(MJpegDecodeContext *s) return AVERROR(ENOMEM); /* unescape buffer of SOS */ - const uint8_t *src = buf_ptr; - const uint8_t *ptr = src; - uint8_t *dst = s->buffer; - PutByteContext pb; + const uint8_t *src = buf_ptr; + const uint8_t *ptr = src; + uint8_t *dst = s->buffer; + PutByteContext pb; - bytestream2_init_writer(&pb, dst, buf_end - src); + bytestream2_init_writer(&pb, dst, buf_end - src); - while (ptr < buf_end) { - if (*ptr++ == 0xff && ptr < buf_end) { - /* Copy verbatim data. */ - int length = (ptr - 1) - src; - if (length > 0) - bytestream2_put_bufferu(&pb, src, length); + while (ptr < buf_end) { + if (*ptr++ == 0xff && ptr < buf_end) { + /* Copy verbatim data. */ + int length = (ptr - 1) - src; + if (length > 0) + bytestream2_put_bufferu(&pb, src, length); - uint8_t x = *ptr++; - /* Discard multiple optional 0xFF fill bytes. */ - while (x == 0xff && ptr < buf_end) - x = *ptr++; + uint8_t x = *ptr++; + /* Discard multiple optional 0xFF fill bytes. */ + while (x == 0xff && ptr < buf_end) + x = *ptr++; - 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 */ - ptr -= 2; - 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 */ + ptr -= 2; + break; } } - /* Copy remaining verbatim data. */ - int length = ptr - src; - if (length > 0) - bytestream2_put_bufferu(&pb, src, length); + } + /* Copy remaining verbatim data. */ + int length = ptr - src; + if (length > 0) + bytestream2_put_bufferu(&pb, src, length); - unescaped_buf_ptr = s->buffer; - unescaped_buf_size = bytestream2_tell_p(&pb); - memset(s->buffer + unescaped_buf_size, 0, - AV_INPUT_BUFFER_PADDING_SIZE); + unescaped_buf_ptr = s->buffer; + unescaped_buf_size = bytestream2_tell_p(&pb); + memset(s->buffer + unescaped_buf_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); - bytestream2_skipu(&s->gB, ptr - buf_ptr); + bytestream2_skipu(&s->gB, ptr - buf_ptr); - av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %"PTRDIFF_SPECIFIER" bytes\n", - (buf_end - buf_ptr) - (unescaped_buf_size)); + av_log(s->avctx, AV_LOG_DEBUG, "escaping removed %"PTRDIFF_SPECIFIER" bytes\n", + (buf_end - buf_ptr) - (unescaped_buf_size)); the_end: return init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size); -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
reply other threads:[~2025-09-29 13:32 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=175915271680.25.18314306363754935782@bf249f23a2c8 \ --to=ffmpeg-devel@ffmpeg.org \ --cc=code@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror http://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ http://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git