This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit f68ccd1b3941cebba1cafaf6c7982bd977af1580 Author: Lynne <[email protected]> AuthorDate: Wed Sep 23 20:26:12 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Sun Oct 4 17:51:01 2026 +0900 avcodec/aacpsy: stronger PE demand shaping, signal-relative mask floor PE demand shaping. The 3GPP bit-demand curve modulates per-frame budgets far too gently for the trellis coder: amplifying the deviation from unity by 2.5x is worth -2.5% Zimtohrli at 128k stereo and -13.5% at 64k (ViSQOL up at both), with a sharp cliff beyond ~2.8 as budgets start swinging into the clamps. Listening guards flat, pulse-train sample stays click-free. The amplified factor goes negative in high-PE frames on a starved reservoir - 15-30% of CBR channel-frames. The desired PE is then zero or below, and the reduction raises every threshold as far as the min-SNR and hole rules allow: those frames are shaped for constant SNR rather than by the mask. This is load-bearing, not an accident: flooring the returned demand at the documented frame_bits/8 costs +5.2% / +3.3% Zimtohrli at 64k on the two ABC/HR corpora (mixed at 128k). The reservoir-fill correction of the desired PE is clamped symmetrically in the log domain, [1/1.15, 1.15] instead of [0.85, 1.15]: a negative previous demand sits on the lower bound, and the asymmetric 0.85 there costs 2.4% Zimtohrli at 64k. Signal-relative mask floor. 3GPP 5.6.1.3.3 exempts bands that are quiet against the spread energy from the min-SNR floor ("holes allowed here"), and nothing downstream bounds them: measured at 128k, the published mask ends up only 1.5-2 dB below the band's own energy across the whole 6-16 kHz range, on every sample tested. The model is asserting the top of the spectrum is inaudible, and every consumer believes it - the allocator buys those bands nothing, and a quality-target solver sees a mask it cannot fail. A band we still choose to code may now not have its mask come within PSY_THRFL_* dB of its own energy. The two rate-control families want different shapes, and measurably so, because the mask does a different job in each: in quality-target modes it IS the rate authority, so the floor is broadband (10 dB); in CBR the budget is fixed and the mask only ranks bands against each other, so a broadband floor merely shuffles bits and costs ~2% Zim on random content, while restricting it to above 8 kHz (6 dB) keeps the win. Here the quality-target branch covers -q:a; the ABR mode later in the series joins it. CBR at matched rate, Zimtohrli / ViSQOL over the two ABC/HR corpora and two random-clip corpora: -14% / +0.017, 0% / +0.008, +1.4% / +0.003, 0% / +0.004. ViSQOL improves on every corpus. The +1.4% Zim is on the 16 canonical clips only; a fresh 32-clip random corpus is flat there, and the ungated floor costs +1.8% on that same set, so the frequency gate is what makes CBR safe. Threshold-shape experiments from the harness sweeps - spreading slopes, the min-SNR clamp, the tonality-dependent TMN/NMT offsets and the TNS gain bars - all measured at or worse than the flat 3GPP values, confirming the earlier static-threshold verdict, and are not included. The fast-coder encode tests move with the thresholds and are re-pinned: ln-encode-128k 622 -> 618, is-encode 500 -> 503, ms-encode 550 -> 563. The id3v2 re-encode refs follow the default NMR output. --- libavcodec/aacpsy.c | 61 ++++++++++++++++++++-- tests/fate/aac.mak | 6 +-- tests/ref/fate/id3v2-reenc-delete-metadata | 4 +- tests/ref/fate/id3v2-reenc-delete-metadata-keep | 4 +- .../fate/id3v2-reenc-delete-metadata-keep-format | 4 +- .../fate/id3v2-reenc-delete-metadata-keep-stream | 4 +- .../fate/id3v2-reenc-delete-metadata-map-metadata | 4 +- 7 files changed, 71 insertions(+), 16 deletions(-) diff --git a/libavcodec/aacpsy.c b/libavcodec/aacpsy.c index 55ba412de4..ceb978d0a6 100644 --- a/libavcodec/aacpsy.c +++ b/libavcodec/aacpsy.c @@ -78,6 +78,30 @@ #define PSY_3GPP_CLIP_HI_L 0.95f #define PSY_3GPP_CLIP_HI_S 0.75f +/* Floor on how close a coded band's mask may come to the band's own energy. + * The two rate-control families want different shapes, and measurably so: + * in VBR/ABR the mask IS the rate authority, so a mask that has risen to meet + * its band's energy tells the solver the band is free to destroy and it buys + * no bits for it - a broadband floor is right there. In CBR the budget is + * fixed and the mask only ranks bands against each other, so the same + * broadband floor just moves bits around and costs ~2% Zim on random content; + * restricted to the top end, where the mask degenerates to within 2 dB of band + * energy on every sample measured, it is a clear win. */ +#define PSY_THRFL_QUALITY 10.0f /* VBR/ABR depth, dB, all bands */ +#define PSY_THRFL_CBR 6.0f /* CBR depth, dB */ +#define PSY_THRFL_CBR_KNEE 8000.0f /* CBR: only above this frequency */ + +/* Strength of the 3GPP bit-demand curve: its deviation from unity is + * amplified, since the trellis coder wants far stronger per-frame budget + * modulation than the reference encoder's gentle curve provides. + * The amplified factor goes negative in high-PE frames on a starved + * reservoir (15-30% of CBR frames). The desired PE is then <= 0 and the + * reduction raises every threshold as far as the min-SNR and hole rules + * allow, so those frames are shaped for constant SNR instead of by the + * mask. That regime is load-bearing: flooring the demand at frame_bits/8 + * costs 3-5% Zimtohrli at 64 kbps stereo. */ +#define PSY_3GPP_DEMAND_SCALE 2.5f + #define PSY_3GPP_AH_THR_LONG 0.5f #define PSY_3GPP_AH_THR_SHORT 0.63f @@ -551,7 +575,7 @@ static int calc_bit_demand(AacPsyContext *ctx, float pe, int bits, int size, int short_window) { const float bitsave_slope = short_window ? PSY_3GPP_SAVE_SLOPE_S : PSY_3GPP_SAVE_SLOPE_L; - const float bitsave_add = short_window ? PSY_3GPP_SAVE_ADD_S : PSY_3GPP_SAVE_ADD_L; + const float bitsave_add = short_window ? PSY_3GPP_SAVE_ADD_S : PSY_3GPP_SAVE_ADD_L; const float bitspend_slope = short_window ? PSY_3GPP_SPEND_SLOPE_S : PSY_3GPP_SPEND_SLOPE_L; const float bitspend_add = short_window ? PSY_3GPP_SPEND_ADD_S : PSY_3GPP_SPEND_ADD_L; const float clip_low = short_window ? PSY_3GPP_CLIP_LO_S : PSY_3GPP_CLIP_LO_L; @@ -573,6 +597,7 @@ static int calc_bit_demand(AacPsyContext *ctx, float pe, int bits, int size, * Hopefully below is correct. */ bit_factor = 1.0f - bit_save + ((bit_spend - bit_save) / (ctx->pe.max - ctx->pe.min)) * (clipped_pe - ctx->pe.min); + bit_factor = 1.0f + (bit_factor - 1.0f) * PSY_3GPP_DEMAND_SCALE; /* NOTE: The reference encoder attempts to center pe max/min around the current pe. * Here we do that by slowly forgetting pe.min when pe stays in a range that makes * it unlikely (ie: above the mean) @@ -806,9 +831,13 @@ static void psy_3gpp_analyze_channel(FFPsyContext *ctx, int channel, * little effect on the final bitrate. Probably a good idea to come * back and do more testing later. */ - if (ctx->bitres.bits > 0) + if (ctx->bitres.bits > 0) { + /* symmetric in the log domain: a negative previous demand (see + * PSY_3GPP_DEMAND_SCALE) sits on the lower bound, and the + * asymmetric 0.85 there costs ~2% Zimtohrli at 64 kbps */ desired_pe *= av_clipf(pctx->pe.previous / PSY_3GPP_BITS_TO_PE(ctx->bitres.bits), - 0.85f, 1.15f); + 1.0f / 1.15f, 1.15f); + } } pctx->pe.previous = PSY_3GPP_BITS_TO_PE(desired_bits); ctx->bitres.alloc = desired_bits; @@ -905,6 +934,32 @@ static void psy_3gpp_analyze_channel(FFPsyContext *ctx, int channel, } } + /* Signal-relative mask ceiling. 5.6.1.3.3 exempts bands that are quiet + * relative to the spread energy from the min-SNR floor ("holes allowed + * here"), so on spectrally lopsided programme their mask is free to rise + * until it meets their own energy - the model then calls a band that + * carries real texture inaudible, and every consumer of the mask agrees: + * the allocator buys it nothing and the quality-target solver sees a mask + * 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); + 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); + float l2f = ctx->avctx->sample_rate / 2.0f / + (wi->num_windows == 1 ? 1024.0f : 128.0f); + for (w = 0; w < wi->num_windows*16; w += 16) { + int start = 0; + for (g = 0; g < num_bands; g++) { + AacPsyBand *band = &pch->band[w+g]; + if (start * l2f >= knee) + band->thr = FFMIN(band->thr, band->energy * lo); + start += band_sizes[g]; + } + } + } + for (w = 0; w < wi->num_windows*16; w += 16) { for (g = 0; g < num_bands; g++) { AacPsyBand *band = &pch->band[w+g]; diff --git a/tests/fate/aac.mak b/tests/fate/aac.mak index 7bbcc6d5f6..ec49b83b7d 100644 --- a/tests/fate/aac.mak +++ b/tests/fate/aac.mak @@ -224,7 +224,7 @@ FATE_AAC_ENCODE += fate-aac-ln-encode-128k fate-aac-ln-encode-128k: CMD = enc_dec_pcm mp4 wav s16le $(TARGET_SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav -c:a aac -aac_coder fast -aac_is 0 -aac_pns 0 -aac_ms 0 -aac_tns 0 -b:a 128k -cutoff 22050 -fflags +bitexact -flags +bitexact fate-aac-ln-encode-128k: CMP = stddev fate-aac-ln-encode-128k: REF = $(SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav -fate-aac-ln-encode-128k: CMP_TARGET = 622 +fate-aac-ln-encode-128k: CMP_TARGET = 618 fate-aac-ln-encode-128k: FUZZ = 10 FATE_AAC_ENCODE += fate-aac-pns-encode @@ -245,14 +245,14 @@ FATE_AAC_ENCODE += fate-aac-is-encode fate-aac-is-encode: CMD = enc_dec_pcm mp4 wav s16le $(TARGET_SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav -c:a aac -aac_coder fast -aac_pns 0 -aac_is 1 -aac_ms 0 -b:a 128k -aac_tns 0 -cutoff 22050 -fflags +bitexact -flags +bitexact fate-aac-is-encode: CMP = stddev fate-aac-is-encode: REF = $(SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav -fate-aac-is-encode: CMP_TARGET = 500 +fate-aac-is-encode: CMP_TARGET = 503 fate-aac-is-encode: FUZZ = 10 FATE_AAC_ENCODE += fate-aac-ms-encode fate-aac-ms-encode: CMD = enc_dec_pcm mp4 wav s16le $(TARGET_SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav -c:a aac -aac_coder fast -aac_pns 0 -aac_is 0 -aac_ms 1 -aac_tns 0 -b:a 128k -cutoff 22050 -fflags +bitexact -flags +bitexact fate-aac-ms-encode: CMP = stddev fate-aac-ms-encode: REF = $(SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav -fate-aac-ms-encode: CMP_TARGET = 550 +fate-aac-ms-encode: CMP_TARGET = 563 fate-aac-ms-encode: FUZZ = 15 #Ticket1784 diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata b/tests/ref/fate/id3v2-reenc-delete-metadata index 2d19a3071b..bd7aad1783 100644 --- a/tests/ref/fate/id3v2-reenc-delete-metadata +++ b/tests/ref/fate/id3v2-reenc-delete-metadata @@ -1,5 +1,5 @@ -54b8e2b9f3d83cbd601ae625978a5c4a *tests/data/fate/id3v2-reenc-delete-metadata.nut -2683 tests/data/fate/id3v2-reenc-delete-metadata.nut +1805fde23636947a81abf947d75d2115 *tests/data/fate/id3v2-reenc-delete-metadata.nut +2651 tests/data/fate/id3v2-reenc-delete-metadata.nut [FORMAT] TAG:title=7rk [/FORMAT] diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata-keep b/tests/ref/fate/id3v2-reenc-delete-metadata-keep index 98caa8c82b..84c562559d 100644 --- a/tests/ref/fate/id3v2-reenc-delete-metadata-keep +++ b/tests/ref/fate/id3v2-reenc-delete-metadata-keep @@ -1,5 +1,5 @@ -46e8ca22aa8be69d14bfad4f4ed3cefc *tests/data/fate/id3v2-reenc-delete-metadata-keep.nut -2823 tests/data/fate/id3v2-reenc-delete-metadata-keep.nut +759156778fafe46d062c502bb5bfdc3f *tests/data/fate/id3v2-reenc-delete-metadata-keep.nut +2791 tests/data/fate/id3v2-reenc-delete-metadata-keep.nut [FORMAT] TAG:title=7rk TAG:comment-iTunSMPB-eng= 00000000 00000210 0000086A 0000000000066486 00000000 0002DA9D 00000000 00000000 00000000 00000000 00000000 00000000 diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata-keep-format b/tests/ref/fate/id3v2-reenc-delete-metadata-keep-format index a3e9fbd819..2e823807b9 100644 --- a/tests/ref/fate/id3v2-reenc-delete-metadata-keep-format +++ b/tests/ref/fate/id3v2-reenc-delete-metadata-keep-format @@ -1,5 +1,5 @@ -46e8ca22aa8be69d14bfad4f4ed3cefc *tests/data/fate/id3v2-reenc-delete-metadata-keep-format.nut -2823 tests/data/fate/id3v2-reenc-delete-metadata-keep-format.nut +759156778fafe46d062c502bb5bfdc3f *tests/data/fate/id3v2-reenc-delete-metadata-keep-format.nut +2791 tests/data/fate/id3v2-reenc-delete-metadata-keep-format.nut [FORMAT] TAG:title=7rk TAG:comment-iTunSMPB-eng= 00000000 00000210 0000086A 0000000000066486 00000000 0002DA9D 00000000 00000000 00000000 00000000 00000000 00000000 diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata-keep-stream b/tests/ref/fate/id3v2-reenc-delete-metadata-keep-stream index 6415519a53..4a4df4db15 100644 --- a/tests/ref/fate/id3v2-reenc-delete-metadata-keep-stream +++ b/tests/ref/fate/id3v2-reenc-delete-metadata-keep-stream @@ -1,5 +1,5 @@ -54b8e2b9f3d83cbd601ae625978a5c4a *tests/data/fate/id3v2-reenc-delete-metadata-keep-stream.nut -2683 tests/data/fate/id3v2-reenc-delete-metadata-keep-stream.nut +1805fde23636947a81abf947d75d2115 *tests/data/fate/id3v2-reenc-delete-metadata-keep-stream.nut +2651 tests/data/fate/id3v2-reenc-delete-metadata-keep-stream.nut [FORMAT] TAG:title=7rk [/FORMAT] diff --git a/tests/ref/fate/id3v2-reenc-delete-metadata-map-metadata b/tests/ref/fate/id3v2-reenc-delete-metadata-map-metadata index 62a9411e4e..725ab61b1c 100644 --- a/tests/ref/fate/id3v2-reenc-delete-metadata-map-metadata +++ b/tests/ref/fate/id3v2-reenc-delete-metadata-map-metadata @@ -1,5 +1,5 @@ -54b8e2b9f3d83cbd601ae625978a5c4a *tests/data/fate/id3v2-reenc-delete-metadata-map-metadata.nut -2683 tests/data/fate/id3v2-reenc-delete-metadata-map-metadata.nut +1805fde23636947a81abf947d75d2115 *tests/data/fate/id3v2-reenc-delete-metadata-map-metadata.nut +2651 tests/data/fate/id3v2-reenc-delete-metadata-map-metadata.nut [FORMAT] TAG:title=7rk [/FORMAT] -- 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]
