Libav
|
00001 /* 00002 * ID3v2 header parser 00003 * Copyright (c) 2003 Fabrice Bellard 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "id3v2.h" 00023 #include "id3v1.h" 00024 #include "libavutil/avstring.h" 00025 00026 int ff_id3v2_match(const uint8_t *buf) 00027 { 00028 return buf[0] == 'I' && 00029 buf[1] == 'D' && 00030 buf[2] == '3' && 00031 buf[3] != 0xff && 00032 buf[4] != 0xff && 00033 (buf[6] & 0x80) == 0 && 00034 (buf[7] & 0x80) == 0 && 00035 (buf[8] & 0x80) == 0 && 00036 (buf[9] & 0x80) == 0; 00037 } 00038 00039 int ff_id3v2_tag_len(const uint8_t * buf) 00040 { 00041 int len = ((buf[6] & 0x7f) << 21) + 00042 ((buf[7] & 0x7f) << 14) + 00043 ((buf[8] & 0x7f) << 7) + 00044 (buf[9] & 0x7f) + 00045 ID3v2_HEADER_SIZE; 00046 if (buf[5] & 0x10) 00047 len += ID3v2_HEADER_SIZE; 00048 return len; 00049 } 00050 00051 void ff_id3v2_read(AVFormatContext *s) 00052 { 00053 int len, ret; 00054 uint8_t buf[ID3v2_HEADER_SIZE]; 00055 00056 ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE); 00057 if (ret != ID3v2_HEADER_SIZE) 00058 return; 00059 if (ff_id3v2_match(buf)) { 00060 /* parse ID3v2 header */ 00061 len = ((buf[6] & 0x7f) << 21) | 00062 ((buf[7] & 0x7f) << 14) | 00063 ((buf[8] & 0x7f) << 7) | 00064 (buf[9] & 0x7f); 00065 ff_id3v2_parse(s, len, buf[3], buf[5]); 00066 } else { 00067 url_fseek(s->pb, 0, SEEK_SET); 00068 } 00069 } 00070 00071 static unsigned int get_size(ByteIOContext *s, int len) 00072 { 00073 int v = 0; 00074 while (len--) 00075 v = (v << 7) + (get_byte(s) & 0x7F); 00076 return v; 00077 } 00078 00079 static void read_ttag(AVFormatContext *s, int taglen, const char *key) 00080 { 00081 char *q, dst[512]; 00082 const char *val = NULL; 00083 int len, dstlen = sizeof(dst) - 1; 00084 unsigned genre; 00085 unsigned int (*get)(ByteIOContext*) = get_be16; 00086 00087 dst[0] = 0; 00088 if (taglen < 1) 00089 return; 00090 00091 taglen--; /* account for encoding type byte */ 00092 00093 switch (get_byte(s->pb)) { /* encoding type */ 00094 00095 case 0: /* ISO-8859-1 (0 - 255 maps directly into unicode) */ 00096 q = dst; 00097 while (taglen-- && q - dst < dstlen - 7) { 00098 uint8_t tmp; 00099 PUT_UTF8(get_byte(s->pb), tmp, *q++ = tmp;) 00100 } 00101 *q = 0; 00102 break; 00103 00104 case 1: /* UTF-16 with BOM */ 00105 taglen -= 2; 00106 switch (get_be16(s->pb)) { 00107 case 0xfffe: 00108 get = get_le16; 00109 case 0xfeff: 00110 break; 00111 default: 00112 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key); 00113 return; 00114 } 00115 // fall-through 00116 00117 case 2: /* UTF-16BE without BOM */ 00118 q = dst; 00119 while (taglen > 1 && q - dst < dstlen - 7) { 00120 uint32_t ch; 00121 uint8_t tmp; 00122 00123 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(s->pb) : 0), break;) 00124 PUT_UTF8(ch, tmp, *q++ = tmp;) 00125 } 00126 *q = 0; 00127 break; 00128 00129 case 3: /* UTF-8 */ 00130 len = FFMIN(taglen, dstlen); 00131 get_buffer(s->pb, dst, len); 00132 dst[len] = 0; 00133 break; 00134 default: 00135 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key); 00136 } 00137 00138 if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) 00139 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) 00140 && genre <= ID3v1_GENRE_MAX) 00141 val = ff_id3v1_genre_str[genre]; 00142 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { 00143 /* dst now contains two 0-terminated strings */ 00144 dst[dstlen] = 0; 00145 len = strlen(dst); 00146 key = dst; 00147 val = dst + FFMIN(len + 1, dstlen); 00148 } 00149 else if (*dst) 00150 val = dst; 00151 00152 if (val) 00153 av_metadata_set2(&s->metadata, key, val, 0); 00154 } 00155 00156 void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) 00157 { 00158 int isv34, tlen; 00159 char tag[5]; 00160 int64_t next; 00161 int taghdrlen; 00162 const char *reason; 00163 00164 switch (version) { 00165 case 2: 00166 if (flags & 0x40) { 00167 reason = "compression"; 00168 goto error; 00169 } 00170 isv34 = 0; 00171 taghdrlen = 6; 00172 break; 00173 00174 case 3: 00175 case 4: 00176 isv34 = 1; 00177 taghdrlen = 10; 00178 break; 00179 00180 default: 00181 reason = "version"; 00182 goto error; 00183 } 00184 00185 if (flags & 0x80) { 00186 reason = "unsynchronization"; 00187 goto error; 00188 } 00189 00190 if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */ 00191 int extlen = get_size(s->pb, 4); 00192 if (version == 4) 00193 extlen -= 4; // in v2.4 the length includes the length field we just read 00194 00195 if (extlen < 0) { 00196 reason = "invalid extended header length"; 00197 goto error; 00198 } 00199 url_fskip(s->pb, extlen); 00200 } 00201 00202 while (len >= taghdrlen) { 00203 if (isv34) { 00204 get_buffer(s->pb, tag, 4); 00205 tag[4] = 0; 00206 if(version==3){ 00207 tlen = get_be32(s->pb); 00208 }else 00209 tlen = get_size(s->pb, 4); 00210 get_be16(s->pb); /* flags */ 00211 } else { 00212 get_buffer(s->pb, tag, 3); 00213 tag[3] = 0; 00214 tlen = get_be24(s->pb); 00215 } 00216 len -= taghdrlen + tlen; 00217 00218 if (len < 0) 00219 break; 00220 00221 next = url_ftell(s->pb) + tlen; 00222 00223 if (tag[0] == 'T') 00224 read_ttag(s, tlen, tag); 00225 else if (!tag[0]) { 00226 if (tag[1]) 00227 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); 00228 url_fskip(s->pb, len); 00229 break; 00230 } 00231 /* Skip to end of tag */ 00232 url_fseek(s->pb, next, SEEK_SET); 00233 } 00234 00235 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ 00236 url_fskip(s->pb, 10); 00237 return; 00238 00239 error: 00240 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); 00241 url_fskip(s->pb, len); 00242 } 00243 00244 const AVMetadataConv ff_id3v2_metadata_conv[] = { 00245 { "TALB", "album"}, 00246 { "TAL", "album"}, 00247 { "TCOM", "composer"}, 00248 { "TCON", "genre"}, 00249 { "TCO", "genre"}, 00250 { "TCOP", "copyright"}, 00251 { "TDRL", "date"}, 00252 { "TDRC", "date"}, 00253 { "TENC", "encoded_by"}, 00254 { "TEN", "encoded_by"}, 00255 { "TIT2", "title"}, 00256 { "TT2", "title"}, 00257 { "TLAN", "language"}, 00258 { "TPE1", "artist"}, 00259 { "TP1", "artist"}, 00260 { "TPE2", "album_artist"}, 00261 { "TP2", "album_artist"}, 00262 { "TPE3", "performer"}, 00263 { "TP3", "performer"}, 00264 { "TPOS", "disc"}, 00265 { "TPUB", "publisher"}, 00266 { "TRCK", "track"}, 00267 { "TRK", "track"}, 00268 { "TSOA", "album-sort"}, 00269 { "TSOP", "artist-sort"}, 00270 { "TSOT", "title-sort"}, 00271 { "TSSE", "encoder"}, 00272 { 0 } 00273 }; 00274 00275 const char ff_id3v2_tags[][4] = { 00276 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", 00277 "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", 00278 "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", 00279 "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", 00280 "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", 00281 { 0 }, 00282 };