This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

The following commit(s) were added to refs/heads/master by this push:
     new 58a2dc3c2e avutil/base64: add aarch64 NEON implementation for base64 
encode
58a2dc3c2e is described below

commit 58a2dc3c2ea31da1c73b81ea8162abdb0a00e628
Author:     Shreesh Adiga <[email protected]>
AuthorDate: Thu Jul 2 21:54:16 2026 +0530
Commit:     michaelni <[email protected]>
CommitDate: Fri Sep 18 14:20:59 2026 +0000

    avutil/base64: add aarch64 NEON implementation for base64 encode
    
    Add NEON implementation using LD3 to load 48 bytes with de-interleaving
    using which 6 bits are extracted using USHR and SLI to get 4 registers, 
which
    is used as a index into lookup table using TBL with 4 registers to translate
    into the 64-byte output, which is written using ST4 to interleave.
    Add a checkasm test to validate the input lengths from 0 to 239 bytes to 
cover
    all the code paths introduced in the NEON implementation.
    Refactored the C implementation into a header file so that it can be used
    from checkasm test without having to duplicate the code there.
    
    Observed about 4x speedup by checkasm on A720 core on 1kB input:
    C:
     - base64.base64 [OK]
    NEON:
     - base64.base64 [OK]
    checkasm: all 1 tests passed
    Benchmark results:
      name                      nsecs (vs ref)
      av_base64_encode_c:      1286.9
      av_base64_encode_neon:    315.5 ( 4.08x)
---
 libavutil/aarch64/Makefile                         |   1 +
 libavutil/aarch64/base64.S                         | 162 +++++++++++++++++++++
 .../aarch64/fdct.h => libavutil/aarch64/base64.h   |   8 +-
 libavutil/base64.c                                 |  39 ++---
 libavutil/base64_internal.h                        |  66 +++++++++
 tests/checkasm/Makefile                            |   1 +
 tests/checkasm/base64.c                            |  75 ++++++++++
 tests/checkasm/checkasm.c                          |   1 +
 tests/checkasm/checkasm.h                          |   1 +
 tests/fate/checkasm.mak                            |   1 +
 10 files changed, 323 insertions(+), 32 deletions(-)

diff --git a/libavutil/aarch64/Makefile b/libavutil/aarch64/Makefile
index ba5e0a4a6a..b286cfadb0 100644
--- a/libavutil/aarch64/Makefile
+++ b/libavutil/aarch64/Makefile
@@ -6,6 +6,7 @@ OBJS += aarch64/aes_init.o                                      
      \
 ARMV8-OBJS += aarch64/crc.o
 
 NEON-OBJS += aarch64/aes.o                                            \
+             aarch64/base64.o                                         \
              aarch64/float_dsp_neon.o                                 \
              aarch64/tx_float_neon.o                                  \
 
diff --git a/libavutil/aarch64/base64.S b/libavutil/aarch64/base64.S
new file mode 100644
index 0000000000..74cb75e21d
--- /dev/null
+++ b/libavutil/aarch64/base64.S
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2026 Shreesh Adiga <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "asm.S"
+
+function ff_base64_encode_neon, export=1
+        // x0 - char *out
+        // x1 - const char *in
+        // w2 - int in_size
+        // x3 - const char *tbl
+        uxtw            x2, w2
+        cmp             x2, #48
+        add             x4, x2, x1 // end = src + in_size
+        mov             x5, x0     // dst = out
+        b.lt            2f         // skip NEON loop
+        ld1             { v0.16b - v3.16b }, [x3]
+        movi            v4.16b, #63
+        sub             x6, x4, #48
+
+1: // NEON 48bytes loop
+        ld3             { v5.16b - v7.16b }, [x1], #48
+        ushr            v18.16b, v5.16b, #2
+        ushr            v16.16b, v6.16b, #4
+        tbl             v18.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v18.16b
+        ushr            v17.16b, v7.16b, #6
+        sli             v16.16b, v5.16b, #4
+        and             v5.16b, v7.16b, v4.16b
+        sli             v17.16b, v6.16b, #2
+        tbl             v21.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v5.16b
+        and             v16.16b, v16.16b, v4.16b
+        cmp             x1, x6
+        tbl             v19.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v16.16b
+        and             v17.16b, v17.16b, v4.16b
+        tbl             v20.16b, { v0.16b, v1.16b, v2.16b, v3.16b }, v17.16b
+        st4             { v18.16b - v21.16b }, [x5], #64
+        b.ls            1b // NEON 48bytes loop
+
+        sub             x6, x4, #24
+        cmp             x1, x6
+        b.gt            2f
+
+        // process 24 bytes using half width vector
+        ld3             { v5.8b - v7.8b }, [x1], #24
+        ushr            v18.8b, v5.8b, #2
+        ushr            v16.8b, v6.8b, #4
+        tbl             v18.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v18.8b
+        ushr            v17.8b, v7.8b, #6
+        sli             v16.8b, v5.8b, #4
+        and             v5.8b, v7.8b, v4.8b
+        sli             v17.8b, v6.8b, #2
+        tbl             v21.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v5.8b
+        and             v16.8b, v16.8b, v4.8b
+        tbl             v19.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v16.8b
+        and             v17.8b, v17.8b, v4.8b
+        tbl             v20.8b, { v0.16b, v1.16b, v2.16b, v3.16b }, v17.8b
+        st4             { v18.8b - v21.8b }, [x5], #32
+2: // skip NEON loop
+        sub             x6, x4, #4
+        cmp             x1, x6
+        b.gt            4f // <4 bytes tail
+
+3: // 4bytes available loop
+        ldr             w7, [x1], #3
+        rev             w7, w7
+        lsr             x8, x7, #26
+        ubfx            x9, x7, #20, #6
+        ldrb            w12, [x3, x8]
+        ubfx            x10, x7, #14, #6
+        ldrb            w13, [x3, x9]
+        ubfx            x11, x7, #8, #6
+        ldrb            w14, [x3, x10]
+        orr             w12, w12, w13, lsl #8
+        cmp             x1, x6
+        ldrb            w15, [x3, x11]
+        orr             w12, w12, w14, lsl #16
+        orr             w12, w12, w15, lsl #24
+        str             w12, [x5], #4
+        b.ls            3b // 4bytes available loop
+
+4: // <4 bytes tail
+        sub             x6, x4, x1
+        cmp             x6, #3
+        b.eq            7f
+        cmp             x6, #2
+        b.eq            6f
+        cmp             x6, #1
+        b.eq            5f
+        strb            wzr, [x5]
+        ret
+
+5:
+        // 1 byte input remaining, 2 bytes padding needed
+        ldrb            w7, [x1]
+        lsr             x8, x7, #2
+        ubfiz           x9, x7, #4, #2
+        ldrb            w8, [x3, x8]
+        movz            w10, #0x3d3d, lsl #16
+        ldrb            w9, [x3, x9]
+        orr             w10, w10, w8
+        orr             w10, w10, w9, lsl #8
+        str             w10, [x5]
+        strb            wzr, [x5, #4]
+        ret
+
+6:
+        // 2 bytes input remaining, 1 byte padding needed
+        ldrb            w7, [x1]
+        ldrb            w8, [x1, #1]
+        lsr             w9, w7, #2
+        lsr             w10, w8, #4
+        ubfiz           w11, w8, #2, #4
+        ldrb            w12, [x3, x9]
+        bfi             w10, w7, #4, #2
+        ldrb            w14, [x3, x11]
+        movz            w15, #0x3d00, lsl #16
+        ldrb            w13, [x3, x10]
+        orr             w15, w15, w12
+        orr             w15, w15, w14, lsl #16
+        orr             w15, w15, w13, lsl #8
+        str             w15, [x5]
+        strb            wzr, [x5, #4]
+        ret
+
+7:
+        // 3 bytes input remaining, no padding needed
+        ldrb            w7, [x1]
+        ldrb            w8, [x1, #1]
+        lsr             w10, w7, #2
+        ldrb            w9, [x1, #2]
+        lsr             w11, w8, #4
+        ldrb            w10, [x3, x10]
+        lsr             w12, w9, #6
+        bfi             w11, w7, #4, #2
+        bfi             w12, w8, #2, #4
+        ldrb            w11, [x3, x11]
+        and             w13, w9, #0x3f
+        ldrb            w12, [x3, x12]
+        orr             w10, w10, w11, lsl #8
+        ldrb            w13, [x3, x13]
+        orr             w10, w10, w12, lsl #16
+        orr             w10, w10, w13, lsl #24
+        str             w10, [x5]
+        strb            wzr, [x5, #4]
+        ret
+endfunc
diff --git a/libavcodec/aarch64/fdct.h b/libavutil/aarch64/base64.h
similarity index 80%
copy from libavcodec/aarch64/fdct.h
copy to libavutil/aarch64/base64.h
index 0901b53a83..ed91669177 100644
--- a/libavcodec/aarch64/fdct.h
+++ b/libavutil/aarch64/base64.h
@@ -16,11 +16,11 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVCODEC_AARCH64_FDCT_H
-#define AVCODEC_AARCH64_FDCT_H
+#ifndef AVUTIL_AARCH64_BASE64_H
+#define AVUTIL_AARCH64_BASE64_H
 
 #include <stdint.h>
 
-void ff_fdct_neon(int16_t *block);
+char *ff_base64_encode_neon(char *out, const uint8_t *in, int in_size, const 
char *tbl);
 
-#endif /* AVCODEC_AARCH64_FDCT_H */
+#endif /* AVUTIL_AARCH64_BASE64_H */
diff --git a/libavutil/base64.c b/libavutil/base64.c
index 69e11e6f5e..a6c21b05e7 100644
--- a/libavutil/base64.c
+++ b/libavutil/base64.c
@@ -28,8 +28,13 @@
 #include <stddef.h>
 
 #include "base64.h"
+#include "base64_internal.h"
 #include "error.h"
-#include "intreadwrite.h"
+
+#if ARCH_AARCH64
+#include "libavutil/aarch64/cpu.h"
+#include "libavutil/aarch64/base64.h"
+#endif
 
 /* ---------------- private code */
 static const uint8_t map2[256] =
@@ -148,36 +153,14 @@ char *av_base64_encode(char *out, int out_size, const 
uint8_t *in, int in_size)
 {
     static const char b64[] =
         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-    char *ret, *dst;
-    unsigned i_bits = 0;
-    int i_shift = 0;
-    int bytes_remaining = in_size;
 
     if (in_size >= UINT_MAX / 4 ||
         out_size < AV_BASE64_SIZE(in_size))
         return NULL;
-    ret = dst = out;
-    while (bytes_remaining > 3) {
-        i_bits = AV_RB32(in);
-        in += 3; bytes_remaining -= 3;
-        *dst++ = b64[ i_bits>>26        ];
-        *dst++ = b64[(i_bits>>20) & 0x3F];
-        *dst++ = b64[(i_bits>>14) & 0x3F];
-        *dst++ = b64[(i_bits>>8 ) & 0x3F];
-    }
-    i_bits = 0;
-    while (bytes_remaining) {
-        i_bits = (i_bits << 8) + *in++;
-        bytes_remaining--;
-        i_shift += 8;
+#if ARCH_AARCH64
+    if (in_size >= 48 && have_neon(av_get_cpu_flags())) {
+        return ff_base64_encode_neon(out, in, in_size, b64);
     }
-    while (i_shift > 0) {
-        *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
-        i_shift -= 6;
-    }
-    while ((dst - ret) & 3)
-        *dst++ = '=';
-    *dst = '\0';
-
-    return ret;
+#endif
+    return ff_base64_encode_c(out, in, in_size, b64);
 }
diff --git a/libavutil/base64_internal.h b/libavutil/base64_internal.h
new file mode 100644
index 0000000000..754720f9da
--- /dev/null
+++ b/libavutil/base64_internal.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006 Ryan Martell. ([email protected])
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_BASE64_INTERNAL_H
+#define AVUTIL_BASE64_INTERNAL_H
+
+#include <stdint.h>
+#include "intreadwrite.h"
+
+/*****************************************************************************
+* b64_encode: Stolen from VLC's http.c.
+* Simplified by Michael.
+* Fixed edge cases and made it work from data (vs. strings) by Ryan.
+*****************************************************************************/
+
+static inline char *ff_base64_encode_c(char *out, const uint8_t *in, int 
in_size, const char *b64)
+{
+    char *ret, *dst;
+    unsigned i_bits = 0;
+    int i_shift = 0;
+    int bytes_remaining = in_size;
+
+    ret = dst = out;
+    while (bytes_remaining > 3) {
+        i_bits = AV_RB32(in);
+        in += 3; bytes_remaining -= 3;
+        *dst++ = b64[ i_bits>>26        ];
+        *dst++ = b64[(i_bits>>20) & 0x3F];
+        *dst++ = b64[(i_bits>>14) & 0x3F];
+        *dst++ = b64[(i_bits>>8 ) & 0x3F];
+    }
+    i_bits = 0;
+    while (bytes_remaining) {
+        i_bits = (i_bits << 8) + *in++;
+        bytes_remaining--;
+        i_shift += 8;
+    }
+    while (i_shift > 0) {
+        *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
+        i_shift -= 6;
+    }
+    while ((dst - ret) & 3)
+        *dst++ = '=';
+    *dst = '\0';
+
+    return ret;
+}
+
+#endif /* AVUTIL_BASE64_INTERNAL_H */
diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile
index c5b25e753a..6b78f0d35a 100644
--- a/tests/checkasm/Makefile
+++ b/tests/checkasm/Makefile
@@ -101,6 +101,7 @@ CHECKASMOBJS-$(CONFIG_SWSCALE)  += $(SWSCALEOBJS)
 # libavutil tests
 AVUTILOBJS                              += aes.o
 AVUTILOBJS                              += av_tx.o
+AVUTILOBJS                              += base64.o
 AVUTILOBJS                              += crc.o
 AVUTILOBJS                              += fixed_dsp.o
 AVUTILOBJS                              += float_dsp.o
diff --git a/tests/checkasm/base64.c b/tests/checkasm/base64.c
new file mode 100644
index 0000000000..dd75aed9e6
--- /dev/null
+++ b/tests/checkasm/base64.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2026 Shreesh Adiga <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "checkasm.h"
+#include "libavutil/base64.h"
+#include "libavutil/base64_internal.h"
+#include "libavutil/mem_internal.h"
+#if ARCH_AARCH64
+#include "libavutil/aarch64/cpu.h"
+#include "libavutil/aarch64/base64.h"
+#endif
+#include <string.h>
+
+#define BUF_SIZE (1024)
+
+static char *(*base64_encode_get_fn(void))(char *, const uint8_t *, int, const 
char *) {
+#if ARCH_AARCH64
+    int cpu_flags = av_get_cpu_flags();
+    if (have_neon(cpu_flags))
+        return ff_base64_encode_neon;
+    else
+#endif
+        return ff_base64_encode_c;
+}
+
+static void check_base64_encode(void)
+{
+    LOCAL_ALIGNED_32(uint8_t, buf, [BUF_SIZE]);
+    LOCAL_ALIGNED_32(uint8_t, out1, [AV_BASE64_SIZE(BUF_SIZE)]);
+    LOCAL_ALIGNED_32(uint8_t, out2, [AV_BASE64_SIZE(BUF_SIZE)]);
+    static const char b64[] =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    for (int i = 0; i < BUF_SIZE; i++)
+        buf[i] = rnd();
+
+    declare_func(char *, char *out, const uint8_t *in, int in_size, const char 
*b64);
+
+    func_type *fn = base64_encode_get_fn();
+
+    if (check_func(fn, "av_base64_encode")) {
+        for (int i = 0; i < 48 * 5; i++) {
+            size_t offset = rnd() % (BUF_SIZE - i);
+            call_new(out1, i ? buf + offset : NULL, i, b64);
+            call_ref(out2, i ? buf + offset : NULL, i, b64);
+            if (memcmp(out1, out2, AV_BASE64_SIZE(i)))
+                fail();
+        }
+
+        bench_new(out1, buf, BUF_SIZE, b64);
+    }
+}
+
+void checkasm_check_base64(void)
+{
+    check_base64_encode();
+    report("base64");
+}
diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c
index f0f6f097de..5127b35729 100644
--- a/tests/checkasm/checkasm.c
+++ b/tests/checkasm/checkasm.c
@@ -303,6 +303,7 @@ static const CheckasmTest tests[] = {
 #endif
 #if CONFIG_AVUTIL
         { "aes",       checkasm_check_aes },
+        { "base64",    checkasm_check_base64 },
         { "crc",       checkasm_check_crc,   .uninit = checkasm_uninit_crc },
         { "fixed_dsp", checkasm_check_fixed_dsp },
         { "float_dsp", checkasm_check_float_dsp },
diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h
index 84fc0b0c08..3e975e54a7 100644
--- a/tests/checkasm/checkasm.h
+++ b/tests/checkasm/checkasm.h
@@ -39,6 +39,7 @@ void checkasm_check_alacdsp(void);
 void checkasm_check_apv_dsp(void);
 void checkasm_check_audiodsp(void);
 void checkasm_check_av_tx(void);
+void checkasm_check_base64(void);
 void checkasm_check_blackdetect(void);
 void checkasm_check_blend(void);
 void checkasm_check_blockdsp(void);
diff --git a/tests/fate/checkasm.mak b/tests/fate/checkasm.mak
index fad24e5861..df05224e76 100644
--- a/tests/fate/checkasm.mak
+++ b/tests/fate/checkasm.mak
@@ -8,6 +8,7 @@ FATE_CHECKASM = fate-checkasm-aacencdsp                         
        \
                 fate-checkasm-apv_dsp                                   \
                 fate-checkasm-audiodsp                                  \
                 fate-checkasm-av_tx                                     \
+                fate-checkasm-base64                                    \
                 fate-checkasm-blockdsp                                  \
                 fate-checkasm-bswapdsp                                  \
                 fate-checkasm-cavsdsp                                   \

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to