Browse code

Generic metadata API. 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

Michael Niedermayer authored on 2009/01/05 03:48:37
Showing 8 changed files
... ...
@@ -14,6 +14,7 @@ OBJS = allcodecs.o                                                      \
14 14
        faanidct.o                                                       \
15 15
        imgconvert.o                                                     \
16 16
        jrevdct.o                                                        \
17
+       metadata.o                                                       \
17 18
        opt.o                                                            \
18 19
        parser.o                                                         \
19 20
        raw.o                                                            \
... ...
@@ -400,6 +400,51 @@ enum SampleFormat {
400 400
  */
401 401
 #define FF_MIN_BUFFER_SIZE 16384
402 402
 
403
+
404
+/*
405
+ * public Metadata API.
406
+ * Important concepts, to keep in mind
407
+ * 1. keys are unique, there are never 2 tags with equal keys, this is also
408
+ *    meant semantically that is a demuxer should not knowingly produce
409
+ *    several keys that are litterally different but semantically identical,
410
+ *    like key=Author5, key=Author6.
411
+ *    All authors have to be placed in the same tag for the case of Authors.
412
+ * 2. Metadata is flat, there are no subtags, if you for whatever obscene
413
+ *    reason want to store the email address of the child of producer alice
414
+ *    and actor bob, that could have key=alice_and_bobs_childs_email_address.
415
+ * 3. A tag whichs value is translated has the ISO 639 3-letter language code
416
+ *    with a '-' between appended. So for example Author-ger=Michael, Author-eng=Mike
417
+ *    the original/default language is in the unqualified "Author"
418
+ *    A demuxer should set a default if it sets any translated tag.
419
+ */
420
+
421
+#define AV_METADATA_IGNORE_CASE     1
422
+#define AV_METADATA_IGNORE_SUFFIX   2
423
+
424
+typedef struct {
425
+    char *key;
426
+    char *value;
427
+}AVMetaDataTag;
428
+
429
+struct AVMetaData;
430
+
431
+/**
432
+ * gets a metadata element with matching key.
433
+ * @param prev set to the previous matching element to find the next.
434
+ * @param flags allows case as well as suffix insensitive comparissions.
435
+ * @return found tag or NULL, changing key or value leads to undefined behavior.
436
+ */
437
+AVMetaDataTag *
438
+av_metadata_get(struct AVMetaData *m, const char *key, const AVMetaDataTag *prev, int flags);
439
+
440
+/**
441
+ * sets the given tag in m, overwriting an existing tag.
442
+ * @param tag tag to add to m, key and value will be av_strduped.
443
+ * @return >= 0 if success otherwise error code that is <0.
444
+ */
445
+int av_metadata_set(struct AVMetaData **m, AVMetaDataTag tag);
446
+
447
+
403 448
 /**
404 449
  * motion estimation type.
405 450
  */
406 451
new file mode 100644
... ...
@@ -0,0 +1,75 @@
0
+/*
1
+ * copyright (c) 2009 Michael Niedermayer
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+#include "metadata.h"
21
+
22
+AVMetaDataTag *
23
+av_metadata_get(struct AVMetaData *m, const char *key, const AVMetaDataTag *prev, int flags)
24
+{
25
+    unsigned int i, j;
26
+
27
+    if(!m)
28
+        return NULL;
29
+
30
+    if(prev) i= prev - m->elems + 1;
31
+    else     i= 0;
32
+
33
+    for(; i<m->count; i++){
34
+        const char *s= m->elems[i].key;
35
+        if(flags & AV_METADATA_IGNORE_CASE) for(j=0; toupper(s[j]) == toupper(key[j]) && key[j]; j++);
36
+        else                                for(j=0;         s[j]  ==         key[j]  && key[j]; j++);
37
+        if(key[j])
38
+            continue;
39
+        if(s[j] && !(flags & AV_METADATA_IGNORE_SUFFIX))
40
+            continue;
41
+        return &m->elems[i];
42
+    }
43
+    return NULL;
44
+}
45
+
46
+int av_metadata_set(struct AVMetaData **pm, AVMetaDataTag elem)
47
+{
48
+    struct AVMetaData *m= *pm;
49
+    AVMetaDataTag *tag= av_metadata_get(m, elem.key, NULL, 0);
50
+
51
+    if(!m)
52
+        m=*pm= av_mallocz(sizeof(*m));
53
+
54
+    if(tag){
55
+        av_free(tag->value);
56
+        av_free(tag->key);
57
+        *tag= m->elems[--m->count];
58
+    }else{
59
+        AVMetaDataTag *tmp= av_realloc(m->elems, (m->count+1) * sizeof(*m->elems));
60
+        if(tmp){
61
+            m->elems= tmp;
62
+        }else
63
+            return AVERROR(ENOMEM);
64
+    }
65
+    if(elem.value){
66
+        elem.key  = av_strdup(elem.key  );
67
+        elem.value= av_strdup(elem.value);
68
+        m->elems[m->count++]= elem;
69
+    }
70
+    if(!m->count)
71
+        av_freep(pm);
72
+
73
+    return 0;
74
+}
0 75
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+/*
1
+ * copyright (c) 2009 Michael Niedermayer
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+#ifndef AVCODEC_METADATA_H
21
+#define AVCODEC_METADATA_H
22
+
23
+/**
24
+ * @file metadata.h
25
+ * internal metadata API header
26
+ * see avcodec.h or the public API!
27
+ */
28
+
29
+
30
+#include "avcodec.h"
31
+
32
+struct AVMetaData{
33
+    int count;
34
+    AVMetaDataTag *elems;
35
+};
36
+
37
+#endif /* AVCODEC_METADATA_H */
... ...
@@ -608,6 +608,8 @@ typedef struct AVFormatContext {
608 608
     struct AVPacketList *raw_packet_buffer_end;
609 609
 
610 610
     struct AVPacketList *packet_buffer_end;
611
+
612
+    struct AVMetaData *meta_data;
611 613
 } AVFormatContext;
612 614
 
613 615
 typedef struct AVPacketList {
... ...
@@ -216,13 +216,17 @@ static void clean_index(AVFormatContext *s){
216 216
     }
217 217
 }
218 218
 
219
-static int avi_read_tag(ByteIOContext *pb, char *buf, int maxlen,  unsigned int size)
219
+static int avi_read_tag(AVFormatContext *s, const char *key, unsigned int size)
220 220
 {
221
+    ByteIOContext *pb = s->pb;
222
+    uint8_t value[1024];
223
+
221 224
     int64_t i = url_ftell(pb);
222 225
     size += (size & 1);
223
-    get_strz(pb, buf, maxlen);
226
+    get_strz(pb, value, sizeof(value));
224 227
     url_fseek(pb, i+size, SEEK_SET);
225
-    return 0;
228
+
229
+    return av_metadata_set(&s->meta_data, (const AVMetaDataTag){key, value});
226 230
 }
227 231
 
228 232
 static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
... ...
@@ -235,7 +239,6 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
235 235
     int i;
236 236
     AVStream *st;
237 237
     AVIStream *ast = NULL;
238
-    char str_track[4];
239 238
     int avih_width=0, avih_height=0;
240 239
     int amv_file_format=0;
241 240
 
... ...
@@ -561,26 +564,25 @@ static int avi_read_header(AVFormatContext *s, AVFormatParameters *ap)
561 561
             url_fseek(pb, size, SEEK_CUR);
562 562
             break;
563 563
         case MKTAG('I', 'N', 'A', 'M'):
564
-            avi_read_tag(pb, s->title, sizeof(s->title), size);
564
+            avi_read_tag(s, "Title", size);
565 565
             break;
566 566
         case MKTAG('I', 'A', 'R', 'T'):
567
-            avi_read_tag(pb, s->author, sizeof(s->author), size);
567
+            avi_read_tag(s, "Artist", size);
568 568
             break;
569 569
         case MKTAG('I', 'C', 'O', 'P'):
570
-            avi_read_tag(pb, s->copyright, sizeof(s->copyright), size);
570
+            avi_read_tag(s, "Copyright", size);
571 571
             break;
572 572
         case MKTAG('I', 'C', 'M', 'T'):
573
-            avi_read_tag(pb, s->comment, sizeof(s->comment), size);
573
+            avi_read_tag(s, "Comment", size);
574 574
             break;
575 575
         case MKTAG('I', 'G', 'N', 'R'):
576
-            avi_read_tag(pb, s->genre, sizeof(s->genre), size);
576
+            avi_read_tag(s, "Genre", size);
577 577
             break;
578 578
         case MKTAG('I', 'P', 'R', 'D'):
579
-            avi_read_tag(pb, s->album, sizeof(s->album), size);
579
+            avi_read_tag(s, "Album", size);
580 580
             break;
581 581
         case MKTAG('I', 'P', 'R', 'T'):
582
-            avi_read_tag(pb, str_track, sizeof(str_track), size);
583
-            sscanf(str_track, "%d", &s->track);
582
+            avi_read_tag(s, "Track", size);
584 583
             break;
585 584
         default:
586 585
             if(size > 1000000){
... ...
@@ -103,6 +103,15 @@ static void avi_write_info_tag(ByteIOContext *pb, const char *tag, const char *s
103 103
     }
104 104
 }
105 105
 
106
+static void avi_write_info_tag2(AVFormatContext *s, const char *fourcc, const char *key1, const char *key2)
107
+{
108
+    AVMetaDataTag *tag= av_metadata_get(s->meta_data, key1, NULL, AV_METADATA_IGNORE_CASE);
109
+    if(!tag && key2)
110
+        tag= av_metadata_get(s->meta_data, key2, NULL, AV_METADATA_IGNORE_CASE);
111
+    if(tag)
112
+        avi_write_info_tag(s->pb, fourcc, tag->value);
113
+}
114
+
106 115
 static int avi_write_counters(AVFormatContext* s, int riff_id)
107 116
 {
108 117
     ByteIOContext *pb = s->pb;
... ...
@@ -332,17 +341,13 @@ static int avi_write_header(AVFormatContext *s)
332 332
 
333 333
     list2 = start_tag(pb, "LIST");
334 334
     put_tag(pb, "INFO");
335
-    avi_write_info_tag(pb, "INAM", s->title);
336
-    avi_write_info_tag(pb, "IART", s->author);
337
-    avi_write_info_tag(pb, "ICOP", s->copyright);
338
-    avi_write_info_tag(pb, "ICMT", s->comment);
339
-    avi_write_info_tag(pb, "IPRD", s->album);
340
-    avi_write_info_tag(pb, "IGNR", s->genre);
341
-    if (s->track) {
342
-        char str_track[4];
343
-        snprintf(str_track, 4, "%d", s->track);
344
-        avi_write_info_tag(pb, "IPRT", str_track);
345
-    }
335
+    avi_write_info_tag2(s, "INAM", "Title", NULL);
336
+    avi_write_info_tag2(s, "IART", "Artist", "Author");
337
+    avi_write_info_tag2(s, "ICOP", "Copyright", NULL);
338
+    avi_write_info_tag2(s, "ICMT", "Comment", NULL);
339
+    avi_write_info_tag2(s, "IPRD", "Album", NULL);
340
+    avi_write_info_tag2(s, "IGNR", "Genre", NULL);
341
+    avi_write_info_tag2(s, "IPRT", "Track", NULL);
346 342
     if(!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT))
347 343
         avi_write_info_tag(pb, "ISFT", LIBAVFORMAT_IDENT);
348 344
     end_tag(pb, list2);
... ...
@@ -21,6 +21,7 @@
21 21
 #include "avformat.h"
22 22
 #include "internal.h"
23 23
 #include "libavcodec/opt.h"
24
+#include "libavcodec/metadata.h"
24 25
 #include "libavutil/avstring.h"
25 26
 #include "riff.h"
26 27
 #include <sys/time.h>
... ...
@@ -2305,6 +2306,14 @@ void av_close_input_stream(AVFormatContext *s)
2305 2305
         av_free(s->chapters[s->nb_chapters]);
2306 2306
     }
2307 2307
     av_freep(&s->chapters);
2308
+    if(s->meta_data){
2309
+        while(s->meta_data->count--){
2310
+            av_freep(&s->meta_data->elems[s->meta_data->count].key);
2311
+            av_freep(&s->meta_data->elems[s->meta_data->count].value);
2312
+        }
2313
+        av_freep(&s->meta_data->elems);
2314
+    }
2315
+    av_freep(&s->meta_data);
2308 2316
     av_free(s);
2309 2317
 }
2310 2318