On 16/05/2024 12:26, Andreas Rheinhardt wrote:
Lynne via ffmpeg-devel:
This commit adds a decoder for the frequency-domain part of USAC.

What works:
  - Mono
  - Stereo (no prediction)
  - Stereo (mid/side coding)

What doesn't:
  - Preroll decoding (every single decoder seems faulty or weird?)
  - Complex stereo prediction

Known issues:
  - Spec incompliance (noise synthesis in particular)
  - Lack of robustness
---


diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h
index 20545a24d4..3e6592cf0e 100644
--- a/libavcodec/aac/aacdec.h
+++ b/libavcodec/aac/aacdec.h
@@ -42,6 +42,8 @@
  #include "libavcodec/avcodec.h"
  #include "libavcodec/mpeg4audio.h"
+#include "aacdec_ac.h"
+
  typedef struct AACDecContext AACDecContext;
/**
@@ -69,6 +71,32 @@ enum CouplingPoint {
      AFTER_IMDCT = 3,
  };
+enum AACUsacElem {
+    ID_USAC_SCE = 0,
+    ID_USAC_CPE = 1,
+    ID_USAC_LFE = 2,
+    ID_USAC_EXT = 3,
+};
+
+enum ExtensionHeaderType {
+    ID_CONFIG_EXT_FILL = 0,
+    ID_CONFIG_EXT_LOUDNESS_INFO = 2,
+    ID_CONFIG_EXT_STREAM_ID = 7,
+};
+
+enum AACUsacExtension {
+    ID_EXT_ELE_FILL,
+    ID_EXT_ELE_MPEGS,
+    ID_EXT_ELE_SAOC,
+    ID_EXT_ELE_AUDIOPREROLL,
+    ID_EXT_ELE_UNI_DRC,
+};
+
+enum AACUSACLoudnessExt {
+    UNIDRCLOUDEXT_TERM = 0x0,
+    UNIDRCLOUDEXT_EQ = 0x1,
+};
+
  // Supposed to be equal to AAC_RENAME() in case of USE_FIXED.
  #define RENAME_FIXED(name) name ## _fixed
@@ -93,6 +121,40 @@ typedef struct LongTermPrediction {
      int8_t used[MAX_LTP_LONG_SFB];
  } LongTermPrediction;
+/* Per channel core mode */
+typedef struct AACUsacElemData {
+    uint8_t core_mode;
+    uint8_t scale_factor_grouping;
+
+    /* Timewarping ratio */
+#define NUM_TW_NODES 16
+    uint8_t tw_ratio[NUM_TW_NODES];
+
+    struct {
+        uint8_t acelp_core_mode : 3;
+        uint8_t lpd_mode : 5;
+
+        uint8_t bpf_control_info : 1;
+        uint8_t core_mode_last : 1;
+        uint8_t fac_data_present : 1;
+
+        int last_lpd_mode;
+    } ldp;
+
+    struct {
+        unsigned int seed;
+        uint8_t level : 3;
+        uint8_t offset : 5;
+    } noise;
+
+    struct {
+        uint8_t gain;
+        uint32_t kv[8 /* (1024 / 16) / 8 */][8];
+    } fac;
+
+    AACArithState ac;
+} AACUsacElemData;
+
  /**
   * Individual Channel Stream
   */
@@ -145,6 +207,7 @@ typedef struct ChannelCoupling {
   */
  typedef struct SingleChannelElement {
      IndividualChannelStream ics;
+    AACUsacElemData ue;                             ///< USAC element data
      TemporalNoiseShaping tns;
      enum BandType band_type[128];                   ///< band types
      int sfo[128];                                   ///< scalefactor offsets
@@ -163,25 +226,141 @@ typedef struct SingleChannelElement {
      };
  } SingleChannelElement;
+typedef struct AACUsacStereo {
+    uint8_t common_window;
+    uint8_t common_tw;
+
+    uint8_t ms_mask_mode;
+    uint8_t config_idx;
+
+    struct {
+        uint8_t use_prev_frame;
+        uint8_t pred_dir;
+        uint8_t delta_code_time;
+        uint8_t pred_used[8][64];
+
+        AVComplexFloat pred[8][64];
+    } cplx;
+} AACUsacStereo;
+
  /**
   * channel element - generic struct for SCE/CPE/CCE/LFE
   */
  typedef struct ChannelElement {
      int present;
      // CPE specific
+    uint8_t max_sfb_ste;      ///< (USAC) Maximum of both max_sfb values
      uint8_t ms_mask[128];     ///< Set if mid/side stereo is used for each 
scalefactor window band
      // shared
      SingleChannelElement ch[2];
      // CCE specific
      ChannelCoupling coup;
+    // USAC stereo coupling data
+    AACUsacStereo us;
  } ChannelElement;
+typedef struct AACUSACLoudnessInfo {
+    uint8_t drc_set_id : 6;
+    uint8_t downmix_id : 7;
+    struct {
+        uint16_t lvl : 12;
+        uint8_t present : 1;
+    } sample_peak;
+
+    struct {
+        uint16_t lvl : 12;
+        uint8_t measurement : 4;
+        uint8_t reliability : 2;
+        uint8_t present : 1;
+    } true_peak;
+
+    uint8_t nb_measurements : 4;
+    struct {
+        uint8_t method_def : 4;
+        uint8_t method_val;
+        uint8_t measurement : 4;
+        uint8_t reliability : 2;
+    } measurements[16];
+} AACUSACLoudnessInfo;
+
+typedef struct AACUsacElemConfig {
+    enum AACUsacElem type;
+
+    uint8_t tw_mdct : 1;
+    uint8_t noise_fill : 1;
+
+    uint8_t stereo_config_index;
+
+    struct {
+        int ratio;
+
+        uint8_t harmonic_sbr : 1; /* harmonicSBR */
+        uint8_t bs_intertes : 1; /* bs_interTes */
+        uint8_t bs_pvc : 1; /* bs_pvc */
+
+        struct {
+            uint8_t start_freq; /* dflt_start_freq */
+            uint8_t stop_freq; /* dflt_stop_freq */
+
+            uint8_t freq_scale; /* dflt_freq_scale */
+            uint8_t alter_scale : 1; /* dflt_alter_scale */
+            uint8_t noise_scale; /* dflt_noise_scale */
+
+            uint8_t limiter_bands; /* dflt_limiter_bands */
+            uint8_t limiter_gains; /* dflt_limiter_gains */
+            uint8_t interpol_freq : 1; /* dflt_interpol_freq */
+            uint8_t smoothing_mode : 1; /* dflt_smoothing_mode */
+        } dflt;
+    } sbr;
+
+    struct {
+        uint8_t freq_res; /* bsFreqRes */
+        uint8_t fixed_gain; /* bsFixedGainDMX */
+        uint8_t temp_shape_config; /* bsTempShapeConfig */
+        uint8_t decorr_config; /* bsDecorrConfig */
+        uint8_t high_rate_mode : 1; /* bsHighRateMode */
+        uint8_t phase_coding : 1; /* bsPhaseCoding */
+
+        uint8_t otts_bands_phase; /* bsOttBandsPhase */
+        uint8_t residual_coding; /* bsResidualCoding */
+        uint8_t residual_bands; /* bsResidualBands */
+        uint8_t pseudo_lr : 1; /* bsPseudoLr */
+        uint8_t env_quant_mode : 1; /* bsEnvQuantMode */

Is using bitfields really worth it given that they force to use masking
for accesses?

+    } mps;
+
+    struct {
+        enum AACUsacExtension type;
+        uint8_t payload_frag;
+        uint32_t default_len;
+        uint32_t pl_data_offset;
+        uint8_t *pl_data;
+    } ext;
+} AACUsacElemConfig;
+
+typedef struct AACUSACConfig {
+    uint8_t core_sbr_frame_len_idx; /* coreSbrFrameLengthIndex */
+    uint8_t rate_idx;
+    uint16_t core_frame_len;
+    uint16_t stream_identifier;
+
+    AACUsacElemConfig elems[64];
+    int nb_elems;
+
+    struct {
+        uint8_t nb_album;
+        AACUSACLoudnessInfo album_info[64];
+        uint8_t nb_info;
+        AACUSACLoudnessInfo info[64];
+    } loudness;
+} AACUSACConfig;
+
  typedef struct OutputConfiguration {
      MPEG4AudioConfig m4ac;
      uint8_t layout_map[MAX_ELEM_ID*4][3];
      int layout_map_tags;
      AVChannelLayout ch_layout;
      enum OCStatus status;
+    AACUSACConfig usac;
  } OutputConfiguration;
/**
diff --git a/libavcodec/aac/aacdec_ac.c b/libavcodec/aac/aacdec_ac.c
new file mode 100644
index 0000000000..326d716bd3
--- /dev/null
+++ b/libavcodec/aac/aacdec_ac.c
@@ -0,0 +1,224 @@
+/*
+ * AAC definitions and structures
+ * Copyright (c) 2024 Lynne
+ *
+ * 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 "libavcodec/aactab.h"
+#include "aacdec_ac.h"
+
+uint32_t ff_aac_ac_map_process(AACArithState *state, int reset, int N)
+{
+    float ratio;
+    if (reset) {
+        memset(state->last, 0, sizeof(state->last));
+        state->last_len = N;
+        memset(state->cur, 0, sizeof(state->cur));
+        state->cur[3] = 0;
+        state->cur[2] = 0;
+        state->cur[1] = 0;
+        state->cur[0] = 1;
+        state->last[0] = 0 << 12;
+        state->state_pre = 0;
+        return 0;
+    } else if (state->last_len != N) {
+        int i;
+        uint8_t last[512 /* 2048 / 4 */];
+        memcpy(last, state->last, sizeof(last));
+
+        ratio = state->last_len / (float)N;
+        for (i = 0; i < N/2; i++) {
+            int k = (int)(i * ratio);
+            state->last[i] = last[k];
+        }
+
+        for (; i < FF_ARRAY_ELEMS(state->last); i++)
+            state->last[i] = 0;
+
+        state->last_len = N;
+    }
+
+    memset(state->cur, 0, sizeof(state->cur));
+    state->cur[3] = 0;
+    state->cur[2] = 0;
+    state->cur[1] = 0;
+    state->cur[0] = 1;
+
+    state->state_pre = state->last[0] << 12;
+    return state->last[0] << 12;
+}
+
+extern int ec_debug;
+
+int trig = 0;
+
+uint32_t ff_aac_ac_get_context(AACArithState *state, uint32_t c, int i, int N)
+{
+    c = state->state_pre >> 8;
+    c = c + (state->last[i + 1] << 8);
+    c = (c << 4);
+    c += state->cur[1];
+
+    state->state_pre = c;
+
+    if (i > 3 &&
+        ((state->cur[3] + state->cur[2] + state->cur[1]) < 5))
+        return c + 0x10000;
+
+    return c;
+}
+
+uint32_t ff_aac_ac_get_pk(uint32_t c)
+{
+    int i_min = -1;
+    int i, j;
+    int i_max = FF_ARRAY_ELEMS(ff_aac_ac_lookup_m) - 1;
+    while ((i_max - i_min) > 1) {
+        i = i_min + ((i_max - i_min) / 2);
+        j = ff_aac_ac_hash_m[i];
+        if (c < (j >> 8))
+            i_max = i;
+        else if (c > (j >> 8))
+            i_min = i;
+        else
+            return (j & 0xFF);
+    }
+    return ff_aac_ac_lookup_m[i_max];
+}
+
+void ff_aac_ac_update_context(AACArithState *state, int idx,
+                              uint16_t a, uint16_t b)
+{
+    state->cur[0] = a + b + 1;
+    if (state->cur[0] > 0xF)
+        state->cur[0] = 0xF;
+
+    state->cur[3] = state->cur[2];
+    state->cur[2] = state->cur[1];
+    state->cur[1] = state->cur[0];
+
+    state->last[idx] = state->cur[0];
+}
+
+/* Initialize AC */
+void ff_aac_ac_init(AACArith *ac, GetBitContext *gb)
+{
+    ac->low = 0;
+    ac->high = UINT16_MAX;
+    ac->val = get_bits(gb, 16);
+}
+
+uint16_t ff_aac_ac_decode(AACArith *ac, GetBitContext *gb,
+                          const uint16_t *cdf, uint16_t cdf_len)
+{
+    int val = ac->val;
+    int low = ac->low;
+    int high = ac->high;
+
+    int rng = high - low + 1;
+    int c = ((((int)(val - low + 1)) << 14) - ((int)1));
+
+    /* Note: this could be done faster via heuristics, the total number of
+     * configurations is low */
+    const uint16_t *p = cdf - 1;
+    const uint16_t *q;
+
+    switch (cdf_len) {
+    case 2:
+        if ((p[1] * rng) > c)
+            p += 1;
+        break;
+    case 4:
+        if ((p[2] * rng) > c)
+            p += 2;
+        if ((p[1] * rng) > c)
+            p += 1;
+        break;
+    case 17:
+        /* First check if the current probability is even met at all */
+        if ((p[1] * rng) <= c)
+            break;
+        p += 1;
+        for (int i = 8; i >= 1; i >>= 1)
+            if ((p[i] * rng) > c)
+                p += i;
+        break;
+    case 27:
+        const uint16_t *p_24 = p + 24;
+
+        if ((p[16] * rng) > c)
+            p += 16;
+        if ((p[8] * rng) > c)
+            p += 8;
+        if (p != p_24)
+            if ((p[4] * rng) > c)
+                p += 4;
+        if ((p[2] * rng) > c)
+            p += 2;
+
+        if (p != &p_24[2])
+            if ((p[1] * rng) > c)
+                p += 1;
+        break;
+    default:
+        /* This should never happen */
+        av_assert2(0);
+    }
+
+    int sym = (int)((ptrdiff_t)(p - cdf)) + 1;
+    if (sym)
+        high = low + ((rng * cdf[sym - 1]) >> 14) - 1;
+    low += (rng * cdf[sym]) >> 14;
+
+    /* This loop could be done faster */
+    while (1) {
+        if (high < 32768) {
+            ;
+        } else if (low >= 32768) {
+            val -= 32768;
+            low -= 32768;
+            high -= 32768;
+        } else if (low >= 16384 && high < 49152) {
+            val -= 16384;
+            low -= 16384;
+            high -= 16384;
+        } else {
+            break;
+        }
+        low += low;
+        high += high + 1;
+        val = (val << 1) | get_bits1(gb);
+    };
+
+    ac->low = low;
+    ac->high = high;
+    ac->val = val;
+
+    return sym;
+}
+
+void ff_aac_ac_finish(AACArithState *state, int offset, int N)
+{
+    int i;
+
+    for (i = offset; i < N/2; i++)
+        state->last[i] = 1;
+
+    for (; i < FF_ARRAY_ELEMS(state->last); i++)
+        state->last[i] = 0;
+}
diff --git a/libavcodec/aac/aacdec_ac.h b/libavcodec/aac/aacdec_ac.h
new file mode 100644
index 0000000000..ef96bed770
--- /dev/null
+++ b/libavcodec/aac/aacdec_ac.h
@@ -0,0 +1,54 @@
+/*
+ * AAC definitions and structures
+ * Copyright (c) 2024 Lynne
+ *
+ * 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 AVCODEC_AACDEC_AC_H
+#define AVCODEC_AACDEC_AC_H
+
+#include "libavcodec/get_bits.h"
+
+typedef struct AACArithState {
+    uint8_t last[512 /* 2048 / 4 */];
+    int last_len;
+    uint8_t cur[4];
+    uint16_t state_pre;
+} AACArithState;
+
+typedef struct AACArith {
+    uint16_t low;
+    uint16_t high;
+    uint16_t val;
+} AACArith;
+
+#define FF_AAC_AC_ESCAPE 16
+
+uint32_t ff_aac_ac_map_process(AACArithState *state, int reset, int len);
+uint32_t ff_aac_ac_get_context(AACArithState *state, uint32_t old_c, int idx, 
int len);
+uint32_t ff_aac_ac_get_pk(uint32_t c);
+
+void ff_aac_ac_update_context(AACArithState *state, int idx, uint16_t a, 
uint16_t b);
+void ff_aac_ac_init(AACArith *ac, GetBitContext *gb);
+
+uint16_t ff_aac_ac_decode(AACArith *ac, GetBitContext *gb,
+                          const uint16_t *cdf, uint16_t cdf_len);
+
+void ff_aac_ac_finish(AACArithState *state, int offset, int nb);
+
+#endif /* AVCODEC_AACDEC_AC_H */
diff --git a/libavcodec/aac/aacdec_dsp_template.c 
b/libavcodec/aac/aacdec_dsp_template.c
index 59a69d88f3..8d31af22f8 100644
--- a/libavcodec/aac/aacdec_dsp_template.c
+++ b/libavcodec/aac/aacdec_dsp_template.c
@@ -88,8 +88,8 @@ static void AAC_RENAME(apply_mid_side_stereo)(AACDecContext 
*ac, ChannelElement
      INTFLOAT *ch1 = cpe->ch[1].AAC_RENAME(coeffs);
      const uint16_t *offsets = ics->swb_offset;
      for (int g = 0; g < ics->num_window_groups; g++) {
-        for (int sfb = 0; sfb < ics->max_sfb; sfb++) {
-            const int idx = g*ics->max_sfb + sfb;
+        for (int sfb = 0; sfb < cpe->max_sfb_ste; sfb++) {
+            const int idx = g*cpe->max_sfb_ste + sfb;
              if (cpe->ms_mask[idx] &&
                  cpe->ch[0].band_type[idx] < NOISE_BT &&
                  cpe->ch[1].band_type[idx] < NOISE_BT) {
diff --git a/libavcodec/aac/aacdec_latm.h b/libavcodec/aac/aacdec_latm.h
index e40a2fe1a7..047c11e0fb 100644
--- a/libavcodec/aac/aacdec_latm.h
+++ b/libavcodec/aac/aacdec_latm.h
@@ -56,7 +56,8 @@ static int latm_decode_audio_specific_config(struct 
LATMContext *latmctx,
  {
      AACDecContext *ac     = &latmctx->aac_ctx;
      AVCodecContext *avctx = ac->avctx;
-    MPEG4AudioConfig m4ac = { 0 };
+    OutputConfiguration oc = { 0 };
+    MPEG4AudioConfig *m4ac = &oc.m4ac;
      GetBitContext gbc;
      int config_start_bit  = get_bits_count(gb);
      int sync_extension    = 0;
@@ -76,7 +77,7 @@ static int latm_decode_audio_specific_config(struct 
LATMContext *latmctx,
      if (get_bits_left(gb) <= 0)
          return AVERROR_INVALIDDATA;
- bits_consumed = decode_audio_specific_config_gb(NULL, avctx, &m4ac,
+    bits_consumed = decode_audio_specific_config_gb(NULL, avctx, &oc,
                                                      &gbc, config_start_bit,
                                                      sync_extension);
@@ -88,11 +89,12 @@ static int latm_decode_audio_specific_config(struct LATMContext *latmctx,
        asclen = bits_consumed;
if (!latmctx->initialized ||
-        ac->oc[1].m4ac.sample_rate != m4ac.sample_rate ||
-        ac->oc[1].m4ac.chan_config != m4ac.chan_config) {
+        ac->oc[1].m4ac.sample_rate != m4ac->sample_rate ||
+        ac->oc[1].m4ac.chan_config != m4ac->chan_config) {
if (latmctx->initialized) {
-            av_log(avctx, AV_LOG_INFO, "audio config changed (sample_rate=%d, 
chan_config=%d)\n", m4ac.sample_rate, m4ac.chan_config);
+            av_log(avctx, AV_LOG_INFO, "audio config changed (sample_rate=%d, 
chan_config=%d)\n",
+                   m4ac->sample_rate, m4ac->chan_config);
          } else {
              av_log(avctx, AV_LOG_DEBUG, "initializing latmctx\n");
          }
@@ -280,7 +282,7 @@ static int latm_decode_frame(AVCodecContext *avctx, AVFrame 
*out,
          } else {
              push_output_configuration(&latmctx->aac_ctx);
              if ((err = decode_audio_specific_config(
-                    &latmctx->aac_ctx, avctx, &latmctx->aac_ctx.oc[1].m4ac,
+                    &latmctx->aac_ctx, avctx, &latmctx->aac_ctx.oc[1],
                      avctx->extradata, avctx->extradata_size*8LL, 1)) < 0) {
                  pop_output_configuration(&latmctx->aac_ctx);
                  return err;
diff --git a/libavcodec/aac/aacdec_lpd.c b/libavcodec/aac/aacdec_lpd.c
new file mode 100644
index 0000000000..be39e2c175
--- /dev/null
+++ b/libavcodec/aac/aacdec_lpd.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2024 Lynne <d...@lynne.ee>
+ *
+ * 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 "aacdec_lpd.h"
+#include "aacdec_usac.h"
+
+const uint8_t ff_aac_lpd_mode_tab[32][4] = {
+    { 0, 0, 0, 0 },
+    { 1, 0, 0, 0 },
+    { 0, 1, 0, 0 },
+    { 1, 1, 0, 0 },
+    { 0, 0, 1, 0 },
+    { 1, 0, 1, 0 },
+    { 0, 1, 1, 0 },
+    { 1, 1, 1, 0 },
+    { 0, 0, 0, 1 },
+    { 1, 0, 0, 1 },
+    { 0, 1, 0, 1 },
+    { 1, 1, 0, 1 },
+    { 0, 0, 1, 1 },
+    { 1, 0, 1, 1 },
+    { 0, 1, 1, 1 },
+    { 1, 1, 1, 1 },
+    { 2, 2, 0, 0 },
+    { 2, 2, 1, 0 },
+    { 2, 2, 0, 1 },
+    { 2, 2, 1, 1 },
+    { 0, 0, 2, 2 },
+    { 1, 0, 2, 2 },
+    { 0, 1, 2, 2 },
+    { 1, 1, 2, 2 },
+    { 2, 2, 2, 2 },
+    { 3, 3, 3, 3 },
+    /* Larger values are reserved, but permit them for resilience */
+    { 0, 0, 0, 0 },
+    { 0, 0, 0, 0 },
+    { 0, 0, 0, 0 },
+    { 0, 0, 0, 0 },
+    { 0, 0, 0, 0 },
+    { 0, 0, 0, 0 },
+};
+
+static void parse_qn(GetBitContext *gb, int *qn, int nk_mode, int no_qn)
+{
+    if (nk_mode == 1) {
+        for (int k = 0; k < no_qn; k++) {
+            qn[k] = ff_aac_get_vlclbf(gb);
+            if (qn[k])
+                qn[k]++;
+        }
+        return;
+    }
+
+    for (int k = 0; k < no_qn; k++)
+        qn[k] = get_bits(gb, 2) + 2;
+
+    if (nk_mode == 2) {
+        for (int k = 0; k < no_qn; k++) {
+            if (qn[k] > 4) {
+                qn[k] = ff_aac_get_vlclbf(gb);
+                if (qn[k])
+                    qn[k] += 4;
+            }
+        }
+        return;
+    }
+
+    for (int k = 0; k < no_qn; k++) {
+        if (qn[k] > 4) {
+            int qn_ext = ff_aac_get_vlclbf(gb);
+            switch (qn_ext) {
+            case 0: qn[k] = 5; break;
+            case 1: qn[k] = 6; break;
+            case 2: qn[k] = 0; break;
+            default: qn[k] = qn_ext + 4; break;
+            }
+        }
+    }
+}
+
+static int parse_codebook_idx(GetBitContext *gb, uint32_t *kv,
+                              int nk_mode, int no_qn)
+{
+    int n, nk;
+
+    int qn[2];
+    parse_qn(gb, qn, nk_mode, no_qn);
+
+    for (int k = 0; k < no_qn; k++) {
+        if (qn[k] > 4) {
+            nk = (qn[k] - 3) / 2;
+            n = qn[k] - nk*2;
+        } else {
+            nk = 0;
+            n = qn[k];
+        }
+    }
+
+    int idx = get_bits(gb, 4*n);
+
+    if (nk > 0)
+        for (int i = 0; i < 8; i++)
+            kv[i] = get_bits(gb, nk);
+
+    return 0;
+}
+
+int ff_aac_parse_fac_data(AACUsacElemData *ce, GetBitContext *gb,
+                          int use_gain, int len)
+{
+    int ret;
+    if (use_gain)
+        ce->fac.gain = get_bits(gb, 7);
+
+    for (int i = 0; i < len/8; i++) {
+        ret = parse_codebook_idx(gb, ce->fac.kv[i], 1, 1);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+int ff_aac_ldp_parse_channel_stream(AACDecContext *ac, AACUSACConfig *usac,
+                                    AACUsacElemData *ce, GetBitContext *gb)
+{
+    ce->ldp.acelp_core_mode = get_bits(gb, 3);
+    ce->ldp.lpd_mode = get_bits(gb, 5);
+
+    ce->ldp.bpf_control_info = get_bits1(gb);
+    ce->ldp.core_mode_last = get_bits1(gb);
+    ce->ldp.fac_data_present = get_bits1(gb);
+
+    const uint8_t *mod = ff_aac_lpd_mode_tab[ce->ldp.lpd_mode];
+
+    int first_ldp_flag = !ce->ldp.core_mode_last;
+    int first_tcx_flag = 1;
+    if (first_ldp_flag)
+        ce->ldp.last_lpd_mode = -1; /* last_ldp_mode is a **STATEFUL** value */
+
+    int k = 0;
+    while (k < 0) {
+        if (!k) {
+            if (ce->ldp.core_mode_last && ce->ldp.fac_data_present)
+                ff_aac_parse_fac_data(ce, gb, 0, usac->core_frame_len/8);
+        } else {
+            if (!ce->ldp.last_lpd_mode && mod[k] > 0 ||
+                ce->ldp.last_lpd_mode && !mod[k])
+                ff_aac_parse_fac_data(ce, gb, 0, usac->core_frame_len/8);
+        }
+        if (!mod[k]) {
+//            parse_acelp_coding();
+            ce->ldp.last_lpd_mode = 0;
+            k++;
+        } else {
+//            parse_tcx_coding();
+            ce->ldp.last_lpd_mode = mod[k];
+            k += (1 << (mod[k] - 1));
+            first_tcx_flag = 0;
+        }
+    }
+
+//    parse_lpc_data(first_lpd_flag);
+
+    if (!ce->ldp.core_mode_last && ce->ldp.fac_data_present) {
+        uint16_t len_8 = usac->core_frame_len / 8;
+        uint16_t len_16 = usac->core_frame_len / 16;
+        uint16_t fac_len = get_bits1(gb) /* short_fac_flag */ ? len_8 : len_16;
+        int ret = ff_aac_parse_fac_data(ce, gb, 1, fac_len);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
diff --git a/libavcodec/aac/aacdec_lpd.h b/libavcodec/aac/aacdec_lpd.h
new file mode 100644
index 0000000000..924ff75e52
--- /dev/null
+++ b/libavcodec/aac/aacdec_lpd.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2024 Lynne <d...@lynne.ee>
+ *
+ * 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 AVCODEC_AAC_AACDEC_LPD_H
+#define AVCODEC_AAC_AACDEC_LPD_H
+
+#include "aacdec.h"
+#include "libavcodec/get_bits.h"
+
+int ff_aac_parse_fac_data(AACUsacElemData *ce, GetBitContext *gb,
+                          int use_gain, int len);
+
+int ff_aac_ldp_parse_channel_stream(AACDecContext *ac, AACUSACConfig *usac,
+                                    AACUsacElemData *ce, GetBitContext *gb);
+
+#endif /* AVCODEC_AAC_AACDEC_LPD_H */
diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c
new file mode 100644
index 0000000000..4b48c4d6ca
--- /dev/null
+++ b/libavcodec/aac/aacdec_usac.c
@@ -0,0 +1,1230 @@
+/*
+ * Copyright (c) 2024 Lynne <d...@lynne.ee>
+ *
+ * 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 "aacdec_usac.h"
+#include "aacdec_tab.h"
+#include "aacdec_lpd.h"
+#include "aacdec_ac.h"
+
+#include "libavcodec/opusdsp.h"
+#include "libavcodec/aactab.h"
+#include "libavutil/mem.h"
+#include "libavcodec/mpeg4audio.h"
+
+/* Number of scalefactor bands per complex prediction band, equal to 2. */
+#define SFB_PER_PRED_BAND 2
+
+static inline uint32_t get_escaped_value(GetBitContext *gb, int nb1, int nb2, 
int nb3)
+{
+    uint32_t val = get_bits(gb, nb1);
+    if (val < ((1 << nb1) - 1))
+        return val;
+
+    val += get_bits(gb, nb2);
+    if (val == ((1 << nb2) - 1))
+        val += get_bits(gb, nb3);
+
+    return val;
+}
+
+static int aac_usac_samplerate[] = {

Missing const

+    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+    16000, 12000, 11025, 8000, 7350, -1, -1, 57600, 51200,
+    40000, 38400, 34150, 28800, 25600, 20000, 19200, 17075, 14400, 12800, 
9600, -1, -1, -1, -1,
+};
+

+static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
+                         GetBitContext *gb)
+{
+    if (get_bits1(gb)) { /* usacExtElementPresent */
+        uint32_t len;
+        if (get_bits1(gb)) { /* usacExtElementUseDefaultLength */
+            len = e->ext.default_len;
+        } else {
+            len = get_bits(gb, 8); /* usacExtElementPayloadLength */
+            if (len == 255)
+                len += get_bits(gb, 16) - 2;
+        }
+
+        if (len) {
+            uint8_t *tmp;
+            uint8_t pl_frag_start = 1;
+            uint8_t pl_frag_end = 1;
+            if (e->ext.payload_frag) {
+                pl_frag_start = get_bits1(gb); /* usacExtElementStart */
+                pl_frag_end = get_bits1(gb); /* usacExtElementStop */
+            }
+
+            if (pl_frag_start)
+                e->ext.pl_data_offset = 0;
+
+            tmp = av_realloc(e->ext.pl_data, e->ext.pl_data_offset + len);
+            if (!tmp) {
+                free(e->ext.pl_data);

Wrong deallocator.

+                return AVERROR(ENOMEM);
+            }
+            e->ext.pl_data = tmp;
+
+            for (int i = 0; i < len; i++)
+                e->ext.pl_data[e->ext.pl_data_offset + i] = get_bits(gb, 8);
+
+            if (pl_frag_end) {
+                int ret;
+                e->ext.pl_data_offset = 0;
+                switch (e->ext.type) {
+                case ID_EXT_ELE_FILL:
+                    av_freep(&e->ext.pl_data);
+                    break;
+                case ID_EXT_ELE_AUDIOPREROLL:
+                    ret = parse_audio_preroll(ac, e->ext.pl_data,
+                                              e->ext.pl_data_offset);
+                    if (ret < 0) {
+                        av_freep(&e->ext.pl_data);
+                        return ret;
+                    }
+                    break;
+                default:
+                    av_freep(&e->ext.pl_data);

Pointless if you abort in the next line

+                    /* This should never happen */
+                    av_assert0(0);
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+


+#include "libavcodec/opusdsp.h"
+
+#ifndef AVCODEC_AAC_AACDEC_USAC_H
+#define AVCODEC_AAC_AACDEC_USAC_H
+
+#include "aacdec.h"
+
+#include "libavcodec/get_bits.h"
+
+static inline uint8_t ff_aac_get_vlclbf(GetBitContext *gb)
+{
+    uint8_t ret = 0;
+    while (get_bits1(gb) && ret <= 36)
+        ret++;
+    return ret;
+}

Look at unary.h

That's convenient, thanks.

I've synced my changes on my repo in
https://github.com/cyanreg/FFmpeg/tree/xhe
Though I'll likely upload the patchset on the ML again tomorrow with some fixes for preroll parsing and complex synth.

Attachment: OpenPGP_0xA2FEA5F03F034464.asc
Description: OpenPGP public key

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".

Reply via email to