Skip to content

Conversation

@xaviliz
Copy link
Contributor

@xaviliz xaviliz commented Dec 18, 2025

Fix FFmpeg compatibility issues

Problem

Essentia fails to build/import on systems with FFmpeg 5.x+ due to deprecated and removed APIs, causing undefined symbol: avcodec_decode_audio4 errors.

Loading and writing algorithms like AudioLoader, Monoloader, EqloudLoader, EasyLoader, MonoWriter and AudioWriter, and any example or extractor that reuse those algorithms are affected by this failure.

Solution

#1481 Extended
Updated AudioLoader and AudioContext to use modern FFmpeg APIs:

  • Replace avcodec_decode_audio4() with avcodec_send_packet() + avcodec_receive_frame()
  • Replace avcodec_encode_audio2() with avcodec_send_frame() + avcodec_receive_packet()
  • Remove deprecated av_register_all() calls
  • Update AVStream->codec to AVStream->codecpar
  • Replace AVCodecContext->channels with AVCodecContext->ch_layout
  • Fix flushPacket() to properly handle AVERROR_EOF
  • Replace float* buffer with a byte-oriented buffer
  • Support for planar formats (mp3, vorbis, FLAC, WAV/AIFF codecs and some PCM variants)
  • Allocate proper buffer arrays for planar formats

Updated build_config.sh to upgrade FFmpeg from 2.8.12 to 7.1.1:

  • Update new FFmpeg version
  • Migrate from avresample to swresample flags
  • Update sdl to sld2 flags

Update unit tests for AudioLoader, EasyLoader, EqLoudLoader and MonoLoader:

  • Update essentia_test.py to fix compatibility shim for Python < 3.12 style assert_
  • Small tasks in test_audioloader_streaming.py, test_easyloader_streaming.py, test_eqloudloader_streaming.py and test_monoloader.py
    • Fix numpy compatibility by replacing assertEquals with assertEqual
    • Remove semicolons

Prerequisites

  • python >= 3.10
  • cmake >= 3.28

Testing

  • Builds successfully from the source code with FFmpeg 7.1.1
  • All audio formats (WAV, MP3, FLAC, Vorbis) load and write without warnings and with proper audio length
  • No runtime errors or compatibility issues
  • All unittests running OK for AudioLoader, MonoLoader, EqloudLoader, EasyLoader, MonoWriter and AudioWriter

Closes

How to test

Tested with ffmpeg version 7.1.1 compiled from the source code https://ffmpeg.org/releases/ffmpeg-7.1.1.tar.gz
in:

How to build FFmpeg

After installing Essentia dependencies in a virtual environment, install cmake

python3 -m pip install cmake
which cmake

Then we'll run the building script:

cd packaging/debian_3rdparty
bash build_lame.h	# ffmpeg dependency for mp3 codec
bash build_ffmpeg.sh

How to build Essentia

MacOS

# Build essentia with ffmpeg
cd -
python3 waf clean
python3 waf configure --fft=KISS \
  --include-algos=AudioLoader,MonoLoader,EqloudLoader,EasyLoader,MonoWriter,AudioWriter,StereoDemuxer,Centroid,MonoMixer,Resample,Trimmer,Scale,EqualLoudness,IIR,ZeroCrossingRate,NoiseAdder,RealAccumulator,FileOutputProxy,FrameCutter \
  --static-dependencies \
  --pkg-config-path=/packaging/debian_3rdparty/lib/pkgconfig:/opt/homebrew/lib/pkgconfig \
  --std=c++14 \
  --lightweight=libav,libsamplerate \
  --with-python \
  --pythondir=.env/lib/python3.13/site-packages
python3 waf -v && python3 waf install

Linux

cd -
python3 waf clean
python3 waf configure --fft=KISS \
  --static-dependencies \
  --pkg-config-path=/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig \
  --std=c++14 \
  --lightweight=libav,libsamplerate \
  --include-algos=AudioLoader,MonoLoader,EqloudLoader,EasyLoader,MonoWriter,AudioWriter,StereoDemuxer,Centroid,MonoMixer,Resample,Trimmer,Scale,EqualLoudness,IIR,ZeroCrossingRate,NoiseAdder,RealAccumulator,FileOutputProxy,FrameCutter \
  --with-python \
  --pythondir=/opt/python/cp310-cp310/lib/python3.10/site-packages \
  --prefix=/usr/local

python3 waf build
python3 waf install

# Avoid issues finding the .so
export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64:$LD_LIBRARY_PATH

How to unittest

# Prepare essentia-audio repo for testing
git clone https://github.com/MTG/essentia-audio.git test/essentia-audio
rm -rf test/audio && mv test/essentia-audio test/audio
git submodule update --init --recursive

# Run unittests
python3 test/src/unittests/all_tests.py audioloader_streaming
python3 test/src/unittests/all_tests.py monoloader
python3 test/src/unittests/all_tests.py easyloader_streaming
python3 test/src/unittests/all_tests.py monowriter
python3 test/src/unittests/all_tests.py audiowriter_streaming

fergarciadlc and others added 16 commits July 29, 2025 17:53
- Remove deprecated av_register_all() calls
- Update AudioContext to use modern FFmpeg APIs:
  - Replace AVStream->codec with AVStream->codecpar
  - Use AVCodecContext->ch_layout instead of channels
  - Update encoding API to avcodec_send_frame() + avcodec_receive_packet()
  - Fix packet handling with av_packet_unref()
- Update AudioLoader to use modern decoding API:
  - Replace avcodec_decode_audio4() with avcodec_send_packet() + avcodec_receive_frame()
  - Fix codec context creation and management
  - Update channel layout API usage
- Fix type declarations for const pointers
- Successfully builds and imports with FFmpeg 7.1.1

Resolves GitHub issue MTG#1248: Support ffmpeg version 5
- Fix decode_audio_frame() to properly handle flush packets (empty packets)
- Handle AVERROR_EOF as expected response when flushing, not as error
- Return correct values for modern send_packet/receive_frame API
- Successfully tested with WAV, MP3, and FLAC formats
- No more decoding warnings or runtime errors

The AudioLoader now works perfectly with FFmpeg 7.1.1
- Document complete resolution of FFmpeg 5.x compatibility issues
- Add testing results for WAV, MP3, and FLAC formats
- Confirm no more warnings or runtime errors
- Mark all phases as completed successfully
- Migrate from avresample to swresample flags
- Update sdl to sld2 flags

- Small clean
- Small clean
- Apply 2-white space tab indentation
- Small clean.
- In process()
	- Set _dataSize to number of bytes written without mutating _packet.data and _packet.size
	- Assess the buffer has been written before to copy FFmpeg output.
- In flushPacket()
	- Support for Receive_result errors (AVERROR(EAGAIN) and AVERROR_EOF)
	- Fix how to get frames and update _dataSize
- In decodePacket()
	- Redefined with modern safe API
	- Avoid mutating _packet_data /_packet.size to decode one frame
- In copyFFmpegOutput()
	- Use of _buffer as float pointer with an explicit cast.
- In process()
	- Ensures that it reads a single packet and outputs at most one output fragment per call.
- In flushPacket() guarantees it only flush once-per-frame.
… between byte counts and sample counts and prevent accidental overruns.

- Fix buffer initialization.
- In configure()
	- Set AV log level to AV_LOG_QUIET
- In AudioContext()
	- Initialize _pts to 0 by default.
- In create()
	- Find encoder before to create audio stream
	- Create audio stream and pass the codec to help FFmpeg initialize defaults
	- Create codec context
	- Define channel layout
	- Open codec
	- Ensure stream is marked as audio and set a sensible time_base for muxer
	- Determine frame_size fallback for PCM codecs (some PCM codecs do not set frame_size)
	- Allocate input audio FLT buffer sized for codecCtx->frame_size samples
	- Guarantees configure sample format conversion
- In open()
	- Open the output IO and check errors
	- Write header in a safe way.
- In close()
	- Close output IO with the modern safe API
	- Use modern API for codec context cleanup
	- Free input buffer, swresample context and context.
- In encodePacket()
	- Prepare conversion buffers (bufferFmt[ch]) and linesize[ch]
	- Perform sample format conversion
	- Allocate frame
	- Copy converted audio into AVFrame
	- Send frame to encoder
	- Receive packets and write them (may be 0..N packets)
	- Update cleanup
- Initialize _pts to 0 by default
- Remove semicolons
- Fix numpy compatibility by replacing assertEquals with assertEqual
- Remove semicolons
- Fix numpy compatibility by replacing assertEquals with assertEqual
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment