00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00029 #include "config.h"
00030
00031 #if CONFIG_ZLIB
00032 #include <zlib.h>
00033 #endif
00034
00035 #include "id3v2.h"
00036 #include "id3v1.h"
00037 #include "libavutil/avstring.h"
00038 #include "libavutil/intreadwrite.h"
00039 #include "libavutil/dict.h"
00040 #include "avio_internal.h"
00041 #include "internal.h"
00042
00043 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
00044 { "TALB", "album"},
00045 { "TCOM", "composer"},
00046 { "TCON", "genre"},
00047 { "TCOP", "copyright"},
00048 { "TENC", "encoded_by"},
00049 { "TIT2", "title"},
00050 { "TLAN", "language"},
00051 { "TPE1", "artist"},
00052 { "TPE2", "album_artist"},
00053 { "TPE3", "performer"},
00054 { "TPOS", "disc"},
00055 { "TPUB", "publisher"},
00056 { "TRCK", "track"},
00057 { "TSSE", "encoder"},
00058 { 0 }
00059 };
00060
00061 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
00062 { "TDRL", "date"},
00063 { "TDRC", "date"},
00064 { "TDEN", "creation_time"},
00065 { "TSOA", "album-sort"},
00066 { "TSOP", "artist-sort"},
00067 { "TSOT", "title-sort"},
00068 { 0 }
00069 };
00070
00071 static const AVMetadataConv id3v2_2_metadata_conv[] = {
00072 { "TAL", "album"},
00073 { "TCO", "genre"},
00074 { "TT2", "title"},
00075 { "TEN", "encoded_by"},
00076 { "TP1", "artist"},
00077 { "TP2", "album_artist"},
00078 { "TP3", "performer"},
00079 { "TRK", "track"},
00080 { 0 }
00081 };
00082
00083
00084 const char ff_id3v2_tags[][4] = {
00085 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
00086 "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
00087 "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
00088 "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
00089 { 0 },
00090 };
00091
00092 const char ff_id3v2_4_tags[][4] = {
00093 "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
00094 "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
00095 { 0 },
00096 };
00097
00098 const char ff_id3v2_3_tags[][4] = {
00099 "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
00100 { 0 },
00101 };
00102
00103 const char *ff_id3v2_picture_types[21] = {
00104 "Other",
00105 "32x32 pixels 'file icon'",
00106 "Other file icon",
00107 "Cover (front)",
00108 "Cover (back)",
00109 "Leaflet page",
00110 "Media (e.g. label side of CD)",
00111 "Lead artist/lead performer/soloist",
00112 "Artist/performer",
00113 "Conductor",
00114 "Band/Orchestra",
00115 "Composer",
00116 "Lyricist/text writer",
00117 "Recording Location",
00118 "During recording",
00119 "During performance",
00120 "Movie/video screen capture",
00121 "A bright coloured fish",
00122 "Illustration",
00123 "Band/artist logotype",
00124 "Publisher/Studio logotype",
00125 };
00126
00127 const CodecMime ff_id3v2_mime_tags[] = {
00128 {"image/gif" , AV_CODEC_ID_GIF},
00129 {"image/jpeg", AV_CODEC_ID_MJPEG},
00130 {"image/jpg", AV_CODEC_ID_MJPEG},
00131 {"image/png" , AV_CODEC_ID_PNG},
00132 {"image/tiff", AV_CODEC_ID_TIFF},
00133 {"image/bmp", AV_CODEC_ID_BMP},
00134 {"JPG", AV_CODEC_ID_MJPEG},
00135 {"PNG" , AV_CODEC_ID_PNG},
00136 {"", AV_CODEC_ID_NONE},
00137 };
00138
00139 int ff_id3v2_match(const uint8_t *buf, const char * magic)
00140 {
00141 return buf[0] == magic[0] &&
00142 buf[1] == magic[1] &&
00143 buf[2] == magic[2] &&
00144 buf[3] != 0xff &&
00145 buf[4] != 0xff &&
00146 (buf[6] & 0x80) == 0 &&
00147 (buf[7] & 0x80) == 0 &&
00148 (buf[8] & 0x80) == 0 &&
00149 (buf[9] & 0x80) == 0;
00150 }
00151
00152 int ff_id3v2_tag_len(const uint8_t * buf)
00153 {
00154 int len = ((buf[6] & 0x7f) << 21) +
00155 ((buf[7] & 0x7f) << 14) +
00156 ((buf[8] & 0x7f) << 7) +
00157 (buf[9] & 0x7f) +
00158 ID3v2_HEADER_SIZE;
00159 if (buf[5] & 0x10)
00160 len += ID3v2_HEADER_SIZE;
00161 return len;
00162 }
00163
00164 static unsigned int get_size(AVIOContext *s, int len)
00165 {
00166 int v = 0;
00167 while (len--)
00168 v = (v << 7) + (avio_r8(s) & 0x7F);
00169 return v;
00170 }
00171
00175 static void free_geobtag(void *obj)
00176 {
00177 ID3v2ExtraMetaGEOB *geob = obj;
00178 av_free(geob->mime_type);
00179 av_free(geob->file_name);
00180 av_free(geob->description);
00181 av_free(geob->data);
00182 av_free(geob);
00183 }
00184
00197 static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
00198 uint8_t **dst, int *maxread)
00199 {
00200 int ret;
00201 uint8_t tmp;
00202 uint32_t ch = 1;
00203 int left = *maxread;
00204 unsigned int (*get)(AVIOContext*) = avio_rb16;
00205 AVIOContext *dynbuf;
00206
00207 if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) {
00208 av_log(s, AV_LOG_ERROR, "Error opening memory stream\n");
00209 return ret;
00210 }
00211
00212 switch (encoding) {
00213
00214 case ID3v2_ENCODING_ISO8859:
00215 while (left && ch) {
00216 ch = avio_r8(pb);
00217 PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
00218 left--;
00219 }
00220 break;
00221
00222 case ID3v2_ENCODING_UTF16BOM:
00223 if ((left -= 2) < 0) {
00224 av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n");
00225 avio_close_dyn_buf(dynbuf, dst);
00226 av_freep(dst);
00227 return AVERROR_INVALIDDATA;
00228 }
00229 switch (avio_rb16(pb)) {
00230 case 0xfffe:
00231 get = avio_rl16;
00232 case 0xfeff:
00233 break;
00234 default:
00235 av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n");
00236 avio_close_dyn_buf(dynbuf, dst);
00237 av_freep(dst);
00238 *maxread = left;
00239 return AVERROR_INVALIDDATA;
00240 }
00241
00242
00243 case ID3v2_ENCODING_UTF16BE:
00244 while ((left > 1) && ch) {
00245 GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
00246 PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
00247 }
00248 if (left < 0)
00249 left += 2;
00250 break;
00251
00252 case ID3v2_ENCODING_UTF8:
00253 while (left && ch) {
00254 ch = avio_r8(pb);
00255 avio_w8(dynbuf, ch);
00256 left--;
00257 }
00258 break;
00259 default:
00260 av_log(s, AV_LOG_WARNING, "Unknown encoding\n");
00261 }
00262
00263 if (ch)
00264 avio_w8(dynbuf, 0);
00265
00266 avio_close_dyn_buf(dynbuf, dst);
00267 *maxread = left;
00268
00269 return 0;
00270 }
00271
00275 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
00276 {
00277 uint8_t *dst;
00278 int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
00279 unsigned genre;
00280
00281 if (taglen < 1)
00282 return;
00283
00284 encoding = avio_r8(pb);
00285 taglen--;
00286
00287 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
00288 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
00289 return;
00290 }
00291
00292 if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
00293 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
00294 && genre <= ID3v1_GENRE_MAX) {
00295 av_freep(&dst);
00296 dst = av_strdup(ff_id3v1_genre_str[genre]);
00297 } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
00298
00299 key = dst;
00300 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
00301 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
00302 av_freep(&key);
00303 return;
00304 }
00305 dict_flags |= AV_DICT_DONT_STRDUP_KEY;
00306 } else if (!*dst)
00307 av_freep(&dst);
00308
00309 if (dst)
00310 av_dict_set(&s->metadata, key, dst, dict_flags);
00311 }
00312
00316 static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta)
00317 {
00318 ID3v2ExtraMetaGEOB *geob_data = NULL;
00319 ID3v2ExtraMeta *new_extra = NULL;
00320 char encoding;
00321 unsigned int len;
00322
00323 if (taglen < 1)
00324 return;
00325
00326 geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
00327 if (!geob_data) {
00328 av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMetaGEOB));
00329 return;
00330 }
00331
00332 new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
00333 if (!new_extra) {
00334 av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMeta));
00335 goto fail;
00336 }
00337
00338
00339 encoding = avio_r8(pb);
00340 taglen--;
00341
00342
00343 if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, &taglen) < 0
00344 || taglen <= 0)
00345 goto fail;
00346
00347
00348 if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0
00349 || taglen <= 0)
00350 goto fail;
00351
00352
00353 if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0
00354 || taglen < 0)
00355 goto fail;
00356
00357 if (taglen) {
00358
00359 geob_data->data = av_malloc(taglen);
00360 if (!geob_data->data) {
00361 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen);
00362 goto fail;
00363 }
00364 if ((len = avio_read(pb, geob_data->data, taglen)) < taglen)
00365 av_log(s, AV_LOG_WARNING, "Error reading GEOB frame, data truncated.\n");
00366 geob_data->datasize = len;
00367 } else {
00368 geob_data->data = NULL;
00369 geob_data->datasize = 0;
00370 }
00371
00372
00373 new_extra->tag = "GEOB";
00374 new_extra->data = geob_data;
00375 new_extra->next = *extra_meta;
00376 *extra_meta = new_extra;
00377
00378 return;
00379
00380 fail:
00381 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag);
00382 free_geobtag(geob_data);
00383 av_free(new_extra);
00384 return;
00385 }
00386
00387 static int is_number(const char *str)
00388 {
00389 while (*str >= '0' && *str <= '9') str++;
00390 return !*str;
00391 }
00392
00393 static AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag)
00394 {
00395 AVDictionaryEntry *t;
00396 if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
00397 strlen(t->value) == 4 && is_number(t->value))
00398 return t;
00399 return NULL;
00400 }
00401
00402 static void merge_date(AVDictionary **m)
00403 {
00404 AVDictionaryEntry *t;
00405 char date[17] = {0};
00406
00407 if (!(t = get_date_tag(*m, "TYER")) &&
00408 !(t = get_date_tag(*m, "TYE")))
00409 return;
00410 av_strlcpy(date, t->value, 5);
00411 av_dict_set(m, "TYER", NULL, 0);
00412 av_dict_set(m, "TYE", NULL, 0);
00413
00414 if (!(t = get_date_tag(*m, "TDAT")) &&
00415 !(t = get_date_tag(*m, "TDA")))
00416 goto finish;
00417 snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
00418 av_dict_set(m, "TDAT", NULL, 0);
00419 av_dict_set(m, "TDA", NULL, 0);
00420
00421 if (!(t = get_date_tag(*m, "TIME")) &&
00422 !(t = get_date_tag(*m, "TIM")))
00423 goto finish;
00424 snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
00425 av_dict_set(m, "TIME", NULL, 0);
00426 av_dict_set(m, "TIM", NULL, 0);
00427
00428 finish:
00429 if (date[0])
00430 av_dict_set(m, "date", date, 0);
00431 }
00432
00433 static void free_apic(void *obj)
00434 {
00435 ID3v2ExtraMetaAPIC *apic = obj;
00436 av_freep(&apic->data);
00437 av_freep(&apic->description);
00438 av_freep(&apic);
00439 }
00440
00441 static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta)
00442 {
00443 int enc, pic_type;
00444 char mimetype[64];
00445 const CodecMime *mime = ff_id3v2_mime_tags;
00446 enum AVCodecID id = AV_CODEC_ID_NONE;
00447 ID3v2ExtraMetaAPIC *apic = NULL;
00448 ID3v2ExtraMeta *new_extra = NULL;
00449 int64_t end = avio_tell(pb) + taglen;
00450
00451 if (taglen <= 4)
00452 goto fail;
00453
00454 new_extra = av_mallocz(sizeof(*new_extra));
00455 apic = av_mallocz(sizeof(*apic));
00456 if (!new_extra || !apic)
00457 goto fail;
00458
00459 enc = avio_r8(pb);
00460 taglen--;
00461
00462
00463 taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype));
00464 while (mime->id != AV_CODEC_ID_NONE) {
00465 if (!av_strncasecmp(mime->str, mimetype, sizeof(mimetype))) {
00466 id = mime->id;
00467 break;
00468 }
00469 mime++;
00470 }
00471 if (id == AV_CODEC_ID_NONE) {
00472 av_log(s, AV_LOG_WARNING, "Unknown attached picture mimetype: %s, skipping.\n", mimetype);
00473 goto fail;
00474 }
00475 apic->id = id;
00476
00477
00478 pic_type = avio_r8(pb);
00479 taglen--;
00480 if (pic_type < 0 || pic_type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) {
00481 av_log(s, AV_LOG_WARNING, "Unknown attached picture type %d.\n", pic_type);
00482 pic_type = 0;
00483 }
00484 apic->type = ff_id3v2_picture_types[pic_type];
00485
00486
00487 if (decode_str(s, pb, enc, &apic->description, &taglen) < 0) {
00488 av_log(s, AV_LOG_ERROR, "Error decoding attached picture description.\n");
00489 goto fail;
00490 }
00491
00492 apic->len = taglen;
00493 apic->data = av_malloc(taglen);
00494 if (!apic->data || !apic->len || avio_read(pb, apic->data, taglen) != taglen)
00495 goto fail;
00496
00497 new_extra->tag = "APIC";
00498 new_extra->data = apic;
00499 new_extra->next = *extra_meta;
00500 *extra_meta = new_extra;
00501
00502 return;
00503
00504 fail:
00505 if (apic)
00506 free_apic(apic);
00507 av_freep(&new_extra);
00508 avio_seek(pb, end, SEEK_SET);
00509 }
00510
00511 typedef struct ID3v2EMFunc {
00512 const char *tag3;
00513 const char *tag4;
00514 void (*read)(AVFormatContext*, AVIOContext*, int, char*, ID3v2ExtraMeta **);
00515 void (*free)(void *obj);
00516 } ID3v2EMFunc;
00517
00518 static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
00519 { "GEO", "GEOB", read_geobtag, free_geobtag },
00520 { "PIC", "APIC", read_apic, free_apic },
00521 { NULL }
00522 };
00523
00529 static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
00530 {
00531 int i = 0;
00532 while (id3v2_extra_meta_funcs[i].tag3) {
00533 if (tag && !memcmp(tag,
00534 (isv34 ? id3v2_extra_meta_funcs[i].tag4 :
00535 id3v2_extra_meta_funcs[i].tag3),
00536 (isv34 ? 4 : 3)))
00537 return &id3v2_extra_meta_funcs[i];
00538 i++;
00539 }
00540 return NULL;
00541 }
00542
00543 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags, ID3v2ExtraMeta **extra_meta)
00544 {
00545 int isv34, unsync;
00546 unsigned tlen;
00547 char tag[5];
00548 int64_t next, end = avio_tell(s->pb) + len;
00549 int taghdrlen;
00550 const char *reason = NULL;
00551 AVIOContext pb;
00552 AVIOContext *pbx;
00553 unsigned char *buffer = NULL;
00554 int buffer_size = 0;
00555 const ID3v2EMFunc *extra_func = NULL;
00556 unsigned char *compressed_buffer = NULL;
00557 int compressed_buffer_size = 0;
00558
00559 switch (version) {
00560 case 2:
00561 if (flags & 0x40) {
00562 reason = "compression";
00563 goto error;
00564 }
00565 isv34 = 0;
00566 taghdrlen = 6;
00567 break;
00568
00569 case 3:
00570 case 4:
00571 isv34 = 1;
00572 taghdrlen = 10;
00573 break;
00574
00575 default:
00576 reason = "version";
00577 goto error;
00578 }
00579
00580 unsync = flags & 0x80;
00581
00582 if (isv34 && flags & 0x40) {
00583 int extlen = get_size(s->pb, 4);
00584 if (version == 4)
00585 extlen -= 4;
00586
00587 if (extlen < 0) {
00588 reason = "invalid extended header length";
00589 goto error;
00590 }
00591 avio_skip(s->pb, extlen);
00592 len -= extlen + 4;
00593 if (len < 0) {
00594 reason = "extended header too long.";
00595 goto error;
00596 }
00597 }
00598
00599 while (len >= taghdrlen) {
00600 unsigned int tflags = 0;
00601 int tunsync = 0;
00602 int tcomp = 0;
00603 int tencr = 0;
00604 unsigned long dlen;
00605
00606 if (isv34) {
00607 avio_read(s->pb, tag, 4);
00608 tag[4] = 0;
00609 if(version==3){
00610 tlen = avio_rb32(s->pb);
00611 }else
00612 tlen = get_size(s->pb, 4);
00613 tflags = avio_rb16(s->pb);
00614 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
00615 } else {
00616 avio_read(s->pb, tag, 3);
00617 tag[3] = 0;
00618 tlen = avio_rb24(s->pb);
00619 }
00620 if (tlen > (1<<28))
00621 break;
00622 len -= taghdrlen + tlen;
00623
00624 if (len < 0)
00625 break;
00626
00627 next = avio_tell(s->pb) + tlen;
00628
00629 if (!tlen) {
00630 if (tag[0])
00631 av_log(s, AV_LOG_DEBUG, "Invalid empty frame %s, skipping.\n", tag);
00632 continue;
00633 }
00634
00635 if (tflags & ID3v2_FLAG_DATALEN) {
00636 if (tlen < 4)
00637 break;
00638 dlen = avio_rb32(s->pb);
00639 tlen -= 4;
00640 } else
00641 dlen = tlen;
00642
00643 tcomp = tflags & ID3v2_FLAG_COMPRESSION;
00644 tencr = tflags & ID3v2_FLAG_ENCRYPTION;
00645
00646
00647 if (tencr || (!CONFIG_ZLIB && tcomp)) {
00648 const char *type;
00649 if (!tcomp)
00650 type = "encrypted";
00651 else if (!tencr)
00652 type = "compressed";
00653 else
00654 type = "encrypted and compressed";
00655
00656 av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
00657 avio_skip(s->pb, tlen);
00658
00659 } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)))) {
00660 if (unsync || tunsync || tcomp) {
00661 int i, j;
00662
00663 av_fast_malloc(&buffer, &buffer_size, dlen);
00664 if (!buffer) {
00665 av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen);
00666 goto seek;
00667 }
00668 #if CONFIG_ZLIB
00669 if (tcomp) {
00670 int n, err;
00671
00672 av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen);
00673
00674 av_fast_malloc(&compressed_buffer, &compressed_buffer_size, tlen);
00675 if (!compressed_buffer) {
00676 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
00677 goto seek;
00678 }
00679
00680 n = avio_read(s->pb, compressed_buffer, tlen);
00681 if (n < 0) {
00682 av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
00683 goto seek;
00684 }
00685
00686 err = uncompress(buffer, &dlen, compressed_buffer, n);
00687 if (err != Z_OK) {
00688 av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
00689 goto seek;
00690 }
00691 }
00692 #endif
00693
00694 for (i = 0, j = 0; i < dlen; i++, j++) {
00695 if (!tcomp)
00696 buffer[j] = avio_r8(s->pb);
00697 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
00698
00699 j--;
00700 }
00701 }
00702 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
00703 tlen = j;
00704 pbx = &pb;
00705 } else {
00706 pbx = s->pb;
00707 }
00708 if (tag[0] == 'T')
00709
00710 read_ttag(s, pbx, tlen, tag);
00711 else
00712
00713 extra_func->read(s, pbx, tlen, tag, extra_meta);
00714 }
00715 else if (!tag[0]) {
00716 if (tag[1])
00717 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n");
00718 avio_skip(s->pb, tlen);
00719 break;
00720 }
00721
00722 seek:
00723 avio_seek(s->pb, next, SEEK_SET);
00724 }
00725
00726 if (version == 4 && flags & 0x10)
00727 end += 10;
00728
00729 error:
00730 if (reason)
00731 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
00732 avio_seek(s->pb, end, SEEK_SET);
00733 av_free(buffer);
00734 av_free(compressed_buffer);
00735 return;
00736 }
00737
00738 void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta)
00739 {
00740 int len, ret;
00741 uint8_t buf[ID3v2_HEADER_SIZE];
00742 int found_header;
00743 int64_t off;
00744
00745 do {
00746
00747 off = avio_tell(s->pb);
00748 ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
00749 if (ret != ID3v2_HEADER_SIZE)
00750 break;
00751 found_header = ff_id3v2_match(buf, magic);
00752 if (found_header) {
00753
00754 len = ((buf[6] & 0x7f) << 21) |
00755 ((buf[7] & 0x7f) << 14) |
00756 ((buf[8] & 0x7f) << 7) |
00757 (buf[9] & 0x7f);
00758 ff_id3v2_parse(s, len, buf[3], buf[5], extra_meta);
00759 } else {
00760 avio_seek(s->pb, off, SEEK_SET);
00761 }
00762 } while (found_header);
00763 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
00764 ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv);
00765 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
00766 merge_date(&s->metadata);
00767 }
00768
00769 void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
00770 {
00771 ID3v2ExtraMeta *current = *extra_meta, *next;
00772 const ID3v2EMFunc *extra_func;
00773
00774 while (current) {
00775 if ((extra_func = get_extra_meta_func(current->tag, 1)))
00776 extra_func->free(current->data);
00777 next = current->next;
00778 av_freep(¤t);
00779 current = next;
00780 }
00781 }
00782
00783 int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
00784 {
00785 ID3v2ExtraMeta *cur;
00786
00787 for (cur = *extra_meta; cur; cur = cur->next) {
00788 ID3v2ExtraMetaAPIC *apic;
00789 AVStream *st;
00790
00791 if (strcmp(cur->tag, "APIC"))
00792 continue;
00793 apic = cur->data;
00794
00795 if (!(st = avformat_new_stream(s, NULL)))
00796 return AVERROR(ENOMEM);
00797
00798 st->disposition |= AV_DISPOSITION_ATTACHED_PIC;
00799 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00800 st->codec->codec_id = apic->id;
00801 av_dict_set(&st->metadata, "title", apic->description, 0);
00802 av_dict_set(&st->metadata, "comment", apic->type, 0);
00803
00804 av_init_packet(&st->attached_pic);
00805 st->attached_pic.data = apic->data;
00806 st->attached_pic.size = apic->len;
00807 st->attached_pic.destruct = av_destruct_packet;
00808 st->attached_pic.stream_index = st->index;
00809 st->attached_pic.flags |= AV_PKT_FLAG_KEY;
00810
00811 apic->data = NULL;
00812 apic->len = 0;
00813 }
00814
00815 return 0;
00816 }