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 const struct mp4_callback *cb;
48 int64_t current_position;
58 /* incremental track index while reading the file */
62 struct mp4_track *track[MAX_TRACKS];
65 struct mp4_metadata tags;
68 int32_t mp4_total_tracks(const struct mp4 *f)
70 return f->total_tracks;
73 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
77 result = f->cb->read(f->cb->user_data, data, size);
82 f->current_position += size;
87 static uint64_t read_int64(struct mp4 *f)
91 read_data(f, data, 8);
92 return read_u64_be(data);
95 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
96 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
98 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
102 /* atoms with subatoms */
110 ATOM_ILST = 8, /* iTunes Metadata list */
121 ATOM_COMPILATION = 19,
131 /* atoms without subatoms */
151 ATOM_META = 148, /* iTunes Metadata box */
152 ATOM_NAME = 149, /* iTunes Metadata name box */
153 ATOM_DATA = 150, /* iTunes Metadata data box */
160 ATOM_ALBUM_ARTIST = 157,
161 ATOM_CONTENTGROUP = 158,
163 ATOM_DESCRIPTION = 160,
166 ATOM_EPISODENAME = 163,
167 ATOM_SORTTITLE = 164,
168 ATOM_SORTALBUM = 165,
169 ATOM_SORTARTIST = 166,
170 ATOM_SORTALBUMARTIST = 167,
171 ATOM_SORTWRITER = 168,
180 #define ATOM_FREE ATOM_UNKNOWN
181 #define ATOM_SKIP ATOM_UNKNOWN
183 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
185 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
188 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
190 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
192 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
194 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
196 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
198 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
200 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
202 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
204 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
206 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
208 } else if (a == 't') {
209 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
211 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
213 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
215 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
217 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
219 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
221 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
223 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
224 return ATOM_EPISODENAME;
225 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
227 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
229 } else if (a == 's') {
230 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
232 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
234 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
236 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
238 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
240 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
242 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
244 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
246 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
248 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
250 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
252 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
253 return ATOM_SORTTITLE;
254 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
255 return ATOM_SORTALBUM;
256 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
257 return ATOM_SORTARTIST;
258 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
259 return ATOM_SORTALBUMARTIST;
260 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
261 return ATOM_SORTWRITER;
262 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
263 return ATOM_SORTSHOW;
264 } else if (a == COPYRIGHT_SYMBOL) {
265 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
267 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
269 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
271 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
273 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
275 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
277 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
279 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
281 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
282 return ATOM_CONTENTGROUP;
283 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
287 if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
289 else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
291 else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
293 else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
295 else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
297 else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
299 else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
301 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
303 else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
305 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
307 else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
309 else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
311 else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
313 else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
314 return ATOM_COMPILATION;
315 else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
317 else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
319 else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
321 else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
323 else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
325 else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
327 else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
329 else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
330 return ATOM_ALBUM_ARTIST;
331 else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
332 return ATOM_DESCRIPTION;
333 else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
339 /* read atom header, return atom size, atom size is with header included */
340 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
341 uint8_t * header_size)
345 int8_t atom_header[8];
347 ret = read_data(f, atom_header, 8);
351 size = read_u32_be(atom_header);
354 /* check for 64 bit atom size */
357 size = read_int64(f);
359 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
360 atom_header[6], atom_header[7]);
364 static int64_t get_position(const struct mp4 *f)
366 return f->current_position;
369 static int need_parse_when_meta_only(uint8_t atom_type)
390 static int32_t set_position(struct mp4 *f, int64_t position)
392 f->cb->seek(f->cb->user_data, position);
393 f->current_position = position;
398 static void track_add(struct mp4 *f)
402 if (f->total_tracks > MAX_TRACKS) {
407 f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
410 static uint8_t read_char(struct mp4 *f)
413 read_data(f, &output, 1);
417 static uint32_t read_int24(struct mp4 *f)
421 read_data(f, data, 3);
422 return read_u24_be(data);
425 static uint32_t read_int32(struct mp4 *f)
429 read_data(f, data, 4);
430 return read_u32_be(data);
433 static int32_t read_stsz(struct mp4 *f)
438 if (f->total_tracks == 0)
440 t = f->track[f->total_tracks - 1];
441 read_char(f); /* version */
442 read_int24(f); /* flags */
443 t->stsz_sample_size = read_int32(f);
444 t->stsz_sample_count = read_int32(f);
445 if (t->stsz_sample_size != 0)
447 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
448 for (i = 0; i < t->stsz_sample_count && !f->read_error; i++)
449 t->stsz_table[i] = read_int32(f);
453 static int32_t read_stts(struct mp4 *f)
459 if (f->total_tracks == 0)
461 t = f->track[f->total_tracks - 1];
462 if (t->stts_entry_count)
464 read_char(f); /* version */
465 read_int24(f); /* flags */
466 t->stts_entry_count = read_int32(f);
468 t->stts_sample_count = para_malloc(t->stts_entry_count
470 t->stts_sample_delta = para_malloc(t->stts_entry_count
473 for (i = 0; i < t->stts_entry_count && !f->read_error; i++) {
474 t->stts_sample_count[i] = read_int32(f);
475 t->stts_sample_delta[i] = read_int32(f);
480 static int32_t read_stsc(struct mp4 *f)
485 if (f->total_tracks == 0)
487 t = f->track[f->total_tracks - 1];
489 read_char(f); /* version */
490 read_int24(f); /* flags */
491 t->stsc_entry_count = read_int32(f);
492 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
493 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
495 t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
499 for (i = 0; i < t->stsc_entry_count && !f->read_error; i++) {
500 t->stsc_first_chunk[i] = read_int32(f);
501 t->stsc_samples_per_chunk[i] = read_int32(f);
502 t->stsc_sample_desc_index[i] = read_int32(f);
507 static int32_t read_stco(struct mp4 *f)
512 if (f->total_tracks == 0)
514 t = f->track[f->total_tracks - 1];
516 read_char(f); /* version */
517 read_int24(f); /* flags */
518 t->stco_entry_count = read_int32(f);
519 t->stco_chunk_offset = para_malloc(t->stco_entry_count
522 for (i = 0; i < t->stco_entry_count && !f->read_error; i++)
523 t->stco_chunk_offset[i] = read_int32(f);
527 static uint16_t read_int16(struct mp4 *f)
531 read_data(f, data, 2);
532 return read_u16_be(data);
535 static int32_t read_mp4a(struct mp4 *f)
538 uint8_t atom_type = 0;
539 uint8_t header_size = 0;
542 if (f->total_tracks == 0)
544 t = f->track[f->total_tracks - 1];
546 for (i = 0; i < 6; i++) {
547 read_char(f); /* reserved */
549 /* data_reference_index */ read_int16(f);
551 read_int32(f); /* reserved */
552 read_int32(f); /* reserved */
554 t->channelCount = read_int16(f);
560 t->sampleRate = read_int16(f);
564 atom_read_header(f, &atom_type, &header_size);
568 static int32_t read_stsd(struct mp4 *f)
570 int32_t i, entry_count;
571 uint8_t header_size = 0;
575 if (f->total_tracks == 0)
577 t = f->track[f->total_tracks - 1];
579 read_char(f); /* version */
580 read_int24(f); /* flags */
582 entry_count = read_int32(f);
585 for (i = 0; i < entry_count && !f->read_error; i++) {
586 uint64_t skip = get_position(f);
588 uint8_t atom_type = 0;
589 size = atom_read_header(f, &atom_type, &header_size);
591 t->is_audio = atom_type == ATOM_MP4A;
594 set_position(f, skip);
600 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
601 const char *value, int32_t len)
603 if (!item || (item && !*item) || !value)
605 tags->tags = para_realloc(tags->tags,
606 (tags->count + 1) * sizeof(struct mp4_tag));
607 tags->tags[tags->count].item = para_strdup(item);
608 tags->tags[tags->count].len = len;
610 tags->tags[tags->count].value = para_malloc(len + 1);
611 memcpy(tags->tags[tags->count].value, value, len);
612 tags->tags[tags->count].value[len] = 0;
614 tags->tags[tags->count].value = para_strdup(value);
620 static const char *ID3v1GenreList[] = {
621 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
622 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
623 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
624 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
625 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
626 "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
627 "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
628 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
629 "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
630 "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
631 "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
632 "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
633 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
634 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
635 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
636 "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
637 "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
638 "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
639 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
640 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
641 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
642 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
643 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
644 "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
645 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
646 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
647 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
648 "Anime", "JPop", "SynthPop",
651 static const char *meta_index_to_genre(uint32_t idx)
653 if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
654 return ID3v1GenreList[idx - 1];
660 static char *read_string(struct mp4 *f, uint32_t length)
662 char *str = para_malloc(length + 1);
663 if ((uint32_t)read_data(f, str, length) != length) {
671 static const char *get_metadata_name(uint8_t atom_type)
674 case ATOM_TITLE: return "title";
675 case ATOM_ARTIST: return "artist";
676 case ATOM_WRITER: return "writer";
677 case ATOM_ALBUM: return "album";
678 case ATOM_DATE: return "date";
679 case ATOM_TOOL: return "tool";
680 case ATOM_COMMENT: return "comment";
681 case ATOM_GENRE1: return "genre";
682 case ATOM_TRACK: return "track";
683 case ATOM_DISC: return "disc";
684 case ATOM_COMPILATION: return "compilation";
685 case ATOM_GENRE2: return "genre";
686 case ATOM_TEMPO: return "tempo";
687 case ATOM_COVER: return "cover";
688 case ATOM_ALBUM_ARTIST: return "album_artist";
689 case ATOM_CONTENTGROUP: return "contentgroup";
690 case ATOM_LYRICS: return "lyrics";
691 case ATOM_DESCRIPTION: return "description";
692 case ATOM_NETWORK: return "network";
693 case ATOM_SHOW: return "show";
694 case ATOM_EPISODENAME: return "episodename";
695 case ATOM_SORTTITLE: return "sorttitle";
696 case ATOM_SORTALBUM: return "sortalbum";
697 case ATOM_SORTARTIST: return "sortartist";
698 case ATOM_SORTALBUMARTIST: return "sortalbumartist";
699 case ATOM_SORTWRITER: return "sortwriter";
700 case ATOM_SORTSHOW: return "sortshow";
701 case ATOM_SEASON: return "season";
702 case ATOM_EPISODE: return "episode";
703 case ATOM_PODCAST: return "podcast";
704 default: return "unknown";
708 static uint32_t min_body_size(uint8_t atom_type)
715 return sizeof (char) /* version */
716 + sizeof(uint8_t) * 3 /* flags */
717 + sizeof(uint32_t) /* reserved */
718 + sizeof(uint16_t) /* leading uint16_t */
719 + sizeof(uint16_t) /* track */
720 + sizeof(uint16_t); /* totaltracks */
722 return sizeof (char) /* version */
723 + sizeof(uint8_t) * 3 /* flags */
724 + sizeof(uint32_t) /* reserved */
725 + sizeof(uint16_t) /* disc */
726 + sizeof(uint16_t); /* totaldiscs */
727 default: assert(false);
731 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
734 uint8_t header_size = 0;
735 uint64_t subsize, sumsize;
744 sumsize < size && !f->read_error; /* CVE-2017-9222 */
745 set_position(f, destpos), sumsize += subsize
747 subsize = atom_read_header(f, &atom_type, &header_size);
748 destpos = get_position(f) + subsize - header_size;
751 if (atom_type == ATOM_NAME) {
752 read_char(f); /* version */
753 read_int24(f); /* flags */
755 name = read_string(f, subsize - (header_size + 4));
758 if (atom_type != ATOM_DATA)
760 read_char(f); /* version */
761 read_int24(f); /* flags */
762 read_int32(f); /* reserved */
764 /* some need special attention */
765 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
767 if (subsize - header_size < min_body_size(parent))
770 if (parent == ATOM_TEMPO) {
772 sprintf(temp, "%.5u BPM", val);
773 tag_add_field(&(f-> tags), "tempo",
776 const char *tmp = meta_index_to_genre(val);
778 tag_add_field (&(f->tags),
782 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
783 uint16_t index, total;
785 if (subsize - header_size < min_body_size(parent))
788 index = read_int16(f);
789 total = read_int16(f);
790 if (parent == ATOM_TRACK)
792 sprintf(temp, "%d", index);
793 tag_add_field(&(f->tags), parent == ATOM_TRACK?
794 "track" : "disc", temp, -1);
796 sprintf(temp, "%d", total);
797 tag_add_field(& (f-> tags),
798 parent == ATOM_TRACK?
799 "totaltracks" : "totaldiscs", temp, -1);
804 data = read_string(f, subsize - (header_size + 8));
805 len = subsize - (header_size + 8);
811 name = para_strdup(get_metadata_name(parent));
812 tag_add_field(&(f->tags), name, data, len);
820 static int32_t read_mdhd(struct mp4 *f)
826 if (f->total_tracks == 0)
828 t = f->track[f->total_tracks - 1];
830 version = read_int32(f);
832 read_int64(f); //creation-time
833 read_int64(f); //modification-time
834 t->timeScale = read_int32(f); //timescale
835 t->duration = read_int64(f); //duration
836 } else { //version == 0
839 read_int32(f); //creation-time
840 read_int32(f); //modification-time
841 t->timeScale = read_int32(f); //timescale
842 temp = read_int32(f);
843 t->duration = (temp == (uint32_t) (-1))?
844 (uint64_t) (-1) : (uint64_t) (temp);
851 static int32_t parse_metadata(struct mp4 *f, int32_t size)
853 uint64_t subsize, sumsize = 0;
855 uint8_t header_size = 0;
857 while (sumsize < size) {
858 subsize = atom_read_header(f, &atom_type, &header_size);
861 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
868 static int32_t read_meta(struct mp4 *f, uint64_t size)
870 uint64_t subsize, sumsize = 0;
872 uint8_t header_size = 0;
874 read_char(f); /* version */
875 read_int24(f); /* flags */
877 while (sumsize < (size - (header_size + 4))) {
878 subsize = atom_read_header(f, &atom_type, &header_size);
879 if (subsize <= header_size + 4)
881 if (atom_type == ATOM_ILST) {
882 parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
884 set_position(f, get_position(f) + subsize - header_size);
892 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
894 uint64_t dest_position = get_position(f) + size - 8;
895 if (atom_type == ATOM_STSZ) {
896 /* sample size box */
898 } else if (atom_type == ATOM_STTS) {
899 /* time to sample box */
901 } else if (atom_type == ATOM_STSC) {
902 /* sample to chunk box */
904 } else if (atom_type == ATOM_STCO) {
905 /* chunk offset box */
907 } else if (atom_type == ATOM_STSD) {
908 /* sample description box */
910 } else if (atom_type == ATOM_MDHD) {
913 } else if (atom_type == ATOM_META) {
914 /* iTunes Metadata box */
918 set_position(f, dest_position);
922 /* parse atoms that are sub atoms of other atoms */
923 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
926 uint8_t atom_type = 0;
927 uint64_t counted_size = 0;
928 uint8_t header_size = 0;
930 while (counted_size < total_size) {
931 size = atom_read_header(f, &atom_type, &header_size);
932 counted_size += size;
934 /* check for end of file */
938 /* we're starting to read a new track, update index,
939 * so that all data and tables get written in the right place
941 if (atom_type == ATOM_TRAK)
944 if (meta_only && !need_parse_when_meta_only(atom_type)) {
945 set_position(f, get_position(f) + size - header_size);
946 } else if (atom_type < SUBATOMIC) {
947 parse_sub_atoms(f, size - header_size, meta_only);
949 atom_read(f, (uint32_t) size, atom_type);
956 /* parse root atoms */
957 static int32_t parse_atoms(struct mp4 *f, int meta_only)
960 uint8_t atom_type = 0;
961 uint8_t header_size = 0;
967 atom_read_header(f, &atom_type, &header_size)) != 0) {
968 f->file_size += size;
969 f->last_atom = atom_type;
971 if (atom_type == ATOM_MOOV && size > header_size) {
972 f->moov_offset = get_position(f) - header_size;
977 if (meta_only && !need_parse_when_meta_only(atom_type)) {
978 set_position(f, get_position(f) + size - header_size);
979 } else if (atom_type < SUBATOMIC) {
980 parse_sub_atoms(f, size - header_size, meta_only);
983 set_position(f, get_position(f) + size - header_size);
990 struct mp4 *mp4_open_read(const struct mp4_callback *cb)
992 struct mp4 *f = para_calloc(sizeof(struct mp4));
1003 static int32_t tag_delete(struct mp4_metadata *tags)
1007 for (i = 0; i < tags->count; i++) {
1008 free(tags->tags[i].item);
1009 free(tags->tags[i].value);
1018 void mp4_close(struct mp4 *f)
1022 for (i = 0; i < f->total_tracks; i++) {
1024 free(f->track[i]->stsz_table);
1025 free(f->track[i]->stts_sample_count);
1026 free(f->track[i]->stts_sample_delta);
1027 free(f->track[i]->stsc_first_chunk);
1028 free(f->track[i]->stsc_samples_per_chunk);
1029 free(f->track[i]->stsc_sample_desc_index);
1030 free(f->track[i]->stco_chunk_offset);
1035 tag_delete(&(f->tags));
1039 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1040 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1042 int32_t total_entries = 0;
1043 int32_t chunk2entry;
1044 int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1048 if (f->track[track] == NULL) {
1052 total_entries = f->track[track]->stsc_entry_count;
1059 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1060 *chunk = chunk2 - chunk1;
1061 range_samples = *chunk * chunk1samples;
1063 if (sample < total + range_samples)
1066 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1069 if (chunk2entry < total_entries) {
1071 total += range_samples;
1073 } while (chunk2entry < total_entries);
1076 *chunk = (sample - total) / chunk1samples + chunk1;
1080 *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1085 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1088 const struct mp4_track *p_track = f->track[track];
1090 if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1091 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1093 } else if (p_track->stco_entry_count) {
1094 return p_track->stco_chunk_offset[chunk - 1];
1102 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1103 int32_t chunk_sample, int32_t sample)
1106 const struct mp4_track *p_track = f->track[track];
1108 if (p_track->stsz_sample_size) {
1109 return (sample - chunk_sample) * p_track->stsz_sample_size;
1111 if (sample >= p_track->stsz_sample_count)
1114 for (i = chunk_sample, total = 0; i < sample; i++) {
1115 total += p_track->stsz_table[i];
1122 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1125 int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1127 chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1129 chunk_offset1 = chunk_to_offset(f, track, chunk);
1130 chunk_offset2 = chunk_offset1 + sample_range_size(f,
1131 track, chunk_sample, sample);
1132 return chunk_offset2;
1136 * Return the number of milliseconds of the given track.
1138 * \param f As returned by \ref mp4_open_read(), must not be NULL.
1139 * \param track Between zero and the value returned by \ref mp4_total_tracks().
1141 * The function returns zero if the audio file is of zero length or contains a
1142 * corrupt track header.
1144 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1146 const struct mp4_track *t = f->track[track];
1148 if (t->timeScale == 0)
1150 return t->duration * 1000 / t->timeScale;
1154 * Check whether the given track number corresponds to an audio track.
1156 * \param f See \ref mp4_get_duration().
1157 * \param track See \ref mp4_get_duration().
1159 * Besides audio tracks, an mp4 file may contain video and system tracks. For
1160 * those the function returns false.
1162 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1164 return f->track[track]->is_audio;
1167 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1169 int32_t offset = sample_to_offset(f, track, sample);
1170 set_position(f, offset);
1173 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1175 const struct mp4_track *t = f->track[track];
1177 if (t->stsz_sample_size != 0)
1178 return t->stsz_sample_size;
1179 return t->stsz_table[sample];
1182 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1184 return f->track[track]->sampleRate;
1187 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1189 return f->track[track]->channelCount;
1192 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1197 for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1198 total += f->track[track]->stts_sample_count[i];
1203 struct mp4 *mp4_open_meta(const struct mp4_callback *cb)
1205 struct mp4 *f = para_calloc(sizeof(struct mp4));
1216 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1218 return f->tags.count;
1221 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1222 char **item, char **value)
1224 if (index >= f->tags.count) {
1229 *item = para_strdup(f->tags.tags[index].item);
1230 *value = para_strdup(f->tags.tags[index].value);
1235 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1238 uint32_t remaining = size;
1239 uint64_t atom_offset = base;
1244 set_position(f, atom_offset);
1248 atom_size = read_int32(f);
1249 if (atom_size > remaining || atom_size < 8)
1251 read_data(f, atom_name, 4);
1253 if (!memcmp(atom_name, name, 4)) {
1254 set_position(f, atom_offset);
1258 remaining -= atom_size;
1259 atom_offset += atom_size;
1264 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1265 const char *name, uint32_t extraheaders, const char *name_inside)
1267 uint64_t first_base = (uint64_t) (-1);
1268 while (find_atom(f, base, size, name)) //try to find atom <name> with atom <name_inside> in it
1270 uint64_t mybase = get_position(f);
1271 uint32_t mysize = read_int32(f);
1273 if (first_base == (uint64_t) (-1))
1274 first_base = mybase;
1276 if (mysize < 8 + extraheaders)
1279 if (find_atom (f, mybase + (8 + extraheaders),
1280 mysize - (8 + extraheaders), name_inside)) {
1281 set_position(f, mybase);
1285 if (size <= mysize) {
1292 if (first_base != (uint64_t) (-1)) //wanted atom inside not found
1294 set_position(f, first_base);
1307 static struct membuffer *membuffer_create(void)
1309 const unsigned initial_size = 256;
1311 struct membuffer *buf = para_malloc(sizeof(*buf));
1312 buf->data = para_malloc(initial_size);
1314 buf->allocated = initial_size;
1315 buf->error = buf->data == 0 ? 1 : 0;
1320 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1322 unsigned dest_size = buf->written + bytes;
1326 if (dest_size > buf->allocated) {
1328 buf->allocated <<= 1;
1329 } while (dest_size > buf->allocated);
1330 buf->data = para_realloc(buf->data, buf->allocated);
1334 memcpy((char *) buf->data + buf->written, ptr, bytes);
1335 buf->written += bytes;
1339 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1341 return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1344 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1348 write_u16_be(temp, data);
1349 return membuffer_write(buf, temp, 2);
1352 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1355 write_u32_be(temp, data);
1356 return membuffer_write(buf, temp, 4);
1359 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1360 uint32_t index, uint32_t total)
1362 membuffer_write_int32(buf,
1363 8 /*atom header */ + 8 /*data atom header */ +
1364 8 /*flags + reserved */ + 8 /*actual data */ );
1365 membuffer_write_atom_name(buf, name);
1366 membuffer_write_int32(buf,
1367 8 /*data atom header */ +
1368 8 /*flags + reserved */ + 8 /*actual data */ );
1369 membuffer_write_atom_name(buf, "data");
1370 membuffer_write_int32(buf, 0); //flags
1371 membuffer_write_int32(buf, 0); //reserved
1372 membuffer_write_int16(buf, 0);
1373 membuffer_write_int16(buf, (uint16_t) index); //track number
1374 membuffer_write_int16(buf, (uint16_t) total); //total tracks
1375 membuffer_write_int16(buf, 0);
1378 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1381 membuffer_write_int32(buf,
1382 8 /*atom header */ + 8 /*data atom header */ +
1383 8 /*flags + reserved */ + 2 /*actual data */ );
1384 membuffer_write_atom_name(buf, name);
1385 membuffer_write_int32(buf,
1386 8 /*data atom header */ +
1387 8 /*flags + reserved */ + 2 /*actual data */ );
1388 membuffer_write_atom_name(buf, "data");
1389 membuffer_write_int32(buf, 0); //flags
1390 membuffer_write_int32(buf, 0); //reserved
1391 membuffer_write_int16(buf, value); //value
1394 static uint32_t myatoi(const char *param)
1396 return param ? atoi(param) : 0;
1399 static uint32_t meta_genre_to_index(const char *genrestr)
1402 for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1403 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1409 struct stdmeta_entry {
1414 struct stdmeta_entry stdmetas[] = {
1415 {"\xA9" "nam", "title"},
1416 {"\xA9" "ART", "artist"},
1417 {"\xA9" "wrt", "writer"},
1418 {"\xA9" "alb", "album"},
1419 {"\xA9" "day", "date"},
1420 {"\xA9" "too", "tool"},
1421 {"\xA9" "cmt", "comment"},
1422 {"cpil", "compilation"},
1424 {"aART", "album_artist"},
1427 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1430 for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1431 if (!strcasecmp(name, stdmetas[n].name))
1432 return stdmetas[n].atom;
1437 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1442 /* special check for compilation flag */
1443 if (strcmp(name, "cpil") == 0) {
1447 membuffer_write_int32(buf,
1448 8 /*atom header */ + 8 /*data atom header */ +
1449 8 /*flags + reserved */ + strlen(value));
1450 membuffer_write_atom_name(buf, name);
1451 membuffer_write_int32(buf,
1452 8 /*data atom header */ +
1453 8 /*flags + reserved */ + strlen(value));
1454 membuffer_write_atom_name(buf, "data");
1455 membuffer_write_int32(buf, flags); //flags
1456 membuffer_write_int32(buf, 0); //reserved
1457 membuffer_write(buf, value, strlen(value));
1460 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1463 membuffer_write_int32(buf,
1464 8 /*atom header */ +
1465 0x1C /*weirdo itunes atom */ +
1466 12 /*name atom header */ + strlen(name) +
1467 16 /*data atom header + flags */ + strlen(value));
1468 membuffer_write_atom_name(buf, "----");
1469 membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
1470 membuffer_write_atom_name(buf, "mean");
1471 membuffer_write_int32(buf, 0);
1472 membuffer_write(buf, "com.apple.iTunes", 16);
1473 membuffer_write_int32(buf, 12 + strlen(name));
1474 membuffer_write_atom_name(buf, "name");
1475 membuffer_write_int32(buf, 0);
1476 membuffer_write(buf, name, strlen(name));
1477 membuffer_write_int32(buf,
1478 8 /*data atom header */ +
1479 8 /*flags + reserved */ + strlen(value));
1480 membuffer_write_atom_name(buf, "data");
1481 membuffer_write_int32(buf, 1); //flags
1482 membuffer_write_int32(buf, 0); //reserved
1483 membuffer_write(buf, value, strlen(value));
1486 static unsigned membuffer_error(const struct membuffer *buf)
1491 static void membuffer_free(struct membuffer *buf)
1497 static unsigned membuffer_get_size(const struct membuffer *buf)
1499 return buf->written;
1502 static void *membuffer_detach(struct membuffer *buf)
1508 ret = para_realloc(buf->data, buf->written);
1514 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1515 uint32_t * out_size)
1517 struct membuffer *buf = membuffer_create();
1519 char *mask = para_calloc(data->count);
1520 const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1521 const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1522 const char *genre_ptr = 0, *tempo_ptr = 0;
1524 for (metaptr = 0; metaptr < data->count; metaptr++) {
1525 struct mp4_tag *tag = &data->tags[metaptr];
1526 if (!strcasecmp(tag->item, "tracknumber")
1527 || !strcasecmp(tag->item, "track")) {
1528 if (tracknumber_ptr == 0)
1529 tracknumber_ptr = tag->value;
1531 } else if (!strcasecmp(tag->item, "totaltracks")) {
1532 if (totaltracks_ptr == 0)
1533 totaltracks_ptr = tag->value;
1535 } else if (!strcasecmp(tag->item, "discnumber")
1536 || !strcasecmp(tag->item, "disc")) {
1537 if (discnumber_ptr == 0)
1538 discnumber_ptr = tag->value;
1540 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1541 if (totaldiscs_ptr == 0)
1542 totaldiscs_ptr = tag->value;
1544 } else if (!strcasecmp(tag->item, "genre")) {
1546 genre_ptr = tag->value;
1548 } else if (!strcasecmp(tag->item, "tempo")) {
1550 tempo_ptr = tag->value;
1555 if (tracknumber_ptr)
1556 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1557 myatoi(totaltracks_ptr));
1559 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1560 myatoi(totaldiscs_ptr));
1562 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1565 uint32_t index = meta_genre_to_index(genre_ptr);
1567 membuffer_write_std_tag(buf, "©gen", genre_ptr);
1569 membuffer_write_int16_tag(buf, "gnre", index);
1571 for (metaptr = 0; metaptr < data->count; metaptr++) {
1572 struct mp4_tag *tag;
1573 const char *std_meta_atom;
1577 tag = &data->tags[metaptr];
1578 std_meta_atom = find_standard_meta(tag->item);
1580 membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1582 membuffer_write_custom_tag(buf, tag->item, tag->value);
1586 if (membuffer_error(buf)) {
1587 membuffer_free(buf);
1591 *out_size = membuffer_get_size(buf);
1592 *out_buffer = membuffer_detach(buf);
1593 membuffer_free(buf);
1598 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1601 membuffer_write_int32(buf, size + 8);
1602 membuffer_write_atom_name(buf, name);
1603 membuffer_write(buf, data, size);
1606 static void *membuffer_get_ptr(const struct membuffer *buf)
1611 static void membuffer_set_error(struct membuffer *buf)
1616 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1622 oldsize = membuffer_get_size(buf);
1623 if (membuffer_write(buf, 0, bytes) != bytes)
1626 bufptr = membuffer_get_ptr(buf);
1630 if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1632 membuffer_set_error(buf);
1639 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1640 uint32_t * out_size)
1642 struct membuffer *buf;
1646 if (!create_ilst(data, &ilst_buffer, &ilst_size))
1649 buf = membuffer_create();
1651 membuffer_write_int32(buf, 0);
1652 membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1655 *out_size = membuffer_get_size(buf);
1656 *out_buffer = membuffer_detach(buf);
1657 membuffer_free(buf);
1661 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1662 uint32_t * out_size)
1664 struct membuffer *buf;
1668 if (!create_meta(data, &meta_buffer, &meta_size))
1671 buf = membuffer_create();
1673 membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1677 *out_size = membuffer_get_size(buf);
1678 *out_buffer = membuffer_detach(buf);
1679 membuffer_free(buf);
1683 static uint32_t fix_byte_order_32(uint32_t src)
1685 return read_u32_be(&src);
1688 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1689 void **out_buffer, uint32_t * out_size)
1691 uint64_t total_base = f->moov_offset + 8;
1692 uint32_t total_size = (uint32_t) (f->moov_size - 8);
1694 uint64_t udta_offset, meta_offset, ilst_offset;
1695 uint32_t udta_size, meta_size, ilst_size;
1697 uint32_t new_ilst_size;
1698 void *new_ilst_buffer;
1703 if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1704 struct membuffer *buf;
1705 void *new_udta_buffer;
1706 uint32_t new_udta_size;
1707 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1710 buf = membuffer_create();
1711 set_position(f, total_base);
1712 membuffer_transfer_from_file(buf, f, total_size);
1714 membuffer_write_atom(buf, "udta", new_udta_size,
1717 free(new_udta_buffer);
1719 *out_size = membuffer_get_size(buf);
1720 *out_buffer = membuffer_detach(buf);
1721 membuffer_free(buf);
1724 udta_offset = get_position(f);
1725 udta_size = read_int32(f);
1726 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1727 struct membuffer *buf;
1728 void *new_meta_buffer;
1729 uint32_t new_meta_size;
1730 if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1733 buf = membuffer_create();
1734 set_position(f, total_base);
1735 membuffer_transfer_from_file(buf, f,
1736 (uint32_t)(udta_offset - total_base));
1738 membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1739 membuffer_write_atom_name(buf, "udta");
1740 membuffer_transfer_from_file(buf, f, udta_size);
1742 membuffer_write_atom(buf, "meta", new_meta_size,
1744 free(new_meta_buffer);
1746 *out_size = membuffer_get_size(buf);
1747 *out_buffer = membuffer_detach(buf);
1748 membuffer_free(buf);
1751 meta_offset = get_position(f);
1752 meta_size = read_int32(f);
1753 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1754 return 0; //shouldn't happen, find_atom_v2 above takes care of it
1755 ilst_offset = get_position(f);
1756 ilst_size = read_int32(f);
1758 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1761 size_delta = new_ilst_size - (ilst_size - 8);
1763 *out_size = total_size + size_delta;
1764 *out_buffer = para_malloc(*out_size);
1765 p_out = (uint8_t *) * out_buffer;
1767 set_position(f, total_base);
1769 (uint32_t) (udta_offset - total_base));
1770 p_out += (uint32_t) (udta_offset - total_base);
1771 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1773 read_data(f, p_out, 4);
1776 (uint32_t) (meta_offset - udta_offset - 8));
1777 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1778 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1780 read_data(f, p_out, 4);
1783 (uint32_t) (ilst_offset - meta_offset - 8));
1784 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1785 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1787 read_data(f, p_out, 4);
1790 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1791 p_out += new_ilst_size;
1793 set_position(f, ilst_offset + ilst_size);
1794 read_data(f, p_out, (uint32_t) (total_size
1795 - (ilst_offset - total_base) - ilst_size));
1797 free(new_ilst_buffer);
1802 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1806 result = f->cb->write(f->cb->user_data, data, size);
1808 f->current_position += size;
1813 static int32_t write_int32(struct mp4 *f, uint32_t data)
1816 write_u32_be(temp, data);
1817 return write_data(f, temp, sizeof(temp));
1820 int32_t mp4_meta_update(const struct mp4_callback *cb,
1821 const struct mp4_metadata *data)
1823 void *new_moov_data;
1824 uint32_t new_moov_size;
1826 struct mp4 *f = para_calloc(sizeof(struct mp4));
1830 if (!modify_moov(f, data, &new_moov_data, &new_moov_size)) {
1835 /* copy moov atom to end of the file */
1836 if (f->last_atom != ATOM_MOOV) {
1837 char *free_data = "free";
1839 /* rename old moov to free */
1840 set_position(f, f->moov_offset + 4);
1841 write_data(f, free_data, 4);
1843 set_position(f, f->file_size);
1844 write_int32(f, new_moov_size + 8);
1845 write_data(f, "moov", 4);
1846 write_data(f, new_moov_data, new_moov_size);
1848 set_position(f, f->moov_offset);
1849 write_int32(f, new_moov_size + 8);
1850 write_data(f, "moov", 4);
1851 write_data(f, new_moov_data, new_moov_size);
1853 cb->truncate(cb->user_data);
1858 /* find a metadata item by name */
1859 /* returns 0 if item found, 1 if no such item */
1860 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1865 for (i = 0; i < f->tags.count; i++) {
1866 if (!strcasecmp(f->tags.tags[i].item, item)) {
1867 *value = para_strdup(f->tags.tags[i].value);
1878 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
1880 return meta_find_by_name(f, "artist", value);
1883 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
1885 return meta_find_by_name(f, "title", value);
1888 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
1890 return meta_find_by_name(f, "date", value);
1893 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
1895 return meta_find_by_name(f, "album", value);
1898 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
1900 return meta_find_by_name(f, "comment", value);