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;
57 /* incremental track index while reading the file */
61 struct mp4_track *track[MAX_TRACKS];
64 struct mp4_metadata tags;
67 int32_t mp4_total_tracks(const struct mp4 *f)
69 return f->total_tracks;
72 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
76 result = f->stream->read(f->stream->user_data, data, size);
79 f->stream->read_error++;
81 f->current_position += size;
86 static uint64_t read_int64(struct mp4 *f)
90 read_data(f, data, 8);
91 return read_u64_be(data);
94 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
95 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
97 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
101 /* atoms with subatoms */
109 ATOM_ILST = 8, /* iTunes Metadata list */
120 ATOM_COMPILATION = 19,
130 /* atoms without subatoms */
150 ATOM_META = 148, /* iTunes Metadata box */
151 ATOM_NAME = 149, /* iTunes Metadata name box */
152 ATOM_DATA = 150, /* iTunes Metadata data box */
159 ATOM_ALBUM_ARTIST = 157,
160 ATOM_CONTENTGROUP = 158,
162 ATOM_DESCRIPTION = 160,
165 ATOM_EPISODENAME = 163,
166 ATOM_SORTTITLE = 164,
167 ATOM_SORTALBUM = 165,
168 ATOM_SORTARTIST = 166,
169 ATOM_SORTALBUMARTIST = 167,
170 ATOM_SORTWRITER = 168,
179 #define ATOM_FREE ATOM_UNKNOWN
180 #define ATOM_SKIP ATOM_UNKNOWN
182 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
184 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
187 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
189 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
191 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
193 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
195 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
197 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
199 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
201 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
203 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
205 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
207 } else if (a == 't') {
208 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
210 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
212 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
214 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
216 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
218 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
220 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
222 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
223 return ATOM_EPISODENAME;
224 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
226 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
228 } else if (a == 's') {
229 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
231 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
233 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
235 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
237 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
239 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
241 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
243 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
245 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
247 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
249 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
251 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
252 return ATOM_SORTTITLE;
253 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
254 return ATOM_SORTALBUM;
255 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
256 return ATOM_SORTARTIST;
257 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
258 return ATOM_SORTALBUMARTIST;
259 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
260 return ATOM_SORTWRITER;
261 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
262 return ATOM_SORTSHOW;
263 } else if (a == COPYRIGHT_SYMBOL) {
264 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
266 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
268 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
270 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
272 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
274 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
276 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
278 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
280 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
281 return ATOM_CONTENTGROUP;
282 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
286 if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
288 else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
290 else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
292 else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
294 else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
296 else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
298 else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
300 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
302 else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
304 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
306 else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
308 else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
310 else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
312 else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
313 return ATOM_COMPILATION;
314 else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
316 else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
318 else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
320 else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
322 else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
324 else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
326 else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
328 else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
329 return ATOM_ALBUM_ARTIST;
330 else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
331 return ATOM_DESCRIPTION;
332 else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
338 /* read atom header, return atom size, atom size is with header included */
339 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
340 uint8_t * header_size)
344 int8_t atom_header[8];
346 ret = read_data(f, atom_header, 8);
350 size = read_u32_be(atom_header);
353 /* check for 64 bit atom size */
356 size = read_int64(f);
358 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
359 atom_header[6], atom_header[7]);
363 static int64_t get_position(const struct mp4 *f)
365 return f->current_position;
368 static int need_parse_when_meta_only(uint8_t atom_type)
389 static int32_t set_position(struct mp4 *f, int64_t position)
391 f->stream->seek(f->stream->user_data, position);
392 f->current_position = position;
397 static void track_add(struct mp4 *f)
401 if (f->total_tracks > MAX_TRACKS) {
406 f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
409 static uint8_t read_char(struct mp4 *f)
412 read_data(f, &output, 1);
416 static uint32_t read_int24(struct mp4 *f)
420 read_data(f, data, 3);
421 return read_u24_be(data);
424 static uint32_t read_int32(struct mp4 *f)
428 read_data(f, data, 4);
429 return read_u32_be(data);
432 static int32_t read_stsz(struct mp4 *f)
437 if (f->total_tracks == 0)
439 t = f->track[f->total_tracks - 1];
440 read_char(f); /* version */
441 read_int24(f); /* flags */
442 t->stsz_sample_size = read_int32(f);
443 t->stsz_sample_count = read_int32(f);
444 if (t->stsz_sample_size != 0)
446 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
447 for (i = 0; i < t->stsz_sample_count && !f->stream->read_error; i++)
448 t->stsz_table[i] = read_int32(f);
452 static int32_t read_stts(struct mp4 *f)
458 if (f->total_tracks == 0)
460 t = f->track[f->total_tracks - 1];
461 if (t->stts_entry_count)
463 read_char(f); /* version */
464 read_int24(f); /* flags */
465 t->stts_entry_count = read_int32(f);
467 t->stts_sample_count = para_malloc(t->stts_entry_count
469 t->stts_sample_delta = para_malloc(t->stts_entry_count
472 for (i = 0; i < t->stts_entry_count && !f->stream->read_error; i++) {
473 t->stts_sample_count[i] = read_int32(f);
474 t->stts_sample_delta[i] = read_int32(f);
479 static int32_t read_stsc(struct mp4 *f)
484 if (f->total_tracks == 0)
486 t = f->track[f->total_tracks - 1];
488 read_char(f); /* version */
489 read_int24(f); /* flags */
490 t->stsc_entry_count = read_int32(f);
491 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
492 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
494 t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
498 for (i = 0; i < t->stsc_entry_count && !f->stream->read_error; i++) {
499 t->stsc_first_chunk[i] = read_int32(f);
500 t->stsc_samples_per_chunk[i] = read_int32(f);
501 t->stsc_sample_desc_index[i] = read_int32(f);
506 static int32_t read_stco(struct mp4 *f)
511 if (f->total_tracks == 0)
513 t = f->track[f->total_tracks - 1];
515 read_char(f); /* version */
516 read_int24(f); /* flags */
517 t->stco_entry_count = read_int32(f);
518 t->stco_chunk_offset = para_malloc(t->stco_entry_count
521 for (i = 0; i < t->stco_entry_count && !f->stream->read_error; i++)
522 t->stco_chunk_offset[i] = read_int32(f);
526 static uint16_t read_int16(struct mp4 *f)
530 read_data(f, data, 2);
531 return read_u16_be(data);
534 static int32_t read_mp4a(struct mp4 *f)
537 uint8_t atom_type = 0;
538 uint8_t header_size = 0;
541 if (f->total_tracks == 0)
543 t = f->track[f->total_tracks - 1];
545 for (i = 0; i < 6; i++) {
546 read_char(f); /* reserved */
548 /* data_reference_index */ read_int16(f);
550 read_int32(f); /* reserved */
551 read_int32(f); /* reserved */
553 t->channelCount = read_int16(f);
559 t->sampleRate = read_int16(f);
563 atom_read_header(f, &atom_type, &header_size);
567 static int32_t read_stsd(struct mp4 *f)
569 int32_t i, entry_count;
570 uint8_t header_size = 0;
574 if (f->total_tracks == 0)
576 t = f->track[f->total_tracks - 1];
578 read_char(f); /* version */
579 read_int24(f); /* flags */
581 entry_count = read_int32(f);
584 for (i = 0; i < entry_count && !f->stream->read_error; i++) {
585 uint64_t skip = get_position(f);
587 uint8_t atom_type = 0;
588 size = atom_read_header(f, &atom_type, &header_size);
590 t->is_audio = atom_type == ATOM_MP4A;
593 set_position(f, skip);
599 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
600 const char *value, int32_t len)
602 if (!item || (item && !*item) || !value)
604 tags->tags = para_realloc(tags->tags,
605 (tags->count + 1) * sizeof(struct mp4_tag));
606 tags->tags[tags->count].item = para_strdup(item);
607 tags->tags[tags->count].len = len;
609 tags->tags[tags->count].value = para_malloc(len + 1);
610 memcpy(tags->tags[tags->count].value, value, len);
611 tags->tags[tags->count].value[len] = 0;
613 tags->tags[tags->count].value = para_strdup(value);
619 static const char *ID3v1GenreList[] = {
620 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
621 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
622 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
623 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
624 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
625 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
626 "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
627 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
628 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
629 "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
630 "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
631 "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
632 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
633 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
634 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
635 "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
636 "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
637 "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
638 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
639 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
640 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
641 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
642 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
643 "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
644 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
645 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
646 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
647 "Anime", "JPop", "SynthPop",
650 static const char *meta_index_to_genre(uint32_t idx)
652 if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
653 return ID3v1GenreList[idx - 1];
659 static char *read_string(struct mp4 *f, uint32_t length)
661 char *str = para_malloc(length + 1);
662 if ((uint32_t)read_data(f, str, length) != length) {
670 static int32_t set_metadata_name(uint8_t atom_type, char **name)
672 static char *tag_names[] = {
673 "unknown", "title", "artist", "writer", "album",
674 "date", "tool", "comment", "genre", "track",
675 "disc", "compilation", "genre", "tempo", "cover",
676 "album_artist", "contentgroup", "lyrics", "description",
677 "network", "show", "episodename",
678 "sorttitle", "sortalbum", "sortartist", "sortalbumartist",
679 "sortwriter", "sortshow",
680 "season", "episode", "podcast"
715 case ATOM_COMPILATION:
727 case ATOM_ALBUM_ARTIST:
730 case ATOM_CONTENTGROUP:
736 case ATOM_DESCRIPTION:
745 case ATOM_EPISODENAME:
754 case ATOM_SORTARTIST:
757 case ATOM_SORTALBUMARTIST:
760 case ATOM_SORTWRITER:
780 *name = para_strdup(tag_names[tag_idx]);
784 static uint32_t min_body_size(uint8_t atom_type)
791 return sizeof (char) /* version */
792 + sizeof(uint8_t) * 3 /* flags */
793 + sizeof(uint32_t) /* reserved */
794 + sizeof(uint16_t) /* leading uint16_t */
795 + sizeof(uint16_t) /* track */
796 + sizeof(uint16_t); /* totaltracks */
798 return sizeof (char) /* version */
799 + sizeof(uint8_t) * 3 /* flags */
800 + sizeof(uint32_t) /* reserved */
801 + sizeof(uint16_t) /* disc */
802 + sizeof(uint16_t); /* totaldiscs */
803 default: assert(false);
807 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
810 uint8_t header_size = 0;
811 uint64_t subsize, sumsize;
820 sumsize < size && !f->stream->read_error; /* CVE-2017-9222 */
821 set_position(f, destpos), sumsize += subsize
823 subsize = atom_read_header(f, &atom_type, &header_size);
824 destpos = get_position(f) + subsize - header_size;
827 if (atom_type == ATOM_NAME) {
828 read_char(f); /* version */
829 read_int24(f); /* flags */
831 name = read_string(f, subsize - (header_size + 4));
834 if (atom_type != ATOM_DATA)
836 read_char(f); /* version */
837 read_int24(f); /* flags */
838 read_int32(f); /* reserved */
840 /* some need special attention */
841 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
843 if (subsize - header_size < min_body_size(parent))
846 if (parent == ATOM_TEMPO) {
848 sprintf(temp, "%.5u BPM", val);
849 tag_add_field(&(f-> tags), "tempo",
852 const char *tmp = meta_index_to_genre(val);
854 tag_add_field (&(f->tags),
858 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
859 uint16_t index, total;
861 if (subsize - header_size < min_body_size(parent))
864 index = read_int16(f);
865 total = read_int16(f);
866 if (parent == ATOM_TRACK)
868 sprintf(temp, "%d", index);
869 tag_add_field(&(f->tags), parent == ATOM_TRACK?
870 "track" : "disc", temp, -1);
872 sprintf(temp, "%d", total);
873 tag_add_field(& (f-> tags),
874 parent == ATOM_TRACK?
875 "totaltracks" : "totaldiscs", temp, -1);
880 data = read_string(f, subsize - (header_size + 8));
881 len = subsize - (header_size + 8);
887 set_metadata_name(parent , &name);
889 tag_add_field(&(f->tags), name, data, len);
898 static int32_t read_mdhd(struct mp4 *f)
904 if (f->total_tracks == 0)
906 t = f->track[f->total_tracks - 1];
908 version = read_int32(f);
910 read_int64(f); //creation-time
911 read_int64(f); //modification-time
912 t->timeScale = read_int32(f); //timescale
913 t->duration = read_int64(f); //duration
914 } else { //version == 0
917 read_int32(f); //creation-time
918 read_int32(f); //modification-time
919 t->timeScale = read_int32(f); //timescale
920 temp = read_int32(f);
921 t->duration = (temp == (uint32_t) (-1))?
922 (uint64_t) (-1) : (uint64_t) (temp);
929 static int32_t parse_metadata(struct mp4 *f, int32_t size)
931 uint64_t subsize, sumsize = 0;
933 uint8_t header_size = 0;
935 while (sumsize < size) {
936 subsize = atom_read_header(f, &atom_type, &header_size);
939 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
946 static int32_t read_meta(struct mp4 *f, uint64_t size)
948 uint64_t subsize, sumsize = 0;
950 uint8_t header_size = 0;
952 read_char(f); /* version */
953 read_int24(f); /* flags */
955 while (sumsize < (size - (header_size + 4))) {
956 subsize = atom_read_header(f, &atom_type, &header_size);
957 if (subsize <= header_size + 4)
959 if (atom_type == ATOM_ILST) {
960 parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
962 set_position(f, get_position(f) + subsize - header_size);
970 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
972 uint64_t dest_position = get_position(f) + size - 8;
973 if (atom_type == ATOM_STSZ) {
974 /* sample size box */
976 } else if (atom_type == ATOM_STTS) {
977 /* time to sample box */
979 } else if (atom_type == ATOM_STSC) {
980 /* sample to chunk box */
982 } else if (atom_type == ATOM_STCO) {
983 /* chunk offset box */
985 } else if (atom_type == ATOM_STSD) {
986 /* sample description box */
988 } else if (atom_type == ATOM_MDHD) {
991 } else if (atom_type == ATOM_META) {
992 /* iTunes Metadata box */
996 set_position(f, dest_position);
1000 /* parse atoms that are sub atoms of other atoms */
1001 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
1004 uint8_t atom_type = 0;
1005 uint64_t counted_size = 0;
1006 uint8_t header_size = 0;
1008 while (counted_size < total_size) {
1009 size = atom_read_header(f, &atom_type, &header_size);
1010 counted_size += size;
1012 /* check for end of file */
1016 /* we're starting to read a new track, update index,
1017 * so that all data and tables get written in the right place
1019 if (atom_type == ATOM_TRAK)
1021 /* parse subatoms */
1022 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1023 set_position(f, get_position(f) + size - header_size);
1024 } else if (atom_type < SUBATOMIC) {
1025 parse_sub_atoms(f, size - header_size, meta_only);
1027 atom_read(f, (uint32_t) size, atom_type);
1034 /* parse root atoms */
1035 static int32_t parse_atoms(struct mp4 *f, int meta_only)
1038 uint8_t atom_type = 0;
1039 uint8_t header_size = 0;
1042 f->stream->read_error = 0;
1045 atom_read_header(f, &atom_type, &header_size)) != 0) {
1046 f->file_size += size;
1047 f->last_atom = atom_type;
1049 if (atom_type == ATOM_MOOV && size > header_size) {
1050 f->moov_offset = get_position(f) - header_size;
1051 f->moov_size = size;
1054 /* parse subatoms */
1055 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1056 set_position(f, get_position(f) + size - header_size);
1057 } else if (atom_type < SUBATOMIC) {
1058 parse_sub_atoms(f, size - header_size, meta_only);
1060 /* skip this atom */
1061 set_position(f, get_position(f) + size - header_size);
1068 struct mp4 *mp4_open_read(struct mp4_callback *f)
1070 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1084 static int32_t tag_delete(struct mp4_metadata *tags)
1088 for (i = 0; i < tags->count; i++) {
1089 free(tags->tags[i].item);
1090 free(tags->tags[i].value);
1099 void mp4_close(struct mp4 *ff)
1103 for (i = 0; i < ff->total_tracks; i++) {
1105 free(ff->track[i]->stsz_table);
1106 free(ff->track[i]->stts_sample_count);
1107 free(ff->track[i]->stts_sample_delta);
1108 free(ff->track[i]->stsc_first_chunk);
1109 free(ff->track[i]->stsc_samples_per_chunk);
1110 free(ff->track[i]->stsc_sample_desc_index);
1111 free(ff->track[i]->stco_chunk_offset);
1116 tag_delete(&(ff->tags));
1120 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1121 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1123 int32_t total_entries = 0;
1124 int32_t chunk2entry;
1125 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1129 if (f->track[track] == NULL) {
1133 total_entries = f->track[track]->stsc_entry_count;
1140 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1141 *chunk = chunk2 - chunk1;
1142 range_samples = *chunk * chunk1samples;
1144 if (sample < total + range_samples)
1147 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1150 if (chunk2entry < total_entries) {
1152 total += range_samples;
1154 } while (chunk2entry < total_entries);
1157 *chunk = (sample - total) / chunk1samples + chunk1;
1161 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1166 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1169 const struct mp4_track *p_track = f->track[track];
1171 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1172 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1174 } else if (p_track->stco_entry_count) {
1175 return p_track->stco_chunk_offset[chunk - 1];
1183 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1184 int32_t chunk_sample, int32_t sample)
1187 const struct mp4_track *p_track = f->track[track];
1189 if (p_track->stsz_sample_size) {
1190 return (sample - chunk_sample) * p_track->stsz_sample_size;
1192 if (sample >= p_track->stsz_sample_count)
1195 for (i = chunk_sample, total = 0; i < sample; i++) {
1196 total += p_track->stsz_table[i];
1203 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1206 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1208 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1210 chunk_offset1 = chunk_to_offset(f, track, chunk);
1211 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1212 track, chunk_sample, sample);
1213 return chunk_offset2;
1217 * Return the number of milliseconds of the given track.
1219 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1220 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1222 * The function returns zero if the audio file is of zero length or contains a
1223 * corrupt track header.
1225 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1227 const struct mp4_track *t = f->track[track];
1229 if (t->timeScale == 0)
1231 return t->duration * 1000 / t->timeScale;
1235 * Check whether the given track number corresponds to an audio track.
1237 * \param f See \ref mp4_get_duration().
1238 * \param track See \ref mp4_get_duration().
1240 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1241 * those the function returns false.
1243 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1245 return f->track[track]->is_audio;
1248 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1250 int32_t offset = sample_to_offset(f, track, sample);
1251 set_position(f, offset);
1254 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1256 const struct mp4_track *t = f->track[track];
1258 if (t->stsz_sample_size != 0)
1259 return t->stsz_sample_size;
1260 return t->stsz_table[sample];
1263 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1265 return f->track[track]->sampleRate;
1268 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1270 return f->track[track]->channelCount;
1273 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1278 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1279 total += f->track[track]->stts_sample_count[i];
1284 struct mp4 *mp4_open_meta(struct mp4_callback *f)
1286 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1300 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1302 return f->tags.count;
1305 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1306 char **item, char **value)
1308 if (index >= f->tags.count) {
1313 *item = para_strdup(f->tags.tags[index].item);
1314 *value = para_strdup(f->tags.tags[index].value);
1319 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1322 uint32_t remaining = size;
1323 uint64_t atom_offset = base;
1328 set_position(f, atom_offset);
1332 atom_size = read_int32(f);
1333 if (atom_size > remaining || atom_size < 8)
1335 read_data(f, atom_name, 4);
1337 if (!memcmp(atom_name, name, 4)) {
1338 set_position(f, atom_offset);
1342 remaining -= atom_size;
1343 atom_offset += atom_size;
1348 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1349 const char *name, uint32_t extraheaders, const char *name_inside)
1351 uint64_t first_base = (uint64_t) (-1);
1352 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1354 uint64_t mybase = get_position(f);
1355 uint32_t mysize = read_int32(f);
1357 if (first_base == (uint64_t) (-1))
1358 first_base = mybase;
1360 if (mysize < 8 + extraheaders)
1363 if (find_atom (f, mybase + (8 + extraheaders),
1364 mysize - (8 + extraheaders), name_inside)) {
1365 set_position(f, mybase);
1369 if (size <= mysize) {
1376 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1378 set_position(f, first_base);
1391 static struct membuffer *membuffer_create(void)
1393 const unsigned initial_size = 256;
1395 struct membuffer *buf = para_malloc(sizeof(*buf));
1396 buf->data = para_malloc(initial_size);
1398 buf->allocated = initial_size;
1399 buf->error = buf->data == 0 ? 1 : 0;
1404 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1406 unsigned dest_size = buf->written + bytes;
1410 if (dest_size > buf->allocated) {
1412 buf->allocated <<= 1;
1413 } while (dest_size > buf->allocated);
1414 buf->data = para_realloc(buf->data, buf->allocated);
1418 memcpy((char *) buf->data + buf->written, ptr, bytes);
1419 buf->written += bytes;
1423 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1425 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1428 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1432 write_u16_be(temp, data);
1433 return membuffer_write(buf, temp, 2);
1436 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1439 write_u32_be(temp, data);
1440 return membuffer_write(buf, temp, 4);
1443 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1444 uint32_t index, uint32_t total)
1446 membuffer_write_int32(buf,
1447 8 /*atom header */ + 8 /*data atom header */ +
1448 8 /*flags + reserved */ + 8 /*actual data */ );
1449 membuffer_write_atom_name(buf, name);
1450 membuffer_write_int32(buf,
1451 8 /*data atom header */ +
1452 8 /*flags + reserved */ + 8 /*actual data */ );
1453 membuffer_write_atom_name(buf, "data");
1454 membuffer_write_int32(buf, 0); //flags
1455 membuffer_write_int32(buf, 0); //reserved
1456 membuffer_write_int16(buf, 0);
1457 membuffer_write_int16(buf, (uint16_t) index); //track number
1458 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1459 membuffer_write_int16(buf, 0);
1462 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1465 membuffer_write_int32(buf,
1466 8 /*atom header */ + 8 /*data atom header */ +
1467 8 /*flags + reserved */ + 2 /*actual data */ );
1468 membuffer_write_atom_name(buf, name);
1469 membuffer_write_int32(buf,
1470 8 /*data atom header */ +
1471 8 /*flags + reserved */ + 2 /*actual data */ );
1472 membuffer_write_atom_name(buf, "data");
1473 membuffer_write_int32(buf, 0); //flags
1474 membuffer_write_int32(buf, 0); //reserved
1475 membuffer_write_int16(buf, value); //value
1478 static uint32_t myatoi(const char *param)
1480 return param ? atoi(param) : 0;
1483 static uint32_t meta_genre_to_index(const char *genrestr)
1486 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1487 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1493 struct stdmeta_entry {
1498 struct stdmeta_entry stdmetas[] = {
1499 {"\xA9" "nam", "title"},
1500 {"\xA9" "ART", "artist"},
1501 {"\xA9" "wrt", "writer"},
1502 {"\xA9" "alb", "album"},
1503 {"\xA9" "day", "date"},
1504 {"\xA9" "too", "tool"},
1505 {"\xA9" "cmt", "comment"},
1506 {"cpil", "compilation"},
1508 {"aART", "album_artist"},
1511 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1514 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1515 if (!strcasecmp(name, stdmetas[n].name))
1516 return stdmetas[n].atom;
1521 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1526 /* special check for compilation flag */
1527 if (strcmp(name, "cpil") == 0) {
1531 membuffer_write_int32(buf,
1532 8 /*atom header */ + 8 /*data atom header */ +
1533 8 /*flags + reserved */ + strlen(value));
1534 membuffer_write_atom_name(buf, name);
1535 membuffer_write_int32(buf,
1536 8 /*data atom header */ +
1537 8 /*flags + reserved */ + strlen(value));
1538 membuffer_write_atom_name(buf, "data");
1539 membuffer_write_int32(buf, flags); //flags
1540 membuffer_write_int32(buf, 0); //reserved
1541 membuffer_write(buf, value, strlen(value));
1544 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1547 membuffer_write_int32(buf,
1548 8 /*atom header */ +
1549 0x1C /*weirdo itunes atom */ +
1550 12 /*name atom header */ + strlen(name) +
1551 16 /*data atom header + flags */ + strlen(value));
1552 membuffer_write_atom_name(buf, "----");
1553 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1554 membuffer_write_atom_name(buf, "mean");
1555 membuffer_write_int32(buf, 0);
1556 membuffer_write(buf, "com.apple.iTunes", 16);
1557 membuffer_write_int32(buf, 12 + strlen(name));
1558 membuffer_write_atom_name(buf, "name");
1559 membuffer_write_int32(buf, 0);
1560 membuffer_write(buf, name, strlen(name));
1561 membuffer_write_int32(buf,
1562 8 /*data atom header */ +
1563 8 /*flags + reserved */ + strlen(value));
1564 membuffer_write_atom_name(buf, "data");
1565 membuffer_write_int32(buf, 1); //flags
1566 membuffer_write_int32(buf, 0); //reserved
1567 membuffer_write(buf, value, strlen(value));
1570 static unsigned membuffer_error(const struct membuffer *buf)
1575 static void membuffer_free(struct membuffer *buf)
1581 static unsigned membuffer_get_size(const struct membuffer *buf)
1583 return buf->written;
1586 static void *membuffer_detach(struct membuffer *buf)
1592 ret = para_realloc(buf->data, buf->written);
1598 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1599 uint32_t * out_size)
1601 struct membuffer *buf = membuffer_create();
1603 char *mask = para_calloc(data->count);
1604 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1605 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1606 const char *genre_ptr = 0, *tempo_ptr = 0;
1608 for (metaptr = 0; metaptr < data->count; metaptr++) {
1609 struct mp4_tag *tag = &data->tags[metaptr];
1610 if (!strcasecmp(tag->item, "tracknumber")
1611 || !strcasecmp(tag->item, "track")) {
1612 if (tracknumber_ptr == 0)
1613 tracknumber_ptr = tag->value;
1615 } else if (!strcasecmp(tag->item, "totaltracks")) {
1616 if (totaltracks_ptr == 0)
1617 totaltracks_ptr = tag->value;
1619 } else if (!strcasecmp(tag->item, "discnumber")
1620 || !strcasecmp(tag->item, "disc")) {
1621 if (discnumber_ptr == 0)
1622 discnumber_ptr = tag->value;
1624 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1625 if (totaldiscs_ptr == 0)
1626 totaldiscs_ptr = tag->value;
1628 } else if (!strcasecmp(tag->item, "genre")) {
1630 genre_ptr = tag->value;
1632 } else if (!strcasecmp(tag->item, "tempo")) {
1634 tempo_ptr = tag->value;
1639 if (tracknumber_ptr)
1640 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1641 myatoi(totaltracks_ptr));
1643 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1644 myatoi(totaldiscs_ptr));
1646 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1649 uint32_t index = meta_genre_to_index(genre_ptr);
1651 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1653 membuffer_write_int16_tag(buf, "gnre", index);
1655 for (metaptr = 0; metaptr < data->count; metaptr++) {
1656 struct mp4_tag *tag;
1657 const char *std_meta_atom;
1661 tag = &data->tags[metaptr];
1662 std_meta_atom = find_standard_meta(tag->item);
1664 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1666 membuffer_write_custom_tag(buf, tag->item, tag->value);
1670 if (membuffer_error(buf)) {
1671 membuffer_free(buf);
1675 *out_size = membuffer_get_size(buf);
1676 *out_buffer = membuffer_detach(buf);
1677 membuffer_free(buf);
1682 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1685 membuffer_write_int32(buf, size + 8);
1686 membuffer_write_atom_name(buf, name);
1687 membuffer_write(buf, data, size);
1690 static void *membuffer_get_ptr(const struct membuffer *buf)
1695 static void membuffer_set_error(struct membuffer *buf)
1700 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1706 oldsize = membuffer_get_size(buf);
1707 if (membuffer_write(buf, 0, bytes) != bytes)
1710 bufptr = membuffer_get_ptr(buf);
1714 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1716 membuffer_set_error(buf);
1723 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1724 uint32_t * out_size)
1726 struct membuffer *buf;
1730 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1733 buf = membuffer_create();
1735 membuffer_write_int32(buf, 0);
1736 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1739 *out_size = membuffer_get_size(buf);
1740 *out_buffer = membuffer_detach(buf);
1741 membuffer_free(buf);
1745 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1746 uint32_t * out_size)
1748 struct membuffer *buf;
1752 if (!create_meta(data, &meta_buffer, &meta_size))
1755 buf = membuffer_create();
1757 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1761 *out_size = membuffer_get_size(buf);
1762 *out_buffer = membuffer_detach(buf);
1763 membuffer_free(buf);
1767 static uint32_t fix_byte_order_32(uint32_t src)
1769 return read_u32_be(&src);
1772 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1773 void **out_buffer, uint32_t * out_size)
1775 uint64_t total_base = f->moov_offset + 8;
1776 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1778 uint64_t udta_offset, meta_offset, ilst_offset;
1779 uint32_t udta_size, meta_size, ilst_size;
1781 uint32_t new_ilst_size;
1782 void *new_ilst_buffer;
1787 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1788 struct membuffer *buf;
1789 void *new_udta_buffer;
1790 uint32_t new_udta_size;
1791 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1794 buf = membuffer_create();
1795 set_position(f, total_base);
1796 membuffer_transfer_from_file(buf, f, total_size);
1798 membuffer_write_atom(buf, "udta", new_udta_size,
1801 free(new_udta_buffer);
1803 *out_size = membuffer_get_size(buf);
1804 *out_buffer = membuffer_detach(buf);
1805 membuffer_free(buf);
1808 udta_offset = get_position(f);
1809 udta_size = read_int32(f);
1810 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1811 struct membuffer *buf;
1812 void *new_meta_buffer;
1813 uint32_t new_meta_size;
1814 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1817 buf = membuffer_create();
1818 set_position(f, total_base);
1819 membuffer_transfer_from_file(buf, f,
1820 (uint32_t)(udta_offset - total_base));
1822 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1823 membuffer_write_atom_name(buf, "udta");
1824 membuffer_transfer_from_file(buf, f, udta_size);
1826 membuffer_write_atom(buf, "meta", new_meta_size,
1828 free(new_meta_buffer);
1830 *out_size = membuffer_get_size(buf);
1831 *out_buffer = membuffer_detach(buf);
1832 membuffer_free(buf);
1835 meta_offset = get_position(f);
1836 meta_size = read_int32(f);
1837 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1838 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1839 ilst_offset = get_position(f);
1840 ilst_size = read_int32(f);
1842 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1845 size_delta = new_ilst_size - (ilst_size - 8);
1847 *out_size = total_size + size_delta;
1848 *out_buffer = para_malloc(*out_size);
1849 p_out = (uint8_t *) * out_buffer;
1851 set_position(f, total_base);
1853 (uint32_t) (udta_offset - total_base));
1854 p_out += (uint32_t) (udta_offset - total_base);
1855 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1857 read_data(f, p_out, 4);
1860 (uint32_t) (meta_offset - udta_offset - 8));
1861 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1862 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1864 read_data(f, p_out, 4);
1867 (uint32_t) (ilst_offset - meta_offset - 8));
1868 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1869 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1871 read_data(f, p_out, 4);
1874 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1875 p_out += new_ilst_size;
1877 set_position(f, ilst_offset + ilst_size);
1878 read_data(f, p_out, (uint32_t) (total_size
1879 - (ilst_offset - total_base) - ilst_size));
1881 free(new_ilst_buffer);
1886 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1890 result = f->stream->write(f->stream->user_data, data, size);
1892 f->current_position += size;
1897 static int32_t write_int32(struct mp4 *f, uint32_t data)
1900 write_u32_be(temp, data);
1901 return write_data(f, temp, sizeof(temp));
1904 int32_t mp4_meta_update(struct mp4_callback *f, const struct mp4_metadata *data)
1906 void *new_moov_data;
1907 uint32_t new_moov_size;
1909 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1911 set_position(ff, 0);
1915 if (!modify_moov(ff, data, &new_moov_data, &new_moov_size)) {
1920 /* copy moov atom to end of the file */
1921 if (ff->last_atom != ATOM_MOOV) {
1922 char *free_data = "free";
1924 /* rename old moov to free */
1925 set_position(ff, ff->moov_offset + 4);
1926 write_data(ff, free_data, 4);
1928 set_position(ff, ff->file_size);
1929 write_int32(ff, new_moov_size + 8);
1930 write_data(ff, "moov", 4);
1931 write_data(ff, new_moov_data, new_moov_size);
1933 set_position(ff, ff->moov_offset);
1934 write_int32(ff, new_moov_size + 8);
1935 write_data(ff, "moov", 4);
1936 write_data(ff, new_moov_data, new_moov_size);
1938 f->truncate(f->user_data);
1943 /* find a metadata item by name */
1944 /* returns 0 if item found, 1 if no such item */
1945 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1950 for (i = 0; i < f->tags.count; i++) {
1951 if (!strcasecmp(f->tags.tags[i].item, item)) {
1952 *value = para_strdup(f->tags.tags[i].value);
1963 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1965 return meta_find_by_name(f, "artist", value);
1968 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1970 return meta_find_by_name(f, "title", value);
1973 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1975 return meta_find_by_name(f, "date", value);
1978 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1980 return meta_find_by_name(f, "album", value);
1983 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1985 return meta_find_by_name(f, "comment", value);