mathops: change "g" constraint to "rm" in x86-32 version of MUL64().
[libav.git] / libavformat / id3v2.c
CommitLineData
2ea512a6
AC
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"
75411182
PD
23#include "id3v1.h"
24#include "libavutil/avstring.h"
3a1350e8 25#include "libavutil/intreadwrite.h"
03700d39 26#include "metadata.h"
e731b8d8 27#include "avio_internal.h"
2ea512a6 28
3a1350e8 29int ff_id3v2_match(const uint8_t *buf, const char * magic)
2ea512a6 30{
3a1350e8
MK
31 return buf[0] == magic[0] &&
32 buf[1] == magic[1] &&
33 buf[2] == magic[2] &&
7d7b8c32
DB
34 buf[3] != 0xff &&
35 buf[4] != 0xff &&
36 (buf[6] & 0x80) == 0 &&
37 (buf[7] & 0x80) == 0 &&
38 (buf[8] & 0x80) == 0 &&
1d4b1bf2 39 (buf[9] & 0x80) == 0;
2ea512a6 40}
ac3ef4a4
AC
41
42int ff_id3v2_tag_len(const uint8_t * buf)
43{
44 int len = ((buf[6] & 0x7f) << 21) +
7d7b8c32
DB
45 ((buf[7] & 0x7f) << 14) +
46 ((buf[8] & 0x7f) << 7) +
47 (buf[9] & 0x7f) +
48 ID3v2_HEADER_SIZE;
ac3ef4a4
AC
49 if (buf[5] & 0x10)
50 len += ID3v2_HEADER_SIZE;
51 return len;
52}
75411182 53
ae628ec1 54static unsigned int get_size(AVIOContext *s, int len)
75411182 55{
7d7b8c32
DB
56 int v = 0;
57 while (len--)
b7effd4e 58 v = (v << 7) + (avio_r8(s) & 0x7F);
75411182
PD
59 return v;
60}
61
ae628ec1 62static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
75411182
PD
63{
64 char *q, dst[512];
41770abf 65 const char *val = NULL;
75411182
PD
66 int len, dstlen = sizeof(dst) - 1;
67 unsigned genre;
b7effd4e 68 unsigned int (*get)(AVIOContext*) = avio_rb16;
75411182 69
7d7b8c32
DB
70 dst[0] = 0;
71 if (taglen < 1)
75411182
PD
72 return;
73
74 taglen--; /* account for encoding type byte */
75
b7effd4e 76 switch (avio_r8(pb)) { /* encoding type */
75411182 77
d66eff36 78 case ID3v2_ENCODING_ISO8859:
75411182 79 q = dst;
787f8fad 80 while (taglen-- && q - dst < dstlen - 7) {
75411182 81 uint8_t tmp;
b7effd4e 82 PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
75411182 83 }
9aa1bcce 84 *q = 0;
75411182
PD
85 break;
86
d66eff36 87 case ID3v2_ENCODING_UTF16BOM:
20c68378 88 taglen -= 2;
b7effd4e 89 switch (avio_rb16(pb)) {
20c68378 90 case 0xfffe:
b7effd4e 91 get = avio_rl16;
20c68378
AK
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
d66eff36 100 case ID3v2_ENCODING_UTF16BE:
20c68378
AK
101 q = dst;
102 while (taglen > 1 && q - dst < dstlen - 7) {
103 uint32_t ch;
104 uint8_t tmp;
105
18bbe9df 106 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
20c68378
AK
107 PUT_UTF8(ch, tmp, *q++ = tmp;)
108 }
109 *q = 0;
110 break;
111
d66eff36 112 case ID3v2_ENCODING_UTF8:
037e9afd 113 len = FFMIN(taglen, dstlen);
b7effd4e 114 avio_read(pb, dst, len);
75411182
PD
115 dst[len] = 0;
116 break;
20c68378 117 default:
fb61a7c5 118 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
75411182
PD
119 }
120
41770abf 121 if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
75411182
PD
122 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
123 && genre <= ID3v1_GENRE_MAX)
41770abf
AK
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;
75411182 134
41770abf 135 if (val)
75aded83 136 av_metadata_set2(&s->metadata, key, val, AV_METADATA_DONT_OVERWRITE);
75411182
PD
137}
138
46a2da76 139static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
75411182 140{
18bbe9df 141 int isv34, tlen, unsync;
41770abf 142 char tag[5];
75411182
PD
143 int64_t next;
144 int taghdrlen;
145 const char *reason;
ae628ec1 146 AVIOContext pb;
18bbe9df
AK
147 unsigned char *buffer = NULL;
148 int buffer_size = 0;
75411182 149
7d7b8c32 150 switch (version) {
75411182 151 case 2:
7d7b8c32 152 if (flags & 0x40) {
75411182
PD
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
18bbe9df 171 unsync = flags & 0x80;
75411182 172
7d7b8c32 173 if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
e356fc57 174 avio_seek(s->pb, get_size(s->pb, 4), SEEK_CUR);
75411182 175
7d7b8c32 176 while (len >= taghdrlen) {
18bbe9df
AK
177 unsigned int tflags;
178 int tunsync = 0;
179
7d7b8c32 180 if (isv34) {
b7effd4e 181 avio_read(s->pb, tag, 4);
41770abf 182 tag[4] = 0;
3fd5a75b 183 if(version==3){
b7effd4e 184 tlen = avio_rb32(s->pb);
3fd5a75b
MN
185 }else
186 tlen = get_size(s->pb, 4);
b7effd4e 187 tflags = avio_rb16(s->pb);
7a07d158 188 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
75411182 189 } else {
b7effd4e 190 avio_read(s->pb, tag, 3);
41770abf 191 tag[3] = 0;
b7effd4e 192 tlen = avio_rb24(s->pb);
75411182
PD
193 }
194 len -= taghdrlen + tlen;
195
7d7b8c32 196 if (len < 0)
75411182
PD
197 break;
198
a2704c97 199 next = avio_tell(s->pb) + tlen;
75411182 200
a152c77f 201 if (tflags & ID3v2_FLAG_DATALEN) {
b7effd4e 202 avio_rb32(s->pb);
a152c77f
AK
203 tlen -= 4;
204 }
205
407d3d5a
AK
206 if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
207 av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
e356fc57 208 avio_seek(s->pb, tlen, SEEK_CUR);
407d3d5a 209 } else if (tag[0] == 'T') {
18bbe9df
AK
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++) {
b7effd4e 214 buffer[j] = avio_r8(s->pb);
18bbe9df
AK
215 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
216 /* Unsynchronised byte, skip it */
217 j--;
218 }
219 }
e731b8d8 220 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
18bbe9df
AK
221 read_ttag(s, &pb, j, tag);
222 } else {
223 read_ttag(s, s->pb, tlen, tag);
224 }
225 }
2e3ca1ff
JM
226 else if (!tag[0]) {
227 if (tag[1])
228 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
e356fc57 229 avio_seek(s->pb, tlen, SEEK_CUR);
2e3ca1ff
JM
230 break;
231 }
75411182 232 /* Skip to end of tag */
6b4aa5da 233 avio_seek(s->pb, next, SEEK_SET);
75411182
PD
234 }
235
ff58de29
AK
236 if (len > 0) {
237 /* Skip padding */
e356fc57 238 avio_seek(s->pb, len, SEEK_CUR);
ff58de29 239 }
7d7b8c32 240 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
e356fc57 241 avio_seek(s->pb, 10, SEEK_CUR);
18bbe9df
AK
242
243 av_free(buffer);
75411182
PD
244 return;
245
246 error:
247 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
e356fc57 248 avio_seek(s->pb, len, SEEK_CUR);
18bbe9df 249 av_free(buffer);
75411182 250}
6378b062 251
46a2da76
AK
252void 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 */
a2704c97 261 off = avio_tell(s->pb);
b7effd4e 262 ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
46a2da76 263 if (ret != ID3v2_HEADER_SIZE)
f7fcd6a2 264 break;
46a2da76
AK
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 {
6b4aa5da 274 avio_seek(s->pb, off, SEEK_SET);
46a2da76
AK
275 }
276 } while (found_header);
cb6bc576
AK
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);
46a2da76
AK
280}
281
cb6bc576 282const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
6378b062
AK
283 { "TALB", "album"},
284 { "TCOM", "composer"},
285 { "TCON", "genre"},
286 { "TCOP", "copyright"},
ca76a119 287 { "TENC", "encoded_by"},
6378b062
AK
288 { "TIT2", "title"},
289 { "TLAN", "language"},
8a98be1a 290 { "TPE1", "artist"},
ca76a119 291 { "TPE2", "album_artist"},
ca76a119 292 { "TPE3", "performer"},
6378b062
AK
293 { "TPOS", "disc"},
294 { "TPUB", "publisher"},
295 { "TRCK", "track"},
cb6bc576
AK
296 { "TSSE", "encoder"},
297 { 0 }
298};
299
300const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
301 { "TDRL", "date"},
302 { "TDRC", "date"},
303 { "TDEN", "creation_time"},
ca76a119
AK
304 { "TSOA", "album-sort"},
305 { "TSOP", "artist-sort"},
306 { "TSOT", "title-sort"},
6378b062
AK
307 { 0 }
308};
078d89a2 309
cb6bc576
AK
310const 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
078d89a2 323const char ff_id3v2_tags[][4] = {
cb6bc576
AK
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
331const 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
337const char ff_id3v2_3_tags[][4] = {
338 "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
078d89a2
AK
339 { 0 },
340};