Skip to content
Draft
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
21 changes: 21 additions & 0 deletions src/audio/ffmpeg_dec/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ else()
else()
add_local_sources(sof ffmpeg_dec-ffmpeg.c)
add_local_sources(sof ffmpeg_dec-convert.c)
if(CONFIG_FFMPEG_DEC_ENCODE_MODE)
add_local_sources(sof ffmpeg_dec-encode.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 @@ -39,6 +42,9 @@ else()
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec.c
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-ffmpeg.c
${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-convert.c)
if(CONFIG_FFMPEG_DEC_ENCODE_MODE)
list(APPEND _ffmpeg_dec_sources ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg_dec-encode.c)
endif()
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set_source_files_properties(
${_ffmpeg_dec_sources}
Expand Down Expand Up @@ -86,6 +92,21 @@ else()
# calls libm at runtime, but the references still resolve to real kernels
# now, and lossy decoders (AAC/Opus/Vorbis) need them to decode correctly.
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,
# which references its shine_* symbols.
if(CONFIG_FFMPEG_ENC_MP3)
target_link_directories(SOF INTERFACE ${SHINE_INSTALL}/lib)
target_link_libraries(SOF INTERFACE shine)
add_dependencies(modules_sof shine_ext)
endif()
if(CONFIG_FFMPEG_ENC_AAC)
target_link_directories(SOF INTERFACE ${VO_AACENC_INSTALL}/lib)
target_link_libraries(SOF INTERFACE vo-aacenc)
target_include_directories(SOF INTERFACE ${VO_AACENC_INSTALL}/include)
zephyr_include_directories(${VO_AACENC_INSTALL}/include)
add_dependencies(modules_sof vo_aacenc_ext)
endif()
# libm (newlib double math) LAST so libav*/libshine references
# (modf/cos/sin/log/sqrt/exp2) resolve against it.
target_link_libraries(SOF INTERFACE ${FFMPEG_TARGET_LIBM})
Expand Down
36 changes: 36 additions & 0 deletions src/audio/ffmpeg_dec/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,42 @@ config FFMPEG_DEC_MP3
MPEG-1/2 Layer III decoder. Enables --enable-decoder=mp3 and the
mpegaudio parser in the libavcodec build. Uses the float math layer.

config FFMPEG_ENC_MP3
bool "MP3 encoder (libshine, fixed-point)"
help
MPEG-1 Layer III encoder via libshine, a small fixed-point encoder
(west-pinned; FFmpeg has no native MP3 encoder). Cross-builds libshine
and enables --enable-libshine --enable-encoder=libshine. Fixed-point, so
no float math dependency. The encoder is built into the archive; a module
encode path (PCM->MP3) is a separate build mode.

config FFMPEG_ENC_AAC
bool "AAC-LC encoder (vo-aacenc, fixed-point)"
help
MPEG-4 AAC Low Complexity encoder via vo-aacenc, a lightweight 32-bit
fixed-point integer encoder (west-pinned). Pure integer fixed-point, no
float math dependency. Output is a standard ADTS AAC elementary stream.

config FFMPEG_DEC_ENCODE_MODE
bool "Build as an audio encoder (PCM -> compressed) instead of a decoder [EXPERIMENTAL]"
help
Build the module as an encoder: PCM in, compressed elementary stream out
(AAC-LC via vo-aacenc or MP3 via libshine).

choice
prompt "Default encoder for encode mode"
depends on FFMPEG_DEC_ENCODE_MODE
default FFMPEG_ENC_DEFAULT_AAC

config FFMPEG_ENC_DEFAULT_AAC
bool "AAC-LC (vo-aacenc, fixed-point)"
select FFMPEG_ENC_AAC

config FFMPEG_ENC_DEFAULT_MP3
bool "MP3 (libshine, fixed-point)"
select FFMPEG_ENC_MP3
endchoice

# Selected automatically when a decoder that needs IEEE float math is
# built. Gates compilation of the fast single-precision libm (fastmathf.c).
config FFMPEG_DEC_FLOAT_MATH
Expand Down
151 changes: 148 additions & 3 deletions src/audio/ffmpeg_dec/ffmpeg.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ if(CONFIG_FFMPEG_DEC_MP3)
list(APPEND _ff_decoders mp3)
list(APPEND _ff_parsers mpegaudio)
endif()
# A decoder is required.
if(NOT _ff_decoders)
message(FATAL_ERROR "ffmpeg_dec: no decoder selected (Kconfig FFMPEG_DEC_*)")
# A decoder is required unless this is an encoder-only build.
if(NOT _ff_decoders AND NOT CONFIG_FFMPEG_ENC_MP3 AND NOT CONFIG_FFMPEG_ENC_AAC)
message(FATAL_ERROR "ffmpeg_dec: no decoder or encoder selected (Kconfig FFMPEG_DEC_*/FFMPEG_ENC_*)")
endif()
set(_ff_dec_cfg "")
if(_ff_decoders)
Expand Down Expand Up @@ -348,7 +348,150 @@ if(CONFIG_FFMPEG_DEC_COLD_SPLIT)
${FFMPEG_INSTALL_DIR}/lib/libswresample.a)
endif()

# --- 3b. MP3 encoder via libshine (fixed-point; FFmpeg has no native one) ---
set(_ff_cfg_env "PATH=${_tc_dir}:$ENV{PATH}")
set(_ff_shine_cfg "")
set(_ff_shine_dep "")
if(CONFIG_FFMPEG_ENC_MP3)
set(SHINE_SRC "${sof_top_dir}/../modules/audio/shine")
if(NOT EXISTS "${SHINE_SRC}/src/lib/layer3.h")
message(FATAL_ERROR "ffmpeg_dec: libshine source not at '${SHINE_SRC}'; run 'west update'")
endif()
set(SHINE_INSTALL "${CMAKE_CURRENT_BINARY_DIR}/shine-install")
file(MAKE_DIRECTORY "${SHINE_INSTALL}/lib/pkgconfig" "${SHINE_INSTALL}/include/shine")
# pkg-config file for FFmpeg's require_pkg_config libshine.
file(WRITE "${SHINE_INSTALL}/lib/pkgconfig/shine.pc"
"prefix=${SHINE_INSTALL}
libdir=\${prefix}/lib
includedir=\${prefix}/include
Name: shine
Description: Shine fixed-point MP3 encoder
Version: 3.1.1
Libs: -L\${libdir} -lshine ${CMAKE_CURRENT_BINARY_DIR}/shine_cfgstub.o
Cflags: -I\${includedir}
")
# FFmpeg's -lshine link *test* pulls newlib malloc -> Zephyr runtime syms
# (z_errno_wrap, ...) that only exist at module load. Provide dummies so the
# configure test links; --extra-ldflags only affects tests, not the .a build.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/shine_cfgstub.c"
"int z_errno_wrap(void){return 0;}
void *stdout;
int open(const char *p, int f, ...){return -1;}
void *sbrk(int i){return (void *)-1;}
")
# libshine's lib needs no config.h; build the objects directly and archive
# (its autotools CLI/shared link cannot work bare-metal).
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/shine-build.sh"
"#!/bin/sh
set -e
export PATH=${_tc_dir}:\$PATH
CC=\"${_ff_cc}\"
mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/shine-obj
for f in ${SHINE_SRC}/src/lib/*.c; do
\$CC -O2 -fPIC -mlongcalls -mtext-section-literals -DSHINE_HAVE_BSWAP_H \\
-I${SHINE_SRC}/src/lib -c \"\$f\" \\
-o ${CMAKE_CURRENT_BINARY_DIR}/shine-obj/\$(basename \"\$f\").o
done
${_ff_cross_prefix}ar rcs ${SHINE_INSTALL}/lib/libshine.a ${CMAKE_CURRENT_BINARY_DIR}/shine-obj/*.o
cp ${SHINE_SRC}/src/lib/layer3.h ${SHINE_INSTALL}/include/shine/layer3.h
\$CC -fPIC -c ${CMAKE_CURRENT_BINARY_DIR}/shine_cfgstub.c \\
-o ${CMAKE_CURRENT_BINARY_DIR}/shine_cfgstub.o
")
add_custom_command(
OUTPUT "${SHINE_INSTALL}/lib/libshine.a" "${CMAKE_CURRENT_BINARY_DIR}/shine_cfgstub.o"
COMMAND sh "${CMAKE_CURRENT_BINARY_DIR}/shine-build.sh"
VERBATIM)
Comment on lines +400 to +403
add_custom_target(shine_ext DEPENDS "${SHINE_INSTALL}/lib/libshine.a")

set(_ff_cfg_env "PATH=${_tc_dir}:$ENV{PATH}" "PKG_CONFIG_PATH=${SHINE_INSTALL}/lib/pkgconfig")
# NOTE: the cfgstub is scoped to libshine's OWN detection via shine.pc
# Libs (as an object), NOT a global --extra-ldflags. A global stub makes
# FFmpeg's generic check_func tests (posix_memalign/strerror_r/iconv)
# link against sbrk and false-positive HAVE_*=1, emitting libc calls that
# do not exist in the =y newlib env -> final firmware link fails. Scoping
# keeps this build's FFmpeg config identical to the decoder builds.
# --disable-iconv guards the one live check (iconv, probed after libshine).
set(_ff_shine_cfg --enable-libshine --enable-encoder=libshine --pkg-config=pkg-config
--disable-iconv
# The precomputed shine init tables (shine_precomp.h) add ~52KB of
# .rodata to libshine. FFmpeg's bare-metal -lshine configure *probe*
# links against the default GNU linker script whose 'flash' region is
# only 64KB, so the probe overflows ("region flash overflowed"). Grow
# the probe's flash region via --defsym. This affects configure test
# links ONLY (--disable-programs builds .a archives, no real link) and
# provides no symbols, so unlike a global libc stub it cannot trigger
# false-positive HAVE_* detection.
"--extra-ldflags=-Wl,--defsym,__flash_size=0x400000")
set(_ff_shine_dep shine_ext)
message(STATUS "ffmpeg_dec: enabling MP3 encoder via libshine (${SHINE_SRC})")
endif()

# --- 3c. AAC encoder via vo-aacenc (fixed-point integer AAC-LC) ---
set(_ff_vo_aacenc_dep "")
if(CONFIG_FFMPEG_ENC_AAC)
set(VO_AACENC_SRC "${sof_top_dir}/../modules/audio/vo-aacenc")
if(NOT EXISTS "${VO_AACENC_SRC}/common/include/voAAC.h")
message(FATAL_ERROR "ffmpeg_dec: vo-aacenc source not at '${VO_AACENC_SRC}'; run 'west update'")
endif()
set(VO_AACENC_INSTALL "${CMAKE_CURRENT_BINARY_DIR}/vo-aacenc-install")
file(MAKE_DIRECTORY "${VO_AACENC_INSTALL}/lib" "${VO_AACENC_INSTALL}/include/vo-aacenc")

file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/vo-aacenc-build.sh"
"#!/bin/sh
set -e
export PATH=${_tc_dir}:\$PATH
CC=\"${_ff_cc}\"
mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/vo-aacenc-obj
SRCS=\"\
${VO_AACENC_SRC}/common/cmnMemory.c \
${VO_AACENC_SRC}/aacenc/basic_op/basicop2.c \
${VO_AACENC_SRC}/aacenc/basic_op/oper_32b.c \
${VO_AACENC_SRC}/aacenc/src/aac_rom.c \
${VO_AACENC_SRC}/aacenc/src/aacenc.c \
${VO_AACENC_SRC}/aacenc/src/aacenc_core.c \
${VO_AACENC_SRC}/aacenc/src/adj_thr.c \
${VO_AACENC_SRC}/aacenc/src/band_nrg.c \
${VO_AACENC_SRC}/aacenc/src/bit_cnt.c \
${VO_AACENC_SRC}/aacenc/src/bitbuffer.c \
${VO_AACENC_SRC}/aacenc/src/bitenc.c \
${VO_AACENC_SRC}/aacenc/src/block_switch.c \
${VO_AACENC_SRC}/aacenc/src/channel_map.c \
${VO_AACENC_SRC}/aacenc/src/dyn_bits.c \
${VO_AACENC_SRC}/aacenc/src/grp_data.c \
${VO_AACENC_SRC}/aacenc/src/interface.c \
${VO_AACENC_SRC}/aacenc/src/line_pe.c \
${VO_AACENC_SRC}/aacenc/src/memalign.c \
${VO_AACENC_SRC}/aacenc/src/ms_stereo.c \
${VO_AACENC_SRC}/aacenc/src/pre_echo_control.c \
${VO_AACENC_SRC}/aacenc/src/psy_configuration.c \
${VO_AACENC_SRC}/aacenc/src/psy_main.c \
${VO_AACENC_SRC}/aacenc/src/qc_main.c \
${VO_AACENC_SRC}/aacenc/src/quantize.c \
${VO_AACENC_SRC}/aacenc/src/sf_estim.c \
${VO_AACENC_SRC}/aacenc/src/spreading.c \
${VO_AACENC_SRC}/aacenc/src/stat_bits.c \
${VO_AACENC_SRC}/aacenc/src/tns.c \
${VO_AACENC_SRC}/aacenc/src/transform.c \
\"
for f in \$SRCS; do
\$CC -O2 -DNDEBUG -fPIC -mlongcalls -mtext-section-literals -D__unused= \\
-I${VO_AACENC_SRC}/aacenc/inc \\
-I${VO_AACENC_SRC}/aacenc/basic_op \\
-I${VO_AACENC_SRC}/common/include \\
-c \"\$f\" \\
-o ${CMAKE_CURRENT_BINARY_DIR}/vo-aacenc-obj/\$(basename \"\$f\").o
done
${_ff_cross_prefix}ar rcs ${VO_AACENC_INSTALL}/lib/libvo-aacenc.a ${CMAKE_CURRENT_BINARY_DIR}/vo-aacenc-obj/*.o
cp ${VO_AACENC_SRC}/common/include/*.h ${VO_AACENC_INSTALL}/include/vo-aacenc/
")
add_custom_command(
OUTPUT "${VO_AACENC_INSTALL}/lib/libvo-aacenc.a"
COMMAND sh "${CMAKE_CURRENT_BINARY_DIR}/vo-aacenc-build.sh"
VERBATIM)
Comment on lines +487 to +490
add_custom_target(vo_aacenc_ext DEPENDS "${VO_AACENC_INSTALL}/lib/libvo-aacenc.a")
set(_ff_vo_aacenc_dep vo_aacenc_ext)
message(STATUS "ffmpeg_dec: enabling AAC encoder via vo-aacenc (${VO_AACENC_SRC})")
endif()

# --- 4. Configure + make + install (out-of-tree; source must be clean) ---
set(_ff_byproducts
Expand All @@ -357,6 +500,7 @@ set(_ff_byproducts
${FFMPEG_INSTALL_DIR}/lib/libswresample.a)

ExternalProject_Add(ffmpeg_ext
DEPENDS ${_ff_shine_dep} ${_ff_vo_aacenc_dep}
SOURCE_DIR "${SOF_FFMPEG_SRC_DIR}"
BUILD_IN_SOURCE 0
CONFIGURE_COMMAND
Expand All @@ -378,6 +522,7 @@ ExternalProject_Add(ffmpeg_ext
--disable-runtime-cpudetect --disable-debug
--enable-avcodec --enable-avutil --enable-swresample
${_ff_dec_cfg}
${_ff_shine_cfg}
--enable-small --enable-pic --enable-hardcoded-tables
"--extra-cflags=${_ff_extra_cflags}"
BUILD_COMMAND
Expand Down
Loading
Loading