summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@iki.fi>2013-12-30 10:02:59 +0200
committerAnssi Hannula <anssi.hannula@iki.fi>2013-12-31 05:15:19 +0200
commit7fdf245ab9cb06b7195b8554e0b56f0835d1a142 (patch)
treee91badc5330c75886bd492ecfce3b43bd665d1ea
parentd52882faef368264f9fe5a595274ec84d3446132 (diff)
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>
-rw-r--r--libavformat/id3v2.c106
-rw-r--r--libavformat/id3v2.h16
2 files changed, 79 insertions, 43 deletions
diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index 54c85d13b6..3cd23c675a 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -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)
diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h
index 58970a1bb7..935e934311 100644
--- a/libavformat/id3v2.h
+++ b/libavformat/id3v2.h
@@ -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
*/