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 url_fskip(s->pb, get_size(s->pb, 4)); 00192 00193 while (len >= taghdrlen) { 00194 if (isv34) { 00195 get_buffer(s->pb, tag, 4); 00196 tag[4] = 0; 00197 if(version==3){ 00198 tlen = get_be32(s->pb); 00199 }else 00200 tlen = get_size(s->pb, 4); 00201 get_be16(s->pb); /* flags */ 00202 } else { 00203 get_buffer(s->pb, tag, 3); 00204 tag[3] = 0; 00205 tlen = get_be24(s->pb); 00206 } 00207 len -= taghdrlen + tlen; 00208 00209 if (len < 0) 00210 break; 00211 00212 next = url_ftell(s->pb) + tlen; 00213 00214 if (tag[0] == 'T') 00215 read_ttag(s, tlen, tag); 00216 else if (!tag[0]) { 00217 if (tag[1]) 00218 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); 00219 url_fskip(s->pb, len); 00220 break; 00221 } 00222 /* Skip to end of tag */ 00223 url_fseek(s->pb, next, SEEK_SET); 00224 } 00225 00226 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ 00227 url_fskip(s->pb, 10); 00228 return; 00229 00230 error: 00231 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); 00232 url_fskip(s->pb, len); 00233 } 00234 00235 const AVMetadataConv ff_id3v2_metadata_conv[] = { 00236 { "TALB", "album"}, 00237 { "TAL", "album"}, 00238 { "TCOM", "composer"}, 00239 { "TCON", "genre"}, 00240 { "TCO", "genre"}, 00241 { "TCOP", "copyright"}, 00242 { "TDRL", "date"}, 00243 { "TDRC", "date"}, 00244 { "TENC", "encoded_by"}, 00245 { "TEN", "encoded_by"}, 00246 { "TIT2", "title"}, 00247 { "TT2", "title"}, 00248 { "TLAN", "language"}, 00249 { "TPE1", "artist"}, 00250 { "TP1", "artist"}, 00251 { "TPE2", "album_artist"}, 00252 { "TP2", "album_artist"}, 00253 { "TPE3", "performer"}, 00254 { "TP3", "performer"}, 00255 { "TPOS", "disc"}, 00256 { "TPUB", "publisher"}, 00257 { "TRCK", "track"}, 00258 { "TRK", "track"}, 00259 { "TSOA", "album-sort"}, 00260 { "TSOP", "artist-sort"}, 00261 { "TSOT", "title-sort"}, 00262 { "TSSE", "encoder"}, 00263 { 0 } 00264 }; 00265 00266 const char ff_id3v2_tags[][4] = { 00267 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", 00268 "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", 00269 "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", 00270 "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", 00271 "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", 00272 { 0 }, 00273 };