From 7fdf245ab9cb06b7195b8554e0b56f0835d1a142 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 30 Dec 2013 10:02:59 +0200 Subject: 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 --- libavformat/id3v2.c | 106 +++++++++++++++++++++++++++++++--------------------- libavformat/id3v2.h | 16 +++++++- 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 */ -- cgit v1.2.3