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