Generic metadata API.
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 4 Jan 2009 18:48:37 +0000 (18:48 +0000)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 4 Jan 2009 18:48:37 +0000 (18:48 +0000)
avi is updated as example.
No version bump, the API still might change slightly ...
No update to ffmpeg.c as requested by aurel.

Originally committed as revision 16424 to svn://svn.ffmpeg.org/ffmpeg/trunk

libavcodec/Makefile
libavcodec/avcodec.h
libavcodec/metadata.c [new file with mode: 0644]
libavcodec/metadata.h [new file with mode: 0644]
libavformat/avformat.h
libavformat/avidec.c
libavformat/avienc.c
libavformat/utils.c

index e4217d6..d7a1b4b 100644 (file)
@@ -14,6 +14,7 @@ OBJS = allcodecs.o                                                      \
        faanidct.o                                                       \
        imgconvert.o                                                     \
        jrevdct.o                                                        \
        faanidct.o                                                       \
        imgconvert.o                                                     \
        jrevdct.o                                                        \
+       metadata.o                                                       \
        opt.o                                                            \
        parser.o                                                         \
        raw.o                                                            \
        opt.o                                                            \
        parser.o                                                         \
        raw.o                                                            \
index e45f7d6..7038d2d 100644 (file)
@@ -400,6 +400,51 @@ enum SampleFormat {
  */
 #define FF_MIN_BUFFER_SIZE 16384
 
  */
 #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 a demuxer should not knowingly produce
+ *    several keys that are litterally different but semantically identical,
+ *    like key=Author5, key=Author6.
+ *    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, changing key or value leads to undefined behavior.
+ */
+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.
  */
 /**
  * motion estimation type.
  */
diff --git a/libavcodec/metadata.c b/libavcodec/metadata.c
new file mode 100644 (file)
index 0000000..d223d7c
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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;
+}
diff --git a/libavcodec/metadata.h b/libavcodec/metadata.h
new file mode 100644 (file)
index 0000000..0ab39cb
--- /dev/null
@@ -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 /* AVCODEC_METADATA_H */
index 3f4271a..46e6924 100644 (file)
@@ -608,6 +608,8 @@ typedef struct AVFormatContext {
     struct AVPacketList *raw_packet_buffer_end;
 
     struct AVPacketList *packet_buffer_end;
     struct AVPacketList *raw_packet_buffer_end;
 
     struct AVPacketList *packet_buffer_end;
+
+    struct AVMetaData *meta_data;
 } AVFormatContext;
 
 typedef struct AVPacketList {
 } AVFormatContext;
 
 typedef struct AVPacketList {
index 83c01a9..ebb1a4c 100644 (file)
@@ -216,13 +216,17 @@ static void clean_index(AVFormatContext *s){
     }
 }
 
     }
 }
 
-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);
     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);
     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)
 }
 
 static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
@@ -235,7 +239,6 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
     int i;
     AVStream *st;
     AVIStream *ast = NULL;
     int i;
     AVStream *st;
     AVIStream *ast = NULL;
-    char str_track[4];
     int avih_width=0, avih_height=0;
     int amv_file_format=0;
 
     int avih_width=0, avih_height=0;
     int amv_file_format=0;
 
@@ -561,26 +564,25 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
             url_fseek(pb, size, SEEK_CUR);
             break;
         case MKTAG('I', 'N', 'A', 'M'):
             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'):
             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'):
             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'):
             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'):
             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'):
             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'):
             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){
             break;
         default:
             if(size > 1000000){
index a672e8d..4ae608f 100644 (file)
@@ -103,6 +103,15 @@ static void avi_write_info_tag(ByteIOContext *pb, const char *tag, const char *s
     }
 }
 
     }
 }
 
+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;
 static int avi_write_counters(AVFormatContext* s, int riff_id)
 {
     ByteIOContext *pb = s->pb;
@@ -332,17 +341,13 @@ static int avi_write_header(AVFormatContext *s)
 
     list2 = start_tag(pb, "LIST");
     put_tag(pb, "INFO");
 
     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);
     if(!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT))
         avi_write_info_tag(pb, "ISFT", LIBAVFORMAT_IDENT);
     end_tag(pb, list2);
index 80171ca..9bee5cd 100644 (file)
@@ -21,6 +21,7 @@
 #include "avformat.h"
 #include "internal.h"
 #include "libavcodec/opt.h"
 #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>
 #include "libavutil/avstring.h"
 #include "riff.h"
 #include <sys/time.h>
@@ -2305,6 +2306,14 @@ void av_close_input_stream(AVFormatContext *s)
         av_free(s->chapters[s->nb_chapters]);
     }
     av_freep(&s->chapters);
         av_free(s->chapters[s->nb_chapters]);
     }
     av_freep(&s->chapters);
+    if(s->meta_data){
+        while(s->meta_data->count--){
+            av_freep(&s->meta_data->elems[s->meta_data->count].key);
+            av_freep(&s->meta_data->elems[s->meta_data->count].value);
+        }
+        av_freep(&s->meta_data->elems);
+    }
+    av_freep(&s->meta_data);
     av_free(s);
 }
 
     av_free(s);
 }