]> git.sesse.net Git - ffmpeg/blob - libavformat/id3v2.c
ffmpeg: remove unsed variable nopts
[ffmpeg] / libavformat / id3v2.c
1 /*
2  * ID3v2 header parser
3  * Copyright (c) 2003 Fabrice Bellard
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; 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 "libavutil/dict.h"
27 #include "avio_internal.h"
28
29 int ff_id3v2_match(const uint8_t *buf, const char * magic)
30 {
31     return  buf[0]         == magic[0] &&
32             buf[1]         == magic[1] &&
33             buf[2]         == magic[2] &&
34             buf[3]         != 0xff &&
35             buf[4]         != 0xff &&
36            (buf[6] & 0x80) ==    0 &&
37            (buf[7] & 0x80) ==    0 &&
38            (buf[8] & 0x80) ==    0 &&
39            (buf[9] & 0x80) ==    0;
40 }
41
42 int ff_id3v2_tag_len(const uint8_t * buf)
43 {
44     int len = ((buf[6] & 0x7f) << 21) +
45               ((buf[7] & 0x7f) << 14) +
46               ((buf[8] & 0x7f) << 7) +
47                (buf[9] & 0x7f) +
48               ID3v2_HEADER_SIZE;
49     if (buf[5] & 0x10)
50         len += ID3v2_HEADER_SIZE;
51     return len;
52 }
53
54 static unsigned int get_size(AVIOContext *s, int len)
55 {
56     int v = 0;
57     while (len--)
58         v = (v << 7) + (avio_r8(s) & 0x7F);
59     return v;
60 }
61
62 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
63 {
64     char *q, dst[512];
65     const char *val = NULL;
66     int len, dstlen = sizeof(dst) - 1;
67     unsigned genre;
68     unsigned int (*get)(AVIOContext*) = avio_rb16;
69
70     dst[0] = 0;
71     if (taglen < 1)
72         return;
73
74     taglen--; /* account for encoding type byte */
75
76     switch (avio_r8(pb)) { /* encoding type */
77
78     case ID3v2_ENCODING_ISO8859:
79         q = dst;
80         while (taglen-- && q - dst < dstlen - 7) {
81             uint8_t tmp;
82             PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
83         }
84         *q = 0;
85         break;
86
87     case ID3v2_ENCODING_UTF16BOM:
88         taglen -= 2;
89         switch (avio_rb16(pb)) {
90         case 0xfffe:
91             get = avio_rl16;
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
100     case ID3v2_ENCODING_UTF16BE:
101         q = dst;
102         while (taglen > 1 && q - dst < dstlen - 7) {
103             uint32_t ch;
104             uint8_t tmp;
105
106             GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
107             PUT_UTF8(ch, tmp, *q++ = tmp;)
108         }
109         *q = 0;
110         break;
111
112     case ID3v2_ENCODING_UTF8:
113         len = FFMIN(taglen, dstlen);
114         avio_read(pb, dst, len);
115         dst[len] = 0;
116         break;
117     default:
118         av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
119     }
120
121     if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
122         && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
123         && genre <= ID3v1_GENRE_MAX)
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;
134
135     if (val)
136         av_dict_set(&s->metadata, key, val, AV_DICT_DONT_OVERWRITE);
137 }
138
139 static int is_number(const char *str)
140 {
141     while (*str >= '0' && *str <= '9') str++;
142     return !*str;
143 }
144
145 static AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag)
146 {
147     AVDictionaryEntry *t;
148     if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
149         strlen(t->value) == 4 && is_number(t->value))
150         return t;
151     return NULL;
152 }
153
154 static void merge_date(AVDictionary **m)
155 {
156     AVDictionaryEntry *t;
157     char date[17] = {0};      // YYYY-MM-DD hh:mm
158
159     if (!(t = get_date_tag(*m, "TYER")) &&
160         !(t = get_date_tag(*m, "TYE")))
161         return;
162     av_strlcpy(date, t->value, 5);
163     av_dict_set(m, "TYER", NULL, 0);
164     av_dict_set(m, "TYE",  NULL, 0);
165
166     if (!(t = get_date_tag(*m, "TDAT")) &&
167         !(t = get_date_tag(*m, "TDA")))
168         goto finish;
169     snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
170     av_dict_set(m, "TDAT", NULL, 0);
171     av_dict_set(m, "TDA",  NULL, 0);
172
173     if (!(t = get_date_tag(*m, "TIME")) &&
174         !(t = get_date_tag(*m, "TIM")))
175         goto finish;
176     snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
177     av_dict_set(m, "TIME", NULL, 0);
178     av_dict_set(m, "TIM",  NULL, 0);
179
180 finish:
181     if (date[0])
182         av_dict_set(m, "date", date, 0);
183 }
184
185 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
186 {
187     int isv34, tlen, unsync;
188     char tag[5];
189     int64_t next, end = avio_tell(s->pb) + len;
190     int taghdrlen;
191     const char *reason = NULL;
192     AVIOContext pb;
193     unsigned char *buffer = NULL;
194     int buffer_size = 0;
195
196     switch (version) {
197     case 2:
198         if (flags & 0x40) {
199             reason = "compression";
200             goto error;
201         }
202         isv34 = 0;
203         taghdrlen = 6;
204         break;
205
206     case 3:
207     case 4:
208         isv34 = 1;
209         taghdrlen = 10;
210         break;
211
212     default:
213         reason = "version";
214         goto error;
215     }
216
217     unsync = flags & 0x80;
218
219     if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
220         avio_skip(s->pb, get_size(s->pb, 4));
221
222     while (len >= taghdrlen) {
223         unsigned int tflags = 0;
224         int tunsync = 0;
225
226         if (isv34) {
227             avio_read(s->pb, tag, 4);
228             tag[4] = 0;
229             if(version==3){
230                 tlen = avio_rb32(s->pb);
231             }else
232                 tlen = get_size(s->pb, 4);
233             tflags = avio_rb16(s->pb);
234             tunsync = tflags & ID3v2_FLAG_UNSYNCH;
235         } else {
236             avio_read(s->pb, tag, 3);
237             tag[3] = 0;
238             tlen = avio_rb24(s->pb);
239         }
240         if (tlen <= 0 || tlen > len - taghdrlen) {
241             av_log(s, AV_LOG_WARNING, "Invalid size in frame %s, skipping the rest of tag.\n", tag);
242             break;
243         }
244         len -= taghdrlen + tlen;
245         next = avio_tell(s->pb) + tlen;
246
247         if (tflags & ID3v2_FLAG_DATALEN) {
248             avio_rb32(s->pb);
249             tlen -= 4;
250         }
251
252         if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
253             av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
254             avio_skip(s->pb, tlen);
255         } else if (tag[0] == 'T') {
256             if (unsync || tunsync) {
257                 int i, j;
258                 av_fast_malloc(&buffer, &buffer_size, tlen);
259                 if (!buffer) {
260                     av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
261                     goto seek;
262                 }
263                 for (i = 0, j = 0; i < tlen; i++, j++) {
264                     buffer[j] = avio_r8(s->pb);
265                     if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
266                         /* Unsynchronised byte, skip it */
267                         j--;
268                     }
269                 }
270                 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
271                 read_ttag(s, &pb, j, tag);
272             } else {
273                 read_ttag(s, s->pb, tlen, tag);
274             }
275         }
276         else if (!tag[0]) {
277             if (tag[1])
278                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
279             avio_skip(s->pb, tlen);
280             break;
281         }
282         /* Skip to end of tag */
283 seek:
284         avio_seek(s->pb, next, SEEK_SET);
285     }
286
287     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
288         end += 10;
289
290   error:
291     if (reason)
292         av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
293     avio_seek(s->pb, end, SEEK_SET);
294     av_free(buffer);
295     return;
296 }
297
298 void ff_id3v2_read(AVFormatContext *s, const char *magic)
299 {
300     int len, ret;
301     uint8_t buf[ID3v2_HEADER_SIZE];
302     int     found_header;
303     int64_t off;
304
305     do {
306         /* save the current offset in case there's nothing to read/skip */
307         off = avio_tell(s->pb);
308         ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
309         if (ret != ID3v2_HEADER_SIZE)
310             break;
311             found_header = ff_id3v2_match(buf, magic);
312             if (found_header) {
313             /* parse ID3v2 header */
314             len = ((buf[6] & 0x7f) << 21) |
315                   ((buf[7] & 0x7f) << 14) |
316                   ((buf[8] & 0x7f) << 7) |
317                    (buf[9] & 0x7f);
318             ff_id3v2_parse(s, len, buf[3], buf[5]);
319         } else {
320             avio_seek(s->pb, off, SEEK_SET);
321         }
322     } while (found_header);
323     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
324     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
325     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
326     merge_date(&s->metadata);
327 }
328
329 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
330     { "TALB", "album"},
331     { "TCOM", "composer"},
332     { "TCON", "genre"},
333     { "TCOP", "copyright"},
334     { "TENC", "encoded_by"},
335     { "TIT2", "title"},
336     { "TLAN", "language"},
337     { "TPE1", "artist"},
338     { "TPE2", "album_artist"},
339     { "TPE3", "performer"},
340     { "TPOS", "disc"},
341     { "TPUB", "publisher"},
342     { "TRCK", "track"},
343     { "TSSE", "encoder"},
344     { 0 }
345 };
346
347 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
348     { "TDRL", "date"},
349     { "TDRC", "date"},
350     { "TDEN", "creation_time"},
351     { "TSOA", "album-sort"},
352     { "TSOP", "artist-sort"},
353     { "TSOT", "title-sort"},
354     { 0 }
355 };
356
357 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
358     { "TAL",  "album"},
359     { "TCO",  "genre"},
360     { "TT2",  "title"},
361     { "TEN",  "encoded_by"},
362     { "TP1",  "artist"},
363     { "TP2",  "album_artist"},
364     { "TP3",  "performer"},
365     { "TRK",  "track"},
366     { 0 }
367 };
368
369
370 const char ff_id3v2_tags[][4] = {
371    "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
372    "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
373    "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
374    "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
375    { 0 },
376 };
377
378 const char ff_id3v2_4_tags[][4] = {
379    "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
380    "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
381    { 0 },
382 };
383
384 const char ff_id3v2_3_tags[][4] = {
385    "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
386    { 0 },
387 };