Initial revision 6759
[qball-mpd.git] / src / mp4ff / .svn / text-base / mp4meta.c.svn-base
blob762f5dee7929c2f8b821c95be58a160c485aabab
1 /*
2 ** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
3 ** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program 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
13 ** GNU General Public License for more details.
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 ** Any non-GPL usage of this software or parts of this software is strictly
20 ** forbidden.
22 ** Commercial non-GPL licensing of this software is possible.
23 ** For more info contact Ahead Software through Mpeg4AAClicense@nero.com.
25 ** $Id: mp4meta.c,v 1.13 2004/01/11 15:52:18 menno Exp $
26 **/
28 #ifdef USE_TAGGING
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include "mp4ffint.h"
35 static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value)
37     void *backup = (void *)tags->tags;
39     if (!item || (item && !*item) || !value) return 0;
41     tags->tags = (mp4ff_tag_t*)realloc(tags->tags, (tags->count+1) * sizeof(mp4ff_tag_t));
42     if (!tags->tags)
43     {
44         if (backup) free(backup);
45         return 0;
46     } else {
47         tags->tags[tags->count].item = strdup(item);
48         tags->tags[tags->count].value = strdup(value);
50         if (!tags->tags[tags->count].item || !tags->tags[tags->count].value)
51         {
52             if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item);
53             if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value);
54             tags->tags[tags->count].item = NULL;
55             tags->tags[tags->count].value = NULL;
56             return 0;
57         }
59         tags->count++;
60         return 1;
61     }
64 static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value)
66     unsigned int i;
68     if (!item || (item && !*item) || !value) return 0;
70     for (i = 0; i < tags->count; i++)
71     {
72         if (!stricmp(tags->tags[i].item, item))
73         {
74                         free(tags->tags[i].value);
75                         tags->tags[i].value = strdup(value);
76             return 1;
77         }
78     }
80     return mp4ff_tag_add_field(tags, item, value);
83 int32_t mp4ff_tag_delete(mp4ff_metadata_t *tags)
85     uint32_t i;
87     for (i = 0; i < tags->count; i++)
88     {
89         if (tags->tags[i].item) free(tags->tags[i].item);
90         if (tags->tags[i].value) free(tags->tags[i].value);
91     }
93     if (tags->tags) free(tags->tags);
95     tags->tags = NULL;
96     tags->count = 0;
98     return 0;
101 static const char* ID3v1GenreList[] = {
102     "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
103     "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
104     "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
105     "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
106     "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
107     "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
108     "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
109     "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
110     "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
111     "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
112     "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
113     "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
114     "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
115     "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing",
116     "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
117     "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
118     "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
119     "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
120     "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
121     "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
122     "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
123     "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
124     "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
125     "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
126     "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
127     "SynthPop",
130 uint32_t mp4ff_meta_genre_to_index(const char * genrestr)
132         unsigned n;
133         for(n=0;n<sizeof(ID3v1GenreList)/sizeof(ID3v1GenreList[0]);n++)
134         {
135                 if (!stricmp(genrestr,ID3v1GenreList[n])) return n+1;
136         }
137         return 0;
140 const char * mp4ff_meta_index_to_genre(uint32_t idx)
142         if (idx>0 && idx<=sizeof(ID3v1GenreList)/sizeof(ID3v1GenreList[0]))
143         {
144                 return ID3v1GenreList[idx-1];
145         }
146         else
147         {
148                 return 0;
149         }
153 static int32_t TrackToString(char** str, const uint16_t track, const uint16_t totalTracks)
155         char temp[32];
156     sprintf(temp, "%.5u of %.5u", track, totalTracks);
157         *str = strdup(temp);
158     return 0;
161 static int32_t mp4ff_set_metadata_name(mp4ff_t *f, const uint8_t atom_type, char **name)
163     static char *tag_names[] = {
164         "unknown", "title", "artist", "writer", "album",
165         "date", "tool", "comment", "genre", "track",
166         "disc", "compilation", "genre", "tempo", "cover"
167     };
168     uint8_t tag_idx = 0;
170     switch (atom_type)
171     {
172     case ATOM_TITLE: tag_idx = 1; break;
173     case ATOM_ARTIST: tag_idx = 2; break;
174     case ATOM_WRITER: tag_idx = 3; break;
175     case ATOM_ALBUM: tag_idx = 4; break;
176     case ATOM_DATE: tag_idx = 5; break;
177     case ATOM_TOOL: tag_idx = 6; break;
178     case ATOM_COMMENT: tag_idx = 7; break;
179     case ATOM_GENRE1: tag_idx = 8; break;
180     case ATOM_TRACK: tag_idx = 9; break;
181     case ATOM_DISC: tag_idx = 10; break;
182     case ATOM_COMPILATION: tag_idx = 11; break;
183     case ATOM_GENRE2: tag_idx = 12; break;
184     case ATOM_TEMPO: tag_idx = 13; break;
185     case ATOM_COVER: tag_idx = 14; break;
186     default: tag_idx = 0; break;
187     }
189         *name = strdup(tag_names[tag_idx]);
191     return 0;
194 static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size)
196     uint8_t atom_type;
197     uint8_t header_size = 0;
198     uint64_t subsize, sumsize = 0;
199     char * name = NULL;
200         char * data = NULL;
201         uint32_t done = 0;
204     while (sumsize < size)
205     {
206                 uint64_t destpos;
207         subsize = mp4ff_atom_read_header(f, &atom_type, &header_size);
208                 destpos = mp4ff_position(f)+subsize-header_size;
209                 if (!done)
210                 {
211                         if (atom_type == ATOM_DATA)
212                         {
213                                 mp4ff_read_char(f); /* version */
214                                 mp4ff_read_int24(f); /* flags */
215                                 mp4ff_read_int32(f); /* reserved */
217                                 /* some need special attention */
218                                 if (parent_atom_type == ATOM_GENRE2 || parent_atom_type == ATOM_TEMPO)
219                                 {
220                                         if (subsize - header_size >= 8 + 2)
221                                         {
222                                                 uint16_t val = mp4ff_read_int16(f);
224                                                 if (parent_atom_type == ATOM_TEMPO)
225                                                 {
226                                                         char temp[16];
227                                                         sprintf(temp, "%.5u BPM", val);
228                                                         mp4ff_tag_add_field(&(f->tags), "tempo", temp);
229                                                 }
230                                                 else
231                                                 {
232                                                         const char * temp = mp4ff_meta_index_to_genre(val);
233                                                         if (temp)
234                                                         {
235                                                                 mp4ff_tag_add_field(&(f->tags), "genre", temp);
236                                                         }
237                                                 }
238                                                 done = 1;
239                                         }
240                                 } else if (parent_atom_type == ATOM_TRACK || parent_atom_type == ATOM_DISC) {
241                                         if (!done && subsize - header_size >= 8 + 8)
242                                         {
243                                                 uint16_t index,total;
244                                                 char temp[32];
245                                                 mp4ff_read_int16(f);
246                                                 index = mp4ff_read_int16(f);
247                                                 total = mp4ff_read_int16(f);
248                                                 mp4ff_read_int16(f);
250                                                 sprintf(temp,"%d",index);
251                                                 mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "track" : "disc", temp);
252                                                 if (total>0)
253                                                 {
254                                                         sprintf(temp,"%d",total);
255                                                         mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "totaltracks" : "totaldiscs", temp);
256                                                 }
257                                                 done = 1;
258                                         }
259                                 } else
260                                 {
261                                         if (data) {free(data);data = NULL;}
262                                         data = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+8)));
263                                 }
264                         } else if (atom_type == ATOM_NAME) {
265                                 if (!done)
266                                 {
267                                         mp4ff_read_char(f); /* version */
268                                         mp4ff_read_int24(f); /* flags */
269                                         if (name) free(name);
270                                         name = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+4)));
271                                 }
272                         }
273                         mp4ff_set_position(f, destpos);
274                         sumsize += subsize;
275                 }
276     }
278         if (data)
279         {
280                 if (!done)
281                 {
282                         if (name == NULL) mp4ff_set_metadata_name(f, parent_atom_type, &name);
283                         if (name) mp4ff_tag_add_field(&(f->tags), name, data);
284                 }
286                 free(data);
287         }
288         if (name) free(name);
289     return 1;
292 int32_t mp4ff_parse_metadata(mp4ff_t *f, const int32_t size)
294     uint64_t subsize, sumsize = 0;
295     uint8_t atom_type;
296     uint8_t header_size = 0;
298     while (sumsize < size)
299     {
300         subsize = mp4ff_atom_read_header(f, &atom_type, &header_size);
301         mp4ff_parse_tag(f, atom_type, (uint32_t)(subsize-header_size));
302         sumsize += subsize;
303     }
305     return 0;
308 /* find a metadata item by name */
309 /* returns 0 if item found, 1 if no such item */
310 static int32_t mp4ff_meta_find_by_name(const mp4ff_t *f, const char *item, char **value)
312     uint32_t i;
314     for (i = 0; i < f->tags.count; i++)
315     {
316         if (!stricmp(f->tags.tags[i].item, item))
317         {
318                         *value = strdup(f->tags.tags[i].value);
319             return 1;
320         }
321     }
323     *value = NULL;
325     /* not found */
326     return 0;
329 int32_t mp4ff_meta_get_num_items(const mp4ff_t *f)
331     return f->tags.count;
334 int32_t mp4ff_meta_get_by_index(const mp4ff_t *f, uint32_t index,
335                                 char **item, char **value)
337     if (index >= f->tags.count)
338     {
339         *item = NULL;
340         *value = NULL;
341         return 0;
342     } else {
343                 *item = strdup(f->tags.tags[index].item);
344                 *value = strdup(f->tags.tags[index].value);
345                 return 1;
346     }
349 int32_t mp4ff_meta_get_title(const mp4ff_t *f, char **value)
351     return mp4ff_meta_find_by_name(f, "title", value);
354 int32_t mp4ff_meta_get_artist(const mp4ff_t *f, char **value)
356     return mp4ff_meta_find_by_name(f, "artist", value);
359 int32_t mp4ff_meta_get_writer(const mp4ff_t *f, char **value)
361     return mp4ff_meta_find_by_name(f, "writer", value);
364 int32_t mp4ff_meta_get_album(const mp4ff_t *f, char **value)
366     return mp4ff_meta_find_by_name(f, "album", value);
369 int32_t mp4ff_meta_get_date(const mp4ff_t *f, char **value)
371     return mp4ff_meta_find_by_name(f, "date", value);
374 int32_t mp4ff_meta_get_tool(const mp4ff_t *f, char **value)
376     return mp4ff_meta_find_by_name(f, "tool", value);
379 int32_t mp4ff_meta_get_comment(const mp4ff_t *f, char **value)
381     return mp4ff_meta_find_by_name(f, "comment", value);
384 int32_t mp4ff_meta_get_genre(const mp4ff_t *f, char **value)
386     return mp4ff_meta_find_by_name(f, "genre", value);
389 int32_t mp4ff_meta_get_track(const mp4ff_t *f, char **value)
391     return mp4ff_meta_find_by_name(f, "track", value);
394 int32_t mp4ff_meta_get_disc(const mp4ff_t *f, char **value)
396     return mp4ff_meta_find_by_name(f, "disc", value);
399 int32_t mp4ff_meta_get_compilation(const mp4ff_t *f, char **value)
401     return mp4ff_meta_find_by_name(f, "compilation", value);
404 int32_t mp4ff_meta_get_tempo(const mp4ff_t *f, char **value)
406     return mp4ff_meta_find_by_name(f, "tempo", value);
409 int32_t mp4ff_meta_get_coverart(const mp4ff_t *f, char **value)
411     return mp4ff_meta_find_by_name(f, "cover", value);
414 #endif