Merge remote-tracking branch 'qatar/master'
[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 "libavutil/dict.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_dict_set(&s->metadata, key, val, AV_DICT_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 AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag)
153 {
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))
157         return t;
158     return NULL;
159 }
160
161 static void merge_date(AVDictionary **m)
162 {
163     AVDictionaryEntry *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_dict_set(m, "TYER", NULL, 0);
171     av_dict_set(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_dict_set(m, "TDAT", NULL, 0);
178     av_dict_set(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_dict_set(m, "TIME", NULL, 0);
185     av_dict_set(m, "TIM",  NULL, 0);
186
187 finish:
188     if (date[0])
189         av_dict_set(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 = 0;
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) || !tlen)
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             if (tlen < 4)
259                 break;
260             avio_rb32(s->pb);
261             tlen -= 4;
262         }
263
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) {
269                 int i, j;
270                 av_fast_malloc(&buffer, &buffer_size, tlen);
271                 if (!buffer) {
272                     av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
273                     goto seek;
274                 }
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 */
279                         j--;
280                     }
281                 }
282                 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
283                 read_ttag(s, &pb, j, tag);
284             } else {
285                 read_ttag(s, s->pb, tlen, tag);
286             }
287         }
288         else if (!tag[0]) {
289             if (tag[1])
290                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
291             avio_skip(s->pb, tlen);
292             break;
293         }
294         /* Skip to end of tag */
295 seek:
296         avio_seek(s->pb, next, SEEK_SET);
297     }
298
299     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
300         end += 10;
301
302   error:
303     if (reason)
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);
306     av_free(buffer);
307     return;
308 }
309
310 void ff_id3v2_read(AVFormatContext *s, const char *magic)
311 {
312     int len, ret;
313     uint8_t buf[ID3v2_HEADER_SIZE];
314     int     found_header;
315     int64_t off;
316
317     do {
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)
322             break;
323             found_header = ff_id3v2_match(buf, magic);
324             if (found_header) {
325             /* parse ID3v2 header */
326             len = ((buf[6] & 0x7f) << 21) |
327                   ((buf[7] & 0x7f) << 14) |
328                   ((buf[8] & 0x7f) << 7) |
329                    (buf[9] & 0x7f);
330             ff_id3v2_parse(s, len, buf[3], buf[5]);
331         } else {
332             avio_seek(s->pb, off, SEEK_SET);
333         }
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);
339 }
340
341 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
342     { "TALB", "album"},
343     { "TCOM", "composer"},
344     { "TCON", "genre"},
345     { "TCOP", "copyright"},
346     { "TENC", "encoded_by"},
347     { "TIT2", "title"},
348     { "TLAN", "language"},
349     { "TPE1", "artist"},
350     { "TPE2", "album_artist"},
351     { "TPE3", "performer"},
352     { "TPOS", "disc"},
353     { "TPUB", "publisher"},
354     { "TRCK", "track"},
355     { "TSSE", "encoder"},
356     { 0 }
357 };
358
359 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
360     { "TDRL", "date"},
361     { "TDRC", "date"},
362     { "TDEN", "creation_time"},
363     { "TSOA", "album-sort"},
364     { "TSOP", "artist-sort"},
365     { "TSOT", "title-sort"},
366     { 0 }
367 };
368
369 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
370     { "TAL",  "album"},
371     { "TCO",  "genre"},
372     { "TT2",  "title"},
373     { "TEN",  "encoded_by"},
374     { "TP1",  "artist"},
375     { "TP2",  "album_artist"},
376     { "TP3",  "performer"},
377     { "TRK",  "track"},
378     { 0 }
379 };
380
381
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",
387    { 0 },
388 };
389
390 const char ff_id3v2_4_tags[][4] = {
391    "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
392    "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
393    { 0 },
394 };
395
396 const char ff_id3v2_3_tags[][4] = {
397    "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
398    { 0 },
399 };