Patches attached. - Andreas
From 90f466ce00debdd8d4d557ffcd5ec2a7065d9719 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Fri, 11 Apr 2025 22:46:57 +0200 Subject: [PATCH 01/14] avcodec/magicyuvenc: Fix setting nb_slices
Do not derive it via av_cpu_count() in case AVCodecContext.slices is unset. Instead default to AVCodecContext.thread_num instead (which is one in case frame-threading is used and gives the actual number of slice threads for slice threading). Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index bf0e7d99bb..a10da9684f 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -201,7 +201,7 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) s->planes = av_pix_fmt_count_planes(avctx->pix_fmt); - s->nb_slices = (avctx->slices <= 0) ? av_cpu_count() : avctx->slices; + s->nb_slices = avctx->slices > 0 ? avctx->slices : avctx->thread_count; s->nb_slices = FFMIN(s->nb_slices, avctx->height >> s->vshift[1]); s->nb_slices = FFMAX(1, s->nb_slices); s->slice_height = FFALIGN((avctx->height + s->nb_slices - 1) / s->nb_slices, 1 << s->vshift[1]); -- 2.45.2
From 8402cdb2e820f7a9cfe49d62d4b5db4a7070b19c Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Fri, 11 Apr 2025 23:17:24 +0200 Subject: [PATCH 02/14] fate/vcodec: Add MagicYUV tests Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- tests/fate/vcodec.mak | 9 +++++++++ tests/ref/vsynth/vsynth1-magicyuv | 4 ++++ tests/ref/vsynth/vsynth2-magicyuv | 4 ++++ tests/ref/vsynth/vsynth3-magicyuv | 4 ++++ tests/ref/vsynth/vsynth_lena-magicyuv | 4 ++++ 5 files changed, 25 insertions(+) create mode 100644 tests/ref/vsynth/vsynth1-magicyuv create mode 100644 tests/ref/vsynth/vsynth2-magicyuv create mode 100644 tests/ref/vsynth/vsynth3-magicyuv create mode 100644 tests/ref/vsynth/vsynth_lena-magicyuv diff --git a/tests/fate/vcodec.mak b/tests/fate/vcodec.mak index cafe7dfb8d..b579389b7a 100644 --- a/tests/fate/vcodec.mak +++ b/tests/fate/vcodec.mak @@ -233,6 +233,15 @@ fate-vsynth%-jpeg2000-yuva444p16: ENCOPTS = -qscale 8 -pred 1 -pix_fmt yuva4 FATE_VCODEC-$(call ENCDEC, LJPEG MJPEG, AVI) += ljpeg fate-vsynth%-ljpeg: ENCOPTS = -strict -1 +FATE_VCODEC_SCALE-$(call ENCDEC, MAGICYUV, AVI) += magicyuv +fate-vsynth1-magicyuv: ENCOPTS = -threads 7 -thread_type slice +fate-vsynth2-magicyuv: ENCOPTS = -pix_fmt gbrp -pred gradient \ + -sws_flags neighbor+bitexact +fate-vsynth3-magicyuv: ENCOPTS = -pix_fmt yuv444p -pred median \ + -sws_flags neighbor+bitexact +fate-vsynth_lena-magicyuv: ENCOPTS = -slices 3 -pix_fmt gray -pred left +fate-vsynth%-magicyuv: DECOPTS = -sws_flags neighbor+bitexact + FATE_VCODEC_SCALE-$(call ENCDEC, MJPEG, AVI) += mjpeg mjpeg-422 mjpeg-444 mjpeg-trell mjpeg-huffman mjpeg-trell-huffman fate-vsynth%-mjpeg: ENCOPTS = -qscale 9 -pix_fmt yuvj420p -huffman default -threads 5 -thread_type slice fate-vsynth%-mjpeg-422: ENCOPTS = -qscale 9 -pix_fmt yuvj422p diff --git a/tests/ref/vsynth/vsynth1-magicyuv b/tests/ref/vsynth/vsynth1-magicyuv new file mode 100644 index 0000000000..2e883bf1a0 --- /dev/null +++ b/tests/ref/vsynth/vsynth1-magicyuv @@ -0,0 +1,4 @@ +dc919cb0b3415d31fd51dfc6d7c22a6b *tests/data/fate/vsynth1-magicyuv.avi +3013600 tests/data/fate/vsynth1-magicyuv.avi +c5ccac874dbf808e9088bc3107860042 *tests/data/fate/vsynth1-magicyuv.out.rawvideo +stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth2-magicyuv b/tests/ref/vsynth/vsynth2-magicyuv new file mode 100644 index 0000000000..ea1acaf095 --- /dev/null +++ b/tests/ref/vsynth/vsynth2-magicyuv @@ -0,0 +1,4 @@ +af70e6bf02f762a3d481366df1acfd32 *tests/data/fate/vsynth2-magicyuv.avi +7672776 tests/data/fate/vsynth2-magicyuv.avi +5facc17dcac663e8d3fe73612de68ab0 *tests/data/fate/vsynth2-magicyuv.out.rawvideo +stddev: 1.24 PSNR: 46.23 MAXDIFF: 21 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth3-magicyuv b/tests/ref/vsynth/vsynth3-magicyuv new file mode 100644 index 0000000000..924a737e60 --- /dev/null +++ b/tests/ref/vsynth/vsynth3-magicyuv @@ -0,0 +1,4 @@ +380d5b8fe8f566b1e1ab48fb917f316e *tests/data/fate/vsynth3-magicyuv.avi +102448 tests/data/fate/vsynth3-magicyuv.avi +a038ad7c3c09f776304ef7accdea9c74 *tests/data/fate/vsynth3-magicyuv.out.rawvideo +stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 86700/ 86700 diff --git a/tests/ref/vsynth/vsynth_lena-magicyuv b/tests/ref/vsynth/vsynth_lena-magicyuv new file mode 100644 index 0000000000..4a9aae3989 --- /dev/null +++ b/tests/ref/vsynth/vsynth_lena-magicyuv @@ -0,0 +1,4 @@ +3f74bf17bb8d9ba2022004f95de4b666 *tests/data/fate/vsynth_lena-magicyuv.avi +3477676 tests/data/fate/vsynth_lena-magicyuv.avi +d7bfbe259af9ae323bb94b09c33570a5 *tests/data/fate/vsynth_lena-magicyuv.out.rawvideo +stddev: 18.65 PSNR: 22.72 MAXDIFF: 72 bytes: 7603200/ 7603200 -- 2.45.2
From 803b1b706a8062744f915051d04488387c334bce Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 12:54:44 +0200 Subject: [PATCH 03/14] avcodec/magicyuvenc: Restrict number of slice-planes to 256 Every frame contains an array of uint8_t with values 0..(s->planes * s->nb_slices - 1), so this needs to fit into an uint8_t. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index a10da9684f..ba39bbb89f 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -206,6 +206,7 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) s->nb_slices = FFMAX(1, s->nb_slices); s->slice_height = FFALIGN((avctx->height + s->nb_slices - 1) / s->nb_slices, 1 << s->vshift[1]); s->nb_slices = (avctx->height + s->slice_height - 1) / s->slice_height; + s->nb_slices = FFMIN(256U / s->planes, s->nb_slices); s->slices = av_calloc(s->nb_slices * s->planes, sizeof(*s->slices)); if (!s->slices) return AVERROR(ENOMEM); -- 2.45.2
From 84367759fce4c76daa056451c078ff28951eea0c Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Fri, 11 Apr 2025 23:23:56 +0200 Subject: [PATCH 04/14] avcodec/magicyuvenc: Fix Huffman element probabilities The earlier code only used the counts from the last slice. The two FATE tests using slices show compression improvements due to this. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 14 +++++++------- tests/ref/vsynth/vsynth1-magicyuv | 4 ++-- tests/ref/vsynth/vsynth_lena-magicyuv | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index ba39bbb89f..9e9c3ecd46 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -395,20 +395,20 @@ static int encode_table(AVCodecContext *avctx, PutBitContext *pb, HuffEntry *he, int plane) { MagicYUVContext *s = avctx->priv_data; - PTable counts[256] = { {0} }; + PTable counts[256]; uint16_t codes_counts[33] = { 0 }; + for (size_t i = 0; i < FF_ARRAY_ELEMS(counts); i++) { + counts[i].prob = 1; + counts[i].value = i; + } + for (int n = 0; n < s->nb_slices; n++) { Slice *sl = &s->slices[n * s->planes + plane]; PTable *slice_counts = sl->counts; for (int i = 0; i < 256; i++) - counts[i].prob = slice_counts[i].prob; - } - - for (int i = 0; i < 256; i++) { - counts[i].prob++; - counts[i].value = i; + counts[i].prob += slice_counts[i].prob; } magy_huffman_compute_bits(counts, he, codes_counts, 256, 12); diff --git a/tests/ref/vsynth/vsynth1-magicyuv b/tests/ref/vsynth/vsynth1-magicyuv index 2e883bf1a0..c67066088e 100644 --- a/tests/ref/vsynth/vsynth1-magicyuv +++ b/tests/ref/vsynth/vsynth1-magicyuv @@ -1,4 +1,4 @@ -dc919cb0b3415d31fd51dfc6d7c22a6b *tests/data/fate/vsynth1-magicyuv.avi -3013600 tests/data/fate/vsynth1-magicyuv.avi +c52dd1622adefa1d863ed3fea03b304e *tests/data/fate/vsynth1-magicyuv.avi +2972044 tests/data/fate/vsynth1-magicyuv.avi c5ccac874dbf808e9088bc3107860042 *tests/data/fate/vsynth1-magicyuv.out.rawvideo stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth_lena-magicyuv b/tests/ref/vsynth/vsynth_lena-magicyuv index 4a9aae3989..52f515bf62 100644 --- a/tests/ref/vsynth/vsynth_lena-magicyuv +++ b/tests/ref/vsynth/vsynth_lena-magicyuv @@ -1,4 +1,4 @@ -3f74bf17bb8d9ba2022004f95de4b666 *tests/data/fate/vsynth_lena-magicyuv.avi -3477676 tests/data/fate/vsynth_lena-magicyuv.avi +b7cd87cec898d66cfce82e649376bb0d *tests/data/fate/vsynth_lena-magicyuv.avi +3471504 tests/data/fate/vsynth_lena-magicyuv.avi d7bfbe259af9ae323bb94b09c33570a5 *tests/data/fate/vsynth_lena-magicyuv.out.rawvideo stddev: 18.65 PSNR: 22.72 MAXDIFF: 72 bytes: 7603200/ 7603200 -- 2.45.2
From bfa12e4d82353c2d8979f018feceb02d27d07ecc Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 05:48:47 +0200 Subject: [PATCH 05/14] avcodec/magicyuvenc: Only keep in Slice what is used Namely the number of counts. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 9e9c3ecd46..52057c6838 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -58,7 +58,7 @@ typedef struct Slice { unsigned size; uint8_t *slice; uint8_t *bitslice; - PTable counts[256]; + int64_t counts[256]; } Slice; typedef struct MagicYUVContext { @@ -288,11 +288,11 @@ static void calculate_codes(HuffEntry *he, uint16_t codes_count[33]) } static void count_usage(const uint8_t *src, int width, - int height, PTable *counts) + int height, int64_t *counts) { for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) - counts[src[i]].prob++; + counts[src[i]]++; src += width; } } @@ -378,7 +378,7 @@ static int count_plane_slice(AVCodecContext *avctx, int n, int plane) MagicYUVContext *s = avctx->priv_data; Slice *sl = &s->slices[n * s->planes + plane]; const uint8_t *dst = sl->slice; - PTable *counts = sl->counts; + int64_t *counts = sl->counts; const int slice_height = s->slice_height; const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; @@ -405,10 +405,10 @@ static int encode_table(AVCodecContext *avctx, for (int n = 0; n < s->nb_slices; n++) { Slice *sl = &s->slices[n * s->planes + plane]; - PTable *slice_counts = sl->counts; + int64_t *slice_counts = sl->counts; for (int i = 0; i < 256; i++) - counts[i].prob += slice_counts[i].prob; + counts[i].prob += slice_counts[i]; } magy_huffman_compute_bits(counts, he, codes_counts, 256, 12); -- 2.45.2
From f93fa306d52afacf44c63d2978626706c4140c00 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 06:34:22 +0200 Subject: [PATCH 06/14] avcodec/magicyuvenc: Store slice width and height Avoids rederiving it every time. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 52057c6838..1c77048021 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -54,6 +54,8 @@ typedef struct PTable { } PTable; typedef struct Slice { + int width; + int height; unsigned pos; unsigned size; uint8_t *slice; @@ -226,6 +228,10 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) for (int i = 0; i < s->planes; i++) { Slice *sl = &s->slices[n * s->planes + i]; + sl->height = n == s->nb_slices - 1 ? avctx->height - n * s->slice_height : s->slice_height; + sl->height = AV_CEIL_RSHIFT(sl->height, s->vshift[i]); + sl->width = AV_CEIL_RSHIFT(avctx->width, s->hshift[i]); + sl->bitslice = av_malloc(s->bitslice_size + AV_INPUT_BUFFER_PADDING_SIZE); sl->slice = av_malloc(avctx->width * (s->slice_height + 2) + AV_INPUT_BUFFER_PADDING_SIZE); @@ -379,14 +385,10 @@ static int count_plane_slice(AVCodecContext *avctx, int n, int plane) Slice *sl = &s->slices[n * s->planes + plane]; const uint8_t *dst = sl->slice; int64_t *counts = sl->counts; - const int slice_height = s->slice_height; - const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); - const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; memset(counts, 0, sizeof(sl->counts)); - count_usage(dst, AV_CEIL_RSHIFT(avctx->width, s->hshift[plane]), - AV_CEIL_RSHIFT(height, s->vshift[plane]), counts); + count_usage(dst, sl->width, sl->height, counts); return 0; } @@ -478,9 +480,6 @@ static int encode_slice(AVCodecContext *avctx, void *tdata, int n, int threadnr) { MagicYUVContext *s = avctx->priv_data; - const int slice_height = s->slice_height; - const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); - const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; for (int i = 0; i < s->planes; i++) { Slice *sl = &s->slices[n * s->planes + i]; @@ -489,8 +488,7 @@ static int encode_slice(AVCodecContext *avctx, void *tdata, encode_plane_slice(sl->slice, sl->bitslice, s->bitslice_size, - AV_CEIL_RSHIFT(avctx->width, s->hshift[i]), - AV_CEIL_RSHIFT(height, s->vshift[i]), + sl->width, sl->height, s->he[i], s->frame_pred); } @@ -546,8 +544,7 @@ static int predict_slice(AVCodecContext *avctx, void *tdata, s->predict(s, frame->data[i] + n * (slice_height >> s->vshift[i]) * frame->linesize[i], sl->slice, frame->linesize[i], - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(height, s->vshift[i])); + sl->width, sl->height); } } -- 2.45.2
From ad2cd38d79ab013c6ad087b3d7413b449a3b3e1a Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 06:52:13 +0200 Subject: [PATCH 07/14] avcodec/magicyuvenc: Check in advance whether to encode a slice raw Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 1c77048021..2c4c6252e4 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -56,6 +56,7 @@ typedef struct PTable { typedef struct Slice { int width; int height; + int encode_raw; unsigned pos; unsigned size; uint8_t *slice; @@ -425,7 +426,7 @@ static int encode_table(AVCodecContext *avctx, return 0; } -static int encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, unsigned dst_size, +static int encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, int width, int height, int prediction) { unsigned count = width * height; @@ -442,10 +443,9 @@ static int encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, unsigned dst return count; } -static int encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_size, - int width, int height, HuffEntry *he, int prediction) +static void encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_size, + int width, int height, HuffEntry *he, int prediction) { - const uint8_t *osrc = src; PutBitContext pb; int count; @@ -458,8 +458,6 @@ static int encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_siz for (int i = 0; i < width; i++) { const int idx = src[i]; const int len = he[idx].len; - if (put_bits_left(&pb) < len + 32) - return encode_plane_slice_raw(osrc, dst, dst_size, width, height, prediction); put_bits(&pb, len, he[idx].code); } @@ -472,8 +470,7 @@ static int encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_siz put_bits(&pb, 32 - count, 0); flush_put_bits(&pb); - - return put_bytes_output(&pb); + av_assert1(put_bytes_left(&pb, 0) == 0); } static int encode_slice(AVCodecContext *avctx, void *tdata, @@ -484,10 +481,13 @@ static int encode_slice(AVCodecContext *avctx, void *tdata, for (int i = 0; i < s->planes; i++) { Slice *sl = &s->slices[n * s->planes + i]; - sl->size = + if (sl->encode_raw) + encode_plane_slice_raw(sl->slice, sl->bitslice, + sl->width, sl->height, s->frame_pred); + else encode_plane_slice(sl->slice, sl->bitslice, - s->bitslice_size, + sl->size, sl->width, sl->height, s->he[i], s->frame_pred); } @@ -612,6 +612,20 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, tables_size = put_bytes_count(&pbit, 1); bytestream2_skip_p(&pb, tables_size); + for (int i = 0; i < s->planes; ++i) { + for (int j = 0; j < s->nb_slices; ++j) { + Slice *const sl = &s->slices[j * s->planes + i]; + int64_t size = 0; + + for (size_t k = 0; k < FF_ARRAY_ELEMS(sl->counts); ++k) + size += sl->counts[k] * s->he[i][k].len; + size = AV_CEIL_RSHIFT(size, 3); + sl->encode_raw = size >= sl->width * sl->height; + if (sl->encode_raw) + size = sl->width * sl->height; + sl->size = FFALIGN(size + 2, 4); + } + } avctx->execute2(avctx, encode_slice, NULL, NULL, s->nb_slices); for (int n = 0; n < s->nb_slices; n++) { -- 2.45.2
From dec483e107f825ca3aae4bfb7ff80bdfd8317759 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 07:08:05 +0200 Subject: [PATCH 08/14] avcodec/magicyuvenc: Avoid intermediate buffer Given that we can calculate the size of each slice in advance, we can determine the position in the output packet where it needs to be put and can therefore avoid the intermediate buffer. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 44 +++++++++++++--------------------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 2c4c6252e4..a01cafcec6 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -60,7 +60,7 @@ typedef struct Slice { unsigned pos; unsigned size; uint8_t *slice; - uint8_t *bitslice; + uint8_t *dst; int64_t counts[256]; } Slice; @@ -74,7 +74,6 @@ typedef struct MagicYUVContext { int correlate; int hshift[4]; int vshift[4]; - unsigned bitslice_size; uint8_t *decorrelate_buf[2]; Slice *slices; HuffEntry he[4][256]; @@ -224,7 +223,6 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) s->decorrelate_buf[1] = s->decorrelate_buf[0] + (s->nb_slices * s->slice_height) * aligned_width; } - s->bitslice_size = avctx->width * s->slice_height + 2; for (int n = 0; n < s->nb_slices; n++) { for (int i = 0; i < s->planes; i++) { Slice *sl = &s->slices[n * s->planes + i]; @@ -233,10 +231,9 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) sl->height = AV_CEIL_RSHIFT(sl->height, s->vshift[i]); sl->width = AV_CEIL_RSHIFT(avctx->width, s->hshift[i]); - sl->bitslice = av_malloc(s->bitslice_size + AV_INPUT_BUFFER_PADDING_SIZE); sl->slice = av_malloc(avctx->width * (s->slice_height + 2) + AV_INPUT_BUFFER_PADDING_SIZE); - if (!sl->slice || !sl->bitslice) { + if (!sl->slice) { av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer.\n"); return AVERROR(ENOMEM); } @@ -426,21 +423,16 @@ static int encode_table(AVCodecContext *avctx, return 0; } -static int encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, - int width, int height, int prediction) +static void encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, int dst_size, + int width, int height, int prediction) { unsigned count = width * height; dst[0] = 1; dst[1] = prediction; + AV_WN32(dst + dst_size - 4, 0); memcpy(dst + 2, src, count); - count += 2; - AV_WN32(dst + count, 0); - if (count & 3) - count += 4 - (count & 3); - - return count; } static void encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_size, @@ -482,11 +474,11 @@ static int encode_slice(AVCodecContext *avctx, void *tdata, Slice *sl = &s->slices[n * s->planes + i]; if (sl->encode_raw) - encode_plane_slice_raw(sl->slice, sl->bitslice, + encode_plane_slice_raw(sl->slice, sl->dst, sl->size, sl->width, sl->height, s->frame_pred); else encode_plane_slice(sl->slice, - sl->bitslice, + sl->dst, sl->size, sl->width, sl->height, s->he[i], s->frame_pred); @@ -612,32 +604,25 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, tables_size = put_bytes_count(&pbit, 1); bytestream2_skip_p(&pb, tables_size); - for (int i = 0; i < s->planes; ++i) { - for (int j = 0; j < s->nb_slices; ++j) { - Slice *const sl = &s->slices[j * s->planes + i]; + for (int i = 0; i < s->nb_slices; ++i) { + for (int j = 0; j < s->planes; ++j) { + Slice *const sl = &s->slices[i * s->planes + j]; int64_t size = 0; for (size_t k = 0; k < FF_ARRAY_ELEMS(sl->counts); ++k) - size += sl->counts[k] * s->he[i][k].len; + size += sl->counts[k] * s->he[j][k].len; size = AV_CEIL_RSHIFT(size, 3); sl->encode_raw = size >= sl->width * sl->height; if (sl->encode_raw) size = sl->width * sl->height; sl->size = FFALIGN(size + 2, 4); + sl->pos = bytestream2_tell_p(&pb); + sl->dst = pb.buffer; + bytestream2_skip_p(&pb, sl->size); } } avctx->execute2(avctx, encode_slice, NULL, NULL, s->nb_slices); - for (int n = 0; n < s->nb_slices; n++) { - for (int i = 0; i < s->planes; i++) { - Slice *sl = &s->slices[n * s->planes + i]; - - sl->pos = bytestream2_tell_p(&pb); - - bytestream2_put_buffer(&pb, sl->bitslice, sl->size); - } - } - pos = bytestream2_tell_p(&pb); bytestream2_seek_p(&pb, 32, SEEK_SET); bytestream2_put_le32(&pb, s->slices[0].pos - 32); @@ -665,7 +650,6 @@ static av_cold int magy_encode_close(AVCodecContext *avctx) Slice *sl = &s->slices[i]; av_freep(&sl->slice); - av_freep(&sl->bitslice); } av_freep(&s->slices); av_freep(&s->decorrelate_buf); -- 2.45.2
From fab94631720e6278a5cdae4791fdd1c2b41bd42b Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 07:32:12 +0200 Subject: [PATCH 09/14] avcodec/magicyuvenc: Simplify padding slice Do it before writing the actual slice to be able to use a single AV_WN32(). Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index a01cafcec6..067441f00a 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -423,14 +423,13 @@ static int encode_table(AVCodecContext *avctx, return 0; } -static void encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, int dst_size, +static void encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, int width, int height, int prediction) { unsigned count = width * height; dst[0] = 1; dst[1] = prediction; - AV_WN32(dst + dst_size - 4, 0); memcpy(dst + 2, src, count); } @@ -439,7 +438,6 @@ static void encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_si int width, int height, HuffEntry *he, int prediction) { PutBitContext pb; - int count; init_put_bits(&pb, dst, dst_size); @@ -456,13 +454,8 @@ static void encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_si src += width; } - count = put_bits_count(&pb) & 0x1F; - - if (count) - put_bits(&pb, 32 - count, 0); - flush_put_bits(&pb); - av_assert1(put_bytes_left(&pb, 0) == 0); + av_assert1(put_bytes_left(&pb, 0) <= 3); } static int encode_slice(AVCodecContext *avctx, void *tdata, @@ -473,8 +466,11 @@ static int encode_slice(AVCodecContext *avctx, void *tdata, for (int i = 0; i < s->planes; i++) { Slice *sl = &s->slices[n * s->planes + i]; + // Zero the padding now + AV_WN32(sl->dst + sl->size - 4, 0); + if (sl->encode_raw) - encode_plane_slice_raw(sl->slice, sl->dst, sl->size, + encode_plane_slice_raw(sl->slice, sl->dst, sl->width, sl->height, s->frame_pred); else encode_plane_slice(sl->slice, -- 2.45.2
From 8bf36b5b8f232900761fbf1149aeede7aaf28575 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 07:22:20 +0200 Subject: [PATCH 10/14] avcodec/magicyuvenc: Avoid PutBitContext for byte-aligned writes Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 067441f00a..071dce1be3 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -392,7 +392,7 @@ static int count_plane_slice(AVCodecContext *avctx, int n, int plane) } static int encode_table(AVCodecContext *avctx, - PutBitContext *pb, HuffEntry *he, int plane) + PutByteContext *pb, HuffEntry *he, int plane) { MagicYUVContext *s = avctx->priv_data; PTable counts[256]; @@ -416,8 +416,9 @@ static int encode_table(AVCodecContext *avctx, calculate_codes(he, codes_counts); for (int i = 0; i < 256; i++) { - put_bits(pb, 1, 0); - put_bits(pb, 7, he[i].len); + // The seven low bits are len; the top bit means the run of + // codes of this length has length one. + bytestream2_put_byte(pb, he[i].len); } return 0; @@ -548,8 +549,6 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, MagicYUVContext *s = avctx->priv_data; const int width = avctx->width, height = avctx->height; const int slice_height = s->slice_height; - unsigned tables_size; - PutBitContext pbit; PutByteContext pb; int pos, ret = 0; @@ -592,13 +591,8 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, avctx->execute2(avctx, predict_slice, (void *)frame, NULL, s->nb_slices); - init_put_bits(&pbit, pkt->data + bytestream2_tell_p(&pb), bytestream2_get_bytes_left_p(&pb)); - for (int i = 0; i < s->planes; i++) - encode_table(avctx, &pbit, s->he[i], i); - - tables_size = put_bytes_count(&pbit, 1); - bytestream2_skip_p(&pb, tables_size); + encode_table(avctx, &pb, s->he[i], i); for (int i = 0; i < s->nb_slices; ++i) { for (int j = 0; j < s->planes; ++j) { -- 2.45.2
From 35ae6dbf73ea93b52a6b96d40a30b9e5dd2a3657 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 08:27:38 +0200 Subject: [PATCH 11/14] avcodec/magicyuvenc: Calculate proper packet size in advance This can be easily done because we have a count of the number of values and the length of the associated codes. This allows to switch to ff_get_encode_buffer() and thereby avoids an implicit intermediate buffer. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 94 +++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 071dce1be3..d29379d3c1 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -391,8 +391,8 @@ static int count_plane_slice(AVCodecContext *avctx, int n, int plane) return 0; } -static int encode_table(AVCodecContext *avctx, - PutByteContext *pb, HuffEntry *he, int plane) +static void generate_codes(AVCodecContext *avctx, + HuffEntry *he, int plane) { MagicYUVContext *s = avctx->priv_data; PTable counts[256]; @@ -414,14 +414,15 @@ static int encode_table(AVCodecContext *avctx, magy_huffman_compute_bits(counts, he, codes_counts, 256, 12); calculate_codes(he, codes_counts); +} +static void output_codes(PutByteContext *pb, const HuffEntry he[256]) +{ for (int i = 0; i < 256; i++) { // The seven low bits are len; the top bit means the run of // codes of this length has length one. bytestream2_put_byte(pb, he[i].len); } - - return 0; } static void encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, @@ -547,13 +548,35 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) { MagicYUVContext *s = avctx->priv_data; - const int width = avctx->width, height = avctx->height; - const int slice_height = s->slice_height; PutByteContext pb; - int pos, ret = 0; + int header_size = 32 + (4 + 1) * (s->planes * s->nb_slices + 1) + + 256 * s->planes /* Hufftables */; + int64_t pkt_size = header_size; + int ret; + + avctx->execute2(avctx, predict_slice, (void *)frame, NULL, s->nb_slices); + + for (int i = 0; i < s->planes; i++) + generate_codes(avctx, s->he[i], i); + + for (int i = 0; i < s->nb_slices; ++i) { + for (int j = 0; j < s->planes; ++j) { + Slice *const sl = &s->slices[i * s->planes + j]; + int64_t size = 0; - ret = ff_alloc_packet(avctx, pkt, (256 + 4 * s->nb_slices + width * height) * - s->planes + 256); + for (size_t k = 0; k < FF_ARRAY_ELEMS(sl->counts); ++k) + size += sl->counts[k] * s->he[j][k].len; + size = AV_CEIL_RSHIFT(size, 3); + sl->encode_raw = size >= sl->width * sl->height; + if (sl->encode_raw) + size = sl->width * sl->height; + sl->size = FFALIGN(size + 2, 4); + sl->pos = pkt_size; + pkt_size += sl->size; + } + } + + ret = ff_get_encode_buffer(avctx, pkt, pkt_size, 0); if (ret < 0) return ret; @@ -573,13 +596,17 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytestream2_put_le32(&pb, avctx->width); bytestream2_put_le32(&pb, avctx->height); bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, slice_height); - bytestream2_put_le32(&pb, 0); + bytestream2_put_le32(&pb, s->slice_height); - for (int i = 0; i < s->planes; i++) { - bytestream2_put_le32(&pb, 0); - for (int j = 1; j < s->nb_slices; j++) - bytestream2_put_le32(&pb, 0); + // Slice position is relative to the current position (i.e. 32) + bytestream2_put_le32(&pb, header_size - 32); + + for (int i = 0; i < s->planes; ++i) { + for (int j = 0; j < s->nb_slices; ++j) { + Slice *const sl = &s->slices[j * s->planes + i]; + bytestream2_put_le32(&pb, sl->pos - 32); + sl->dst = pkt->data + sl->pos; + } } bytestream2_put_byte(&pb, s->planes); @@ -589,44 +616,11 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytestream2_put_byte(&pb, n * s->planes + i); } - avctx->execute2(avctx, predict_slice, (void *)frame, NULL, s->nb_slices); - - for (int i = 0; i < s->planes; i++) - encode_table(avctx, &pb, s->he[i], i); + for (int i = 0; i < s->planes; ++i) + output_codes(&pb, s->he[i]); - for (int i = 0; i < s->nb_slices; ++i) { - for (int j = 0; j < s->planes; ++j) { - Slice *const sl = &s->slices[i * s->planes + j]; - int64_t size = 0; - - for (size_t k = 0; k < FF_ARRAY_ELEMS(sl->counts); ++k) - size += sl->counts[k] * s->he[j][k].len; - size = AV_CEIL_RSHIFT(size, 3); - sl->encode_raw = size >= sl->width * sl->height; - if (sl->encode_raw) - size = sl->width * sl->height; - sl->size = FFALIGN(size + 2, 4); - sl->pos = bytestream2_tell_p(&pb); - sl->dst = pb.buffer; - bytestream2_skip_p(&pb, sl->size); - } - } avctx->execute2(avctx, encode_slice, NULL, NULL, s->nb_slices); - pos = bytestream2_tell_p(&pb); - bytestream2_seek_p(&pb, 32, SEEK_SET); - bytestream2_put_le32(&pb, s->slices[0].pos - 32); - for (int i = 0; i < s->planes; i++) { - for (int n = 0; n < s->nb_slices; n++) { - Slice *sl = &s->slices[n * s->planes + i]; - - bytestream2_put_le32(&pb, sl->pos - 32); - } - } - bytestream2_seek_p(&pb, pos, SEEK_SET); - - pkt->size = bytestream2_tell_p(&pb); - *got_packet = 1; return 0; -- 2.45.2
From f489f87077cdfbbc25f8cdba4cc5b45cc2ee94eb Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 08:36:31 +0200 Subject: [PATCH 12/14] avcodec/magicyuvenc: Switch to unchecked bytestream2 API We have already calculated the size of the packet and therefore don't need to rely on these implicit checks. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 74 ++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index d29379d3c1..e66d5c11cb 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -257,22 +257,22 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) } bytestream2_init_writer(&pb, avctx->extradata, MAGICYUV_EXTRADATA_SIZE); - bytestream2_put_le32(&pb, MKTAG('M', 'A', 'G', 'Y')); - bytestream2_put_le32(&pb, 32); - bytestream2_put_byte(&pb, 7); - bytestream2_put_byte(&pb, s->format); - bytestream2_put_byte(&pb, 12); - bytestream2_put_byte(&pb, 0); - - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 32); - bytestream2_put_byte(&pb, 0); - - bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, avctx->height); - bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, avctx->height); + bytestream2_put_le32u(&pb, MKTAG('M', 'A', 'G', 'Y')); + bytestream2_put_le32u(&pb, 32); + bytestream2_put_byteu(&pb, 7); + bytestream2_put_byteu(&pb, s->format); + bytestream2_put_byteu(&pb, 12); + bytestream2_put_byteu(&pb, 0); + + bytestream2_put_byteu(&pb, 0); + bytestream2_put_byteu(&pb, 0); + bytestream2_put_byteu(&pb, 32); + bytestream2_put_byteu(&pb, 0); + + bytestream2_put_le32u(&pb, avctx->width); + bytestream2_put_le32u(&pb, avctx->height); + bytestream2_put_le32u(&pb, avctx->width); + bytestream2_put_le32u(&pb, avctx->height); return 0; } @@ -421,7 +421,7 @@ static void output_codes(PutByteContext *pb, const HuffEntry he[256]) for (int i = 0; i < 256; i++) { // The seven low bits are len; the top bit means the run of // codes of this length has length one. - bytestream2_put_byte(pb, he[i].len); + bytestream2_put_byteu(pb, he[i].len); } } @@ -581,39 +581,39 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return ret; bytestream2_init_writer(&pb, pkt->data, pkt->size); - bytestream2_put_le32(&pb, MKTAG('M', 'A', 'G', 'Y')); - bytestream2_put_le32(&pb, 32); // header size - bytestream2_put_byte(&pb, 7); // version - bytestream2_put_byte(&pb, s->format); - bytestream2_put_byte(&pb, 12); // max huffman length - bytestream2_put_byte(&pb, 0); - - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 0); - bytestream2_put_byte(&pb, 32); // coder type - bytestream2_put_byte(&pb, 0); - - bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, avctx->height); - bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, s->slice_height); + bytestream2_put_le32u(&pb, MKTAG('M', 'A', 'G', 'Y')); + bytestream2_put_le32u(&pb, 32); // header size + bytestream2_put_byteu(&pb, 7); // version + bytestream2_put_byteu(&pb, s->format); + bytestream2_put_byteu(&pb, 12); // max huffman length + bytestream2_put_byteu(&pb, 0); + + bytestream2_put_byteu(&pb, 0); + bytestream2_put_byteu(&pb, 0); + bytestream2_put_byteu(&pb, 32); // coder type + bytestream2_put_byteu(&pb, 0); + + bytestream2_put_le32u(&pb, avctx->width); + bytestream2_put_le32u(&pb, avctx->height); + bytestream2_put_le32u(&pb, avctx->width); + bytestream2_put_le32u(&pb, s->slice_height); // Slice position is relative to the current position (i.e. 32) - bytestream2_put_le32(&pb, header_size - 32); + bytestream2_put_le32u(&pb, header_size - 32); for (int i = 0; i < s->planes; ++i) { for (int j = 0; j < s->nb_slices; ++j) { Slice *const sl = &s->slices[j * s->planes + i]; - bytestream2_put_le32(&pb, sl->pos - 32); + bytestream2_put_le32u(&pb, sl->pos - 32); sl->dst = pkt->data + sl->pos; } } - bytestream2_put_byte(&pb, s->planes); + bytestream2_put_byteu(&pb, s->planes); for (int i = 0; i < s->planes; i++) { for (int n = 0; n < s->nb_slices; n++) - bytestream2_put_byte(&pb, n * s->planes + i); + bytestream2_put_byteu(&pb, n * s->planes + i); } for (int i = 0; i < s->planes; ++i) -- 2.45.2
From 60215c334f10ed1cedbb2b99d44323b16f1e9f44 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 13:31:33 +0200 Subject: [PATCH 13/14] avcodec/magicyuvenc: Avoid excessive logmessages AVERROR(ENOMEM) is enough. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index e66d5c11cb..94e8d31122 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -233,10 +233,8 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) sl->slice = av_malloc(avctx->width * (s->slice_height + 2) + AV_INPUT_BUFFER_PADDING_SIZE); - if (!sl->slice) { - av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer.\n"); + if (!sl->slice) return AVERROR(ENOMEM); - } } } @@ -250,11 +248,8 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - - if (!avctx->extradata) { - av_log(avctx, AV_LOG_ERROR, "Could not allocate extradata.\n"); + if (!avctx->extradata) return AVERROR(ENOMEM); - } bytestream2_init_writer(&pb, avctx->extradata, MAGICYUV_EXTRADATA_SIZE); bytestream2_put_le32u(&pb, MKTAG('M', 'A', 'G', 'Y')); -- 2.45.2
From 25030c39b983fd82f216c0cfb8bd3ee65763c54e Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <andreas.rheinha...@outlook.com> Date: Thu, 17 Apr 2025 13:34:53 +0200 Subject: [PATCH 14/14] avcodec/magicyuvenc: Hoist check out of loop Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@outlook.com> --- libavcodec/magicyuvenc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 94e8d31122..02b570d618 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -625,12 +625,14 @@ static av_cold int magy_encode_close(AVCodecContext *avctx) { MagicYUVContext *s = avctx->priv_data; - for (int i = 0; i < s->planes * s->nb_slices && s->slices; i++) { - Slice *sl = &s->slices[i]; + if (s->slices) { + for (int i = 0; i < s->planes * s->nb_slices; i++) { + Slice *sl = &s->slices[i]; - av_freep(&sl->slice); + av_freep(&sl->slice); + } + av_freep(&s->slices); } - av_freep(&s->slices); av_freep(&s->decorrelate_buf); return 0; -- 2.45.2
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".