This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 98afaae587865cfe451b3ff2f5be0b97190edefd Author: Lynne <[email protected]> AuthorDate: Wed Sep 23 20:32:59 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Sun Oct 4 17:51:10 2026 +0900 avcodec/aaccoder_nmr, aacenc, aacpsy: -aac_rc abr -aac_rc cbr|abr (default cbr). ABR runs the nd-target quality solve with the set-point owned by a slow rate servo, and joins VBR as a quality-target mode in the psy model (fixed-reference PE reduction, quality attack map, broadband mask floor). The servo integrates the log rate error but applies it to the nd set-point in rare, discrete, calm-gated steps. The set-point must be quasi-static: any drift on content timescales moves lambda, and with it band/PNS/scalefactor state, tracking sections in antiphase and coarsening exactly what needs bits - measured 2x worse than a fixed target at equal mean rate. Locally this mode IS fixed-target VBR; rate honesty converges on the minutes scale. Bootstrap. The set-point is seeded from the VBR calibration anchor (one constant, NMR_VBR_ANCHOR: the two are required to agree, and moving only one copy made ABR overshoot its rate target by up to 9%). After ~2s of fast rate measurement off a representative window (an all-transient head reads over-target and would coarsen the very content that needs bits), an open-loop jump over the measured loop gain (~ -0.24 log2 rate per target unit) corrects the seed's per-content error, applied at 1.2x - the estimator's own inverse warm-up factor, since the EMA is seeded on target (a cold start must not read as a fake deficit) and so recovers only ~82% of the error. One correction is not enough whatever the gain estimate, because the 0.24 loop gain is only nominal: the boot re-arms while the rate is still off, up to a bounded number of times, re-measuring each time, and re-seeds the estimator on target rather than to zero (a zero estimate pins the integrator at its clip via log2f(0) and reads as a 100% deficit to the fill in the same frame). Its settle window sits below the stepper's hold, or the stepper resets the shared counter first and the re-arm never fires. Every set-point correction is glided in at ~0.5 log2-units/s rather than stepped: a one-frame noise-floor jump at a fixed stream time (the boot lands at ~2s in every stream) is audible against revealing content even at step sizes the corpus cannot see. Bits-fill. Under the long-run target, an easy frame is topped up toward the ask by moving lambda finer only - the quality target is a floor, never traded away. It spends the rate the user asked for on sub-mask margin the nd statistic cannot see (it clips at -4/band); without it, stat-transparent content caps the rate below any ask. The fill runs from frame one and fades continuously as the rate EMA converges (a boot-gated fill flipped regimes at ~2s, an audible quality step), follows the psy PE demand shape, and is slew-bounded, writing the lambda continuity state from long frames only instead of free-diving up to 64x per frame. The un-booted stream head behaves CBR-like until the servo takes over (love.flac: >6k energy had swung 0.61-1.83x vs the source at frame rate while the head ran 25% under the ask). The reachable-range clamps do not track the calibration anchor: their floors are set by the stat's -4/band sub-mask clip. A floor at -1.65 railed the servo on easy content (male_speech at a 128k ask pinned at the clamp with a 10% standing deficit); -3.0 keeps 384k asks reachable without the stall. Easy content can still fall short of very high asks (main_theme: 196k at a 256k ask) - there is no padding. The coarse clamp is a quality guard: set-points past it buy little rate for a lot of quality (2.5 -> 6.0 at a 48k ask: -5% rate, +32% Zimtohrli), so asks below about 56 kbps stereo land above the ask (24k/32k asks: ~40k). The documentation points those rates at CBR. Rate accuracy against the -b:a ask: corpus bias mean|err| worst IgorC-3 -1.3% 2.2% -11.8% IgorC-1 +1.1% 2.0% +5.0% 32 random -0.3% 2.2% -10.0% A 4.5-minute concatenation of the IgorC-3 samples lands at 128.2 kbps on a 128k ask. castanets/velvet ABR beat CBR; ABR corpus 0.00060 at 126k. -aac_rc is documented, and fate-aac-nmr-abr-encode pins the per-packet sizes through the servo's boot. --- doc/encoders.texi | 16 ++++ libavcodec/aaccoder_nmr.h | 192 ++++++++++++++++++++++++++++++++++++-- libavcodec/aacenc.c | 9 +- libavcodec/aacenc.h | 13 +++ libavcodec/aacpsy.c | 10 +- tests/fate/aac.mak | 4 + tests/ref/fate/aac-nmr-abr-encode | 138 +++++++++++++++++++++++++++ 7 files changed, 367 insertions(+), 15 deletions(-) diff --git a/doc/encoders.texi b/doc/encoders.texi index 98c5f22da5..fcb085f2b8 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -85,6 +85,22 @@ and much faster at higher bitrates. @end table +@item aac_rc +Rate-control mode of the @samp{nmr} coder when a bitrate is set with @option{b} +(other coders ignore it; @option{q} takes precedence). Possible values: + +@table @samp +@item cbr +Constant bitrate within the decoder's bit reservoir. This is the default. + +@item abr +Average bitrate: a constant-quality target, as in VBR, whose set-point a slow +servo moves to meet @option{b} over the length of the stream. Local quality +stays constant, so short files and individual passages can deviate from the +requested rate; the average converges over minutes rather than seconds. Below +roughly 56 kbps stereo the rate lands above the request; use @samp{cbr} there. +@end table + @item aac_ms Sets mid/side coding mode. The default value of "auto" will automatically use M/S with bands which will benefit from such coding. Can be forced for all bands diff --git a/libavcodec/aaccoder_nmr.h b/libavcodec/aaccoder_nmr.h index 398cc91059..fd7d6e1cbc 100644 --- a/libavcodec/aaccoder_nmr.h +++ b/libavcodec/aaccoder_nmr.h @@ -91,18 +91,45 @@ /* Quality-target calibration anchor: the nd set-point of -q:a 1, which lands * near 128-136 kbps stereo on the tuning corpora with the q ladder's own - * bandwidth. It moved -2.1 -> -0.75 with the quality-target mask floor - * (PSY_THRFL_QUALITY), which lowers thr_real and so lifts the whole statistic. - * The reachable-range clamp is set by the stat's -4/band sub-mask clip, which - * the mask floor does not touch: targets below it are asymptotically - * unreachable - the bisect rails at the finest lambda, the frame exceeds the - * decoder buffer and the outer re-encode loop never converges. */ + * bandwidth. The ABR servo seeds off the same constant (referenced to 81.5 + * kbps/ch, where it measured before the bandwidth retune; the boot corrects + * the per-content seed error), so they are one constant. It moved + * -2.1 -> -0.75 with the quality-target mask floor (PSY_THRFL_QUALITY), which + * lowers thr_real and so lifts the whole statistic. + * The reachable-range clamps did NOT move + * with it: their floors are set by the stat's -4/band sub-mask clip, which the + * mask floor does not touch. Targets below them are asymptotically unreachable + * - the bisect rails at the finest lambda, the frame exceeds the decoder buffer + * and the outer re-encode loop never converges (the 384k stall). Raising them + * with the anchor railed the ABR servo instead: it wanted a finer target to + * meet its rate ask on easy content and could not ask for one. */ #define NMR_VBR_ANCHOR (-0.75f) #define NMR_VBR_TMIN (-3.8f) +/* ABR set-point range. The coarse end is a quality guard: set-points past it + * buy little rate for a lot of quality (2.5 -> 6.0 at a 48k ask: -5% rate, + * +32% Zimtohrli), so asks below ~56 kbps stereo land above the ask - CBR + * is the mode for those rates. At the fine end, easy content can fall short + * of very high asks (no padding). */ +#define NMR_ABR_TMIN (-3.0f) +#define NMR_ABR_TMAX 2.5f + +/* ABR servo: integrator gain on the log rate error, rate EMA weight after the + * boot, set-point step size and minimum frames between steps */ +#define NMR_ABR_K 0.003f +#define NMR_ABR_EMA 0.0023f +#define NMR_ABR_STEP 0.15f +#define NMR_ABR_HOLD 120 +/* ABR boot: open-loop correction gain over the nominal loop gain, the rate + * error that re-arms it, the re-arm budget and the settle time before one */ +#define NMR_ABR_BOOT_GAIN 1.2f +#define NMR_ABR_TOL 0.02f +#define NMR_ABR_BOOTS 6 +#define NMR_ABR_SETTLE 100 /* VBR shorts: the measured stat inflation counts fully once this share of * the frame's energy sits above 6 kHz */ #define NMR_INFL_HF 0.10f + /* Reservoir half-window (bits/ch); swept 512/1536/3072, 1536 optimal. */ #define NMR_CBR_BUF 1536 /* Slew limit on the FINAL operating lambda per frame; bits deviate instead, @@ -754,9 +781,14 @@ static void nmr_solve_group(AVCodecContext *avctx, AACEncContext *s, } int vbr = (avctx->flags & AV_CODEC_FLAG_QSCALE) && s->nmr; + int abr = !vbr && s->options.rc == 1 && avctx->bit_rate > 0 && s->nmr; int vbr_subst = 1; float vbr_t = 0.0f; - if (vbr) { + if (abr) { + /* ABR: same quality-target solve, set-point owned by the rate servo */ + vbr_t = s->nmr->abr_t; + vbr = 1; + } else if (vbr) { /* nd-target VBR: constant achieved noise-to-mask, bits float. */ /* target in log2(dist/real-mask), anchored so -q:a 1 lands near * 128 kbps stereo on the tuning corpus; higher q is finer, each q @@ -1011,6 +1043,48 @@ static void nmr_solve_group(AVCodecContext *avctx, AACEncContext *s, hardcap = FFMAX(hardcap - (int)(FFMAX(s->nmr->side_ema, 0.0f) * chans / s->channels), 256); for (int k = 0; k < nsl; k++) tot += nmr_slot_bits(sl[k], s->nmr->nb[sl[k]->si], NMR_STEP); + if (abr && s->nmr->abr_ema < rc_rate_frame) { + /* bits-fill: under the long-run target, top an easy frame up + * toward the ask by moving lambda FINER only - the quality + * target is a floor, never traded away. Spends the rate the + * user asked for on sub-mask margin the nd statistic cannot + * see (it clips at -4/band); without this, stat-transparent + * content caps the rate below any ask. Active from frame one + * (the immature EMA reads as a deficit, so the stream head is + * held near the ask) and fades continuously as the EMA + * converges - a boot-gated fill flips regimes at ~2s, an + * audible quality step. + * The fill target follows the psy PE demand shape: per-frame + * demand carries perceptual information the nd model does not + * (the constant-lambda lesson) - and routing it through the + * fill keeps the quality floor intact. */ + float dshape = 1.0f; + int fill; + if (s->psy.bitres.alloc > 0) { + float *aema = &s->nmr->abr_alloc_ema; + if (*aema <= 0.0f) *aema = s->psy.bitres.alloc; + else *aema += 0.01f * (s->psy.bitres.alloc - *aema); + dshape = av_clipf(s->psy.bitres.alloc / *aema, 0.6f, 1.7f); + } + fill = (int)((rc_rate_frame + (rc_rate_frame - (int)s->nmr->abr_ema)) * dshape) * + chans / s->channels; + fill = FFMIN3(fill, 3 * rc_rate_frame * chans / s->channels / 2, hardcap); + if (tot < fill) { + /* the fill dive is the easy-frame operating point: slew it + * like any other lambda move and let it carry the + * continuity state, or per-frame depth variation reads as + * frame-rate HF wobble */ + float flo = lam / 64.0f; + if (*vslew_st > 0.0f) + flo = FFMIN(FFMAX(flo, *vslew_st / NMR_SLEW), lam); + lam = nmr_solve_slots(s, sl, nsl, NMR_STEP, fill, flo, lam, NMR_RC_ITERS); + if (!is8_any) + *vslew_st = s->nmr->lam_slew = lam; + tot = 0; + for (int k = 0; k < nsl; k++) + tot += nmr_slot_bits(sl[k], s->nmr->nb[sl[k]->si], NMR_STEP); + } + } if (tot > hardcap) { lam = nmr_solve_slots(s, sl, nsl, NMR_STEP, hardcap, lam, 1e4f, NMR_RC_ITERS); tot = 0; @@ -1280,7 +1354,7 @@ static void search_for_quantizers_nmr(AVCodecContext *avctx, /* Global-lambda RC: one solve per frame at a servoed centre lambda; the reservoir * holds the long-run mean rate. Bypassed for VBR (-q:a) and the bootstrap frame. */ int rc_eligible = !(avctx->flags & AV_CODEC_FLAG_QSCALE) && avctx->bit_rate > 0 && - avctx->bit_rate_tolerance != 0; + avctx->bit_rate_tolerance != 0 && s->options.rc == 0; /* Signed reservoir; soft steering (bounded repay + rc_off), hard cap = * legality only. */ int rc_rate_frame = avctx->bit_rate * 1024.0 / avctx->sample_rate; @@ -1291,6 +1365,108 @@ static void search_for_quantizers_nmr(AVCodecContext *avctx, s->nmr->counted[s->cur_channel] = 0; + if (s->options.rc == 1 && !(avctx->flags & AV_CODEC_FLAG_QSCALE) && + avctx->bit_rate > 0 && avctx->frame_num != n->abr_frame_num) { + /* ABR servo: integrate the log rate error, but apply it to the nd + * set-point in RARE, DISCRETE steps. The set-point must be + * quasi-static: any drift on content timescales moves lambda, and + * with it band/PNS/scalefactor state - measured 2x worse than a + * fixed target at equal mean rate. Locally this mode IS fixed-target + * VBR; rate honesty converges on the minutes scale. */ + if (n->abr_frame_num == 0 && n->abr_ema <= 0.0f) { + /* seed the set-point from the VBR calibration anchor (see + * NMR_VBR_ANCHOR for the 81.5 kbps/ch reference); the servo + * trims the rest */ + n->abr_t = av_clipf(NMR_VBR_ANCHOR - 2.5f * log2f(avctx->bit_rate / + (81500.0f * s->channels)), NMR_ABR_TMIN, NMR_ABR_TMAX); + n->abr_ema = rc_rate_frame; + } else if (s->last_frame_pb_count > 0) { + /* bootstrap: fast rate measurement for ~2s, then one open-loop + * jump over the measured loop gain (~ -0.24 log2 rate per target + * unit) corrects the seed's per-content error; the quasi-static + * stepper handles drift from there */ + /* seed on-target: an EMA warming up from zero reads as a fake + * deficit and the fill overspends the whole stream head */ + if (n->abr_ema <= 0.0f) + n->abr_ema = rc_rate_frame; + n->abr_ema += (n->abr_booted ? NMR_ABR_EMA : 0.02f) * + (s->last_frame_pb_count - n->abr_ema); + n->abr_hold++; + if (!n->abr_booted) { + n->abr_longs += sce->ics.window_sequence[0] != EIGHT_SHORT_SEQUENCE; + /* boot only off a REPRESENTATIVE window: an all-transient + * head (castanets roll) reads over-target and would coarsen + * the very content that needs bits. Time out eventually. */ + if ((n->abr_hold >= 86 && n->abr_longs >= n->abr_hold / 2) || + n->abr_hold >= 400) { + /* glide the seed correction in, never step it: a + * one-frame noise-floor jump at a fixed stream time is + * audible against revealing content */ + n->abr_glide = NMR_ABR_BOOT_GAIN * log2f(n->abr_ema / rc_rate_frame) / 0.24f; + n->abr_booted = 1; + n->abr_boots++; + n->abr_hold = 0; + } + } else { + /* Re-arm the open-loop correction while the rate is still + * off. One boot leaves a residual whenever the real loop gain + * differs from the nominal 0.24, and the estimator is biased + * toward the ask by construction (the EMA is seeded ON target + * so a cold start cannot read as a deficit), so it understates + * the error it is correcting. The quasi-static stepper cannot + * drain that inside a track - measured, male_speech ended a + * 774-frame file 23% short and still moving. Re-measuring and + * firing again converges regardless of the gain estimate, and + * keeps the set-point quasi-static: corrections stay rare, + * glided, and bounded in number. */ + if (n->abr_boots < NMR_ABR_BOOTS && n->abr_glide == 0.0f && + n->abr_hold >= NMR_ABR_SETTLE && + fabsf(log2f(n->abr_ema / rc_rate_frame)) > NMR_ABR_TOL) { + /* Re-seed the estimator ON target, exactly as the cold + * start does - never to zero. The value is read again + * before anything re-seeds it: by the integrator below + * (log2f(0) is -inf, which pinned the accumulator at its + * clip) and by the bits-fill later in this same frame + * (a zero EMA reads as a 100% deficit and authorises a + * one-frame overspend). Drop the residual integral with + * it: it describes the regime being left, and the boot + * is about to re-measure that same error open-loop. */ + n->abr_booted = 0; + n->abr_ema = rc_rate_frame; + n->abr_acc = 0.0f; + n->abr_hold = 0; + n->abr_longs = 0; + } else { + n->abr_acc = av_clipf(n->abr_acc + NMR_ABR_K * log2f(n->abr_ema / rc_rate_frame), + -4.0f * NMR_ABR_STEP, 4.0f * NMR_ABR_STEP); + /* step in calm stretches (a set-point move during a transient + * section coarsens exactly what needs the bits) - but dense + * content must not deadlock the servo: after 3x the hold the + * step fires regardless. Large errors step at double size; + * the remainder carries over instead of being discarded. */ + if (fabsf(n->abr_acc) >= NMR_ABR_STEP && n->abr_hold >= NMR_ABR_HOLD && + (n->frames_since_short >= 12 || n->abr_hold >= 3 * NMR_ABR_HOLD)) { + float ms = NMR_ABR_STEP * (fabsf(n->abr_acc) > 3.0f * NMR_ABR_STEP ? 2.0f : 1.0f); + float step = av_clipf(n->abr_acc, -ms, ms); + n->abr_glide += step; + n->abr_acc -= step; + n->abr_hold = 0; + } + } + } + if (n->abr_glide != 0.0f) { + /* drain pending set-point corrections smoothly (~0.5 + * log2-units/s): the decision stays quasi-static and + * calm-gated, only the application glides */ + float d = av_clipf(n->abr_glide, -0.012f, 0.012f); + n->abr_t = av_clipf(n->abr_t + d, NMR_ABR_TMIN, NMR_ABR_TMAX); + n->abr_glide -= d; + if (fabsf(n->abr_glide) < 1e-4f) + n->abr_glide = 0.0f; + } + } + n->abr_frame_num = avctx->frame_num; + } if (rc_eligible && !n->rc_fill_seeded) { /* the decoder bit reservoir starts FULL: seed it so the head may frontload */ n->rc_fill = rc_bmax; diff --git a/libavcodec/aacenc.c b/libavcodec/aacenc.c index e415f54247..b6d72e9368 100644 --- a/libavcodec/aacenc.c +++ b/libavcodec/aacenc.c @@ -1692,12 +1692,12 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, avpkt->size = put_bytes_output(&s->pb); /* NMR reports its real operating lambda: the corridor centre in CBR, - * the quality-mode slew state in VBR - the lambda of the last long + * the quality-mode slew state in VBR/ABR - the lambda of the last long * operating-point solve, which short frames and legality re-solves do * not update (the outer-loop s->lambda is never touched for this coder * and would pin Qavg at its 120 init) */ s->lambda_sum += (s->nmr && s->nmr->lam_slew > 0.0f && - (avctx->flags & AV_CODEC_FLAG_QSCALE)) ? + ((avctx->flags & AV_CODEC_FLAG_QSCALE) || s->options.rc == 1)) ? s->nmr->lam_slew : (s->nmr && s->nmr->lam_rc > 0.0f) ? s->nmr->lam_rc : s->lambda; s->lambda_count++; @@ -1964,7 +1964,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) lengths[1] = ff_aac_num_swb_128[s->samplerate_index]; for (i = 0; i < s->chan_map[0]; i++) grouping[i] = s->chan_map[i + 1] == TYPE_CPE; - s->psy.unbounded_pe = (avctx->flags & AV_CODEC_FLAG_QSCALE) && + s->psy.unbounded_pe = ((avctx->flags & AV_CODEC_FLAG_QSCALE) || s->options.rc == 1) && s->options.coder == AAC_CODER_NMR; if ((ret = ff_psy_init(&s->psy, avctx, 2, sizes, lengths, s->chan_map[0], grouping, s->bandwidth)) < 0) @@ -1991,6 +1991,9 @@ static const AVOption aacenc_options[] = { {"aac_pns", "Perceptual noise substitution", offsetof(AACEncContext, options.pns), AV_OPT_TYPE_BOOL, {.i64 = 1}, -1, 1, AACENC_FLAGS}, {"aac_tns", "Temporal noise shaping", offsetof(AACEncContext, options.tns), AV_OPT_TYPE_BOOL, {.i64 = 1}, -1, 1, AACENC_FLAGS}, {"aac_pce", "Forces the use of PCEs", offsetof(AACEncContext, options.pce), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, AACENC_FLAGS}, + {"aac_rc", "Rate-control mode (NMR coder)", offsetof(AACEncContext, options.rc), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AACENC_FLAGS, .unit = "aac_rc"}, + {"cbr", "Constant bitrate (corridor + leaky bucket)", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "aac_rc"}, + {"abr", "Average bitrate (constant-quality target, slow rate servo)", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "aac_rc"}, {"aac_nmr_speed", "NMR coder speed level: 0 = slowest/best, higher trades quality for speed", offsetof(AACEncContext, options.nmr_speed), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, AACENC_FLAGS}, {"aac_allow_71wide", "Allow non-PCE use of 7.1(wide) channel layout", offsetof(AACEncContext, options.allow_71wide), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AACENC_FLAGS}, FF_AAC_PROFILE_OPTS diff --git a/libavcodec/aacenc.h b/libavcodec/aacenc.h index 9d2ba89056..057c2a59d4 100644 --- a/libavcodec/aacenc.h +++ b/libavcodec/aacenc.h @@ -72,6 +72,7 @@ typedef struct AACEncOptions { int intensity_stereo; int nmr_speed; ///< NMR coder speed level: 0 = slowest/best, higher is faster int allow_71wide; + int rc; ///< rate-control mode: 0 = cbr (corridor+bucket), 1 = abr (nd-target + slow servo) } AACEncOptions; /** @@ -248,6 +249,18 @@ typedef struct AACNMRCurves { float lam_short_ema; ///< smoothed operating lambda of short frames float lam_long_ema; ///< smoothed operating lambda of long frames float lam_floor; ///< lambda min-tracker (snaps down, +2%/frame up): sustained-strain floor; bursty spikes at a comfortable rate cannot raise it + + /* ABR: nd-target set-point servoed to hold the long-run mean rate */ + float abr_t; ///< current nd target (log2 dist/mask) + float abr_glide; ///< pending set-point correction, drained per-frame (no discrete quality steps) + float abr_ema; ///< EMA of real frame bits + float abr_acc; ///< accumulated set-point correction + int abr_hold; ///< frames since the target last stepped + int abr_booted; ///< seed correction applied (re-armed while the rate is still off) + int abr_boots; ///< open-loop corrections fired so far + float abr_alloc_ema; ///< EMA of psy bit demand (fill shaping) + int abr_longs; ///< long frames seen during bootstrap + int64_t abr_frame_num; ///< once-per-frame servo guard } AACNMRCurves; typedef struct AACPCEInfo { diff --git a/libavcodec/aacpsy.c b/libavcodec/aacpsy.c index 0fd0c87b85..ac0e17de5b 100644 --- a/libavcodec/aacpsy.c +++ b/libavcodec/aacpsy.c @@ -332,14 +332,16 @@ static float lame_calc_attack_threshold(int bitrate) /** * LAME psy model specific initialization */ -static av_cold void lame_window_init(AacPsyContext *ctx, AVCodecContext *avctx) +static av_cold void lame_window_init(FFPsyContext *fctx, AacPsyContext *ctx, AVCodecContext *avctx) { int i, j; for (i = 0; i < avctx->ch_layout.nb_channels; i++) { AacPsyChannel *pch = &ctx->ch[i]; - if (avctx->flags & AV_CODEC_FLAG_QSCALE) + if ((avctx->flags & AV_CODEC_FLAG_QSCALE) || fctx->unbounded_pe) + /* quality-target coders (VBR and ABR) switch windows for quality, + * not rate: use the quality attack map regardless of bit_rate */ pch->attack_threshold = psy_vbr_map[av_clip(avctx->global_quality / FF_QP2LAMBDA, 0, 10)].st_lrm; else pch->attack_threshold = lame_calc_attack_threshold(avctx->bit_rate / avctx->ch_layout.nb_channels / 1000); @@ -456,7 +458,7 @@ static av_cold int psy_3gpp_init(FFPsyContext *ctx) { for (i = 0; i < ctx->avctx->ch_layout.nb_channels; i++) pctx->ch[i].rc_frame_num = -1; - lame_window_init(pctx, ctx->avctx); + lame_window_init(ctx, pctx, ctx->avctx); return 0; } @@ -953,7 +955,7 @@ static void psy_3gpp_analyze_channel(FFPsyContext *ctx, int channel, * it cannot fail. A band we still choose to code must never be allowed * noise within PSY_THRFL_* dB of its own energy, whatever the hole logic said. */ { - int qmode = !!(ctx->avctx->flags & AV_CODEC_FLAG_QSCALE); + int qmode = ctx->unbounded_pe || (ctx->avctx->flags & AV_CODEC_FLAG_QSCALE); float lim = qmode ? PSY_THRFL_QUALITY : PSY_THRFL_CBR; float knee = qmode ? 0.0f : PSY_THRFL_CBR_KNEE; float lo = ff_exp10f(-lim / 10.0f); diff --git a/tests/fate/aac.mak b/tests/fate/aac.mak index b1f49aff2a..7da94cf30b 100644 --- a/tests/fate/aac.mak +++ b/tests/fate/aac.mak @@ -268,6 +268,10 @@ fate-aac-yoraw-encode: FUZZ = 17 FATE_AAC_ENCODE_TRANSCODE += fate-aac-nmr-vbr-encode fate-aac-nmr-vbr-encode: CMD = transcode wav $(TARGET_SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav adts "-af aresample -c:a aac -q:a 8 -t 2" "-c copy" +# ABR through the rate servo's boot (~2s) +FATE_AAC_ENCODE_TRANSCODE += fate-aac-nmr-abr-encode +fate-aac-nmr-abr-encode: CMD = transcode wav $(TARGET_SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav adts "-af aresample -c:a aac -aac_rc abr -b:a 96k -t 3" "-c copy" + tests/data/fate/aac-5_1_2.adts: TAG = GEN tests/data/fate/aac-5_1_2.adts: tests/data/asynth-44100-8.wav tests/data/fate/aac-5_1_2.adts: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data/fate diff --git a/tests/ref/fate/aac-nmr-abr-encode b/tests/ref/fate/aac-nmr-abr-encode new file mode 100644 index 0000000000..9418f60149 --- /dev/null +++ b/tests/ref/fate/aac-nmr-abr-encode @@ -0,0 +1,138 @@ +8a93613d6b0acab3fc863fda864a48e5 *tests/data/fate/aac-nmr-abr-encode.adts +31792 tests/data/fate/aac-nmr-abr-encode.adts +#tb 0: 1/28224000 +#media_type 0: audio +#codec_id 0: aac +#sample_rate 0: 44100 +#channel_layout_name 0: stereo +0, 0, 0, 655360, 269, 0xfa9b83e0 +0, 655360, 655360, 655360, 464, 0xa230e9b1 +0, 1310720, 1310720, 655360, 260, 0x549a7ba3 +0, 1966080, 1966080, 655360, 262, 0xe5ce7d83 +0, 2621440, 2621440, 655360, 259, 0x26067677 +0, 3276800, 3276800, 655360, 261, 0x2bdc7bc4 +0, 3932160, 3932160, 655360, 244, 0xc16d750e +0, 4587520, 4587520, 655360, 252, 0xb09d7762 +0, 5242880, 5242880, 655360, 263, 0x91157965 +0, 5898240, 5898240, 655360, 259, 0xb3e972f4 +0, 6553600, 6553600, 655360, 268, 0xcbe9813d +0, 7208960, 7208960, 655360, 252, 0xf9c076f3 +0, 7864320, 7864320, 655360, 398, 0x2851be3c +0, 8519680, 8519680, 655360, 320, 0x166d991d +0, 9175040, 9175040, 655360, 408, 0xca4cd36f +0, 9830400, 9830400, 655360, 239, 0x78c2742b +0, 10485760, 10485760, 655360, 246, 0xca377334 +0, 11141120, 11141120, 655360, 240, 0x635e7250 +0, 11796480, 11796480, 655360, 220, 0x3bbd66ae +0, 12451840, 12451840, 655360, 210, 0xf244638d +0, 13107200, 13107200, 655360, 209, 0x64a6563b +0, 13762560, 13762560, 655360, 221, 0xa96762cd +0, 14417920, 14417920, 655360, 226, 0x749f66c4 +0, 15073280, 15073280, 655360, 226, 0xd0eb6387 +0, 15728640, 15728640, 655360, 223, 0xc3826574 +0, 16384000, 16384000, 655360, 220, 0x78a86d47 +0, 17039360, 17039360, 655360, 230, 0xb4446dc3 +0, 17694720, 17694720, 655360, 215, 0x306f7104 +0, 18350080, 18350080, 655360, 229, 0x91de69c3 +0, 19005440, 19005440, 655360, 219, 0x078e672d +0, 19660800, 19660800, 655360, 214, 0xb5b766a4 +0, 20316160, 20316160, 655360, 220, 0x7023610b +0, 20971520, 20971520, 655360, 210, 0x182a5ee8 +0, 21626880, 21626880, 655360, 203, 0xdf3655a9 +0, 22282240, 22282240, 655360, 231, 0x13d565d0 +0, 22937600, 22937600, 655360, 229, 0x5e0063f5 +0, 23592960, 23592960, 655360, 232, 0xf03468e7 +0, 24248320, 24248320, 655360, 244, 0x882a7785 +0, 24903680, 24903680, 655360, 236, 0xf8bd7012 +0, 25559040, 25559040, 655360, 228, 0x1956677a +0, 26214400, 26214400, 655360, 266, 0x3e6b81a9 +0, 26869760, 26869760, 655360, 233, 0x5ab66d73 +0, 27525120, 27525120, 655360, 244, 0x25b56b66 +0, 28180480, 28180480, 655360, 184, 0xd9695100 +0, 28835840, 28835840, 655360, 229, 0x22f771cd +0, 29491200, 29491200, 655360, 260, 0xc6fa8312 +0, 30146560, 30146560, 655360, 256, 0x48197e58 +0, 30801920, 30801920, 655360, 247, 0xea997935 +0, 31457280, 31457280, 655360, 239, 0xa22f7015 +0, 32112640, 32112640, 655360, 259, 0x8b2384c0 +0, 32768000, 32768000, 655360, 252, 0xd23e790d +0, 33423360, 33423360, 655360, 240, 0x25056b8c +0, 34078720, 34078720, 655360, 256, 0xab1077f8 +0, 34734080, 34734080, 655360, 238, 0xd99a6cc5 +0, 35389440, 35389440, 655360, 223, 0x4ffe6c3f +0, 36044800, 36044800, 655360, 222, 0x8c7c5f7a +0, 36700160, 36700160, 655360, 231, 0xcc3172bf +0, 37355520, 37355520, 655360, 224, 0xc23b68cf +0, 38010880, 38010880, 655360, 222, 0xf0986a4d +0, 38666240, 38666240, 655360, 233, 0x92a36bc9 +0, 39321600, 39321600, 655360, 251, 0x11516fb9 +0, 39976960, 39976960, 655360, 263, 0x0f0b7e11 +0, 40632320, 40632320, 655360, 408, 0xbf1ccc26 +0, 41287680, 41287680, 655360, 531, 0xa5690ccd +0, 41943040, 41943040, 655360, 308, 0x5eed9716 +0, 42598400, 42598400, 655360, 277, 0x3668903e +0, 43253760, 43253760, 655360, 239, 0x2bab729b +0, 43909120, 43909120, 655360, 227, 0x3c8b7095 +0, 44564480, 44564480, 655360, 190, 0x6c3257ab +0, 45219840, 45219840, 655360, 209, 0x19125f77 +0, 45875200, 45875200, 655360, 196, 0xcec25cec +0, 46530560, 46530560, 655360, 250, 0x3e2875d4 +0, 47185920, 47185920, 655360, 276, 0x37138362 +0, 47841280, 47841280, 655360, 210, 0x77d55f66 +0, 48496640, 48496640, 655360, 202, 0x88c06283 +0, 49152000, 49152000, 655360, 201, 0x364a5f76 +0, 49807360, 49807360, 655360, 218, 0x38ee6916 +0, 50462720, 50462720, 655360, 205, 0x017c6140 +0, 51118080, 51118080, 655360, 205, 0xba40628e +0, 51773440, 51773440, 655360, 211, 0x3ab1628d +0, 52428800, 52428800, 655360, 208, 0x9b636846 +0, 53084160, 53084160, 655360, 207, 0xcbd06506 +0, 53739520, 53739520, 655360, 207, 0x487f6291 +0, 54394880, 54394880, 655360, 218, 0x7aa870d9 +0, 55050240, 55050240, 655360, 256, 0x3495754e +0, 55705600, 55705600, 655360, 335, 0xd95aa995 +0, 56360960, 56360960, 655360, 196, 0x54e75268 +0, 57016320, 57016320, 655360, 209, 0x04de5e1e +0, 57671680, 57671680, 655360, 206, 0xa2f463a5 +0, 58327040, 58327040, 655360, 204, 0xad526074 +0, 58982400, 58982400, 655360, 209, 0xde2e5aec +0, 59637760, 59637760, 655360, 209, 0x8c6d6905 +0, 60293120, 60293120, 655360, 202, 0x44ca5964 +0, 60948480, 60948480, 655360, 206, 0xd8685ef3 +0, 61603840, 61603840, 655360, 209, 0xff6360ce +0, 62259200, 62259200, 655360, 207, 0xa9fc61b1 +0, 62914560, 62914560, 655360, 209, 0x312d6796 +0, 63569920, 63569920, 655360, 218, 0x4c9f695f +0, 64225280, 64225280, 655360, 224, 0x34996723 +0, 64880640, 64880640, 655360, 234, 0x02096ec5 +0, 65536000, 65536000, 655360, 246, 0xa63a70c2 +0, 66191360, 66191360, 655360, 277, 0x8559914c +0, 66846720, 66846720, 655360, 240, 0xa2087438 +0, 67502080, 67502080, 655360, 245, 0xc61a826b +0, 68157440, 68157440, 655360, 248, 0xd92a7776 +0, 68812800, 68812800, 655360, 244, 0xa4517d63 +0, 69468160, 69468160, 655360, 258, 0x79cb75e2 +0, 70123520, 70123520, 655360, 274, 0x98708776 +0, 70778880, 70778880, 655360, 239, 0xcd83750c +0, 71434240, 71434240, 655360, 232, 0x05336d82 +0, 72089600, 72089600, 655360, 234, 0x55847ac8 +0, 72744960, 72744960, 655360, 234, 0x76da6e4c +0, 73400320, 73400320, 655360, 221, 0x919969bf +0, 74055680, 74055680, 655360, 223, 0xfbfb69c2 +0, 74711040, 74711040, 655360, 228, 0xf9e373d7 +0, 75366400, 75366400, 655360, 224, 0xd0975f50 +0, 76021760, 76021760, 655360, 234, 0xa66d6e10 +0, 76677120, 76677120, 655360, 249, 0x2a276d92 +0, 77332480, 77332480, 655360, 239, 0x53516e65 +0, 77987840, 77987840, 655360, 270, 0xa6b77886 +0, 78643200, 78643200, 655360, 274, 0x076d7c58 +0, 79298560, 79298560, 655360, 269, 0x93fc811b +0, 79953920, 79953920, 655360, 239, 0x9dfc73de +0, 80609280, 80609280, 655360, 255, 0x6aa47d01 +0, 81264640, 81264640, 655360, 236, 0x755271f0 +0, 81920000, 81920000, 655360, 241, 0x8bab73ea +0, 82575360, 82575360, 655360, 246, 0xa0ac7c7a +0, 83230720, 83230720, 655360, 251, 0x74447d8a +0, 83886080, 83886080, 655360, 223, 0xefc36a30 +0, 84541440, 84541440, 655360, 241, 0x88ab6e54 +0, 85196800, 85196800, 655360, 191, 0x89b45c00 -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
