avformat/id3v2: allow ID3 parsing without AVFormatContext

Add ff_id3v2_read_dict() for parsing without AVFormatContext, but
instead with AVIOContext and AVDictionary.

AVFormatContext is still used for logging, if available.

Chapter parsing is the only non-logging functionality that actually
needs AVFormatContext, and AFAICS it should be modified to write the
data to ID3v2ExtraMeta first, from where it can be implanted to
AVFormatContext by a separate function (like it is done with
read_apic() and ff_id3v2_parse_apic()). That is outside the scope of
this patch, though.

Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
This commit is contained in:
Anssi Hannula 2013-12-30 10:02:59 +02:00
parent d52882faef
commit 7fdf245ab9
2 changed files with 79 additions and 43 deletions

View File

@ -532,6 +532,14 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, char *tta
int taglen;
char tag[5];
if (!s) {
/* We should probably just put the chapter data to extra_meta here
* and do the AVFormatContext-needing part in a separate
* ff_id3v2_parse_apic()-like function. */
av_log(NULL, AV_LOG_DEBUG, "No AVFormatContext, skipped ID3 chapter data\n");
return;
}
if (decode_str(s, pb, 0, &dst, &len) < 0)
return;
if (len < 16)
@ -650,16 +658,17 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
return NULL;
}
static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
AVFormatContext *s, int len, uint8_t version,
uint8_t flags, ID3v2ExtraMeta **extra_meta)
{
int isv34, unsync;
unsigned tlen;
char tag[5];
int64_t next, end = avio_tell(s->pb) + len;
int64_t next, end = avio_tell(pb) + len;
int taghdrlen;
const char *reason = NULL;
AVIOContext pb;
AVIOContext pb_local;
AVIOContext *pbx;
unsigned char *buffer = NULL;
int buffer_size = 0;
@ -693,7 +702,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
unsync = flags & 0x80;
if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
int extlen = get_size(s->pb, 4);
int extlen = get_size(pb, 4);
if (version == 4)
/* In v2.4 the length includes the length field we just read. */
extlen -= 4;
@ -702,7 +711,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
reason = "invalid extended header length";
goto error;
}
avio_skip(s->pb, extlen);
avio_skip(pb, extlen);
len -= extlen + 4;
if (len < 0) {
reason = "extended header too long.";
@ -718,20 +727,20 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
unsigned long dlen;
if (isv34) {
if (avio_read(s->pb, tag, 4) < 4)
if (avio_read(pb, tag, 4) < 4)
break;
tag[4] = 0;
if (version == 3) {
tlen = avio_rb32(s->pb);
tlen = avio_rb32(pb);
} else
tlen = get_size(s->pb, 4);
tflags = avio_rb16(s->pb);
tlen = get_size(pb, 4);
tflags = avio_rb16(pb);
tunsync = tflags & ID3v2_FLAG_UNSYNCH;
} else {
if (avio_read(s->pb, tag, 3) < 3)
if (avio_read(pb, tag, 3) < 3)
break;
tag[3] = 0;
tlen = avio_rb24(s->pb);
tlen = avio_rb24(pb);
}
if (tlen > (1<<28))
break;
@ -740,7 +749,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
if (len < 0)
break;
next = avio_tell(s->pb) + tlen;
next = avio_tell(pb) + tlen;
if (!tlen) {
if (tag[0])
@ -752,7 +761,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
if (tflags & ID3v2_FLAG_DATALEN) {
if (tlen < 4)
break;
dlen = avio_rb32(s->pb);
dlen = avio_rb32(pb);
tlen -= 4;
} else
dlen = tlen;
@ -771,12 +780,12 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
type = "encrypted and compressed";
av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
avio_skip(s->pb, tlen);
avio_skip(pb, tlen);
/* check for text tag or supported special meta tag */
} else if (tag[0] == 'T' ||
(extra_meta &&
(extra_func = get_extra_meta_func(tag, isv34)))) {
pbx = s->pb;
pbx = pb;
if (unsync || tunsync || tcomp) {
av_fast_malloc(&buffer, &buffer_size, tlen);
@ -786,23 +795,23 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
}
}
if (unsync || tunsync) {
int64_t end = avio_tell(s->pb) + tlen;
int64_t end = avio_tell(pb) + tlen;
uint8_t *b;
b = buffer;
while (avio_tell(s->pb) < end && b - buffer < tlen && !s->pb->eof_reached) {
*b++ = avio_r8(s->pb);
if (*(b - 1) == 0xff && avio_tell(s->pb) < end - 1 &&
while (avio_tell(pb) < end && b - buffer < tlen && !pb->eof_reached) {
*b++ = avio_r8(pb);
if (*(b - 1) == 0xff && avio_tell(pb) < end - 1 &&
b - buffer < tlen &&
!s->pb->eof_reached ) {
uint8_t val = avio_r8(s->pb);
*b++ = val ? val : avio_r8(s->pb);
!pb->eof_reached ) {
uint8_t val = avio_r8(pb);
*b++ = val ? val : avio_r8(pb);
}
}
ffio_init_context(&pb, buffer, b - buffer, 0, NULL, NULL, NULL,
ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL,
NULL);
tlen = b - buffer;
pbx = &pb; // read from sync buffer
pbx = &pb_local; // read from sync buffer
}
#if CONFIG_ZLIB
@ -818,7 +827,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
}
if (!(unsync || tunsync)) {
err = avio_read(s->pb, buffer, tlen);
err = avio_read(pb, buffer, tlen);
if (err < 0) {
av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
goto seek;
@ -831,26 +840,26 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
goto seek;
}
ffio_init_context(&pb, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL);
ffio_init_context(&pb_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL);
tlen = dlen;
pbx = &pb; // read from sync buffer
pbx = &pb_local; // read from sync buffer
}
#endif
if (tag[0] == 'T')
/* parse text tag */
read_ttag(s, pbx, tlen, &s->metadata, tag);
read_ttag(s, pbx, tlen, metadata, tag);
else
/* parse special meta tag */
extra_func->read(s, pbx, tlen, tag, extra_meta, isv34);
} else if (!tag[0]) {
if (tag[1])
av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n");
avio_skip(s->pb, tlen);
avio_skip(pb, tlen);
break;
}
/* Skip to end of tag */
seek:
avio_seek(s->pb, next, SEEK_SET);
avio_seek(pb, next, SEEK_SET);
}
/* Footer preset, always 10 bytes, skip over it */
@ -861,14 +870,15 @@ error:
if (reason)
av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n",
version, reason);
avio_seek(s->pb, end, SEEK_SET);
avio_seek(pb, end, SEEK_SET);
av_free(buffer);
av_free(uncompressed_buffer);
return;
}
void ff_id3v2_read(AVFormatContext *s, const char *magic,
ID3v2ExtraMeta **extra_meta)
static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
AVFormatContext *s, const char *magic,
ID3v2ExtraMeta **extra_meta)
{
int len, ret;
uint8_t buf[ID3v2_HEADER_SIZE];
@ -877,10 +887,10 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic,
do {
/* save the current offset in case there's nothing to read/skip */
off = avio_tell(s->pb);
ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
off = avio_tell(pb);
ret = avio_read(pb, buf, ID3v2_HEADER_SIZE);
if (ret != ID3v2_HEADER_SIZE) {
avio_seek(s->pb, off, SEEK_SET);
avio_seek(pb, off, SEEK_SET);
break;
}
found_header = ff_id3v2_match(buf, magic);
@ -890,15 +900,27 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic,
((buf[7] & 0x7f) << 14) |
((buf[8] & 0x7f) << 7) |
(buf[9] & 0x7f);
id3v2_parse(s, len, buf[3], buf[5], extra_meta);
id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta);
} else {
avio_seek(s->pb, off, SEEK_SET);
avio_seek(pb, off, SEEK_SET);
}
} while (found_header);
ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv);
ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
merge_date(&s->metadata);
ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv);
ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv);
ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv);
merge_date(metadata);
}
void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata,
const char *magic, ID3v2ExtraMeta **extra_meta)
{
id3v2_read_internal(pb, metadata, NULL, magic, extra_meta);
}
void ff_id3v2_read(AVFormatContext *s, const char *magic,
ID3v2ExtraMeta **extra_meta)
{
id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta);
}
void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)

View File

@ -95,7 +95,21 @@ int ff_id3v2_match(const uint8_t *buf, const char *magic);
int ff_id3v2_tag_len(const uint8_t *buf);
/**
* Read an ID3v2 tag, including supported extra metadata
* Read an ID3v2 tag into specified dictionary and retrieve supported extra metadata.
*
* Chapters are not currently read by this variant.
*
* @param metadata Parsed metadata is stored here
* @param extra_meta If not NULL, extra metadata is parsed into a list of
* ID3v2ExtraMeta structs and *extra_meta points to the head of the list
*/
void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, const char *magic, ID3v2ExtraMeta **extra_meta);
/**
* Read an ID3v2 tag, including supported extra metadata and chapters.
*
* Data is read from and stored to AVFormatContext.
*
* @param extra_meta If not NULL, extra metadata is parsed into a list of
* ID3v2ExtraMeta structs and *extra_meta points to the head of the list
*/