avio: move init_put_byte() to a new private header and rename it
[ffmpeg.git] / libavformat / id3v2.c
1 /*
2  * ID3v2 header parser
3  * Copyright (c) 2003 Fabrice Bellard
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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) + (get_byte(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*) = get_be16;
69
70     dst[0] = 0;
71     if (taglen < 1)
72         return;
73
74     taglen--; /* account for encoding type byte */
75
76     switch (get_byte(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(get_byte(pb), tmp, *q++ = tmp;)
83         }
84         *q = 0;
85         break;
86
87     case ID3v2_ENCODING_UTF16BOM:
88         taglen -= 2;
89         switch (get_be16(pb)) {
90         case 0xfffe:
91             get = get_le16;
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         get_buffer(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 void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
140 {
141     int isv34, tlen, unsync;
142     char tag[5];
143     int64_t next;
144     int taghdrlen;
145     const char *reason;
146     AVIOContext pb;
147     unsigned char *buffer = NULL;
148     int buffer_size = 0;
149
150     switch (version) {
151     case 2:
152         if (flags & 0x40) {
153             reason = "compression";
154             goto error;
155         }
156         isv34 = 0;
157         taghdrlen = 6;
158         break;
159
160     case 3:
161     case 4:
162         isv34 = 1;
163         taghdrlen = 10;
164         break;
165
166     default:
167         reason = "version";
168         goto error;
169     }
170
171     unsync = flags & 0x80;
172
173     if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
174         url_fskip(s->pb, get_size(s->pb, 4));
175
176     while (len >= taghdrlen) {
177         unsigned int tflags;
178         int tunsync = 0;
179
180         if (isv34) {
181             get_buffer(s->pb, tag, 4);
182             tag[4] = 0;
183             if(version==3){
184                 tlen = get_be32(s->pb);
185             }else
186                 tlen = get_size(s->pb, 4);
187             tflags = get_be16(s->pb);
188             tunsync = tflags & ID3v2_FLAG_UNSYNCH;
189         } else {
190             get_buffer(s->pb, tag, 3);
191             tag[3] = 0;
192             tlen = get_be24(s->pb);
193         }
194         len -= taghdrlen + tlen;
195
196         if (len < 0)
197             break;
198
199         next = url_ftell(s->pb) + tlen;
200
201         if (tflags & ID3v2_FLAG_DATALEN) {
202             get_be32(s->pb);
203             tlen -= 4;
204         }
205
206         if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
207             av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
208             url_fskip(s->pb, tlen);
209         } else if (tag[0] == 'T') {
210             if (unsync || tunsync) {
211                 int i, j;
212                 av_fast_malloc(&buffer, &buffer_size, tlen);
213                 for (i = 0, j = 0; i < tlen; i++, j++) {
214                     buffer[j] = get_byte(s->pb);
215                     if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
216                         /* Unsynchronised byte, skip it */
217                         j--;
218                     }
219                 }
220                 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
221                 read_ttag(s, &pb, j, tag);
222             } else {
223                 read_ttag(s, s->pb, tlen, tag);
224             }
225         }
226         else if (!tag[0]) {
227             if (tag[1])
228                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
229             url_fskip(s->pb, tlen);
230             break;
231         }
232         /* Skip to end of tag */
233         url_fseek(s->pb, next, SEEK_SET);
234     }
235
236     if (len > 0) {
237         /* Skip padding */
238         url_fskip(s->pb, len);
239     }
240     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
241         url_fskip(s->pb, 10);
242
243     av_free(buffer);
244     return;
245
246   error:
247     av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
248     url_fskip(s->pb, len);
249     av_free(buffer);
250 }
251
252 void ff_id3v2_read(AVFormatContext *s, const char *magic)
253 {
254     int len, ret;
255     uint8_t buf[ID3v2_HEADER_SIZE];
256     int     found_header;
257     int64_t off;
258
259     do {
260         /* save the current offset in case there's nothing to read/skip */
261         off = url_ftell(s->pb);
262         ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
263         if (ret != ID3v2_HEADER_SIZE)
264             break;
265             found_header = ff_id3v2_match(buf, magic);
266             if (found_header) {
267             /* parse ID3v2 header */
268             len = ((buf[6] & 0x7f) << 21) |
269                   ((buf[7] & 0x7f) << 14) |
270                   ((buf[8] & 0x7f) << 7) |
271                    (buf[9] & 0x7f);
272             ff_id3v2_parse(s, len, buf[3], buf[5]);
273         } else {
274             url_fseek(s->pb, off, SEEK_SET);
275         }
276     } while (found_header);
277     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
278     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
279     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
280 }
281
282 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
283     { "TALB", "album"},
284     { "TCOM", "composer"},
285     { "TCON", "genre"},
286     { "TCOP", "copyright"},
287     { "TENC", "encoded_by"},
288     { "TIT2", "title"},
289     { "TLAN", "language"},
290     { "TPE1", "artist"},
291     { "TPE2", "album_artist"},
292     { "TPE3", "performer"},
293     { "TPOS", "disc"},
294     { "TPUB", "publisher"},
295     { "TRCK", "track"},
296     { "TSSE", "encoder"},
297     { 0 }
298 };
299
300 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
301     { "TDRL", "date"},
302     { "TDRC", "date"},
303     { "TDEN", "creation_time"},
304     { "TSOA", "album-sort"},
305     { "TSOP", "artist-sort"},
306     { "TSOT", "title-sort"},
307     { 0 }
308 };
309
310 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
311     { "TAL",  "album"},
312     { "TCO",  "genre"},
313     { "TT2",  "title"},
314     { "TEN",  "encoded_by"},
315     { "TP1",  "artist"},
316     { "TP2",  "album_artist"},
317     { "TP3",  "performer"},
318     { "TRK",  "track"},
319     { 0 }
320 };
321
322
323 const char ff_id3v2_tags[][4] = {
324    "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
325    "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
326    "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
327    "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
328    { 0 },
329 };
330
331 const char ff_id3v2_4_tags[][4] = {
332    "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
333    "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
334    { 0 },
335 };
336
337 const char ff_id3v2_3_tags[][4] = {
338    "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
339    { 0 },
340 };