Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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/avstring.h" | |
36 | #include "libavutil/dict.h" | |
37 | #include "libavutil/intreadwrite.h" | |
38 | #include "avio_internal.h" | |
39 | #include "internal.h" | |
40 | #include "id3v1.h" | |
41 | #include "id3v2.h" | |
42 | ||
43 | const AVMetadataConv ff_id3v2_34_metadata_conv[] = { | |
44 | { "TALB", "album" }, | |
45 | { "TCOM", "composer" }, | |
46 | { "TCON", "genre" }, | |
47 | { "TCOP", "copyright" }, | |
48 | { "TENC", "encoded_by" }, | |
49 | { "TIT2", "title" }, | |
50 | { "TLAN", "language" }, | |
51 | { "TPE1", "artist" }, | |
52 | { "TPE2", "album_artist" }, | |
53 | { "TPE3", "performer" }, | |
54 | { "TPOS", "disc" }, | |
55 | { "TPUB", "publisher" }, | |
56 | { "TRCK", "track" }, | |
57 | { "TSSE", "encoder" }, | |
58 | { 0 } | |
59 | }; | |
60 | ||
61 | const AVMetadataConv ff_id3v2_4_metadata_conv[] = { | |
62 | { "TCMP", "compilation" }, | |
63 | { "TDRL", "date" }, | |
64 | { "TDRC", "date" }, | |
65 | { "TDEN", "creation_time" }, | |
66 | { "TSOA", "album-sort" }, | |
67 | { "TSOP", "artist-sort" }, | |
68 | { "TSOT", "title-sort" }, | |
69 | { 0 } | |
70 | }; | |
71 | ||
72 | static const AVMetadataConv id3v2_2_metadata_conv[] = { | |
73 | { "TAL", "album" }, | |
74 | { "TCO", "genre" }, | |
75 | { "TCP", "compilation" }, | |
76 | { "TT2", "title" }, | |
77 | { "TEN", "encoded_by" }, | |
78 | { "TP1", "artist" }, | |
79 | { "TP2", "album_artist" }, | |
80 | { "TP3", "performer" }, | |
81 | { "TRK", "track" }, | |
82 | { 0 } | |
83 | }; | |
84 | ||
85 | const char ff_id3v2_tags[][4] = { | |
86 | "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT", | |
87 | "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED", | |
88 | "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", | |
89 | "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE", | |
90 | { 0 }, | |
91 | }; | |
92 | ||
93 | const char ff_id3v2_4_tags[][4] = { | |
94 | "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO", | |
95 | "TPRO", "TSOA", "TSOP", "TSOT", "TSST", | |
96 | { 0 }, | |
97 | }; | |
98 | ||
99 | const char ff_id3v2_3_tags[][4] = { | |
100 | "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER", | |
101 | { 0 }, | |
102 | }; | |
103 | ||
104 | const char *ff_id3v2_picture_types[21] = { | |
105 | "Other", | |
106 | "32x32 pixels 'file icon'", | |
107 | "Other file icon", | |
108 | "Cover (front)", | |
109 | "Cover (back)", | |
110 | "Leaflet page", | |
111 | "Media (e.g. label side of CD)", | |
112 | "Lead artist/lead performer/soloist", | |
113 | "Artist/performer", | |
114 | "Conductor", | |
115 | "Band/Orchestra", | |
116 | "Composer", | |
117 | "Lyricist/text writer", | |
118 | "Recording Location", | |
119 | "During recording", | |
120 | "During performance", | |
121 | "Movie/video screen capture", | |
122 | "A bright coloured fish", | |
123 | "Illustration", | |
124 | "Band/artist logotype", | |
125 | "Publisher/Studio logotype", | |
126 | }; | |
127 | ||
128 | const CodecMime ff_id3v2_mime_tags[] = { | |
129 | { "image/gif", AV_CODEC_ID_GIF }, | |
130 | { "image/jpeg", AV_CODEC_ID_MJPEG }, | |
131 | { "image/jpg", AV_CODEC_ID_MJPEG }, | |
132 | { "image/png", AV_CODEC_ID_PNG }, | |
133 | { "image/tiff", AV_CODEC_ID_TIFF }, | |
134 | { "image/bmp", AV_CODEC_ID_BMP }, | |
135 | { "JPG", AV_CODEC_ID_MJPEG }, /* ID3v2.2 */ | |
136 | { "PNG", AV_CODEC_ID_PNG }, /* ID3v2.2 */ | |
137 | { "", AV_CODEC_ID_NONE }, | |
138 | }; | |
139 | ||
140 | int ff_id3v2_match(const uint8_t *buf, const char *magic) | |
141 | { | |
142 | return buf[0] == magic[0] && | |
143 | buf[1] == magic[1] && | |
144 | buf[2] == magic[2] && | |
145 | buf[3] != 0xff && | |
146 | buf[4] != 0xff && | |
147 | (buf[6] & 0x80) == 0 && | |
148 | (buf[7] & 0x80) == 0 && | |
149 | (buf[8] & 0x80) == 0 && | |
150 | (buf[9] & 0x80) == 0; | |
151 | } | |
152 | ||
153 | int ff_id3v2_tag_len(const uint8_t *buf) | |
154 | { | |
155 | int len = ((buf[6] & 0x7f) << 21) + | |
156 | ((buf[7] & 0x7f) << 14) + | |
157 | ((buf[8] & 0x7f) << 7) + | |
158 | (buf[9] & 0x7f) + | |
159 | ID3v2_HEADER_SIZE; | |
160 | if (buf[5] & 0x10) | |
161 | len += ID3v2_HEADER_SIZE; | |
162 | return len; | |
163 | } | |
164 | ||
165 | static unsigned int get_size(AVIOContext *s, int len) | |
166 | { | |
167 | int v = 0; | |
168 | while (len--) | |
169 | v = (v << 7) + (avio_r8(s) & 0x7F); | |
170 | return v; | |
171 | } | |
172 | ||
173 | /** | |
174 | * Free GEOB type extra metadata. | |
175 | */ | |
176 | static void free_geobtag(void *obj) | |
177 | { | |
178 | ID3v2ExtraMetaGEOB *geob = obj; | |
179 | av_free(geob->mime_type); | |
180 | av_free(geob->file_name); | |
181 | av_free(geob->description); | |
182 | av_free(geob->data); | |
183 | av_free(geob); | |
184 | } | |
185 | ||
186 | /** | |
187 | * Decode characters to UTF-8 according to encoding type. The decoded buffer is | |
188 | * always null terminated. Stop reading when either *maxread bytes are read from | |
189 | * pb or U+0000 character is found. | |
190 | * | |
191 | * @param dst Pointer where the address of the buffer with the decoded bytes is | |
192 | * stored. Buffer must be freed by caller. | |
193 | * @param maxread Pointer to maximum number of characters to read from the | |
194 | * AVIOContext. After execution the value is decremented by the number of bytes | |
195 | * actually read. | |
196 | * @returns 0 if no error occurred, dst is uninitialized on error | |
197 | */ | |
198 | static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, | |
199 | uint8_t **dst, int *maxread) | |
200 | { | |
201 | int ret; | |
202 | uint8_t tmp; | |
203 | uint32_t ch = 1; | |
204 | int left = *maxread; | |
205 | unsigned int (*get)(AVIOContext*) = avio_rb16; | |
206 | AVIOContext *dynbuf; | |
207 | ||
208 | if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) { | |
209 | av_log(s, AV_LOG_ERROR, "Error opening memory stream\n"); | |
210 | return ret; | |
211 | } | |
212 | ||
213 | switch (encoding) { | |
214 | case ID3v2_ENCODING_ISO8859: | |
215 | while (left && ch) { | |
216 | ch = avio_r8(pb); | |
217 | PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);) | |
218 | left--; | |
219 | } | |
220 | break; | |
221 | ||
222 | case ID3v2_ENCODING_UTF16BOM: | |
223 | if ((left -= 2) < 0) { | |
224 | av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n"); | |
225 | avio_close_dyn_buf(dynbuf, dst); | |
226 | av_freep(dst); | |
227 | return AVERROR_INVALIDDATA; | |
228 | } | |
229 | switch (avio_rb16(pb)) { | |
230 | case 0xfffe: | |
231 | get = avio_rl16; | |
232 | case 0xfeff: | |
233 | break; | |
234 | default: | |
235 | av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n"); | |
236 | avio_close_dyn_buf(dynbuf, dst); | |
237 | av_freep(dst); | |
238 | *maxread = left; | |
239 | return AVERROR_INVALIDDATA; | |
240 | } | |
241 | // fall-through | |
242 | ||
243 | case ID3v2_ENCODING_UTF16BE: | |
244 | while ((left > 1) && ch) { | |
245 | GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;) | |
246 | PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);) | |
247 | } | |
248 | if (left < 0) | |
249 | left += 2; /* did not read last char from pb */ | |
250 | break; | |
251 | ||
252 | case ID3v2_ENCODING_UTF8: | |
253 | while (left && ch) { | |
254 | ch = avio_r8(pb); | |
255 | avio_w8(dynbuf, ch); | |
256 | left--; | |
257 | } | |
258 | break; | |
259 | default: | |
260 | av_log(s, AV_LOG_WARNING, "Unknown encoding\n"); | |
261 | } | |
262 | ||
263 | if (ch) | |
264 | avio_w8(dynbuf, 0); | |
265 | ||
266 | avio_close_dyn_buf(dynbuf, dst); | |
267 | *maxread = left; | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
272 | /** | |
273 | * Parse a text tag. | |
274 | */ | |
275 | static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, | |
276 | AVDictionary **metadata, const char *key) | |
277 | { | |
278 | uint8_t *dst; | |
279 | int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL; | |
280 | unsigned genre; | |
281 | ||
282 | if (taglen < 1) | |
283 | return; | |
284 | ||
285 | encoding = avio_r8(pb); | |
286 | taglen--; /* account for encoding type byte */ | |
287 | ||
288 | if (decode_str(s, pb, encoding, &dst, &taglen) < 0) { | |
289 | av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key); | |
290 | return; | |
291 | } | |
292 | ||
293 | if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) && | |
294 | (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) && | |
295 | genre <= ID3v1_GENRE_MAX) { | |
296 | av_freep(&dst); | |
297 | dst = av_strdup(ff_id3v1_genre_str[genre]); | |
298 | } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { | |
299 | /* dst now contains the key, need to get value */ | |
300 | key = dst; | |
301 | if (decode_str(s, pb, encoding, &dst, &taglen) < 0) { | |
302 | av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key); | |
303 | av_freep(&key); | |
304 | return; | |
305 | } | |
306 | dict_flags |= AV_DICT_DONT_STRDUP_KEY; | |
307 | } else if (!*dst) | |
308 | av_freep(&dst); | |
309 | ||
310 | if (dst) | |
311 | av_dict_set(metadata, key, dst, dict_flags); | |
312 | } | |
313 | ||
314 | /** | |
315 | * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct. | |
316 | */ | |
317 | static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, | |
318 | char *tag, ID3v2ExtraMeta **extra_meta, int isv34) | |
319 | { | |
320 | ID3v2ExtraMetaGEOB *geob_data = NULL; | |
321 | ID3v2ExtraMeta *new_extra = NULL; | |
322 | char encoding; | |
323 | unsigned int len; | |
324 | ||
325 | if (taglen < 1) | |
326 | return; | |
327 | ||
328 | geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB)); | |
329 | if (!geob_data) { | |
330 | av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", | |
331 | sizeof(ID3v2ExtraMetaGEOB)); | |
332 | return; | |
333 | } | |
334 | ||
335 | new_extra = av_mallocz(sizeof(ID3v2ExtraMeta)); | |
336 | if (!new_extra) { | |
337 | av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", | |
338 | sizeof(ID3v2ExtraMeta)); | |
339 | goto fail; | |
340 | } | |
341 | ||
342 | /* read encoding type byte */ | |
343 | encoding = avio_r8(pb); | |
344 | taglen--; | |
345 | ||
346 | /* read MIME type (always ISO-8859) */ | |
347 | if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, | |
348 | &taglen) < 0 || | |
349 | taglen <= 0) | |
350 | goto fail; | |
351 | ||
352 | /* read file name */ | |
353 | if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0 || | |
354 | taglen <= 0) | |
355 | goto fail; | |
356 | ||
357 | /* read content description */ | |
358 | if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0 || | |
359 | taglen < 0) | |
360 | goto fail; | |
361 | ||
362 | if (taglen) { | |
363 | /* save encapsulated binary data */ | |
364 | geob_data->data = av_malloc(taglen); | |
365 | if (!geob_data->data) { | |
366 | av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen); | |
367 | goto fail; | |
368 | } | |
369 | if ((len = avio_read(pb, geob_data->data, taglen)) < taglen) | |
370 | av_log(s, AV_LOG_WARNING, | |
371 | "Error reading GEOB frame, data truncated.\n"); | |
372 | geob_data->datasize = len; | |
373 | } else { | |
374 | geob_data->data = NULL; | |
375 | geob_data->datasize = 0; | |
376 | } | |
377 | ||
378 | /* add data to the list */ | |
379 | new_extra->tag = "GEOB"; | |
380 | new_extra->data = geob_data; | |
381 | new_extra->next = *extra_meta; | |
382 | *extra_meta = new_extra; | |
383 | ||
384 | return; | |
385 | ||
386 | fail: | |
387 | av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag); | |
388 | free_geobtag(geob_data); | |
389 | av_free(new_extra); | |
390 | return; | |
391 | } | |
392 | ||
393 | static int is_number(const char *str) | |
394 | { | |
395 | while (*str >= '0' && *str <= '9') | |
396 | str++; | |
397 | return !*str; | |
398 | } | |
399 | ||
400 | static AVDictionaryEntry *get_date_tag(AVDictionary *m, const char *tag) | |
401 | { | |
402 | AVDictionaryEntry *t; | |
403 | if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) && | |
404 | strlen(t->value) == 4 && is_number(t->value)) | |
405 | return t; | |
406 | return NULL; | |
407 | } | |
408 | ||
409 | static void merge_date(AVDictionary **m) | |
410 | { | |
411 | AVDictionaryEntry *t; | |
412 | char date[17] = { 0 }; // YYYY-MM-DD hh:mm | |
413 | ||
414 | if (!(t = get_date_tag(*m, "TYER")) && | |
415 | !(t = get_date_tag(*m, "TYE"))) | |
416 | return; | |
417 | av_strlcpy(date, t->value, 5); | |
418 | av_dict_set(m, "TYER", NULL, 0); | |
419 | av_dict_set(m, "TYE", NULL, 0); | |
420 | ||
421 | if (!(t = get_date_tag(*m, "TDAT")) && | |
422 | !(t = get_date_tag(*m, "TDA"))) | |
423 | goto finish; | |
424 | snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value); | |
425 | av_dict_set(m, "TDAT", NULL, 0); | |
426 | av_dict_set(m, "TDA", NULL, 0); | |
427 | ||
428 | if (!(t = get_date_tag(*m, "TIME")) && | |
429 | !(t = get_date_tag(*m, "TIM"))) | |
430 | goto finish; | |
431 | snprintf(date + 10, sizeof(date) - 10, | |
432 | " %.2s:%.2s", t->value, t->value + 2); | |
433 | av_dict_set(m, "TIME", NULL, 0); | |
434 | av_dict_set(m, "TIM", NULL, 0); | |
435 | ||
436 | finish: | |
437 | if (date[0]) | |
438 | av_dict_set(m, "date", date, 0); | |
439 | } | |
440 | ||
441 | static void free_apic(void *obj) | |
442 | { | |
443 | ID3v2ExtraMetaAPIC *apic = obj; | |
444 | av_buffer_unref(&apic->buf); | |
445 | av_freep(&apic->description); | |
446 | av_freep(&apic); | |
447 | } | |
448 | ||
449 | static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, | |
450 | char *tag, ID3v2ExtraMeta **extra_meta, int isv34) | |
451 | { | |
452 | int enc, pic_type; | |
453 | char mimetype[64]; | |
454 | const CodecMime *mime = ff_id3v2_mime_tags; | |
455 | enum AVCodecID id = AV_CODEC_ID_NONE; | |
456 | ID3v2ExtraMetaAPIC *apic = NULL; | |
457 | ID3v2ExtraMeta *new_extra = NULL; | |
458 | int64_t end = avio_tell(pb) + taglen; | |
459 | ||
460 | if (taglen <= 4) | |
461 | goto fail; | |
462 | ||
463 | new_extra = av_mallocz(sizeof(*new_extra)); | |
464 | apic = av_mallocz(sizeof(*apic)); | |
465 | if (!new_extra || !apic) | |
466 | goto fail; | |
467 | ||
468 | enc = avio_r8(pb); | |
469 | taglen--; | |
470 | ||
471 | /* mimetype */ | |
472 | if (isv34) { | |
473 | taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype)); | |
474 | } else { | |
475 | avio_read(pb, mimetype, 3); | |
476 | mimetype[3] = 0; | |
477 | } | |
478 | while (mime->id != AV_CODEC_ID_NONE) { | |
479 | if (!av_strncasecmp(mime->str, mimetype, sizeof(mimetype))) { | |
480 | id = mime->id; | |
481 | break; | |
482 | } | |
483 | mime++; | |
484 | } | |
485 | if (id == AV_CODEC_ID_NONE) { | |
486 | av_log(s, AV_LOG_WARNING, | |
487 | "Unknown attached picture mimetype: %s, skipping.\n", mimetype); | |
488 | goto fail; | |
489 | } | |
490 | apic->id = id; | |
491 | ||
492 | /* picture type */ | |
493 | pic_type = avio_r8(pb); | |
494 | taglen--; | |
495 | if (pic_type < 0 || pic_type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) { | |
496 | av_log(s, AV_LOG_WARNING, "Unknown attached picture type %d.\n", | |
497 | pic_type); | |
498 | pic_type = 0; | |
499 | } | |
500 | apic->type = ff_id3v2_picture_types[pic_type]; | |
501 | ||
502 | /* description and picture data */ | |
503 | if (decode_str(s, pb, enc, &apic->description, &taglen) < 0) { | |
504 | av_log(s, AV_LOG_ERROR, | |
505 | "Error decoding attached picture description.\n"); | |
506 | goto fail; | |
507 | } | |
508 | ||
509 | apic->buf = av_buffer_alloc(taglen + FF_INPUT_BUFFER_PADDING_SIZE); | |
510 | if (!apic->buf || !taglen || avio_read(pb, apic->buf->data, taglen) != taglen) | |
511 | goto fail; | |
512 | memset(apic->buf->data + taglen, 0, FF_INPUT_BUFFER_PADDING_SIZE); | |
513 | ||
514 | new_extra->tag = "APIC"; | |
515 | new_extra->data = apic; | |
516 | new_extra->next = *extra_meta; | |
517 | *extra_meta = new_extra; | |
518 | ||
519 | return; | |
520 | ||
521 | fail: | |
522 | if (apic) | |
523 | free_apic(apic); | |
524 | av_freep(&new_extra); | |
525 | avio_seek(pb, end, SEEK_SET); | |
526 | } | |
527 | ||
528 | static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, char *ttag, ID3v2ExtraMeta **extra_meta, int isv34) | |
529 | { | |
530 | AVRational time_base = {1, 1000}; | |
531 | uint32_t start, end; | |
532 | AVChapter *chapter; | |
533 | uint8_t *dst = NULL; | |
534 | int taglen; | |
535 | char tag[5]; | |
536 | ||
537 | if (!s) { | |
538 | /* We should probably just put the chapter data to extra_meta here | |
539 | * and do the AVFormatContext-needing part in a separate | |
540 | * ff_id3v2_parse_apic()-like function. */ | |
541 | av_log(NULL, AV_LOG_DEBUG, "No AVFormatContext, skipped ID3 chapter data\n"); | |
542 | return; | |
543 | } | |
544 | ||
545 | if (decode_str(s, pb, 0, &dst, &len) < 0) | |
546 | return; | |
547 | if (len < 16) | |
548 | return; | |
549 | ||
550 | start = avio_rb32(pb); | |
551 | end = avio_rb32(pb); | |
552 | avio_skip(pb, 8); | |
553 | ||
554 | chapter = avpriv_new_chapter(s, s->nb_chapters + 1, time_base, start, end, dst); | |
555 | if (!chapter) { | |
556 | av_free(dst); | |
557 | return; | |
558 | } | |
559 | ||
560 | len -= 16; | |
561 | while (len > 10) { | |
562 | if (avio_read(pb, tag, 4) < 4) | |
563 | goto end; | |
564 | tag[4] = 0; | |
565 | taglen = avio_rb32(pb); | |
566 | avio_skip(pb, 2); | |
567 | len -= 10; | |
568 | if (taglen < 0 || taglen > len) | |
569 | goto end; | |
570 | if (tag[0] == 'T') | |
571 | read_ttag(s, pb, taglen, &chapter->metadata, tag); | |
572 | else | |
573 | avio_skip(pb, taglen); | |
574 | len -= taglen; | |
575 | } | |
576 | ||
577 | ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_34_metadata_conv); | |
578 | ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_4_metadata_conv); | |
579 | end: | |
580 | av_free(dst); | |
581 | } | |
582 | ||
583 | static void free_priv(void *obj) | |
584 | { | |
585 | ID3v2ExtraMetaPRIV *priv = obj; | |
586 | av_freep(&priv->owner); | |
587 | av_freep(&priv->data); | |
588 | av_freep(&priv); | |
589 | } | |
590 | ||
591 | static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, | |
592 | char *tag, ID3v2ExtraMeta **extra_meta, int isv34) | |
593 | { | |
594 | ID3v2ExtraMeta *meta; | |
595 | ID3v2ExtraMetaPRIV *priv; | |
596 | ||
597 | meta = av_mallocz(sizeof(*meta)); | |
598 | priv = av_mallocz(sizeof(*priv)); | |
599 | ||
600 | if (!meta || !priv) | |
601 | goto fail; | |
602 | ||
603 | if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0) | |
604 | goto fail; | |
605 | ||
606 | priv->data = av_malloc(taglen); | |
607 | if (!priv->data) | |
608 | goto fail; | |
609 | ||
610 | priv->datasize = taglen; | |
611 | ||
612 | if (avio_read(pb, priv->data, priv->datasize) != priv->datasize) | |
613 | goto fail; | |
614 | ||
615 | meta->tag = "PRIV"; | |
616 | meta->data = priv; | |
617 | meta->next = *extra_meta; | |
618 | *extra_meta = meta; | |
619 | ||
620 | return; | |
621 | ||
622 | fail: | |
623 | if (priv) | |
624 | free_priv(priv); | |
625 | av_freep(&meta); | |
626 | } | |
627 | ||
628 | typedef struct ID3v2EMFunc { | |
629 | const char *tag3; | |
630 | const char *tag4; | |
631 | void (*read)(AVFormatContext *, AVIOContext *, int, char *, | |
632 | ID3v2ExtraMeta **, int isv34); | |
633 | void (*free)(void *obj); | |
634 | } ID3v2EMFunc; | |
635 | ||
636 | static const ID3v2EMFunc id3v2_extra_meta_funcs[] = { | |
637 | { "GEO", "GEOB", read_geobtag, free_geobtag }, | |
638 | { "PIC", "APIC", read_apic, free_apic }, | |
639 | { "CHAP","CHAP", read_chapter, NULL }, | |
640 | { "PRIV","PRIV", read_priv, free_priv }, | |
641 | { NULL } | |
642 | }; | |
643 | ||
644 | /** | |
645 | * Get the corresponding ID3v2EMFunc struct for a tag. | |
646 | * @param isv34 Determines if v2.2 or v2.3/4 strings are used | |
647 | * @return A pointer to the ID3v2EMFunc struct if found, NULL otherwise. | |
648 | */ | |
649 | static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) | |
650 | { | |
651 | int i = 0; | |
652 | while (id3v2_extra_meta_funcs[i].tag3) { | |
653 | if (tag && !memcmp(tag, | |
654 | (isv34 ? id3v2_extra_meta_funcs[i].tag4 : | |
655 | id3v2_extra_meta_funcs[i].tag3), | |
656 | (isv34 ? 4 : 3))) | |
657 | return &id3v2_extra_meta_funcs[i]; | |
658 | i++; | |
659 | } | |
660 | return NULL; | |
661 | } | |
662 | ||
663 | static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, | |
664 | AVFormatContext *s, int len, uint8_t version, | |
665 | uint8_t flags, ID3v2ExtraMeta **extra_meta) | |
666 | { | |
667 | int isv34, unsync; | |
668 | unsigned tlen; | |
669 | char tag[5]; | |
670 | int64_t next, end = avio_tell(pb) + len; | |
671 | int taghdrlen; | |
672 | const char *reason = NULL; | |
673 | AVIOContext pb_local; | |
674 | AVIOContext *pbx; | |
675 | unsigned char *buffer = NULL; | |
676 | int buffer_size = 0; | |
677 | const ID3v2EMFunc *extra_func = NULL; | |
678 | unsigned char *uncompressed_buffer = NULL; | |
679 | av_unused int uncompressed_buffer_size = 0; | |
680 | ||
681 | av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len); | |
682 | ||
683 | switch (version) { | |
684 | case 2: | |
685 | if (flags & 0x40) { | |
686 | reason = "compression"; | |
687 | goto error; | |
688 | } | |
689 | isv34 = 0; | |
690 | taghdrlen = 6; | |
691 | break; | |
692 | ||
693 | case 3: | |
694 | case 4: | |
695 | isv34 = 1; | |
696 | taghdrlen = 10; | |
697 | break; | |
698 | ||
699 | default: | |
700 | reason = "version"; | |
701 | goto error; | |
702 | } | |
703 | ||
704 | unsync = flags & 0x80; | |
705 | ||
706 | if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */ | |
707 | int extlen = get_size(pb, 4); | |
708 | if (version == 4) | |
709 | /* In v2.4 the length includes the length field we just read. */ | |
710 | extlen -= 4; | |
711 | ||
712 | if (extlen < 0) { | |
713 | reason = "invalid extended header length"; | |
714 | goto error; | |
715 | } | |
716 | avio_skip(pb, extlen); | |
717 | len -= extlen + 4; | |
718 | if (len < 0) { | |
719 | reason = "extended header too long."; | |
720 | goto error; | |
721 | } | |
722 | } | |
723 | ||
724 | while (len >= taghdrlen) { | |
725 | unsigned int tflags = 0; | |
726 | int tunsync = 0; | |
727 | int tcomp = 0; | |
728 | int tencr = 0; | |
729 | unsigned long dlen; | |
730 | ||
731 | if (isv34) { | |
732 | if (avio_read(pb, tag, 4) < 4) | |
733 | break; | |
734 | tag[4] = 0; | |
735 | if (version == 3) { | |
736 | tlen = avio_rb32(pb); | |
737 | } else | |
738 | tlen = get_size(pb, 4); | |
739 | tflags = avio_rb16(pb); | |
740 | tunsync = tflags & ID3v2_FLAG_UNSYNCH; | |
741 | } else { | |
742 | if (avio_read(pb, tag, 3) < 3) | |
743 | break; | |
744 | tag[3] = 0; | |
745 | tlen = avio_rb24(pb); | |
746 | } | |
747 | if (tlen > (1<<28)) | |
748 | break; | |
749 | len -= taghdrlen + tlen; | |
750 | ||
751 | if (len < 0) | |
752 | break; | |
753 | ||
754 | next = avio_tell(pb) + tlen; | |
755 | ||
756 | if (!tlen) { | |
757 | if (tag[0]) | |
758 | av_log(s, AV_LOG_DEBUG, "Invalid empty frame %s, skipping.\n", | |
759 | tag); | |
760 | continue; | |
761 | } | |
762 | ||
763 | if (tflags & ID3v2_FLAG_DATALEN) { | |
764 | if (tlen < 4) | |
765 | break; | |
766 | dlen = avio_rb32(pb); | |
767 | tlen -= 4; | |
768 | } else | |
769 | dlen = tlen; | |
770 | ||
771 | tcomp = tflags & ID3v2_FLAG_COMPRESSION; | |
772 | tencr = tflags & ID3v2_FLAG_ENCRYPTION; | |
773 | ||
774 | /* skip encrypted tags and, if no zlib, compressed tags */ | |
775 | if (tencr || (!CONFIG_ZLIB && tcomp)) { | |
776 | const char *type; | |
777 | if (!tcomp) | |
778 | type = "encrypted"; | |
779 | else if (!tencr) | |
780 | type = "compressed"; | |
781 | else | |
782 | type = "encrypted and compressed"; | |
783 | ||
784 | av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag); | |
785 | avio_skip(pb, tlen); | |
786 | /* check for text tag or supported special meta tag */ | |
787 | } else if (tag[0] == 'T' || | |
788 | (extra_meta && | |
789 | (extra_func = get_extra_meta_func(tag, isv34)))) { | |
790 | pbx = pb; | |
791 | ||
792 | if (unsync || tunsync || tcomp) { | |
793 | av_fast_malloc(&buffer, &buffer_size, tlen); | |
794 | if (!buffer) { | |
795 | av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen); | |
796 | goto seek; | |
797 | } | |
798 | } | |
799 | if (unsync || tunsync) { | |
800 | int64_t end = avio_tell(pb) + tlen; | |
801 | uint8_t *b; | |
802 | ||
803 | b = buffer; | |
804 | while (avio_tell(pb) < end && b - buffer < tlen && !pb->eof_reached) { | |
805 | *b++ = avio_r8(pb); | |
806 | if (*(b - 1) == 0xff && avio_tell(pb) < end - 1 && | |
807 | b - buffer < tlen && | |
808 | !pb->eof_reached ) { | |
809 | uint8_t val = avio_r8(pb); | |
810 | *b++ = val ? val : avio_r8(pb); | |
811 | } | |
812 | } | |
813 | ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL, | |
814 | NULL); | |
815 | tlen = b - buffer; | |
816 | pbx = &pb_local; // read from sync buffer | |
817 | } | |
818 | ||
819 | #if CONFIG_ZLIB | |
820 | if (tcomp) { | |
821 | int err; | |
822 | ||
823 | av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen); | |
824 | ||
825 | av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen); | |
826 | if (!uncompressed_buffer) { | |
827 | av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen); | |
828 | goto seek; | |
829 | } | |
830 | ||
831 | if (!(unsync || tunsync)) { | |
832 | err = avio_read(pb, buffer, tlen); | |
833 | if (err < 0) { | |
834 | av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n"); | |
835 | goto seek; | |
836 | } | |
837 | tlen = err; | |
838 | } | |
839 | ||
840 | err = uncompress(uncompressed_buffer, &dlen, buffer, tlen); | |
841 | if (err != Z_OK) { | |
842 | av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err); | |
843 | goto seek; | |
844 | } | |
845 | ffio_init_context(&pb_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL); | |
846 | tlen = dlen; | |
847 | pbx = &pb_local; // read from sync buffer | |
848 | } | |
849 | #endif | |
850 | if (tag[0] == 'T') | |
851 | /* parse text tag */ | |
852 | read_ttag(s, pbx, tlen, metadata, tag); | |
853 | else | |
854 | /* parse special meta tag */ | |
855 | extra_func->read(s, pbx, tlen, tag, extra_meta, isv34); | |
856 | } else if (!tag[0]) { | |
857 | if (tag[1]) | |
858 | av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n"); | |
859 | avio_skip(pb, tlen); | |
860 | break; | |
861 | } | |
862 | /* Skip to end of tag */ | |
863 | seek: | |
864 | avio_seek(pb, next, SEEK_SET); | |
865 | } | |
866 | ||
867 | /* Footer preset, always 10 bytes, skip over it */ | |
868 | if (version == 4 && flags & 0x10) | |
869 | end += 10; | |
870 | ||
871 | error: | |
872 | if (reason) | |
873 | av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", | |
874 | version, reason); | |
875 | avio_seek(pb, end, SEEK_SET); | |
876 | av_free(buffer); | |
877 | av_free(uncompressed_buffer); | |
878 | return; | |
879 | } | |
880 | ||
881 | static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata, | |
882 | AVFormatContext *s, const char *magic, | |
883 | ID3v2ExtraMeta **extra_meta, int64_t max_search_size) | |
884 | { | |
885 | int len, ret; | |
886 | uint8_t buf[ID3v2_HEADER_SIZE]; | |
887 | int found_header; | |
888 | int64_t start, off; | |
889 | ||
890 | if (max_search_size && max_search_size < ID3v2_HEADER_SIZE) | |
891 | return; | |
892 | ||
893 | start = avio_tell(pb); | |
894 | do { | |
895 | /* save the current offset in case there's nothing to read/skip */ | |
896 | off = avio_tell(pb); | |
897 | if (max_search_size && off - start >= max_search_size - ID3v2_HEADER_SIZE) { | |
898 | avio_seek(pb, off, SEEK_SET); | |
899 | break; | |
900 | } | |
901 | ||
902 | ret = avio_read(pb, buf, ID3v2_HEADER_SIZE); | |
903 | if (ret != ID3v2_HEADER_SIZE) { | |
904 | avio_seek(pb, off, SEEK_SET); | |
905 | break; | |
906 | } | |
907 | found_header = ff_id3v2_match(buf, magic); | |
908 | if (found_header) { | |
909 | /* parse ID3v2 header */ | |
910 | len = ((buf[6] & 0x7f) << 21) | | |
911 | ((buf[7] & 0x7f) << 14) | | |
912 | ((buf[8] & 0x7f) << 7) | | |
913 | (buf[9] & 0x7f); | |
914 | id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta); | |
915 | } else { | |
916 | avio_seek(pb, off, SEEK_SET); | |
917 | } | |
918 | } while (found_header); | |
919 | ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv); | |
920 | ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv); | |
921 | ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv); | |
922 | merge_date(metadata); | |
923 | } | |
924 | ||
925 | void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, | |
926 | const char *magic, ID3v2ExtraMeta **extra_meta) | |
927 | { | |
928 | id3v2_read_internal(pb, metadata, NULL, magic, extra_meta, 0); | |
929 | } | |
930 | ||
931 | void ff_id3v2_read(AVFormatContext *s, const char *magic, | |
932 | ID3v2ExtraMeta **extra_meta, unsigned int max_search_size) | |
933 | { | |
934 | id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta, max_search_size); | |
935 | } | |
936 | ||
937 | void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) | |
938 | { | |
939 | ID3v2ExtraMeta *current = *extra_meta, *next; | |
940 | const ID3v2EMFunc *extra_func; | |
941 | ||
942 | while (current) { | |
943 | if ((extra_func = get_extra_meta_func(current->tag, 1))) | |
944 | extra_func->free(current->data); | |
945 | next = current->next; | |
946 | av_freep(¤t); | |
947 | current = next; | |
948 | } | |
949 | ||
950 | *extra_meta = NULL; | |
951 | } | |
952 | ||
953 | int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) | |
954 | { | |
955 | ID3v2ExtraMeta *cur; | |
956 | ||
957 | for (cur = *extra_meta; cur; cur = cur->next) { | |
958 | ID3v2ExtraMetaAPIC *apic; | |
959 | AVStream *st; | |
960 | ||
961 | if (strcmp(cur->tag, "APIC")) | |
962 | continue; | |
963 | apic = cur->data; | |
964 | ||
965 | if (!(st = avformat_new_stream(s, NULL))) | |
966 | return AVERROR(ENOMEM); | |
967 | ||
968 | st->disposition |= AV_DISPOSITION_ATTACHED_PIC; | |
969 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; | |
970 | st->codec->codec_id = apic->id; | |
971 | av_dict_set(&st->metadata, "title", apic->description, 0); | |
972 | av_dict_set(&st->metadata, "comment", apic->type, 0); | |
973 | ||
974 | av_init_packet(&st->attached_pic); | |
975 | st->attached_pic.buf = apic->buf; | |
976 | st->attached_pic.data = apic->buf->data; | |
977 | st->attached_pic.size = apic->buf->size - FF_INPUT_BUFFER_PADDING_SIZE; | |
978 | st->attached_pic.stream_index = st->index; | |
979 | st->attached_pic.flags |= AV_PKT_FLAG_KEY; | |
980 | ||
981 | apic->buf = NULL; | |
982 | } | |
983 | ||
984 | return 0; | |
985 | } |