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]

Reply via email to