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]