Statistics
| Branch: | Revision:

ffmpeg / libavformat / id3v2.c @ e356fc57

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