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 #include "libavutil/intreadwrite.h"
00026 #include "metadata.h"
00027 #include "avio_internal.h"
00028
00029 int ff_id3v2_match(const uint8_t *buf, const char * magic)
00030 {
00031 return buf[0] == magic[0] &&
00032 buf[1] == magic[1] &&
00033 buf[2] == magic[2] &&
00034 buf[3] != 0xff &&
00035 buf[4] != 0xff &&
00036 (buf[6] & 0x80) == 0 &&
00037 (buf[7] & 0x80) == 0 &&
00038 (buf[8] & 0x80) == 0 &&
00039 (buf[9] & 0x80) == 0;
00040 }
00041
00042 int ff_id3v2_tag_len(const uint8_t * buf)
00043 {
00044 int len = ((buf[6] & 0x7f) << 21) +
00045 ((buf[7] & 0x7f) << 14) +
00046 ((buf[8] & 0x7f) << 7) +
00047 (buf[9] & 0x7f) +
00048 ID3v2_HEADER_SIZE;
00049 if (buf[5] & 0x10)
00050 len += ID3v2_HEADER_SIZE;
00051 return len;
00052 }
00053
00054 static unsigned int get_size(AVIOContext *s, int len)
00055 {
00056 int v = 0;
00057 while (len--)
00058 v = (v << 7) + (avio_r8(s) & 0x7F);
00059 return v;
00060 }
00061
00062 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
00063 {
00064 char *q, dst[512];
00065 const char *val = NULL;
00066 int len, dstlen = sizeof(dst) - 1;
00067 unsigned genre;
00068 unsigned int (*get)(AVIOContext*) = avio_rb16;
00069
00070 dst[0] = 0;
00071 if (taglen < 1)
00072 return;
00073
00074 taglen--;
00075
00076 switch (avio_r8(pb)) {
00077
00078 case ID3v2_ENCODING_ISO8859:
00079 q = dst;
00080 while (taglen-- && q - dst < dstlen - 7) {
00081 uint8_t tmp;
00082 PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
00083 }
00084 *q = 0;
00085 break;
00086
00087 case ID3v2_ENCODING_UTF16BOM:
00088 taglen -= 2;
00089 switch (avio_rb16(pb)) {
00090 case 0xfffe:
00091 get = avio_rl16;
00092 case 0xfeff:
00093 break;
00094 default:
00095 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
00096 return;
00097 }
00098
00099
00100 case ID3v2_ENCODING_UTF16BE:
00101 q = dst;
00102 while (taglen > 1 && q - dst < dstlen - 7) {
00103 uint32_t ch;
00104 uint8_t tmp;
00105
00106 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
00107 PUT_UTF8(ch, tmp, *q++ = tmp;)
00108 }
00109 *q = 0;
00110 break;
00111
00112 case ID3v2_ENCODING_UTF8:
00113 len = FFMIN(taglen, dstlen);
00114 avio_read(pb, dst, len);
00115 dst[len] = 0;
00116 break;
00117 default:
00118 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
00119 }
00120
00121 if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
00122 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
00123 && genre <= ID3v1_GENRE_MAX)
00124 val = ff_id3v1_genre_str[genre];
00125 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
00126
00127 dst[dstlen] = 0;
00128 len = strlen(dst);
00129 key = dst;
00130 val = dst + FFMIN(len + 1, dstlen);
00131 }
00132 else if (*dst)
00133 val = dst;
00134
00135 if (val)
00136 av_metadata_set2(&s->metadata, key, val, AV_METADATA_DONT_OVERWRITE);
00137 }
00138
00139 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
00140 {
00141 int isv34, tlen, unsync;
00142 char tag[5];
00143 int64_t next;
00144 int taghdrlen;
00145 const char *reason;
00146 AVIOContext pb;
00147 unsigned char *buffer = NULL;
00148 int buffer_size = 0;
00149
00150 switch (version) {
00151 case 2:
00152 if (flags & 0x40) {
00153 reason = "compression";
00154 goto error;
00155 }
00156 isv34 = 0;
00157 taghdrlen = 6;
00158 break;
00159
00160 case 3:
00161 case 4:
00162 isv34 = 1;
00163 taghdrlen = 10;
00164 break;
00165
00166 default:
00167 reason = "version";
00168 goto error;
00169 }
00170
00171 unsync = flags & 0x80;
00172
00173 if (isv34 && flags & 0x40)
00174 avio_seek(s->pb, get_size(s->pb, 4), SEEK_CUR);
00175
00176 while (len >= taghdrlen) {
00177 unsigned int tflags;
00178 int tunsync = 0;
00179
00180 if (isv34) {
00181 avio_read(s->pb, tag, 4);
00182 tag[4] = 0;
00183 if(version==3){
00184 tlen = avio_rb32(s->pb);
00185 }else
00186 tlen = get_size(s->pb, 4);
00187 tflags = avio_rb16(s->pb);
00188 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
00189 } else {
00190 avio_read(s->pb, tag, 3);
00191 tag[3] = 0;
00192 tlen = avio_rb24(s->pb);
00193 }
00194 len -= taghdrlen + tlen;
00195
00196 if (len < 0)
00197 break;
00198
00199 next = avio_tell(s->pb) + tlen;
00200
00201 if (tflags & ID3v2_FLAG_DATALEN) {
00202 avio_rb32(s->pb);
00203 tlen -= 4;
00204 }
00205
00206 if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
00207 av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
00208 avio_seek(s->pb, tlen, SEEK_CUR);
00209 } else if (tag[0] == 'T') {
00210 if (unsync || tunsync) {
00211 int i, j;
00212 av_fast_malloc(&buffer, &buffer_size, tlen);
00213 for (i = 0, j = 0; i < tlen; i++, j++) {
00214 buffer[j] = avio_r8(s->pb);
00215 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
00216
00217 j--;
00218 }
00219 }
00220 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
00221 read_ttag(s, &pb, j, tag);
00222 } else {
00223 read_ttag(s, s->pb, tlen, tag);
00224 }
00225 }
00226 else if (!tag[0]) {
00227 if (tag[1])
00228 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
00229 avio_seek(s->pb, tlen, SEEK_CUR);
00230 break;
00231 }
00232
00233 avio_seek(s->pb, next, SEEK_SET);
00234 }
00235
00236 if (len > 0) {
00237
00238 avio_seek(s->pb, len, SEEK_CUR);
00239 }
00240 if (version == 4 && flags & 0x10)
00241 avio_seek(s->pb, 10, SEEK_CUR);
00242
00243 av_free(buffer);
00244 return;
00245
00246 error:
00247 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
00248 avio_seek(s->pb, len, SEEK_CUR);
00249 av_free(buffer);
00250 }
00251
00252 void ff_id3v2_read(AVFormatContext *s, const char *magic)
00253 {
00254 int len, ret;
00255 uint8_t buf[ID3v2_HEADER_SIZE];
00256 int found_header;
00257 int64_t off;
00258
00259 do {
00260
00261 off = avio_tell(s->pb);
00262 ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
00263 if (ret != ID3v2_HEADER_SIZE)
00264 break;
00265 found_header = ff_id3v2_match(buf, magic);
00266 if (found_header) {
00267
00268 len = ((buf[6] & 0x7f) << 21) |
00269 ((buf[7] & 0x7f) << 14) |
00270 ((buf[8] & 0x7f) << 7) |
00271 (buf[9] & 0x7f);
00272 ff_id3v2_parse(s, len, buf[3], buf[5]);
00273 } else {
00274 avio_seek(s->pb, off, SEEK_SET);
00275 }
00276 } while (found_header);
00277 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
00278 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
00279 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
00280 }
00281
00282 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
00283 { "TALB", "album"},
00284 { "TCOM", "composer"},
00285 { "TCON", "genre"},
00286 { "TCOP", "copyright"},
00287 { "TENC", "encoded_by"},
00288 { "TIT2", "title"},
00289 { "TLAN", "language"},
00290 { "TPE1", "artist"},
00291 { "TPE2", "album_artist"},
00292 { "TPE3", "performer"},
00293 { "TPOS", "disc"},
00294 { "TPUB", "publisher"},
00295 { "TRCK", "track"},
00296 { "TSSE", "encoder"},
00297 { 0 }
00298 };
00299
00300 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
00301 { "TDRL", "date"},
00302 { "TDRC", "date"},
00303 { "TDEN", "creation_time"},
00304 { "TSOA", "album-sort"},
00305 { "TSOP", "artist-sort"},
00306 { "TSOT", "title-sort"},
00307 { 0 }
00308 };
00309
00310 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
00311 { "TAL", "album"},
00312 { "TCO", "genre"},
00313 { "TT2", "title"},
00314 { "TEN", "encoded_by"},
00315 { "TP1", "artist"},
00316 { "TP2", "album_artist"},
00317 { "TP3", "performer"},
00318 { "TRK", "track"},
00319 { 0 }
00320 };
00321
00322
00323 const char ff_id3v2_tags[][4] = {
00324 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
00325 "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
00326 "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
00327 "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
00328 { 0 },
00329 };
00330
00331 const char ff_id3v2_4_tags[][4] = {
00332 "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
00333 "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
00334 { 0 },
00335 };
00336
00337 const char ff_id3v2_3_tags[][4] = {
00338 "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
00339 { 0 },
00340 };