[FFmpeg-devel] Subject: [PATCH] Process compressed id3v2 tags.
Adrian Drzewiecki
adrian.drzewiecki at gmail.com
Fri Dec 2 06:50:48 CET 2011
ID3v2.4 allows for zlib compressed tags, but libavformat skips them.
Implement code to inflate compressed tags.
---
libavformat/id3v2.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 89 insertions(+), 8 deletions(-)
diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index bb8819f..651b1db 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -33,6 +33,8 @@
#include "libavutil/dict.h"
#include "avio_internal.h"
+#include <zlib.h>
+
const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
{ "TALB", "album"},
{ "TCOM", "composer"},
@@ -419,6 +421,16 @@ static const ID3v2EMFunc
*get_extra_meta_func(const char *tag, int isv34)
return &id3v2_extra_meta_funcs[i];
}
+static void *ff_id3v2_zalloc(void *opaque, unsigned int items,
unsigned int size)
+{
+ return av_malloc(items * size);
+}
+
+static void ff_id3v2_zfree(void *opaque, void *ptr)
+{
+ av_free(ptr);
+}
+
static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t
version, uint8_t flags, ID3v2ExtraMeta **extra_meta)
{
int isv34, unsync;
@@ -476,6 +488,8 @@ 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 dlen;
if (isv34) {
avio_read(s->pb, tag, 4);
@@ -509,24 +523,91 @@ 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;
- if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
- av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed
ID3v2 frame %s.\n", tag);
+ if (tflags & ID3v2_FLAG_ENCRYPTION) {
+ av_log(s, AV_LOG_WARNING, "Skipping encrypted ID3v2 frame
%s.\n", 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 (tcomp) {
+ z_stream zstrm;
+ int err;
+
+ av_log(s, AV_LOG_INFO, "Compresssed frame %s
tlen=%d dlen=%d\n", tag, tlen, dlen);
+
+ zstrm.next_in = NULL;
+ zstrm.avail_in = 0;
+ zstrm.zalloc = ff_id3v2_zalloc;
+ zstrm.zfree = ff_id3v2_zfree;
+ zstrm.next_out = buffer;
+ zstrm.avail_out = buffer_size;
+
+ 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 = sizeof *buf;
+ if (n > tlen)
+ n = 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;
+ }
+ }
+ }
+
+ 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