Statistics
| Branch: | Revision:

ffmpeg / libavformat / id3v2.c @ 03700d39

History | View | Annotate | Download (8.53 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
#include "metadata.h"
27

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
197
    unsync = flags & 0x80;
198

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

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

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

    
222
        if (len < 0)
223
            break;
224

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

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

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

    
262
    av_free(buffer);
263
    return;
264

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

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

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