2 * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
3 * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
11 #include "portable_io.h"
21 int32_t stsz_sample_size;
22 int32_t stsz_sample_count;
26 int32_t stts_entry_count;
27 int32_t *stts_sample_count;
28 int32_t *stts_sample_delta;
31 int32_t stsc_entry_count;
32 int32_t *stsc_first_chunk;
33 int32_t *stsc_samples_per_chunk;
34 int32_t *stsc_sample_desc_index;
37 int32_t stco_entry_count;
38 int32_t *stco_chunk_offset;
44 #define MAX_TRACKS 1024
47 const struct mp4_callback *cb;
48 int64_t current_position;
58 /* incremental track index while reading the file */
62 struct mp4_track *track[MAX_TRACKS];
65 struct mp4_metadata tags;
68 int32_t mp4_total_tracks(const struct mp4 *f)
70 return f->total_tracks;
73 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
77 result = f->cb->read(f->cb->user_data, data, size);
82 f->current_position += size;
87 static uint64_t read_int64(struct mp4 *f)
91 read_data(f, data, 8);
92 return read_u64_be(data);
95 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
96 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
98 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
102 /* atoms with subatoms */
110 ATOM_ILST = 8, /* iTunes Metadata list */
121 ATOM_COMPILATION = 19,
131 /* atoms without subatoms */
151 ATOM_META = 148, /* iTunes Metadata box */
152 ATOM_NAME = 149, /* iTunes Metadata name box */
153 ATOM_DATA = 150, /* iTunes Metadata data box */
160 ATOM_ALBUM_ARTIST = 157,
161 ATOM_CONTENTGROUP = 158,
163 ATOM_DESCRIPTION = 160,
166 ATOM_EPISODENAME = 163,
167 ATOM_SORTTITLE = 164,
168 ATOM_SORTALBUM = 165,
169 ATOM_SORTARTIST = 166,
170 ATOM_SORTALBUMARTIST = 167,
171 ATOM_SORTWRITER = 168,
180 #define ATOM_FREE ATOM_UNKNOWN
181 #define ATOM_SKIP ATOM_UNKNOWN
183 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
185 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
188 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
190 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
192 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
194 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
196 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
198 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
200 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
202 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
204 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
206 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
208 } else if (a == 't') {
209 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
211 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
213 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
215 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
217 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
219 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
221 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
223 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
224 return ATOM_EPISODENAME;
225 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
227 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
229 } else if (a == 's') {
230 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
232 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
234 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
236 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
238 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
240 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
242 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
244 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
246 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
248 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
250 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
252 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
253 return ATOM_SORTTITLE;
254 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
255 return ATOM_SORTALBUM;
256 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
257 return ATOM_SORTARTIST;
258 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
259 return ATOM_SORTALBUMARTIST;
260 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
261 return ATOM_SORTWRITER;
262 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
263 return ATOM_SORTSHOW;
264 } else if (a == COPYRIGHT_SYMBOL) {
265 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
267 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
269 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
271 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
273 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
275 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
277 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
279 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
281 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
282 return ATOM_CONTENTGROUP;
283 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
287 if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
289 else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
291 else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
293 else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
295 else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
297 else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
299 else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
301 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
303 else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
305 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
307 else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
309 else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
311 else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
313 else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
314 return ATOM_COMPILATION;
315 else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
317 else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
319 else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
321 else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
323 else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
325 else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
327 else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
329 else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
330 return ATOM_ALBUM_ARTIST;
331 else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
332 return ATOM_DESCRIPTION;
333 else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
339 /* read atom header, return atom size, atom size is with header included */
340 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
341 uint8_t * header_size)
345 int8_t atom_header[8];
347 ret = read_data(f, atom_header, 8);
351 size = read_u32_be(atom_header);
354 /* check for 64 bit atom size */
357 size = read_int64(f);
359 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
360 atom_header[6], atom_header[7]);
364 static int64_t get_position(const struct mp4 *f)
366 return f->current_position;
369 static int need_parse_when_meta_only(uint8_t atom_type)
390 static int32_t set_position(struct mp4 *f, int64_t position)
392 f->cb->seek(f->cb->user_data, position);
393 f->current_position = position;
398 static void track_add(struct mp4 *f)
402 if (f->total_tracks > MAX_TRACKS) {
407 f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
410 static uint8_t read_char(struct mp4 *f)
413 read_data(f, &output, 1);
417 static uint32_t read_int24(struct mp4 *f)
421 read_data(f, data, 3);
422 return read_u24_be(data);
425 static uint32_t read_int32(struct mp4 *f)
429 read_data(f, data, 4);
430 return read_u32_be(data);
433 static int32_t read_stsz(struct mp4 *f)
438 if (f->total_tracks == 0)
440 t = f->track[f->total_tracks - 1];
441 read_char(f); /* version */
442 read_int24(f); /* flags */
443 t->stsz_sample_size = read_int32(f);
444 t->stsz_sample_count = read_int32(f);
445 if (t->stsz_sample_size != 0)
447 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
448 for (i = 0; i < t->stsz_sample_count && !f->read_error; i++)
449 t->stsz_table[i] = read_int32(f);
453 static int32_t read_stts(struct mp4 *f)
459 if (f->total_tracks == 0)
461 t = f->track[f->total_tracks - 1];
462 if (t->stts_entry_count)
464 read_char(f); /* version */
465 read_int24(f); /* flags */
466 t->stts_entry_count = read_int32(f);
468 t->stts_sample_count = para_malloc(t->stts_entry_count
470 t->stts_sample_delta = para_malloc(t->stts_entry_count
473 for (i = 0; i < t->stts_entry_count && !f->read_error; i++) {
474 t->stts_sample_count[i] = read_int32(f);
475 t->stts_sample_delta[i] = read_int32(f);
480 static int32_t read_stsc(struct mp4 *f)
485 if (f->total_tracks == 0)
487 t = f->track[f->total_tracks - 1];
489 read_char(f); /* version */
490 read_int24(f); /* flags */
491 t->stsc_entry_count = read_int32(f);
492 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
493 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
495 t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
499 for (i = 0; i < t->stsc_entry_count && !f->read_error; i++) {
500 t->stsc_first_chunk[i] = read_int32(f);
501 t->stsc_samples_per_chunk[i] = read_int32(f);
502 t->stsc_sample_desc_index[i] = read_int32(f);
507 static int32_t read_stco(struct mp4 *f)
512 if (f->total_tracks == 0)
514 t = f->track[f->total_tracks - 1];
516 read_char(f); /* version */
517 read_int24(f); /* flags */
518 t->stco_entry_count = read_int32(f);
519 t->stco_chunk_offset = para_malloc(t->stco_entry_count
522 for (i = 0; i < t->stco_entry_count && !f->read_error; i++)
523 t->stco_chunk_offset[i] = read_int32(f);
527 static uint16_t read_int16(struct mp4 *f)
531 read_data(f, data, 2);
532 return read_u16_be(data);
535 static int32_t read_mp4a(struct mp4 *f)
538 uint8_t atom_type = 0;
539 uint8_t header_size = 0;
542 if (f->total_tracks == 0)
544 t = f->track[f->total_tracks - 1];
546 for (i = 0; i < 6; i++) {
547 read_char(f); /* reserved */
549 /* data_reference_index */ read_int16(f);
551 read_int32(f); /* reserved */
552 read_int32(f); /* reserved */
554 t->channelCount = read_int16(f);
560 t->sampleRate = read_int16(f);
564 atom_read_header(f, &atom_type, &header_size);
568 static int32_t read_stsd(struct mp4 *f)
570 int32_t i, entry_count;
571 uint8_t header_size = 0;
575 if (f->total_tracks == 0)
577 t = f->track[f->total_tracks - 1];
579 read_char(f); /* version */
580 read_int24(f); /* flags */
582 entry_count = read_int32(f);
585 for (i = 0; i < entry_count && !f->read_error; i++) {
586 uint64_t skip = get_position(f);
588 uint8_t atom_type = 0;
589 size = atom_read_header(f, &atom_type, &header_size);
591 t->is_audio = atom_type == ATOM_MP4A;
594 set_position(f, skip);
600 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
601 const char *value, int32_t len)
603 if (!item || (item && !*item) || !value)
605 tags->tags = para_realloc(tags->tags,
606 (tags->count + 1) * sizeof(struct mp4_tag));
607 tags->tags[tags->count].item = para_strdup(item);
608 tags->tags[tags->count].len = len;
610 tags->tags[tags->count].value = para_malloc(len + 1);
611 memcpy(tags->tags[tags->count].value, value, len);
612 tags->tags[tags->count].value[len] = 0;
614 tags->tags[tags->count].value = para_strdup(value);
620 static const char *ID3v1GenreList[] = {
621 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
622 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
623 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
624 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
625 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
626 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
627 "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
628 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
629 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
630 "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
631 "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
632 "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
633 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
634 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
635 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
636 "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
637 "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
638 "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
639 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
640 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
641 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
642 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
643 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
644 "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
645 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
646 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
647 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
648 "Anime", "JPop", "SynthPop",
651 static const char *meta_index_to_genre(uint32_t idx)
653 if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
654 return ID3v1GenreList[idx - 1];
660 static char *read_string(struct mp4 *f, uint32_t length)
662 char *str = para_malloc(length + 1);
663 if ((uint32_t)read_data(f, str, length) != length) {
671 static int32_t set_metadata_name(uint8_t atom_type, char **name)
673 static char *tag_names[] = {
674 "unknown", "title", "artist", "writer", "album",
675 "date", "tool", "comment", "genre", "track",
676 "disc", "compilation", "genre", "tempo", "cover",
677 "album_artist", "contentgroup", "lyrics", "description",
678 "network", "show", "episodename",
679 "sorttitle", "sortalbum", "sortartist", "sortalbumartist",
680 "sortwriter", "sortshow",
681 "season", "episode", "podcast"
716 case ATOM_COMPILATION:
728 case ATOM_ALBUM_ARTIST:
731 case ATOM_CONTENTGROUP:
737 case ATOM_DESCRIPTION:
746 case ATOM_EPISODENAME:
755 case ATOM_SORTARTIST:
758 case ATOM_SORTALBUMARTIST:
761 case ATOM_SORTWRITER:
781 *name = para_strdup(tag_names[tag_idx]);
785 static uint32_t min_body_size(uint8_t atom_type)
792 return sizeof (char) /* version */
793 + sizeof(uint8_t) * 3 /* flags */
794 + sizeof(uint32_t) /* reserved */
795 + sizeof(uint16_t) /* leading uint16_t */
796 + sizeof(uint16_t) /* track */
797 + sizeof(uint16_t); /* totaltracks */
799 return sizeof (char) /* version */
800 + sizeof(uint8_t) * 3 /* flags */
801 + sizeof(uint32_t) /* reserved */
802 + sizeof(uint16_t) /* disc */
803 + sizeof(uint16_t); /* totaldiscs */
804 default: assert(false);
808 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
811 uint8_t header_size = 0;
812 uint64_t subsize, sumsize;
821 sumsize < size && !f->read_error; /* CVE-2017-9222 */
822 set_position(f, destpos), sumsize += subsize
824 subsize = atom_read_header(f, &atom_type, &header_size);
825 destpos = get_position(f) + subsize - header_size;
828 if (atom_type == ATOM_NAME) {
829 read_char(f); /* version */
830 read_int24(f); /* flags */
832 name = read_string(f, subsize - (header_size + 4));
835 if (atom_type != ATOM_DATA)
837 read_char(f); /* version */
838 read_int24(f); /* flags */
839 read_int32(f); /* reserved */
841 /* some need special attention */
842 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
844 if (subsize - header_size < min_body_size(parent))
847 if (parent == ATOM_TEMPO) {
849 sprintf(temp, "%.5u BPM", val);
850 tag_add_field(&(f-> tags), "tempo",
853 const char *tmp = meta_index_to_genre(val);
855 tag_add_field (&(f->tags),
859 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
860 uint16_t index, total;
862 if (subsize - header_size < min_body_size(parent))
865 index = read_int16(f);
866 total = read_int16(f);
867 if (parent == ATOM_TRACK)
869 sprintf(temp, "%d", index);
870 tag_add_field(&(f->tags), parent == ATOM_TRACK?
871 "track" : "disc", temp, -1);
873 sprintf(temp, "%d", total);
874 tag_add_field(& (f-> tags),
875 parent == ATOM_TRACK?
876 "totaltracks" : "totaldiscs", temp, -1);
881 data = read_string(f, subsize - (header_size + 8));
882 len = subsize - (header_size + 8);
888 set_metadata_name(parent , &name);
890 tag_add_field(&(f->tags), name, data, len);
899 static int32_t read_mdhd(struct mp4 *f)
905 if (f->total_tracks == 0)
907 t = f->track[f->total_tracks - 1];
909 version = read_int32(f);
911 read_int64(f); //creation-time
912 read_int64(f); //modification-time
913 t->timeScale = read_int32(f); //timescale
914 t->duration = read_int64(f); //duration
915 } else { //version == 0
918 read_int32(f); //creation-time
919 read_int32(f); //modification-time
920 t->timeScale = read_int32(f); //timescale
921 temp = read_int32(f);
922 t->duration = (temp == (uint32_t) (-1))?
923 (uint64_t) (-1) : (uint64_t) (temp);
930 static int32_t parse_metadata(struct mp4 *f, int32_t size)
932 uint64_t subsize, sumsize = 0;
934 uint8_t header_size = 0;
936 while (sumsize < size) {
937 subsize = atom_read_header(f, &atom_type, &header_size);
940 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
947 static int32_t read_meta(struct mp4 *f, uint64_t size)
949 uint64_t subsize, sumsize = 0;
951 uint8_t header_size = 0;
953 read_char(f); /* version */
954 read_int24(f); /* flags */
956 while (sumsize < (size - (header_size + 4))) {
957 subsize = atom_read_header(f, &atom_type, &header_size);
958 if (subsize <= header_size + 4)
960 if (atom_type == ATOM_ILST) {
961 parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
963 set_position(f, get_position(f) + subsize - header_size);
971 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
973 uint64_t dest_position = get_position(f) + size - 8;
974 if (atom_type == ATOM_STSZ) {
975 /* sample size box */
977 } else if (atom_type == ATOM_STTS) {
978 /* time to sample box */
980 } else if (atom_type == ATOM_STSC) {
981 /* sample to chunk box */
983 } else if (atom_type == ATOM_STCO) {
984 /* chunk offset box */
986 } else if (atom_type == ATOM_STSD) {
987 /* sample description box */
989 } else if (atom_type == ATOM_MDHD) {
992 } else if (atom_type == ATOM_META) {
993 /* iTunes Metadata box */
997 set_position(f, dest_position);
1001 /* parse atoms that are sub atoms of other atoms */
1002 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
1005 uint8_t atom_type = 0;
1006 uint64_t counted_size = 0;
1007 uint8_t header_size = 0;
1009 while (counted_size < total_size) {
1010 size = atom_read_header(f, &atom_type, &header_size);
1011 counted_size += size;
1013 /* check for end of file */
1017 /* we're starting to read a new track, update index,
1018 * so that all data and tables get written in the right place
1020 if (atom_type == ATOM_TRAK)
1022 /* parse subatoms */
1023 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1024 set_position(f, get_position(f) + size - header_size);
1025 } else if (atom_type < SUBATOMIC) {
1026 parse_sub_atoms(f, size - header_size, meta_only);
1028 atom_read(f, (uint32_t) size, atom_type);
1035 /* parse root atoms */
1036 static int32_t parse_atoms(struct mp4 *f, int meta_only)
1039 uint8_t atom_type = 0;
1040 uint8_t header_size = 0;
1046 atom_read_header(f, &atom_type, &header_size)) != 0) {
1047 f->file_size += size;
1048 f->last_atom = atom_type;
1050 if (atom_type == ATOM_MOOV && size > header_size) {
1051 f->moov_offset = get_position(f) - header_size;
1052 f->moov_size = size;
1055 /* parse subatoms */
1056 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1057 set_position(f, get_position(f) + size - header_size);
1058 } else if (atom_type < SUBATOMIC) {
1059 parse_sub_atoms(f, size - header_size, meta_only);
1061 /* skip this atom */
1062 set_position(f, get_position(f) + size - header_size);
1069 struct mp4 *mp4_open_read(const struct mp4_callback *cb)
1071 struct mp4 *f = para_calloc(sizeof(struct mp4));
1082 static int32_t tag_delete(struct mp4_metadata *tags)
1086 for (i = 0; i < tags->count; i++) {
1087 free(tags->tags[i].item);
1088 free(tags->tags[i].value);
1097 void mp4_close(struct mp4 *f)
1101 for (i = 0; i < f->total_tracks; i++) {
1103 free(f->track[i]->stsz_table);
1104 free(f->track[i]->stts_sample_count);
1105 free(f->track[i]->stts_sample_delta);
1106 free(f->track[i]->stsc_first_chunk);
1107 free(f->track[i]->stsc_samples_per_chunk);
1108 free(f->track[i]->stsc_sample_desc_index);
1109 free(f->track[i]->stco_chunk_offset);
1114 tag_delete(&(f->tags));
1118 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1119 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1121 int32_t total_entries = 0;
1122 int32_t chunk2entry;
1123 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1127 if (f->track[track] == NULL) {
1131 total_entries = f->track[track]->stsc_entry_count;
1138 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1139 *chunk = chunk2 - chunk1;
1140 range_samples = *chunk * chunk1samples;
1142 if (sample < total + range_samples)
1145 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1148 if (chunk2entry < total_entries) {
1150 total += range_samples;
1152 } while (chunk2entry < total_entries);
1155 *chunk = (sample - total) / chunk1samples + chunk1;
1159 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1164 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1167 const struct mp4_track *p_track = f->track[track];
1169 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1170 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1172 } else if (p_track->stco_entry_count) {
1173 return p_track->stco_chunk_offset[chunk - 1];
1181 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1182 int32_t chunk_sample, int32_t sample)
1185 const struct mp4_track *p_track = f->track[track];
1187 if (p_track->stsz_sample_size) {
1188 return (sample - chunk_sample) * p_track->stsz_sample_size;
1190 if (sample >= p_track->stsz_sample_count)
1193 for (i = chunk_sample, total = 0; i < sample; i++) {
1194 total += p_track->stsz_table[i];
1201 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1204 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1206 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1208 chunk_offset1 = chunk_to_offset(f, track, chunk);
1209 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1210 track, chunk_sample, sample);
1211 return chunk_offset2;
1215 * Return the number of milliseconds of the given track.
1217 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1218 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1220 * The function returns zero if the audio file is of zero length or contains a
1221 * corrupt track header.
1223 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1225 const struct mp4_track *t = f->track[track];
1227 if (t->timeScale == 0)
1229 return t->duration * 1000 / t->timeScale;
1233 * Check whether the given track number corresponds to an audio track.
1235 * \param f See \ref mp4_get_duration().
1236 * \param track See \ref mp4_get_duration().
1238 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1239 * those the function returns false.
1241 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1243 return f->track[track]->is_audio;
1246 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1248 int32_t offset = sample_to_offset(f, track, sample);
1249 set_position(f, offset);
1252 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1254 const struct mp4_track *t = f->track[track];
1256 if (t->stsz_sample_size != 0)
1257 return t->stsz_sample_size;
1258 return t->stsz_table[sample];
1261 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1263 return f->track[track]->sampleRate;
1266 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1268 return f->track[track]->channelCount;
1271 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1276 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1277 total += f->track[track]->stts_sample_count[i];
1282 struct mp4 *mp4_open_meta(const struct mp4_callback *cb)
1284 struct mp4 *f = para_calloc(sizeof(struct mp4));
1295 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1297 return f->tags.count;
1300 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1301 char **item, char **value)
1303 if (index >= f->tags.count) {
1308 *item = para_strdup(f->tags.tags[index].item);
1309 *value = para_strdup(f->tags.tags[index].value);
1314 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1317 uint32_t remaining = size;
1318 uint64_t atom_offset = base;
1323 set_position(f, atom_offset);
1327 atom_size = read_int32(f);
1328 if (atom_size > remaining || atom_size < 8)
1330 read_data(f, atom_name, 4);
1332 if (!memcmp(atom_name, name, 4)) {
1333 set_position(f, atom_offset);
1337 remaining -= atom_size;
1338 atom_offset += atom_size;
1343 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1344 const char *name, uint32_t extraheaders, const char *name_inside)
1346 uint64_t first_base = (uint64_t) (-1);
1347 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1349 uint64_t mybase = get_position(f);
1350 uint32_t mysize = read_int32(f);
1352 if (first_base == (uint64_t) (-1))
1353 first_base = mybase;
1355 if (mysize < 8 + extraheaders)
1358 if (find_atom (f, mybase + (8 + extraheaders),
1359 mysize - (8 + extraheaders), name_inside)) {
1360 set_position(f, mybase);
1364 if (size <= mysize) {
1371 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1373 set_position(f, first_base);
1386 static struct membuffer *membuffer_create(void)
1388 const unsigned initial_size = 256;
1390 struct membuffer *buf = para_malloc(sizeof(*buf));
1391 buf->data = para_malloc(initial_size);
1393 buf->allocated = initial_size;
1394 buf->error = buf->data == 0 ? 1 : 0;
1399 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1401 unsigned dest_size = buf->written + bytes;
1405 if (dest_size > buf->allocated) {
1407 buf->allocated <<= 1;
1408 } while (dest_size > buf->allocated);
1409 buf->data = para_realloc(buf->data, buf->allocated);
1413 memcpy((char *) buf->data + buf->written, ptr, bytes);
1414 buf->written += bytes;
1418 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1420 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1423 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1427 write_u16_be(temp, data);
1428 return membuffer_write(buf, temp, 2);
1431 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1434 write_u32_be(temp, data);
1435 return membuffer_write(buf, temp, 4);
1438 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1439 uint32_t index, uint32_t total)
1441 membuffer_write_int32(buf,
1442 8 /*atom header */ + 8 /*data atom header */ +
1443 8 /*flags + reserved */ + 8 /*actual data */ );
1444 membuffer_write_atom_name(buf, name);
1445 membuffer_write_int32(buf,
1446 8 /*data atom header */ +
1447 8 /*flags + reserved */ + 8 /*actual data */ );
1448 membuffer_write_atom_name(buf, "data");
1449 membuffer_write_int32(buf, 0); //flags
1450 membuffer_write_int32(buf, 0); //reserved
1451 membuffer_write_int16(buf, 0);
1452 membuffer_write_int16(buf, (uint16_t) index); //track number
1453 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1454 membuffer_write_int16(buf, 0);
1457 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1460 membuffer_write_int32(buf,
1461 8 /*atom header */ + 8 /*data atom header */ +
1462 8 /*flags + reserved */ + 2 /*actual data */ );
1463 membuffer_write_atom_name(buf, name);
1464 membuffer_write_int32(buf,
1465 8 /*data atom header */ +
1466 8 /*flags + reserved */ + 2 /*actual data */ );
1467 membuffer_write_atom_name(buf, "data");
1468 membuffer_write_int32(buf, 0); //flags
1469 membuffer_write_int32(buf, 0); //reserved
1470 membuffer_write_int16(buf, value); //value
1473 static uint32_t myatoi(const char *param)
1475 return param ? atoi(param) : 0;
1478 static uint32_t meta_genre_to_index(const char *genrestr)
1481 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1482 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1488 struct stdmeta_entry {
1493 struct stdmeta_entry stdmetas[] = {
1494 {"\xA9" "nam", "title"},
1495 {"\xA9" "ART", "artist"},
1496 {"\xA9" "wrt", "writer"},
1497 {"\xA9" "alb", "album"},
1498 {"\xA9" "day", "date"},
1499 {"\xA9" "too", "tool"},
1500 {"\xA9" "cmt", "comment"},
1501 {"cpil", "compilation"},
1503 {"aART", "album_artist"},
1506 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1509 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1510 if (!strcasecmp(name, stdmetas[n].name))
1511 return stdmetas[n].atom;
1516 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1521 /* special check for compilation flag */
1522 if (strcmp(name, "cpil") == 0) {
1526 membuffer_write_int32(buf,
1527 8 /*atom header */ + 8 /*data atom header */ +
1528 8 /*flags + reserved */ + strlen(value));
1529 membuffer_write_atom_name(buf, name);
1530 membuffer_write_int32(buf,
1531 8 /*data atom header */ +
1532 8 /*flags + reserved */ + strlen(value));
1533 membuffer_write_atom_name(buf, "data");
1534 membuffer_write_int32(buf, flags); //flags
1535 membuffer_write_int32(buf, 0); //reserved
1536 membuffer_write(buf, value, strlen(value));
1539 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1542 membuffer_write_int32(buf,
1543 8 /*atom header */ +
1544 0x1C /*weirdo itunes atom */ +
1545 12 /*name atom header */ + strlen(name) +
1546 16 /*data atom header + flags */ + strlen(value));
1547 membuffer_write_atom_name(buf, "----");
1548 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1549 membuffer_write_atom_name(buf, "mean");
1550 membuffer_write_int32(buf, 0);
1551 membuffer_write(buf, "com.apple.iTunes", 16);
1552 membuffer_write_int32(buf, 12 + strlen(name));
1553 membuffer_write_atom_name(buf, "name");
1554 membuffer_write_int32(buf, 0);
1555 membuffer_write(buf, name, strlen(name));
1556 membuffer_write_int32(buf,
1557 8 /*data atom header */ +
1558 8 /*flags + reserved */ + strlen(value));
1559 membuffer_write_atom_name(buf, "data");
1560 membuffer_write_int32(buf, 1); //flags
1561 membuffer_write_int32(buf, 0); //reserved
1562 membuffer_write(buf, value, strlen(value));
1565 static unsigned membuffer_error(const struct membuffer *buf)
1570 static void membuffer_free(struct membuffer *buf)
1576 static unsigned membuffer_get_size(const struct membuffer *buf)
1578 return buf->written;
1581 static void *membuffer_detach(struct membuffer *buf)
1587 ret = para_realloc(buf->data, buf->written);
1593 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1594 uint32_t * out_size)
1596 struct membuffer *buf = membuffer_create();
1598 char *mask = para_calloc(data->count);
1599 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1600 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1601 const char *genre_ptr = 0, *tempo_ptr = 0;
1603 for (metaptr = 0; metaptr < data->count; metaptr++) {
1604 struct mp4_tag *tag = &data->tags[metaptr];
1605 if (!strcasecmp(tag->item, "tracknumber")
1606 || !strcasecmp(tag->item, "track")) {
1607 if (tracknumber_ptr == 0)
1608 tracknumber_ptr = tag->value;
1610 } else if (!strcasecmp(tag->item, "totaltracks")) {
1611 if (totaltracks_ptr == 0)
1612 totaltracks_ptr = tag->value;
1614 } else if (!strcasecmp(tag->item, "discnumber")
1615 || !strcasecmp(tag->item, "disc")) {
1616 if (discnumber_ptr == 0)
1617 discnumber_ptr = tag->value;
1619 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1620 if (totaldiscs_ptr == 0)
1621 totaldiscs_ptr = tag->value;
1623 } else if (!strcasecmp(tag->item, "genre")) {
1625 genre_ptr = tag->value;
1627 } else if (!strcasecmp(tag->item, "tempo")) {
1629 tempo_ptr = tag->value;
1634 if (tracknumber_ptr)
1635 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1636 myatoi(totaltracks_ptr));
1638 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1639 myatoi(totaldiscs_ptr));
1641 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1644 uint32_t index = meta_genre_to_index(genre_ptr);
1646 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1648 membuffer_write_int16_tag(buf, "gnre", index);
1650 for (metaptr = 0; metaptr < data->count; metaptr++) {
1651 struct mp4_tag *tag;
1652 const char *std_meta_atom;
1656 tag = &data->tags[metaptr];
1657 std_meta_atom = find_standard_meta(tag->item);
1659 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1661 membuffer_write_custom_tag(buf, tag->item, tag->value);
1665 if (membuffer_error(buf)) {
1666 membuffer_free(buf);
1670 *out_size = membuffer_get_size(buf);
1671 *out_buffer = membuffer_detach(buf);
1672 membuffer_free(buf);
1677 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1680 membuffer_write_int32(buf, size + 8);
1681 membuffer_write_atom_name(buf, name);
1682 membuffer_write(buf, data, size);
1685 static void *membuffer_get_ptr(const struct membuffer *buf)
1690 static void membuffer_set_error(struct membuffer *buf)
1695 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1701 oldsize = membuffer_get_size(buf);
1702 if (membuffer_write(buf, 0, bytes) != bytes)
1705 bufptr = membuffer_get_ptr(buf);
1709 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1711 membuffer_set_error(buf);
1718 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1719 uint32_t * out_size)
1721 struct membuffer *buf;
1725 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1728 buf = membuffer_create();
1730 membuffer_write_int32(buf, 0);
1731 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1734 *out_size = membuffer_get_size(buf);
1735 *out_buffer = membuffer_detach(buf);
1736 membuffer_free(buf);
1740 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1741 uint32_t * out_size)
1743 struct membuffer *buf;
1747 if (!create_meta(data, &meta_buffer, &meta_size))
1750 buf = membuffer_create();
1752 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1756 *out_size = membuffer_get_size(buf);
1757 *out_buffer = membuffer_detach(buf);
1758 membuffer_free(buf);
1762 static uint32_t fix_byte_order_32(uint32_t src)
1764 return read_u32_be(&src);
1767 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1768 void **out_buffer, uint32_t * out_size)
1770 uint64_t total_base = f->moov_offset + 8;
1771 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1773 uint64_t udta_offset, meta_offset, ilst_offset;
1774 uint32_t udta_size, meta_size, ilst_size;
1776 uint32_t new_ilst_size;
1777 void *new_ilst_buffer;
1782 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1783 struct membuffer *buf;
1784 void *new_udta_buffer;
1785 uint32_t new_udta_size;
1786 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1789 buf = membuffer_create();
1790 set_position(f, total_base);
1791 membuffer_transfer_from_file(buf, f, total_size);
1793 membuffer_write_atom(buf, "udta", new_udta_size,
1796 free(new_udta_buffer);
1798 *out_size = membuffer_get_size(buf);
1799 *out_buffer = membuffer_detach(buf);
1800 membuffer_free(buf);
1803 udta_offset = get_position(f);
1804 udta_size = read_int32(f);
1805 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1806 struct membuffer *buf;
1807 void *new_meta_buffer;
1808 uint32_t new_meta_size;
1809 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1812 buf = membuffer_create();
1813 set_position(f, total_base);
1814 membuffer_transfer_from_file(buf, f,
1815 (uint32_t)(udta_offset - total_base));
1817 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1818 membuffer_write_atom_name(buf, "udta");
1819 membuffer_transfer_from_file(buf, f, udta_size);
1821 membuffer_write_atom(buf, "meta", new_meta_size,
1823 free(new_meta_buffer);
1825 *out_size = membuffer_get_size(buf);
1826 *out_buffer = membuffer_detach(buf);
1827 membuffer_free(buf);
1830 meta_offset = get_position(f);
1831 meta_size = read_int32(f);
1832 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1833 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1834 ilst_offset = get_position(f);
1835 ilst_size = read_int32(f);
1837 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1840 size_delta = new_ilst_size - (ilst_size - 8);
1842 *out_size = total_size + size_delta;
1843 *out_buffer = para_malloc(*out_size);
1844 p_out = (uint8_t *) * out_buffer;
1846 set_position(f, total_base);
1848 (uint32_t) (udta_offset - total_base));
1849 p_out += (uint32_t) (udta_offset - total_base);
1850 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1852 read_data(f, p_out, 4);
1855 (uint32_t) (meta_offset - udta_offset - 8));
1856 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1857 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1859 read_data(f, p_out, 4);
1862 (uint32_t) (ilst_offset - meta_offset - 8));
1863 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1864 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1866 read_data(f, p_out, 4);
1869 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1870 p_out += new_ilst_size;
1872 set_position(f, ilst_offset + ilst_size);
1873 read_data(f, p_out, (uint32_t) (total_size
1874 - (ilst_offset - total_base) - ilst_size));
1876 free(new_ilst_buffer);
1881 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1885 result = f->cb->write(f->cb->user_data, data, size);
1887 f->current_position += size;
1892 static int32_t write_int32(struct mp4 *f, uint32_t data)
1895 write_u32_be(temp, data);
1896 return write_data(f, temp, sizeof(temp));
1899 int32_t mp4_meta_update(const struct mp4_callback *cb,
1900 const struct mp4_metadata *data)
1902 void *new_moov_data;
1903 uint32_t new_moov_size;
1905 struct mp4 *f = para_calloc(sizeof(struct mp4));
1909 if (!modify_moov(f, data, &new_moov_data, &new_moov_size)) {
1914 /* copy moov atom to end of the file */
1915 if (f->last_atom != ATOM_MOOV) {
1916 char *free_data = "free";
1918 /* rename old moov to free */
1919 set_position(f, f->moov_offset + 4);
1920 write_data(f, free_data, 4);
1922 set_position(f, f->file_size);
1923 write_int32(f, new_moov_size + 8);
1924 write_data(f, "moov", 4);
1925 write_data(f, new_moov_data, new_moov_size);
1927 set_position(f, f->moov_offset);
1928 write_int32(f, new_moov_size + 8);
1929 write_data(f, "moov", 4);
1930 write_data(f, new_moov_data, new_moov_size);
1932 cb->truncate(cb->user_data);
1937 /* find a metadata item by name */
1938 /* returns 0 if item found, 1 if no such item */
1939 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1944 for (i = 0; i < f->tags.count; i++) {
1945 if (!strcasecmp(f->tags.tags[i].item, item)) {
1946 *value = para_strdup(f->tags.tags[i].value);
1957 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1959 return meta_find_by_name(f, "artist", value);
1962 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1964 return meta_find_by_name(f, "title", value);
1967 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1969 return meta_find_by_name(f, "date", value);
1972 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1974 return meta_find_by_name(f, "album", value);
1977 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1979 return meta_find_by_name(f, "comment", value);