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 stsd_entry_count;
25 int32_t stsz_sample_size;
26 int32_t stsz_sample_count;
30 int32_t stts_entry_count;
31 int32_t *stts_sample_count;
32 int32_t *stts_sample_delta;
35 int32_t stsc_entry_count;
36 int32_t *stsc_first_chunk;
37 int32_t *stsc_samples_per_chunk;
38 int32_t *stsc_sample_desc_index;
41 int32_t stco_entry_count;
42 int32_t *stco_chunk_offset;
48 #define MAX_TRACKS 1024
51 /* stream to read from */
52 struct mp4_callback *stream;
53 int64_t current_position;
61 /* incremental track index while reading the file */
65 struct mp4_track *track[MAX_TRACKS];
68 struct mp4_metadata tags;
71 int32_t mp4_total_tracks(const struct mp4 *f)
73 return f->total_tracks;
76 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
80 result = f->stream->read(f->stream->user_data, data, size);
83 f->stream->read_error++;
85 f->current_position += size;
90 static uint64_t read_int64(struct mp4 *f)
94 read_data(f, data, 8);
95 return read_u64_be(data);
98 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
99 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
101 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
105 /* atoms with subatoms */
113 ATOM_ILST = 8, /* iTunes Metadata list */
124 ATOM_COMPILATION = 19,
134 /* atoms without subatoms */
154 ATOM_META = 148, /* iTunes Metadata box */
155 ATOM_NAME = 149, /* iTunes Metadata name box */
156 ATOM_DATA = 150, /* iTunes Metadata data box */
163 ATOM_ALBUM_ARTIST = 157,
164 ATOM_CONTENTGROUP = 158,
166 ATOM_DESCRIPTION = 160,
169 ATOM_EPISODENAME = 163,
170 ATOM_SORTTITLE = 164,
171 ATOM_SORTALBUM = 165,
172 ATOM_SORTARTIST = 166,
173 ATOM_SORTALBUMARTIST = 167,
174 ATOM_SORTWRITER = 168,
183 #define ATOM_FREE ATOM_UNKNOWN
184 #define ATOM_SKIP ATOM_UNKNOWN
186 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
188 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
191 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
193 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
195 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
197 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
199 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
201 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
203 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
205 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
207 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
209 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
211 } else if (a == 't') {
212 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
214 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
216 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
218 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
220 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
222 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
224 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
226 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
227 return ATOM_EPISODENAME;
228 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
230 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
232 } else if (a == 's') {
233 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
235 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
237 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
239 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
241 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
243 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
245 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
247 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
249 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
251 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
253 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
255 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
256 return ATOM_SORTTITLE;
257 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
258 return ATOM_SORTALBUM;
259 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
260 return ATOM_SORTARTIST;
261 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
262 return ATOM_SORTALBUMARTIST;
263 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
264 return ATOM_SORTWRITER;
265 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
266 return ATOM_SORTSHOW;
267 } else if (a == COPYRIGHT_SYMBOL) {
268 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
270 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
272 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
274 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
276 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
278 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
280 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
282 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
284 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
285 return ATOM_CONTENTGROUP;
286 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
290 if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
292 else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
294 else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
296 else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
298 else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
300 else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
302 else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
304 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
306 else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
308 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
310 else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
312 else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
314 else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
316 else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
317 return ATOM_COMPILATION;
318 else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
320 else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
322 else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
324 else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
326 else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
328 else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
330 else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
332 else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
333 return ATOM_ALBUM_ARTIST;
334 else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
335 return ATOM_DESCRIPTION;
336 else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
342 /* read atom header, return atom size, atom size is with header included */
343 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
344 uint8_t * header_size)
348 int8_t atom_header[8];
350 ret = read_data(f, atom_header, 8);
354 size = read_u32_be(atom_header);
357 /* check for 64 bit atom size */
360 size = read_int64(f);
362 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
363 atom_header[6], atom_header[7]);
367 static int64_t get_position(const struct mp4 *f)
369 return f->current_position;
372 static int need_parse_when_meta_only(uint8_t atom_type)
393 static int32_t set_position(struct mp4 *f, int64_t position)
395 f->stream->seek(f->stream->user_data, position);
396 f->current_position = position;
401 static void track_add(struct mp4 *f)
405 if (f->total_tracks > MAX_TRACKS) {
410 f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
413 static uint8_t read_char(struct mp4 *f)
416 read_data(f, &output, 1);
420 static uint32_t read_int24(struct mp4 *f)
424 read_data(f, data, 3);
425 return read_u24_be(data);
428 static uint32_t read_int32(struct mp4 *f)
432 read_data(f, data, 4);
433 return read_u32_be(data);
436 static int32_t read_stsz(struct mp4 *f)
441 if (f->total_tracks == 0)
443 t = f->track[f->total_tracks - 1];
444 read_char(f); /* version */
445 read_int24(f); /* flags */
446 t->stsz_sample_size = read_int32(f);
447 t->stsz_sample_count = read_int32(f);
448 if (t->stsz_sample_size != 0)
450 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
451 for (i = 0; i < t->stsz_sample_count && !f->stream->read_error; i++)
452 t->stsz_table[i] = read_int32(f);
456 static int32_t read_stts(struct mp4 *f)
462 if (f->total_tracks == 0)
464 t = f->track[f->total_tracks - 1];
465 if (t->stts_entry_count)
467 read_char(f); /* version */
468 read_int24(f); /* flags */
469 t->stts_entry_count = read_int32(f);
471 t->stts_sample_count = para_malloc(t->stts_entry_count
473 t->stts_sample_delta = para_malloc(t->stts_entry_count
476 for (i = 0; i < t->stts_entry_count && !f->stream->read_error; i++) {
477 t->stts_sample_count[i] = read_int32(f);
478 t->stts_sample_delta[i] = read_int32(f);
483 static int32_t read_stsc(struct mp4 *f)
488 if (f->total_tracks == 0)
490 t = f->track[f->total_tracks - 1];
492 read_char(f); /* version */
493 read_int24(f); /* flags */
494 t->stsc_entry_count = read_int32(f);
495 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
496 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
498 t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
502 for (i = 0; i < t->stsc_entry_count && !f->stream->read_error; i++) {
503 t->stsc_first_chunk[i] = read_int32(f);
504 t->stsc_samples_per_chunk[i] = read_int32(f);
505 t->stsc_sample_desc_index[i] = read_int32(f);
510 static int32_t read_stco(struct mp4 *f)
515 if (f->total_tracks == 0)
517 t = f->track[f->total_tracks - 1];
519 read_char(f); /* version */
520 read_int24(f); /* flags */
521 t->stco_entry_count = read_int32(f);
522 t->stco_chunk_offset = para_malloc(t->stco_entry_count
525 for (i = 0; i < t->stco_entry_count && !f->stream->read_error; i++)
526 t->stco_chunk_offset[i] = read_int32(f);
530 static uint16_t read_int16(struct mp4 *f)
534 read_data(f, data, 2);
535 return read_u16_be(data);
538 static int32_t read_mp4a(struct mp4 *f)
541 uint8_t atom_type = 0;
542 uint8_t header_size = 0;
545 if (f->total_tracks == 0)
547 t = f->track[f->total_tracks - 1];
549 for (i = 0; i < 6; i++) {
550 read_char(f); /* reserved */
552 /* data_reference_index */ read_int16(f);
554 read_int32(f); /* reserved */
555 read_int32(f); /* reserved */
557 t->channelCount = read_int16(f);
558 t->sampleSize = read_int16(f);
563 t->sampleRate = read_int16(f);
567 atom_read_header(f, &atom_type, &header_size);
571 static int32_t read_stsd(struct mp4 *f)
574 uint8_t header_size = 0;
578 if (f->total_tracks == 0)
580 t = f->track[f->total_tracks - 1];
582 read_char(f); /* version */
583 read_int24(f); /* flags */
585 t->stsd_entry_count = read_int32(f);
588 for (i = 0; i < t->stsd_entry_count && !f->stream->read_error; i++) {
589 uint64_t skip = get_position(f);
591 uint8_t atom_type = 0;
592 size = atom_read_header(f, &atom_type, &header_size);
594 t->is_audio = atom_type == ATOM_MP4A;
597 set_position(f, skip);
603 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
604 const char *value, int32_t len)
606 if (!item || (item && !*item) || !value)
608 tags->tags = para_realloc(tags->tags,
609 (tags->count + 1) * sizeof(struct mp4_tag));
610 tags->tags[tags->count].item = para_strdup(item);
611 tags->tags[tags->count].len = len;
613 tags->tags[tags->count].value = para_malloc(len + 1);
614 memcpy(tags->tags[tags->count].value, value, len);
615 tags->tags[tags->count].value[len] = 0;
617 tags->tags[tags->count].value = para_strdup(value);
623 static const char *ID3v1GenreList[] = {
624 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
625 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
626 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
627 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
628 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
629 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
630 "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
631 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
632 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
633 "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
634 "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
635 "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
636 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
637 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
638 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
639 "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
640 "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
641 "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
642 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
643 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
644 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
645 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
646 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
647 "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
648 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
649 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
650 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
651 "Anime", "JPop", "SynthPop",
654 static const char *meta_index_to_genre(uint32_t idx)
656 if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
657 return ID3v1GenreList[idx - 1];
663 static char *read_string(struct mp4 *f, uint32_t length)
665 char *str = para_malloc(length + 1);
666 if ((uint32_t)read_data(f, str, length) != length) {
674 static int32_t set_metadata_name(uint8_t atom_type, char **name)
676 static char *tag_names[] = {
677 "unknown", "title", "artist", "writer", "album",
678 "date", "tool", "comment", "genre", "track",
679 "disc", "compilation", "genre", "tempo", "cover",
680 "album_artist", "contentgroup", "lyrics", "description",
681 "network", "show", "episodename",
682 "sorttitle", "sortalbum", "sortartist", "sortalbumartist",
683 "sortwriter", "sortshow",
684 "season", "episode", "podcast"
719 case ATOM_COMPILATION:
731 case ATOM_ALBUM_ARTIST:
734 case ATOM_CONTENTGROUP:
740 case ATOM_DESCRIPTION:
749 case ATOM_EPISODENAME:
758 case ATOM_SORTARTIST:
761 case ATOM_SORTALBUMARTIST:
764 case ATOM_SORTWRITER:
784 *name = para_strdup(tag_names[tag_idx]);
788 static uint32_t min_body_size(uint8_t atom_type)
795 return sizeof (char) /* version */
796 + sizeof(uint8_t) * 3 /* flags */
797 + sizeof(uint32_t) /* reserved */
798 + sizeof(uint16_t) /* leading uint16_t */
799 + sizeof(uint16_t) /* track */
800 + sizeof(uint16_t); /* totaltracks */
802 return sizeof (char) /* version */
803 + sizeof(uint8_t) * 3 /* flags */
804 + sizeof(uint32_t) /* reserved */
805 + sizeof(uint16_t) /* disc */
806 + sizeof(uint16_t); /* totaldiscs */
807 default: assert(false);
811 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
814 uint8_t header_size = 0;
815 uint64_t subsize, sumsize;
824 sumsize < size && !f->stream->read_error; /* CVE-2017-9222 */
825 set_position(f, destpos), sumsize += subsize
827 subsize = atom_read_header(f, &atom_type, &header_size);
828 destpos = get_position(f) + subsize - header_size;
831 if (atom_type == ATOM_NAME) {
832 read_char(f); /* version */
833 read_int24(f); /* flags */
835 name = read_string(f, subsize - (header_size + 4));
838 if (atom_type != ATOM_DATA)
840 read_char(f); /* version */
841 read_int24(f); /* flags */
842 read_int32(f); /* reserved */
844 /* some need special attention */
845 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
847 if (subsize - header_size < min_body_size(parent))
850 if (parent == ATOM_TEMPO) {
852 sprintf(temp, "%.5u BPM", val);
853 tag_add_field(&(f-> tags), "tempo",
856 const char *tmp = meta_index_to_genre(val);
858 tag_add_field (&(f->tags),
862 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
863 uint16_t index, total;
865 if (subsize - header_size < min_body_size(parent))
868 index = read_int16(f);
869 total = read_int16(f);
870 if (parent == ATOM_TRACK)
872 sprintf(temp, "%d", index);
873 tag_add_field(&(f->tags), parent == ATOM_TRACK?
874 "track" : "disc", temp, -1);
876 sprintf(temp, "%d", total);
877 tag_add_field(& (f-> tags),
878 parent == ATOM_TRACK?
879 "totaltracks" : "totaldiscs", temp, -1);
884 data = read_string(f, subsize - (header_size + 8));
885 len = subsize - (header_size + 8);
891 set_metadata_name(parent , &name);
893 tag_add_field(&(f->tags), name, data, len);
902 static int32_t read_mdhd(struct mp4 *f)
908 if (f->total_tracks == 0)
910 t = f->track[f->total_tracks - 1];
912 version = read_int32(f);
914 read_int64(f); //creation-time
915 read_int64(f); //modification-time
916 t->timeScale = read_int32(f); //timescale
917 t->duration = read_int64(f); //duration
918 } else { //version == 0
921 read_int32(f); //creation-time
922 read_int32(f); //modification-time
923 t->timeScale = read_int32(f); //timescale
924 temp = read_int32(f);
925 t->duration = (temp == (uint32_t) (-1))?
926 (uint64_t) (-1) : (uint64_t) (temp);
933 static int32_t parse_metadata(struct mp4 *f, int32_t size)
935 uint64_t subsize, sumsize = 0;
937 uint8_t header_size = 0;
939 while (sumsize < size) {
940 subsize = atom_read_header(f, &atom_type, &header_size);
943 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
950 static int32_t read_meta(struct mp4 *f, uint64_t size)
952 uint64_t subsize, sumsize = 0;
954 uint8_t header_size = 0;
956 read_char(f); /* version */
957 read_int24(f); /* flags */
959 while (sumsize < (size - (header_size + 4))) {
960 subsize = atom_read_header(f, &atom_type, &header_size);
961 if (subsize <= header_size + 4)
963 if (atom_type == ATOM_ILST) {
964 parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
966 set_position(f, get_position(f) + subsize - header_size);
974 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
976 uint64_t dest_position = get_position(f) + size - 8;
977 if (atom_type == ATOM_STSZ) {
978 /* sample size box */
980 } else if (atom_type == ATOM_STTS) {
981 /* time to sample box */
983 } else if (atom_type == ATOM_STSC) {
984 /* sample to chunk box */
986 } else if (atom_type == ATOM_STCO) {
987 /* chunk offset box */
989 } else if (atom_type == ATOM_STSD) {
990 /* sample description box */
992 } else if (atom_type == ATOM_MDHD) {
995 } else if (atom_type == ATOM_META) {
996 /* iTunes Metadata box */
1000 set_position(f, dest_position);
1004 /* parse atoms that are sub atoms of other atoms */
1005 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
1008 uint8_t atom_type = 0;
1009 uint64_t counted_size = 0;
1010 uint8_t header_size = 0;
1012 while (counted_size < total_size) {
1013 size = atom_read_header(f, &atom_type, &header_size);
1014 counted_size += size;
1016 /* check for end of file */
1020 /* we're starting to read a new track, update index,
1021 * so that all data and tables get written in the right place
1023 if (atom_type == ATOM_TRAK)
1025 /* parse subatoms */
1026 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1027 set_position(f, get_position(f) + size - header_size);
1028 } else if (atom_type < SUBATOMIC) {
1029 parse_sub_atoms(f, size - header_size, meta_only);
1031 atom_read(f, (uint32_t) size, atom_type);
1038 /* parse root atoms */
1039 static int32_t parse_atoms(struct mp4 *f, int meta_only)
1042 uint8_t atom_type = 0;
1043 uint8_t header_size = 0;
1046 f->stream->read_error = 0;
1049 atom_read_header(f, &atom_type, &header_size)) != 0) {
1050 f->file_size += size;
1051 f->last_atom = atom_type;
1053 if (atom_type == ATOM_MOOV && size > header_size) {
1054 f->moov_offset = get_position(f) - header_size;
1055 f->moov_size = size;
1058 /* parse subatoms */
1059 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1060 set_position(f, get_position(f) + size - header_size);
1061 } else if (atom_type < SUBATOMIC) {
1062 parse_sub_atoms(f, size - header_size, meta_only);
1064 /* skip this atom */
1065 set_position(f, get_position(f) + size - header_size);
1072 struct mp4 *mp4_open_read(struct mp4_callback *f)
1074 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1088 static int32_t tag_delete(struct mp4_metadata *tags)
1092 for (i = 0; i < tags->count; i++) {
1093 free(tags->tags[i].item);
1094 free(tags->tags[i].value);
1103 void mp4_close(struct mp4 *ff)
1107 for (i = 0; i < ff->total_tracks; i++) {
1109 free(ff->track[i]->stsz_table);
1110 free(ff->track[i]->stts_sample_count);
1111 free(ff->track[i]->stts_sample_delta);
1112 free(ff->track[i]->stsc_first_chunk);
1113 free(ff->track[i]->stsc_samples_per_chunk);
1114 free(ff->track[i]->stsc_sample_desc_index);
1115 free(ff->track[i]->stco_chunk_offset);
1120 tag_delete(&(ff->tags));
1124 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1125 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1127 int32_t total_entries = 0;
1128 int32_t chunk2entry;
1129 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1133 if (f->track[track] == NULL) {
1137 total_entries = f->track[track]->stsc_entry_count;
1144 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1145 *chunk = chunk2 - chunk1;
1146 range_samples = *chunk * chunk1samples;
1148 if (sample < total + range_samples)
1151 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1154 if (chunk2entry < total_entries) {
1156 total += range_samples;
1158 } while (chunk2entry < total_entries);
1161 *chunk = (sample - total) / chunk1samples + chunk1;
1165 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1170 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1173 const struct mp4_track *p_track = f->track[track];
1175 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1176 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1178 } else if (p_track->stco_entry_count) {
1179 return p_track->stco_chunk_offset[chunk - 1];
1187 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1188 int32_t chunk_sample, int32_t sample)
1191 const struct mp4_track *p_track = f->track[track];
1193 if (p_track->stsz_sample_size) {
1194 return (sample - chunk_sample) * p_track->stsz_sample_size;
1196 if (sample >= p_track->stsz_sample_count)
1199 for (i = chunk_sample, total = 0; i < sample; i++) {
1200 total += p_track->stsz_table[i];
1207 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1210 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1212 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1214 chunk_offset1 = chunk_to_offset(f, track, chunk);
1215 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1216 track, chunk_sample, sample);
1217 return chunk_offset2;
1221 * Return the number of milliseconds of the given track.
1223 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1224 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1226 * The function returns zero if the audio file is of zero length or contains a
1227 * corrupt track header.
1229 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1231 const struct mp4_track *t = f->track[track];
1233 if (t->timeScale == 0)
1235 return t->duration * 1000 / t->timeScale;
1239 * Check whether the given track number corresponds to an audio track.
1241 * \param f See \ref mp4_get_duration().
1242 * \param track See \ref mp4_get_duration().
1244 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1245 * those the function returns false.
1247 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1249 return f->track[track]->is_audio;
1252 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1254 int32_t offset = sample_to_offset(f, track, sample);
1255 set_position(f, offset);
1258 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1260 const struct mp4_track *t = f->track[track];
1262 if (t->stsz_sample_size != 0)
1263 return t->stsz_sample_size;
1264 return t->stsz_table[sample];
1267 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1269 return f->track[track]->sampleRate;
1272 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1274 return f->track[track]->channelCount;
1277 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1282 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1283 total += f->track[track]->stts_sample_count[i];
1288 struct mp4 *mp4_open_meta(struct mp4_callback *f)
1290 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1304 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1306 return f->tags.count;
1309 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1310 char **item, char **value)
1312 if (index >= f->tags.count) {
1317 *item = para_strdup(f->tags.tags[index].item);
1318 *value = para_strdup(f->tags.tags[index].value);
1323 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1326 uint32_t remaining = size;
1327 uint64_t atom_offset = base;
1332 set_position(f, atom_offset);
1336 atom_size = read_int32(f);
1337 if (atom_size > remaining || atom_size < 8)
1339 read_data(f, atom_name, 4);
1341 if (!memcmp(atom_name, name, 4)) {
1342 set_position(f, atom_offset);
1346 remaining -= atom_size;
1347 atom_offset += atom_size;
1352 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1353 const char *name, uint32_t extraheaders, const char *name_inside)
1355 uint64_t first_base = (uint64_t) (-1);
1356 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1358 uint64_t mybase = get_position(f);
1359 uint32_t mysize = read_int32(f);
1361 if (first_base == (uint64_t) (-1))
1362 first_base = mybase;
1364 if (mysize < 8 + extraheaders)
1367 if (find_atom (f, mybase + (8 + extraheaders),
1368 mysize - (8 + extraheaders), name_inside)) {
1369 set_position(f, mybase);
1373 if (size <= mysize) {
1380 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1382 set_position(f, first_base);
1395 static struct membuffer *membuffer_create(void)
1397 const unsigned initial_size = 256;
1399 struct membuffer *buf = para_malloc(sizeof(*buf));
1400 buf->data = para_malloc(initial_size);
1402 buf->allocated = initial_size;
1403 buf->error = buf->data == 0 ? 1 : 0;
1408 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1410 unsigned dest_size = buf->written + bytes;
1414 if (dest_size > buf->allocated) {
1416 buf->allocated <<= 1;
1417 } while (dest_size > buf->allocated);
1418 buf->data = para_realloc(buf->data, buf->allocated);
1422 memcpy((char *) buf->data + buf->written, ptr, bytes);
1423 buf->written += bytes;
1427 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1429 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1432 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1436 write_u16_be(temp, data);
1437 return membuffer_write(buf, temp, 2);
1440 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1443 write_u32_be(temp, data);
1444 return membuffer_write(buf, temp, 4);
1447 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1448 uint32_t index, uint32_t total)
1450 membuffer_write_int32(buf,
1451 8 /*atom header */ + 8 /*data atom header */ +
1452 8 /*flags + reserved */ + 8 /*actual data */ );
1453 membuffer_write_atom_name(buf, name);
1454 membuffer_write_int32(buf,
1455 8 /*data atom header */ +
1456 8 /*flags + reserved */ + 8 /*actual data */ );
1457 membuffer_write_atom_name(buf, "data");
1458 membuffer_write_int32(buf, 0); //flags
1459 membuffer_write_int32(buf, 0); //reserved
1460 membuffer_write_int16(buf, 0);
1461 membuffer_write_int16(buf, (uint16_t) index); //track number
1462 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1463 membuffer_write_int16(buf, 0);
1466 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1469 membuffer_write_int32(buf,
1470 8 /*atom header */ + 8 /*data atom header */ +
1471 8 /*flags + reserved */ + 2 /*actual data */ );
1472 membuffer_write_atom_name(buf, name);
1473 membuffer_write_int32(buf,
1474 8 /*data atom header */ +
1475 8 /*flags + reserved */ + 2 /*actual data */ );
1476 membuffer_write_atom_name(buf, "data");
1477 membuffer_write_int32(buf, 0); //flags
1478 membuffer_write_int32(buf, 0); //reserved
1479 membuffer_write_int16(buf, value); //value
1482 static uint32_t myatoi(const char *param)
1484 return param ? atoi(param) : 0;
1487 static uint32_t meta_genre_to_index(const char *genrestr)
1490 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1491 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1497 struct stdmeta_entry {
1502 struct stdmeta_entry stdmetas[] = {
1503 {"\xA9" "nam", "title"},
1504 {"\xA9" "ART", "artist"},
1505 {"\xA9" "wrt", "writer"},
1506 {"\xA9" "alb", "album"},
1507 {"\xA9" "day", "date"},
1508 {"\xA9" "too", "tool"},
1509 {"\xA9" "cmt", "comment"},
1510 {"cpil", "compilation"},
1512 {"aART", "album_artist"},
1515 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1518 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1519 if (!strcasecmp(name, stdmetas[n].name))
1520 return stdmetas[n].atom;
1525 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1530 /* special check for compilation flag */
1531 if (strcmp(name, "cpil") == 0) {
1535 membuffer_write_int32(buf,
1536 8 /*atom header */ + 8 /*data atom header */ +
1537 8 /*flags + reserved */ + strlen(value));
1538 membuffer_write_atom_name(buf, name);
1539 membuffer_write_int32(buf,
1540 8 /*data atom header */ +
1541 8 /*flags + reserved */ + strlen(value));
1542 membuffer_write_atom_name(buf, "data");
1543 membuffer_write_int32(buf, flags); //flags
1544 membuffer_write_int32(buf, 0); //reserved
1545 membuffer_write(buf, value, strlen(value));
1548 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1551 membuffer_write_int32(buf,
1552 8 /*atom header */ +
1553 0x1C /*weirdo itunes atom */ +
1554 12 /*name atom header */ + strlen(name) +
1555 16 /*data atom header + flags */ + strlen(value));
1556 membuffer_write_atom_name(buf, "----");
1557 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1558 membuffer_write_atom_name(buf, "mean");
1559 membuffer_write_int32(buf, 0);
1560 membuffer_write(buf, "com.apple.iTunes", 16);
1561 membuffer_write_int32(buf, 12 + strlen(name));
1562 membuffer_write_atom_name(buf, "name");
1563 membuffer_write_int32(buf, 0);
1564 membuffer_write(buf, name, strlen(name));
1565 membuffer_write_int32(buf,
1566 8 /*data atom header */ +
1567 8 /*flags + reserved */ + strlen(value));
1568 membuffer_write_atom_name(buf, "data");
1569 membuffer_write_int32(buf, 1); //flags
1570 membuffer_write_int32(buf, 0); //reserved
1571 membuffer_write(buf, value, strlen(value));
1574 static unsigned membuffer_error(const struct membuffer *buf)
1579 static void membuffer_free(struct membuffer *buf)
1585 static unsigned membuffer_get_size(const struct membuffer *buf)
1587 return buf->written;
1590 static void *membuffer_detach(struct membuffer *buf)
1596 ret = para_realloc(buf->data, buf->written);
1602 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1603 uint32_t * out_size)
1605 struct membuffer *buf = membuffer_create();
1607 char *mask = para_calloc(data->count);
1608 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1609 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1610 const char *genre_ptr = 0, *tempo_ptr = 0;
1612 for (metaptr = 0; metaptr < data->count; metaptr++) {
1613 struct mp4_tag *tag = &data->tags[metaptr];
1614 if (!strcasecmp(tag->item, "tracknumber")
1615 || !strcasecmp(tag->item, "track")) {
1616 if (tracknumber_ptr == 0)
1617 tracknumber_ptr = tag->value;
1619 } else if (!strcasecmp(tag->item, "totaltracks")) {
1620 if (totaltracks_ptr == 0)
1621 totaltracks_ptr = tag->value;
1623 } else if (!strcasecmp(tag->item, "discnumber")
1624 || !strcasecmp(tag->item, "disc")) {
1625 if (discnumber_ptr == 0)
1626 discnumber_ptr = tag->value;
1628 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1629 if (totaldiscs_ptr == 0)
1630 totaldiscs_ptr = tag->value;
1632 } else if (!strcasecmp(tag->item, "genre")) {
1634 genre_ptr = tag->value;
1636 } else if (!strcasecmp(tag->item, "tempo")) {
1638 tempo_ptr = tag->value;
1643 if (tracknumber_ptr)
1644 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1645 myatoi(totaltracks_ptr));
1647 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1648 myatoi(totaldiscs_ptr));
1650 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1653 uint32_t index = meta_genre_to_index(genre_ptr);
1655 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1657 membuffer_write_int16_tag(buf, "gnre", index);
1659 for (metaptr = 0; metaptr < data->count; metaptr++) {
1660 struct mp4_tag *tag;
1661 const char *std_meta_atom;
1665 tag = &data->tags[metaptr];
1666 std_meta_atom = find_standard_meta(tag->item);
1668 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1670 membuffer_write_custom_tag(buf, tag->item, tag->value);
1674 if (membuffer_error(buf)) {
1675 membuffer_free(buf);
1679 *out_size = membuffer_get_size(buf);
1680 *out_buffer = membuffer_detach(buf);
1681 membuffer_free(buf);
1686 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1689 membuffer_write_int32(buf, size + 8);
1690 membuffer_write_atom_name(buf, name);
1691 membuffer_write(buf, data, size);
1694 static void *membuffer_get_ptr(const struct membuffer *buf)
1699 static void membuffer_set_error(struct membuffer *buf)
1704 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1710 oldsize = membuffer_get_size(buf);
1711 if (membuffer_write(buf, 0, bytes) != bytes)
1714 bufptr = membuffer_get_ptr(buf);
1718 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1720 membuffer_set_error(buf);
1727 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1728 uint32_t * out_size)
1730 struct membuffer *buf;
1734 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1737 buf = membuffer_create();
1739 membuffer_write_int32(buf, 0);
1740 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1743 *out_size = membuffer_get_size(buf);
1744 *out_buffer = membuffer_detach(buf);
1745 membuffer_free(buf);
1749 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1750 uint32_t * out_size)
1752 struct membuffer *buf;
1756 if (!create_meta(data, &meta_buffer, &meta_size))
1759 buf = membuffer_create();
1761 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1765 *out_size = membuffer_get_size(buf);
1766 *out_buffer = membuffer_detach(buf);
1767 membuffer_free(buf);
1771 static uint32_t fix_byte_order_32(uint32_t src)
1773 return read_u32_be(&src);
1776 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1777 void **out_buffer, uint32_t * out_size)
1779 uint64_t total_base = f->moov_offset + 8;
1780 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1782 uint64_t udta_offset, meta_offset, ilst_offset;
1783 uint32_t udta_size, meta_size, ilst_size;
1785 uint32_t new_ilst_size;
1786 void *new_ilst_buffer;
1791 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1792 struct membuffer *buf;
1793 void *new_udta_buffer;
1794 uint32_t new_udta_size;
1795 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1798 buf = membuffer_create();
1799 set_position(f, total_base);
1800 membuffer_transfer_from_file(buf, f, total_size);
1802 membuffer_write_atom(buf, "udta", new_udta_size,
1805 free(new_udta_buffer);
1807 *out_size = membuffer_get_size(buf);
1808 *out_buffer = membuffer_detach(buf);
1809 membuffer_free(buf);
1812 udta_offset = get_position(f);
1813 udta_size = read_int32(f);
1814 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1815 struct membuffer *buf;
1816 void *new_meta_buffer;
1817 uint32_t new_meta_size;
1818 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1821 buf = membuffer_create();
1822 set_position(f, total_base);
1823 membuffer_transfer_from_file(buf, f,
1824 (uint32_t)(udta_offset - total_base));
1826 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1827 membuffer_write_atom_name(buf, "udta");
1828 membuffer_transfer_from_file(buf, f, udta_size);
1830 membuffer_write_atom(buf, "meta", new_meta_size,
1832 free(new_meta_buffer);
1834 *out_size = membuffer_get_size(buf);
1835 *out_buffer = membuffer_detach(buf);
1836 membuffer_free(buf);
1839 meta_offset = get_position(f);
1840 meta_size = read_int32(f);
1841 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1842 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1843 ilst_offset = get_position(f);
1844 ilst_size = read_int32(f);
1846 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1849 size_delta = new_ilst_size - (ilst_size - 8);
1851 *out_size = total_size + size_delta;
1852 *out_buffer = para_malloc(*out_size);
1853 p_out = (uint8_t *) * out_buffer;
1855 set_position(f, total_base);
1857 (uint32_t) (udta_offset - total_base));
1858 p_out += (uint32_t) (udta_offset - total_base);
1859 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1861 read_data(f, p_out, 4);
1864 (uint32_t) (meta_offset - udta_offset - 8));
1865 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1866 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1868 read_data(f, p_out, 4);
1871 (uint32_t) (ilst_offset - meta_offset - 8));
1872 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1873 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1875 read_data(f, p_out, 4);
1878 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1879 p_out += new_ilst_size;
1881 set_position(f, ilst_offset + ilst_size);
1882 read_data(f, p_out, (uint32_t) (total_size
1883 - (ilst_offset - total_base) - ilst_size));
1885 free(new_ilst_buffer);
1890 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1894 result = f->stream->write(f->stream->user_data, data, size);
1896 f->current_position += size;
1901 static int32_t write_int32(struct mp4 *f, uint32_t data)
1904 write_u32_be(temp, data);
1905 return write_data(f, temp, sizeof(temp));
1908 static int32_t truncate_stream(struct mp4 *f)
1910 return f->stream->truncate(f->stream->user_data);
1913 int32_t mp4_meta_update(struct mp4_callback *f, const struct mp4_metadata *data)
1915 void *new_moov_data;
1916 uint32_t new_moov_size;
1918 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1920 set_position(ff, 0);
1924 if (!modify_moov(ff, data, &new_moov_data, &new_moov_size)) {
1929 /* copy moov atom to end of the file */
1930 if (ff->last_atom != ATOM_MOOV) {
1931 char *free_data = "free";
1933 /* rename old moov to free */
1934 set_position(ff, ff->moov_offset + 4);
1935 write_data(ff, free_data, 4);
1937 set_position(ff, ff->file_size);
1938 write_int32(ff, new_moov_size + 8);
1939 write_data(ff, "moov", 4);
1940 write_data(ff, new_moov_data, new_moov_size);
1942 set_position(ff, ff->moov_offset);
1943 write_int32(ff, new_moov_size + 8);
1944 write_data(ff, "moov", 4);
1945 write_data(ff, new_moov_data, new_moov_size);
1948 truncate_stream(ff);
1954 /* find a metadata item by name */
1955 /* returns 0 if item found, 1 if no such item */
1956 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1961 for (i = 0; i < f->tags.count; i++) {
1962 if (!strcasecmp(f->tags.tags[i].item, item)) {
1963 *value = para_strdup(f->tags.tags[i].value);
1974 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1976 return meta_find_by_name(f, "artist", value);
1979 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1981 return meta_find_by_name(f, "title", value);
1984 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1986 return meta_find_by_name(f, "date", value);
1989 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1991 return meta_find_by_name(f, "album", value);
1994 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1996 return meta_find_by_name(f, "comment", value);