id3v2 reader
patch by Andreas Ãman andreas olebyn nu original thread: [FFmpeg-devel] [Ffmpeg-devel] ID3v2 Originally committed as revision 9101 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
parent
cb2578d5ef
commit
52b53f8653
@ -169,6 +169,149 @@ static int id3v2_match(const uint8_t *buf)
|
|||||||
(buf[9] & 0x80) == 0);
|
(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,
|
static void id3v1_get_string(char *str, int str_size,
|
||||||
const uint8_t *buf, int buf_size)
|
const uint8_t *buf, int buf_size)
|
||||||
{
|
{
|
||||||
@ -313,12 +456,12 @@ static int mp3_read_header(AVFormatContext *s,
|
|||||||
if (ret != ID3v2_HEADER_SIZE)
|
if (ret != ID3v2_HEADER_SIZE)
|
||||||
return -1;
|
return -1;
|
||||||
if (id3v2_match(buf)) {
|
if (id3v2_match(buf)) {
|
||||||
/* skip ID3v2 header */
|
/* parse ID3v2 header */
|
||||||
len = ((buf[6] & 0x7f) << 21) |
|
len = ((buf[6] & 0x7f) << 21) |
|
||||||
((buf[7] & 0x7f) << 14) |
|
((buf[7] & 0x7f) << 14) |
|
||||||
((buf[8] & 0x7f) << 7) |
|
((buf[8] & 0x7f) << 7) |
|
||||||
(buf[9] & 0x7f);
|
(buf[9] & 0x7f);
|
||||||
url_fskip(&s->pb, len);
|
id3v2_parse(s, len, buf[3], buf[5]);
|
||||||
} else {
|
} else {
|
||||||
url_fseek(&s->pb, 0, SEEK_SET);
|
url_fseek(&s->pb, 0, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user