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;
51 #define MAX_TRACKS 1024
54 /* stream to read from */
55 struct mp4_callback *stream;
56 int64_t current_position;
64 /* incremental track index while reading the file */
68 struct mp4_track *track[MAX_TRACKS];
71 struct mp4_metadata tags;
74 int32_t mp4_total_tracks(const struct mp4 *f)
76 return f->total_tracks;
79 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
83 result = f->stream->read(f->stream->user_data, data, size);
86 f->stream->read_error++;
88 f->current_position += size;
93 static uint64_t read_int64(struct mp4 *f)
97 read_data(f, data, 8);
98 return read_u64_be(data);
101 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
102 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
104 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
108 /* atoms with subatoms */
116 ATOM_ILST = 8, /* iTunes Metadata list */
127 ATOM_COMPILATION = 19,
137 /* atoms without subatoms */
157 ATOM_META = 148, /* iTunes Metadata box */
158 ATOM_NAME = 149, /* iTunes Metadata name box */
159 ATOM_DATA = 150, /* iTunes Metadata data box */
166 ATOM_ALBUM_ARTIST = 157,
167 ATOM_CONTENTGROUP = 158,
169 ATOM_DESCRIPTION = 160,
172 ATOM_EPISODENAME = 163,
173 ATOM_SORTTITLE = 164,
174 ATOM_SORTALBUM = 165,
175 ATOM_SORTARTIST = 166,
176 ATOM_SORTALBUMARTIST = 167,
177 ATOM_SORTWRITER = 168,
186 #define ATOM_FREE ATOM_UNKNOWN
187 #define ATOM_SKIP ATOM_UNKNOWN
189 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
191 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
194 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
196 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
198 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
200 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
202 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
204 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
206 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
208 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
210 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
212 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
214 } else if (a == 't') {
215 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
217 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
219 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
221 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
223 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
225 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
227 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
229 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
230 return ATOM_EPISODENAME;
231 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
233 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
235 } else if (a == 's') {
236 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
238 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
240 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
242 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
244 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
246 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
248 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
250 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
252 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
254 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
256 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
258 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
259 return ATOM_SORTTITLE;
260 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
261 return ATOM_SORTALBUM;
262 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
263 return ATOM_SORTARTIST;
264 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
265 return ATOM_SORTALBUMARTIST;
266 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
267 return ATOM_SORTWRITER;
268 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
269 return ATOM_SORTSHOW;
270 } else if (a == COPYRIGHT_SYMBOL) {
271 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
273 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
275 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
277 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
279 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
281 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
283 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
285 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
287 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
288 return ATOM_CONTENTGROUP;
289 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
293 if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
295 else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
297 else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
299 else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
301 else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
303 else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
305 else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
307 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
309 else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
311 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
313 else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
315 else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
317 else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
319 else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
320 return ATOM_COMPILATION;
321 else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
323 else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
325 else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
327 else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
329 else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
331 else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
333 else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
335 else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
336 return ATOM_ALBUM_ARTIST;
337 else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
338 return ATOM_DESCRIPTION;
339 else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
345 /* read atom header, return atom size, atom size is with header included */
346 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
347 uint8_t * header_size)
351 int8_t atom_header[8];
353 ret = read_data(f, atom_header, 8);
357 size = read_u32_be(atom_header);
360 /* check for 64 bit atom size */
363 size = read_int64(f);
365 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
366 atom_header[6], atom_header[7]);
370 static int64_t get_position(const struct mp4 *f)
372 return f->current_position;
375 static int need_parse_when_meta_only(uint8_t atom_type)
396 static int32_t set_position(struct mp4 *f, int64_t position)
398 f->stream->seek(f->stream->user_data, position);
399 f->current_position = position;
404 static void track_add(struct mp4 *f)
408 if (f->total_tracks > MAX_TRACKS) {
413 f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
416 static uint8_t read_char(struct mp4 *f)
419 read_data(f, &output, 1);
423 static uint32_t read_int24(struct mp4 *f)
427 read_data(f, data, 3);
428 return read_u24_be(data);
431 static uint32_t read_int32(struct mp4 *f)
435 read_data(f, data, 4);
436 return read_u32_be(data);
439 static int32_t read_stsz(struct mp4 *f)
444 if (f->total_tracks == 0)
446 t = f->track[f->total_tracks - 1];
447 read_char(f); /* version */
448 read_int24(f); /* flags */
449 t->stsz_sample_size = read_int32(f);
450 t->stsz_sample_count = read_int32(f);
451 if (t->stsz_sample_size != 0)
453 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
454 for (i = 0; i < t->stsz_sample_count && !f->stream->read_error; i++)
455 t->stsz_table[i] = read_int32(f);
459 static int32_t read_stts(struct mp4 *f)
465 if (f->total_tracks == 0)
467 t = f->track[f->total_tracks - 1];
468 if (t->stts_entry_count)
470 read_char(f); /* version */
471 read_int24(f); /* flags */
472 t->stts_entry_count = read_int32(f);
474 t->stts_sample_count = para_malloc(t->stts_entry_count
476 t->stts_sample_delta = para_malloc(t->stts_entry_count
479 for (i = 0; i < t->stts_entry_count && !f->stream->read_error; i++) {
480 t->stts_sample_count[i] = read_int32(f);
481 t->stts_sample_delta[i] = read_int32(f);
486 static int32_t read_stsc(struct mp4 *f)
491 if (f->total_tracks == 0)
493 t = f->track[f->total_tracks - 1];
495 read_char(f); /* version */
496 read_int24(f); /* flags */
497 t->stsc_entry_count = read_int32(f);
498 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
499 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
501 t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
505 for (i = 0; i < t->stsc_entry_count && !f->stream->read_error; i++) {
506 t->stsc_first_chunk[i] = read_int32(f);
507 t->stsc_samples_per_chunk[i] = read_int32(f);
508 t->stsc_sample_desc_index[i] = read_int32(f);
513 static int32_t read_stco(struct mp4 *f)
518 if (f->total_tracks == 0)
520 t = f->track[f->total_tracks - 1];
522 read_char(f); /* version */
523 read_int24(f); /* flags */
524 t->stco_entry_count = read_int32(f);
525 t->stco_chunk_offset = para_malloc(t->stco_entry_count
528 for (i = 0; i < t->stco_entry_count && !f->stream->read_error; i++)
529 t->stco_chunk_offset[i] = read_int32(f);
533 static uint16_t read_int16(struct mp4 *f)
537 read_data(f, data, 2);
538 return read_u16_be(data);
541 static int32_t read_mp4a(struct mp4 *f)
544 uint8_t atom_type = 0;
545 uint8_t header_size = 0;
548 if (f->total_tracks == 0)
550 t = f->track[f->total_tracks - 1];
552 for (i = 0; i < 6; i++) {
553 read_char(f); /* reserved */
555 /* data_reference_index */ read_int16(f);
557 read_int32(f); /* reserved */
558 read_int32(f); /* reserved */
560 t->channelCount = read_int16(f);
561 t->sampleSize = read_int16(f);
566 t->sampleRate = read_int16(f);
570 atom_read_header(f, &atom_type, &header_size);
574 static int32_t read_stsd(struct mp4 *f)
577 uint8_t header_size = 0;
581 if (f->total_tracks == 0)
583 t = f->track[f->total_tracks - 1];
585 read_char(f); /* version */
586 read_int24(f); /* flags */
588 t->stsd_entry_count = read_int32(f);
591 for (i = 0; i < t->stsd_entry_count && !f->stream->read_error; i++) {
592 uint64_t skip = get_position(f);
594 uint8_t atom_type = 0;
595 size = atom_read_header(f, &atom_type, &header_size);
597 t->is_audio = atom_type == ATOM_MP4A;
600 set_position(f, skip);
606 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
607 const char *value, int32_t len)
609 if (!item || (item && !*item) || !value)
611 tags->tags = para_realloc(tags->tags,
612 (tags->count + 1) * sizeof(struct mp4_tag));
613 tags->tags[tags->count].item = para_strdup(item);
614 tags->tags[tags->count].len = len;
616 tags->tags[tags->count].value = para_malloc(len + 1);
617 memcpy(tags->tags[tags->count].value, value, len);
618 tags->tags[tags->count].value[len] = 0;
620 tags->tags[tags->count].value = para_strdup(value);
626 static const char *ID3v1GenreList[] = {
627 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
628 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
629 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
630 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
631 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
632 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
633 "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
634 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
635 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
636 "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
637 "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
638 "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
639 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
640 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
641 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
642 "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
643 "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
644 "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
645 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
646 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
647 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
648 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
649 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
650 "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
651 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
652 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
653 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
654 "Anime", "JPop", "SynthPop",
657 static const char *meta_index_to_genre(uint32_t idx)
659 if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
660 return ID3v1GenreList[idx - 1];
666 static char *read_string(struct mp4 *f, uint32_t length)
668 char *str = para_malloc(length + 1);
669 if ((uint32_t)read_data(f, str, length) != length) {
677 static int32_t set_metadata_name(uint8_t atom_type, char **name)
679 static char *tag_names[] = {
680 "unknown", "title", "artist", "writer", "album",
681 "date", "tool", "comment", "genre", "track",
682 "disc", "compilation", "genre", "tempo", "cover",
683 "album_artist", "contentgroup", "lyrics", "description",
684 "network", "show", "episodename",
685 "sorttitle", "sortalbum", "sortartist", "sortalbumartist",
686 "sortwriter", "sortshow",
687 "season", "episode", "podcast"
722 case ATOM_COMPILATION:
734 case ATOM_ALBUM_ARTIST:
737 case ATOM_CONTENTGROUP:
743 case ATOM_DESCRIPTION:
752 case ATOM_EPISODENAME:
761 case ATOM_SORTARTIST:
764 case ATOM_SORTALBUMARTIST:
767 case ATOM_SORTWRITER:
787 *name = para_strdup(tag_names[tag_idx]);
791 static uint32_t min_body_size(uint8_t atom_type)
798 return sizeof (char) /* version */
799 + sizeof(uint8_t) * 3 /* flags */
800 + sizeof(uint32_t) /* reserved */
801 + sizeof(uint16_t) /* leading uint16_t */
802 + sizeof(uint16_t) /* track */
803 + sizeof(uint16_t); /* totaltracks */
805 return sizeof (char) /* version */
806 + sizeof(uint8_t) * 3 /* flags */
807 + sizeof(uint32_t) /* reserved */
808 + sizeof(uint16_t) /* disc */
809 + sizeof(uint16_t); /* totaldiscs */
810 default: assert(false);
814 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
817 uint8_t header_size = 0;
818 uint64_t subsize, sumsize;
827 sumsize < size && !f->stream->read_error; /* CVE-2017-9222 */
828 set_position(f, destpos), sumsize += subsize
830 subsize = atom_read_header(f, &atom_type, &header_size);
831 destpos = get_position(f) + subsize - header_size;
834 if (atom_type == ATOM_NAME) {
835 read_char(f); /* version */
836 read_int24(f); /* flags */
838 name = read_string(f, subsize - (header_size + 4));
841 if (atom_type != ATOM_DATA)
843 read_char(f); /* version */
844 read_int24(f); /* flags */
845 read_int32(f); /* reserved */
847 /* some need special attention */
848 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
850 if (subsize - header_size < min_body_size(parent))
853 if (parent == ATOM_TEMPO) {
855 sprintf(temp, "%.5u BPM", val);
856 tag_add_field(&(f-> tags), "tempo",
859 const char *tmp = meta_index_to_genre(val);
861 tag_add_field (&(f->tags),
865 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
866 uint16_t index, total;
868 if (subsize - header_size < min_body_size(parent))
871 index = read_int16(f);
872 total = read_int16(f);
873 if (parent == ATOM_TRACK)
875 sprintf(temp, "%d", index);
876 tag_add_field(&(f->tags), parent == ATOM_TRACK?
877 "track" : "disc", temp, -1);
879 sprintf(temp, "%d", total);
880 tag_add_field(& (f-> tags),
881 parent == ATOM_TRACK?
882 "totaltracks" : "totaldiscs", temp, -1);
887 data = read_string(f, subsize - (header_size + 8));
888 len = subsize - (header_size + 8);
894 set_metadata_name(parent , &name);
896 tag_add_field(&(f->tags), name, data, len);
905 static int32_t read_mdhd(struct mp4 *f)
911 if (f->total_tracks == 0)
913 t = f->track[f->total_tracks - 1];
915 version = read_int32(f);
917 read_int64(f); //creation-time
918 read_int64(f); //modification-time
919 t->timeScale = read_int32(f); //timescale
920 t->duration = read_int64(f); //duration
921 } else { //version == 0
924 read_int32(f); //creation-time
925 read_int32(f); //modification-time
926 t->timeScale = read_int32(f); //timescale
927 temp = read_int32(f);
928 t->duration = (temp == (uint32_t) (-1))?
929 (uint64_t) (-1) : (uint64_t) (temp);
936 static int32_t parse_metadata(struct mp4 *f, int32_t size)
938 uint64_t subsize, sumsize = 0;
940 uint8_t header_size = 0;
942 while (sumsize < size) {
943 subsize = atom_read_header(f, &atom_type, &header_size);
946 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
953 static int32_t read_meta(struct mp4 *f, uint64_t size)
955 uint64_t subsize, sumsize = 0;
957 uint8_t header_size = 0;
959 read_char(f); /* version */
960 read_int24(f); /* flags */
962 while (sumsize < (size - (header_size + 4))) {
963 subsize = atom_read_header(f, &atom_type, &header_size);
964 if (subsize <= header_size + 4)
966 if (atom_type == ATOM_ILST) {
967 parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
969 set_position(f, get_position(f) + subsize - header_size);
977 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
979 uint64_t dest_position = get_position(f) + size - 8;
980 if (atom_type == ATOM_STSZ) {
981 /* sample size box */
983 } else if (atom_type == ATOM_STTS) {
984 /* time to sample box */
986 } else if (atom_type == ATOM_STSC) {
987 /* sample to chunk box */
989 } else if (atom_type == ATOM_STCO) {
990 /* chunk offset box */
992 } else if (atom_type == ATOM_STSD) {
993 /* sample description box */
995 } else if (atom_type == ATOM_MDHD) {
998 } else if (atom_type == ATOM_META) {
999 /* iTunes Metadata box */
1003 set_position(f, dest_position);
1007 /* parse atoms that are sub atoms of other atoms */
1008 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
1011 uint8_t atom_type = 0;
1012 uint64_t counted_size = 0;
1013 uint8_t header_size = 0;
1015 while (counted_size < total_size) {
1016 size = atom_read_header(f, &atom_type, &header_size);
1017 counted_size += size;
1019 /* check for end of file */
1023 /* we're starting to read a new track, update index,
1024 * so that all data and tables get written in the right place
1026 if (atom_type == ATOM_TRAK)
1028 /* parse subatoms */
1029 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1030 set_position(f, get_position(f) + size - header_size);
1031 } else if (atom_type < SUBATOMIC) {
1032 parse_sub_atoms(f, size - header_size, meta_only);
1034 atom_read(f, (uint32_t) size, atom_type);
1041 /* parse root atoms */
1042 static int32_t parse_atoms(struct mp4 *f, int meta_only)
1045 uint8_t atom_type = 0;
1046 uint8_t header_size = 0;
1049 f->stream->read_error = 0;
1052 atom_read_header(f, &atom_type, &header_size)) != 0) {
1053 f->file_size += size;
1054 f->last_atom = atom_type;
1056 if (atom_type == ATOM_MOOV && size > header_size) {
1057 f->moov_offset = get_position(f) - header_size;
1058 f->moov_size = size;
1061 /* parse subatoms */
1062 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1063 set_position(f, get_position(f) + size - header_size);
1064 } else if (atom_type < SUBATOMIC) {
1065 parse_sub_atoms(f, size - header_size, meta_only);
1067 /* skip this atom */
1068 set_position(f, get_position(f) + size - header_size);
1075 struct mp4 *mp4_open_read(struct mp4_callback *f)
1077 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1091 static int32_t tag_delete(struct mp4_metadata *tags)
1095 for (i = 0; i < tags->count; i++) {
1096 free(tags->tags[i].item);
1097 free(tags->tags[i].value);
1106 void mp4_close(struct mp4 *ff)
1110 for (i = 0; i < ff->total_tracks; i++) {
1112 free(ff->track[i]->stsz_table);
1113 free(ff->track[i]->stts_sample_count);
1114 free(ff->track[i]->stts_sample_delta);
1115 free(ff->track[i]->stsc_first_chunk);
1116 free(ff->track[i]->stsc_samples_per_chunk);
1117 free(ff->track[i]->stsc_sample_desc_index);
1118 free(ff->track[i]->stco_chunk_offset);
1123 tag_delete(&(ff->tags));
1127 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1128 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1130 int32_t total_entries = 0;
1131 int32_t chunk2entry;
1132 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1136 if (f->track[track] == NULL) {
1140 total_entries = f->track[track]->stsc_entry_count;
1147 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1148 *chunk = chunk2 - chunk1;
1149 range_samples = *chunk * chunk1samples;
1151 if (sample < total + range_samples)
1154 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1157 if (chunk2entry < total_entries) {
1159 total += range_samples;
1161 } while (chunk2entry < total_entries);
1164 *chunk = (sample - total) / chunk1samples + chunk1;
1168 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1173 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1176 const struct mp4_track *p_track = f->track[track];
1178 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1179 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1181 } else if (p_track->stco_entry_count) {
1182 return p_track->stco_chunk_offset[chunk - 1];
1190 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1191 int32_t chunk_sample, int32_t sample)
1194 const struct mp4_track *p_track = f->track[track];
1196 if (p_track->stsz_sample_size) {
1197 return (sample - chunk_sample) * p_track->stsz_sample_size;
1199 if (sample >= p_track->stsz_sample_count)
1202 for (i = chunk_sample, total = 0; i < sample; i++) {
1203 total += p_track->stsz_table[i];
1210 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1213 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1215 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1217 chunk_offset1 = chunk_to_offset(f, track, chunk);
1218 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1219 track, chunk_sample, sample);
1220 return chunk_offset2;
1224 * Return the number of milliseconds of the given track.
1226 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1227 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1229 * The function returns zero if the audio file is of zero length or contains a
1230 * corrupt track header.
1232 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1234 const struct mp4_track *t = f->track[track];
1236 if (t->timeScale == 0)
1238 return t->duration * 1000 / t->timeScale;
1242 * Check whether the given track number corresponds to an audio track.
1244 * \param f See \ref mp4_get_duration().
1245 * \param track See \ref mp4_get_duration().
1247 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1248 * those the function returns false.
1250 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1252 return f->track[track]->is_audio;
1255 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1257 int32_t offset = sample_to_offset(f, track, sample);
1258 set_position(f, offset);
1261 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1263 const struct mp4_track *t = f->track[track];
1265 if (t->stsz_sample_size != 0)
1266 return t->stsz_sample_size;
1267 return t->stsz_table[sample];
1270 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1272 return f->track[track]->sampleRate;
1275 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1277 return f->track[track]->channelCount;
1280 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1285 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1286 total += f->track[track]->stts_sample_count[i];
1291 struct mp4 *mp4_open_meta(struct mp4_callback *f)
1293 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1307 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1309 return f->tags.count;
1312 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1313 char **item, char **value)
1315 if (index >= f->tags.count) {
1320 *item = para_strdup(f->tags.tags[index].item);
1321 *value = para_strdup(f->tags.tags[index].value);
1326 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1329 uint32_t remaining = size;
1330 uint64_t atom_offset = base;
1335 set_position(f, atom_offset);
1339 atom_size = read_int32(f);
1340 if (atom_size > remaining || atom_size < 8)
1342 read_data(f, atom_name, 4);
1344 if (!memcmp(atom_name, name, 4)) {
1345 set_position(f, atom_offset);
1349 remaining -= atom_size;
1350 atom_offset += atom_size;
1355 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1356 const char *name, uint32_t extraheaders, const char *name_inside)
1358 uint64_t first_base = (uint64_t) (-1);
1359 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1361 uint64_t mybase = get_position(f);
1362 uint32_t mysize = read_int32(f);
1364 if (first_base == (uint64_t) (-1))
1365 first_base = mybase;
1367 if (mysize < 8 + extraheaders)
1370 if (find_atom (f, mybase + (8 + extraheaders),
1371 mysize - (8 + extraheaders), name_inside)) {
1372 set_position(f, mybase);
1376 if (size <= mysize) {
1383 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1385 set_position(f, first_base);
1398 static struct membuffer *membuffer_create(void)
1400 const unsigned initial_size = 256;
1402 struct membuffer *buf = para_malloc(sizeof(*buf));
1403 buf->data = para_malloc(initial_size);
1405 buf->allocated = initial_size;
1406 buf->error = buf->data == 0 ? 1 : 0;
1411 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1413 unsigned dest_size = buf->written + bytes;
1417 if (dest_size > buf->allocated) {
1419 buf->allocated <<= 1;
1420 } while (dest_size > buf->allocated);
1421 buf->data = para_realloc(buf->data, buf->allocated);
1425 memcpy((char *) buf->data + buf->written, ptr, bytes);
1426 buf->written += bytes;
1430 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1432 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1435 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1439 write_u16_be(temp, data);
1440 return membuffer_write(buf, temp, 2);
1443 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1446 write_u32_be(temp, data);
1447 return membuffer_write(buf, temp, 4);
1450 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1451 uint32_t index, uint32_t total)
1453 membuffer_write_int32(buf,
1454 8 /*atom header */ + 8 /*data atom header */ +
1455 8 /*flags + reserved */ + 8 /*actual data */ );
1456 membuffer_write_atom_name(buf, name);
1457 membuffer_write_int32(buf,
1458 8 /*data atom header */ +
1459 8 /*flags + reserved */ + 8 /*actual data */ );
1460 membuffer_write_atom_name(buf, "data");
1461 membuffer_write_int32(buf, 0); //flags
1462 membuffer_write_int32(buf, 0); //reserved
1463 membuffer_write_int16(buf, 0);
1464 membuffer_write_int16(buf, (uint16_t) index); //track number
1465 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1466 membuffer_write_int16(buf, 0);
1469 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1472 membuffer_write_int32(buf,
1473 8 /*atom header */ + 8 /*data atom header */ +
1474 8 /*flags + reserved */ + 2 /*actual data */ );
1475 membuffer_write_atom_name(buf, name);
1476 membuffer_write_int32(buf,
1477 8 /*data atom header */ +
1478 8 /*flags + reserved */ + 2 /*actual data */ );
1479 membuffer_write_atom_name(buf, "data");
1480 membuffer_write_int32(buf, 0); //flags
1481 membuffer_write_int32(buf, 0); //reserved
1482 membuffer_write_int16(buf, value); //value
1485 static uint32_t myatoi(const char *param)
1487 return param ? atoi(param) : 0;
1490 static uint32_t meta_genre_to_index(const char *genrestr)
1493 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1494 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1500 struct stdmeta_entry {
1505 struct stdmeta_entry stdmetas[] = {
1506 {"\xA9" "nam", "title"},
1507 {"\xA9" "ART", "artist"},
1508 {"\xA9" "wrt", "writer"},
1509 {"\xA9" "alb", "album"},
1510 {"\xA9" "day", "date"},
1511 {"\xA9" "too", "tool"},
1512 {"\xA9" "cmt", "comment"},
1513 {"cpil", "compilation"},
1515 {"aART", "album_artist"},
1518 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1521 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1522 if (!strcasecmp(name, stdmetas[n].name))
1523 return stdmetas[n].atom;
1528 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1533 /* special check for compilation flag */
1534 if (strcmp(name, "cpil") == 0) {
1538 membuffer_write_int32(buf,
1539 8 /*atom header */ + 8 /*data atom header */ +
1540 8 /*flags + reserved */ + strlen(value));
1541 membuffer_write_atom_name(buf, name);
1542 membuffer_write_int32(buf,
1543 8 /*data atom header */ +
1544 8 /*flags + reserved */ + strlen(value));
1545 membuffer_write_atom_name(buf, "data");
1546 membuffer_write_int32(buf, flags); //flags
1547 membuffer_write_int32(buf, 0); //reserved
1548 membuffer_write(buf, value, strlen(value));
1551 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1554 membuffer_write_int32(buf,
1555 8 /*atom header */ +
1556 0x1C /*weirdo itunes atom */ +
1557 12 /*name atom header */ + strlen(name) +
1558 16 /*data atom header + flags */ + strlen(value));
1559 membuffer_write_atom_name(buf, "----");
1560 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1561 membuffer_write_atom_name(buf, "mean");
1562 membuffer_write_int32(buf, 0);
1563 membuffer_write(buf, "com.apple.iTunes", 16);
1564 membuffer_write_int32(buf, 12 + strlen(name));
1565 membuffer_write_atom_name(buf, "name");
1566 membuffer_write_int32(buf, 0);
1567 membuffer_write(buf, name, strlen(name));
1568 membuffer_write_int32(buf,
1569 8 /*data atom header */ +
1570 8 /*flags + reserved */ + strlen(value));
1571 membuffer_write_atom_name(buf, "data");
1572 membuffer_write_int32(buf, 1); //flags
1573 membuffer_write_int32(buf, 0); //reserved
1574 membuffer_write(buf, value, strlen(value));
1577 static unsigned membuffer_error(const struct membuffer *buf)
1582 static void membuffer_free(struct membuffer *buf)
1588 static unsigned membuffer_get_size(const struct membuffer *buf)
1590 return buf->written;
1593 static void *membuffer_detach(struct membuffer *buf)
1599 ret = para_realloc(buf->data, buf->written);
1605 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1606 uint32_t * out_size)
1608 struct membuffer *buf = membuffer_create();
1610 char *mask = para_calloc(data->count);
1611 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1612 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1613 const char *genre_ptr = 0, *tempo_ptr = 0;
1615 for (metaptr = 0; metaptr < data->count; metaptr++) {
1616 struct mp4_tag *tag = &data->tags[metaptr];
1617 if (!strcasecmp(tag->item, "tracknumber")
1618 || !strcasecmp(tag->item, "track")) {
1619 if (tracknumber_ptr == 0)
1620 tracknumber_ptr = tag->value;
1622 } else if (!strcasecmp(tag->item, "totaltracks")) {
1623 if (totaltracks_ptr == 0)
1624 totaltracks_ptr = tag->value;
1626 } else if (!strcasecmp(tag->item, "discnumber")
1627 || !strcasecmp(tag->item, "disc")) {
1628 if (discnumber_ptr == 0)
1629 discnumber_ptr = tag->value;
1631 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1632 if (totaldiscs_ptr == 0)
1633 totaldiscs_ptr = tag->value;
1635 } else if (!strcasecmp(tag->item, "genre")) {
1637 genre_ptr = tag->value;
1639 } else if (!strcasecmp(tag->item, "tempo")) {
1641 tempo_ptr = tag->value;
1646 if (tracknumber_ptr)
1647 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1648 myatoi(totaltracks_ptr));
1650 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1651 myatoi(totaldiscs_ptr));
1653 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1656 uint32_t index = meta_genre_to_index(genre_ptr);
1658 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1660 membuffer_write_int16_tag(buf, "gnre", index);
1662 for (metaptr = 0; metaptr < data->count; metaptr++) {
1663 struct mp4_tag *tag;
1664 const char *std_meta_atom;
1668 tag = &data->tags[metaptr];
1669 std_meta_atom = find_standard_meta(tag->item);
1671 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1673 membuffer_write_custom_tag(buf, tag->item, tag->value);
1677 if (membuffer_error(buf)) {
1678 membuffer_free(buf);
1682 *out_size = membuffer_get_size(buf);
1683 *out_buffer = membuffer_detach(buf);
1684 membuffer_free(buf);
1689 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1692 membuffer_write_int32(buf, size + 8);
1693 membuffer_write_atom_name(buf, name);
1694 membuffer_write(buf, data, size);
1697 static void *membuffer_get_ptr(const struct membuffer *buf)
1702 static void membuffer_set_error(struct membuffer *buf)
1707 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1713 oldsize = membuffer_get_size(buf);
1714 if (membuffer_write(buf, 0, bytes) != bytes)
1717 bufptr = membuffer_get_ptr(buf);
1721 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1723 membuffer_set_error(buf);
1730 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1731 uint32_t * out_size)
1733 struct membuffer *buf;
1737 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1740 buf = membuffer_create();
1742 membuffer_write_int32(buf, 0);
1743 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1746 *out_size = membuffer_get_size(buf);
1747 *out_buffer = membuffer_detach(buf);
1748 membuffer_free(buf);
1752 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1753 uint32_t * out_size)
1755 struct membuffer *buf;
1759 if (!create_meta(data, &meta_buffer, &meta_size))
1762 buf = membuffer_create();
1764 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1768 *out_size = membuffer_get_size(buf);
1769 *out_buffer = membuffer_detach(buf);
1770 membuffer_free(buf);
1774 static uint32_t fix_byte_order_32(uint32_t src)
1776 return read_u32_be(&src);
1779 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1780 void **out_buffer, uint32_t * out_size)
1782 uint64_t total_base = f->moov_offset + 8;
1783 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1785 uint64_t udta_offset, meta_offset, ilst_offset;
1786 uint32_t udta_size, meta_size, ilst_size;
1788 uint32_t new_ilst_size;
1789 void *new_ilst_buffer;
1794 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1795 struct membuffer *buf;
1796 void *new_udta_buffer;
1797 uint32_t new_udta_size;
1798 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1801 buf = membuffer_create();
1802 set_position(f, total_base);
1803 membuffer_transfer_from_file(buf, f, total_size);
1805 membuffer_write_atom(buf, "udta", new_udta_size,
1808 free(new_udta_buffer);
1810 *out_size = membuffer_get_size(buf);
1811 *out_buffer = membuffer_detach(buf);
1812 membuffer_free(buf);
1815 udta_offset = get_position(f);
1816 udta_size = read_int32(f);
1817 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1818 struct membuffer *buf;
1819 void *new_meta_buffer;
1820 uint32_t new_meta_size;
1821 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1824 buf = membuffer_create();
1825 set_position(f, total_base);
1826 membuffer_transfer_from_file(buf, f,
1827 (uint32_t)(udta_offset - total_base));
1829 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1830 membuffer_write_atom_name(buf, "udta");
1831 membuffer_transfer_from_file(buf, f, udta_size);
1833 membuffer_write_atom(buf, "meta", new_meta_size,
1835 free(new_meta_buffer);
1837 *out_size = membuffer_get_size(buf);
1838 *out_buffer = membuffer_detach(buf);
1839 membuffer_free(buf);
1842 meta_offset = get_position(f);
1843 meta_size = read_int32(f);
1844 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1845 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1846 ilst_offset = get_position(f);
1847 ilst_size = read_int32(f);
1849 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1852 size_delta = new_ilst_size - (ilst_size - 8);
1854 *out_size = total_size + size_delta;
1855 *out_buffer = para_malloc(*out_size);
1856 p_out = (uint8_t *) * out_buffer;
1858 set_position(f, total_base);
1860 (uint32_t) (udta_offset - total_base));
1861 p_out += (uint32_t) (udta_offset - total_base);
1862 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1864 read_data(f, p_out, 4);
1867 (uint32_t) (meta_offset - udta_offset - 8));
1868 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1869 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1871 read_data(f, p_out, 4);
1874 (uint32_t) (ilst_offset - meta_offset - 8));
1875 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1876 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1878 read_data(f, p_out, 4);
1881 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1882 p_out += new_ilst_size;
1884 set_position(f, ilst_offset + ilst_size);
1885 read_data(f, p_out, (uint32_t) (total_size
1886 - (ilst_offset - total_base) - ilst_size));
1888 free(new_ilst_buffer);
1893 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1897 result = f->stream->write(f->stream->user_data, data, size);
1899 f->current_position += size;
1904 static int32_t write_int32(struct mp4 *f, uint32_t data)
1907 write_u32_be(temp, data);
1908 return write_data(f, temp, sizeof(temp));
1911 static int32_t truncate_stream(struct mp4 *f)
1913 return f->stream->truncate(f->stream->user_data);
1916 int32_t mp4_meta_update(struct mp4_callback *f, const struct mp4_metadata *data)
1918 void *new_moov_data;
1919 uint32_t new_moov_size;
1921 struct mp4 *ff = para_calloc(sizeof(struct mp4));
1923 set_position(ff, 0);
1927 if (!modify_moov(ff, data, &new_moov_data, &new_moov_size)) {
1932 /* copy moov atom to end of the file */
1933 if (ff->last_atom != ATOM_MOOV) {
1934 char *free_data = "free";
1936 /* rename old moov to free */
1937 set_position(ff, ff->moov_offset + 4);
1938 write_data(ff, free_data, 4);
1940 set_position(ff, ff->file_size);
1941 write_int32(ff, new_moov_size + 8);
1942 write_data(ff, "moov", 4);
1943 write_data(ff, new_moov_data, new_moov_size);
1945 set_position(ff, ff->moov_offset);
1946 write_int32(ff, new_moov_size + 8);
1947 write_data(ff, "moov", 4);
1948 write_data(ff, new_moov_data, new_moov_size);
1951 truncate_stream(ff);
1957 /* find a metadata item by name */
1958 /* returns 0 if item found, 1 if no such item */
1959 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1964 for (i = 0; i < f->tags.count; i++) {
1965 if (!strcasecmp(f->tags.tags[i].item, item)) {
1966 *value = para_strdup(f->tags.tags[i].value);
1977 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1979 return meta_find_by_name(f, "artist", value);
1982 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1984 return meta_find_by_name(f, "title", value);
1987 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1989 return meta_find_by_name(f, "date", value);
1992 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1994 return meta_find_by_name(f, "album", value);
1997 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1999 return meta_find_by_name(f, "comment", value);