PR #25195 opened by thardin URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25195 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25195.patch
This is essentially #23216 but with FATE changes applied. Not sure if this is the best way to go about fixing someone else's PR. Original commit message below: --- The Saturn Cinepak libraries require that all chunks start on a 32-bit boundary, presumably for performance reasons: the SH-2 processor used doesn't support unaligned 32-bit or 16-bit loads. We can easily achieve this by padding all chunks to a multiple of 4 bytes using zeroes. This is not gated behind an option as the overhead is negligible (eg. ~0.02% for a 3-minute, 320x224 video at -q:v 50) >From 578a4cda6272ebd9bb3cb3581df36f247fe96166 Mon Sep 17 00:00:00 2001 From: abrasive <[email protected]> Date: Sun, 24 May 2026 12:48:37 +1000 Subject: [PATCH] avcodec/cinepakenc: align for vintage Sega Saturn decoder The Saturn Cinepak libraries require that all chunks start on a 32-bit boundary, presumably for performance reasons: the SH-2 processor used doesn't support unaligned 32-bit or 16-bit loads. We can easily achieve this by padding all chunks to a multiple of 4 bytes using zeroes. This is not gated behind an option as the overhead is negligible (eg. ~0.02% for a 3-minute, 320x224 video at -q:v 50) --- libavcodec/cinepakenc.c | 30 ++++++++++++++++++++++++---- tests/ref/vsynth/vsynth1-cinepak | 4 ++-- tests/ref/vsynth/vsynth2-cinepak | 4 ++-- tests/ref/vsynth/vsynth_lena-cinepak | 4 ++-- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c index 104a9f485e..bfc1df9bce 100644 --- a/libavcodec/cinepakenc.c +++ b/libavcodec/cinepakenc.c @@ -379,20 +379,30 @@ static int write_chunk_header(unsigned char *buf, int chunk_type, int chunk_size return CHUNK_HEADER_SIZE; } +static int align_longword(int size) +{ + // Sega Saturn vintage decoder requires that all chunks start on a longword (4 + // byte) boundary, as it can't do unaligned 32-bit or 16-bit loads. Making + // sure all chunks are a multiple of 4 bytes long achieves this + return (size + 3) & ~3; +} + static int encode_codebook(CinepakEncContext *s, int *codebook, int size, int chunk_type_yuv, int chunk_type_gray, unsigned char *buf) { - int x, y, ret, entry_size = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4; + int x, y, ret, chunk_size, entry_size = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4; int incremental_codebook_replacement_mode = 0; // hardcoded here, // the compiler should notice that this is a constant -- rl + chunk_size = align_longword(entry_size * size + + (incremental_codebook_replacement_mode ? (size + 31) / 32 * 4 : 0)); + ret = write_chunk_header(buf, s->pix_fmt == AV_PIX_FMT_RGB24 ? chunk_type_yuv + (incremental_codebook_replacement_mode ? 1 : 0) : chunk_type_gray + (incremental_codebook_replacement_mode ? 1 : 0), - entry_size * size + - (incremental_codebook_replacement_mode ? (size + 31) / 32 * 4 : 0)); + chunk_size); // we do codebook encoding according to the "intra" mode // but we keep the "dead" code for reference in case we will want @@ -423,6 +433,9 @@ static int encode_codebook(CinepakEncContext *s, int *codebook, int size, for (y = 0; y < entry_size; y++) buf[ret++] = codebook[y + x * entry_size] ^ (y >= 4 ? 0x80 : 0); + while (ret & 3) + buf[ret++] = 0; + return ret; } @@ -568,11 +581,14 @@ static int encode_mode(CinepakEncContext *s, int h, switch (info->mode) { case MODE_V1_ONLY: - ret += write_chunk_header(buf + ret, 0x32, mb_count); + ret += write_chunk_header(buf + ret, 0x32, align_longword(mb_count)); for (x = 0; x < mb_count; x++) buf[ret++] = s->mb[x].v1_vector; + while (ret & 3) + buf[ret++] = 0; + break; case MODE_V1_V4: // remember header position @@ -599,6 +615,9 @@ static int encode_mode(CinepakEncContext *s, int h, } } + while (ret & 3) + buf[ret++] = 0; + write_chunk_header(buf + header_ofs, 0x30, ret - header_ofs - CHUNK_HEADER_SIZE); break; @@ -659,6 +678,9 @@ static int encode_mode(CinepakEncContext *s, int h, ret += temp_size; } + while (ret & 3) + buf[ret++] = 0; + write_chunk_header(buf + header_ofs, 0x31, ret - header_ofs - CHUNK_HEADER_SIZE); break; diff --git a/tests/ref/vsynth/vsynth1-cinepak b/tests/ref/vsynth/vsynth1-cinepak index 7cb33220e7..66469b4c78 100644 --- a/tests/ref/vsynth/vsynth1-cinepak +++ b/tests/ref/vsynth/vsynth1-cinepak @@ -1,4 +1,4 @@ -6b9b4d2b32854fd8b0f84ee4d76b1fed *tests/data/fate/vsynth1-cinepak.avi -408910 tests/data/fate/vsynth1-cinepak.avi +9ac6a985c698a34f6d9e6251324a6bfb *tests/data/fate/vsynth1-cinepak.avi +409272 tests/data/fate/vsynth1-cinepak.avi 4acdcc3e664d12facbcefc389079cfa6 *tests/data/fate/vsynth1-cinepak.out.rawvideo stddev: 61.38 PSNR: 12.37 MAXDIFF: 223 bytes: 7603200/ 921600 diff --git a/tests/ref/vsynth/vsynth2-cinepak b/tests/ref/vsynth/vsynth2-cinepak index 72b8794116..8a420e916c 100644 --- a/tests/ref/vsynth/vsynth2-cinepak +++ b/tests/ref/vsynth/vsynth2-cinepak @@ -1,4 +1,4 @@ -675d6a4370c315dc53c219a9271be4c6 *tests/data/fate/vsynth2-cinepak.avi -400720 tests/data/fate/vsynth2-cinepak.avi +d7420dd4f4656006f2491626bf225655 *tests/data/fate/vsynth2-cinepak.avi +401072 tests/data/fate/vsynth2-cinepak.avi cbf6900b882b484bc531f9b4799c226e *tests/data/fate/vsynth2-cinepak.out.rawvideo stddev: 80.96 PSNR: 9.96 MAXDIFF: 229 bytes: 7603200/ 921600 diff --git a/tests/ref/vsynth/vsynth_lena-cinepak b/tests/ref/vsynth/vsynth_lena-cinepak index c9cb2cfaba..4d9c5ccbcb 100644 --- a/tests/ref/vsynth/vsynth_lena-cinepak +++ b/tests/ref/vsynth/vsynth_lena-cinepak @@ -1,4 +1,4 @@ -a79f907fecb95c8a5b5de670ccda215b *tests/data/fate/vsynth_lena-cinepak.avi -407952 tests/data/fate/vsynth_lena-cinepak.avi +c6ab0e311006ab71d1fea084a4ece5db *tests/data/fate/vsynth_lena-cinepak.avi +408304 tests/data/fate/vsynth_lena-cinepak.avi 2664cdff5f14508e891477b498cc840e *tests/data/fate/vsynth_lena-cinepak.out.rawvideo stddev: 58.10 PSNR: 12.85 MAXDIFF: 185 bytes: 7603200/ 921600 -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
