This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit eb107d87723508907f1690552b29f803f241068e Author: Romain Beauxis <[email protected]> AuthorDate: Tue Dec 2 10:43:46 2025 -0600 Commit: Romain Beauxis <[email protected]> CommitDate: Sat Jul 18 09:59:57 2026 -0500 libavformat/id3v2.c: Factor out support for reading lyrics and comment tags. --- libavformat/Makefile | 19 +++-- libavformat/id3v2.c | 136 ++++++++++++++++++------------------ tests/ref/fate/id3v2-lang-xxx | 2 +- tests/ref/fate/id3v2-lang-xxx-remux | 4 +- tests/ref/fate/id3v2-wma-comm | 2 +- tests/ref/fate/id3v2-wma-comm-asf_o | 2 +- 6 files changed, 82 insertions(+), 83 deletions(-) diff --git a/libavformat/Makefile b/libavformat/Makefile index 3595567464..2f85e8a2ad 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -8,6 +8,7 @@ HEADERS = avformat.h \ OBJS = allformats.o \ av1.o \ + avlanguage.o \ avformat.o \ avio.o \ aviobuf.o \ @@ -138,11 +139,10 @@ OBJS-$(CONFIG_ARGO_BRP_DEMUXER) += argo_brp.o argo_asf.o OBJS-$(CONFIG_ARGO_CVG_DEMUXER) += argo_cvg.o OBJS-$(CONFIG_ARGO_CVG_MUXER) += argo_cvg.o OBJS-$(CONFIG_ASF_DEMUXER) += asfdec_f.o asf.o asfcrypt.o \ - asf_tags.o avlanguage.o + asf_tags.o OBJS-$(CONFIG_ASF_O_DEMUXER) += asfdec_o.o asf.o asfcrypt.o \ - asf_tags.o avlanguage.o -OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o asf_tags.o \ - avlanguage.o + asf_tags.o +OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o asf_tags.o OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o subtitles.o OBJS-$(CONFIG_ASS_MUXER) += assenc.o OBJS-$(CONFIG_AST_DEMUXER) += ast.o astdec.o @@ -150,7 +150,7 @@ OBJS-$(CONFIG_AST_MUXER) += ast.o astenc.o OBJS-$(CONFIG_AU_DEMUXER) += au.o pcm.o OBJS-$(CONFIG_AU_MUXER) += au.o rawenc.o OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o -OBJS-$(CONFIG_AVI_MUXER) += avienc.o mpegtsenc.o avlanguage.o rawutils.o +OBJS-$(CONFIG_AVI_MUXER) += avienc.o mpegtsenc.o rawutils.o OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o swf.o OBJS-$(CONFIG_AVR_DEMUXER) += avr.o pcm.o OBJS-$(CONFIG_AVS_DEMUXER) += avs.o voc_packet.o voc.o @@ -368,8 +368,8 @@ OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \ oggparsevorbis.o vorbiscomment.o \ qtpalette.o replaygain.o dovi_isom.o OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ - flacenc_header.o avlanguage.o \ - vorbiscomment.o wv.o dovi_isom.o + flacenc_header.o vorbiscomment.o \ + wv.o dovi_isom.o OBJS-$(CONFIG_MCA_DEMUXER) += mca.o OBJS-$(CONFIG_MCC_DEMUXER) += mccdec.o subtitles.o OBJS-$(CONFIG_MCC_MUXER) += mccenc.o @@ -424,7 +424,7 @@ OBJS-$(CONFIG_MUSX_DEMUXER) += musx.o OBJS-$(CONFIG_MV_DEMUXER) += mvdec.o OBJS-$(CONFIG_MVI_DEMUXER) += mvi.o OBJS-$(CONFIG_MVR_DEMUXER) += mvrdec.o -OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o avlanguage.o +OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o OBJS-$(CONFIG_MXG_DEMUXER) += mxg.o OBJS-$(CONFIG_NC_DEMUXER) += ncdec.o @@ -644,8 +644,7 @@ OBJS-$(CONFIG_WAVARC_DEMUXER) += wavarc.o OBJS-$(CONFIG_WAV_DEMUXER) += wavdec.o pcm.o OBJS-$(CONFIG_WAV_MUXER) += wavenc.o OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o -OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ - avlanguage.o +OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o OBJS-$(CONFIG_WEBP_ANIM_DEMUXER) += webp_anim_dec.o diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index 21c09d13b4..3e931d2bd2 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -39,8 +39,10 @@ #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" +#include "libavutil/opt.h" #include "libavcodec/png.h" #include "avio_internal.h" +#include "avlanguage.h" #include "demux.h" #include "id3v1.h" #include "id3v2.h" @@ -374,92 +376,90 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, av_dict_set(metadata, key, dst, dict_flags); } -static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen, - AVDictionary **metadata) +/** + * Parse a lang descr tag such as COMM and USLT. + * + * COMM with a non-empty descriptor: the descriptor becomes the bare key + * (e.g. "MusicMatch_Bio"). USLT and all other cases produce + * "<base>-<descriptor>-<lang>". + */ +static void read_lang_descr_tag(AVFormatContext *s, AVIOContext *pb, + const char *key, int taglen, + AVDictionary **metadata) { - uint8_t lang[4]; - uint8_t *descriptor = NULL; // 'Content descriptor' - uint8_t *text; - char *key; + char *full_key = NULL; + uint8_t *dst, *descriptor = NULL; int encoding; - int ok = 0; + char language[4] = {0}; + int flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL; if (taglen < 4) - goto error; + return; encoding = avio_r8(pb); - taglen--; - if (avio_read(pb, lang, 3) < 3) - goto error; - lang[3] = '\0'; - taglen -= 3; - - if (decode_str(s, pb, encoding, &descriptor, &taglen) < 0 || taglen < 0) - goto error; - - if (decode_str(s, pb, encoding, &text, &taglen) < 0 || taglen < 0) - goto error; - - // FFmpeg does not support hierarchical metadata, so concatenate the keys. - key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "", - descriptor[0] ? "-" : "", - lang); - if (!key) { - av_free(text); - goto error; + if (avio_read(pb, language, 3) < 3) { + av_log(s, AV_LOG_ERROR, "Error reading %s frame language, skipped\n", key); + return; } - av_dict_set(metadata, key, text, - AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + for (char *p = language; *p; p++) + *p = av_tolower(*p); - ok = 1; -error: - if (!ok) - av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n"); - av_free(descriptor); -} + // Some libraries set XXX for unknown language. + if (!strcmp(language, "xxx")) + memset(language, 0, sizeof(language)); -/** - * Parse a comment tag. - */ -static void read_comment(AVFormatContext *s, AVIOContext *pb, int taglen, - AVDictionary **metadata) -{ - const char *key = "comment"; - uint8_t *dst; - int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL; - av_unused int language; - - if (taglen < 4) - return; - - encoding = avio_r8(pb); - language = avio_rl24(pb); taglen -= 4; - if (decode_str(s, pb, encoding, &dst, &taglen) < 0) { - av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n"); + if (decode_str(s, pb, encoding, &descriptor, &taglen) < 0) { + av_log(s, AV_LOG_ERROR, "Error reading %s frame descriptor, skipped\n", key); return; } - if (dst && !*dst) - av_freep(&dst); - - if (dst) { - key = (const char *) dst; - dict_flags |= AV_DICT_DONT_STRDUP_KEY; - } - if (decode_str(s, pb, encoding, &dst, &taglen) < 0) { - av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n"); - if (dict_flags & AV_DICT_DONT_STRDUP_KEY) - av_freep((void*)&key); + av_freep(&descriptor); + av_log(s, AV_LOG_ERROR, "Error reading %s frame, skipped\n", key); return; } - if (dst) - av_dict_set(metadata, key, (const char *) dst, dict_flags); + if (descriptor && *descriptor) { + if (!strcmp(key, "comment")) { + /* legacy COMM: non-empty descriptor becomes the metadata key */ + flags |= AV_DICT_DONT_STRDUP_KEY; + key = (char *)descriptor; + descriptor = NULL; + } else { + /* USLT: <tag>-<descriptor>-<lang> */ + if (av_strnlen(language, 3) > 0) + full_key = av_asprintf("%s-%s-%s", key, descriptor, language); + else if (strlen((char *)descriptor) == 3 && + ff_convert_lang_to((char *)descriptor, AV_LANG_ISO639_2_BIBL)) + /* Descriptor looks like a lang code: add trailing lang to + * keep the key unambiguous on the write side. */ + full_key = av_asprintf("%s-%s-und", key, descriptor); + else + full_key = av_asprintf("%s-%s", key, descriptor); + if (!full_key) { + av_freep(&descriptor); + av_freep(&dst); + return; + } + key = full_key; + } + } else if (av_strnlen(language, 3) == 3) { + full_key = av_asprintf("%s-%s", key, language); + if (!full_key) { + av_freep(&descriptor); + av_freep(&dst); + return; + } + key = full_key; + } + + av_freep(&descriptor); + av_dict_set(metadata, key, (const char *)dst, flags); + av_freep(&full_key); } typedef struct ExtraMetaList { @@ -1079,9 +1079,9 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, /* parse text tag */ read_ttag(s, pbx, tlen, metadata, tag); else if (!memcmp(tag, "USLT", 4)) - read_uslt(s, pbx, tlen, metadata); + read_lang_descr_tag(s, pbx, "lyrics", tlen, metadata); else if (!strcmp(tag, comm_frame)) - read_comment(s, pbx, tlen, metadata); + read_lang_descr_tag(s, pbx, "comment", tlen, metadata); else /* parse special meta tag */ extra_func->read(s, pbx, tlen, tag, extra_meta, isv34); diff --git a/tests/ref/fate/id3v2-lang-xxx b/tests/ref/fate/id3v2-lang-xxx index 56538dd63c..9d9575554f 100644 --- a/tests/ref/fate/id3v2-lang-xxx +++ b/tests/ref/fate/id3v2-lang-xxx @@ -1,4 +1,4 @@ ID3v2 frame COMM (18 bytes):|.XXX.Dummy comment| ID3v2 frame USLT (17 bytes):|.XXX.Dummy lyrics| comment=Dummy comment -lyrics-XXX=Dummy lyrics +lyrics=Dummy lyrics diff --git a/tests/ref/fate/id3v2-lang-xxx-remux b/tests/ref/fate/id3v2-lang-xxx-remux index 033e6f1fed..ee7e77b8ed 100644 --- a/tests/ref/fate/id3v2-lang-xxx-remux +++ b/tests/ref/fate/id3v2-lang-xxx-remux @@ -1,4 +1,4 @@ ID3v2 frame TXXX (23 bytes):|.comment.Dummy comment.| -ID3v2 frame TXXX (25 bytes):|.lyrics-XXX.Dummy lyrics.| +ID3v2 frame TXXX (19 bytes):|.USLT.Dummy lyrics.| comment=Dummy comment -lyrics-XXX=Dummy lyrics +lyrics=Dummy lyrics diff --git a/tests/ref/fate/id3v2-wma-comm b/tests/ref/fate/id3v2-wma-comm index 59a79bef01..68d113862c 100644 --- a/tests/ref/fate/id3v2-wma-comm +++ b/tests/ref/fate/id3v2-wma-comm @@ -8,7 +8,7 @@ TAG:WM/GenreID=Inconnu TAG:track=00 TAG:album=Tombé du Ciel TAG:genre=Inconnu -TAG:comment= +TAG:comment-eng= TAG:lyrics-eng= TAG:MusicMatch_Bio= TAG:TLEN=286000 diff --git a/tests/ref/fate/id3v2-wma-comm-asf_o b/tests/ref/fate/id3v2-wma-comm-asf_o index 49b9b9723b..639f0a02ce 100644 --- a/tests/ref/fate/id3v2-wma-comm-asf_o +++ b/tests/ref/fate/id3v2-wma-comm-asf_o @@ -10,7 +10,7 @@ TAG:track=00 TAG:artist=Higelin TAG:album=Tombé du Ciel TAG:genre=Inconnu -TAG:comment= +TAG:comment-eng= TAG:lyrics-eng= TAG:MusicMatch_Bio= TAG:TLEN=286000 _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
