PR #24910 opened by Martin Storsjö (mstorsjo)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24910
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24910.patch

This is a new library, separate from the existing fdk-aac (but
built from the same upstream repo), for encoding xHE-AAC.

This library has the same traits as fdk-aac; it's being distributed
from Fraunhofer to Android Open Source Project, and further
repackaged and distributed through opencore-amr [1] (and in a
personal repo on github at [2]). It is distributed under the
same license as fdk-aac.

This is an initial, barebones wrapper around the library - not
all features of the library are hooked up currently.

In particular, do note that the wrapper currently only handles
loudness in the live operating mode (single pass), where encoding
is done with a single loudness level set before encoding. This may
affect the dynamics of the input.

The preferred and intended mode of use for the fdk-xhe-aac encoder
is for it to be used in two pass mode, which handles the loudness
properly. The two pass operation mode is not set up in the
encoder wrapper yet, but may be added later.

[1] https://sourceforge.net/projects/opencore-amr/
[2] https://github.com/mstorsjo/fdk-aac



From 798d1a2d91855102e4e57a107e61c1be962df23a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <[email protected]>
Date: Tue, 1 Sep 2026 23:06:55 +0300
Subject: [PATCH] avcodec: Add encoder wrapper for fdk-xhe-aac

This is a new library, separate from the existing fdk-aac (but
built from the same upstream repo), for encoding xHE-AAC.

This library has the same traits as fdk-aac; it's being distributed
from Fraunhofer to Android Open Source Project, and further
repackaged and distributed through opencore-amr [1] (and in a
personal repo on github at [2]). It is distributed under the
same license as fdk-aac.

This is an initial, barebones wrapper around the library - not
all features of the library are hooked up currently.

In particular, do note that the wrapper currently only handles
loudness in the live operating mode (single pass), where encoding
is done with a single loudness level set before encoding. This may
affect the dynamics of the input.

The preferred and intended mode of use for the fdk-xhe-aac encoder
is for it to be used in two pass mode, which handles the loudness
properly. The two pass operation mode is not set up in the
encoder wrapper yet, but may be added later.

[1] https://sourceforge.net/projects/opencore-amr/
[2] https://github.com/mstorsjo/fdk-aac
---
 Changelog                      |   1 +
 configure                      |   4 +
 doc/encoders.texi              |  11 +
 doc/general_contents.texi      |  10 +
 libavcodec/Makefile            |   1 +
 libavcodec/allcodecs.c         |   1 +
 libavcodec/libfdk-xhe-aacenc.c | 541 +++++++++++++++++++++++++++++++++
 libavcodec/version.h           |   2 +-
 8 files changed, 570 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/libfdk-xhe-aacenc.c

diff --git a/Changelog b/Changelog
index 876497f2e1..09fe32f362 100644
--- a/Changelog
+++ b/Changelog
@@ -16,6 +16,7 @@ version <next>:
 - DSD (dsd_msbf) encoder
 - Rhetorex ADPCM decoder
 - ADPCM IMA Citrix decoder
+- xHE-AAC encoding via libfdk-xhe-aac
 
 
 version 9.0:
diff --git a/configure b/configure
index 08ff6892fb..bde9ede99d 100755
--- a/configure
+++ b/configure
@@ -233,6 +233,7 @@ External library support:
   --enable-libdvdnav       enable libdvdnav, needed for DVD demuxing [no]
   --enable-libdvdread      enable libdvdread, needed for DVD demuxing [no]
   --enable-libfdk-aac      enable AAC de/encoding via libfdk-aac [no]
+  --enable-libfdk-xhe-aac  enable xHE-AAC encoding via libfdk-xhe-aac [no]
   --enable-libflite        enable flite (voice synthesis) support via libflite 
[no]
   --enable-libfontconfig   enable libfontconfig, useful for drawtext filter 
[no]
   --enable-libfreetype     enable libfreetype, needed for drawtext filter [no]
@@ -2031,6 +2032,7 @@ EXTERNAL_LIBRARY_GPL_LIST="
 EXTERNAL_LIBRARY_NONFREE_LIST="
     decklink
     libfdk_aac
+    libfdk_xhe_aac
     libmpeghdec
 "
 
@@ -3805,6 +3807,7 @@ libdavs2_decoder_select="avs2_parser"
 libfdk_aac_decoder_deps="libfdk_aac"
 libfdk_aac_encoder_deps="libfdk_aac"
 libfdk_aac_encoder_select="audio_frame_queue"
+libfdk_xhe_aac_encoder_deps="libfdk_xhe_aac"
 libgme_demuxer_deps="libgme"
 libgsm_decoder_deps="libgsm"
 libgsm_encoder_deps="libgsm"
@@ -7237,6 +7240,7 @@ enabled libdvdread        && require_pkg_config 
libdvdread "dvdread >= 6.1.1" dv
 enabled libfdk_aac        && { check_pkg_config libfdk_aac fdk-aac 
"fdk-aac/aacenc_lib.h" aacEncOpen ||
                                { require libfdk_aac fdk-aac/aacenc_lib.h 
aacEncOpen -lfdk-aac &&
                                  warn "using libfdk without pkg-config"; } }
+enabled libfdk_xhe_aac    && require_pkg_config libfdk_xhe_aac fdk-xhe-aac 
"fdk-xhe-aac/xHEAACEnc.h" IIS_xHEAACEnc_Open
 flite_extralibs="-lflite_cmu_time_awb -lflite_cmu_us_awb -lflite_cmu_us_kal 
-lflite_cmu_us_kal16 -lflite_cmu_us_rms -lflite_cmu_us_slt -lflite_usenglish 
-lflite_cmulex -lflite"
 enabled libflite          && require libflite "flite/flite.h" flite_init 
$flite_extralibs
 enabled fontconfig        && enable libfontconfig
diff --git a/doc/encoders.texi b/doc/encoders.texi
index fcb085f2b8..51b48569b5 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -3349,6 +3349,17 @@ Force to use a specific number of threads
 
 @end table
 
+@section libfdk-xhe-aac
+
+FDK-xHE-AAC (Extended High-Efficiency Advanced Audio Coding) encoder wrapper.
+
+The fdk-xhe-aac library is based on the The Fraunhofer FDK Extended High
+Efficiency AAC Encoder Software code from the Android project.
+
+Requires the presence of the libfdk-xhe-aac headers and library during
+configuration. You need to explicitly configure the build with
+@code{--enable-libfdk-xhe-aac}.
+
 @section libxvid
 
 Xvid MPEG-4 Part 2 encoder wrapper.
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 597314ac04..58e4d1657b 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -326,6 +326,14 @@ Go to @url{http://sourceforge.net/projects/opencore-amr/} 
and follow the
 instructions for installing the library.
 Then pass @code{--enable-libfdk-aac} to configure to enable it.
 
+@subsection Fraunhofer xHE-AAC library
+
+FFmpeg can make use of the Fraunhofer xHE-AAC library for xHE-AAC encoding.
+
+Go to @url{http://sourceforge.net/projects/opencore-amr/} and follow the
+instructions for installing the library.
+Then pass @code{--enable-libfdk-xhe-aac} to configure to enable it.
+
 @subsection Fraunhofer MPEG-H 3D Audio decoder library
 
 FFmpeg can make use of the Fraunhofer MPEG-H decoder library for MPEG-H 3DA 
decoding.
@@ -1533,6 +1541,8 @@ following image formats are supported:
 @item Windows Media Audio Voice @tab  @tab  X
 @item Xbox Media Audio 1     @tab     @tab  X
 @item Xbox Media Audio 2     @tab     @tab  X
+@item xHE-AAC                @tab  E  @tab
+    @tab encoding supported through external library fdk-xhe-aac
 @end multitable
 
 @code{X} means that the feature in that column (encoding / decoding) is 
supported.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index ece43a46d6..41dcf0d72e 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1193,6 +1193,7 @@ OBJS-$(CONFIG_LIBDAV1D_DECODER)           += libdav1d.o 
av1_parse.o
 OBJS-$(CONFIG_LIBDAVS2_DECODER)           += libdavs2.o
 OBJS-$(CONFIG_LIBFDK_AAC_DECODER)         += libfdk-aacdec.o
 OBJS-$(CONFIG_LIBFDK_AAC_ENCODER)         += libfdk-aacenc.o
+OBJS-$(CONFIG_LIBFDK_XHE_AAC_ENCODER)     += libfdk-xhe-aacenc.o
 OBJS-$(CONFIG_LIBGSM_DECODER)             += libgsmdec.o
 OBJS-$(CONFIG_LIBGSM_ENCODER)             += libgsmenc.o
 OBJS-$(CONFIG_LIBGSM_MS_DECODER)          += libgsmdec.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 9c3d62f058..306564a003 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -784,6 +784,7 @@ extern const FFCodec ff_libdav1d_decoder;
 extern const FFCodec ff_libdavs2_decoder;
 extern const FFCodec ff_libfdk_aac_encoder;
 extern const FFCodec ff_libfdk_aac_decoder;
+extern const FFCodec ff_libfdk_xhe_aac_encoder;
 extern const FFCodec ff_libgsm_encoder;
 extern const FFCodec ff_libgsm_decoder;
 extern const FFCodec ff_libgsm_ms_encoder;
diff --git a/libavcodec/libfdk-xhe-aacenc.c b/libavcodec/libfdk-xhe-aacenc.c
new file mode 100644
index 0000000000..b31b241b64
--- /dev/null
+++ b/libavcodec/libfdk-xhe-aacenc.c
@@ -0,0 +1,541 @@
+/*
+ * FDK-XHE-AAC encoder wrapper
+ * Copyright (c) 2026 Martin Storsjo
+ *
+ * This file is part of FFmpeg.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "libavutil/channel_layout.h"
+#include "libavutil/common.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "avcodec.h"
+#include "audio_frame_queue.h"
+#include "codec_internal.h"
+#include "encode.h"
+#include "profiles.h"
+
+#include <fdk-xhe-aac/xHEAACEnc.h>
+
+typedef struct XHEAACContext {
+    const AVClass *class;
+    IIS_XHEAACENC_INSTANCE_HANDLE handle;
+
+    int stream_id;
+    float live_loudness_level;
+    int vbr;
+    int delay;
+    int frame_length;
+    int sample_rate;
+
+    AudioFrameQueue afq;
+
+    float *sample_queue;
+    int sample_queue_size;
+    int nb_sample_queue;
+} XHEAACContext;
+
+static const AVOption xhe_aac_enc_options[] = {
+    { "stream_id", "unique ID of this stream within one adaptation set", 
offsetof(XHEAACContext, stream_id), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 65536, 
AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
+    { "vbr", "VBR mode (0-6)", offsetof(XHEAACContext, vbr), AV_OPT_TYPE_INT, 
{ .i64 = -1 }, -1, 6, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
+    { "live_loudness_level", "target loudness level in live encoding mode 
[LKFS/LUFS]", offsetof(XHEAACContext, live_loudness_level), AV_OPT_TYPE_FLOAT, 
{ .dbl = -24 }, -31.0, -10.0, AV_OPT_FLAG_AUDIO_PARAM | 
AV_OPT_FLAG_ENCODING_PARAM },
+    FF_AAC_PROFILE_OPTS
+    { NULL }
+};
+
+static const AVClass xhe_aac_enc_class = {
+    .class_name = "libfdk_xhe_aac",
+    .item_name  = av_default_item_name,
+    .option     = xhe_aac_enc_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const char *xhe_aac_get_error(IIS_XHEAACENC_RETURN_CODE err)
+{
+    switch (err) {
+    case IIS_XHEAACENC_NO_ERROR:
+        return "No error";
+    case IIS_XHEAACENC_ERROR_PARAM_BITRATE:
+        return "Invalid or missing parameter for bitrate";
+    case IIS_XHEAACENC_ERROR_TOO_MANY_INPUT_SAMPLES:
+        return "Too many input samples";
+    case IIS_XHEAACENC_ERROR_PARAMCOMB_BASE:
+        return "Invalid parameter combination of AOT, operating mode, bitrate, 
bitrate mode, channel config or transport format";
+    case IIS_XHEAACENC_ERROR_INSAMPLERATE_UNSUPPORTED:
+        return "Unsupported input sample rate";
+    case IIS_XHEAACENC_ERROR_COMB_INSAMPLERATE_UNSUPPORTED:
+        return "Unsupported input sample rate for a specific configuration";
+    case IIS_XHEAACENC_ERROR_PARAMCOMB_STREAMID_SWITCHABLE_AOT:
+        return "Operating mode switchable requires a stream ID";
+    default:
+        return "Unknown error";
+    }
+}
+
+static int xhe_aac_encode_close(AVCodecContext *avctx)
+{
+    XHEAACContext *s = avctx->priv_data;
+    IIS_XHEAACENC_RETURN_CODE err;
+
+    if (s->handle) {
+        err = IIS_xHEAACEnc_Delete(s->handle);
+        if (err != IIS_XHEAACENC_NO_ERROR)
+            av_log(avctx, AV_LOG_ERROR, "Unexpected error on close: %s (%d)\n",
+                   xhe_aac_get_error(err), err);
+        s->handle = NULL;
+    }
+    ff_af_queue_close(&s->afq);
+    av_freep(&s->sample_queue);
+
+    return 0;
+}
+
+static av_cold int xhe_aac_encode_init(AVCodecContext *avctx)
+{
+    XHEAACContext *s = avctx->priv_data;
+    int ret = AVERROR(EINVAL);
+    AVCPBProperties *cpb_props;
+    IIS_XHEAACENC_CONFIG_INSTANCE_HANDLE config_handle = NULL;
+    IIS_XHEAACENC_CHANNELCONFIG channel_config;
+    IIS_XHEAACENC_BITRATEMODE bitrate_mode;
+    IIS_XHEAACENC_RETURN_CODE err;
+    int out_framesize;
+
+    if (avctx->profile != AV_PROFILE_UNKNOWN && avctx->profile != 
AV_PROFILE_AAC_USAC) {
+        av_log(avctx, AV_LOG_ERROR, "This encoder can only encode xHE-AAC (AOT 
42).\n");
+        goto error;
+    }
+
+    switch (avctx->ch_layout.nb_channels) {
+    case 1: channel_config = IIS_XHEAACENC_CHANNELCONFIG_MONO;   break;
+    case 2: channel_config = IIS_XHEAACENC_CHANNELCONFIG_STEREO; break;
+    default:
+        av_log(avctx, AV_LOG_ERROR,
+               "Unsupported number of channels %d\n", 
avctx->ch_layout.nb_channels);
+        goto error;
+    }
+
+    err = IIS_xHEAACEnc_Config_Open(&config_handle, avctx->sample_rate, 
channel_config);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to open config: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    if (avctx->flags & AV_CODEC_FLAG_QSCALE || s->vbr >= 0) {
+        int vbr = s->vbr >= 0 ? s->vbr : avctx->global_quality;
+        if (vbr <  0 || vbr > 6) {
+            av_log(avctx, AV_LOG_WARNING,
+                   "VBR quality %d out of range, should be 0-6\n", vbr);
+            vbr = av_clip(vbr, 0, 6);
+        }
+        switch (vbr) {
+        case 0: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR0; break;
+        case 1: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR1; break;
+        case 2: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR2; break;
+        case 3: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR3; break;
+        case 4: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR4; break;
+        case 5: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR5; break;
+        case 6: bitrate_mode = IIS_XHEAACENC_BITRATEMODE_AAC_VBR6; break;
+        }
+        s->vbr = vbr;
+    } else {
+        bitrate_mode = IIS_XHEAACENC_BITRATEMODE_CBR;
+
+        if (avctx->bit_rate <= 0) {
+            avctx->bit_rate = (32 + 32*avctx->ch_layout.nb_channels) * 
avctx->sample_rate / 44;
+        }
+
+        /* The bitrate needs to be a multiple of 2000 */
+        avctx->bit_rate = (avctx->bit_rate / 2000) * 2000;
+
+        err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+                  IIS_XHEAACENC_PARAMETER_BITRATE, avctx->bit_rate);
+        if (err != IIS_XHEAACENC_NO_ERROR) {
+            av_log(avctx, AV_LOG_ERROR, "Unable to set bitrate: %s (%d)\n",
+                   xhe_aac_get_error(err), err);
+            goto error;
+        }
+    }
+    err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+              IIS_XHEAACENC_PARAMETER_BITRATEMODE, bitrate_mode);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set bitrate mode: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    /* Choose bitstream format - if global header is requested, use
+     * raw access units, otherwise use LOAS. */
+    err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+              IIS_XHEAACENC_PARAMETER_TRANSPORTFORMAT,
+              avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER ?
+                  IIS_XHEAACENC_TRANSPORTFORMAT_RAW :
+                  IIS_XHEAACENC_TRANSPORTFORMAT_LATMLOAS);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set transport format: %s 
(%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+              IIS_XHEAACENC_PARAMETER_AOT, IIS_XHEAACENC_AOT_USAC);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set AOT: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    if (s->stream_id < 0) {
+        /* The encoder requires us to provide a stream ID. Within an
+         * adaptation set, the stream ID needs to be unique. If the user didn't
+         * specfiy one, try to make one up that probably is unique. */
+        if (s->vbr >= 0)
+            s->stream_id = s->vbr;
+        else
+            s->stream_id = avctx->bit_rate / 1000;
+    }
+    err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+              IIS_XHEAACENC_PARAMETER_STREAMID, s->stream_id);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set stream ID: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+              IIS_XHEAACENC_PARAMETER_RAP_OCCURRENCE,
+              IIS_XHEAACENC_RAP_OCCURRENCE_CONSTANT_INTERVAL);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set RAP occurrence: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    err = IIS_xHEAACEnc_Config_AddParamValueInt(config_handle,
+              IIS_XHEAACENC_PARAMETER_RAP_INTERVAL_MS, 1000);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set RAP interval: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    err = IIS_xHEAACEnc_Config_AddParamValueFloat(config_handle,
+              IIS_XHEAACENC_PARAMETER_LIVE_LOUDNESS_LEVEL, 
s->live_loudness_level);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to set live loudness level: %s 
(%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    err = IIS_xHEAACEnc_Config_Finalize(config_handle);
+    if (err >= IIS_XHEAACENC_ERROR_FIRST) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to finalize config: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    } else if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_WARNING, "Warning when finalizing config: %s 
(%d)\n",
+               xhe_aac_get_error(err), err);
+    }
+
+    err = IIS_xHEAACEnc_Open(&s->handle, config_handle);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to open encoder: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    err = IIS_xHEAACEnc_Config_Delete(config_handle);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to delete config: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    config_handle = NULL;
+
+    err = IIS_xHEAACEnc_GetParam(s->handle, 
IIS_XHEAACENC_PARAMETER_OUTSAMPLERATE,
+                                 IIS_XHEAACENC_PARAM_INT,
+                                 &s->sample_rate, sizeof(s->sample_rate));
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to get sample rate: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    if (s->sample_rate != avctx->sample_rate)
+        av_log(avctx, AV_LOG_INFO, "Internal sample rate changed from %d to 
%d\n",
+               avctx->sample_rate, s->sample_rate);
+
+    err = IIS_xHEAACEnc_GetParam(s->handle, 
IIS_XHEAACENC_PARAMETER_FRAMESAMPLES,
+                                 IIS_XHEAACENC_PARAM_INT,
+                                 &out_framesize, sizeof(out_framesize));
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to get frame size: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    avctx->frame_size = (int64_t) out_framesize * avctx->sample_rate / 
s->sample_rate;
+
+    /* Note - this encoder doesn't incur any extra avctx->initial_padding
+     * that needs to be signaled and removed after decoding. (There exists
+     * a parameter IIS_XHEAACENC_PARAMETER_PRIMING we could query, but
+     * it returns zero.) */
+
+    ff_af_queue_init(avctx, &s->afq);
+
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+        IIS_XHEAACENC_ASCINFO asc_info;
+        err = IIS_xHEAACEnc_GetAscInfo(s->handle, &asc_info);
+        if (err != IIS_XHEAACENC_NO_ERROR) {
+            av_log(avctx, AV_LOG_ERROR, "Unable to get ASC info: %s (%d)\n",
+                   xhe_aac_get_error(err), err);
+            goto error;
+        }
+
+        avctx->extradata_size = (asc_info.ascSizeBits + 7) / 8;
+        avctx->extradata      = av_mallocz(avctx->extradata_size +
+                                           AV_INPUT_BUFFER_PADDING_SIZE);
+        if (!avctx->extradata) {
+            ret = AVERROR(ENOMEM);
+            goto error;
+        }
+        memcpy(avctx->extradata, asc_info.ascBuffer, avctx->extradata_size);
+    }
+
+    cpb_props = ff_encode_add_cpb_side_data(avctx);
+    if (!cpb_props) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+
+    err = IIS_xHEAACEnc_GetParam(s->handle, 
IIS_XHEAACENC_PARAMETER_BITRATELIMIT,
+                                 IIS_XHEAACENC_PARAM_INT,
+                                 &cpb_props->max_bitrate, 
sizeof(cpb_props->max_bitrate));
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to get max bitrate: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+
+    err = IIS_xHEAACEnc_GetParam(s->handle, IIS_XHEAACENC_PARAMETER_BITRATE,
+                                 IIS_XHEAACENC_PARAM_INT,
+                                 &cpb_props->avg_bitrate, 
sizeof(cpb_props->avg_bitrate));
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to get avg bitrate: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        goto error;
+    }
+    if (avctx->bit_rate <= 0)
+        avctx->bit_rate = cpb_props->avg_bitrate;
+
+    s->sample_queue_size = 4096*20;
+    s->sample_queue = av_malloc(s->sample_queue_size * 
sizeof(*s->sample_queue));
+    if (!s->sample_queue) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+
+    return 0;
+error:
+    if (config_handle) {
+        err = IIS_xHEAACEnc_Config_Delete(config_handle);
+        if (err != IIS_XHEAACENC_NO_ERROR)
+            av_log(avctx, AV_LOG_ERROR, "Unable to delete config: %s (%d)\n",
+                   xhe_aac_get_error(err), err);
+    }
+    xhe_aac_encode_close(avctx);
+    return ret;
+}
+
+static int xhe_aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                            const AVFrame *frame, int *got_packet_ptr)
+{
+    XHEAACContext *s = avctx->priv_data;
+    IIS_XHEAACENC_RETURN_CODE err;
+    int samples_next;
+    int ret;
+
+    if (frame) {
+        int nb_new_samples = frame->nb_samples * avctx->ch_layout.nb_channels;
+
+        if ((ret = ff_af_queue_add(&s->afq, frame)) < 0)
+            return ret;
+
+        if (s->nb_sample_queue + nb_new_samples > s->sample_queue_size) {
+            int size = s->nb_sample_queue + nb_new_samples + 4096;
+            float *ptr = av_realloc(s->sample_queue, size * 
sizeof(*s->sample_queue));
+            if (!ptr)
+                return AVERROR(ENOMEM);
+            s->sample_queue = ptr;
+            s->sample_queue_size = size;
+        }
+
+        /* As long as we don't have enough samples, we could also feed them
+         * straight into the encoder - it does accept getting too few samples.
+         *
+         * But we can't feed it too many samples, and samples_next doesn't
+         * align with the frame size, so we need to buffer the remainder in any
+         * case. Thus for simplicity, just buffer up all samples in the 
wrapper.
+         *
+         * (The encoder has got a parameter IIS_XHEAACENC_PARAMETER_SAMPLES_MAX
+         * which is said to possibly be larger than 
IIS_XHEAACENC_PARAMETER_SAMPLES_NEXT,
+         * but currently, at least at start, one can't pass more than
+         * samples_next.)
+         */
+        memcpy(s->sample_queue + s->nb_sample_queue, frame->data[0],
+               nb_new_samples * sizeof(*s->sample_queue));
+        s->nb_sample_queue += nb_new_samples;
+    }
+
+    err = IIS_xHEAACEnc_GetParam(s->handle, 
IIS_XHEAACENC_PARAMETER_SAMPLES_NEXT,
+                                 IIS_XHEAACENC_PARAM_INT,
+                                 &samples_next, sizeof(samples_next));
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to get number of required samples: 
%s (%d)\n",
+               xhe_aac_get_error(err), err);
+        return AVERROR(EINVAL);
+    }
+
+    if (s->nb_sample_queue < samples_next && frame) {
+        /* Not enough samples yet, and not end of stream - don't push it
+         * yet. */
+        return 0;
+    }
+
+    /* The maximum packet size is 6144 bits aka 768 bytes per channel. */
+    int alloc_size = FFMAX(8192, 768 * avctx->ch_layout.nb_channels);
+    ret = ff_alloc_packet(avctx, avpkt, alloc_size);
+    if (ret < 0)
+        return ret;
+
+    int nb_samples = FFMIN(s->nb_sample_queue, samples_next);
+    int out_bytes;
+    IIS_XHEAACENC_AUINFO au_info;
+
+    err = IIS_xHEAACEnc_EncodeFrame(s->handle, s->sample_queue, nb_samples,
+                                    (unsigned char *)avpkt->data, &out_bytes,
+                                    avpkt->size, &au_info);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to encode frame: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        return AVERROR(EINVAL);
+    }
+    s->nb_sample_queue -= nb_samples;
+    memmove(s->sample_queue, s->sample_queue + nb_samples,
+            s->nb_sample_queue * sizeof(*s->sample_queue));
+
+    IIS_XHEAACENC_ENCODER_STATE state;
+    err = IIS_xHEAACEnc_GetEncoderState(s->handle, &state);
+    if (err != IIS_XHEAACENC_NO_ERROR) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to get encoder state: %s (%d)\n",
+               xhe_aac_get_error(err), err);
+        return AVERROR(EINVAL);
+    }
+
+    /* If no output packet was generated and the encoder isn't ready to
+     * close yet, force the encoder into flushing mode. */
+    if (out_bytes <= 0 && !frame && state != 
IIS_XHEAACENC_ENCODER_STATE_READY_TO_CLOSE) {
+        while (state != IIS_XHEAACENC_ENCODER_STATE_FLUSHING) {
+            err = IIS_xHEAACEnc_EncodeFrame(s->handle, NULL, 0,
+                                            (unsigned char *)avpkt->data,
+                                            &out_bytes, avpkt->size, &au_info);
+            if (err != IIS_XHEAACENC_NO_ERROR) {
+                av_log(avctx, AV_LOG_ERROR, "Unable to flush encoder: %s 
(%d)\n",
+                       xhe_aac_get_error(err), err);
+                return AVERROR(EINVAL);
+            }
+
+            err = IIS_xHEAACEnc_GetEncoderState(s->handle, &state);
+            if (err != IIS_XHEAACENC_NO_ERROR) {
+                av_log(avctx, AV_LOG_ERROR, "Unable to get encoder state: %s 
(%d)\n",
+                       xhe_aac_get_error(err), err);
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    avpkt->size = out_bytes;
+    if (out_bytes > 0) {
+        *got_packet_ptr = 1;
+
+        if (au_info.auOffset != 0) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "Encoder reports auOffset of %d. This is currently not 
implemented.\n",
+                   au_info.auOffset);
+            return AVERROR(EINVAL);
+        }
+
+        switch (au_info.isSyncFrame) {
+        case IIS_XHEAACENC_SYNCFRAME_NO:
+        case IIS_XHEAACENC_SYNCFRAME_INDEPENDENT_FRAME:
+        case IIS_XHEAACENC_SYNCFRAME_STREAM_MUX_CONFIG_IF:
+            break;
+        case IIS_XHEAACENC_SYNCFRAME_IMMEDIATE_PLAY_OUT_FRAME:
+        case IIS_XHEAACENC_SYNCFRAME_STREAM_MUX_CONFIG_IPF:
+            avpkt->flags |= AV_PKT_FLAG_KEY;
+            break;
+        case IIS_XHEAACENC_SYNCFRAME_INVALID:
+        default:
+            av_log(avctx, AV_LOG_ERROR, "Unexpected isSyncFrame status %d\n",
+                                        au_info.isSyncFrame);
+            return AVERROR(EINVAL);
+        }
+
+        int nb_samples_valid = au_info.auSamplesValid * avctx->sample_rate / 
s->sample_rate;
+        ff_af_queue_remove(&s->afq, nb_samples_valid, avpkt);
+    } else {
+        avpkt->size = 0;
+    }
+
+    return 0;
+}
+
+static const AVProfile profiles[] = {
+    { AV_PROFILE_AAC_USAC, "xHE-AAC" },
+    { AV_PROFILE_UNKNOWN },
+};
+
+static const FFCodecDefault xhe_aac_encode_defaults[] = {
+    { "b", "0" },
+    { NULL }
+};
+
+static const AVChannelLayout xhe_aac_ch_layouts[16] = {
+    AV_CHANNEL_LAYOUT_MONO,
+    AV_CHANNEL_LAYOUT_STEREO,
+    { 0 },
+};
+
+static const int xhe_aac_sample_rates[] = {
+    96000, 88200, 48000, 44100, 32000, 24000,
+    22050, 16000, 12000, 11025, 8000, 0
+};
+
+const FFCodec ff_libfdk_xhe_aac_encoder = {
+    .p.name                = "libfdk_xhe_aac",
+    CODEC_LONG_NAME("Fraunhofer FDK AAC"),
+    .p.type                = AVMEDIA_TYPE_AUDIO,
+    .p.id                  = AV_CODEC_ID_AAC,
+    .p.capabilities        = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
+                             AV_CODEC_CAP_SMALL_LAST_FRAME,
+    .caps_internal         = FF_CODEC_CAP_NOT_INIT_THREADSAFE,
+    .priv_data_size        = sizeof(XHEAACContext),
+    .init                  = xhe_aac_encode_init,
+    FF_CODEC_ENCODE_CB(xhe_aac_encode_frame),
+    .close                 = xhe_aac_encode_close,
+    CODEC_SAMPLEFMTS(AV_SAMPLE_FMT_FLT),
+    .p.priv_class          = &xhe_aac_enc_class,
+    .defaults              = xhe_aac_encode_defaults,
+    .p.profiles            = profiles,
+    CODEC_SAMPLERATES_ARRAY(xhe_aac_sample_rates),
+    .p.wrapper_name        = "libfdk-xhe-aac",
+    CODEC_CH_LAYOUTS_ARRAY(xhe_aac_ch_layouts),
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 2618016a83..65bc52fb24 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR  17
+#define LIBAVCODEC_VERSION_MINOR  18
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to