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 26int ff_id3v2_match(const uint8_t *buf) 27{ 28 return buf[0] == 'I' && 29 buf[1] == 'D' && 30 buf[2] == '3' && 31 buf[3] != 0xff && 32 buf[4] != 0xff && 33 (buf[6] & 0x80) == 0 && 34 (buf[7] & 0x80) == 0 && 35 (buf[8] & 0x80) == 0 && 36 (buf[9] & 0x80) == 0; 37} 38 39int ff_id3v2_tag_len(const uint8_t * buf) 40{ 41 int len = ((buf[6] & 0x7f) << 21) + 42 ((buf[7] & 0x7f) << 14) + 43 ((buf[8] & 0x7f) << 7) + 44 (buf[9] & 0x7f) + 45 ID3v2_HEADER_SIZE; 46 if (buf[5] & 0x10) 47 len += ID3v2_HEADER_SIZE; 48 return len; 49} 50 51void ff_id3v2_read(AVFormatContext *s) 52{ 53 int len, ret; 54 uint8_t buf[ID3v2_HEADER_SIZE]; 55 56 ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE); 57 if (ret != ID3v2_HEADER_SIZE) 58 return; 59 if (ff_id3v2_match(buf)) { 60 /* parse ID3v2 header */ 61 len = ((buf[6] & 0x7f) << 21) | 62 ((buf[7] & 0x7f) << 14) | 63 ((buf[8] & 0x7f) << 7) | 64 (buf[9] & 0x7f); 65 ff_id3v2_parse(s, len, buf[3], buf[5]); 66 } else { 67 url_fseek(s->pb, 0, SEEK_SET); 68 } 69} 70 71static unsigned int get_size(ByteIOContext *s, int len) 72{ 73 int v = 0; 74 while (len--) 75 v = (v << 7) + (get_byte(s) & 0x7F); 76 return v; 77} 78 79static void read_ttag(AVFormatContext *s, int taglen, const char *key) 80{ 81 char *q, dst[512]; 82 const char *val = NULL; 83 int len, dstlen = sizeof(dst) - 1; 84 unsigned genre; 85 unsigned int (*get)(ByteIOContext*) = get_be16; 86 87 dst[0] = 0; 88 if (taglen < 1) 89 return; 90 91 taglen--; /* account for encoding type byte */ 92 93 switch (get_byte(s->pb)) { /* encoding type */ 94 95 case 0: /* ISO-8859-1 (0 - 255 maps directly into unicode) */ 96 q = dst; 97 while (taglen-- && q - dst < dstlen - 7) { 98 uint8_t tmp; 99 PUT_UTF8(get_byte(s->pb), tmp, *q++ = tmp;) 100 } 101 *q = 0; 102 break; 103 104 case 1: /* UTF-16 with BOM */ 105 taglen -= 2; 106 switch (get_be16(s->pb)) { 107 case 0xfffe: 108 get = get_le16; 109 case 0xfeff: 110 break; 111 default: 112 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key); 113 return; 114 } 115 // fall-through 116 117 case 2: /* UTF-16BE without BOM */ 118 q = dst; 119 while (taglen > 1 && q - dst < dstlen - 7) { 120 uint32_t ch; 121 uint8_t tmp; 122 123 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(s->pb) : 0), break;) 124 PUT_UTF8(ch, tmp, *q++ = tmp;) 125 } 126 *q = 0; 127 break; 128 129 case 3: /* UTF-8 */ 130 len = FFMIN(taglen, dstlen); 131 get_buffer(s->pb, dst, len); 132 dst[len] = 0; 133 break; 134 default: 135 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key); 136 } 137 138 if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) 139 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) 140 && genre <= ID3v1_GENRE_MAX) 141 val = ff_id3v1_genre_str[genre]; 142 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { 143 /* dst now contains two 0-terminated strings */ 144 dst[dstlen] = 0; 145 len = strlen(dst); 146 key = dst; 147 val = dst + FFMIN(len + 1, dstlen); 148 } 149 else if (*dst) 150 val = dst; 151 152 if (val) 153 av_metadata_set2(&s->metadata, key, val, 0); 154} 155 156void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) 157{ 158 int isv34, tlen; 159 char tag[5]; 160 int64_t next; 161 int taghdrlen; 162 const char *reason; 163 164 switch (version) { 165 case 2: 166 if (flags & 0x40) { 167 reason = "compression"; 168 goto error; 169 } 170 isv34 = 0; 171 taghdrlen = 6; 172 break; 173 174 case 3: 175 case 4: 176 isv34 = 1; 177 taghdrlen = 10; 178 break; 179 180 default: 181 reason = "version"; 182 goto error; 183 } 184 185 if (flags & 0x80) { 186 reason = "unsynchronization"; 187 goto error; 188 } 189 190 if (isv34 && flags & 0x40) /* Extended header present, just skip over it */ 191 url_fskip(s->pb, get_size(s->pb, 4)); 192 193 while (len >= taghdrlen) { 194 if (isv34) { 195 get_buffer(s->pb, tag, 4); 196 tag[4] = 0; 197 if(version==3){ 198 tlen = get_be32(s->pb); 199 }else 200 tlen = get_size(s->pb, 4); 201 get_be16(s->pb); /* flags */ 202 } else { 203 get_buffer(s->pb, tag, 3); 204 tag[3] = 0; 205 tlen = get_be24(s->pb); 206 } 207 len -= taghdrlen + tlen; 208 209 if (len < 0) 210 break; 211 212 next = url_ftell(s->pb) + tlen; 213 214 if (tag[0] == 'T') 215 read_ttag(s, tlen, tag); 216 else if (!tag[0]) { 217 if (tag[1]) 218 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); 219 url_fskip(s->pb, len); 220 break; 221 } 222 /* Skip to end of tag */ 223 url_fseek(s->pb, next, SEEK_SET); 224 } 225 226 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ 227 url_fskip(s->pb, 10); 228 return; 229 230 error: 231 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); 232 url_fskip(s->pb, len); 233} 234 235const AVMetadataConv ff_id3v2_metadata_conv[] = { 236 { "TALB", "album"}, 237 { "TAL", "album"}, 238 { "TCOM", "composer"}, 239 { "TCON", "genre"}, 240 { "TCO", "genre"}, 241 { "TCOP", "copyright"}, 242 { "TDRL", "date"}, 243 { "TDRC", "date"}, 244 { "TENC", "encoded_by"}, 245 { "TEN", "encoded_by"}, 246 { "TIT2", "title"}, 247 { "TT2", "title"}, 248 { "TLAN", "language"}, 249 { "TPE1", "artist"}, 250 { "TP1", "artist"}, 251 { "TPE2", "album_artist"}, 252 { "TP2", "album_artist"}, 253 { "TPE3", "performer"}, 254 { "TP3", "performer"}, 255 { "TPOS", "disc"}, 256 { "TPUB", "publisher"}, 257 { "TRCK", "track"}, 258 { "TRK", "track"}, 259 { "TSOA", "album-sort"}, 260 { "TSOP", "artist-sort"}, 261 { "TSOT", "title-sort"}, 262 { "TSSE", "encoder"}, 263 { 0 } 264}; 265 266const char ff_id3v2_tags[][4] = { 267 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", 268 "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", 269 "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", 270 "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", 271 "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", 272 { 0 }, 273}; 274