This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit a51ec04adfcd0a872a2ac5826c42cad304d7c5e3 Author: Lynne <[email protected]> AuthorDate: Thu Jun 11 02:25:13 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Wed Jun 17 17:34:24 2026 +0900 aacdec_usac: parse loudnessInfoV1 Real-world xHE-AAC streams and the ISO/IEC 23003-3 conformance sequences carry their loudness metadata exclusively as loudnessInfoV1() inside loudnessInfoSetExtension(), which was previously rejected with AVERROR_PATCHWELCOME, making such streams undecodable and loudness normalization inoperative on them. loudnessInfoV1() is identical to loudnessInfo() apart from an added eqSetId field. Parse it, restrict measurement selection to eqSetId == 0 (in line with the downmixId/drcSetId restrictions), and skip unknown loudnessInfoSetExtension() payloads using their explicitly coded size instead of erroring out. --- libavcodec/aac/aacdec.h | 1 + libavcodec/aac/aacdec_usac.c | 64 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h index 80a77289e6..18412fb97a 100644 --- a/libavcodec/aac/aacdec.h +++ b/libavcodec/aac/aacdec.h @@ -308,6 +308,7 @@ typedef struct ChannelElement { typedef struct AACUSACLoudnessInfo { uint8_t drc_set_id : 6; + uint8_t eq_set_id : 6; /* loudnessInfoV1() only, 0 otherwise */ uint8_t downmix_id : 7; struct { uint16_t lvl : 12; diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c index 6d48a5746a..00b4d4586d 100644 --- a/libavcodec/aac/aacdec_usac.c +++ b/libavcodec/aac/aacdec_usac.c @@ -95,10 +95,13 @@ static int methodvalue_width(int method_def) } } +/* ISO/IEC 23003-4, Table 58/60: loudnessInfo(), loudnessInfoV1(). + * The only difference in V1 is the added eqSetId field. */ static int decode_loudness_info(AACDecContext *ac, AACUSACLoudnessInfo *info, - GetBitContext *gb) + GetBitContext *gb, int v1) { info->drc_set_id = get_bits(gb, 6); + info->eq_set_id = v1 ? get_bits(gb, 6) : 0; info->downmix_id = get_bits(gb, 7); if ((info->sample_peak.present = get_bits1(gb))) /* samplePeakLevelPresent */ @@ -122,16 +125,46 @@ static int decode_loudness_info(AACDecContext *ac, AACUSACLoudnessInfo *info, return 0; } +/* ISO/IEC 23003-4, Table 61: loudnessInfoSetExtension(), UNIDRCLOUDEXT_EQ */ +static int decode_loudness_set_v1(AACDecContext *ac, AACUSACConfig *usac, + GetBitContext *gb) +{ + int ret; + int nb_album = get_bits(gb, 6); /* loudnessInfoV1AlbumCount */ + int nb_info = get_bits(gb, 6); /* loudnessInfoV1Count */ + + for (int i = 0; i < nb_album; i++) { + AACUSACLoudnessInfo tmp; + ret = decode_loudness_info(ac, &tmp, gb, 1); + if (ret < 0) + return ret; + if (usac->loudness.nb_album < FF_ARRAY_ELEMS(usac->loudness.album_info)) + usac->loudness.album_info[usac->loudness.nb_album++] = tmp; + } + + for (int i = 0; i < nb_info; i++) { + AACUSACLoudnessInfo tmp; + ret = decode_loudness_info(ac, &tmp, gb, 1); + if (ret < 0) + return ret; + if (usac->loudness.nb_info < FF_ARRAY_ELEMS(usac->loudness.info)) + usac->loudness.info[usac->loudness.nb_info++] = tmp; + } + + return 0; +} + /* Pick the bsMethodValue of a program- or anchor-loudness measurement. - * Per ISO/IEC 23003-4 6.1.2.5, downmixId and drcSetId identify the signal a - * loudnessInfo() applies to; only downmixId == 0 (base layout) together with - * drcSetId == 0 (no DRC) describes the unprocessed signal we output, so - * measurements for any other downmix/DRC set must not be used. */ + * Per ISO/IEC 23003-4 6.1.2.5, downmixId, drcSetId and eqSetId identify the + * signal a loudnessInfo() applies to; only downmixId == 0 (base layout) + * together with drcSetId == 0 and eqSetId == 0 (no DRC/EQ) describes the + * unprocessed signal we output, so measurements for any other + * downmix/DRC/EQ set must not be used. */ static int select_loudness_measurement(const AACUSACConfig *usac) { for (int i = 0; i < usac->loudness.nb_info; i++) { const AACUSACLoudnessInfo *info = &usac->loudness.info[i]; - if (info->downmix_id != 0 || info->drc_set_id != 0) + if (info->downmix_id != 0 || info->drc_set_id != 0 || info->eq_set_id != 0) continue; for (int j = 0; j < info->nb_measurements; j++) { int method = info->measurements[j].method_def; @@ -151,13 +184,13 @@ static int decode_loudness_set(AACDecContext *ac, AACUSACConfig *usac, usac->loudness.nb_info = get_bits(gb, 6); /* loudnessInfoCount */ for (int i = 0; i < usac->loudness.nb_album; i++) { - ret = decode_loudness_info(ac, &usac->loudness.album_info[i], gb); + ret = decode_loudness_info(ac, &usac->loudness.album_info[i], gb, 0); if (ret < 0) return ret; } for (int i = 0; i < usac->loudness.nb_info; i++) { - ret = decode_loudness_info(ac, &usac->loudness.info[i], gb); + ret = decode_loudness_info(ac, &usac->loudness.info[i], gb, 0); if (ret < 0) return ret; } @@ -167,14 +200,23 @@ static int decode_loudness_set(AACDecContext *ac, AACUSACConfig *usac, while ((type = get_bits(gb, 4)) != UNIDRCLOUDEXT_TERM) { uint8_t size_bits = get_bits(gb, 4) + 4; /* bitSizeLen */ uint32_t bit_size = get_bits_long(gb, size_bits) + 1; /* bitSize */ + int start = get_bits_count(gb); + int skip; switch (type) { case UNIDRCLOUDEXT_EQ: - avpriv_report_missing_feature(ac->avctx, "loudnessInfoV1"); - return AVERROR_PATCHWELCOME; + ret = decode_loudness_set_v1(ac, usac, gb); + if (ret < 0) + return ret; + break; default: - skip_bits_long(gb, bit_size); break; } + /* The extension size is explicit, so unparsed (or unknown) + * data can be skipped without desynchronizing. */ + skip = bit_size - (get_bits_count(gb) - start); + if (skip < 0) + return AVERROR_INVALIDDATA; + skip_bits_long(gb, skip); } } _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
