[FFmpeg-devel] [PATCH] Metadata

Michael Niedermayer michaelni
Sat Jan 3 00:55:59 CET 2009


Hi

Attached patch adds generic metadata support to
ffmpeg.c, and the avi muxer and demuxer
The implementation should be pretty much as simple as possible.

Differences to aurels variant
* flat metadata, no trees
* fully abstracted, implementation can be changed with no effect on ABI/API
* only a small set of muxers & demuxers are updated yet.

Comments welcome, especally from aurel.

I plan to commit soon if i receive no suggestions for improvment!

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Into a blind darkness they enter who follow after the Ignorance,
they as if into a greater darkness enter who devote themselves
to the Knowledge alone. -- Isha Upanishad
-------------- next part --------------
Index: ffmpeg.c
===================================================================
--- ffmpeg.c	(revision 16302)
+++ ffmpeg.c	(working copy)
@@ -172,12 +172,8 @@
 static int64_t rec_timestamp = 0;
 static int64_t input_ts_offset = 0;
 static int file_overwrite = 0;
-static char *str_title = NULL;
-static char *str_author = NULL;
-static char *str_copyright = NULL;
-static char *str_comment = NULL;
-static char *str_genre = NULL;
-static char *str_album = NULL;
+static int metadata_count;
+static AVMetaDataTag *metadata;
 static int do_benchmark = 0;
 static int do_hex_dump = 0;
 static int do_pkt_dump = 0;
@@ -1959,6 +1955,7 @@
     for (i=0;i<nb_meta_data_maps;i++) {
         AVFormatContext *out_file;
         AVFormatContext *in_file;
+        AVMetaDataTag *mtag;
 
         int out_file_index = meta_data_maps[i].out_file;
         int in_file_index = meta_data_maps[i].in_file;
@@ -1984,6 +1981,10 @@
         out_file->year = in_file->year;
         out_file->track = in_file->track;
         strcpy(out_file->genre, in_file->genre);
+
+        mtag=NULL;
+        while((mtag=av_metadata_get(in_file->meta_data, "", mtag, AV_METADATA_IGNORE_SUFFIX)))
+            av_metadata_set(&out_file->meta_data, *mtag);
     }
 
     /* open files and write file headers */
@@ -2510,6 +2511,24 @@
     frame_aspect_ratio = ar;
 }
 
+static int opt_metadata(const char *opt, const char *arg)
+{
+    char *mid= strchr(arg, '=');
+
+    if(!mid){
+        fprintf(stderr, "Missing =\n");
+        av_exit(1);
+    }
+    *mid++= 0;
+
+    metadata_count++;
+    metadata= av_realloc(metadata, sizeof(*metadata)*metadata_count);
+    metadata[metadata_count-1].key  = av_strdup(arg);
+    metadata[metadata_count-1].value= av_strdup(mid);
+
+    return 0;
+}
+
 static void opt_qscale(const char *arg)
 {
     video_qscale = atof(arg);
@@ -3303,18 +3322,9 @@
 
         oc->timestamp = rec_timestamp;
 
-        if (str_title)
-            av_strlcpy(oc->title, str_title, sizeof(oc->title));
-        if (str_author)
-            av_strlcpy(oc->author, str_author, sizeof(oc->author));
-        if (str_copyright)
-            av_strlcpy(oc->copyright, str_copyright, sizeof(oc->copyright));
-        if (str_comment)
-            av_strlcpy(oc->comment, str_comment, sizeof(oc->comment));
-        if (str_album)
-            av_strlcpy(oc->album, str_album, sizeof(oc->album));
-        if (str_genre)
-            av_strlcpy(oc->genre, str_genre, sizeof(oc->genre));
+        for(; metadata_count>0; metadata_count--){
+            av_metadata_set(&oc->meta_data, metadata[metadata_count-1]);
+        }
     }
 
     output_files[nb_output_files++] = oc;
@@ -3739,13 +3749,8 @@
     { "ss", OPT_FUNC2 | HAS_ARG, {(void*)opt_start_time}, "set the start time offset", "time_off" },
     { "itsoffset", OPT_FUNC2 | HAS_ARG, {(void*)opt_input_ts_offset}, "set the input ts offset", "time_off" },
     { "itsscale", HAS_ARG, {(void*)opt_input_ts_scale}, "set the input ts scale", "stream:scale" },
-    { "title", HAS_ARG | OPT_STRING, {(void*)&str_title}, "set the title", "string" },
     { "timestamp", OPT_FUNC2 | HAS_ARG, {(void*)&opt_rec_timestamp}, "set the timestamp", "time" },
-    { "author", HAS_ARG | OPT_STRING, {(void*)&str_author}, "set the author", "string" },
-    { "copyright", HAS_ARG | OPT_STRING, {(void*)&str_copyright}, "set the copyright", "string" },
-    { "comment", HAS_ARG | OPT_STRING, {(void*)&str_comment}, "set the comment", "string" },
-    { "genre", HAS_ARG | OPT_STRING, {(void*)&str_genre}, "set the genre", "string" },
-    { "album", HAS_ARG | OPT_STRING, {(void*)&str_album}, "set the album", "string" },
+    { "metadata", OPT_FUNC2 | HAS_ARG, {(void*)&opt_metadata}, "add metadata", "string=string" },
     { "dframes", OPT_INT | HAS_ARG, {(void*)&max_frames[CODEC_TYPE_DATA]}, "set the number of data frames to record", "number" },
     { "benchmark", OPT_BOOL | OPT_EXPERT, {(void*)&do_benchmark},
       "add timings for benchmarking" },
Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile	(revision 16349)
+++ libavcodec/Makefile	(working copy)
@@ -21,6 +21,7 @@
        resample2.o                                                      \
        simple_idct.o                                                    \
        utils.o                                                          \
+       metadata.o                                                       \
 
 # parts needed for many different codecs
 OBJS-$(CONFIG_AANDCT)                  += aandcttab.o
Index: libavcodec/metadata.c
===================================================================
--- libavcodec/metadata.c	(revision 0)
+++ libavcodec/metadata.c	(revision 0)
@@ -0,0 +1,72 @@
+/*
+ * copyright (c) 2009 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "metadata.h"
+
+AVMetaDataTag *av_metadata_get(struct AVMetaData *m, const char *key, const AVMetaDataTag *prev, int flags){
+    unsigned int i, j;
+
+    if(!m)
+        return NULL;
+
+    if(prev) i= prev - m->elems + 1;
+    else     i= 0;
+
+    for(; i<m->count; i++){
+        const char *s= m->elems[i].key;
+        if(flags & AV_METADATA_IGNORE_CASE) for(j=0; toupper(s[j]) == toupper(key[j]) && key[j]; j++);
+        else                                for(j=0;         s[j]  ==         key[j]  && key[j]; j++);
+        if(key[j])
+            continue;
+        if(s[j] && !(flags & AV_METADATA_IGNORE_SUFFIX))
+            continue;
+        return &m->elems[i];
+    }
+    return NULL;
+}
+
+int av_metadata_set(struct AVMetaData **pm, AVMetaDataTag elem){
+    struct AVMetaData *m= *pm;
+    AVMetaDataTag *tag= av_metadata_get(m, elem.key, NULL, 0);
+
+    if(!m)
+        m=*pm= av_mallocz(sizeof(*m));
+
+    if(tag){
+        av_free(tag->value);
+        av_free(tag->key);
+        *tag= m->elems[--m->count];
+    }else{
+        AVMetaDataTag *tmp= av_realloc(m->elems, (m->count+1) * sizeof(*m->elems));
+        if(tmp){
+            m->elems= tmp;
+        }else
+            return AVERROR(ENOMEM);
+    }
+    if(elem.value){
+        elem.key  = av_strdup(elem.key  );
+        elem.value= av_strdup(elem.value);
+        m->elems[m->count++]= elem;
+    }
+    if(!m->count)
+        av_freep(pm);
+
+    return 0;
+}
Index: libavcodec/metadata.h
===================================================================
--- libavcodec/metadata.h	(revision 0)
+++ libavcodec/metadata.h	(revision 0)
@@ -0,0 +1,38 @@
+/*
+ * copyright (c) 2009 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_METADATA_H
+#define AVCODEC_METADATA_H
+
+/**
+ * @file metadata.h
+ * internal metadata API header
+ * see avcodec.h or the public API!
+ */
+
+
+#include "avcodec.h"
+
+struct AVMetaData{
+    int count;
+    AVMetaDataTag *elems;
+};
+
+#endif
Index: libavcodec/avcodec.h
===================================================================
--- libavcodec/avcodec.h	(revision 16302)
+++ libavcodec/avcodec.h	(working copy)
@@ -400,7 +400,50 @@
  */
 #define FF_MIN_BUFFER_SIZE 16384
 
+
+/*
+ * public Metadata API.
+ * Important concepts, to keep in mind
+ * 1. keys are unique, there are never 2 tags with equal keys, this is also
+ *    meant semantically that is key=Author, key=Author2 is illegal as well.
+ *    All authors have to be placed in the same tag for the case of Authors.
+ * 2. Metadata is flat, there are no subtags, if you for whatever obscene
+ *    reason want to store the email address of the child of producer alice
+ *    and actor bob, that could have key=alice_and_bobs_childs_email_address.
+ * 3. A tag whichs value is translated has the ISO 639 3-letter language code
+ *    with a '-' between appended. So for example Author-ger=Michael, Author-eng=Mike
+ *    the original/default language is in the unqualified "Author"
+ *    A demuxer should set a default if it sets any translated tag.
+ */
+
+#define AV_METADATA_IGNORE_CASE     1
+#define AV_METADATA_IGNORE_SUFFIX   2
+
+typedef struct {
+    char *key;
+    char *value;
+}AVMetaDataTag;
+
+struct AVMetaData;
+
 /**
+ * gets a metadata element with matching key.
+ * @param prev set to the previous matching element to find the next.
+ * @param flags allows case as well as suffix insensitive comparissions.
+ * @return found tag or NULL, the value of the tag may be av_realloced or
+ *         changed by the caller, the key MUST NOT be changed.
+ */
+AVMetaDataTag *av_metadata_get(struct AVMetaData *m, const char *key, const AVMetaDataTag *prev, int flags);
+
+/**
+ * sets the given tag in m, overwriting an existing tag.
+ * @param tag tag to add to m, key and value will be av_strduped.
+ * @return >= 0 if success otherwise error code that is <0.
+ */
+int av_metadata_set(struct AVMetaData **m, AVMetaDataTag tag);
+
+
+/**
  * motion estimation type.
  */
 enum Motion_Est_ID {
Index: libavformat/utils.c
===================================================================
--- libavformat/utils.c	(revision 16397)
+++ libavformat/utils.c	(working copy)
@@ -21,6 +21,7 @@
 #include "avformat.h"
 #include "internal.h"
 #include "libavcodec/opt.h"
+#include "libavcodec/metadata.h"
 #include "libavutil/avstring.h"
 #include "riff.h"
 #include <sys/time.h>
@@ -2305,6 +2306,9 @@
         av_free(s->chapters[s->nb_chapters]);
     }
     av_freep(&s->chapters);
+    if(s->meta_data)
+        av_freep(&s->meta_data->elems);
+    av_freep(&s->meta_data);
     av_free(s);
 }
 
Index: libavformat/avformat.h
===================================================================
--- libavformat/avformat.h	(revision 16397)
+++ libavformat/avformat.h	(working copy)
@@ -22,8 +22,8 @@
 #define AVFORMAT_AVFORMAT_H
 
 #define LIBAVFORMAT_VERSION_MAJOR 52
-#define LIBAVFORMAT_VERSION_MINOR 23
-#define LIBAVFORMAT_VERSION_MICRO  1
+#define LIBAVFORMAT_VERSION_MINOR 24
+#define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
@@ -608,6 +608,8 @@
     struct AVPacketList *raw_packet_buffer_end;
 
     struct AVPacketList *packet_buffer_end;
+
+    struct AVMetaData *meta_data;
 } AVFormatContext;
 
 typedef struct AVPacketList {
Index: libavformat/avidec.c
===================================================================
--- libavformat/avidec.c	(revision 16397)
+++ libavformat/avidec.c	(working copy)
@@ -216,13 +216,17 @@
     }
 }
 
-static int avi_read_tag(ByteIOContext *pb, char *buf, int maxlen,  unsigned int size)
+static int avi_read_tag(AVFormatContext *s, const char *key, unsigned int size)
 {
+    ByteIOContext *pb = s->pb;
+    uint8_t value[1024];
+
     int64_t i = url_ftell(pb);
     size += (size & 1);
-    get_strz(pb, buf, maxlen);
+    get_strz(pb, value, sizeof(value));
     url_fseek(pb, i+size, SEEK_SET);
-    return 0;
+
+    return av_metadata_set(&s->meta_data, (const AVMetaDataTag){key, value});
 }
 
 static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
@@ -235,7 +239,6 @@
     int i;
     AVStream *st;
     AVIStream *ast = NULL;
-    char str_track[4];
     int avih_width=0, avih_height=0;
     int amv_file_format=0;
 
@@ -561,26 +564,25 @@
             url_fseek(pb, size, SEEK_CUR);
             break;
         case MKTAG('I', 'N', 'A', 'M'):
-            avi_read_tag(pb, s->title, sizeof(s->title), size);
+            avi_read_tag(s, "Title", size);
             break;
         case MKTAG('I', 'A', 'R', 'T'):
-            avi_read_tag(pb, s->author, sizeof(s->author), size);
+            avi_read_tag(s, "Artist", size);
             break;
         case MKTAG('I', 'C', 'O', 'P'):
-            avi_read_tag(pb, s->copyright, sizeof(s->copyright), size);
+            avi_read_tag(s, "Copyright", size);
             break;
         case MKTAG('I', 'C', 'M', 'T'):
-            avi_read_tag(pb, s->comment, sizeof(s->comment), size);
+            avi_read_tag(s, "Comment", size);
             break;
         case MKTAG('I', 'G', 'N', 'R'):
-            avi_read_tag(pb, s->genre, sizeof(s->genre), size);
+            avi_read_tag(s, "Genre", size);
             break;
         case MKTAG('I', 'P', 'R', 'D'):
-            avi_read_tag(pb, s->album, sizeof(s->album), size);
+            avi_read_tag(s, "Album", size);
             break;
         case MKTAG('I', 'P', 'R', 'T'):
-            avi_read_tag(pb, str_track, sizeof(str_track), size);
-            sscanf(str_track, "%d", &s->track);
+            avi_read_tag(s, "Track", size);
             break;
         default:
             if(size > 1000000){
Index: libavformat/avienc.c
===================================================================
--- libavformat/avienc.c	(revision 16397)
+++ libavformat/avienc.c	(working copy)
@@ -103,6 +103,15 @@
     }
 }
 
+static void avi_write_info_tag2(AVFormatContext *s, const char *fourcc, const char *key1, const char *key2)
+{
+    AVMetaDataTag *tag= av_metadata_get(s->meta_data, key1, NULL, AV_METADATA_IGNORE_CASE);
+    if(!tag && key2)
+        tag= av_metadata_get(s->meta_data, key2, NULL, AV_METADATA_IGNORE_CASE);
+    if(tag)
+        avi_write_info_tag(s->pb, fourcc, tag->value);
+}
+
 static int avi_write_counters(AVFormatContext* s, int riff_id)
 {
     ByteIOContext *pb = s->pb;
@@ -332,17 +341,13 @@
 
     list2 = start_tag(pb, "LIST");
     put_tag(pb, "INFO");
-    avi_write_info_tag(pb, "INAM", s->title);
-    avi_write_info_tag(pb, "IART", s->author);
-    avi_write_info_tag(pb, "ICOP", s->copyright);
-    avi_write_info_tag(pb, "ICMT", s->comment);
-    avi_write_info_tag(pb, "IPRD", s->album);
-    avi_write_info_tag(pb, "IGNR", s->genre);
-    if (s->track) {
-        char str_track[4];
-        snprintf(str_track, 4, "%d", s->track);
-        avi_write_info_tag(pb, "IPRT", str_track);
-    }
+    avi_write_info_tag2(s, "INAM", "Title", NULL);
+    avi_write_info_tag2(s, "IART", "Artist", "Author");
+    avi_write_info_tag2(s, "ICOP", "Copyright", NULL);
+    avi_write_info_tag2(s, "ICMT", "Comment", NULL);
+    avi_write_info_tag2(s, "IPRD", "Album", NULL);
+    avi_write_info_tag2(s, "IGNR", "Genre", NULL);
+    avi_write_info_tag2(s, "IPRT", "Track", NULL);
     if(!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT))
         avi_write_info_tag(pb, "ISFT", LIBAVFORMAT_IDENT);
     end_tag(pb, list2);
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20090103/1c6e829d/attachment.pgp>



More information about the ffmpeg-devel mailing list