add ff_ prefix to metadata_conv()
[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
28 int ff_id3v2_match(const uint8_t *buf, const char * magic)
29 {
30     return  buf[0]         == magic[0] &&
31             buf[1]         == magic[1] &&
32             buf[2]         == magic[2] &&
33             buf[3]         != 0xff &&
34             buf[4]         != 0xff &&
35            (buf[6] & 0x80) ==    0 &&
36            (buf[7] & 0x80) ==    0 &&
37            (buf[8] & 0x80) ==    0 &&
38            (buf[9] & 0x80) ==    0;
39 }
40
41 int ff_id3v2_tag_len(const uint8_t * buf)
42 {
43     int len = ((buf[6] & 0x7f) << 21) +
44               ((buf[7] & 0x7f) << 14) +
45               ((buf[8] & 0x7f) << 7) +
46                (buf[9] & 0x7f) +
47               ID3v2_HEADER_SIZE;
48     if (buf[5] & 0x10)
49         len += ID3v2_HEADER_SIZE;
50     return len;
51 }
52
53 void ff_id3v2_read(AVFormatContext *s, const char *magic)
54 {
55     int len, ret;
56     uint8_t buf[ID3v2_HEADER_SIZE];
57     int     found_header;
58     int64_t off;
59
60     do {
61         /* save the current offset in case there's nothing to read/skip */
62         off = url_ftell(s->pb);
63         ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
64         if (ret != ID3v2_HEADER_SIZE)
65             return;
66             found_header = ff_id3v2_match(buf, magic);
67             if (found_header) {
68             /* parse ID3v2 header */
69             len = ((buf[6] & 0x7f) << 21) |
70                   ((buf[7] & 0x7f) << 14) |
71                   ((buf[8] & 0x7f) << 7) |
72                    (buf[9] & 0x7f);
73             ff_id3v2_parse(s, len, buf[3], buf[5]);
74         } else {
75             url_fseek(s->pb, off, SEEK_SET);
76         }
77     } while (found_header);
78 }
79
80 static unsigned int get_size(ByteIOContext *s, int len)
81 {
82     int v = 0;
83     while (len--)
84         v = (v << 7) + (get_byte(s) & 0x7F);
85     return v;
86 }
87
88 static void read_ttag(AVFormatContext *s, ByteIOContext *pb, int taglen, const char *key)
89 {
90     char *q, dst[512];
91     const char *val = NULL;
92     int len, dstlen = sizeof(dst) - 1;
93     unsigned genre;
94     unsigned int (*get)(ByteIOContext*) = get_be16;
95
96     dst[0] = 0;
97     if (taglen < 1)
98         return;
99
100     taglen--; /* account for encoding type byte */
101
102     switch (get_byte(pb)) { /* encoding type */
103
104     case 0:  /* ISO-8859-1 (0 - 255 maps directly into unicode) */
105         q = dst;
106         while (taglen-- && q - dst < dstlen - 7) {
107             uint8_t tmp;
108             PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;)
109         }
110         *q = 0;
111         break;
112
113     case 1:  /* UTF-16 with BOM */
114         taglen -= 2;
115         switch (get_be16(pb)) {
116         case 0xfffe:
117             get = get_le16;
118         case 0xfeff:
119             break;
120         default:
121             av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
122             return;
123         }
124         // fall-through
125
126     case 2:  /* UTF-16BE without BOM */
127         q = dst;
128         while (taglen > 1 && q - dst < dstlen - 7) {
129             uint32_t ch;
130             uint8_t tmp;
131
132             GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
133             PUT_UTF8(ch, tmp, *q++ = tmp;)
134         }
135         *q = 0;
136         break;
137
138     case 3:  /* UTF-8 */
139         len = FFMIN(taglen, dstlen);
140         get_buffer(pb, dst, len);
141         dst[len] = 0;
142         break;
143     default:
144         av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
145     }
146
147     if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
148         && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
149         && genre <= ID3v1_GENRE_MAX)
150         val = ff_id3v1_genre_str[genre];
151     else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
152         /* dst now contains two 0-terminated strings */
153         dst[dstlen] = 0;
154         len = strlen(dst);
155         key = dst;
156         val = dst + FFMIN(len + 1, dstlen);
157     }
158     else if (*dst)
159         val = dst;
160
161     if (val)
162         av_metadata_set2(&s->metadata, key, val, 0);
163 }
164
165 void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
166 {
167     int isv34, tlen, unsync;
168     char tag[5];
169     int64_t next;
170     int taghdrlen;
171     const char *reason;
172     ByteIOContext pb;
173     unsigned char *buffer = NULL;
174     int buffer_size = 0;
175
176     switch (version) {
177     case 2:
178         if (flags & 0x40) {
179             reason = "compression";
180             goto error;
181         }
182         isv34 = 0;
183         taghdrlen = 6;
184         break;
185
186     case 3:
187     case 4:
188         isv34 = 1;
189         taghdrlen = 10;
190         break;
191
192     default:
193         reason = "version";
194         goto error;
195     }
196
197     unsync = flags & 0x80;
198
199     if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
200         url_fskip(s->pb, get_size(s->pb, 4));
201
202     while (len >= taghdrlen) {
203         unsigned int tflags;
204         int tunsync = 0;
205
206         if (isv34) {
207             get_buffer(s->pb, tag, 4);
208             tag[4] = 0;
209             if(version==3){
210                 tlen = get_be32(s->pb);
211             }else
212                 tlen = get_size(s->pb, 4);
213             tflags = get_be16(s->pb);
214             tunsync = tflags & 0x02;
215         } else {
216             get_buffer(s->pb, tag, 3);
217             tag[3] = 0;
218             tlen = get_be24(s->pb);
219         }
220         len -= taghdrlen + tlen;
221
222         if (len < 0)
223             break;
224
225         next = url_ftell(s->pb) + tlen;
226
227         if (tag[0] == 'T') {
228             if (unsync || tunsync) {
229                 int i, j;
230                 av_fast_malloc(&buffer, &buffer_size, tlen);
231                 for (i = 0, j = 0; i < tlen; i++, j++) {
232                     buffer[j] = get_byte(s->pb);
233                     if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
234                         /* Unsynchronised byte, skip it */
235                         j--;
236                     }
237                 }
238                 init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
239                 read_ttag(s, &pb, j, tag);
240             } else {
241                 read_ttag(s, s->pb, tlen, tag);
242             }
243         }
244         else if (!tag[0]) {
245             if (tag[1])
246                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
247             url_fskip(s->pb, tlen);
248             break;
249         }
250         /* Skip to end of tag */
251         url_fseek(s->pb, next, SEEK_SET);
252     }
253     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_metadata_conv);
254
255     if (len > 0) {
256         /* Skip padding */
257         url_fskip(s->pb, len);
258     }
259     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
260         url_fskip(s->pb, 10);
261
262     av_free(buffer);
263     return;
264
265   error:
266     av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
267     url_fskip(s->pb, len);
268     av_free(buffer);
269 }
270
271 const AVMetadataConv ff_id3v2_metadata_conv[] = {
272     { "TALB", "album"},
273     { "TAL",  "album"},
274     { "TCOM", "composer"},
275     { "TCON", "genre"},
276     { "TCO",  "genre"},
277     { "TCOP", "copyright"},
278     { "TDRL", "date"},
279     { "TDRC", "date"},
280     { "TENC", "encoded_by"},
281     { "TEN",  "encoded_by"},
282     { "TIT2", "title"},
283     { "TT2",  "title"},
284     { "TLAN", "language"},
285     { "TPE1", "artist"},
286     { "TP1",  "artist"},
287     { "TPE2", "album_artist"},
288     { "TP2",  "album_artist"},
289     { "TPE3", "performer"},
290     { "TP3",  "performer"},
291     { "TPOS", "disc"},
292     { "TPUB", "publisher"},
293     { "TRCK", "track"},
294     { "TRK",  "track"},
295     { "TSOA", "album-sort"},
296     { "TSOP", "artist-sort"},
297     { "TSOT", "title-sort"},
298     { "TSSE", "encoder"},
299     { 0 }
300 };
301
302 const char ff_id3v2_tags[][4] = {
303    "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC",
304    "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3",
305    "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY",
306    "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB",
307    "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST",
308    { 0 },
309 };