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 /* stream to read from */
48 struct mp4_callback *stream;
49 int64_t current_position;
59 /* incremental track index while reading the file */
63 struct mp4_track *track[MAX_TRACKS];
66 struct mp4_metadata tags;
69 int32_t mp4_total_tracks(const struct mp4 *f)
71 return f->total_tracks;
74 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
78 result = f->stream->read(f->stream->user_data, data, size);
83 f->current_position += size;
88 static uint64_t read_int64(struct mp4 *f)
92 read_data(f, data, 8);
93 return read_u64_be(data);
96 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
97 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
99 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
103 /* atoms with subatoms */
111 ATOM_ILST = 8, /* iTunes Metadata list */
122 ATOM_COMPILATION = 19,
132 /* atoms without subatoms */
152 ATOM_META = 148, /* iTunes Metadata box */
153 ATOM_NAME = 149, /* iTunes Metadata name box */
154 ATOM_DATA = 150, /* iTunes Metadata data box */
161 ATOM_ALBUM_ARTIST = 157,
162 ATOM_CONTENTGROUP = 158,
164 ATOM_DESCRIPTION = 160,
167 ATOM_EPISODENAME = 163,
168 ATOM_SORTTITLE = 164,
169 ATOM_SORTALBUM = 165,
170 ATOM_SORTARTIST = 166,
171 ATOM_SORTALBUMARTIST = 167,
172 ATOM_SORTWRITER = 168,
181 #define ATOM_FREE ATOM_UNKNOWN
182 #define ATOM_SKIP ATOM_UNKNOWN
184 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
186 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
189 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
191 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
193 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
195 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
197 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
199 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
201 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
203 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
205 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
207 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
209 } else if (a == 't') {
210 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
212 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
214 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
216 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
218 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
220 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
222 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
224 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
225 return ATOM_EPISODENAME;
226 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
228 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
230 } else if (a == 's') {
231 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
233 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
235 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
237 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
239 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
241 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
243 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
245 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
247 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
249 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
251 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
253 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
254 return ATOM_SORTTITLE;
255 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
256 return ATOM_SORTALBUM;
257 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
258 return ATOM_SORTARTIST;
259 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
260 return ATOM_SORTALBUMARTIST;
261 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
262 return ATOM_SORTWRITER;
263 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
264 return ATOM_SORTSHOW;
265 } else if (a == COPYRIGHT_SYMBOL) {
266 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
268 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
270 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
272 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
274 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
276 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
278 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
280 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
282 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
283 return ATOM_CONTENTGROUP;
284 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
288 if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
290 else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
292 else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
294 else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
296 else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
298 else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
300 else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
302 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
304 else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
306 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
308 else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
310 else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
312 else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
314 else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
315 return ATOM_COMPILATION;
316 else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
318 else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
320 else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
322 else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
324 else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
326 else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
328 else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
330 else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
331 return ATOM_ALBUM_ARTIST;
332 else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
333 return ATOM_DESCRIPTION;
334 else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
340 /* read atom header, return atom size, atom size is with header included */
341 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
342 uint8_t * header_size)
346 int8_t atom_header[8];
348 ret = read_data(f, atom_header, 8);
352 size = read_u32_be(atom_header);
355 /* check for 64 bit atom size */
358 size = read_int64(f);
360 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
361 atom_header[6], atom_header[7]);
365 static int64_t get_position(const struct mp4 *f)
367 return f->current_position;
370 static int need_parse_when_meta_only(uint8_t atom_type)
391 static int32_t set_position(struct mp4 *f, int64_t position)
393 f->stream->seek(f->stream->user_data, position);
394 f->current_position = position;
399 static void track_add(struct mp4 *f)
403 if (f->total_tracks > MAX_TRACKS) {
408 f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
411 static uint8_t read_char(struct mp4 *f)
414 read_data(f, &output, 1);
418 static uint32_t read_int24(struct mp4 *f)
422 read_data(f, data, 3);
423 return read_u24_be(data);
426 static uint32_t read_int32(struct mp4 *f)
430 read_data(f, data, 4);
431 return read_u32_be(data);
434 static int32_t read_stsz(struct mp4 *f)
439 if (f->total_tracks == 0)
441 t = f->track[f->total_tracks - 1];
442 read_char(f); /* version */
443 read_int24(f); /* flags */
444 t->stsz_sample_size = read_int32(f);
445 t->stsz_sample_count = read_int32(f);
446 if (t->stsz_sample_size != 0)
448 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
449 for (i = 0; i < t->stsz_sample_count && !f->read_error; i++)
450 t->stsz_table[i] = read_int32(f);
454 static int32_t read_stts(struct mp4 *f)
460 if (f->total_tracks == 0)
462 t = f->track[f->total_tracks - 1];
463 if (t->stts_entry_count)
465 read_char(f); /* version */
466 read_int24(f); /* flags */
467 t->stts_entry_count = read_int32(f);
469 t->stts_sample_count = para_malloc(t->stts_entry_count
471 t->stts_sample_delta = para_malloc(t->stts_entry_count
474 for (i = 0; i < t->stts_entry_count && !f->read_error; i++) {
475 t->stts_sample_count[i] = read_int32(f);
476 t->stts_sample_delta[i] = read_int32(f);
481 static int32_t read_stsc(struct mp4 *f)
486 if (f->total_tracks == 0)
488 t = f->track[f->total_tracks - 1];
490 read_char(f); /* version */
491 read_int24(f); /* flags */
492 t->stsc_entry_count = read_int32(f);
493 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
494 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
496 t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
500 for (i = 0; i < t->stsc_entry_count && !f->read_error; i++) {
501 t->stsc_first_chunk[i] = read_int32(f);
502 t->stsc_samples_per_chunk[i] = read_int32(f);
503 t->stsc_sample_desc_index[i] = read_int32(f);
508 static int32_t read_stco(struct mp4 *f)
513 if (f->total_tracks == 0)
515 t = f->track[f->total_tracks - 1];
517 read_char(f); /* version */
518 read_int24(f); /* flags */
519 t->stco_entry_count = read_int32(f);
520 t->stco_chunk_offset = para_malloc(t->stco_entry_count
523 for (i = 0; i < t->stco_entry_count && !f->read_error; i++)
524 t->stco_chunk_offset[i] = read_int32(f);
528 static uint16_t read_int16(struct mp4 *f)
532 read_data(f, data, 2);
533 return read_u16_be(data);
536 static int32_t read_mp4a(struct mp4 *f)
539 uint8_t atom_type = 0;
540 uint8_t header_size = 0;
543 if (f->total_tracks == 0)
545 t = f->track[f->total_tracks - 1];
547 for (i = 0; i < 6; i++) {
548 read_char(f); /* reserved */
550 /* data_reference_index */ read_int16(f);
552 read_int32(f); /* reserved */
553 read_int32(f); /* reserved */
555 t->channelCount = read_int16(f);
561 t->sampleRate = read_int16(f);
565 atom_read_header(f, &atom_type, &header_size);
569 static int32_t read_stsd(struct mp4 *f)
571 int32_t i, entry_count;
572 uint8_t header_size = 0;
576 if (f->total_tracks == 0)
578 t = f->track[f->total_tracks - 1];
580 read_char(f); /* version */
581 read_int24(f); /* flags */
583 entry_count = read_int32(f);
586 for (i = 0; i < entry_count && !f->read_error; i++) {
587 uint64_t skip = get_position(f);
589 uint8_t atom_type = 0;
590 size = atom_read_header(f, &atom_type, &header_size);
592 t->is_audio = atom_type == ATOM_MP4A;
595 set_position(f, skip);
601 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
602 const char *value, int32_t len)
604 if (!item || (item && !*item) || !value)
606 tags->tags = para_realloc(tags->tags,
607 (tags->count + 1) * sizeof(struct mp4_tag));
608 tags->tags[tags->count].item = para_strdup(item);
609 tags->tags[tags->count].len = len;
611 tags->tags[tags->count].value = para_malloc(len + 1);
612 memcpy(tags->tags[tags->count].value, value, len);
613 tags->tags[tags->count].value[len] = 0;
615 tags->tags[tags->count].value = para_strdup(value);
621 static const char *ID3v1GenreList[] = {
622 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
623 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
624 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
625 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
626 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
627 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
628 "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
629 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
630 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
631 "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
632 "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
633 "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
634 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
635 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
636 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
637 "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
638 "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
639 "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
640 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
641 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
642 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
643 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
644 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
645 "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
646 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
647 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
648 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
649 "Anime", "JPop", "SynthPop",
652 static const char *meta_index_to_genre(uint32_t idx)
654 if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
655 return ID3v1GenreList[idx - 1];
661 static char *read_string(struct mp4 *f, uint32_t length)
663 char *str = para_malloc(length + 1);
664 if ((uint32_t)read_data(f, str, length) != length) {
672 static int32_t set_metadata_name(uint8_t atom_type, char **name)
674 static char *tag_names[] = {
675 "unknown", "title", "artist", "writer", "album",
676 "date", "tool", "comment", "genre", "track",
677 "disc", "compilation", "genre", "tempo", "cover",
678 "album_artist", "contentgroup", "lyrics", "description",
679 "network", "show", "episodename",
680 "sorttitle", "sortalbum", "sortartist", "sortalbumartist",
681 "sortwriter", "sortshow",
682 "season", "episode", "podcast"
717 case ATOM_COMPILATION:
729 case ATOM_ALBUM_ARTIST:
732 case ATOM_CONTENTGROUP:
738 case ATOM_DESCRIPTION:
747 case ATOM_EPISODENAME:
756 case ATOM_SORTARTIST:
759 case ATOM_SORTALBUMARTIST:
762 case ATOM_SORTWRITER:
782 *name = para_strdup(tag_names[tag_idx]);
786 static uint32_t min_body_size(uint8_t atom_type)
793 return sizeof (char) /* version */
794 + sizeof(uint8_t) * 3 /* flags */
795 + sizeof(uint32_t) /* reserved */
796 + sizeof(uint16_t) /* leading uint16_t */
797 + sizeof(uint16_t) /* track */
798 + sizeof(uint16_t); /* totaltracks */
800 return sizeof (char) /* version */
801 + sizeof(uint8_t) * 3 /* flags */
802 + sizeof(uint32_t) /* reserved */
803 + sizeof(uint16_t) /* disc */
804 + sizeof(uint16_t); /* totaldiscs */
805 default: assert(false);
809 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
812 uint8_t header_size = 0;
813 uint64_t subsize, sumsize;
822 sumsize < size && !f->read_error; /* CVE-2017-9222 */
823 set_position(f, destpos), sumsize += subsize
825 subsize = atom_read_header(f, &atom_type, &header_size);
826 destpos = get_position(f) + subsize - header_size;
829 if (atom_type == ATOM_NAME) {
830 read_char(f); /* version */
831 read_int24(f); /* flags */
833 name = read_string(f, subsize - (header_size + 4));
836 if (atom_type != ATOM_DATA)
838 read_char(f); /* version */
839 read_int24(f); /* flags */
840 read_int32(f); /* reserved */
842 /* some need special attention */
843 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
845 if (subsize - header_size < min_body_size(parent))
848 if (parent == ATOM_TEMPO) {
850 sprintf(temp, "%.5u BPM", val);
851 tag_add_field(&(f-> tags), "tempo",
854 const char *tmp = meta_index_to_genre(val);
856 tag_add_field (&(f->tags),
860 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
861 uint16_t index, total;
863 if (subsize - header_size < min_body_size(parent))
866 index = read_int16(f);
867 total = read_int16(f);
868 if (parent == ATOM_TRACK)
870 sprintf(temp, "%d", index);
871 tag_add_field(&(f->tags), parent == ATOM_TRACK?
872 "track" : "disc", temp, -1);
874 sprintf(temp, "%d", total);
875 tag_add_field(& (f-> tags),
876 parent == ATOM_TRACK?
877 "totaltracks" : "totaldiscs", temp, -1);
882 data = read_string(f, subsize - (header_size + 8));
883 len = subsize - (header_size + 8);
889 set_metadata_name(parent , &name);
891 tag_add_field(&(f->tags), name, data, len);
900 static int32_t read_mdhd(struct mp4 *f)
906 if (f->total_tracks == 0)
908 t = f->track[f->total_tracks - 1];
910 version = read_int32(f);
912 read_int64(f); //creation-time
913 read_int64(f); //modification-time
914 t->timeScale = read_int32(f); //timescale
915 t->duration = read_int64(f); //duration
916 } else { //version == 0
919 read_int32(f); //creation-time
920 read_int32(f); //modification-time
921 t->timeScale = read_int32(f); //timescale
922 temp = read_int32(f);
923 t->duration = (temp == (uint32_t) (-1))?
924 (uint64_t) (-1) : (uint64_t) (temp);
931 static int32_t parse_metadata(struct mp4 *f, int32_t size)
933 uint64_t subsize, sumsize = 0;
935 uint8_t header_size = 0;
937 while (sumsize < size) {
938 subsize = atom_read_header(f, &atom_type, &header_size);
941 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
948 static int32_t read_meta(struct mp4 *f, uint64_t size)
950 uint64_t subsize, sumsize = 0;
952 uint8_t header_size = 0;
954 read_char(f); /* version */
955 read_int24(f); /* flags */
957 while (sumsize < (size - (header_size + 4))) {
958 subsize = atom_read_header(f, &atom_type, &header_size);
959 if (subsize <= header_size + 4)
961 if (atom_type == ATOM_ILST) {
962 parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
964 set_position(f, get_position(f) + subsize - header_size);
972 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
974 uint64_t dest_position = get_position(f) + size - 8;
975 if (atom_type == ATOM_STSZ) {
976 /* sample size box */
978 } else if (atom_type == ATOM_STTS) {
979 /* time to sample box */
981 } else if (atom_type == ATOM_STSC) {
982 /* sample to chunk box */
984 } else if (atom_type == ATOM_STCO) {
985 /* chunk offset box */
987 } else if (atom_type == ATOM_STSD) {
988 /* sample description box */
990 } else if (atom_type == ATOM_MDHD) {
993 } else if (atom_type == ATOM_META) {
994 /* iTunes Metadata box */
998 set_position(f, dest_position);
1002 /* parse atoms that are sub atoms of other atoms */
1003 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
1006 uint8_t atom_type = 0;
1007 uint64_t counted_size = 0;
1008 uint8_t header_size = 0;
1010 while (counted_size < total_size) {
1011 size = atom_read_header(f, &atom_type, &header_size);
1012 counted_size += size;
1014 /* check for end of file */
1018 /* we're starting to read a new track, update index,
1019 * so that all data and tables get written in the right place
1021 if (atom_type == ATOM_TRAK)
1023 /* parse subatoms */
1024 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1025 set_position(f, get_position(f) + size - header_size);
1026 } else if (atom_type < SUBATOMIC) {
1027 parse_sub_atoms(f, size - header_size, meta_only);
1029 atom_read(f, (uint32_t) size, atom_type);
1036 /* parse root atoms */
1037 static int32_t parse_atoms(struct mp4 *f, int meta_only)
1040 uint8_t atom_type = 0;
1041 uint8_t header_size = 0;
1047 atom_read_header(f, &atom_type, &header_size)) != 0) {
1048 f->file_size += size;
1049 f->last_atom = atom_type;
1051 if (atom_type == ATOM_MOOV && size > header_size) {
1052 f->moov_offset = get_position(f) - header_size;
1053 f->moov_size = size;
1056 /* parse subatoms */
1057 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1058 set_position(f, get_position(f) + size - header_size);
1059 } else if (atom_type < SUBATOMIC) {
1060 parse_sub_atoms(f, size - header_size, meta_only);
1062 /* skip this atom */
1063 set_position(f, get_position(f) + size - header_size);
1070 struct mp4 *mp4_open_read(struct mp4_callback *f)
1072 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1086 static int32_t tag_delete(struct mp4_metadata *tags)
1090 for (i = 0; i < tags->count; i++) {
1091 free(tags->tags[i].item);
1092 free(tags->tags[i].value);
1101 void mp4_close(struct mp4 *ff)
1105 for (i = 0; i < ff->total_tracks; i++) {
1107 free(ff->track[i]->stsz_table);
1108 free(ff->track[i]->stts_sample_count);
1109 free(ff->track[i]->stts_sample_delta);
1110 free(ff->track[i]->stsc_first_chunk);
1111 free(ff->track[i]->stsc_samples_per_chunk);
1112 free(ff->track[i]->stsc_sample_desc_index);
1113 free(ff->track[i]->stco_chunk_offset);
1118 tag_delete(&(ff->tags));
1122 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1123 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1125 int32_t total_entries = 0;
1126 int32_t chunk2entry;
1127 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1131 if (f->track[track] == NULL) {
1135 total_entries = f->track[track]->stsc_entry_count;
1142 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1143 *chunk = chunk2 - chunk1;
1144 range_samples = *chunk * chunk1samples;
1146 if (sample < total + range_samples)
1149 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1152 if (chunk2entry < total_entries) {
1154 total += range_samples;
1156 } while (chunk2entry < total_entries);
1159 *chunk = (sample - total) / chunk1samples + chunk1;
1163 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1168 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1171 const struct mp4_track *p_track = f->track[track];
1173 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1174 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1176 } else if (p_track->stco_entry_count) {
1177 return p_track->stco_chunk_offset[chunk - 1];
1185 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1186 int32_t chunk_sample, int32_t sample)
1189 const struct mp4_track *p_track = f->track[track];
1191 if (p_track->stsz_sample_size) {
1192 return (sample - chunk_sample) * p_track->stsz_sample_size;
1194 if (sample >= p_track->stsz_sample_count)
1197 for (i = chunk_sample, total = 0; i < sample; i++) {
1198 total += p_track->stsz_table[i];
1205 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1208 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1210 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1212 chunk_offset1 = chunk_to_offset(f, track, chunk);
1213 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1214 track, chunk_sample, sample);
1215 return chunk_offset2;
1219 * Return the number of milliseconds of the given track.
1221 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1222 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1224 * The function returns zero if the audio file is of zero length or contains a
1225 * corrupt track header.
1227 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1229 const struct mp4_track *t = f->track[track];
1231 if (t->timeScale == 0)
1233 return t->duration * 1000 / t->timeScale;
1237 * Check whether the given track number corresponds to an audio track.
1239 * \param f See \ref mp4_get_duration().
1240 * \param track See \ref mp4_get_duration().
1242 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1243 * those the function returns false.
1245 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1247 return f->track[track]->is_audio;
1250 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1252 int32_t offset = sample_to_offset(f, track, sample);
1253 set_position(f, offset);
1256 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1258 const struct mp4_track *t = f->track[track];
1260 if (t->stsz_sample_size != 0)
1261 return t->stsz_sample_size;
1262 return t->stsz_table[sample];
1265 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1267 return f->track[track]->sampleRate;
1270 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1272 return f->track[track]->channelCount;
1275 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1280 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1281 total += f->track[track]->stts_sample_count[i];
1286 struct mp4 *mp4_open_meta(struct mp4_callback *f)
1288 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1302 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1304 return f->tags.count;
1307 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1308 char **item, char **value)
1310 if (index >= f->tags.count) {
1315 *item = para_strdup(f->tags.tags[index].item);
1316 *value = para_strdup(f->tags.tags[index].value);
1321 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1324 uint32_t remaining = size;
1325 uint64_t atom_offset = base;
1330 set_position(f, atom_offset);
1334 atom_size = read_int32(f);
1335 if (atom_size > remaining || atom_size < 8)
1337 read_data(f, atom_name, 4);
1339 if (!memcmp(atom_name, name, 4)) {
1340 set_position(f, atom_offset);
1344 remaining -= atom_size;
1345 atom_offset += atom_size;
1350 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1351 const char *name, uint32_t extraheaders, const char *name_inside)
1353 uint64_t first_base = (uint64_t) (-1);
1354 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1356 uint64_t mybase = get_position(f);
1357 uint32_t mysize = read_int32(f);
1359 if (first_base == (uint64_t) (-1))
1360 first_base = mybase;
1362 if (mysize < 8 + extraheaders)
1365 if (find_atom (f, mybase + (8 + extraheaders),
1366 mysize - (8 + extraheaders), name_inside)) {
1367 set_position(f, mybase);
1371 if (size <= mysize) {
1378 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1380 set_position(f, first_base);
1393 static struct membuffer *membuffer_create(void)
1395 const unsigned initial_size = 256;
1397 struct membuffer *buf = para_malloc(sizeof(*buf));
1398 buf->data = para_malloc(initial_size);
1400 buf->allocated = initial_size;
1401 buf->error = buf->data == 0 ? 1 : 0;
1406 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1408 unsigned dest_size = buf->written + bytes;
1412 if (dest_size > buf->allocated) {
1414 buf->allocated <<= 1;
1415 } while (dest_size > buf->allocated);
1416 buf->data = para_realloc(buf->data, buf->allocated);
1420 memcpy((char *) buf->data + buf->written, ptr, bytes);
1421 buf->written += bytes;
1425 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1427 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1430 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1434 write_u16_be(temp, data);
1435 return membuffer_write(buf, temp, 2);
1438 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1441 write_u32_be(temp, data);
1442 return membuffer_write(buf, temp, 4);
1445 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1446 uint32_t index, uint32_t total)
1448 membuffer_write_int32(buf,
1449 8 /*atom header */ + 8 /*data atom header */ +
1450 8 /*flags + reserved */ + 8 /*actual data */ );
1451 membuffer_write_atom_name(buf, name);
1452 membuffer_write_int32(buf,
1453 8 /*data atom header */ +
1454 8 /*flags + reserved */ + 8 /*actual data */ );
1455 membuffer_write_atom_name(buf, "data");
1456 membuffer_write_int32(buf, 0); //flags
1457 membuffer_write_int32(buf, 0); //reserved
1458 membuffer_write_int16(buf, 0);
1459 membuffer_write_int16(buf, (uint16_t) index); //track number
1460 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1461 membuffer_write_int16(buf, 0);
1464 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1467 membuffer_write_int32(buf,
1468 8 /*atom header */ + 8 /*data atom header */ +
1469 8 /*flags + reserved */ + 2 /*actual data */ );
1470 membuffer_write_atom_name(buf, name);
1471 membuffer_write_int32(buf,
1472 8 /*data atom header */ +
1473 8 /*flags + reserved */ + 2 /*actual data */ );
1474 membuffer_write_atom_name(buf, "data");
1475 membuffer_write_int32(buf, 0); //flags
1476 membuffer_write_int32(buf, 0); //reserved
1477 membuffer_write_int16(buf, value); //value
1480 static uint32_t myatoi(const char *param)
1482 return param ? atoi(param) : 0;
1485 static uint32_t meta_genre_to_index(const char *genrestr)
1488 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1489 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1495 struct stdmeta_entry {
1500 struct stdmeta_entry stdmetas[] = {
1501 {"\xA9" "nam", "title"},
1502 {"\xA9" "ART", "artist"},
1503 {"\xA9" "wrt", "writer"},
1504 {"\xA9" "alb", "album"},
1505 {"\xA9" "day", "date"},
1506 {"\xA9" "too", "tool"},
1507 {"\xA9" "cmt", "comment"},
1508 {"cpil", "compilation"},
1510 {"aART", "album_artist"},
1513 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1516 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1517 if (!strcasecmp(name, stdmetas[n].name))
1518 return stdmetas[n].atom;
1523 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1528 /* special check for compilation flag */
1529 if (strcmp(name, "cpil") == 0) {
1533 membuffer_write_int32(buf,
1534 8 /*atom header */ + 8 /*data atom header */ +
1535 8 /*flags + reserved */ + strlen(value));
1536 membuffer_write_atom_name(buf, name);
1537 membuffer_write_int32(buf,
1538 8 /*data atom header */ +
1539 8 /*flags + reserved */ + strlen(value));
1540 membuffer_write_atom_name(buf, "data");
1541 membuffer_write_int32(buf, flags); //flags
1542 membuffer_write_int32(buf, 0); //reserved
1543 membuffer_write(buf, value, strlen(value));
1546 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1549 membuffer_write_int32(buf,
1550 8 /*atom header */ +
1551 0x1C /*weirdo itunes atom */ +
1552 12 /*name atom header */ + strlen(name) +
1553 16 /*data atom header + flags */ + strlen(value));
1554 membuffer_write_atom_name(buf, "----");
1555 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1556 membuffer_write_atom_name(buf, "mean");
1557 membuffer_write_int32(buf, 0);
1558 membuffer_write(buf, "com.apple.iTunes", 16);
1559 membuffer_write_int32(buf, 12 + strlen(name));
1560 membuffer_write_atom_name(buf, "name");
1561 membuffer_write_int32(buf, 0);
1562 membuffer_write(buf, name, strlen(name));
1563 membuffer_write_int32(buf,
1564 8 /*data atom header */ +
1565 8 /*flags + reserved */ + strlen(value));
1566 membuffer_write_atom_name(buf, "data");
1567 membuffer_write_int32(buf, 1); //flags
1568 membuffer_write_int32(buf, 0); //reserved
1569 membuffer_write(buf, value, strlen(value));
1572 static unsigned membuffer_error(const struct membuffer *buf)
1577 static void membuffer_free(struct membuffer *buf)
1583 static unsigned membuffer_get_size(const struct membuffer *buf)
1585 return buf->written;
1588 static void *membuffer_detach(struct membuffer *buf)
1594 ret = para_realloc(buf->data, buf->written);
1600 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1601 uint32_t * out_size)
1603 struct membuffer *buf = membuffer_create();
1605 char *mask = para_calloc(data->count);
1606 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1607 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1608 const char *genre_ptr = 0, *tempo_ptr = 0;
1610 for (metaptr = 0; metaptr < data->count; metaptr++) {
1611 struct mp4_tag *tag = &data->tags[metaptr];
1612 if (!strcasecmp(tag->item, "tracknumber")
1613 || !strcasecmp(tag->item, "track")) {
1614 if (tracknumber_ptr == 0)
1615 tracknumber_ptr = tag->value;
1617 } else if (!strcasecmp(tag->item, "totaltracks")) {
1618 if (totaltracks_ptr == 0)
1619 totaltracks_ptr = tag->value;
1621 } else if (!strcasecmp(tag->item, "discnumber")
1622 || !strcasecmp(tag->item, "disc")) {
1623 if (discnumber_ptr == 0)
1624 discnumber_ptr = tag->value;
1626 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1627 if (totaldiscs_ptr == 0)
1628 totaldiscs_ptr = tag->value;
1630 } else if (!strcasecmp(tag->item, "genre")) {
1632 genre_ptr = tag->value;
1634 } else if (!strcasecmp(tag->item, "tempo")) {
1636 tempo_ptr = tag->value;
1641 if (tracknumber_ptr)
1642 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1643 myatoi(totaltracks_ptr));
1645 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1646 myatoi(totaldiscs_ptr));
1648 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1651 uint32_t index = meta_genre_to_index(genre_ptr);
1653 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1655 membuffer_write_int16_tag(buf, "gnre", index);
1657 for (metaptr = 0; metaptr < data->count; metaptr++) {
1658 struct mp4_tag *tag;
1659 const char *std_meta_atom;
1663 tag = &data->tags[metaptr];
1664 std_meta_atom = find_standard_meta(tag->item);
1666 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1668 membuffer_write_custom_tag(buf, tag->item, tag->value);
1672 if (membuffer_error(buf)) {
1673 membuffer_free(buf);
1677 *out_size = membuffer_get_size(buf);
1678 *out_buffer = membuffer_detach(buf);
1679 membuffer_free(buf);
1684 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1687 membuffer_write_int32(buf, size + 8);
1688 membuffer_write_atom_name(buf, name);
1689 membuffer_write(buf, data, size);
1692 static void *membuffer_get_ptr(const struct membuffer *buf)
1697 static void membuffer_set_error(struct membuffer *buf)
1702 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1708 oldsize = membuffer_get_size(buf);
1709 if (membuffer_write(buf, 0, bytes) != bytes)
1712 bufptr = membuffer_get_ptr(buf);
1716 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1718 membuffer_set_error(buf);
1725 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1726 uint32_t * out_size)
1728 struct membuffer *buf;
1732 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1735 buf = membuffer_create();
1737 membuffer_write_int32(buf, 0);
1738 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1741 *out_size = membuffer_get_size(buf);
1742 *out_buffer = membuffer_detach(buf);
1743 membuffer_free(buf);
1747 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1748 uint32_t * out_size)
1750 struct membuffer *buf;
1754 if (!create_meta(data, &meta_buffer, &meta_size))
1757 buf = membuffer_create();
1759 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1763 *out_size = membuffer_get_size(buf);
1764 *out_buffer = membuffer_detach(buf);
1765 membuffer_free(buf);
1769 static uint32_t fix_byte_order_32(uint32_t src)
1771 return read_u32_be(&src);
1774 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1775 void **out_buffer, uint32_t * out_size)
1777 uint64_t total_base = f->moov_offset + 8;
1778 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1780 uint64_t udta_offset, meta_offset, ilst_offset;
1781 uint32_t udta_size, meta_size, ilst_size;
1783 uint32_t new_ilst_size;
1784 void *new_ilst_buffer;
1789 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1790 struct membuffer *buf;
1791 void *new_udta_buffer;
1792 uint32_t new_udta_size;
1793 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1796 buf = membuffer_create();
1797 set_position(f, total_base);
1798 membuffer_transfer_from_file(buf, f, total_size);
1800 membuffer_write_atom(buf, "udta", new_udta_size,
1803 free(new_udta_buffer);
1805 *out_size = membuffer_get_size(buf);
1806 *out_buffer = membuffer_detach(buf);
1807 membuffer_free(buf);
1810 udta_offset = get_position(f);
1811 udta_size = read_int32(f);
1812 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1813 struct membuffer *buf;
1814 void *new_meta_buffer;
1815 uint32_t new_meta_size;
1816 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1819 buf = membuffer_create();
1820 set_position(f, total_base);
1821 membuffer_transfer_from_file(buf, f,
1822 (uint32_t)(udta_offset - total_base));
1824 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1825 membuffer_write_atom_name(buf, "udta");
1826 membuffer_transfer_from_file(buf, f, udta_size);
1828 membuffer_write_atom(buf, "meta", new_meta_size,
1830 free(new_meta_buffer);
1832 *out_size = membuffer_get_size(buf);
1833 *out_buffer = membuffer_detach(buf);
1834 membuffer_free(buf);
1837 meta_offset = get_position(f);
1838 meta_size = read_int32(f);
1839 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1840 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1841 ilst_offset = get_position(f);
1842 ilst_size = read_int32(f);
1844 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1847 size_delta = new_ilst_size - (ilst_size - 8);
1849 *out_size = total_size + size_delta;
1850 *out_buffer = para_malloc(*out_size);
1851 p_out = (uint8_t *) * out_buffer;
1853 set_position(f, total_base);
1855 (uint32_t) (udta_offset - total_base));
1856 p_out += (uint32_t) (udta_offset - total_base);
1857 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1859 read_data(f, p_out, 4);
1862 (uint32_t) (meta_offset - udta_offset - 8));
1863 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1864 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1866 read_data(f, p_out, 4);
1869 (uint32_t) (ilst_offset - meta_offset - 8));
1870 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1871 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1873 read_data(f, p_out, 4);
1876 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1877 p_out += new_ilst_size;
1879 set_position(f, ilst_offset + ilst_size);
1880 read_data(f, p_out, (uint32_t) (total_size
1881 - (ilst_offset - total_base) - ilst_size));
1883 free(new_ilst_buffer);
1888 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1892 result = f->stream->write(f->stream->user_data, data, size);
1894 f->current_position += size;
1899 static int32_t write_int32(struct mp4 *f, uint32_t data)
1902 write_u32_be(temp, data);
1903 return write_data(f, temp, sizeof(temp));
1906 int32_t mp4_meta_update(struct mp4_callback *f, const struct mp4_metadata *data)
1908 void *new_moov_data;
1909 uint32_t new_moov_size;
1911 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1913 set_position(ff, 0);
1917 if (!modify_moov(ff, data, &new_moov_data, &new_moov_size)) {
1922 /* copy moov atom to end of the file */
1923 if (ff->last_atom != ATOM_MOOV) {
1924 char *free_data = "free";
1926 /* rename old moov to free */
1927 set_position(ff, ff->moov_offset + 4);
1928 write_data(ff, free_data, 4);
1930 set_position(ff, ff->file_size);
1931 write_int32(ff, new_moov_size + 8);
1932 write_data(ff, "moov", 4);
1933 write_data(ff, new_moov_data, new_moov_size);
1935 set_position(ff, ff->moov_offset);
1936 write_int32(ff, new_moov_size + 8);
1937 write_data(ff, "moov", 4);
1938 write_data(ff, new_moov_data, new_moov_size);
1940 f->truncate(f->user_data);
1945 /* find a metadata item by name */
1946 /* returns 0 if item found, 1 if no such item */
1947 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1952 for (i = 0; i < f->tags.count; i++) {
1953 if (!strcasecmp(f->tags.tags[i].item, item)) {
1954 *value = para_strdup(f->tags.tags[i].value);
1965 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1967 return meta_find_by_name(f, "artist", value);
1970 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1972 return meta_find_by_name(f, "title", value);
1975 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1977 return meta_find_by_name(f, "date", value);
1980 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1982 return meta_find_by_name(f, "album", value);
1985 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1987 return meta_find_by_name(f, "comment", value);