2 ** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
3 ** Copyright (C) 2003-2004 M. Bakker, Ahead Software AG, http://www.nero.com
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.
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
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 $
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));
44 if (backup) free(backup);
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)
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;
64 static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value)
68 if (!item || (item && !*item) || !value) return 0;
70 for (i = 0; i < tags->count; i++)
72 if (!stricmp(tags->tags[i].item, item))
74 free(tags->tags[i].value);
75 tags->tags[i].value = strdup(value);
80 return mp4ff_tag_add_field(tags, item, value);
83 int32_t mp4ff_tag_delete(mp4ff_metadata_t *tags)
87 for (i = 0; i < tags->count; i++)
89 if (tags->tags[i].item) free(tags->tags[i].item);
90 if (tags->tags[i].value) free(tags->tags[i].value);
93 if (tags->tags) free(tags->tags);
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",
130 uint32_t mp4ff_meta_genre_to_index(const char * genrestr)
133 for(n=0;n<sizeof(ID3v1GenreList)/sizeof(ID3v1GenreList[0]);n++)
135 if (!stricmp(genrestr,ID3v1GenreList[n])) return n+1;
140 const char * mp4ff_meta_index_to_genre(uint32_t idx)
142 if (idx>0 && idx<=sizeof(ID3v1GenreList)/sizeof(ID3v1GenreList[0]))
144 return ID3v1GenreList[idx-1];
153 static int32_t TrackToString(char** str, const uint16_t track, const uint16_t totalTracks)
156 sprintf(temp, "%.5u of %.5u", track, totalTracks);
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"
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;
189 *name = strdup(tag_names[tag_idx]);
194 static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size)
197 uint8_t header_size = 0;
198 uint64_t subsize, sumsize = 0;
204 while (sumsize < size)
207 subsize = mp4ff_atom_read_header(f, &atom_type, &header_size);
208 destpos = mp4ff_position(f)+subsize-header_size;
211 if (atom_type == ATOM_DATA)
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)
220 if (subsize - header_size >= 8 + 2)
222 uint16_t val = mp4ff_read_int16(f);
224 if (parent_atom_type == ATOM_TEMPO)
227 sprintf(temp, "%.5u BPM", val);
228 mp4ff_tag_add_field(&(f->tags), "tempo", temp);
232 const char * temp = mp4ff_meta_index_to_genre(val);
235 mp4ff_tag_add_field(&(f->tags), "genre", temp);
240 } else if (parent_atom_type == ATOM_TRACK || parent_atom_type == ATOM_DISC) {
241 if (!done && subsize - header_size >= 8 + 8)
243 uint16_t index,total;
246 index = mp4ff_read_int16(f);
247 total = mp4ff_read_int16(f);
250 sprintf(temp,"%d",index);
251 mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "track" : "disc", temp);
254 sprintf(temp,"%d",total);
255 mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "totaltracks" : "totaldiscs", temp);
261 if (data) {free(data);data = NULL;}
262 data = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+8)));
264 } else if (atom_type == ATOM_NAME) {
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)));
273 mp4ff_set_position(f, destpos);
282 if (name == NULL) mp4ff_set_metadata_name(f, parent_atom_type, &name);
283 if (name) mp4ff_tag_add_field(&(f->tags), name, data);
288 if (name) free(name);
292 int32_t mp4ff_parse_metadata(mp4ff_t *f, const int32_t size)
294 uint64_t subsize, sumsize = 0;
296 uint8_t header_size = 0;
298 while (sumsize < size)
300 subsize = mp4ff_atom_read_header(f, &atom_type, &header_size);
301 mp4ff_parse_tag(f, atom_type, (uint32_t)(subsize-header_size));
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)
314 for (i = 0; i < f->tags.count; i++)
316 if (!stricmp(f->tags.tags[i].item, item))
318 *value = strdup(f->tags.tags[i].value);
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)
343 *item = strdup(f->tags.tags[index].item);
344 *value = strdup(f->tags.tags[index].value);
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);