00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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--;
00092
00093 switch (get_byte(s->pb)) {
00094
00095 case 0:
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:
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
00116
00117 case 2:
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:
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
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) {
00191 int extlen = get_size(s->pb, 4);
00192 if (version == 4)
00193 extlen -= 4;
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);
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
00232 url_fseek(s->pb, next, SEEK_SET);
00233 }
00234
00235 if (version == 4 && flags & 0x10)
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 };