[FFmpeg-devel] [PATCH 03/18 v2] avformat/id3v2: allow ID3 parsing without AVFormatContext

Anssi Hannula anssi.hannula at iki.fi
Tue Dec 31 02:53:35 CET 2013


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 at iki.fi>
---

Updated to keep possibly NULL AVFormatContext around for av_logs.


 libavformat/id3v2.c | 101 ++++++++++++++++++++++++++++++----------------------
 libavformat/id3v2.h |  16 ++++++++-
 2 files changed, 74 insertions(+), 43 deletions(-)

diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index 54c85d1..de9e0c4 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -532,6 +532,9 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, char *tta
     int taglen;
     char tag[5];
 
+    if (!s)
+        return;
+
     if (decode_str(s, pb, 0, &dst, &len) < 0)
         return;
     if (len < 16)
@@ -650,16 +653,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 +697,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 +706,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 +722,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 +744,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 +756,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 +775,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 +790,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 +822,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 +835,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 +865,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 ff_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 +882,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 +895,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)
+{
+    ff_id3v2_read_internal(pb, metadata, NULL, magic, extra_meta);
+}
+
+void ff_id3v2_read(AVFormatContext *s, const char *magic,
+                   ID3v2ExtraMeta **extra_meta)
+{
+    ff_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 58970a1..935e934 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
  */
-- 
1.8.1.5



More information about the ffmpeg-devel mailing list