FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/id3v2.c
Date: 2026-04-24 19:58:39
Exec Total Coverage
Lines: 478 668 71.6%
Functions: 32 33 97.0%
Branches: 269 458 58.7%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2003 Fabrice Bellard
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file
23 * ID3v2 header parser
24 *
25 * Specifications available at:
26 * http://id3.org/Developer_Information
27 */
28
29 #include "config.h"
30
31 #if CONFIG_ZLIB
32 #include <zlib.h>
33 #endif
34
35 #include "libavutil/attributes_internal.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/bprint.h"
38 #include "libavutil/dict.h"
39 #include "libavutil/intreadwrite.h"
40 #include "libavutil/mem.h"
41 #include "libavcodec/png.h"
42 #include "avio_internal.h"
43 #include "demux.h"
44 #include "id3v1.h"
45 #include "id3v2.h"
46
47 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
48 { "TALB", "album" },
49 { "TCOM", "composer" },
50 { "TCON", "genre" },
51 { "TCOP", "copyright" },
52 { "TENC", "encoded_by" },
53 { "TIT2", "title" },
54 { "TLAN", "language" },
55 { "TPE1", "artist" },
56 { "TPE2", "album_artist" },
57 { "TPE3", "performer" },
58 { "TPOS", "disc" },
59 { "TPUB", "publisher" },
60 { "TRCK", "track" },
61 { "TSSE", "encoder" },
62 { "USLT", "lyrics" },
63 { 0 }
64 };
65
66 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
67 { "TCMP", "compilation" },
68 { "TDRC", "date" },
69 { "TDRL", "date" },
70 { "TDEN", "creation_time" },
71 { "TSOA", "album-sort" },
72 { "TSOP", "artist-sort" },
73 { "TSOT", "title-sort" },
74 { "TIT1", "grouping" },
75 { 0 }
76 };
77
78 static const AVMetadataConv id3v2_2_metadata_conv[] = {
79 { "TAL", "album" },
80 { "TCO", "genre" },
81 { "TCP", "compilation" },
82 { "TT2", "title" },
83 { "TEN", "encoded_by" },
84 { "TP1", "artist" },
85 { "TP2", "album_artist" },
86 { "TP3", "performer" },
87 { "TRK", "track" },
88 { 0 }
89 };
90
91 attribute_nonstring const char ff_id3v2_tags[][4] = {
92 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
93 "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
94 "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
95 "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
96 { 0 },
97 };
98
99 attribute_nonstring const char ff_id3v2_4_tags[][4] = {
100 "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
101 "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
102 { 0 },
103 };
104
105 attribute_nonstring const char ff_id3v2_3_tags[][4] = {
106 "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
107 { 0 },
108 };
109
110 const char * const ff_id3v2_picture_types[21] = {
111 "Other",
112 "32x32 pixels 'file icon'",
113 "Other file icon",
114 "Cover (front)",
115 "Cover (back)",
116 "Leaflet page",
117 "Media (e.g. label side of CD)",
118 "Lead artist/lead performer/soloist",
119 "Artist/performer",
120 "Conductor",
121 "Band/Orchestra",
122 "Composer",
123 "Lyricist/text writer",
124 "Recording Location",
125 "During recording",
126 "During performance",
127 "Movie/video screen capture",
128 "A bright coloured fish",
129 "Illustration",
130 "Band/artist logotype",
131 "Publisher/Studio logotype",
132 };
133
134 const CodecMime ff_id3v2_mime_tags[] = {
135 { "image/gif", AV_CODEC_ID_GIF },
136 { "image/jpeg", AV_CODEC_ID_MJPEG },
137 { "image/jpg", AV_CODEC_ID_MJPEG },
138 { "image/jxl", AV_CODEC_ID_JPEGXL },
139 { "image/png", AV_CODEC_ID_PNG },
140 { "image/tiff", AV_CODEC_ID_TIFF },
141 { "image/bmp", AV_CODEC_ID_BMP },
142 { "image/webp", AV_CODEC_ID_WEBP },
143 { "JPG", AV_CODEC_ID_MJPEG }, /* ID3v2.2 */
144 { "PNG", AV_CODEC_ID_PNG }, /* ID3v2.2 */
145 { "", AV_CODEC_ID_NONE },
146 };
147
148 22595 int ff_id3v2_match(const uint8_t *buf, const char *magic)
149 {
150 22753 return buf[0] == magic[0] &&
151
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 30 times.
158 buf[1] == magic[1] &&
152
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 buf[2] == magic[2] &&
153
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 buf[3] != 0xff &&
154
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 buf[4] != 0xff &&
155
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 (buf[6] & 0x80) == 0 &&
156
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 (buf[7] & 0x80) == 0 &&
157
3/4
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 22437 times.
✓ Branch 2 taken 128 times.
✗ Branch 3 not taken.
22881 (buf[8] & 0x80) == 0 &&
158
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 (buf[9] & 0x80) == 0;
159 }
160
161 89 int ff_id3v2_tag_len(const uint8_t *buf)
162 {
163 89 int len = ((buf[6] & 0x7f) << 21) +
164 89 ((buf[7] & 0x7f) << 14) +
165 89 ((buf[8] & 0x7f) << 7) +
166 89 (buf[9] & 0x7f) +
167 ID3v2_HEADER_SIZE;
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (buf[5] & 0x10)
169 len += ID3v2_HEADER_SIZE;
170 89 return len;
171 }
172
173 static unsigned int get_size(AVIOContext *s, int len)
174 {
175 int v = 0;
176 while (len--)
177 v = (v << 7) + (avio_r8(s) & 0x7F);
178 return v;
179 }
180
181 20 static unsigned int size_to_syncsafe(unsigned int size)
182 {
183 20 return (((size) & (0x7f << 0)) >> 0) +
184 20 (((size) & (0x7f << 8)) >> 1) +
185 40 (((size) & (0x7f << 16)) >> 2) +
186 20 (((size) & (0x7f << 24)) >> 3);
187 }
188
189 /* No real verification, only check that the tag consists of
190 * a combination of capital alpha-numerical characters */
191 4 static int is_tag(const char *buf, unsigned int len)
192 {
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!len)
194 return 0;
195
196
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 while (len--)
197
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if ((buf[len] < 'A' ||
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 buf[len] > 'Z') &&
199 (buf[len] < '0' ||
200 buf[len] > '9'))
201 return 0;
202
203 4 return 1;
204 }
205
206 /**
207 * Return 1 if the tag of length len at the given offset is valid, 0 if not, -1 on error
208 */
209 4 static int check_tag(AVIOContext *s, int offset, unsigned int len)
210 {
211 char tag[4];
212
213
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 if (len > 4 ||
214 4 avio_seek(s, offset, SEEK_SET) < 0 ||
215
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 avio_read(s, tag, len) < (int)len)
216 return -1;
217
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 else if (!AV_RB32(tag) || is_tag(tag, len))
218 4 return 1;
219
220 return 0;
221 }
222
223 /**
224 * Free GEOB type extra metadata.
225 */
226 2 static void free_geobtag(void *obj)
227 {
228 2 ID3v2ExtraMetaGEOB *geob = obj;
229 2 av_freep(&geob->mime_type);
230 2 av_freep(&geob->file_name);
231 2 av_freep(&geob->description);
232 2 av_freep(&geob->data);
233 2 }
234
235 /**
236 * Decode characters to UTF-8 according to encoding type. The decoded buffer is
237 * always null terminated. Stop reading when either *maxread bytes are read from
238 * pb or U+0000 character is found.
239 *
240 * @param dst Pointer where the address of the buffer with the decoded bytes is
241 * stored. Buffer must be freed by caller.
242 * @param maxread Pointer to maximum number of characters to read from the
243 * AVIOContext. After execution the value is decremented by the number of bytes
244 * actually read.
245 * @returns 0 if no error occurred, dst is uninitialized on error
246 */
247 261 static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
248 uint8_t **dst, int *maxread)
249 {
250 int ret;
251 uint8_t tmp;
252 261 uint32_t ch = 1;
253 261 int left = *maxread, dynsize;
254 261 unsigned int (*get)(AVIOContext*) = avio_rb16;
255 AVIOContext *dynbuf;
256
257
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 261 times.
261 if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) {
258 av_log(s, AV_LOG_ERROR, "Error opening memory stream\n");
259 return ret;
260 }
261
262
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 253 times.
261 if (left == 0)
263 8 goto end;
264
265
4/5
✓ Branch 0 taken 83 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 143 times.
✗ Branch 4 not taken.
253 switch (encoding) {
266 83 case ID3v2_ENCODING_ISO8859:
267
4/4
✓ Branch 0 taken 828 times.
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 790 times.
✓ Branch 3 taken 38 times.
873 while (left && ch) {
268 790 ch = avio_r8(pb);
269
4/4
✓ Branch 0 taken 789 times.
✓ Branch 1 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
791 PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
270 790 left--;
271 }
272 83 break;
273
274 9 case ID3v2_ENCODING_UTF16BOM:
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if ((left -= 2) < 0) {
276 av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short %d\n", left);
277 ffio_free_dyn_buf(&dynbuf);
278 *dst = NULL;
279 return AVERROR_INVALIDDATA;
280 }
281 9 uint16_t bom = avio_rb16(pb);
282
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
9 switch (bom) {
283 8 case 0xfffe:
284 8 get = avio_rl16;
285 8 case 0xfeff:
286 8 break;
287 1 case 0: // empty string without bom
288 1 goto end;
289 default:
290 av_log(s, AV_LOG_ERROR, "Incorrect BOM value: 0x%x\n", bom);
291 ffio_free_dyn_buf(&dynbuf);
292 *dst = NULL;
293 *maxread = left;
294 return AVERROR_INVALIDDATA;
295 }
296 // fall-through
297
298 case ID3v2_ENCODING_UTF16BE:
299
4/4
✓ Branch 0 taken 206 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 197 times.
✓ Branch 3 taken 9 times.
223 while ((left > 1) && ch) {
300
2/10
✓ Branch 0 taken 197 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 197 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
197 GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
301
4/4
✓ Branch 0 taken 133 times.
✓ Branch 1 taken 64 times.
✓ Branch 5 taken 64 times.
✓ Branch 6 taken 64 times.
261 PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
302 }
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (left < 0)
304 left += 2; /* did not read last char from pb */
305 26 break;
306
307 143 case ID3v2_ENCODING_UTF8:
308
4/4
✓ Branch 0 taken 1547 times.
✓ Branch 1 taken 111 times.
✓ Branch 2 taken 1515 times.
✓ Branch 3 taken 32 times.
1658 while (left && ch) {
309 1515 ch = avio_r8(pb);
310 1515 avio_w8(dynbuf, ch);
311 1515 left--;
312 }
313 143 break;
314 default:
315 av_log(s, AV_LOG_WARNING, "Unknown encoding %d\n", encoding);
316 }
317
318 261 end:
319
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 238 times.
261 if (ch)
320 23 avio_w8(dynbuf, 0);
321
322 261 dynsize = avio_close_dyn_buf(dynbuf, dst);
323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 261 times.
261 if (dynsize <= 0) {
324 av_freep(dst);
325 return AVERROR(ENOMEM);
326 }
327 261 *maxread = left;
328
329 261 return 0;
330 }
331
332 /**
333 * Parse a text tag.
334 */
335 167 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
336 AVDictionary **metadata, const char *key)
337 {
338 uint8_t *dst;
339 167 int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
340 unsigned genre;
341
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 167 times.
167 if (taglen < 1)
343 return;
344
345 167 encoding = avio_r8(pb);
346 167 taglen--; /* account for encoding type byte */
347
348
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 167 times.
167 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
349 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
350 return;
351 }
352
353
3/4
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 154 times.
167 if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) &&
354
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) &&
355 genre <= ID3v1_GENRE_MAX) {
356 av_freep(&dst);
357 dst = av_strdup(ff_id3v1_genre_str[genre]);
358
3/4
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 140 times.
167 } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
359 /* dst now contains the key, need to get value */
360 27 key = dst;
361
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
362 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
363 av_freep(&key);
364 return;
365 }
366 27 dict_flags |= AV_DICT_DONT_STRDUP_KEY;
367
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 135 times.
140 } else if (!*dst)
368 5 av_freep(&dst);
369
370
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 5 times.
167 if (dst)
371 162 av_dict_set(metadata, key, dst, dict_flags);
372 }
373
374 1 static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen,
375 AVDictionary **metadata)
376 {
377 uint8_t lang[4];
378 1 uint8_t *descriptor = NULL; // 'Content descriptor'
379 uint8_t *text;
380 char *key;
381 int encoding;
382 1 int ok = 0;
383
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (taglen < 4)
385 goto error;
386
387 1 encoding = avio_r8(pb);
388 1 taglen--;
389
390
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (avio_read(pb, lang, 3) < 3)
391 goto error;
392 1 lang[3] = '\0';
393 1 taglen -= 3;
394
395
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (decode_str(s, pb, encoding, &descriptor, &taglen) < 0 || taglen < 0)
396 goto error;
397
398
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (decode_str(s, pb, encoding, &text, &taglen) < 0 || taglen < 0)
399 goto error;
400
401 // FFmpeg does not support hierarchical metadata, so concatenate the keys.
402
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "",
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 descriptor[0] ? "-" : "",
404 lang);
405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!key) {
406 av_free(text);
407 goto error;
408 }
409
410 1 av_dict_set(metadata, key, text,
411 AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
412
413 1 ok = 1;
414 1 error:
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ok)
416 av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n");
417 1 av_free(descriptor);
418 1 }
419
420 /**
421 * Parse a comment tag.
422 */
423 7 static void read_comment(AVFormatContext *s, AVIOContext *pb, int taglen,
424 AVDictionary **metadata)
425 {
426 7 const char *key = "comment";
427 uint8_t *dst;
428 7 int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
429 av_unused int language;
430
431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (taglen < 4)
432 return;
433
434 7 encoding = avio_r8(pb);
435 7 language = avio_rl24(pb);
436 7 taglen -= 4;
437
438
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
439 av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
440 return;
441 }
442
443
3/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5 times.
7 if (dst && !*dst)
444 2 av_freep(&dst);
445
446
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (dst) {
447 5 key = (const char *) dst;
448 5 dict_flags |= AV_DICT_DONT_STRDUP_KEY;
449 }
450
451
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
452 av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
453 if (dict_flags & AV_DICT_DONT_STRDUP_KEY)
454 av_freep((void*)&key);
455 return;
456 }
457
458
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (dst)
459 7 av_dict_set(metadata, key, (const char *) dst, dict_flags);
460 }
461
462 typedef struct ExtraMetaList {
463 ID3v2ExtraMeta *head, *tail;
464 } ExtraMetaList;
465
466 47 static void list_append(ID3v2ExtraMeta *new_elem, ExtraMetaList *list)
467 {
468
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 24 times.
47 if (list->tail)
469 23 list->tail->next = new_elem;
470 else
471 24 list->head = new_elem;
472 47 list->tail = new_elem;
473 47 }
474
475 /**
476 * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
477 */
478 2 static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
479 const char *tag, ExtraMetaList *extra_meta, int isv34)
480 {
481 2 ID3v2ExtraMetaGEOB *geob_data = NULL;
482 2 ID3v2ExtraMeta *new_extra = NULL;
483 char encoding;
484 unsigned int len;
485
486
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (taglen < 1)
487 return;
488
489 2 new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!new_extra) {
491 av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n",
492 sizeof(ID3v2ExtraMeta));
493 return;
494 }
495
496 2 geob_data = &new_extra->data.geob;
497
498 /* read encoding type byte */
499 2 encoding = avio_r8(pb);
500 2 taglen--;
501
502 /* read MIME type (always ISO-8859) */
503
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type,
504 2 &taglen) < 0 ||
505
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 taglen <= 0)
506 goto fail;
507
508 /* read file name */
509
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0 ||
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 taglen <= 0)
511 goto fail;
512
513 /* read content description */
514
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0 ||
515
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 taglen < 0)
516 goto fail;
517
518
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (taglen) {
519 /* save encapsulated binary data */
520 2 geob_data->data = av_malloc(taglen);
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!geob_data->data) {
522 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen);
523 goto fail;
524 }
525
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((len = avio_read(pb, geob_data->data, taglen)) < taglen)
526 av_log(s, AV_LOG_WARNING,
527 "Error reading GEOB frame, data truncated.\n");
528 2 geob_data->datasize = len;
529 } else {
530 geob_data->data = NULL;
531 geob_data->datasize = 0;
532 }
533
534 /* add data to the list */
535 2 new_extra->tag = "GEOB";
536 2 list_append(new_extra, extra_meta);
537
538 2 return;
539
540 fail:
541 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag);
542 free_geobtag(geob_data);
543 av_free(new_extra);
544 return;
545 }
546
547 7 static int is_number(const char *str)
548 {
549
3/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
35 while (*str >= '0' && *str <= '9')
550 28 str++;
551 7 return !*str;
552 }
553
554 249 static AVDictionaryEntry *get_date_tag(AVDictionary *m, const char *tag)
555 {
556 AVDictionaryEntry *t;
557
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 242 times.
249 if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
558
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 strlen(t->value) == 4 && is_number(t->value))
559 7 return t;
560 242 return NULL;
561 }
562
563 121 static void merge_date(AVDictionary **m)
564 {
565 AVDictionaryEntry *t;
566 121 char date[17] = { 0 }; // YYYY-MM-DD hh:mm
567
568
3/4
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 116 times.
✗ Branch 4 not taken.
237 if (!(t = get_date_tag(*m, "TYER")) &&
569 116 !(t = get_date_tag(*m, "TYE")))
570 116 return;
571 5 av_strlcpy(date, t->value, 5);
572 5 av_dict_set(m, "TYER", NULL, 0);
573 5 av_dict_set(m, "TYE", NULL, 0);
574
575
3/4
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
8 if (!(t = get_date_tag(*m, "TDAT")) &&
576 3 !(t = get_date_tag(*m, "TDA")))
577 3 goto finish;
578 2 snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
579 2 av_dict_set(m, "TDAT", NULL, 0);
580 2 av_dict_set(m, "TDA", NULL, 0);
581
582
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
4 if (!(t = get_date_tag(*m, "TIME")) &&
583 2 !(t = get_date_tag(*m, "TIM")))
584 2 goto finish;
585 snprintf(date + 10, sizeof(date) - 10,
586 " %.2s:%.2s", t->value, t->value + 2);
587 av_dict_set(m, "TIME", NULL, 0);
588 av_dict_set(m, "TIM", NULL, 0);
589
590 5 finish:
591
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (date[0])
592 5 av_dict_set(m, "date", date, 0);
593 }
594
595 20 static void free_apic(void *obj)
596 {
597 20 ID3v2ExtraMetaAPIC *apic = obj;
598 20 av_buffer_unref(&apic->buf);
599 20 av_freep(&apic->description);
600 20 }
601
602 20 static void rstrip_spaces(char *buf)
603 {
604 20 size_t len = strlen(buf);
605
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
20 while (len > 0 && buf[len - 1] == ' ')
606 buf[--len] = 0;
607 20 }
608
609 20 static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
610 const char *tag, ExtraMetaList *extra_meta, int isv34)
611 {
612 int enc, pic_type;
613 20 char mimetype[64] = {0};
614 20 const CodecMime *mime = ff_id3v2_mime_tags;
615 20 enum AVCodecID id = AV_CODEC_ID_NONE;
616 20 ID3v2ExtraMetaAPIC *apic = NULL;
617 20 ID3v2ExtraMeta *new_extra = NULL;
618 20 int64_t end = avio_tell(pb) + taglen;
619
620
2/6
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
20 if (taglen <= 4 || (!isv34 && taglen <= 6))
621 goto fail;
622
623 20 new_extra = av_mallocz(sizeof(*new_extra));
624
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (!new_extra)
625 goto fail;
626
627 20 apic = &new_extra->data.apic;
628
629 20 enc = avio_r8(pb);
630 20 taglen--;
631
632 /* mimetype */
633
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (isv34) {
634 20 int ret = avio_get_str(pb, taglen, mimetype, sizeof(mimetype));
635
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
20 if (ret < 0 || ret >= taglen)
636 goto fail;
637 20 taglen -= ret;
638 } else {
639 if (avio_read(pb, mimetype, 3) < 0)
640 goto fail;
641
642 mimetype[3] = 0;
643 taglen -= 3;
644 }
645
646
1/2
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
75 while (mime->id != AV_CODEC_ID_NONE) {
647
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 55 times.
75 if (!av_strncasecmp(mime->str, mimetype, sizeof(mimetype))) {
648 20 id = mime->id;
649 20 break;
650 }
651 55 mime++;
652 }
653
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (id == AV_CODEC_ID_NONE) {
654 av_log(s, AV_LOG_WARNING,
655 "Unknown attached picture mimetype: %s, skipping.\n", mimetype);
656 goto fail;
657 }
658 20 apic->id = id;
659
660 /* picture type */
661 20 pic_type = avio_r8(pb);
662 20 taglen--;
663
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
20 if (pic_type < 0 || pic_type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) {
664 av_log(s, AV_LOG_WARNING, "Unknown attached picture type %d.\n",
665 pic_type);
666 pic_type = 0;
667 }
668 20 apic->type = ff_id3v2_picture_types[pic_type];
669
670 /* description and picture data */
671
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
20 if (decode_str(s, pb, enc, &apic->description, &taglen) < 0) {
672 av_log(s, AV_LOG_ERROR,
673 "Error decoding attached picture description.\n");
674 goto fail;
675 }
676
677 20 apic->buf = av_buffer_alloc(taglen + AV_INPUT_BUFFER_PADDING_SIZE);
678
3/6
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 20 times.
20 if (!apic->buf || !taglen || avio_read(pb, apic->buf->data, taglen) != taglen)
679 goto fail;
680 20 memset(apic->buf->data + taglen, 0, AV_INPUT_BUFFER_PADDING_SIZE);
681
682 20 new_extra->tag = "APIC";
683
684 // The description must be unique, and some ID3v2 tag writers add spaces
685 // to write several APIC entries with the same description.
686 20 rstrip_spaces(apic->description);
687 20 list_append(new_extra, extra_meta);
688
689 20 return;
690
691 fail:
692 if (apic)
693 free_apic(apic);
694 av_freep(&new_extra);
695 avio_seek(pb, end, SEEK_SET);
696 }
697
698 10 static void free_chapter(void *obj)
699 {
700 10 ID3v2ExtraMetaCHAP *chap = obj;
701 10 av_freep(&chap->element_id);
702 10 av_dict_free(&chap->meta);
703 10 }
704
705 10 static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len,
706 const char *ttag, ExtraMetaList *extra_meta, int isv34)
707 {
708 int taglen;
709 char tag[5];
710 10 ID3v2ExtraMeta *new_extra = NULL;
711 10 ID3v2ExtraMetaCHAP *chap = NULL;
712
713 10 new_extra = av_mallocz(sizeof(*new_extra));
714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!new_extra)
715 10 return;
716
717 10 chap = &new_extra->data.chap;
718
719
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (decode_str(s, pb, 0, &chap->element_id, &len) < 0)
720 goto fail;
721
722
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (len < 16)
723 goto fail;
724
725 10 chap->start = avio_rb32(pb);
726 10 chap->end = avio_rb32(pb);
727 10 avio_skip(pb, 8);
728
729 10 len -= 16;
730
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 10 times.
28 while (len > 10) {
731
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 if (avio_read(pb, tag, 4) < 4)
732 goto fail;
733 18 tag[4] = 0;
734 18 taglen = avio_rb32(pb);
735 18 avio_skip(pb, 2);
736 18 len -= 10;
737
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (taglen < 0 || taglen > len)
738 goto fail;
739
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (tag[0] == 'T')
740 18 read_ttag(s, pb, taglen, &chap->meta, tag);
741 else
742 avio_skip(pb, taglen);
743 18 len -= taglen;
744 }
745
746 10 ff_metadata_conv(&chap->meta, NULL, ff_id3v2_34_metadata_conv);
747 10 ff_metadata_conv(&chap->meta, NULL, ff_id3v2_4_metadata_conv);
748
749 10 new_extra->tag = "CHAP";
750 10 list_append(new_extra, extra_meta);
751
752 10 return;
753
754 fail:
755 free_chapter(chap);
756 av_freep(&new_extra);
757 }
758
759 15 static void free_priv(void *obj)
760 {
761 15 ID3v2ExtraMetaPRIV *priv = obj;
762 15 av_freep(&priv->owner);
763 15 av_freep(&priv->data);
764 15 }
765
766 15 static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen,
767 const char *tag, ExtraMetaList *extra_meta, int isv34)
768 {
769 ID3v2ExtraMeta *meta;
770 ID3v2ExtraMetaPRIV *priv;
771
772 15 meta = av_mallocz(sizeof(*meta));
773
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!meta)
774 15 return;
775
776 15 priv = &meta->data.priv;
777
778
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0)
779 goto fail;
780
781 15 priv->data = av_malloc(taglen);
782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!priv->data)
783 goto fail;
784
785 15 priv->datasize = taglen;
786
787
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if (avio_read(pb, priv->data, priv->datasize) != priv->datasize)
788 goto fail;
789
790 15 meta->tag = "PRIV";
791 15 list_append(meta, extra_meta);
792
793 15 return;
794
795 fail:
796 free_priv(priv);
797 av_freep(&meta);
798 }
799
800 typedef struct ID3v2EMFunc {
801 const char *tag3;
802 const char *tag4;
803 void (*read)(AVFormatContext *s, AVIOContext *pb, int taglen,
804 const char *tag, ExtraMetaList *extra_meta,
805 int isv34);
806 void (*free)(void *obj);
807 } ID3v2EMFunc;
808
809 static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
810 { "GEO", "GEOB", read_geobtag, free_geobtag },
811 { "PIC", "APIC", read_apic, free_apic },
812 { "CHAP","CHAP", read_chapter, free_chapter },
813 { "PRIV","PRIV", read_priv, free_priv },
814 { NULL }
815 };
816
817 /**
818 * Get the corresponding ID3v2EMFunc struct for a tag.
819 * @param isv34 Determines if v2.2 or v2.3/4 strings are used
820 * @return A pointer to the ID3v2EMFunc struct if found, NULL otherwise.
821 */
822 96 static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
823 {
824 96 int i = 0;
825
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 2 times.
274 while (id3v2_extra_meta_funcs[i].tag3) {
826
5/8
✓ Branch 0 taken 272 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 272 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 272 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 94 times.
✓ Branch 7 taken 178 times.
272 if (tag && !memcmp(tag,
827 (isv34 ? id3v2_extra_meta_funcs[i].tag4 :
828 id3v2_extra_meta_funcs[i].tag3),
829 (isv34 ? 4 : 3)))
830 94 return &id3v2_extra_meta_funcs[i];
831 178 i++;
832 }
833 2 return NULL;
834 }
835
836 39 static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
837 AVFormatContext *s, int len, uint8_t version,
838 uint8_t flags, ExtraMetaList *extra_meta)
839 {
840 int isv34, unsync;
841 unsigned tlen;
842 char tag[5];
843 39 int64_t next, end = avio_tell(pb);
844 int taghdrlen;
845 39 const char *reason = NULL;
846 FFIOContext pb_local;
847 AVIOContext *pbx;
848 39 unsigned char *buffer = NULL;
849 39 int buffer_size = 0;
850 39 const ID3v2EMFunc *extra_func = NULL;
851 39 unsigned char *uncompressed_buffer = NULL;
852 39 av_unused int uncompressed_buffer_size = 0;
853 const char *comm_frame;
854
855
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (end > INT64_MAX - len - 10)
856 return;
857 39 end += len;
858
859 39 av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len);
860
861
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 switch (version) {
862 case 2:
863 if (flags & 0x40) {
864 reason = "compression";
865 goto error;
866 }
867 isv34 = 0;
868 taghdrlen = 6;
869 comm_frame = "COM";
870 break;
871
872 39 case 3:
873 case 4:
874 39 isv34 = 1;
875 39 taghdrlen = 10;
876 39 comm_frame = "COMM";
877 39 break;
878
879 default:
880 reason = "version";
881 goto error;
882 }
883
884 39 unsync = flags & 0x80;
885
886
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
39 if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
887 int extlen = get_size(pb, 4);
888 if (version == 4)
889 /* In v2.4 the length includes the length field we just read. */
890 extlen -= 4;
891
892 if (extlen < 0) {
893 reason = "invalid extended header length";
894 goto error;
895 }
896 avio_skip(pb, extlen);
897 len -= extlen + 4;
898 if (len < 0) {
899 reason = "extended header too long.";
900 goto error;
901 }
902 }
903
904
2/2
✓ Branch 0 taken 1937 times.
✓ Branch 1 taken 38 times.
1975 while (len >= taghdrlen) {
905 1937 unsigned int tflags = 0;
906 1937 int tunsync = 0;
907 1937 int tcomp = 0;
908 1937 int tencr = 0;
909 av_unused unsigned long dlen;
910
911
1/2
✓ Branch 0 taken 1937 times.
✗ Branch 1 not taken.
1937 if (isv34) {
912
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1936 times.
1937 if (avio_read(pb, tag, 4) < 4)
913 1 break;
914 1936 tag[4] = 0;
915
2/2
✓ Branch 0 taken 990 times.
✓ Branch 1 taken 946 times.
1936 if (version == 3) {
916 990 tlen = avio_rb32(pb);
917 } else {
918 /* some encoders incorrectly uses v3 sizes instead of syncsafe ones
919 * so check the next tag to see which one to use */
920 946 tlen = avio_rb32(pb);
921
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 930 times.
946 if (tlen > 0x7f) {
922
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
16 if (tlen < len) {
923 4 int64_t cur = avio_tell(pb);
924
925
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (ffio_ensure_seekback(pb, 2 /* tflags */ + tlen + 4 /* next tag */))
926 break;
927
928
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if (check_tag(pb, cur + 2 + size_to_syncsafe(tlen), 4) == 1)
929 4 tlen = size_to_syncsafe(tlen);
930 else if (check_tag(pb, cur + 2 + tlen, 4) != 1)
931 break;
932 4 avio_seek(pb, cur, SEEK_SET);
933 } else
934 12 tlen = size_to_syncsafe(tlen);
935 }
936 }
937 1936 tflags = avio_rb16(pb);
938 1936 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
939 } else {
940 if (avio_read(pb, tag, 3) < 3)
941 break;
942 tag[3] = 0;
943 tlen = avio_rb24(pb);
944 }
945
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1936 times.
1936 if (tlen > (1<<28))
946 break;
947 1936 len -= taghdrlen + tlen;
948
949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1936 times.
1936 if (len < 0)
950 break;
951
952 1936 next = avio_tell(pb) + tlen;
953
954
2/2
✓ Branch 0 taken 1730 times.
✓ Branch 1 taken 206 times.
1936 if (!tlen) {
955
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1726 times.
1730 if (tag[0])
956 4 av_log(s, AV_LOG_DEBUG, "Invalid empty frame %s, skipping.\n",
957 tag);
958 1730 continue;
959 }
960
961
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if (tflags & ID3v2_FLAG_DATALEN) {
962 if (tlen < 4)
963 break;
964 dlen = avio_rb32(pb);
965 tlen -= 4;
966 } else
967 206 dlen = tlen;
968
969 206 tcomp = tflags & ID3v2_FLAG_COMPRESSION;
970 206 tencr = tflags & ID3v2_FLAG_ENCRYPTION;
971
972 /* skip encrypted tags and, if no zlib, compressed tags */
973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if (tencr || (!CONFIG_ZLIB && tcomp)) {
974 const char *type;
975 if (!tcomp)
976 type = "encrypted";
977 else if (!tencr)
978 type = "compressed";
979 else
980 type = "encrypted and compressed";
981
982 av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
983 avio_skip(pb, tlen);
984 /* check for text tag or supported special meta tag */
985
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 149 times.
206 } else if (tag[0] == 'T' ||
986
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 1 times.
57 !memcmp(tag, "USLT", 4) ||
987
3/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
56 !strcmp(tag, comm_frame) ||
988
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 2 times.
49 (extra_meta &&
989 49 (extra_func = get_extra_meta_func(tag, isv34)))) {
990 204 pbx = pb;
991
992
4/6
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 197 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 197 times.
204 if (unsync || tunsync || tcomp) {
993 7 av_fast_malloc(&buffer, &buffer_size, tlen);
994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!buffer) {
995 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
996 goto seek;
997 }
998 }
999
3/4
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 197 times.
204 if (unsync || tunsync) {
1000 7 uint8_t *b = buffer;
1001 7 uint8_t *t = buffer;
1002
1003
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (avio_read(pb, buffer, tlen) != tlen) {
1004 av_log(s, AV_LOG_ERROR, "Failed to read tag data\n");
1005 goto seek;
1006 }
1007
1008 7 const uint8_t *const buf_end = t + tlen;
1009
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 7 times.
118 while (t != buf_end) {
1010 111 *b++ = *t++;
1011
3/6
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 104 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
111 if (t != buf_end && t[-1] == 0xff && !t[0])
1012 t++;
1013 }
1014
1015 7 ffio_init_read_context(&pb_local, buffer, b - buffer);
1016 7 tlen = b - buffer;
1017 7 pbx = &pb_local.pub; // read from sync buffer
1018 }
1019
1020 #if CONFIG_ZLIB
1021
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
204 if (tcomp) {
1022 int err;
1023
1024 av_log(s, AV_LOG_DEBUG, "Compressed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen);
1025
1026 if (tlen <= 0)
1027 goto seek;
1028 if (dlen / 32768 > tlen)
1029 goto seek;
1030
1031 av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen);
1032 if (!uncompressed_buffer) {
1033 av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen);
1034 goto seek;
1035 }
1036
1037 if (!(unsync || tunsync)) {
1038 err = avio_read(pb, buffer, tlen);
1039 if (err < 0) {
1040 av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
1041 goto seek;
1042 }
1043 tlen = err;
1044 }
1045
1046 err = uncompress(uncompressed_buffer, &dlen, buffer, tlen);
1047 if (err != Z_OK) {
1048 av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
1049 goto seek;
1050 }
1051 ffio_init_read_context(&pb_local, uncompressed_buffer, dlen);
1052 tlen = dlen;
1053 pbx = &pb_local.pub; // read from sync buffer
1054 }
1055 #endif
1056
2/2
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 55 times.
204 if (tag[0] == 'T')
1057 /* parse text tag */
1058 149 read_ttag(s, pbx, tlen, metadata, tag);
1059
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 54 times.
55 else if (!memcmp(tag, "USLT", 4))
1060 1 read_uslt(s, pbx, tlen, metadata);
1061
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 47 times.
54 else if (!strcmp(tag, comm_frame))
1062 7 read_comment(s, pbx, tlen, metadata);
1063 else
1064 /* parse special meta tag */
1065 47 extra_func->read(s, pbx, tlen, tag, extra_meta, isv34);
1066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (!tag[0]) {
1067 if (tag[1])
1068 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n");
1069 avio_skip(pb, tlen);
1070 break;
1071 }
1072 /* Skip to end of tag */
1073 2 seek:
1074 206 avio_seek(pb, next, SEEK_SET);
1075 }
1076
1077 /* Footer preset, always 10 bytes, skip over it */
1078
3/4
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
39 if (version == 4 && flags & 0x10)
1079 end += 10;
1080
1081 39 error:
1082
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (reason)
1083 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n",
1084 version, reason);
1085 39 avio_seek(pb, end, SEEK_SET);
1086 39 av_free(buffer);
1087 39 av_free(uncompressed_buffer);
1088 39 return;
1089 }
1090
1091 121 static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
1092 AVFormatContext *s, const char *magic,
1093 ID3v2ExtraMeta **extra_metap, int64_t max_search_size)
1094 {
1095 int len, ret;
1096 uint8_t buf[ID3v2_HEADER_SIZE];
1097 121 ExtraMetaList extra_meta = { NULL };
1098 int found_header;
1099 int64_t start, off;
1100
1101
1/2
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
121 if (extra_metap)
1102 121 *extra_metap = NULL;
1103
1104
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
121 if (max_search_size && max_search_size < ID3v2_HEADER_SIZE)
1105 return;
1106
1107 121 start = avio_tell(pb);
1108 do {
1109 /* save the current offset in case there's nothing to read/skip */
1110 160 off = avio_tell(pb);
1111
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 146 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 7 times.
160 if (max_search_size && off - start >= max_search_size - ID3v2_HEADER_SIZE) {
1112 7 avio_seek(pb, off, SEEK_SET);
1113 7 break;
1114 }
1115
1116 153 ret = ffio_ensure_seekback(pb, ID3v2_HEADER_SIZE);
1117
1/2
✓ Branch 0 taken 153 times.
✗ Branch 1 not taken.
153 if (ret >= 0)
1118 153 ret = avio_read(pb, buf, ID3v2_HEADER_SIZE);
1119
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 149 times.
153 if (ret != ID3v2_HEADER_SIZE) {
1120 4 avio_seek(pb, off, SEEK_SET);
1121 4 break;
1122 }
1123 149 found_header = ff_id3v2_match(buf, magic);
1124
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 110 times.
149 if (found_header) {
1125 /* parse ID3v2 header */
1126 39 len = ((buf[6] & 0x7f) << 21) |
1127 39 ((buf[7] & 0x7f) << 14) |
1128 39 ((buf[8] & 0x7f) << 7) |
1129 39 (buf[9] & 0x7f);
1130
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 id3v2_parse(pb, metadata, s, len, buf[3], buf[5],
1131 extra_metap ? &extra_meta : NULL);
1132 } else {
1133 110 avio_seek(pb, off, SEEK_SET);
1134 }
1135
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 110 times.
149 } while (found_header);
1136 121 ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv);
1137 121 ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv);
1138 121 ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv);
1139 121 merge_date(metadata);
1140
1/2
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
121 if (extra_metap)
1141 121 *extra_metap = extra_meta.head;
1142 }
1143
1144 107 void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata,
1145 const char *magic, ID3v2ExtraMeta **extra_meta)
1146 {
1147 107 id3v2_read_internal(pb, metadata, NULL, magic, extra_meta, 0);
1148 107 }
1149
1150 14 void ff_id3v2_read(AVFormatContext *s, const char *magic,
1151 ID3v2ExtraMeta **extra_meta, unsigned int max_search_size)
1152 {
1153 14 id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta, max_search_size);
1154 14 }
1155
1156 43 void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
1157 {
1158 43 ID3v2ExtraMeta *current = *extra_meta, *next;
1159 const ID3v2EMFunc *extra_func;
1160
1161
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 43 times.
90 while (current) {
1162
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 if ((extra_func = get_extra_meta_func(current->tag, 1)))
1163 47 extra_func->free(&current->data);
1164 47 next = current->next;
1165 47 av_freep(&current);
1166 47 current = next;
1167 }
1168
1169 43 *extra_meta = NULL;
1170 43 }
1171
1172 18 int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta)
1173 {
1174 ID3v2ExtraMeta *cur;
1175
1176
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 18 times.
58 for (cur = extra_meta; cur; cur = cur->next) {
1177 ID3v2ExtraMetaAPIC *apic;
1178 AVStream *st;
1179 int ret;
1180
1181
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 if (strcmp(cur->tag, "APIC"))
1182 20 continue;
1183 20 apic = &cur->data.apic;
1184
1185 20 ret = ff_add_attached_pic(s, NULL, NULL, &apic->buf, 0);
1186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (ret < 0)
1187 return ret;
1188 20 st = s->streams[s->nb_streams - 1];
1189 20 st->codecpar->codec_id = apic->id;
1190
1191
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 16 times.
20 if (AV_RB64(st->attached_pic.data) == PNGSIG)
1192 4 st->codecpar->codec_id = AV_CODEC_ID_PNG;
1193
1194
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
20 if (apic->description[0])
1195 10 av_dict_set(&st->metadata, "title", apic->description, 0);
1196
1197 20 av_dict_set(&st->metadata, "comment", apic->type, 0);
1198 }
1199
1200 18 return 0;
1201 }
1202
1203 25 int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta *cur)
1204 {
1205 25 AVRational time_base = {1, 1000};
1206 int ret;
1207
1208
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 25 times.
69 for (unsigned i = 0; cur; cur = cur->next) {
1209 ID3v2ExtraMetaCHAP *chap;
1210 AVChapter *chapter;
1211
1212
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 10 times.
44 if (strcmp(cur->tag, "CHAP"))
1213 34 continue;
1214
1215 10 chap = &cur->data.chap;
1216 10 chapter = avpriv_new_chapter(s, i++, time_base, chap->start,
1217 10 chap->end, chap->element_id);
1218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!chapter)
1219 continue;
1220
1221
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if ((ret = av_dict_copy(&chapter->metadata, chap->meta, 0)) < 0)
1222 return ret;
1223 }
1224
1225 25 return 0;
1226 }
1227
1228 14 int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta *extra_meta)
1229 {
1230 ID3v2ExtraMeta *cur;
1231 14 int dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL;
1232
1233
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 14 times.
36 for (cur = extra_meta; cur; cur = cur->next) {
1234
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 9 times.
22 if (!strcmp(cur->tag, "PRIV")) {
1235 13 ID3v2ExtraMetaPRIV *priv = &cur->data.priv;
1236 AVBPrint bprint;
1237 char *escaped, *key;
1238 int i, ret;
1239
1240
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if ((key = av_asprintf(ID3v2_PRIV_METADATA_PREFIX "%s", priv->owner)) == NULL) {
1241 return AVERROR(ENOMEM);
1242 }
1243
1244 13 av_bprint_init(&bprint, priv->datasize + 1, AV_BPRINT_SIZE_UNLIMITED);
1245
1246
2/2
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 13 times.
108 for (i = 0; i < priv->datasize; i++) {
1247
5/6
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 51 times.
95 if (priv->data[i] < 32 || priv->data[i] > 126 || priv->data[i] == '\\') {
1248 44 av_bprintf(&bprint, "\\x%02x", priv->data[i]);
1249 } else {
1250 51 av_bprint_chars(&bprint, priv->data[i], 1);
1251 }
1252 }
1253
1254
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if ((ret = av_bprint_finalize(&bprint, &escaped)) < 0) {
1255 av_free(key);
1256 return ret;
1257 }
1258
1259
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if ((ret = av_dict_set(metadata, key, escaped, dict_flags)) < 0) {
1260 return ret;
1261 }
1262 }
1263 }
1264
1265 14 return 0;
1266 }
1267
1268 11 int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta *extra_meta)
1269 {
1270 11 return ff_id3v2_parse_priv_dict(&s->metadata, extra_meta);
1271 }
1272