From 18bbe9df9ae58c9452968364ab9863d7d7674a34 Mon Sep 17 00:00:00 2001 From: Alexander Kojevnikov Date: Wed, 18 Aug 2010 16:29:46 +0000 Subject: [PATCH] Support unsynchronisation for id3v2 tags. Patch by Alexander Kojevnikov, alexander kojevnikov com Originally committed as revision 24824 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/id3v2.c | 50 ++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index 1f513d9196..f2515d5206 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -77,7 +77,7 @@ static unsigned int get_size(ByteIOContext *s, int len) return v; } -static void read_ttag(AVFormatContext *s, int taglen, const char *key) +static void read_ttag(AVFormatContext *s, ByteIOContext *pb, int taglen, const char *key) { char *q, dst[512]; const char *val = NULL; @@ -91,20 +91,20 @@ static void read_ttag(AVFormatContext *s, int taglen, const char *key) taglen--; /* account for encoding type byte */ - switch (get_byte(s->pb)) { /* encoding type */ + switch (get_byte(pb)) { /* encoding type */ case 0: /* ISO-8859-1 (0 - 255 maps directly into unicode) */ q = dst; while (taglen-- && q - dst < dstlen - 7) { uint8_t tmp; - PUT_UTF8(get_byte(s->pb), tmp, *q++ = tmp;) + PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;) } *q = 0; break; case 1: /* UTF-16 with BOM */ taglen -= 2; - switch (get_be16(s->pb)) { + switch (get_be16(pb)) { case 0xfffe: get = get_le16; case 0xfeff: @@ -121,7 +121,7 @@ static void read_ttag(AVFormatContext *s, int taglen, const char *key) uint32_t ch; uint8_t tmp; - GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(s->pb) : 0), break;) + GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;) PUT_UTF8(ch, tmp, *q++ = tmp;) } *q = 0; @@ -129,7 +129,7 @@ static void read_ttag(AVFormatContext *s, int taglen, const char *key) case 3: /* UTF-8 */ len = FFMIN(taglen, dstlen); - get_buffer(s->pb, dst, len); + get_buffer(pb, dst, len); dst[len] = 0; break; default: @@ -156,11 +156,14 @@ static void read_ttag(AVFormatContext *s, int taglen, const char *key) void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) { - int isv34, tlen; + int isv34, tlen, unsync; char tag[5]; int64_t next; int taghdrlen; const char *reason; + ByteIOContext pb; + unsigned char *buffer = NULL; + int buffer_size = 0; switch (version) { case 2: @@ -183,15 +186,15 @@ void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) goto error; } - if (flags & 0x80) { - reason = "unsynchronization"; - goto error; - } + unsync = flags & 0x80; if (isv34 && flags & 0x40) /* Extended header present, just skip over it */ url_fskip(s->pb, get_size(s->pb, 4)); while (len >= taghdrlen) { + unsigned int tflags; + int tunsync = 0; + if (isv34) { get_buffer(s->pb, tag, 4); tag[4] = 0; @@ -199,7 +202,8 @@ void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) tlen = get_be32(s->pb); }else tlen = get_size(s->pb, 4); - get_be16(s->pb); /* flags */ + tflags = get_be16(s->pb); + tunsync = tflags & 0x02; } else { get_buffer(s->pb, tag, 3); tag[3] = 0; @@ -212,8 +216,23 @@ void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) next = url_ftell(s->pb) + tlen; - if (tag[0] == 'T') - read_ttag(s, tlen, tag); + if (tag[0] == 'T') { + if (unsync || tunsync) { + int i, j; + av_fast_malloc(&buffer, &buffer_size, tlen); + for (i = 0, j = 0; i < tlen; i++, j++) { + buffer[j] = get_byte(s->pb); + if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) { + /* Unsynchronised byte, skip it */ + j--; + } + } + init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL); + read_ttag(s, &pb, j, tag); + } else { + read_ttag(s, s->pb, tlen, tag); + } + } else if (!tag[0]) { if (tag[1]) av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); @@ -230,11 +249,14 @@ void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) } if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ url_fskip(s->pb, 10); + + av_free(buffer); return; error: av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); url_fskip(s->pb, len); + av_free(buffer); } const AVMetadataConv ff_id3v2_metadata_conv[] = {