[FFmpeg-devel] [PATCH] Rudimentary support for id3v2 APIC tags.
Adrian Drzewiecki
adrian.drzewiecki at gmail.com
Tue Dec 27 18:50:22 CET 2011
Extend dictionary to support binary data. Then use that to store ID3 APIC (image)
tags.
---
libavformat/id3v2.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++
libavutil/dict.c | 73 ++++++++++++++++++++++++++-------
libavutil/dict.h | 19 +++++++++
3 files changed, 189 insertions(+), 15 deletions(-)
diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index 9ee3271..6122b5d 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -274,6 +274,115 @@ static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const cha
av_dict_set(&s->metadata, key, dst, dict_flags);
}
+/*
+ * Parse an image tag.
+ * The image will be prefixed by the mime-type string (\0 terminated) and
+ * 32bit byte size of the image.
+ */
+static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, const char *tag)
+{
+ /* from http://id3.org/id3v2.4.0-frames #4.14 */
+ const static char *key[] = {
+ "other_picture",
+ "32x32_icon",
+ "other_icon",
+ "front_cover",
+ "back_cover",
+ "leaflet_page",
+ "media_picture",
+ "lead_performer_picture",
+ "artist_picture",
+ "conductor_picture",
+ "band_picture",
+ "compose_picture",
+ "lyricist_picture",
+ "location_picture",
+ "recording_picture",
+ "performance_picture",
+ "screen_capture",
+ "a_bright_coloured_fish",
+ "illustration",
+ "band_logo",
+ "studio_logo"
+ };
+ static int max_key = sizeof(key)/sizeof(key[0]);
+ char *mime_type = NULL;
+ unsigned char text_encoding;
+ unsigned char picture_type;
+ int i, len;
+ void *data = NULL;
+
+ text_encoding = avio_r8(pb);
+ taglen --;
+
+ /* read mime type */
+ i = len = 0;
+ for (;;) {
+ unsigned char c = avio_r8(pb);
+ taglen --;
+ if (i == len) {
+ len = len ? (2 * len) : 8;
+ mime_type = av_realloc_f(mime_type, len, 1);
+ if (!mime_type) {
+ av_log(s, AV_LOG_ERROR, "Alloc of mime type failed\n");
+ return;
+ }
+ }
+ mime_type[i++] = c;
+ if (!c)
+ break;
+ if (!taglen) {
+ av_log(s, AV_LOG_ERROR, "Couldn't find end of mime type\n");
+ av_free(mime_type);
+ return;
+ }
+ }
+ len = i;
+
+ if (!taglen) {
+ av_log(s, AV_LOG_ERROR, "Truncated APIC tag.\n");
+ av_free(mime_type);
+ return;
+ }
+
+ /* which tag */
+ picture_type = avio_r8(pb);
+ taglen --;
+
+ av_log(s, AV_LOG_INFO, "Found %s APIC\n", mime_type);
+
+ if (picture_type >= max_key || !key[picture_type]) {
+ av_log(s, AV_LOG_INFO, "Unsupported picture type %x\n", picture_type);
+ av_free(mime_type);
+ return;
+ }
+
+ av_log(s, AV_LOG_INFO, "APIC is %s\n", key[picture_type]);
+
+ /* skip over description */
+ while (taglen && avio_r8(pb))
+ taglen --;
+
+ if (!taglen) {
+ av_log(s, AV_LOG_ERROR, "Truncated APIC tag.\n");
+ av_free(mime_type);
+ return;
+ }
+
+ data = av_malloc(taglen);
+ if (!data) {
+ av_log(s, AV_LOG_ERROR, "Failed to allocate image buffer.\n");
+ av_free(mime_type);
+ return;
+ }
+
+ avio_read(pb, data, taglen);
+
+ av_log(s, AV_LOG_INFO, "Read image buffer of %d bytes\n", taglen);
+ av_dict_set_data(&s->metadata, key[picture_type], mime_type, data, taglen,
+ AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_COPY_DATA);
+}
+
/**
* Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
*/
@@ -597,6 +706,9 @@ static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t
/* parse special meta tag */
extra_func->read(s, pbx, tlen, tag, extra_meta);
}
+ else if (!strcmp(tag, "APIC"))
+ /* an image! */
+ read_apic(s, pbx, tlen, tag);
else if (!tag[0]) {
if (tag[1])
av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
diff --git a/libavutil/dict.c b/libavutil/dict.c
index 6177ddd..55b7c43 100644
--- a/libavutil/dict.c
+++ b/libavutil/dict.c
@@ -49,12 +49,21 @@ av_dict_get(AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
{
+ return av_dict_set_data(pm, key, value, NULL, 0, flags);
+}
+
+int av_dict_set_data(AVDictionary **pm, const char *key, const char *value,
+ const void *data, int size, int flags)
+{
AVDictionary *m = *pm;
AVDictionaryEntry *tag = av_dict_get(m, key, NULL, flags);
char *oldval = NULL;
- if(!m)
+ if(!m) {
m = *pm = av_mallocz(sizeof(*m));
+ if (!m)
+ return AVERROR(ENOMEM);
+ }
if(tag) {
if (flags & AV_DICT_DONT_OVERWRITE)
@@ -63,38 +72,71 @@ int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags
oldval = tag->value;
else
av_free(tag->value);
+ av_free(tag->data);
av_free(tag->key);
*tag = m->elems[--m->count];
} else {
- AVDictionaryEntry *tmp = av_realloc(m->elems, (m->count+1) * sizeof(*m->elems));
- if(tmp) {
- m->elems = tmp;
- } else
+ tag = av_realloc(m->elems, (m->count+1) * sizeof(*m->elems));
+ if (!tag)
return AVERROR(ENOMEM);
+ m->elems = tag;
}
+
if (value) {
- if (flags & AV_DICT_DONT_STRDUP_KEY) {
- m->elems[m->count].key = (char*)(intptr_t)key;
- } else
- m->elems[m->count].key = av_strdup(key );
+ tag = &m->elems[m->count];
+ memset(tag, 0, sizeof *tag);
+
+ if (flags & AV_DICT_DONT_STRDUP_KEY)
+ tag->key = (char*)(intptr_t)key;
+ else {
+ tag->key = av_strdup(key);
+ if (!tag->key)
+ goto nomem;
+ }
+
if (flags & AV_DICT_DONT_STRDUP_VAL) {
- m->elems[m->count].value = (char*)(intptr_t)value;
+ tag->value = (char*)(intptr_t)value;
} else if (oldval && flags & AV_DICT_APPEND) {
int len = strlen(oldval) + strlen(value) + 1;
if (!(oldval = av_realloc(oldval, len)))
- return AVERROR(ENOMEM);
+ goto nomem;
av_strlcat(oldval, value, len);
- m->elems[m->count].value = oldval;
- } else
- m->elems[m->count].value = av_strdup(value);
+ tag->value = oldval;
+ oldval = NULL;
+ } else {
+ tag->value = av_strdup(value);
+ if (!tag->value)
+ goto nomem;
+ }
+
+ if (data) {
+ if (flags & AV_DICT_DONT_COPY_DATA)
+ tag->data = (void *)(intptr_t)data;
+ else {
+ tag->data = av_malloc(size);
+ if (!tag->data)
+ goto nomem;
+ memcpy(tag->data, data, size);
+ }
+ }
+
+ tag->size = size;
m->count++;
}
+
if (!m->count) {
av_free(m->elems);
av_freep(pm);
}
return 0;
+nomem:
+ if (!(flags & AV_DICT_DONT_STRDUP_KEY))
+ av_free(tag->key);
+ if (!(flags & AV_DICT_DONT_STRDUP_VAL))
+ av_free(tag->value);
+ av_free(oldval);
+ return AVERROR(ENOMEM);
}
void av_dict_free(AVDictionary **pm)
@@ -105,6 +147,7 @@ void av_dict_free(AVDictionary **pm)
while(m->count--) {
av_free(m->elems[m->count].key);
av_free(m->elems[m->count].value);
+ av_free(m->elems[m->count].data);
}
av_free(m->elems);
}
@@ -116,5 +159,5 @@ void av_dict_copy(AVDictionary **dst, AVDictionary *src, int flags)
AVDictionaryEntry *t = NULL;
while ((t = av_dict_get(src, "", t, AV_DICT_IGNORE_SUFFIX)))
- av_dict_set(dst, t->key, t->value, flags);
+ av_dict_set_data(dst, t->key, t->value, t->data, t->size, flags);
}
diff --git a/libavutil/dict.h b/libavutil/dict.h
index 2adf28c..6963594 100644
--- a/libavutil/dict.h
+++ b/libavutil/dict.h
@@ -74,9 +74,13 @@
#define AV_DICT_APPEND 32 /**< If the entry already exists, append to it. Note that no
delimiter is added, the strings are simply concatenated. */
+#define AV_DICT_DONT_COPY_DATA 64 /**< Do not copy entry data */
+
typedef struct {
char *key;
char *value;
+ void *data;
+ int size;
} AVDictionaryEntry;
typedef struct AVDictionary AVDictionary;
@@ -105,6 +109,21 @@ av_dict_get(AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);
/**
+ * Set the given entry in *pm, overwriting an existing entry.
+ *
+ * @param pm pointer to a pointer to a dictionary struct. If *pm is NULL
+ * a dictionary struct is allocated and put in *pm.
+ * @param key entry key to add to *pm (will be av_strduped depending on flags)
+ * @param value entry value to add to *pm (will be av_strduped depending on flags).
+ * Passing a NULL value will cause an existing tag to be deleted.
+ * @param data additional value to associate with key
+ * @param size size of additional data
+ * @return >= 0 on success otherwise an error code <0
+ */
+int av_dict_set_data(AVDictionary **pm, const char *key, const char *value,
+ const void *data, int size, int flags);
+
+/**
* Copy entries from one AVDictionary struct into another.
* @param dst pointer to a pointer to a AVDictionary struct. If *dst is NULL,
* this function will allocate a struct for you and put it in *dst
--
1.7.8.1
More information about the ffmpeg-devel
mailing list