Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/audio/ffmpeg_dec/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ else()
else()
add_local_sources(sof ffmpeg_dec-ffmpeg.c)
add_local_sources(sof ffmpeg_dec-convert.c)
add_local_sources(sof ffmpeg-convert-hifi.c)
if(CONFIG_FFMPEG_ENC)
add_local_sources(sof ffmpeg-encode.c)
endif()
Expand All @@ -33,6 +34,9 @@ else()
if(CONFIG_FFMPEG_ENC_AAC)
add_local_sources(sof ffmpeg-enc-aac.c)
endif()
if(CONFIG_FFMPEG_FILTER)
add_local_sources(sof ffmpeg-filter.c)
endif()
# Scalar soft-float for the SOF glue that touches float. core-isa.h reports
# XCHAL_HAVE_FP=1 so LLVM would lower the FLT/FLTP -> Q1.31 sample conversion
# in ffmpeg_dec_emit_frame() to hardware FP0 ops (lsi/mul.s/trunc.s), which
Expand All @@ -47,7 +51,9 @@ else()
set(_ffmpeg_dec_sources
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec.c
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-ffmpeg.c
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-convert.c)
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-convert.c
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg-convert-hifi.c
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-builtin-libc.c)
if(CONFIG_FFMPEG_ENC)
list(APPEND _ffmpeg_dec_sources ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg-encode.c)
endif()
Expand All @@ -57,6 +63,9 @@ else()
if(CONFIG_FFMPEG_ENC_AAC)
list(APPEND _ffmpeg_dec_sources ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg-enc-aac.c)
endif()
if(CONFIG_FFMPEG_FILTER)
list(APPEND _ffmpeg_dec_sources ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg-filter.c)
endif()
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set_source_files_properties(
${_ffmpeg_dec_sources}
Expand Down Expand Up @@ -103,6 +112,9 @@ else()
# stubs in ffmpeg_dec-builtin-libc.c). FLAC is integer-only so it never
# calls libm at runtime, but the references still resolve to real kernels
# now, and lossy decoders (AAC/Opus/Vorbis) need them to decode correctly.
if(CONFIG_FFMPEG_BUILD_AVFILTER)
target_link_libraries(SOF INTERFACE avfilter)
endif()
target_link_libraries(SOF INTERFACE avcodec swresample avutil)
# Encode mode: libshine MP3 encoder archive (built by ffmpeg.cmake
# section 3b when FFMPEG_ENC_MP3 is set). Linked AFTER avcodec,
Expand Down
58 changes: 56 additions & 2 deletions src/audio/ffmpeg_dec/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,65 @@ config FFMPEG_ENC
bool
default y if FFMPEG_ENC_MP3 || FFMPEG_ENC_AAC

# Selected automatically when a decoder that needs IEEE float math is
comment "FFmpeg audio filters to build (libavfilter)"
depends on IPC_MAJOR_4

config FFMPEG_FILTER_AFFTDN
bool "FFT noise reduction (afftdn)"
depends on IPC_MAJOR_4
help
FFT-based audio denoiser (libavfilter af_afftdn). Builds libavfilter
with abuffer, abuffersink, aformat and afftdn, plus the avfilter-graph
runtime path (ffmpeg-filter.c).

config FFMPEG_FILTER_ALIMITER
bool "Lookahead peak limiter (alimiter)"
depends on IPC_MAJOR_4
help
Lookahead brickwall peak limiter (libavfilter af_alimiter). Prevents
digital clipping and provides speaker protection. Builds libavfilter
with abuffer, abuffersink, aformat and alimiter, plus the avfilter-graph
runtime path (ffmpeg-filter.c).

# Turns on libavfilter in the FFmpeg cross-build when any filter is selected.
config FFMPEG_BUILD_AVFILTER
bool
default y if FFMPEG_FILTER_AFFTDN || FFMPEG_FILTER_ALIMITER

# Gates compilation of the runtime avfilter-graph path (ffmpeg-filter.c).
config FFMPEG_FILTER
bool
default y if FFMPEG_BUILD_AVFILTER

choice
prompt "Default FFmpeg audio filter"
depends on FFMPEG_FILTER
default FFMPEG_FILTER_DEFAULT_AFFTDN if FFMPEG_FILTER_AFFTDN
default FFMPEG_FILTER_DEFAULT_ALIMITER if FFMPEG_FILTER_ALIMITER

config FFMPEG_FILTER_DEFAULT_AFFTDN
bool "FFT noise reduction (afftdn)"
depends on FFMPEG_FILTER_AFFTDN
help
Select afftdn (FFT noise reduction) as the default libavfilter audio
filter when no explicit filter profile or runtime configuration is
supplied.

config FFMPEG_FILTER_DEFAULT_ALIMITER
bool "Lookahead peak limiter (alimiter)"
depends on FFMPEG_FILTER_ALIMITER
help
Select alimiter (lookahead brickwall peak limiter) as the default
libavfilter audio filter when no explicit filter profile or runtime
configuration is supplied.

endchoice

# Selected automatically when a decoder or filter that needs IEEE float math is
# built. Gates compilation of the fast single-precision libm (fastmathf.c).
config FFMPEG_DEC_FLOAT_MATH
bool
default y if FFMPEG_DEC_AAC || FFMPEG_DEC_OPUS || FFMPEG_DEC_MP3
default y if FFMPEG_DEC_AAC || FFMPEG_DEC_OPUS || FFMPEG_DEC_MP3 || FFMPEG_BUILD_AVFILTER

config FFMPEG_DEC_COLD_SPLIT
bool "Place cold FFmpeg code (av_cold init/setup) in DRAM [EXPERIMENTAL]"
Expand Down
158 changes: 158 additions & 0 deletions src/audio/ffmpeg_dec/ffmpeg-convert-hifi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2026 Intel Corporation.
//
// Xtensa HiFi3 / HiFi4 / HiFi5 VFPU SIMD routines for FFmpeg PCM format
// conversion and interleaving.
//
// Each helper processes the SIMD-aligned prefix of @nb_samples and returns the
// number of samples per channel converted (0 when the target core or compiler
// does not provide the required TIE intrinsics). The caller in
// ffmpeg_dec-convert.c finishes any remaining tail samples in scalar C.

#include <stdint.h>
#include "ffmpeg_dec-convert.h"

#if defined(__XTENSA__) && defined(__has_include)
#if __has_include(<xtensa/config/core-isa.h>)
#include <xtensa/config/core-isa.h>
#endif

#if defined(__has_builtin) && __has_builtin(__builtin_xtensa_trunc_sx2) && \
__has_builtin(__builtin_xtensa_float_sx2) && \
defined(XCHAL_HAVE_HIFI4_VFPU) && XCHAL_HAVE_HIFI4_VFPU && \
__has_include(<xtensahifiintrin.h>)
#include <xtensahifiintrin.h>
#define FFMPEG_DEC_CONV_VFPU 1
#endif

#if defined(XCHAL_HAVE_HIFI3) && XCHAL_HAVE_HIFI3 && __has_include(<xtensa/tie/xt_hifi3.h>)
#include <xtensa/tie/xt_hifi3.h>
#define FFMPEG_DEC_CONV_HIFI3 1
#endif
#endif

int ffmpeg_hifi_fltp_to_s32(const float *const *src, int32_t *dst,
int channels, int nb_samples)
{
#if defined(FFMPEG_DEC_CONV_VFPU)
int i = 0;

if (channels == 1) {
const float *c0 = src[0];

for (; i + 2 <= nb_samples; i += 2) {
int32_t tmp[2] __attribute__((aligned(8)));
ae_int32x2 f = AE_L32X2_I((const ae_int32x2 *)&c0[i], 0);
ae_int32x2 q = XT_TRUNC_SX2((ae_xtfloatx2)f, 31);

AE_S32X2_I(q, (ae_int32x2 *)tmp, 0);
dst[i + 0] = tmp[0];
dst[i + 1] = tmp[1];
}
return i;
}

if (channels == 2) {
const float *c0 = src[0];
const float *c1 = src[1];

for (; i + 2 <= nb_samples; i += 2) {
ae_int32x2 f0 = AE_L32X2_I((const ae_int32x2 *)&c0[i], 0);
ae_int32x2 f1 = AE_L32X2_I((const ae_int32x2 *)&c1[i], 0);
ae_int32x2 q0 = XT_TRUNC_SX2((ae_xtfloatx2)f0, 31);
ae_int32x2 q1 = XT_TRUNC_SX2((ae_xtfloatx2)f1, 31);
int32_t tmp0[2] __attribute__((aligned(8)));
int32_t tmp1[2] __attribute__((aligned(8)));

AE_S32X2_I(q0, tmp0, 0);
AE_S32X2_I(q1, tmp1, 0);

dst[2 * i + 0] = tmp0[0];
dst[2 * i + 1] = tmp1[0];
dst[2 * i + 2] = tmp0[1];
dst[2 * i + 3] = tmp1[1];
}
return i;
}

for (int c = 0; c < channels; c++) {
const float *pl = src[c];
int32_t *dc = dst + c;

for (i = 0; i + 2 <= nb_samples; i += 2) {
int32_t tmp[2] __attribute__((aligned(8)));
ae_int32x2 f = AE_L32X2_I((const ae_int32x2 *)&pl[i], 0);
ae_int32x2 q = XT_TRUNC_SX2((ae_xtfloatx2)f, 31);

AE_S32X2_I(q, (ae_int32x2 *)tmp, 0);
dc[i * channels] = tmp[0];
dc[(i + 1) * channels] = tmp[1];
}
}
return nb_samples & ~1;
#else
(void)src;
(void)dst;
(void)channels;
(void)nb_samples;
return 0;
#endif
}

int ffmpeg_hifi_s32_to_fltp(const int32_t *src, float *const *dst,
int channels, int nb_samples)
{
#if defined(FFMPEG_DEC_CONV_VFPU)
int i = 0;

for (int c = 0; c < channels; c++) {
float *pl = dst[c];
const int32_t *sc = src + c;

for (i = 0; i + 2 <= nb_samples; i += 2) {
int32_t tmp[2] __attribute__((aligned(8)));
ae_int32x2 p;
ae_xtfloatx2 r;

tmp[0] = sc[i * channels];
tmp[1] = sc[(i + 1) * channels];
p = AE_L32X2_I((const ae_int32x2 *)tmp, 0);
r = XT_FLOAT_SX2(p, 31);
AE_S32X2_I((ae_int32x2)r, (ae_int32x2 *)&pl[i], 0);
}
}
return nb_samples & ~1;
#else
(void)src;
(void)dst;
(void)channels;
(void)nb_samples;
return 0;
#endif
}

int ffmpeg_hifi_s32p_to_s32_stereo(const int32_t *c0, const int32_t *c1,
int32_t *dst, int nb_samples)
{
#if defined(FFMPEG_DEC_CONV_HIFI3)
int i = 0;

for (; i + 2 <= nb_samples; i += 2) {
ae_int32x2 v0 = AE_L32X2_I((const ae_int32x2 *)&c0[i], 0);
ae_int32x2 v1 = AE_L32X2_I((const ae_int32x2 *)&c1[i], 0);
ae_int32x2 pair0 = AE_SEL32_LL(v0, v1);
ae_int32x2 pair1 = AE_SEL32_HH(v0, v1);

AE_S32X2_I(pair0, (ae_int32x2 *)&dst[2 * i + 0], 0);
AE_S32X2_I(pair1, (ae_int32x2 *)&dst[2 * i + 2], 0);
}
return i;
#else
(void)c0;
(void)c1;
(void)dst;
(void)nb_samples;
return 0;
#endif
}
Loading
Loading