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"
22 int32_t stsz_sample_size;
23 int32_t stsz_sample_count;
27 int32_t stts_entry_count;
28 int32_t *stts_sample_count;
29 int32_t *stts_sample_delta;
32 int32_t stsc_entry_count;
33 int32_t *stsc_first_chunk;
34 int32_t *stsc_samples_per_chunk;
35 int32_t *stsc_sample_desc_index;
38 int32_t stco_entry_count;
39 int32_t *stco_chunk_offset;
45 #define MAX_TRACKS 1024
48 /* stream to read from */
49 struct mp4_callback *stream;
50 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->stream->read(f->stream->user_data, data, size);
80 f->stream->read_error++;
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->stream->seek(f->stream->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->stream->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->stream->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->stream->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->stream->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);
555 t->sampleSize = 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->stream->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->stream->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;
1043 f->stream->read_error = 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(struct mp4_callback *f)
1071 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1085 static int32_t tag_delete(struct mp4_metadata *tags)
1089 for (i = 0; i < tags->count; i++) {
1090 free(tags->tags[i].item);
1091 free(tags->tags[i].value);
1100 void mp4_close(struct mp4 *ff)
1104 for (i = 0; i < ff->total_tracks; i++) {
1106 free(ff->track[i]->stsz_table);
1107 free(ff->track[i]->stts_sample_count);
1108 free(ff->track[i]->stts_sample_delta);
1109 free(ff->track[i]->stsc_first_chunk);
1110 free(ff->track[i]->stsc_samples_per_chunk);
1111 free(ff->track[i]->stsc_sample_desc_index);
1112 free(ff->track[i]->stco_chunk_offset);
1117 tag_delete(&(ff->tags));
1121 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1122 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1124 int32_t total_entries = 0;
1125 int32_t chunk2entry;
1126 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1130 if (f->track[track] == NULL) {
1134 total_entries = f->track[track]->stsc_entry_count;
1141 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1142 *chunk = chunk2 - chunk1;
1143 range_samples = *chunk * chunk1samples;
1145 if (sample < total + range_samples)
1148 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1151 if (chunk2entry < total_entries) {
1153 total += range_samples;
1155 } while (chunk2entry < total_entries);
1158 *chunk = (sample - total) / chunk1samples + chunk1;
1162 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1167 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1170 const struct mp4_track *p_track = f->track[track];
1172 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1173 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1175 } else if (p_track->stco_entry_count) {
1176 return p_track->stco_chunk_offset[chunk - 1];
1184 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1185 int32_t chunk_sample, int32_t sample)
1188 const struct mp4_track *p_track = f->track[track];
1190 if (p_track->stsz_sample_size) {
1191 return (sample - chunk_sample) * p_track->stsz_sample_size;
1193 if (sample >= p_track->stsz_sample_count)
1196 for (i = chunk_sample, total = 0; i < sample; i++) {
1197 total += p_track->stsz_table[i];
1204 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1207 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1209 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1211 chunk_offset1 = chunk_to_offset(f, track, chunk);
1212 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1213 track, chunk_sample, sample);
1214 return chunk_offset2;
1218 * Return the number of milliseconds of the given track.
1220 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1221 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1223 * The function returns zero if the audio file is of zero length or contains a
1224 * corrupt track header.
1226 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1228 const struct mp4_track *t = f->track[track];
1230 if (t->timeScale == 0)
1232 return t->duration * 1000 / t->timeScale;
1236 * Check whether the given track number corresponds to an audio track.
1238 * \param f See \ref mp4_get_duration().
1239 * \param track See \ref mp4_get_duration().
1241 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1242 * those the function returns false.
1244 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1246 return f->track[track]->is_audio;
1249 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1251 int32_t offset = sample_to_offset(f, track, sample);
1252 set_position(f, offset);
1255 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1257 const struct mp4_track *t = f->track[track];
1259 if (t->stsz_sample_size != 0)
1260 return t->stsz_sample_size;
1261 return t->stsz_table[sample];
1264 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1266 return f->track[track]->sampleRate;
1269 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1271 return f->track[track]->channelCount;
1274 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1279 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1280 total += f->track[track]->stts_sample_count[i];
1285 struct mp4 *mp4_open_meta(struct mp4_callback *f)
1287 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1301 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1303 return f->tags.count;
1306 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1307 char **item, char **value)
1309 if (index >= f->tags.count) {
1314 *item = para_strdup(f->tags.tags[index].item);
1315 *value = para_strdup(f->tags.tags[index].value);
1320 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1323 uint32_t remaining = size;
1324 uint64_t atom_offset = base;
1329 set_position(f, atom_offset);
1333 atom_size = read_int32(f);
1334 if (atom_size > remaining || atom_size < 8)
1336 read_data(f, atom_name, 4);
1338 if (!memcmp(atom_name, name, 4)) {
1339 set_position(f, atom_offset);
1343 remaining -= atom_size;
1344 atom_offset += atom_size;
1349 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1350 const char *name, uint32_t extraheaders, const char *name_inside)
1352 uint64_t first_base = (uint64_t) (-1);
1353 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1355 uint64_t mybase = get_position(f);
1356 uint32_t mysize = read_int32(f);
1358 if (first_base == (uint64_t) (-1))
1359 first_base = mybase;
1361 if (mysize < 8 + extraheaders)
1364 if (find_atom (f, mybase + (8 + extraheaders),
1365 mysize - (8 + extraheaders), name_inside)) {
1366 set_position(f, mybase);
1370 if (size <= mysize) {
1377 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1379 set_position(f, first_base);
1392 static struct membuffer *membuffer_create(void)
1394 const unsigned initial_size = 256;
1396 struct membuffer *buf = para_malloc(sizeof(*buf));
1397 buf->data = para_malloc(initial_size);
1399 buf->allocated = initial_size;
1400 buf->error = buf->data == 0 ? 1 : 0;
1405 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1407 unsigned dest_size = buf->written + bytes;
1411 if (dest_size > buf->allocated) {
1413 buf->allocated <<= 1;
1414 } while (dest_size > buf->allocated);
1415 buf->data = para_realloc(buf->data, buf->allocated);
1419 memcpy((char *) buf->data + buf->written, ptr, bytes);
1420 buf->written += bytes;
1424 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1426 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1429 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1433 write_u16_be(temp, data);
1434 return membuffer_write(buf, temp, 2);
1437 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1440 write_u32_be(temp, data);
1441 return membuffer_write(buf, temp, 4);
1444 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1445 uint32_t index, uint32_t total)
1447 membuffer_write_int32(buf,
1448 8 /*atom header */ + 8 /*data atom header */ +
1449 8 /*flags + reserved */ + 8 /*actual data */ );
1450 membuffer_write_atom_name(buf, name);
1451 membuffer_write_int32(buf,
1452 8 /*data atom header */ +
1453 8 /*flags + reserved */ + 8 /*actual data */ );
1454 membuffer_write_atom_name(buf, "data");
1455 membuffer_write_int32(buf, 0); //flags
1456 membuffer_write_int32(buf, 0); //reserved
1457 membuffer_write_int16(buf, 0);
1458 membuffer_write_int16(buf, (uint16_t) index); //track number
1459 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1460 membuffer_write_int16(buf, 0);
1463 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1466 membuffer_write_int32(buf,
1467 8 /*atom header */ + 8 /*data atom header */ +
1468 8 /*flags + reserved */ + 2 /*actual data */ );
1469 membuffer_write_atom_name(buf, name);
1470 membuffer_write_int32(buf,
1471 8 /*data atom header */ +
1472 8 /*flags + reserved */ + 2 /*actual data */ );
1473 membuffer_write_atom_name(buf, "data");
1474 membuffer_write_int32(buf, 0); //flags
1475 membuffer_write_int32(buf, 0); //reserved
1476 membuffer_write_int16(buf, value); //value
1479 static uint32_t myatoi(const char *param)
1481 return param ? atoi(param) : 0;
1484 static uint32_t meta_genre_to_index(const char *genrestr)
1487 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1488 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1494 struct stdmeta_entry {
1499 struct stdmeta_entry stdmetas[] = {
1500 {"\xA9" "nam", "title"},
1501 {"\xA9" "ART", "artist"},
1502 {"\xA9" "wrt", "writer"},
1503 {"\xA9" "alb", "album"},
1504 {"\xA9" "day", "date"},
1505 {"\xA9" "too", "tool"},
1506 {"\xA9" "cmt", "comment"},
1507 {"cpil", "compilation"},
1509 {"aART", "album_artist"},
1512 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1515 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1516 if (!strcasecmp(name, stdmetas[n].name))
1517 return stdmetas[n].atom;
1522 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1527 /* special check for compilation flag */
1528 if (strcmp(name, "cpil") == 0) {
1532 membuffer_write_int32(buf,
1533 8 /*atom header */ + 8 /*data atom header */ +
1534 8 /*flags + reserved */ + strlen(value));
1535 membuffer_write_atom_name(buf, name);
1536 membuffer_write_int32(buf,
1537 8 /*data atom header */ +
1538 8 /*flags + reserved */ + strlen(value));
1539 membuffer_write_atom_name(buf, "data");
1540 membuffer_write_int32(buf, flags); //flags
1541 membuffer_write_int32(buf, 0); //reserved
1542 membuffer_write(buf, value, strlen(value));
1545 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1548 membuffer_write_int32(buf,
1549 8 /*atom header */ +
1550 0x1C /*weirdo itunes atom */ +
1551 12 /*name atom header */ + strlen(name) +
1552 16 /*data atom header + flags */ + strlen(value));
1553 membuffer_write_atom_name(buf, "----");
1554 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1555 membuffer_write_atom_name(buf, "mean");
1556 membuffer_write_int32(buf, 0);
1557 membuffer_write(buf, "com.apple.iTunes", 16);
1558 membuffer_write_int32(buf, 12 + strlen(name));
1559 membuffer_write_atom_name(buf, "name");
1560 membuffer_write_int32(buf, 0);
1561 membuffer_write(buf, name, strlen(name));
1562 membuffer_write_int32(buf,
1563 8 /*data atom header */ +
1564 8 /*flags + reserved */ + strlen(value));
1565 membuffer_write_atom_name(buf, "data");
1566 membuffer_write_int32(buf, 1); //flags
1567 membuffer_write_int32(buf, 0); //reserved
1568 membuffer_write(buf, value, strlen(value));
1571 static unsigned membuffer_error(const struct membuffer *buf)
1576 static void membuffer_free(struct membuffer *buf)
1582 static unsigned membuffer_get_size(const struct membuffer *buf)
1584 return buf->written;
1587 static void *membuffer_detach(struct membuffer *buf)
1593 ret = para_realloc(buf->data, buf->written);
1599 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1600 uint32_t * out_size)
1602 struct membuffer *buf = membuffer_create();
1604 char *mask = para_calloc(data->count);
1605 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1606 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1607 const char *genre_ptr = 0, *tempo_ptr = 0;
1609 for (metaptr = 0; metaptr < data->count; metaptr++) {
1610 struct mp4_tag *tag = &data->tags[metaptr];
1611 if (!strcasecmp(tag->item, "tracknumber")
1612 || !strcasecmp(tag->item, "track")) {
1613 if (tracknumber_ptr == 0)
1614 tracknumber_ptr = tag->value;
1616 } else if (!strcasecmp(tag->item, "totaltracks")) {
1617 if (totaltracks_ptr == 0)
1618 totaltracks_ptr = tag->value;
1620 } else if (!strcasecmp(tag->item, "discnumber")
1621 || !strcasecmp(tag->item, "disc")) {
1622 if (discnumber_ptr == 0)
1623 discnumber_ptr = tag->value;
1625 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1626 if (totaldiscs_ptr == 0)
1627 totaldiscs_ptr = tag->value;
1629 } else if (!strcasecmp(tag->item, "genre")) {
1631 genre_ptr = tag->value;
1633 } else if (!strcasecmp(tag->item, "tempo")) {
1635 tempo_ptr = tag->value;
1640 if (tracknumber_ptr)
1641 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1642 myatoi(totaltracks_ptr));
1644 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1645 myatoi(totaldiscs_ptr));
1647 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1650 uint32_t index = meta_genre_to_index(genre_ptr);
1652 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1654 membuffer_write_int16_tag(buf, "gnre", index);
1656 for (metaptr = 0; metaptr < data->count; metaptr++) {
1657 struct mp4_tag *tag;
1658 const char *std_meta_atom;
1662 tag = &data->tags[metaptr];
1663 std_meta_atom = find_standard_meta(tag->item);
1665 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1667 membuffer_write_custom_tag(buf, tag->item, tag->value);
1671 if (membuffer_error(buf)) {
1672 membuffer_free(buf);
1676 *out_size = membuffer_get_size(buf);
1677 *out_buffer = membuffer_detach(buf);
1678 membuffer_free(buf);
1683 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1686 membuffer_write_int32(buf, size + 8);
1687 membuffer_write_atom_name(buf, name);
1688 membuffer_write(buf, data, size);
1691 static void *membuffer_get_ptr(const struct membuffer *buf)
1696 static void membuffer_set_error(struct membuffer *buf)
1701 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1707 oldsize = membuffer_get_size(buf);
1708 if (membuffer_write(buf, 0, bytes) != bytes)
1711 bufptr = membuffer_get_ptr(buf);
1715 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1717 membuffer_set_error(buf);
1724 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1725 uint32_t * out_size)
1727 struct membuffer *buf;
1731 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1734 buf = membuffer_create();
1736 membuffer_write_int32(buf, 0);
1737 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1740 *out_size = membuffer_get_size(buf);
1741 *out_buffer = membuffer_detach(buf);
1742 membuffer_free(buf);
1746 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1747 uint32_t * out_size)
1749 struct membuffer *buf;
1753 if (!create_meta(data, &meta_buffer, &meta_size))
1756 buf = membuffer_create();
1758 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1762 *out_size = membuffer_get_size(buf);
1763 *out_buffer = membuffer_detach(buf);
1764 membuffer_free(buf);
1768 static uint32_t fix_byte_order_32(uint32_t src)
1770 return read_u32_be(&src);
1773 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1774 void **out_buffer, uint32_t * out_size)
1776 uint64_t total_base = f->moov_offset + 8;
1777 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1779 uint64_t udta_offset, meta_offset, ilst_offset;
1780 uint32_t udta_size, meta_size, ilst_size;
1782 uint32_t new_ilst_size;
1783 void *new_ilst_buffer;
1788 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1789 struct membuffer *buf;
1790 void *new_udta_buffer;
1791 uint32_t new_udta_size;
1792 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1795 buf = membuffer_create();
1796 set_position(f, total_base);
1797 membuffer_transfer_from_file(buf, f, total_size);
1799 membuffer_write_atom(buf, "udta", new_udta_size,
1802 free(new_udta_buffer);
1804 *out_size = membuffer_get_size(buf);
1805 *out_buffer = membuffer_detach(buf);
1806 membuffer_free(buf);
1809 udta_offset = get_position(f);
1810 udta_size = read_int32(f);
1811 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1812 struct membuffer *buf;
1813 void *new_meta_buffer;
1814 uint32_t new_meta_size;
1815 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1818 buf = membuffer_create();
1819 set_position(f, total_base);
1820 membuffer_transfer_from_file(buf, f,
1821 (uint32_t)(udta_offset - total_base));
1823 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1824 membuffer_write_atom_name(buf, "udta");
1825 membuffer_transfer_from_file(buf, f, udta_size);
1827 membuffer_write_atom(buf, "meta", new_meta_size,
1829 free(new_meta_buffer);
1831 *out_size = membuffer_get_size(buf);
1832 *out_buffer = membuffer_detach(buf);
1833 membuffer_free(buf);
1836 meta_offset = get_position(f);
1837 meta_size = read_int32(f);
1838 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1839 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1840 ilst_offset = get_position(f);
1841 ilst_size = read_int32(f);
1843 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1846 size_delta = new_ilst_size - (ilst_size - 8);
1848 *out_size = total_size + size_delta;
1849 *out_buffer = para_malloc(*out_size);
1850 p_out = (uint8_t *) * out_buffer;
1852 set_position(f, total_base);
1854 (uint32_t) (udta_offset - total_base));
1855 p_out += (uint32_t) (udta_offset - total_base);
1856 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1858 read_data(f, p_out, 4);
1861 (uint32_t) (meta_offset - udta_offset - 8));
1862 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1863 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1865 read_data(f, p_out, 4);
1868 (uint32_t) (ilst_offset - meta_offset - 8));
1869 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1870 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1872 read_data(f, p_out, 4);
1875 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1876 p_out += new_ilst_size;
1878 set_position(f, ilst_offset + ilst_size);
1879 read_data(f, p_out, (uint32_t) (total_size
1880 - (ilst_offset - total_base) - ilst_size));
1882 free(new_ilst_buffer);
1887 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1891 result = f->stream->write(f->stream->user_data, data, size);
1893 f->current_position += size;
1898 static int32_t write_int32(struct mp4 *f, uint32_t data)
1901 write_u32_be(temp, data);
1902 return write_data(f, temp, sizeof(temp));
1905 static int32_t truncate_stream(struct mp4 *f)
1907 return f->stream->truncate(f->stream->user_data);
1910 int32_t mp4_meta_update(struct mp4_callback *f, const struct mp4_metadata *data)
1912 void *new_moov_data;
1913 uint32_t new_moov_size;
1915 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1917 set_position(ff, 0);
1921 if (!modify_moov(ff, data, &new_moov_data, &new_moov_size)) {
1926 /* copy moov atom to end of the file */
1927 if (ff->last_atom != ATOM_MOOV) {
1928 char *free_data = "free";
1930 /* rename old moov to free */
1931 set_position(ff, ff->moov_offset + 4);
1932 write_data(ff, free_data, 4);
1934 set_position(ff, ff->file_size);
1935 write_int32(ff, new_moov_size + 8);
1936 write_data(ff, "moov", 4);
1937 write_data(ff, new_moov_data, new_moov_size);
1939 set_position(ff, ff->moov_offset);
1940 write_int32(ff, new_moov_size + 8);
1941 write_data(ff, "moov", 4);
1942 write_data(ff, new_moov_data, new_moov_size);
1945 truncate_stream(ff);
1951 /* find a metadata item by name */
1952 /* returns 0 if item found, 1 if no such item */
1953 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1958 for (i = 0; i < f->tags.count; i++) {
1959 if (!strcasecmp(f->tags.tags[i].item, item)) {
1960 *value = para_strdup(f->tags.tags[i].value);
1971 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1973 return meta_find_by_name(f, "artist", value);
1976 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1978 return meta_find_by_name(f, "title", value);
1981 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1983 return meta_find_by_name(f, "date", value);
1986 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1988 return meta_find_by_name(f, "album", value);
1991 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1993 return meta_find_by_name(f, "comment", value);