Statistics
| Branch: | Revision:

ffmpeg / libavformat / id3v2.c @ 0c41d554

History | View | Annotate | Download (8.39 KB)

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"
23
#include "id3v1.h"
24
#include "libavutil/avstring.h"
25
#include "libavutil/intreadwrite.h"
26

    
27
int ff_id3v2_match(const uint8_t *buf, const char * magic)
28
{
29
    return  buf[0]         == magic[0] &&
30
            buf[1]         == magic[1] &&
31
            buf[2]         == magic[2] &&
32
            buf[3]         != 0xff &&
33
            buf[4]         != 0xff &&
34
           (buf[6] & 0x80) ==    0 &&
35
           (buf[7] & 0x80) ==    0 &&
36
           (buf[8] & 0x80) ==    0 &&
37
           (buf[9] & 0x80) ==    0;
38
}
39

    
40
int ff_id3v2_tag_len(const uint8_t * buf)
41
{
42
    int len = ((buf[6] & 0x7f) << 21) +
43
              ((buf[7] & 0x7f) << 14) +
44
              ((buf[8] & 0x7f) << 7) +
45
               (buf[9] & 0x7f) +
46
              ID3v2_HEADER_SIZE;
47
    if (buf[5] & 0x10)
48
        len += ID3v2_HEADER_SIZE;
49
    return len;
50
}
51

    
52
void ff_id3v2_read(AVFormatContext *s, const char *magic)
53
{
54
    int len, ret;
55
    uint8_t buf[ID3v2_HEADER_SIZE];
56
    int     found_header;
57
    int64_t off;
58

    
59
    do {
60
        /* save the current offset in case there's nothing to read/skip */
61
        off = url_ftell(s->pb);
62
    ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
63
    if (ret != ID3v2_HEADER_SIZE)
64
        return;
65
        found_header = ff_id3v2_match(buf, magic);
66
        if (found_header) {
67
        /* parse ID3v2 header */
68
        len = ((buf[6] & 0x7f) << 21) |
69
            ((buf[7] & 0x7f) << 14) |
70
            ((buf[8] & 0x7f) << 7) |
71
            (buf[9] & 0x7f);
72
        ff_id3v2_parse(s, len, buf[3], buf[5]);
73
    } else {
74
        url_fseek(s->pb, off, SEEK_SET);
75
    }
76
    } while (found_header);
77
}
78

    
79
static unsigned int get_size(ByteIOContext *s, int len)
80
{
81
    int v = 0;
82
    while (len--)
83
        v = (v << 7) + (get_byte(s) & 0x7F);
84
    return v;
85
}
86

    
87
static void read_ttag(AVFormatContext *s, ByteIOContext *pb, int taglen, const char *key)
88
{
89
    char *q, dst[512];
90
    const char *val = NULL;
91
    int len, dstlen = sizeof(dst) - 1;
92
    unsigned genre;
93
    unsigned int (*get)(ByteIOContext*) = get_be16;
94

    
95
    dst[0] = 0;
96
    if (taglen < 1)
97
        return;
98

    
99
    taglen--; /* account for encoding type byte */
100

    
101
    switch (get_byte(pb)) { /* encoding type */
102

    
103
    case 0:  /* ISO-8859-1 (0 - 255 maps directly into unicode) */
104
        q = dst;
105
        while (taglen-- && q - dst < dstlen - 7) {
106
            uint8_t tmp;
107
            PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;)
108
        }
109
        *q = 0;
110
        break;
111

    
112
    case 1:  /* UTF-16 with BOM */
113
        taglen -= 2;
114
        switch (get_be16(pb)) {
115
        case 0xfffe:
116
            get = get_le16;
117
        case 0xfeff:
118
            break;
119
        default:
120
            av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
121
            return;
122
        }
123
        // fall-through
124

    
125
    case 2:  /* UTF-16BE without BOM */
126
        q = dst;
127
        while (taglen > 1 && q - dst < dstlen - 7) {
128
            uint32_t ch;
129
            uint8_t tmp;
130

    
131
            GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
132
            PUT_UTF8(ch, tmp, *q++ = tmp;)
133
        }
134
        *q = 0;
135
        break;
136

    
137
    case 3:  /* UTF-8 */
138
        len = FFMIN(taglen, dstlen);
139
        get_buffer(pb, dst, len);
140
        dst[len] = 0;
141
        break;
142
    default:
143
        av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
144
    }
145

    
146
    if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
147
        && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
148
        && genre <= ID3v1_GENRE_MAX)
149
        val = ff_id3v1_genre_str[genre];
150
    else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
151
        /* dst now contains two 0-terminated strings */
152
        dst[dstlen] = 0;
153
        len = strlen(dst);
154
        key = dst;
155
        val = dst + FFMIN(len + 1, dstlen);
156
    }
157
    else if (*dst)
158
        val = dst;
159

    
160
    if (val)
161
        av_metadata_set2(&s->metadata, key, val, 0);
162
}
163

    
164
void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
165
{
166
    int isv34, tlen, unsync;
167
    char tag[5];
168
    int64_t next;
169
    int taghdrlen;
170
    const char *reason;
171
    ByteIOContext pb;
172
    unsigned char *buffer = NULL;
173
    int buffer_size = 0;
174

    
175
    switch (version) {
176
    case 2:
177
        if (flags & 0x40) {
178
            reason = "compression";
179
            goto error;
180
        }
181
        isv34 = 0;
182
        taghdrlen = 6;
183
        break;
184

    
185
    case 3:
186
    case 4:
187
        isv34 = 1;
188
        taghdrlen = 10;
189
        break;
190

    
191
    default:
192
        reason = "version";
193
        goto error;
194
    }
195

    
196
    unsync = flags & 0x80;
197

    
198
    if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
199
        url_fskip(s->pb, get_size(s->pb, 4));
200

    
201
    while (len >= taghdrlen) {
202
        unsigned int tflags;
203
        int tunsync = 0;
204

    
205
        if (isv34) {
206
            get_buffer(s->pb, tag, 4);
207
            tag[4] = 0;
208
            if(version==3){
209
                tlen = get_be32(s->pb);
210
            }else
211
                tlen = get_size(s->pb, 4);
212
            tflags = get_be16(s->pb);
213
            tunsync = tflags & 0x02;
214
        } else {
215
            get_buffer(s->pb, tag, 3);
216
            tag[3] = 0;
217
            tlen = get_be24(s->pb);
218
        }
219
        len -= taghdrlen + tlen;
220

    
221
        if (len < 0)
222
            break;
223

    
224
        next = url_ftell(s->pb) + tlen;
225

    
226
        if (tag[0] == 'T') {
227
            if (unsync || tunsync) {
228
                int i, j;
229
                av_fast_malloc(&buffer, &buffer_size, tlen);
230
                for (i = 0, j = 0; i < tlen; i++, j++) {
231
                    buffer[j] = get_byte(s->pb);
232
                    if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
233
                        /* Unsynchronised byte, skip it */
234
                        j--;
235
                    }
236
                }
237
                init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
238
                read_ttag(s, &pb, j, tag);
239
            } else {
240
                read_ttag(s, s->pb, tlen, tag);
241
            }
242
        }
243
        else if (!tag[0]) {
244
            if (tag[1])
245
                av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
246
            url_fskip(s->pb, tlen);
247
            break;
248
        }
249
        /* Skip to end of tag */
250
        url_fseek(s->pb, next, SEEK_SET);
251
    }
252

    
253
    if (len > 0) {
254
        /* Skip padding */
255
        url_fskip(s->pb, len);
256
    }
257
    if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
258
        url_fskip(s->pb, 10);
259

    
260
    av_free(buffer);
261
    return;
262

    
263
  error:
264
    av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
265
    url_fskip(s->pb, len);
266
    av_free(buffer);
267
}
268

    
269
const AVMetadataConv ff_id3v2_metadata_conv[] = {
270
    { "TALB", "album"},
271
    { "TAL",  "album"},
272
    { "TCOM", "composer"},
273
    { "TCON", "genre"},
274
    { "TCO",  "genre"},
275
    { "TCOP", "copyright"},
276
    { "TDRL", "date"},
277
    { "TDRC", "date"},
278
    { "TENC", "encoded_by"},
279
    { "TEN",  "encoded_by"},
280
    { "TIT2", "title"},
281
    { "TT2",  "title"},
282
    { "TLAN", "language"},
283
    { "TPE1", "artist"},
284
    { "TP1",  "artist"},
285
    { "TPE2", "album_artist"},
286
    { "TP2",  "album_artist"},
287
    { "TPE3", "performer"},
288
    { "TP3",  "performer"},
289
    { "TPOS", "disc"},
290
    { "TPUB", "publisher"},
291
    { "TRCK", "track"},
292
    { "TRK",  "track"},
293
    { "TSOA", "album-sort"},
294
    { "TSOP", "artist-sort"},
295
    { "TSOT", "title-sort"},
296
    { "TSSE", "encoder"},
297
    { 0 }
298
};
299

    
300
const char ff_id3v2_tags[][4] = {
301
   "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC",
302
   "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3",
303
   "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY",
304
   "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB",
305
   "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST",
306
   { 0 },
307
};