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" , CODEC_ID_GIF},
00129 {"image/jpeg", CODEC_ID_MJPEG},
00130 {"image/jpg", CODEC_ID_MJPEG},
00131 {"image/png" , CODEC_ID_PNG},
00132 {"image/tiff", CODEC_ID_TIFF},
00133 {"", CODEC_ID_NONE},
00134 };
00135
00136 int ff_id3v2_match(const uint8_t *buf, const char * magic)
00137 {
00138 return buf[0] == magic[0] &&
00139 buf[1] == magic[1] &&
00140 buf[2] == magic[2] &&
00141 buf[3] != 0xff &&
00142 buf[4] != 0xff &&
00143 (buf[6] & 0x80) == 0 &&
00144 (buf[7] & 0x80) == 0 &&
00145 (buf[8] & 0x80) == 0 &&
00146 (buf[9] & 0x80) == 0;
00147 }
00148
00149 int ff_id3v2_tag_len(const uint8_t * buf)
00150 {
00151 int len = ((buf[6] & 0x7f) << 21) +
00152 ((buf[7] & 0x7f) << 14) +
00153 ((buf[8] & 0x7f) << 7) +
00154 (buf[9] & 0x7f) +
00155 ID3v2_HEADER_SIZE;
00156 if (buf[5] & 0x10)
00157 len += ID3v2_HEADER_SIZE;
00158 return len;
00159 }
00160
00161 static unsigned int get_size(AVIOContext *s, int len)
00162 {
00163 int v = 0;
00164 while (len--)
00165 v = (v << 7) + (avio_r8(s) & 0x7F);
00166 return v;
00167 }
00168
00172 static void free_geobtag(void *obj)
00173 {
00174 ID3v2ExtraMetaGEOB *geob = obj;
00175 av_free(geob->mime_type);
00176 av_free(geob->file_name);
00177 av_free(geob->description);
00178 av_free(geob->data);
00179 av_free(geob);
00180 }
00181
00194 static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
00195 uint8_t **dst, int *maxread)
00196 {
00197 int ret;
00198 uint8_t tmp;
00199 uint32_t ch = 1;
00200 int left = *maxread;
00201 unsigned int (*get)(AVIOContext*) = avio_rb16;
00202 AVIOContext *dynbuf;
00203
00204 if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) {
00205 av_log(s, AV_LOG_ERROR, "Error opening memory stream\n");
00206 return ret;
00207 }
00208
00209 switch (encoding) {
00210
00211 case ID3v2_ENCODING_ISO8859:
00212 while (left && ch) {
00213 ch = avio_r8(pb);
00214 PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
00215 left--;
00216 }
00217 break;
00218
00219 case ID3v2_ENCODING_UTF16BOM:
00220 if ((left -= 2) < 0) {
00221 av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n");
00222 avio_close_dyn_buf(dynbuf, dst);
00223 av_freep(dst);
00224 return AVERROR_INVALIDDATA;
00225 }
00226 switch (avio_rb16(pb)) {
00227 case 0xfffe:
00228 get = avio_rl16;
00229 case 0xfeff:
00230 break;
00231 default:
00232 av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n");
00233 avio_close_dyn_buf(dynbuf, dst);
00234 av_freep(dst);
00235 *maxread = left;
00236 return AVERROR_INVALIDDATA;
00237 }
00238
00239
00240 case ID3v2_ENCODING_UTF16BE:
00241 while ((left > 1) && ch) {
00242 GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
00243 PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
00244 }
00245 if (left < 0)
00246 left += 2;
00247 break;
00248
00249 case ID3v2_ENCODING_UTF8:
00250 while (left && ch) {
00251 ch = avio_r8(pb);
00252 avio_w8(dynbuf, ch);
00253 left--;
00254 }
00255 break;
00256 default:
00257 av_log(s, AV_LOG_WARNING, "Unknown encoding\n");
00258 }
00259
00260 if (ch)
00261 avio_w8(dynbuf, 0);
00262
00263 avio_close_dyn_buf(dynbuf, dst);
00264 *maxread = left;
00265
00266 return 0;
00267 }
00268
00272 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
00273 {
00274 uint8_t *dst;
00275 int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
00276 unsigned genre;
00277
00278 if (taglen < 1)
00279 return;
00280
00281 encoding = avio_r8(pb);
00282 taglen--;
00283
00284 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
00285 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
00286 return;
00287 }
00288
00289 if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
00290 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
00291 && genre <= ID3v1_GENRE_MAX) {
00292 av_freep(&dst);
00293 dst = av_strdup(ff_id3v1_genre_str[genre]);
00294 } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
00295
00296 key = dst;
00297 if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
00298 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
00299 av_freep(&key);
00300 return;
00301 }
00302 dict_flags |= AV_DICT_DONT_STRDUP_KEY;
00303 } else if (!*dst)
00304 av_freep(&dst);
00305
00306 if (dst)
00307 av_dict_set(&s->metadata, key, dst, dict_flags);
00308 }
00309
00313 static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta)
00314 {
00315 ID3v2ExtraMetaGEOB *geob_data = NULL;
00316 ID3v2ExtraMeta *new_extra = NULL;
00317 char encoding;
00318 unsigned int len;
00319
00320 if (taglen < 1)
00321 return;
00322
00323 geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
00324 if (!geob_data) {
00325 av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMetaGEOB));
00326 return;
00327 }
00328
00329 new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
00330 if (!new_extra) {
00331 av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMeta));
00332 goto fail;
00333 }
00334
00335
00336 encoding = avio_r8(pb);
00337 taglen--;
00338
00339
00340 if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, &taglen) < 0
00341 || taglen <= 0)
00342 goto fail;
00343
00344
00345 if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0
00346 || taglen <= 0)
00347 goto fail;
00348
00349
00350 if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0
00351 || taglen < 0)
00352 goto fail;
00353
00354 if (taglen) {
00355
00356 geob_data->data = av_malloc(taglen);
00357 if (!geob_data->data) {
00358 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen);
00359 goto fail;
00360 }
00361 if ((len = avio_read(pb, geob_data->data, taglen)) < taglen)
00362 av_log(s, AV_LOG_WARNING, "Error reading GEOB frame, data truncated.\n");
00363 geob_data->datasize = len;
00364 } else {
00365 geob_data->data = NULL;
00366 geob_data->datasize = 0;
00367 }
00368
00369
00370 new_extra->tag = "GEOB";
00371 new_extra->data = geob_data;
00372 new_extra->next = *extra_meta;
00373 *extra_meta = new_extra;
00374
00375 return;
00376
00377 fail:
00378 av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag);
00379 free_geobtag(geob_data);
00380 av_free(new_extra);
00381 return;
00382 }
00383
00384 static int is_number(const char *str)
00385 {
00386 while (*str >= '0' && *str <= '9') str++;
00387 return !*str;
00388 }
00389
00390 static AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag)
00391 {
00392 AVDictionaryEntry *t;
00393 if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
00394 strlen(t->value) == 4 && is_number(t->value))
00395 return t;
00396 return NULL;
00397 }
00398
00399 static void merge_date(AVDictionary **m)
00400 {
00401 AVDictionaryEntry *t;
00402 char date[17] = {0};
00403
00404 if (!(t = get_date_tag(*m, "TYER")) &&
00405 !(t = get_date_tag(*m, "TYE")))
00406 return;
00407 av_strlcpy(date, t->value, 5);
00408 av_dict_set(m, "TYER", NULL, 0);
00409 av_dict_set(m, "TYE", NULL, 0);
00410
00411 if (!(t = get_date_tag(*m, "TDAT")) &&
00412 !(t = get_date_tag(*m, "TDA")))
00413 goto finish;
00414 snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
00415 av_dict_set(m, "TDAT", NULL, 0);
00416 av_dict_set(m, "TDA", NULL, 0);
00417
00418 if (!(t = get_date_tag(*m, "TIME")) &&
00419 !(t = get_date_tag(*m, "TIM")))
00420 goto finish;
00421 snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
00422 av_dict_set(m, "TIME", NULL, 0);
00423 av_dict_set(m, "TIM", NULL, 0);
00424
00425 finish:
00426 if (date[0])
00427 av_dict_set(m, "date", date, 0);
00428 }
00429
00430 static void free_apic(void *obj)
00431 {
00432 ID3v2ExtraMetaAPIC *apic = obj;
00433 av_freep(&apic->data);
00434 av_freep(&apic->description);
00435 av_freep(&apic);
00436 }
00437
00438 static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta)
00439 {
00440 int enc, pic_type;
00441 char mimetype[64];
00442 const CodecMime *mime = ff_id3v2_mime_tags;
00443 enum CodecID id = CODEC_ID_NONE;
00444 ID3v2ExtraMetaAPIC *apic = NULL;
00445 ID3v2ExtraMeta *new_extra = NULL;
00446 int64_t end = avio_tell(pb) + taglen;
00447
00448 if (taglen <= 4)
00449 goto fail;
00450
00451 new_extra = av_mallocz(sizeof(*new_extra));
00452 apic = av_mallocz(sizeof(*apic));
00453 if (!new_extra || !apic)
00454 goto fail;
00455
00456 enc = avio_r8(pb);
00457 taglen--;
00458
00459
00460 taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype));
00461 while (mime->id != CODEC_ID_NONE) {
00462 if (!strncmp(mime->str, mimetype, sizeof(mimetype))) {
00463 id = mime->id;
00464 break;
00465 }
00466 mime++;
00467 }
00468 if (id == CODEC_ID_NONE) {
00469 av_log(s, AV_LOG_WARNING, "Unknown attached picture mimetype: %s, skipping.\n", mimetype);
00470 goto fail;
00471 }
00472 apic->id = id;
00473
00474
00475 pic_type = avio_r8(pb);
00476 taglen--;
00477 if (pic_type < 0 || pic_type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) {
00478 av_log(s, AV_LOG_WARNING, "Unknown attached picture type %d.\n", pic_type);
00479 pic_type = 0;
00480 }
00481 apic->type = ff_id3v2_picture_types[pic_type];
00482
00483
00484 if (decode_str(s, pb, enc, &apic->description, &taglen) < 0) {
00485 av_log(s, AV_LOG_ERROR, "Error decoding attached picture description.\n");
00486 goto fail;
00487 }
00488
00489 apic->len = taglen;
00490 apic->data = av_malloc(taglen);
00491 if (!apic->data || !apic->len || avio_read(pb, apic->data, taglen) != taglen)
00492 goto fail;
00493
00494 new_extra->tag = "APIC";
00495 new_extra->data = apic;
00496 new_extra->next = *extra_meta;
00497 *extra_meta = new_extra;
00498
00499 return;
00500
00501 fail:
00502 if (apic)
00503 free_apic(apic);
00504 av_freep(&new_extra);
00505 avio_seek(pb, end, SEEK_SET);
00506 }
00507
00508 typedef struct ID3v2EMFunc {
00509 const char *tag3;
00510 const char *tag4;
00511 void (*read)(AVFormatContext*, AVIOContext*, int, char*, ID3v2ExtraMeta **);
00512 void (*free)(void *obj);
00513 } ID3v2EMFunc;
00514
00515 static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
00516 { "GEO", "GEOB", read_geobtag, free_geobtag },
00517 { "PIC", "APIC", read_apic, free_apic },
00518 { NULL }
00519 };
00520
00526 static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
00527 {
00528 int i = 0;
00529 while (id3v2_extra_meta_funcs[i].tag3) {
00530 if (tag && !memcmp(tag,
00531 (isv34 ? id3v2_extra_meta_funcs[i].tag4 :
00532 id3v2_extra_meta_funcs[i].tag3),
00533 (isv34 ? 4 : 3)))
00534 return &id3v2_extra_meta_funcs[i];
00535 i++;
00536 }
00537 return NULL;
00538 }
00539
00540 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags, ID3v2ExtraMeta **extra_meta)
00541 {
00542 int isv34, unsync;
00543 unsigned tlen;
00544 char tag[5];
00545 int64_t next, end = avio_tell(s->pb) + len;
00546 int taghdrlen;
00547 const char *reason = NULL;
00548 AVIOContext pb;
00549 AVIOContext *pbx;
00550 unsigned char *buffer = NULL;
00551 int buffer_size = 0;
00552 const ID3v2EMFunc *extra_func = NULL;
00553 unsigned char *compressed_buffer = NULL;
00554 int compressed_buffer_size = 0;
00555
00556 switch (version) {
00557 case 2:
00558 if (flags & 0x40) {
00559 reason = "compression";
00560 goto error;
00561 }
00562 isv34 = 0;
00563 taghdrlen = 6;
00564 break;
00565
00566 case 3:
00567 case 4:
00568 isv34 = 1;
00569 taghdrlen = 10;
00570 break;
00571
00572 default:
00573 reason = "version";
00574 goto error;
00575 }
00576
00577 unsync = flags & 0x80;
00578
00579 if (isv34 && flags & 0x40) {
00580 int extlen = get_size(s->pb, 4);
00581 if (version == 4)
00582 extlen -= 4;
00583
00584 if (extlen < 0) {
00585 reason = "invalid extended header length";
00586 goto error;
00587 }
00588 avio_skip(s->pb, extlen);
00589 len -= extlen + 4;
00590 if (len < 0) {
00591 reason = "extended header too long.";
00592 goto error;
00593 }
00594 }
00595
00596 while (len >= taghdrlen) {
00597 unsigned int tflags = 0;
00598 int tunsync = 0;
00599 int tcomp = 0;
00600 int tencr = 0;
00601 unsigned long dlen;
00602
00603 if (isv34) {
00604 avio_read(s->pb, tag, 4);
00605 tag[4] = 0;
00606 if(version==3){
00607 tlen = avio_rb32(s->pb);
00608 }else
00609 tlen = get_size(s->pb, 4);
00610 tflags = avio_rb16(s->pb);
00611 tunsync = tflags & ID3v2_FLAG_UNSYNCH;
00612 } else {
00613 avio_read(s->pb, tag, 3);
00614 tag[3] = 0;
00615 tlen = avio_rb24(s->pb);
00616 }
00617 if (tlen > (1<<28))
00618 break;
00619 len -= taghdrlen + tlen;
00620
00621 if (len < 0)
00622 break;
00623
00624 next = avio_tell(s->pb) + tlen;
00625
00626 if (!tlen) {
00627 if (tag[0])
00628 av_log(s, AV_LOG_DEBUG, "Invalid empty frame %s, skipping.\n", tag);
00629 continue;
00630 }
00631
00632 if (tflags & ID3v2_FLAG_DATALEN) {
00633 if (tlen < 4)
00634 break;
00635 dlen = avio_rb32(s->pb);
00636 tlen -= 4;
00637 } else
00638 dlen = tlen;
00639
00640 tcomp = tflags & ID3v2_FLAG_COMPRESSION;
00641 tencr = tflags & ID3v2_FLAG_ENCRYPTION;
00642
00643
00644 if (tencr || (!CONFIG_ZLIB && tcomp)) {
00645 const char *type;
00646 if (!tcomp)
00647 type = "encrypted";
00648 else if (!tencr)
00649 type = "compressed";
00650 else
00651 type = "encrypted and compressed";
00652
00653 av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
00654 avio_skip(s->pb, tlen);
00655
00656 } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)))) {
00657 if (unsync || tunsync || tcomp) {
00658 int i, j;
00659
00660 av_fast_malloc(&buffer, &buffer_size, dlen);
00661 if (!buffer) {
00662 av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen);
00663 goto seek;
00664 }
00665 #if CONFIG_ZLIB
00666 if (tcomp) {
00667 int n, err;
00668
00669 av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen);
00670
00671 av_fast_malloc(&compressed_buffer, &compressed_buffer_size, tlen);
00672 if (!compressed_buffer) {
00673 av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
00674 goto seek;
00675 }
00676
00677 n = avio_read(s->pb, compressed_buffer, tlen);
00678 if (n < 0) {
00679 av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
00680 goto seek;
00681 }
00682
00683 err = uncompress(buffer, &dlen, compressed_buffer, n);
00684 if (err != Z_OK) {
00685 av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
00686 goto seek;
00687 }
00688 }
00689 #endif
00690
00691 for (i = 0, j = 0; i < dlen; i++, j++) {
00692 if (!tcomp)
00693 buffer[j] = avio_r8(s->pb);
00694 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
00695
00696 j--;
00697 }
00698 }
00699 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
00700 tlen = j;
00701 pbx = &pb;
00702 } else {
00703 pbx = s->pb;
00704 }
00705 if (tag[0] == 'T')
00706
00707 read_ttag(s, pbx, tlen, tag);
00708 else
00709
00710 extra_func->read(s, pbx, tlen, tag, extra_meta);
00711 }
00712 else if (!tag[0]) {
00713 if (tag[1])
00714 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
00715 avio_skip(s->pb, tlen);
00716 break;
00717 }
00718
00719 seek:
00720 avio_seek(s->pb, next, SEEK_SET);
00721 }
00722
00723 if (version == 4 && flags & 0x10)
00724 end += 10;
00725
00726 error:
00727 if (reason)
00728 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
00729 avio_seek(s->pb, end, SEEK_SET);
00730 av_free(buffer);
00731 av_free(compressed_buffer);
00732 return;
00733 }
00734
00735 void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta)
00736 {
00737 int len, ret;
00738 uint8_t buf[ID3v2_HEADER_SIZE];
00739 int found_header;
00740 int64_t off;
00741
00742 do {
00743
00744 off = avio_tell(s->pb);
00745 ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
00746 if (ret != ID3v2_HEADER_SIZE)
00747 break;
00748 found_header = ff_id3v2_match(buf, magic);
00749 if (found_header) {
00750
00751 len = ((buf[6] & 0x7f) << 21) |
00752 ((buf[7] & 0x7f) << 14) |
00753 ((buf[8] & 0x7f) << 7) |
00754 (buf[9] & 0x7f);
00755 ff_id3v2_parse(s, len, buf[3], buf[5], extra_meta);
00756 } else {
00757 avio_seek(s->pb, off, SEEK_SET);
00758 }
00759 } while (found_header);
00760 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
00761 ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv);
00762 ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
00763 merge_date(&s->metadata);
00764 }
00765
00766 void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
00767 {
00768 ID3v2ExtraMeta *current = *extra_meta, *next;
00769 const ID3v2EMFunc *extra_func;
00770
00771 while (current) {
00772 if ((extra_func = get_extra_meta_func(current->tag, 1)))
00773 extra_func->free(current->data);
00774 next = current->next;
00775 av_freep(¤t);
00776 current = next;
00777 }
00778 }
00779
00780 int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
00781 {
00782 ID3v2ExtraMeta *cur;
00783
00784 for (cur = *extra_meta; cur; cur = cur->next) {
00785 ID3v2ExtraMetaAPIC *apic;
00786 AVStream *st;
00787
00788 if (strcmp(cur->tag, "APIC"))
00789 continue;
00790 apic = cur->data;
00791
00792 if (!(st = avformat_new_stream(s, NULL)))
00793 return AVERROR(ENOMEM);
00794
00795 st->disposition |= AV_DISPOSITION_ATTACHED_PIC;
00796 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00797 st->codec->codec_id = apic->id;
00798 av_dict_set(&st->metadata, "title", apic->description, 0);
00799 av_dict_set(&st->metadata, "comment", apic->type, 0);
00800
00801 av_init_packet(&st->attached_pic);
00802 st->attached_pic.data = apic->data;
00803 st->attached_pic.size = apic->len;
00804 st->attached_pic.destruct = av_destruct_packet;
00805 st->attached_pic.stream_index = st->index;
00806 st->attached_pic.flags |= AV_PKT_FLAG_KEY;
00807
00808 apic->data = NULL;
00809 apic->len = 0;
00810 }
00811
00812 return 0;
00813 }