id3v2: add @file doxy and link to format documentation
[ffmpeg.git] / libavformat / id3v2.c
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 "id3v2.h"
30 #include "id3v1.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/intreadwrite.h"
33 #include "metadata.h"
34 #include "avio_internal.h"
35
36 int ff_id3v2_match(const uint8_t *buf, const char * magic)
37 {
38     return  buf[0]         == magic[0] &&
39             buf[1]         == magic[1] &&
40             buf[2]         == magic[2] &&
41             buf[3]         != 0xff &&
42             buf[4]         != 0xff &&
43            (buf[6] & 0x80) ==    0 &&
44            (buf[7] & 0x80) ==    0 &&
45            (buf[8] & 0x80) ==    0 &&
46            (buf[9] & 0x80) ==    0;
47 }
48
49 int ff_id3v2_tag_len(const uint8_t * buf)
50 {
51     int len = ((buf[6] & 0x7f) << 21) +
52               ((buf[7] & 0x7f) << 14) +
53               ((buf[8] & 0x7f) << 7) +
54                (buf[9] & 0x7f) +
55               ID3v2_HEADER_SIZE;
56     if (buf[5] & 0x10)
57         len += ID3v2_HEADER_SIZE;
58     return len;
59 }
60
61 static unsigned int get_size(AVIOContext *s, int len)
62 {
63     int v = 0;
64     while (len--)
65         v = (v << 7) + (avio_r8(s) & 0x7F);
66     return v;
67 }
68
69 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
70 {
71     char *q, dst[512];
72     const char *val = NULL;
73     int len, dstlen = sizeof(dst) - 1;
74     unsigned genre;
75     unsigned int (*get)(AVIOContext*) = avio_rb16;
76
77     dst[0] = 0;
78     if (taglen < 1)
79         return;
80
81     taglen--; /* account for encoding type byte */
82
83     switch (avio_r8(pb)) { /* encoding type */
84
85     case ID3v2_ENCODING_ISO8859:
86         q = dst;
87         while (taglen-- && q - dst < dstlen - 7) {
88             uint8_t tmp;
89             PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
90         }
91         *q = 0;
92         break;
93
94     case ID3v2_ENCODING_UTF16BOM:
95         taglen -= 2;
96         switch (avio_rb16(pb)) {
97         case 0xfffe:
98             get = avio_rl16;
99         case 0xfeff:
100             break;
101         default:
102             av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
103             return;
104         }
105         // fall-through
106
107     case ID3v2_ENCODING_UTF16BE:
108         q = dst;
109         while (taglen > 1 && q - dst < dstlen - 7) {
110             uint32_t ch;
111             uint8_t tmp;
112
113             GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
114             PUT_UTF8(ch, tmp, *q++ = tmp;)
115         }
116         *q = 0;
117         break;
118
119     case ID3v2_ENCODING_UTF8:
120         len = FFMIN(taglen, dstlen);
121         avio_read(pb, dst, len);
122         dst[len] = 0;
123         break;
124     default:
125         av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
126     }
127
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 */
134         dst[dstlen] = 0;
135         len = strlen(dst);
136         key = dst;
137         val = dst + FFMIN(len + 1, dstlen);
138     }
139     else if (*dst)
140         val = dst;
141
142     if (val)
143         av_metadata_set2(&s->metadata, key, val, AV_METADATA_DONT_OVERWRITE);
144 }
145
146 static int is_number(const char *str)
147 {
148     while (*str >= '0' && *str <= '9') str++;
149     return !*str;
150 }
151
152 static AVMetadataTag* get_date_tag(AVMetadata *m, const char *tag)
153 {
154     AVMetadataTag *t;
155     if ((t = av_metadata_get(m, tag, NULL, AV_METADATA_MATCH_CASE)) &&
156         strlen(t->value) == 4 && is_number(t->value))
157         return t;
158     return NULL;
159 }
160
161 static void merge_date(AVMetadata **m)
162 {
163     AVMetadataTag *t;
164     char date[17] = {0};      // YYYY-MM-DD hh:mm
165
166     if (!(t = get_date_tag(*m, "TYER")) &&
167         !(t = get_date_tag(*m, "TYE")))
168         return;
169     av_strlcpy(date, t->value, 5);
170     av_metadata_set2(m, "TYER", NULL, 0);
171     av_metadata_set2(m, "TYE",  NULL, 0);
172
173     if (!(t = get_date_tag(*m, "TDAT")) &&
174         !(t = get_date_tag(*m, "TDA")))
175         goto finish;
176     snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
177     av_metadata_set2(m, "TDAT", NULL, 0);
178     av_metadata_set2(m, "TDA",  NULL, 0);
179
180     if (!(t = get_date_tag(*m, "TIME")) &&
181         !(t = get_date_tag(*m, "TIM")))
182         goto finish;
183     snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
184     av_metadata_set2(m, "TIME", NULL, 0);
185     av_metadata_set2(m, "TIM",  NULL, 0);
186
187 finish:
188     if (date[0])
189         av_metadata_set2(m, "date", date, 0);
190 }
191
192 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
193 {
194     int isv34, unsync;
195     unsigned tlen;
196     char tag[5];
197     int64_t next, end = avio_tell(s->pb) + len;
198     int taghdrlen;
199     const char *reason = NULL;
200     AVIOContext pb;
201     unsigned char *buffer = NULL;
202     int buffer_size = 0;
203
204     switch (version) {
205     case 2:
206         if (flags & 0x40) {
207             reason = "compression";
208             goto error;
209         }
210         isv34 = 0;
211         taghdrlen = 6;
212         break;
213
214     case 3:
215     case 4:
216         isv34 = 1;
217         taghdrlen = 10;
218         break;
219
220     default:
221         reason = "version";
222         goto error;
223     }
224
225     unsync = flags & 0x80;
226
227     if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
228         avio_skip(s->pb, get_size(s->pb, 4));
229
230     while (len >= taghdrlen) {
231         unsigned int tflags;
232         int tunsync = 0;
233
234         if (isv34) {
235             avio_read(s->pb, tag, 4);
236             tag[4] = 0;
237             if(version==3){
238                 tlen = avio_rb32(s->pb);
239             }else
240                 tlen = get_size(s->pb, 4);
241             tflags = avio_rb16(s->pb);
242             tunsync = tflags & ID3v2_FLAG_UNSYNCH;
243         } else {
244             avio_read(s->pb, tag, 3);
245             tag[3] = 0;
246             tlen = avio_rb24(s->pb);
247         }
248         if (tlen > (1<<28))
249             break;
250         len -= taghdrlen + tlen;
251
252         if (len < 0)
253             break;
254
255         next = avio_tell(s->pb) + tlen;
256
257         if (tflags & ID3v2_FLAG_DATALEN) {
258             avio_rb32(s->pb);
259             tlen -= 4;
260         }
261
262         if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
263             av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
264             avio_skip(s->pb, tlen);
265         } else if (tag[0] == 'T') {
266             if (unsync || tunsync) {
267                 int i, j;
268                 av_fast_malloc(&buffer, &buffer_size, tlen);
269                 for (i = 0, j = 0; i < tlen; i++, j++) {
270                     buffer[j] = avio_r8(s->pb);
271                     if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
272                         /* Unsynchronised byte, skip it */
273                         j--;
274                     }
275                 }
276                 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
277                 read_ttag(s, &pb, j, tag);
278             } else {
279                 read_ttag(s, s->pb, tlen, tag);
280             }
281         }
282         else if (!tag[0]) {
283             if (tag[1])
284                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
285             avio_skip(s->pb, tlen);
286             break;
287         }
288         /* Skip to end of tag */
289         avio_seek(s->pb, next, SEEK_SET);
290     }
291
292     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
293         end += 10;
294
295   error:
296     if (reason)
297         av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
298     avio_seek(s->pb, end, SEEK_SET);
299     av_free(buffer);
300     return;
301 }
302
303 void ff_id3v2_read(AVFormatContext *s, const char *magic)
304 {
305     int len, ret;
306     uint8_t buf[ID3v2_HEADER_SIZE];
307     int     found_header;
308     int64_t off;
309
310     do {
311         /* save the current offset in case there's nothing to read/skip */
312         off = avio_tell(s->pb);
313         ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
314         if (ret != ID3v2_HEADER_SIZE)
315             break;
316             found_header = ff_id3v2_match(buf, magic);
317             if (found_header) {
318             /* parse ID3v2 header */
319             len = ((buf[6] & 0x7f) << 21) |
320                   ((buf[7] & 0x7f) << 14) |
321                   ((buf[8] & 0x7f) << 7) |
322                    (buf[9] & 0x7f);
323             ff_id3v2_parse(s, len, buf[3], buf[5]);
324         } else {
325             avio_seek(s->pb, off, SEEK_SET);
326         }
327     } while (found_header);
328     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
329     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
330     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
331     merge_date(&s->metadata);
332 }
333
334 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
335     { "TALB", "album"},
336     { "TCOM", "composer"},
337     { "TCON", "genre"},
338     { "TCOP", "copyright"},
339     { "TENC", "encoded_by"},
340     { "TIT2", "title"},
341     { "TLAN", "language"},
342     { "TPE1", "artist"},
343     { "TPE2", "album_artist"},
344     { "TPE3", "performer"},
345     { "TPOS", "disc"},
346     { "TPUB", "publisher"},
347     { "TRCK", "track"},
348     { "TSSE", "encoder"},
349     { 0 }
350 };
351
352 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
353     { "TDRL", "date"},
354     { "TDRC", "date"},
355     { "TDEN", "creation_time"},
356     { "TSOA", "album-sort"},
357     { "TSOP", "artist-sort"},
358     { "TSOT", "title-sort"},
359     { 0 }
360 };
361
362 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
363     { "TAL",  "album"},
364     { "TCO",  "genre"},
365     { "TT2",  "title"},
366     { "TEN",  "encoded_by"},
367     { "TP1",  "artist"},
368     { "TP2",  "album_artist"},
369     { "TP3",  "performer"},
370     { "TRK",  "track"},
371     { 0 }
372 };
373
374
375 const char ff_id3v2_tags[][4] = {
376    "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
377    "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
378    "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
379    "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
380    { 0 },
381 };
382
383 const char ff_id3v2_4_tags[][4] = {
384    "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
385    "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
386    { 0 },
387 };
388
389 const char ff_id3v2_3_tags[][4] = {
390    "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
391    { 0 },
392 };