1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-04 22:03:09 +02:00

lavf/id3v2dec: support multiple values and TIPL frames

Fixes https://trac.ffmpeg.org/ticket/6949

Ordinary text frames in ID3v2 are allowed to have multiple
(null-separated) values. This technically isn't allowed in TXXX,
but it's used in practice by Picard, and supporting it is harmless.

TIPL/IPL (Involved People List) and TMCL (Musician Credits List) work
similarly to TXXX, but alternate key-value-key-value.

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
This commit is contained in:
rcombs
2022-08-24 19:27:22 -05:00
committed by Michael Niedermayer
parent 172e7ca668
commit 80b77e8e8d

View File

@ -327,8 +327,12 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
AVDictionary **metadata, const char *key)
{
uint8_t *dst;
int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
uint8_t *dst_key = NULL;
int encoding, dict_flags = AV_DICT_MULTIKEY | AV_DICT_DONT_STRDUP_VAL | AV_DICT_DEDUP;
unsigned genre;
int count = 0;
int is_tipl = !(strcmp(key, "TIPL") && strcmp(key, "TMCL") &&
strcmp(key, "IPL"));
if (taglen < 1)
return;
@ -336,25 +340,25 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
encoding = avio_r8(pb);
taglen--; /* account for encoding type byte */
while (taglen > 1) {
if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
return;
}
count++;
if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) &&
(sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) &&
genre <= ID3v1_GENRE_MAX) {
av_freep(&dst);
dst = av_strdup(ff_id3v1_genre_str[genre]);
} else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
} else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX")) ||
(is_tipl && (count & 1))) {
/* dst now contains the key, need to get value */
key = dst;
if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
av_freep(&key);
return;
}
dict_flags |= AV_DICT_DONT_STRDUP_KEY;
av_free(dst_key);
key = dst_key = dst;
continue;
} else if (!*dst)
av_freep(&dst);
@ -362,6 +366,9 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
av_dict_set(metadata, key, dst, dict_flags);
}
av_free(dst_key);
}
static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen,
AVDictionary **metadata)
{
@ -1044,7 +1051,7 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
pbx = &pb_local.pub; // read from sync buffer
}
#endif
if (tag[0] == 'T')
if (tag[0] == 'T' || !strcmp(tag, "IPL"))
/* parse text tag */
read_ttag(s, pbx, tlen, metadata, tag);
else if (!memcmp(tag, "USLT", 4))