|
|
@@ -169,6 +169,149 @@ static int id3v2_match(const uint8_t *buf) |
|
|
|
(buf[9] & 0x80) == 0); |
|
|
|
} |
|
|
|
|
|
|
|
static unsigned int id3v2_get_size(ByteIOContext *s, int len) |
|
|
|
{ |
|
|
|
int v=0; |
|
|
|
while(len--) |
|
|
|
v= (v<<7) + (get_byte(s)&0x7F); |
|
|
|
return v; |
|
|
|
} |
|
|
|
|
|
|
|
static void id3v2_read_ttag(AVFormatContext *s, int taglen, char *dst, int dstlen) |
|
|
|
{ |
|
|
|
char *q; |
|
|
|
int len; |
|
|
|
|
|
|
|
if(taglen < 1) |
|
|
|
return; |
|
|
|
|
|
|
|
taglen--; /* account for encoding type byte */ |
|
|
|
dstlen--; /* Leave space for zero terminator */ |
|
|
|
|
|
|
|
switch(get_byte(&s->pb)) { /* encoding type */ |
|
|
|
|
|
|
|
case 0: /* ISO-8859-1 (0 - 255 maps directly into unicode) */ |
|
|
|
q = dst; |
|
|
|
while(taglen--) { |
|
|
|
uint8_t tmp; |
|
|
|
PUT_UTF8(get_byte(&s->pb), tmp, if (q - dst < dstlen - 1) *q++ = tmp;) |
|
|
|
} |
|
|
|
*q = '\0'; |
|
|
|
break; |
|
|
|
|
|
|
|
case 3: /* UTF-8 */ |
|
|
|
len = FFMIN(taglen, dstlen); |
|
|
|
get_buffer(&s->pb, dst, len); |
|
|
|
dst[len] = 0; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* ID3v2 parser |
|
|
|
* |
|
|
|
* Handles ID3v2.2, 2.3 and 2.4. |
|
|
|
* |
|
|
|
*/ |
|
|
|
|
|
|
|
static void id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) |
|
|
|
{ |
|
|
|
int isv34, tlen; |
|
|
|
uint32_t tag; |
|
|
|
offset_t next; |
|
|
|
char tmp[16]; |
|
|
|
int taghdrlen; |
|
|
|
const char *reason; |
|
|
|
|
|
|
|
switch(version) { |
|
|
|
case 2: |
|
|
|
if(flags & 0x40) { |
|
|
|
reason = "compression"; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
isv34 = 0; |
|
|
|
taghdrlen = 6; |
|
|
|
break; |
|
|
|
|
|
|
|
case 3 ... 4: |
|
|
|
isv34 = 1; |
|
|
|
taghdrlen = 10; |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
reason = "version"; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
if(flags & 0x80) { |
|
|
|
reason = "unsynchronization"; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
if(isv34 && flags & 0x40) /* Extended header present, just skip over it */ |
|
|
|
url_fskip(&s->pb, id3v2_get_size(&s->pb, 4)); |
|
|
|
|
|
|
|
while(len >= taghdrlen) { |
|
|
|
if(isv34) { |
|
|
|
tag = get_be32(&s->pb); |
|
|
|
tlen = id3v2_get_size(&s->pb, 4); |
|
|
|
get_be16(&s->pb); /* flags */ |
|
|
|
} else { |
|
|
|
tag = get_be24(&s->pb); |
|
|
|
tlen = id3v2_get_size(&s->pb, 3); |
|
|
|
} |
|
|
|
len -= taghdrlen + tlen; |
|
|
|
|
|
|
|
if(len < 0) |
|
|
|
break; |
|
|
|
|
|
|
|
next = url_ftell(&s->pb) + tlen; |
|
|
|
|
|
|
|
switch(tag) { |
|
|
|
case MKBETAG('T', 'I', 'T', '2'): |
|
|
|
case MKBETAG(0, 'T', 'T', '2'): |
|
|
|
id3v2_read_ttag(s, tlen, s->title, sizeof(s->title)); |
|
|
|
break; |
|
|
|
case MKBETAG('T', 'P', 'E', '1'): |
|
|
|
case MKBETAG(0, 'T', 'P', '1'): |
|
|
|
id3v2_read_ttag(s, tlen, s->author, sizeof(s->author)); |
|
|
|
break; |
|
|
|
case MKBETAG('T', 'A', 'L', 'B'): |
|
|
|
case MKBETAG(0, 'T', 'A', 'L'): |
|
|
|
id3v2_read_ttag(s, tlen, s->album, sizeof(s->album)); |
|
|
|
break; |
|
|
|
case MKBETAG('T', 'C', 'O', 'N'): |
|
|
|
case MKBETAG(0, 'T', 'C', 'O'): |
|
|
|
id3v2_read_ttag(s, tlen, s->genre, sizeof(s->genre)); |
|
|
|
break; |
|
|
|
case MKBETAG('T', 'C', 'O', 'P'): |
|
|
|
case MKBETAG(0, 'T', 'C', 'R'): |
|
|
|
id3v2_read_ttag(s, tlen, s->copyright, sizeof(s->copyright)); |
|
|
|
break; |
|
|
|
case MKBETAG('T', 'R', 'C', 'K'): |
|
|
|
case MKBETAG(0, 'T', 'R', 'K'): |
|
|
|
id3v2_read_ttag(s, tlen, tmp, sizeof(tmp)); |
|
|
|
s->track = atoi(tmp); |
|
|
|
break; |
|
|
|
case 0: |
|
|
|
/* padding, skip to end */ |
|
|
|
url_fskip(&s->pb, len); |
|
|
|
len = 0; |
|
|
|
continue; |
|
|
|
} |
|
|
|
/* Skip to end of tag */ |
|
|
|
url_fseek(&s->pb, next, SEEK_SET); |
|
|
|
} |
|
|
|
|
|
|
|
if(version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ |
|
|
|
url_fskip(&s->pb, 10); |
|
|
|
return; |
|
|
|
|
|
|
|
error: |
|
|
|
av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); |
|
|
|
url_fskip(&s->pb, len); |
|
|
|
} |
|
|
|
|
|
|
|
static void id3v1_get_string(char *str, int str_size, |
|
|
|
const uint8_t *buf, int buf_size) |
|
|
|
{ |
|
|
@@ -313,12 +456,12 @@ static int mp3_read_header(AVFormatContext *s, |
|
|
|
if (ret != ID3v2_HEADER_SIZE) |
|
|
|
return -1; |
|
|
|
if (id3v2_match(buf)) { |
|
|
|
/* skip ID3v2 header */ |
|
|
|
/* parse ID3v2 header */ |
|
|
|
len = ((buf[6] & 0x7f) << 21) | |
|
|
|
((buf[7] & 0x7f) << 14) | |
|
|
|
((buf[8] & 0x7f) << 7) | |
|
|
|
(buf[9] & 0x7f); |
|
|
|
url_fskip(&s->pb, len); |
|
|
|
id3v2_parse(s, len, buf[3], buf[5]); |
|
|
|
} else { |
|
|
|
url_fseek(&s->pb, 0, SEEK_SET); |
|
|
|
} |
|
|
|