2 * Copyright (c) 2003 Fabrice Bellard
4 * This file is part of FFmpeg.
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.
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.
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
25 * Specifications available at:
26 * http://id3.org/Developer_Information
31 #include "libavutil/avstring.h"
32 #include "libavutil/intreadwrite.h"
33 #include "libavutil/dict.h"
34 #include "avio_internal.h"
36 int ff_id3v2_match(const uint8_t *buf, const char * magic)
38 return buf[0] == magic[0] &&
43 (buf[6] & 0x80) == 0 &&
44 (buf[7] & 0x80) == 0 &&
45 (buf[8] & 0x80) == 0 &&
49 int ff_id3v2_tag_len(const uint8_t * buf)
51 int len = ((buf[6] & 0x7f) << 21) +
52 ((buf[7] & 0x7f) << 14) +
53 ((buf[8] & 0x7f) << 7) +
57 len += ID3v2_HEADER_SIZE;
61 static unsigned int get_size(AVIOContext *s, int len)
65 v = (v << 7) + (avio_r8(s) & 0x7F);
69 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
72 const char *val = NULL;
73 int len, dstlen = sizeof(dst) - 1;
75 unsigned int (*get)(AVIOContext*) = avio_rb16;
81 taglen--; /* account for encoding type byte */
83 switch (avio_r8(pb)) { /* encoding type */
85 case ID3v2_ENCODING_ISO8859:
87 while (taglen-- && q - dst < dstlen - 7) {
89 PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
94 case ID3v2_ENCODING_UTF16BOM:
96 switch (avio_rb16(pb)) {
102 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
107 case ID3v2_ENCODING_UTF16BE:
109 while (taglen > 1 && q - dst < dstlen - 7) {
113 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
114 PUT_UTF8(ch, tmp, *q++ = tmp;)
119 case ID3v2_ENCODING_UTF8:
120 len = FFMIN(taglen, dstlen);
121 avio_read(pb, dst, len);
125 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
128 if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
129 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
130 && genre <= ID3v1_GENRE_MAX)
131 val = ff_id3v1_genre_str[genre];
132 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
133 /* dst now contains two 0-terminated strings */
137 val = dst + FFMIN(len + 1, dstlen);
143 av_dict_set(&s->metadata, key, val, AV_DICT_DONT_OVERWRITE);
146 static int is_number(const char *str)
148 while (*str >= '0' && *str <= '9') str++;
152 static AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag)
154 AVDictionaryEntry *t;
155 if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
156 strlen(t->value) == 4 && is_number(t->value))
161 static void merge_date(AVDictionary **m)
163 AVDictionaryEntry *t;
164 char date[17] = {0}; // YYYY-MM-DD hh:mm
166 if (!(t = get_date_tag(*m, "TYER")) &&
167 !(t = get_date_tag(*m, "TYE")))
169 av_strlcpy(date, t->value, 5);
170 av_dict_set(m, "TYER", NULL, 0);
171 av_dict_set(m, "TYE", NULL, 0);
173 if (!(t = get_date_tag(*m, "TDAT")) &&
174 !(t = get_date_tag(*m, "TDA")))
176 snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
177 av_dict_set(m, "TDAT", NULL, 0);
178 av_dict_set(m, "TDA", NULL, 0);
180 if (!(t = get_date_tag(*m, "TIME")) &&
181 !(t = get_date_tag(*m, "TIM")))
183 snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
184 av_dict_set(m, "TIME", NULL, 0);
185 av_dict_set(m, "TIM", NULL, 0);
189 av_dict_set(m, "date", date, 0);
192 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
197 int64_t next, end = avio_tell(s->pb) + len;
199 const char *reason = NULL;
201 unsigned char *buffer = NULL;
207 reason = "compression";
225 unsync = flags & 0x80;
227 if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
228 avio_skip(s->pb, get_size(s->pb, 4));
230 while (len >= taghdrlen) {
231 unsigned int tflags = 0;
235 avio_read(s->pb, tag, 4);
238 tlen = avio_rb32(s->pb);
240 tlen = get_size(s->pb, 4);
241 tflags = avio_rb16(s->pb);
242 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
244 avio_read(s->pb, tag, 3);
246 tlen = avio_rb24(s->pb);
248 if (tlen > (1<<28) || !tlen)
250 len -= taghdrlen + tlen;
255 next = avio_tell(s->pb) + tlen;
257 if (tflags & ID3v2_FLAG_DATALEN) {
264 if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
265 av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
266 avio_skip(s->pb, tlen);
267 } else if (tag[0] == 'T') {
268 if (unsync || tunsync) {
270 av_fast_malloc(&buffer, &buffer_size, tlen);
272 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
275 for (i = 0, j = 0; i < tlen; i++, j++) {
276 buffer[j] = avio_r8(s->pb);
277 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
278 /* Unsynchronised byte, skip it */
282 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
283 read_ttag(s, &pb, j, tag);
285 read_ttag(s, s->pb, tlen, tag);
290 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
291 avio_skip(s->pb, tlen);
294 /* Skip to end of tag */
296 avio_seek(s->pb, next, SEEK_SET);
299 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
304 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
305 avio_seek(s->pb, end, SEEK_SET);
310 void ff_id3v2_read(AVFormatContext *s, const char *magic)
313 uint8_t buf[ID3v2_HEADER_SIZE];
318 /* save the current offset in case there's nothing to read/skip */
319 off = avio_tell(s->pb);
320 ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
321 if (ret != ID3v2_HEADER_SIZE)
323 found_header = ff_id3v2_match(buf, magic);
325 /* parse ID3v2 header */
326 len = ((buf[6] & 0x7f) << 21) |
327 ((buf[7] & 0x7f) << 14) |
328 ((buf[8] & 0x7f) << 7) |
330 ff_id3v2_parse(s, len, buf[3], buf[5]);
332 avio_seek(s->pb, off, SEEK_SET);
334 } while (found_header);
335 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
336 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
337 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
338 merge_date(&s->metadata);
341 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
343 { "TCOM", "composer"},
345 { "TCOP", "copyright"},
346 { "TENC", "encoded_by"},
348 { "TLAN", "language"},
350 { "TPE2", "album_artist"},
351 { "TPE3", "performer"},
353 { "TPUB", "publisher"},
355 { "TSSE", "encoder"},
359 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
362 { "TDEN", "creation_time"},
363 { "TSOA", "album-sort"},
364 { "TSOP", "artist-sort"},
365 { "TSOT", "title-sort"},
369 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
373 { "TEN", "encoded_by"},
375 { "TP2", "album_artist"},
376 { "TP3", "performer"},
382 const char ff_id3v2_tags[][4] = {
383 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
384 "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
385 "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
386 "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
390 const char ff_id3v2_4_tags[][4] = {
391 "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
392 "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
396 const char ff_id3v2_3_tags[][4] = {
397 "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",