[FFmpeg-devel] Subject: [PATCH] Process compressed id3v2 tags.

Adrian Drzewiecki adrian.drzewiecki at gmail.com
Sat Dec 3 19:46:46 CET 2011


Here's the updated patch:

Subject: [PATCH] Process compressed id3v2 tags.

ID3v2.4 allows for zlib compressed tags, but libavformat skips them.
Implement code to inflate compressed tags.
---
 libavformat/id3v2.c |  114 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 106 insertions(+), 8 deletions(-)

diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index bb8819f..44b77c5 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -26,6 +26,12 @@
  * http://id3.org/Developer_Information
  */

+#include "config.h"
+
+#if CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
 #include "id3v2.h"
 #include "id3v1.h"
 #include "libavutil/avstring.h"
@@ -419,6 +425,18 @@ static const ID3v2EMFunc
*get_extra_meta_func(const char *tag, int isv34)
     return &id3v2_extra_meta_funcs[i];
 }

+#if CONFIG_ZLIB
+static void *id3v2_zalloc(void *opaque, unsigned int items, unsigned int size)
+{
+    return av_calloc(items, size);
+}
+
+static void id3v2_zfree(void *opaque, void *ptr)
+{
+    av_free(ptr);
+}
+#endif
+
 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t
version, uint8_t flags, ID3v2ExtraMeta **extra_meta)
 {
     int isv34, unsync;
@@ -476,6 +494,9 @@ static void ff_id3v2_parse(AVFormatContext *s, int
len, uint8_t version, uint8_t
     while (len >= taghdrlen) {
         unsigned int tflags = 0;
         int tunsync = 0;
+        int tcomp = 0;
+        int tencr = 0;
+        int dlen;

         if (isv34) {
             avio_read(s->pb, tag, 4);
@@ -509,24 +530,101 @@ static void ff_id3v2_parse(AVFormatContext *s,
int len, uint8_t version, uint8_t
         if (tflags & ID3v2_FLAG_DATALEN) {
             if (tlen < 4)
                 break;
-            avio_rb32(s->pb);
+            dlen = avio_rb32(s->pb);
             tlen -= 4;
-        }
+        } else
+            dlen = tlen;
+
+        tcomp = tflags & ID3v2_FLAG_COMPRESSION;
+        tencr = tflags & ID3v2_FLAG_ENCRYPTION;
+
+        /* skip encrypted tags and, if no zlib, compressed tags */
+        if (tencr || (!CONFIG_ZLIB && tcomp)) {
+            char *type;
+            if (!tcomp)
+                type = "encrypted";
+            else if (!tencr)
+                type = "compressed";
+            else
+                type = "encrypted and compressed";

-        if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
-            av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed
ID3v2 frame %s.\n", tag);
+            av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame
%s.\n", type, tag);
             avio_skip(s->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)))) {
-            if (unsync || tunsync) {
+            if (unsync || tunsync || tcomp) {
                 int i, j;
-                av_fast_malloc(&buffer, &buffer_size, tlen);
+                av_fast_malloc(&buffer, &buffer_size, dlen);
                 if (!buffer) {
                     av_log(s, AV_LOG_ERROR, "Failed to alloc %d
bytes\n", tlen);
                     goto seek;
                 }
-                for (i = 0, j = 0; i < tlen; i++, j++) {
-                    buffer[j] = avio_r8(s->pb);
+#if CONFIG_ZLIB
+                if (tcomp) {
+                    z_stream zstrm = {
+                        .next_in   = NULL,
+                        .avail_in  = 0,
+                        .zalloc    = id3v2_zalloc,
+                        .zfree     = id3v2_zfree,
+                        .next_out  = buffer,
+                        .avail_out = buffer_size,
+                    };
+                    int err;
+
+                    av_log(s, AV_LOG_DEBUG, "Compresssed frame %s
tlen=%d dlen=%d\n", tag, tlen, dlen);
+
+                    err = inflateInit(&zstrm);
+                    if (err != Z_OK) {
+                        av_log(s, AV_LOG_ERROR, "inflate init failed:
%d\n", err);
+                        goto seek;
+                    }
+
+                    for (;;) {
+                        char buf[4096];
+                        int n, end;
+
+                        n = FFMIN(sizeof *buf, tlen);
+
+                        n = avio_read(s->pb, buf, n);
+                        if (n < 0) {
+                            av_log(s, AV_LOG_ERROR, "read failed\n");
+                            goto seek;
+                        }
+
+                        tlen -= n;
+
+                        zstrm.next_in = buf;
+                        zstrm.avail_in = n;
+
+                        end = (zstrm.avail_out == 0);
+
+                        err = inflate(&zstrm, Z_SYNC_FLUSH);
+                        if (err == Z_STREAM_END) {
+                            inflateEnd(&zstrm);
+                            break;
+                        }
+                        if (err != Z_OK) {
+                            av_log(s, AV_LOG_ERROR, "inflate failed;
%d\n", err);
+                            inflateEnd(&zstrm);
+                            goto seek;
+                        }
+                        if (!tlen) {
+                            av_log(s, AV_LOG_ERROR, "inflate reached
end of tag\n");
+                            inflateEnd(&zstrm);
+                            goto seek;
+                        }
+                        if (end) {
+                            av_log(s, AV_LOG_ERROR, "expected end of
uncompressed data\n");
+                            inflateEnd(&zstrm);
+                            goto seek;
+                        }
+                    }
+                }
+#endif
+
+                for (i = 0, j = 0; i < dlen; i++, j++) {
+                    if (tcomp)
+                        buffer[j] = avio_r8(s->pb);
                     if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
                         /* Unsynchronised byte, skip it */
                         j--;
-- 
1.7.7.4


More information about the ffmpeg-devel mailing list