Commit 79d8801e8e for ffmpeg
commit 79d8801e8e14fedd7153be6f775a671175263c9c
Author: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Sat Oct 17 11:17:16 2026 -0700
avcodec/mjpegenc: fix restart markers for pictures one macroblock wide
The row of the restart marker was derived from the macroblock position,
as the previous row when called at the start of a new row (mb_x == 0)
and the current row at the end of a slice (mb_x == mb_width - 1).
With a single macroblock column both cases have mb_x == 0, so the end of
a slice wrote the marker of the previous row, and a marker after the
last row. Pass the row of the last coded macroblock explicitly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c
index 4305f11774..4bdeef8b66 100644
--- a/libavcodec/mjpegenc.c
+++ b/libavcodec/mjpegenc.c
@@ -233,13 +233,13 @@ static void mjpeg_build_optimal_huffman(MJpegContext *m)
* Header + values + stuffing.
*
* @param s The MPVEncContext.
+ * @param last_mb_y Row of the last coded macroblock.
* @return int Error code, 0 if successful.
*/
-int ff_mjpeg_encode_stuffing(MPVEncContext *const s)
+int ff_mjpeg_encode_stuffing(MPVEncContext *const s, int last_mb_y)
{
MJpegContext *const m = s->mjpeg_ctx;
PutBitContext *pbc = &s->pb;
- int mb_y = s->c.mb_y - !s->c.mb_x;
int ret;
#if CONFIG_MJPEG_ENCODER
@@ -273,8 +273,8 @@ int ff_mjpeg_encode_stuffing(MPVEncContext *const s)
ff_mjpeg_escape_FF(pbc, s->esc_pos);
- if (s->c.slice_context_count > 1 && mb_y < s->c.mb_height - 1)
- put_marker(pbc, RST0 + (mb_y&7));
+ if (s->c.slice_context_count > 1 && last_mb_y < s->c.mb_height - 1)
+ put_marker(pbc, RST0 + (last_mb_y & 7));
s->esc_pos = put_bytes_count(pbc, 0);
fail:
diff --git a/libavcodec/mjpegenc.h b/libavcodec/mjpegenc.h
index 865f13f50e..0db364e648 100644
--- a/libavcodec/mjpegenc.h
+++ b/libavcodec/mjpegenc.h
@@ -97,6 +97,6 @@ static inline void put_marker(PutBitContext *p, enum JpegMarker code)
typedef struct MPVEncContext MPVEncContext;
-int ff_mjpeg_encode_stuffing(MPVEncContext *s);
+int ff_mjpeg_encode_stuffing(MPVEncContext *s, int last_mb_y);
#endif /* AVCODEC_MJPEGENC_H */
diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index 8180436ed1..b160626610 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -2864,7 +2864,10 @@ static int mb_var_thread(AVCodecContext *c, void *arg){
return 0;
}
-static void write_slice_end(MPVEncContext *const s)
+/**
+ * @param last_mb_y row of the last coded macroblock
+ */
+static void write_slice_end(MPVEncContext *const s, int last_mb_y)
{
if (CONFIG_MPEG4_ENCODER && s->c.codec_id == AV_CODEC_ID_MPEG4) {
if (s->partitioned_frame)
@@ -2873,7 +2876,7 @@ static void write_slice_end(MPVEncContext *const s)
ff_mpeg4_stuffing(&s->pb);
} else if ((CONFIG_MJPEG_ENCODER || CONFIG_AMV_ENCODER) &&
s->c.out_format == FMT_MJPEG) {
- ff_mjpeg_encode_stuffing(s);
+ ff_mjpeg_encode_stuffing(s, last_mb_y);
} else if (CONFIG_SPEEDHQ_ENCODER && s->c.out_format == FMT_SPEEDHQ) {
ff_speedhq_end_slice(s);
}
@@ -3082,7 +3085,7 @@ static int encode_thread(AVCodecContext *c, void *arg){
if(is_gob_start){
if (s->c.start_mb_y != mb_y || mb_x != 0) {
- write_slice_end(s);
+ write_slice_end(s, mb_y - !mb_x);
if (CONFIG_MPEG4_ENCODER && s->c.codec_id == AV_CODEC_ID_MPEG4 && s->partitioned_frame)
ff_mpeg4_init_partitions(s);
@@ -3586,7 +3589,7 @@ static int encode_thread(AVCodecContext *c, void *arg){
ff_msmpeg4_encode_ext_header(s);
#endif
- write_slice_end(s);
+ write_slice_end(s, s->c.mb_y);
return 0;
}