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