ffmpeg / libavformat / id3v2.c @ d66eff36
History | View | Annotate | Download (8.88 KB)
1 | 2ea512a6 | Alex Converse | /*
|
---|---|---|---|
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 | 75411182 | Patrick Dehne | #include "id3v1.h" |
24 | #include "libavutil/avstring.h" |
||
25 | 3a1350e8 | Michael Karcher | #include "libavutil/intreadwrite.h" |
26 | 03700d39 | Anton Khirnov | #include "metadata.h" |
27 | 2ea512a6 | Alex Converse | |
28 | 3a1350e8 | Michael Karcher | int ff_id3v2_match(const uint8_t *buf, const char * magic) |
29 | 2ea512a6 | Alex Converse | { |
30 | 3a1350e8 | Michael Karcher | return buf[0] == magic[0] && |
31 | buf[1] == magic[1] && |
||
32 | buf[2] == magic[2] && |
||
33 | 7d7b8c32 | Diego Biurrun | buf[3] != 0xff && |
34 | buf[4] != 0xff && |
||
35 | (buf[6] & 0x80) == 0 && |
||
36 | (buf[7] & 0x80) == 0 && |
||
37 | (buf[8] & 0x80) == 0 && |
||
38 | 1d4b1bf2 | Diego Biurrun | (buf[9] & 0x80) == 0; |
39 | 2ea512a6 | Alex Converse | } |
40 | ac3ef4a4 | Alex Converse | |
41 | int ff_id3v2_tag_len(const uint8_t * buf) |
||
42 | { |
||
43 | int len = ((buf[6] & 0x7f) << 21) + |
||
44 | 7d7b8c32 | Diego Biurrun | ((buf[7] & 0x7f) << 14) + |
45 | ((buf[8] & 0x7f) << 7) + |
||
46 | (buf[9] & 0x7f) + |
||
47 | ID3v2_HEADER_SIZE; |
||
48 | ac3ef4a4 | Alex Converse | if (buf[5] & 0x10) |
49 | len += ID3v2_HEADER_SIZE; |
||
50 | return len;
|
||
51 | } |
||
52 | 75411182 | Patrick Dehne | |
53 | static unsigned int get_size(ByteIOContext *s, int len) |
||
54 | { |
||
55 | 7d7b8c32 | Diego Biurrun | int v = 0; |
56 | while (len--)
|
||
57 | v = (v << 7) + (get_byte(s) & 0x7F); |
||
58 | 75411182 | Patrick Dehne | return v;
|
59 | } |
||
60 | |||
61 | 18bbe9df | Alexander Kojevnikov | static void read_ttag(AVFormatContext *s, ByteIOContext *pb, int taglen, const char *key) |
62 | 75411182 | Patrick Dehne | { |
63 | char *q, dst[512]; |
||
64 | 41770abf | Anton Khirnov | const char *val = NULL; |
65 | 75411182 | Patrick Dehne | int len, dstlen = sizeof(dst) - 1; |
66 | unsigned genre;
|
||
67 | 20c68378 | Anton Khirnov | unsigned int (*get)(ByteIOContext*) = get_be16; |
68 | 75411182 | Patrick Dehne | |
69 | 7d7b8c32 | Diego Biurrun | dst[0] = 0; |
70 | if (taglen < 1) |
||
71 | 75411182 | Patrick Dehne | return;
|
72 | |||
73 | taglen--; /* account for encoding type byte */
|
||
74 | |||
75 | 18bbe9df | Alexander Kojevnikov | switch (get_byte(pb)) { /* encoding type */ |
76 | 75411182 | Patrick Dehne | |
77 | d66eff36 | Anton Khirnov | case ID3v2_ENCODING_ISO8859:
|
78 | 75411182 | Patrick Dehne | q = dst; |
79 | 787f8fad | Anton Khirnov | while (taglen-- && q - dst < dstlen - 7) { |
80 | 75411182 | Patrick Dehne | uint8_t tmp; |
81 | 18bbe9df | Alexander Kojevnikov | PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;) |
82 | 75411182 | Patrick Dehne | } |
83 | 9aa1bcce | Anton Khirnov | *q = 0;
|
84 | 75411182 | Patrick Dehne | break;
|
85 | |||
86 | d66eff36 | Anton Khirnov | case ID3v2_ENCODING_UTF16BOM:
|
87 | 20c68378 | Anton Khirnov | taglen -= 2;
|
88 | 18bbe9df | Alexander Kojevnikov | switch (get_be16(pb)) {
|
89 | 20c68378 | Anton Khirnov | case 0xfffe: |
90 | get = get_le16; |
||
91 | case 0xfeff: |
||
92 | break;
|
||
93 | default:
|
||
94 | av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
|
||
95 | return;
|
||
96 | } |
||
97 | // fall-through
|
||
98 | |||
99 | d66eff36 | Anton Khirnov | case ID3v2_ENCODING_UTF16BE:
|
100 | 20c68378 | Anton Khirnov | q = dst; |
101 | while (taglen > 1 && q - dst < dstlen - 7) { |
||
102 | uint32_t ch; |
||
103 | uint8_t tmp; |
||
104 | |||
105 | 18bbe9df | Alexander Kojevnikov | GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;) |
106 | 20c68378 | Anton Khirnov | PUT_UTF8(ch, tmp, *q++ = tmp;) |
107 | } |
||
108 | *q = 0;
|
||
109 | break;
|
||
110 | |||
111 | d66eff36 | Anton Khirnov | case ID3v2_ENCODING_UTF8:
|
112 | 037e9afd | Jai Menon | len = FFMIN(taglen, dstlen); |
113 | 18bbe9df | Alexander Kojevnikov | get_buffer(pb, dst, len); |
114 | 75411182 | Patrick Dehne | dst[len] = 0;
|
115 | break;
|
||
116 | 20c68378 | Anton Khirnov | default:
|
117 | av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
|
||
118 | 75411182 | Patrick Dehne | } |
119 | |||
120 | 41770abf | Anton Khirnov | if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) |
121 | 75411182 | Patrick Dehne | && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) |
122 | && genre <= ID3v1_GENRE_MAX) |
||
123 | 41770abf | Anton Khirnov | val = ff_id3v1_genre_str[genre]; |
124 | else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { |
||
125 | /* dst now contains two 0-terminated strings */
|
||
126 | dst[dstlen] = 0;
|
||
127 | len = strlen(dst); |
||
128 | key = dst; |
||
129 | val = dst + FFMIN(len + 1, dstlen);
|
||
130 | } |
||
131 | else if (*dst) |
||
132 | val = dst; |
||
133 | 75411182 | Patrick Dehne | |
134 | 41770abf | Anton Khirnov | if (val)
|
135 | 75aded83 | Anton Khirnov | av_metadata_set2(&s->metadata, key, val, AV_METADATA_DONT_OVERWRITE); |
136 | 75411182 | Patrick Dehne | } |
137 | |||
138 | 46a2da76 | Anton Khirnov | static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) |
139 | 75411182 | Patrick Dehne | { |
140 | 18bbe9df | Alexander Kojevnikov | int isv34, tlen, unsync;
|
141 | 41770abf | Anton Khirnov | char tag[5]; |
142 | 75411182 | Patrick Dehne | int64_t next; |
143 | int taghdrlen;
|
||
144 | const char *reason; |
||
145 | 18bbe9df | Alexander Kojevnikov | ByteIOContext pb; |
146 | unsigned char *buffer = NULL; |
||
147 | int buffer_size = 0; |
||
148 | 75411182 | Patrick Dehne | |
149 | 7d7b8c32 | Diego Biurrun | switch (version) {
|
150 | 75411182 | Patrick Dehne | case 2: |
151 | 7d7b8c32 | Diego Biurrun | if (flags & 0x40) { |
152 | 75411182 | Patrick Dehne | reason = "compression";
|
153 | goto error;
|
||
154 | } |
||
155 | isv34 = 0;
|
||
156 | taghdrlen = 6;
|
||
157 | break;
|
||
158 | |||
159 | case 3: |
||
160 | case 4: |
||
161 | isv34 = 1;
|
||
162 | taghdrlen = 10;
|
||
163 | break;
|
||
164 | |||
165 | default:
|
||
166 | reason = "version";
|
||
167 | goto error;
|
||
168 | } |
||
169 | |||
170 | 18bbe9df | Alexander Kojevnikov | unsync = flags & 0x80;
|
171 | 75411182 | Patrick Dehne | |
172 | 7d7b8c32 | Diego Biurrun | if (isv34 && flags & 0x40) /* Extended header present, just skip over it */ |
173 | 75411182 | Patrick Dehne | url_fskip(s->pb, get_size(s->pb, 4));
|
174 | |||
175 | 7d7b8c32 | Diego Biurrun | while (len >= taghdrlen) {
|
176 | 18bbe9df | Alexander Kojevnikov | unsigned int tflags; |
177 | int tunsync = 0; |
||
178 | |||
179 | 7d7b8c32 | Diego Biurrun | if (isv34) {
|
180 | 41770abf | Anton Khirnov | get_buffer(s->pb, tag, 4);
|
181 | tag[4] = 0; |
||
182 | 3fd5a75b | Michael Niedermayer | if(version==3){ |
183 | d004179e | Michael Niedermayer | tlen = get_be32(s->pb); |
184 | 3fd5a75b | Michael Niedermayer | }else
|
185 | tlen = get_size(s->pb, 4);
|
||
186 | 18bbe9df | Alexander Kojevnikov | tflags = get_be16(s->pb); |
187 | 7a07d158 | Anton Khirnov | tunsync = tflags & ID3v2_FLAG_UNSYNCH; |
188 | 75411182 | Patrick Dehne | } else {
|
189 | 41770abf | Anton Khirnov | get_buffer(s->pb, tag, 3);
|
190 | tag[3] = 0; |
||
191 | 1cd44221 | Michael Niedermayer | tlen = get_be24(s->pb); |
192 | 75411182 | Patrick Dehne | } |
193 | len -= taghdrlen + tlen; |
||
194 | |||
195 | 7d7b8c32 | Diego Biurrun | if (len < 0) |
196 | 75411182 | Patrick Dehne | break;
|
197 | |||
198 | next = url_ftell(s->pb) + tlen; |
||
199 | |||
200 | a152c77f | Anton Khirnov | if (tflags & ID3v2_FLAG_DATALEN) {
|
201 | get_be32(s->pb); |
||
202 | tlen -= 4;
|
||
203 | } |
||
204 | |||
205 | 407d3d5a | Anton Khirnov | 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 | 18bbe9df | Alexander Kojevnikov | if (unsync || tunsync) {
|
210 | int i, j;
|
||
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 */
|
||
216 | j--; |
||
217 | } |
||
218 | } |
||
219 | init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL); |
||
220 | read_ttag(s, &pb, j, tag); |
||
221 | } else {
|
||
222 | read_ttag(s, s->pb, tlen, tag); |
||
223 | } |
||
224 | } |
||
225 | 2e3ca1ff | Jai Menon | else if (!tag[0]) { |
226 | if (tag[1]) |
||
227 | av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
|
||
228 | ff58de29 | Alexander Kojevnikov | url_fskip(s->pb, tlen); |
229 | 2e3ca1ff | Jai Menon | break;
|
230 | } |
||
231 | 75411182 | Patrick Dehne | /* Skip to end of tag */
|
232 | url_fseek(s->pb, next, SEEK_SET); |
||
233 | } |
||
234 | |||
235 | ff58de29 | Alexander Kojevnikov | if (len > 0) { |
236 | /* Skip padding */
|
||
237 | url_fskip(s->pb, len); |
||
238 | } |
||
239 | 7d7b8c32 | Diego Biurrun | if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ |
240 | 75411182 | Patrick Dehne | url_fskip(s->pb, 10);
|
241 | 18bbe9df | Alexander Kojevnikov | |
242 | av_free(buffer); |
||
243 | 75411182 | Patrick Dehne | return;
|
244 | |||
245 | error:
|
||
246 | av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
|
||
247 | url_fskip(s->pb, len); |
||
248 | 18bbe9df | Alexander Kojevnikov | av_free(buffer); |
249 | 75411182 | Patrick Dehne | } |
250 | 6378b062 | Anton Khirnov | |
251 | 46a2da76 | Anton Khirnov | void ff_id3v2_read(AVFormatContext *s, const char *magic) |
252 | { |
||
253 | int len, ret;
|
||
254 | uint8_t buf[ID3v2_HEADER_SIZE]; |
||
255 | int found_header;
|
||
256 | int64_t off; |
||
257 | |||
258 | do {
|
||
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)
|
||
263 | f7fcd6a2 | Anton Khirnov | break;
|
264 | 46a2da76 | Anton Khirnov | found_header = ff_id3v2_match(buf, magic); |
265 | if (found_header) {
|
||
266 | /* parse ID3v2 header */
|
||
267 | len = ((buf[6] & 0x7f) << 21) | |
||
268 | ((buf[7] & 0x7f) << 14) | |
||
269 | ((buf[8] & 0x7f) << 7) | |
||
270 | (buf[9] & 0x7f); |
||
271 | ff_id3v2_parse(s, len, buf[3], buf[5]); |
||
272 | } else {
|
||
273 | url_fseek(s->pb, off, SEEK_SET); |
||
274 | } |
||
275 | } while (found_header);
|
||
276 | f7fcd6a2 | Anton Khirnov | ff_metadata_conv(&s->metadata, NULL, ff_id3v2_metadata_conv);
|
277 | 46a2da76 | Anton Khirnov | } |
278 | |||
279 | 6378b062 | Anton Khirnov | const AVMetadataConv ff_id3v2_metadata_conv[] = {
|
280 | { "TALB", "album"}, |
||
281 | dfe9ee6b | Michael Niedermayer | { "TAL", "album"}, |
282 | 6378b062 | Anton Khirnov | { "TCOM", "composer"}, |
283 | { "TCON", "genre"}, |
||
284 | dfe9ee6b | Michael Niedermayer | { "TCO", "genre"}, |
285 | 6378b062 | Anton Khirnov | { "TCOP", "copyright"}, |
286 | { "TDRL", "date"}, |
||
287 | ca76a119 | Anton Khirnov | { "TDRC", "date"}, |
288 | bcb5d217 | Anton Khirnov | { "TDEN", "creation_time"}, |
289 | ca76a119 | Anton Khirnov | { "TENC", "encoded_by"}, |
290 | { "TEN", "encoded_by"}, |
||
291 | 6378b062 | Anton Khirnov | { "TIT2", "title"}, |
292 | dfe9ee6b | Michael Niedermayer | { "TT2", "title"}, |
293 | 6378b062 | Anton Khirnov | { "TLAN", "language"}, |
294 | 8a98be1a | Michael Niedermayer | { "TPE1", "artist"}, |
295 | dfe9ee6b | Michael Niedermayer | { "TP1", "artist"}, |
296 | ca76a119 | Anton Khirnov | { "TPE2", "album_artist"}, |
297 | { "TP2", "album_artist"}, |
||
298 | { "TPE3", "performer"}, |
||
299 | { "TP3", "performer"}, |
||
300 | 6378b062 | Anton Khirnov | { "TPOS", "disc"}, |
301 | { "TPUB", "publisher"}, |
||
302 | { "TRCK", "track"}, |
||
303 | dfe9ee6b | Michael Niedermayer | { "TRK", "track"}, |
304 | ca76a119 | Anton Khirnov | { "TSOA", "album-sort"}, |
305 | { "TSOP", "artist-sort"}, |
||
306 | { "TSOT", "title-sort"}, |
||
307 | { "TSSE", "encoder"}, |
||
308 | 6378b062 | Anton Khirnov | { 0 }
|
309 | }; |
||
310 | 078d89a2 | Anton Khirnov | |
311 | const char ff_id3v2_tags[][4] = { |
||
312 | "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", |
||
313 | "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", |
||
314 | "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", |
||
315 | "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", |
||
316 | "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", |
||
317 | { 0 },
|
||
318 | }; |