3 * Copyright (c) 2003 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
24 #include "libavutil/avstring.h"
25 #include "libavutil/intreadwrite.h"
28 int ff_id3v2_match(const uint8_t *buf, const char * magic)
30 return buf[0] == magic[0] &&
35 (buf[6] & 0x80) == 0 &&
36 (buf[7] & 0x80) == 0 &&
37 (buf[8] & 0x80) == 0 &&
41 int ff_id3v2_tag_len(const uint8_t * buf)
43 int len = ((buf[6] & 0x7f) << 21) +
44 ((buf[7] & 0x7f) << 14) +
45 ((buf[8] & 0x7f) << 7) +
49 len += ID3v2_HEADER_SIZE;
53 static unsigned int get_size(ByteIOContext *s, int len)
57 v = (v << 7) + (get_byte(s) & 0x7F);
61 static void read_ttag(AVFormatContext *s, ByteIOContext *pb, int taglen, const char *key)
64 const char *val = NULL;
65 int len, dstlen = sizeof(dst) - 1;
67 unsigned int (*get)(ByteIOContext*) = get_be16;
73 taglen--; /* account for encoding type byte */
75 switch (get_byte(pb)) { /* encoding type */
77 case ID3v2_ENCODING_ISO8859:
79 while (taglen-- && q - dst < dstlen - 7) {
81 PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;)
86 case ID3v2_ENCODING_UTF16BOM:
88 switch (get_be16(pb)) {
94 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
99 case ID3v2_ENCODING_UTF16BE:
101 while (taglen > 1 && q - dst < dstlen - 7) {
105 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
106 PUT_UTF8(ch, tmp, *q++ = tmp;)
111 case ID3v2_ENCODING_UTF8:
112 len = FFMIN(taglen, dstlen);
113 get_buffer(pb, dst, len);
117 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
120 if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
121 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
122 && genre <= ID3v1_GENRE_MAX)
123 val = ff_id3v1_genre_str[genre];
124 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
125 /* dst now contains two 0-terminated strings */
129 val = dst + FFMIN(len + 1, dstlen);
135 av_metadata_set2(&s->metadata, key, val, AV_METADATA_DONT_OVERWRITE);
138 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
140 int isv34, tlen, unsync;
146 unsigned char *buffer = NULL;
152 reason = "compression";
170 unsync = flags & 0x80;
172 if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
173 url_fskip(s->pb, get_size(s->pb, 4));
175 while (len >= taghdrlen) {
180 get_buffer(s->pb, tag, 4);
183 tlen = get_be32(s->pb);
185 tlen = get_size(s->pb, 4);
186 tflags = get_be16(s->pb);
187 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
189 get_buffer(s->pb, tag, 3);
191 tlen = get_be24(s->pb);
193 len -= taghdrlen + tlen;
198 next = url_ftell(s->pb) + tlen;
200 if (tflags & ID3v2_FLAG_DATALEN) {
205 if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
206 av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
207 url_fskip(s->pb, tlen);
208 } else if (tag[0] == 'T') {
209 if (unsync || tunsync) {
211 av_fast_malloc(&buffer, &buffer_size, tlen);
212 for (i = 0, j = 0; i < tlen; i++, j++) {
213 buffer[j] = get_byte(s->pb);
214 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
215 /* Unsynchronised byte, skip it */
219 init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
220 read_ttag(s, &pb, j, tag);
222 read_ttag(s, s->pb, tlen, tag);
227 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
228 url_fskip(s->pb, tlen);
231 /* Skip to end of tag */
232 url_fseek(s->pb, next, SEEK_SET);
237 url_fskip(s->pb, len);
239 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
240 url_fskip(s->pb, 10);
246 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
247 url_fskip(s->pb, len);
251 void ff_id3v2_read(AVFormatContext *s, const char *magic)
254 uint8_t buf[ID3v2_HEADER_SIZE];
259 /* save the current offset in case there's nothing to read/skip */
260 off = url_ftell(s->pb);
261 ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
262 if (ret != ID3v2_HEADER_SIZE)
264 found_header = ff_id3v2_match(buf, magic);
266 /* parse ID3v2 header */
267 len = ((buf[6] & 0x7f) << 21) |
268 ((buf[7] & 0x7f) << 14) |
269 ((buf[8] & 0x7f) << 7) |
271 ff_id3v2_parse(s, len, buf[3], buf[5]);
273 url_fseek(s->pb, off, SEEK_SET);
275 } while (found_header);
276 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
277 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
278 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
281 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
283 { "TCOM", "composer"},
285 { "TCOP", "copyright"},
286 { "TENC", "encoded_by"},
288 { "TLAN", "language"},
290 { "TPE2", "album_artist"},
291 { "TPE3", "performer"},
293 { "TPUB", "publisher"},
295 { "TSSE", "encoder"},
299 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
302 { "TDEN", "creation_time"},
303 { "TSOA", "album-sort"},
304 { "TSOP", "artist-sort"},
305 { "TSOT", "title-sort"},
309 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
313 { "TEN", "encoded_by"},
315 { "TP2", "album_artist"},
316 { "TP3", "performer"},
322 const char ff_id3v2_tags[][4] = {
323 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
324 "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
325 "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
326 "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
330 const char ff_id3v2_4_tags[][4] = {
331 "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
332 "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
336 const char ff_id3v2_3_tags[][4] = {
337 "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",