PR #23216 opened by abrasive URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23216 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23216.patch
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 78cb4ab9cac744bfe5e2033fe0bae2d021d09f9f 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 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 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; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
