]> git.tuebingen.mpg.de Git - paraslash.git/blob - mp4.c
mp4: Merge parse_leaf_atom() into parse_sub_atoms().
[paraslash.git] / mp4.c
1 /*
2  * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
3  * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
4  *
5  * See file COPYING.
6  */
7
8 #include <regex.h>
9
10 #include "para.h"
11 #include "error.h"
12 #include "portable_io.h"
13 #include "string.h"
14 #include "mp4.h"
15
16 struct mp4_track {
17         uint16_t channelCount;
18         uint16_t sampleRate;
19
20         /* stsz */
21         uint32_t stsz_sample_size;
22         uint32_t stsz_sample_count;
23         uint32_t *stsz_table;
24
25         /* stts */
26         uint32_t stts_entry_count;
27         uint32_t *stts_sample_count;
28
29         /* stsc */
30         uint32_t stsc_entry_count;
31         uint32_t *stsc_first_chunk;
32         uint32_t *stsc_samples_per_chunk;
33
34         /* stsc */
35         uint32_t stco_entry_count;
36         uint32_t *stco_chunk_offset;
37
38         uint32_t timeScale;
39         uint64_t duration;
40 };
41
42 #define MAX_TRACKS 1024
43
44 struct mp4 {
45         const struct mp4_callback *cb;
46         int64_t current_position;
47
48         uint64_t moov_offset;
49         uint64_t moov_size;
50         uint64_t meta_offset;
51         uint32_t meta_size;
52         uint64_t ilst_offset;
53         uint32_t ilst_size;
54         uint64_t udta_offset;
55         uint32_t udta_size;
56
57         uint8_t last_atom;
58         uint64_t file_size;
59
60         /* incremental track index while reading the file */
61         int32_t total_tracks;
62         /* track data */
63         struct mp4_track *track[MAX_TRACKS];
64         /* the first audio track found */
65         struct mp4_track *audio_track;
66
67         /* metadata */
68         struct mp4_metadata meta;
69 };
70
71 /*
72  * Returns -1, 0, or 1 on errors/EOF/success. Partial reads followed by EOF or
73  * read errors are treated as errors.
74  */
75 static int read_data(struct mp4 *f, void *data, size_t size)
76 {
77         while (size > 0) {
78                 ssize_t ret = f->cb->read(f->cb->user_data, data, size);
79                 if (ret < 0 && errno == EINTR)
80                         continue;
81                 /* regard EAGAIN as an error as reads should be blocking. */
82                 if (ret <= 0)
83                         return ret < 0? -1 : 0;
84                 f->current_position += ret;
85                 size -= ret;
86         }
87         return 1;
88 }
89
90 static int read_int64(struct mp4 *f, uint64_t *result)
91 {
92         uint8_t data[8];
93         int ret = read_data(f, data, 8);
94
95         if (ret > 0 && result)
96                 *result = read_u64_be(data);
97         return ret;
98 }
99
100 static int read_int32(struct mp4 *f, uint32_t *result)
101 {
102         uint8_t data[4];
103         int ret = read_data(f, data, 4);
104
105         if (ret > 0 && result)
106                 *result = read_u32_be(data);
107         return ret;
108 }
109
110 static int read_int24(struct mp4 *f, uint32_t *result)
111 {
112         uint8_t data[3];
113         int ret = read_data(f, data, 3);
114
115         if (ret > 0 && result)
116                 *result = read_u24_be(data);
117         return ret;
118 }
119
120 static int read_int16(struct mp4 *f, uint16_t *result)
121 {
122         uint8_t data[2];
123         int ret = read_data(f, data, 2);
124
125         if (ret > 0 && result)
126                 *result = read_u16_be(data);
127         return ret;
128 }
129
130 static uint8_t read_int8(struct mp4 *f, uint8_t *result)
131 {
132         uint8_t data[1];
133         int ret = read_data(f, data, 1);
134
135         if (ret > 0 && result)
136                 *result = data[0];
137         return ret;
138 }
139
140 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
141                 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
142 {
143         return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
144 }
145
146 enum atoms {
147         /* atoms with subatoms */
148         ATOM_MOOV,
149         ATOM_TRAK,
150         ATOM_MDIA,
151         ATOM_MINF,
152         ATOM_STBL,
153         ATOM_UDTA,
154         ATOM_ILST, /* iTunes Metadata list */
155         ATOM_TITLE,
156         ATOM_ARTIST,
157         ATOM_ALBUM,
158         ATOM_DATE,
159         ATOM_COMMENT,
160
161         /* atoms without subatoms */
162         ATOM_MDHD, /* track header */
163         ATOM_STSD, /* sample description box */
164         ATOM_STTS, /* time to sample box */
165         ATOM_STSZ, /* sample size box */
166         ATOM_STCO, /* chunk offset box */
167         ATOM_STSC, /* sample to chunk box */
168         ATOM_MP4A,
169         ATOM_META, /* iTunes Metadata box */
170         ATOM_DATA, /* iTunes Metadata data box */
171         ATOM_UNKNOWN = 255
172 };
173
174 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
175
176 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
177 {
178         if (a == 'm') {
179                 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
180                         return ATOM_MOOV;
181                 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
182                         return ATOM_MINF;
183                 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
184                         return ATOM_MDIA;
185                 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
186                         return ATOM_MDHD;
187                 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
188                         return ATOM_MP4A;
189                 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
190                         return ATOM_META;
191         } else if (a == 't') {
192                 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
193                         return ATOM_TRAK;
194         } else if (a == 's') {
195                 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
196                         return ATOM_STBL;
197                 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
198                         return ATOM_STSD;
199                 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
200                         return ATOM_STTS;
201                 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
202                         return ATOM_STCO;
203                 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
204                         return ATOM_STSC;
205                 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
206                         return ATOM_STSZ;
207         } else if (a == COPYRIGHT_SYMBOL) {
208                 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
209                         return ATOM_TITLE;
210                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
211                         return ATOM_ARTIST;
212                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
213                         return ATOM_ALBUM;
214                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
215                         return ATOM_DATE;
216                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
217                         return ATOM_COMMENT;
218         }
219         if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
220                 return ATOM_UDTA;
221         else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
222                 return ATOM_ILST;
223         else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
224                 return ATOM_DATA;
225         else
226                 return ATOM_UNKNOWN;
227 }
228
229 /* read atom header, atom size is returned with header included. */
230 static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
231                 uint8_t *header_size, uint64_t *atom_size)
232 {
233         uint32_t size;
234         int ret;
235         int8_t atom_header[8];
236
237         ret = read_data(f, atom_header, 8);
238         if (ret <= 0)
239                 return ret;
240         size = read_u32_be(atom_header);
241         if (size == 1) { /* 64 bit atom size */
242                 if (header_size)
243                         *header_size = 16;
244                 ret = read_int64(f, atom_size);
245                 if (ret <= 0)
246                         return ret;
247         } else {
248                 if (header_size)
249                         *header_size = 8;
250                 if (atom_size)
251                         *atom_size = size;
252         }
253         *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
254                 atom_header[6], atom_header[7]);
255         return 1;
256 }
257
258 static int64_t get_position(const struct mp4 *f)
259 {
260         return f->current_position;
261 }
262
263 static int32_t set_position(struct mp4 *f, int64_t position)
264 {
265         f->cb->seek(f->cb->user_data, position);
266         f->current_position = position;
267
268         return 0;
269 }
270
271 static int read_stsz(struct mp4 *f)
272 {
273         int ret;
274         int32_t i;
275         struct mp4_track *t;
276
277         if (f->total_tracks == 0)
278                 return -1;
279         t = f->track[f->total_tracks - 1];
280         ret = read_int8(f, NULL); /* version */
281         if (ret <= 0)
282                 return ret;
283         ret = read_int24(f, NULL); /* flags */
284         if (ret <= 0)
285                 return ret;
286         ret = read_int32(f, &t->stsz_sample_size);
287         if (ret <= 0)
288                 return ret;
289         ret = read_int32(f, &t->stsz_sample_count);
290         if (ret <= 0)
291                 return ret;
292         if (t->stsz_sample_size != 0)
293                 return 1;
294         t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
295         for (i = 0; i < t->stsz_sample_count; i++) {
296                 ret = read_int32(f, &t->stsz_table[i]);
297                 if (ret <= 0)
298                         return ret;
299         }
300         return 1;
301 }
302
303 static int read_stts(struct mp4 *f)
304 {
305         int ret;
306         int32_t i;
307         struct mp4_track *t;
308
309         if (f->total_tracks == 0)
310                 return -1;
311         t = f->track[f->total_tracks - 1];
312         if (t->stts_entry_count)
313                 return 0;
314         ret = read_int8(f, NULL); /* version */
315         if (ret <= 0)
316                 return ret;
317         ret = read_int24(f, NULL); /* flags */
318         if (ret <= 0)
319                 return ret;
320         ret = read_int32(f, &t->stts_entry_count);
321         if (ret <= 0)
322                 return ret;
323         t->stts_sample_count = para_malloc(t->stts_entry_count
324                 * sizeof(int32_t));
325         for (i = 0; i < t->stts_entry_count; i++) {
326                 ret = read_int32(f, &t->stts_sample_count[i]);
327                 if (ret <= 0)
328                         return ret;
329                 ret = read_int32(f, NULL); /* sample delta */
330                 if (ret <= 0)
331                         return ret;
332         }
333         return 1;
334 }
335
336 static int read_stsc(struct mp4 *f)
337 {
338         int ret;
339         int32_t i;
340         struct mp4_track *t;
341
342         if (f->total_tracks == 0)
343                 return -1;
344         t = f->track[f->total_tracks - 1];
345
346         ret = read_int8(f, NULL); /* version */
347         if (ret <= 0)
348                 return ret;
349         ret = read_int24(f, NULL); /* flags */
350         if (ret <= 0)
351                 return ret;
352         ret = read_int32(f, &t->stsc_entry_count);
353         if (ret <= 0)
354                 return ret;
355         t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
356         t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
357                 * sizeof (int32_t));
358         for (i = 0; i < t->stsc_entry_count; i++) {
359                 ret = read_int32(f, &t->stsc_first_chunk[i]);
360                 if (ret <= 0)
361                         return ret;
362                 ret = read_int32(f, &t->stsc_samples_per_chunk[i]);
363                 if (ret <= 0)
364                         return ret;
365                 ret = read_int32(f, NULL); /* sample desc index */
366                 if (ret <= 0)
367                         return ret;
368         }
369         return 1;
370 }
371
372 static int read_stco(struct mp4 *f)
373 {
374         int ret;
375         int32_t i;
376         struct mp4_track *t;
377
378         if (f->total_tracks == 0)
379                 return -1;
380         t = f->track[f->total_tracks - 1];
381
382         ret = read_int8(f, NULL); /* version */
383         if (ret <= 0)
384                 return ret;
385         ret = read_int24(f, NULL); /* flags */
386         if (ret <= 0)
387                 return ret;
388         ret = read_int32(f, &t->stco_entry_count);
389         if (ret <= 0)
390                 return ret;
391         t->stco_chunk_offset = para_malloc(t->stco_entry_count
392                 * sizeof(int32_t));
393         for (i = 0; i < t->stco_entry_count; i++) {
394                 ret = read_int32(f, &t->stco_chunk_offset[i]);
395                 if (ret <= 0)
396                         return ret;
397         }
398         return 1;
399 }
400
401 static int read_mp4a(struct mp4 *f)
402 {
403         int ret;
404         int32_t i;
405         struct mp4_track *t;
406
407         if (f->total_tracks == 0)
408                 return -1;
409         t = f->track[f->total_tracks - 1];
410
411         for (i = 0; i < 6; i++) {
412                 ret = read_int8(f, NULL); /* reserved */
413                 if (ret <= 0)
414                         return ret;
415         }
416         ret = read_int16(f, NULL); /* data_reference_index */
417         if (ret <= 0)
418                 return ret;
419         ret = read_int32(f, NULL); /* reserved */
420         if (ret <= 0)
421                 return ret;
422         ret = read_int32(f, NULL); /* reserved */
423         if (ret <= 0)
424                 return ret;
425         ret = read_int16(f, &t->channelCount);
426         if (ret <= 0)
427                 return ret;
428         ret = read_int16(f, NULL);
429         if (ret <= 0)
430                 return ret;
431         ret = read_int16(f, NULL);
432         if (ret <= 0)
433                 return ret;
434         ret = read_int16(f, NULL);
435         if (ret <= 0)
436                 return ret;
437         return read_int16(f, &t->sampleRate);
438 }
439
440 static int read_stsd(struct mp4 *f)
441 {
442         int ret;
443         uint32_t i, entry_count;
444         struct mp4_track *t;
445
446         if (f->total_tracks == 0)
447                 return -1;
448         t = f->track[f->total_tracks - 1];
449         ret = read_int8(f, NULL); /* version */
450         if (ret <= 0)
451                 return ret;
452         ret = read_int24(f, NULL); /* flags */
453         if (ret <= 0)
454                 return ret;
455         ret = read_int32(f, &entry_count);
456         if (ret <= 0)
457                 return ret;
458         for (i = 0; i < entry_count; i++) {
459                 uint64_t skip = get_position(f);
460                 uint64_t size;
461                 uint8_t atom_type = 0;
462                 ret = atom_read_header(f, &atom_type, NULL, &size);
463                 if (ret <= 0)
464                         return ret;
465                 skip += size;
466                 if (!f->audio_track && atom_type == ATOM_MP4A) {
467                         f->audio_track = t;
468                         read_mp4a(f);
469                 }
470                 set_position(f, skip);
471         }
472         return 1;
473 }
474
475 static const char *get_metadata_name(uint8_t atom_type)
476 {
477         switch (atom_type) {
478         case ATOM_TITLE: return "title";
479         case ATOM_ARTIST: return "artist";
480         case ATOM_ALBUM: return "album";
481         case ATOM_DATE: return "date";
482         case ATOM_COMMENT: return "comment";
483         default: return "unknown";
484         }
485 }
486
487 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
488 {
489         int ret;
490         uint64_t subsize, sumsize;
491         char *value = NULL;
492         uint32_t len = 0;
493         uint64_t destpos;
494         struct mp4_tag *tag;
495
496         for (
497                 sumsize = 0;
498                 sumsize < size;
499                 set_position(f, destpos), sumsize += subsize
500         ) {
501                 uint8_t atom_type;
502                 uint8_t header_size = 0;
503                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
504                 if (ret <= 0)
505                         goto fail;
506                 destpos = get_position(f) + subsize - header_size;
507                 if (atom_type != ATOM_DATA)
508                         continue;
509                 ret = read_int8(f, NULL); /* version */
510                 if (ret <= 0)
511                         goto fail;
512                 ret = read_int24(f, NULL); /* flags */
513                 if (ret <= 0)
514                         goto fail;
515                 ret = read_int32(f, NULL); /* reserved */
516                 if (ret <= 0)
517                         goto fail;
518                 ret = -ERRNO_TO_PARA_ERROR(EINVAL);
519                 if (subsize < header_size + 8 || subsize > UINT_MAX)
520                         goto fail;
521                 len = subsize - (header_size + 8);
522                 free(value);
523                 value = para_malloc(len + 1);
524                 ret = read_data(f, value, len);
525                 if (ret <= 0)
526                         goto fail;
527                 value[len] = '\0';
528         }
529         if (!value)
530                 return -ERRNO_TO_PARA_ERROR(EINVAL);
531         f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
532                 * sizeof(struct mp4_tag));
533         tag = f->meta.tags + f->meta.count;
534         tag->item = para_strdup(get_metadata_name(parent));
535         tag->value = value;
536         tag->len = len;
537         f->meta.count++;
538         return 1;
539 fail:
540         free(value);
541         return ret;
542 }
543
544 static int read_mdhd(struct mp4 *f)
545 {
546         int ret;
547         uint32_t version;
548         struct mp4_track *t;
549
550         if (f->total_tracks == 0)
551                 return -1;
552         t = f->track[f->total_tracks - 1];
553
554         ret = read_int32(f, &version);
555         if (ret <= 0)
556                 return ret;
557         if (version == 1) {
558                 ret = read_int64(f, NULL); /* creation-time */
559                 if (ret <= 0)
560                         return ret;
561                 ret = read_int64(f, NULL); /* modification-time */
562                 if (ret <= 0)
563                         return ret;
564                 ret = read_int32(f, &t->timeScale);
565                 if (ret <= 0)
566                         return ret;
567                 ret = read_int64(f, &t->duration);
568                 if (ret <= 0)
569                         return ret;
570         } else { //version == 0
571                 uint32_t temp;
572
573                 ret = read_int32(f, NULL); /* creation-time */
574                 if (ret <= 0)
575                         return ret;
576                 ret = read_int32(f, NULL); /* modification-time */
577                 if (ret <= 0)
578                         return ret;
579                 ret = read_int32(f, &t->timeScale);
580                 if (ret <= 0)
581                         return ret;
582                 ret = read_int32(f, &temp);
583                 if (ret <= 0)
584                         return ret;
585                 t->duration = (temp == (uint32_t) (-1))?
586                         (uint64_t) (-1) : (uint64_t) (temp);
587         }
588         ret = read_int16(f, NULL);
589         if (ret <= 0)
590                 return ret;
591         ret = read_int16(f, NULL);
592         if (ret <= 0)
593                 return ret;
594         return 1;
595 }
596
597 static int32_t read_ilst(struct mp4 *f, int32_t size)
598 {
599         int ret;
600         uint64_t sumsize = 0;
601
602         while (sumsize < size) {
603                 uint8_t atom_type;
604                 uint64_t subsize, destpos;
605                 uint8_t header_size = 0;
606                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
607                 if (ret <= 0)
608                         return ret;
609                 destpos = get_position(f) + subsize - header_size;
610                 switch (atom_type) {
611                 case ATOM_ARTIST:
612                 case ATOM_TITLE:
613                 case ATOM_ALBUM:
614                 case ATOM_COMMENT:
615                 case ATOM_DATE:
616                         ret = parse_tag(f, atom_type, subsize - header_size);
617                         if (ret <= 0)
618                                 return ret;
619                 }
620                 set_position(f, destpos);
621                 sumsize += subsize;
622         }
623         return 1;
624 }
625
626 static int32_t read_meta(struct mp4 *f, uint64_t size)
627 {
628         int ret;
629         uint64_t subsize, sumsize = 0;
630         uint8_t atom_type;
631         uint8_t header_size = 0;
632
633         ret = read_int8(f, NULL); /* version */
634         if (ret <= 0)
635                 return ret;
636         ret = read_int24(f, NULL); /* flags */
637         if (ret <= 0)
638                 return ret;
639         while (sumsize < (size - (header_size + 4))) {
640                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
641                 if (ret <= 0)
642                         return ret;
643                 if (subsize <= header_size + 4)
644                         return 1;
645                 if (atom_type == ATOM_ILST) {
646                         f->ilst_offset = get_position(f) - header_size;
647                         f->ilst_size = subsize;
648                         ret = read_ilst(f, subsize - (header_size + 4));
649                         if (ret <= 0)
650                                 return ret;
651                 } else
652                         set_position(f, get_position(f) + subsize - header_size);
653                 sumsize += subsize;
654         }
655         return 1;
656 }
657
658 static bool need_atom(uint8_t atom_type, bool meta_only)
659 {
660         /* these are needed in any case */
661         switch (atom_type) {
662         case ATOM_STSD:
663         case ATOM_META:
664         case ATOM_TRAK:
665         case ATOM_MDIA:
666         case ATOM_MINF:
667         case ATOM_STBL:
668         case ATOM_UDTA:
669                 return true;
670         }
671         /* meta-only opens don't need anything else */
672         if (meta_only)
673                 return false;
674         /* these are only required for regular opens */
675         switch (atom_type) {
676         case ATOM_STTS:
677         case ATOM_STSZ:
678         case ATOM_STCO:
679         case ATOM_STSC:
680         case ATOM_MDHD:
681                 return true;
682         }
683         return false;
684 }
685
686 /* parse atoms that are sub atoms of other atoms */
687 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
688 {
689         int ret;
690         uint64_t dest, size, end = get_position(f) + total_size;
691
692         for (dest = get_position(f); dest < end; set_position(f, dest)) {
693                 uint8_t header_size, atom_type;
694                 ret = atom_read_header(f, &atom_type, &header_size, &size);
695                 if (ret <= 0)
696                         return ret;
697                 if (size == 0)
698                         return -1;
699                 dest = get_position(f) + size - header_size;
700                 if (atom_type == ATOM_TRAK) {
701                         if (f->total_tracks >= MAX_TRACKS)
702                                 return -1;
703                         f->total_tracks++;
704                         f->track[f->total_tracks - 1] = para_calloc(
705                                 sizeof(struct mp4_track));
706                 } else if (atom_type == ATOM_UDTA) {
707                         f->udta_offset = get_position(f) - header_size;
708                         f->udta_size = size;
709                 }
710                 if (!need_atom(atom_type, meta_only))
711                         continue;
712                 switch (atom_type) {
713                 case ATOM_STSZ: ret = read_stsz(f); break;
714                 case ATOM_STTS: ret = read_stts(f); break;
715                 case ATOM_STSC: ret = read_stsc(f); break;
716                 case ATOM_STCO: ret = read_stco(f); break;
717                 case ATOM_STSD: ret = read_stsd(f); break;
718                 case ATOM_MDHD: ret = read_mdhd(f); break;
719                 case ATOM_META:
720                         f->meta_offset = get_position(f) - header_size;
721                         f->meta_size = size;
722                         ret = read_meta(f, size);
723                         break;
724                 default:
725                         ret = parse_sub_atoms(f, size - header_size, meta_only);
726                 }
727                 if (ret <= 0)
728                         return ret;
729         }
730         return 1;
731 }
732
733 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
734 {
735         int ret;
736         uint64_t size;
737         uint8_t atom_type, header_size;
738         struct mp4 *f = para_calloc(sizeof(*f));
739
740         f->cb = cb;
741         while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
742                 f->file_size += size;
743                 f->last_atom = atom_type;
744                 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
745                         set_position(f, get_position(f) + size - header_size);
746                         continue;
747                 }
748                 f->moov_offset = get_position(f) - header_size;
749                 f->moov_size = size;
750                 ret = parse_sub_atoms(f, size - header_size, meta_only);
751                 if (ret <= 0)
752                         break;
753         }
754         if (ret < 0) {
755                 ret = -E_MP4_OPEN;
756                 goto fail;
757         }
758         ret = -E_MP4_TRACK;
759         if (!f->audio_track)
760                 goto fail;
761         *result = f;
762         return 1;
763 fail:
764         *result = NULL;
765         free(f);
766         return ret;
767 }
768
769 int mp4_open_read(const struct mp4_callback *cb, struct mp4 **result)
770 {
771         return open_file(cb, false, result);
772 }
773
774 void mp4_close(struct mp4 *f)
775 {
776         int32_t i;
777
778         for (i = 0; i < f->total_tracks; i++) {
779                 if (f->track[i]) {
780                         free(f->track[i]->stsz_table);
781                         free(f->track[i]->stts_sample_count);
782                         free(f->track[i]->stsc_first_chunk);
783                         free(f->track[i]->stsc_samples_per_chunk);
784                         free(f->track[i]->stco_chunk_offset);
785                         free(f->track[i]);
786                 }
787         }
788         for (i = 0; i < f->meta.count; i++) {
789                 free(f->meta.tags[i].item);
790                 free(f->meta.tags[i].value);
791         }
792         free(f->meta.tags);
793         free(f);
794 }
795
796 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
797                 int32_t *chunk)
798 {
799         const struct mp4_track *t = f->audio_track;
800         uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
801         int32_t chunk1, chunk1samples, n, total, i;
802
803         for (i = 1, total = 0; i < t->stsc_entry_count; i++, total += n) {
804                 n = (fc[i] - fc[i - 1]) * spc[i - 1]; /* number of samples */
805                 if (sample < total + n)
806                         break;
807         }
808         chunk1 = fc[i - 1];
809         chunk1samples = spc[i - 1];
810         if (chunk1samples != 0)
811                 *chunk = (sample - total) / chunk1samples + chunk1;
812         else
813                 *chunk = 1;
814         return total + (*chunk - chunk1) * chunk1samples;
815 }
816
817 /**
818  * Return the number of milliseconds of the audio track.
819  *
820  * \param f As returned by \ref mp4_open_read(), must not be NULL.
821  */
822 uint64_t mp4_get_duration(const struct mp4 *f)
823 {
824         const struct mp4_track *t = f->audio_track;
825
826         if (t->timeScale == 0)
827                 return 0;
828         return t->duration * 1000 / t->timeScale;
829 }
830
831 int mp4_set_sample_position(struct mp4 *f, int32_t sample)
832 {
833         const struct mp4_track *t = f->audio_track;
834         int32_t offset, chunk, chunk_sample;
835         uint32_t n, srs; /* sample range size */
836
837         if (sample >= t->stsz_sample_count)
838                 return -ERRNO_TO_PARA_ERROR(EINVAL);
839         chunk_sample = chunk_of_sample(f, sample, &chunk);
840         if (t->stsz_sample_size > 0)
841                 srs = (sample - chunk_sample) * t->stsz_sample_size;
842         else {
843                 for (srs = 0, n = chunk_sample; n < sample; n++)
844                         srs += t->stsz_table[n];
845         }
846         if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
847                 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
848         else if (t->stco_entry_count > 0)
849                 offset = t->stco_chunk_offset[chunk - 1];
850         else
851                 offset = 8;
852         set_position(f, offset + srs);
853         return 1;
854 }
855
856 int32_t mp4_get_sample_size(const struct mp4 *f, int sample)
857 {
858         const struct mp4_track *t = f->audio_track;
859
860         if (t->stsz_sample_size != 0)
861                 return t->stsz_sample_size;
862         return t->stsz_table[sample];
863 }
864
865 uint32_t mp4_get_sample_rate(const struct mp4 *f)
866 {
867         return f->audio_track->sampleRate;
868 }
869
870 uint32_t mp4_get_channel_count(const struct mp4 *f)
871 {
872         return f->audio_track->channelCount ;
873 }
874
875 int32_t mp4_num_samples(const struct mp4 *f)
876 {
877         const struct mp4_track *t = f->audio_track;
878         int32_t i;
879         int32_t total = 0;
880
881         for (i = 0; i < t->stts_entry_count; i++)
882                 total += t->stts_sample_count[i];
883         return total;
884 }
885
886 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
887 {
888         struct mp4 *f;
889         int ret = open_file(cb, true, &f);
890
891         if (ret < 0)
892                 return ret;
893         if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0) {
894                 mp4_close(f);
895                 *result = NULL;
896                 return -E_MP4_MISSING_ATOM;
897         }
898         *result = f;
899         return 1;
900 }
901
902 /**
903  * Return the metadata of an mp4 file.
904  *
905  * \param f As returned by either \ref mp4_open_read() or \ref mp4_open_meta().
906  *
907  * The caller is allowed to add, delete or modify the entries of the returned
908  * structure in order to pass the modified version to \ref mp4_meta_update().
909  */
910 struct mp4_metadata *mp4_get_meta(struct mp4 *f)
911 {
912         return &f->meta;
913 }
914
915 /** Total length of an on-disk metadata tag. */
916 #define TAG_LEN(_len) (24 + (_len))
917 static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
918 {
919         for (unsigned n = 0; n < meta->count; n++) {
920                 struct mp4_tag *tag = meta->tags + n;
921                 unsigned len = strlen(tag->value);
922                 const char *atom_name;
923
924                 if (!strcasecmp(tag->item, "title"))
925                         atom_name = "\xA9" "nam";
926                 else if (!strcasecmp(tag->item, "artist"))
927                         atom_name = "\xA9" "ART";
928                 else if (!strcasecmp(tag->item, "album"))
929                         atom_name = "\xA9" "alb";
930                 else if (!strcasecmp(tag->item, "date"))
931                         atom_name = "\xA9" "day";
932                 else if (!strcasecmp(tag->item, "comment"))
933                         atom_name = "\xA9" "cmt";
934                 else
935                         assert(false);
936                 write_u32_be(out, TAG_LEN(len));
937                 memcpy(out + 4, atom_name, 4);
938                 write_u32_be(out + 8, 8 /* data atom header */
939                         + 8 /* flags + reserved */
940                         + len);
941                 memcpy(out + 12, "data", 4);
942                 write_u32_be(out + 16, 1); /* flags */
943                 write_u32_be(out + 20, 0); /* reserved */
944                 memcpy(out + 24, tag->value, len);
945                 out += TAG_LEN(len);
946         }
947 }
948
949 static uint32_t fix_byte_order_32(uint32_t src)
950 {
951         return read_u32_be(&src);
952 }
953
954 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
955 {
956         int ret;
957         uint64_t total_base = f->moov_offset + 8;
958         uint32_t total_size = (uint32_t) (f->moov_size - 8);
959         uint32_t new_ilst_size = 0;
960         void *out_buffer;
961         uint8_t *p_out;
962         int32_t size_delta;
963         uint32_t tmp;
964
965         for (unsigned n = 0; n < f->meta.count; n++)
966                 new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
967         size_delta = new_ilst_size - (f->ilst_size - 8);
968         *out_size = total_size + size_delta;
969         out_buffer = para_malloc(*out_size);
970         p_out = out_buffer;
971         set_position(f, total_base);
972         ret = read_data(f, p_out, f->udta_offset - total_base);
973         if (ret <= 0)
974                 return NULL;
975         p_out += f->udta_offset - total_base;
976         ret = read_int32(f, &tmp);
977         if (ret <= 0)
978                 return NULL;
979         *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
980         p_out += 4;
981         ret = read_data(f, p_out, 4);
982         if (ret <= 0)
983                 return NULL;
984         p_out += 4;
985         ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
986         if (ret <= 0)
987                 return NULL;
988         p_out += f->meta_offset - f->udta_offset - 8;
989         ret = read_int32(f, &tmp);
990         if (ret <= 0)
991                 return NULL;
992         *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
993         p_out += 4;
994         ret = read_data(f, p_out, 4);
995         if (ret <= 0)
996                 return NULL;
997         p_out += 4;
998         ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
999         if (ret <= 0)
1000                 return NULL;
1001         p_out += f->ilst_offset - f->meta_offset - 8;
1002         ret = read_int32(f, &tmp);
1003         if (ret <= 0)
1004                 return NULL;
1005         *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
1006         p_out += 4;
1007         ret = read_data(f, p_out, 4);
1008         if (ret <= 0)
1009                 return NULL;
1010         p_out += 4;
1011         create_ilst(&f->meta, p_out);
1012         p_out += new_ilst_size;
1013         set_position(f, f->ilst_offset + f->ilst_size);
1014         ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
1015                 - f->ilst_size);
1016         if (ret <= 0)
1017                 return NULL;
1018         return out_buffer;
1019 }
1020
1021 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1022 {
1023         int32_t result = 1;
1024
1025         result = f->cb->write(f->cb->user_data, data, size);
1026
1027         f->current_position += size;
1028
1029         return result;
1030 }
1031
1032 static int32_t write_int32(struct mp4 *f, uint32_t data)
1033 {
1034         int8_t temp[4];
1035         write_u32_be(temp, data);
1036         return write_data(f, temp, sizeof(temp));
1037 }
1038
1039 int32_t mp4_meta_update(struct mp4 *f)
1040 {
1041         void *new_moov_data;
1042         uint32_t new_moov_size;
1043
1044         set_position(f, 0);
1045         new_moov_data = modify_moov(f, &new_moov_size);
1046         if (!new_moov_data ) {
1047                 mp4_close(f);
1048                 return 0;
1049         }
1050         /* copy moov atom to end of the file */
1051         if (f->last_atom != ATOM_MOOV) {
1052                 char *free_data = "free";
1053
1054                 /* rename old moov to free */
1055                 set_position(f, f->moov_offset + 4);
1056                 write_data(f, free_data, 4);
1057
1058                 set_position(f, f->file_size);
1059                 write_int32(f, new_moov_size + 8);
1060                 write_data(f, "moov", 4);
1061                 write_data(f, new_moov_data, new_moov_size);
1062         } else {
1063                 set_position(f, f->moov_offset);
1064                 write_int32(f, new_moov_size + 8);
1065                 write_data(f, "moov", 4);
1066                 write_data(f, new_moov_data, new_moov_size);
1067         }
1068         free(new_moov_data);
1069         f->cb->truncate(f->cb->user_data);
1070         return 1;
1071 }
1072
1073 static char *meta_find_by_name(const struct mp4 *f, const char *item)
1074 {
1075         uint32_t i;
1076
1077         for (i = 0; i < f->meta.count; i++)
1078                 if (!strcasecmp(f->meta.tags[i].item, item))
1079                         return para_strdup(f->meta.tags[i].value);
1080         return NULL;
1081 }
1082
1083 /**
1084  * Return the value of the artist meta tag of an mp4 file.
1085  *
1086  * \param f Must not be NULL.
1087  *
1088  * \return If the file does not contain this metadata tag, the function returns
1089  * NULL. Otherwise, a copy of the tag value is returned. The caller should free
1090  * this memory when it is no longer needed.
1091  */
1092 char *mp4_meta_get_artist(const struct mp4 *f)
1093 {
1094         return meta_find_by_name(f, "artist");
1095 }
1096
1097 /**
1098  * Return the value of the title meta tag of an mp4 file.
1099  *
1100  * \param f See \ref mp4_meta_get_artist().
1101  * \return See \ref mp4_meta_get_artist().
1102  */
1103 char *mp4_meta_get_title(const struct mp4 *f)
1104 {
1105         return meta_find_by_name(f, "title");
1106 }
1107
1108 /**
1109  * Return the value of the date meta tag of an mp4 file.
1110  *
1111  * \param f See \ref mp4_meta_get_artist().
1112  * \return See \ref mp4_meta_get_artist().
1113  */
1114 char *mp4_meta_get_date(const struct mp4 *f)
1115 {
1116         return meta_find_by_name(f, "date");
1117 }
1118
1119 /**
1120  * Return the value of the album meta tag of an mp4 file.
1121  *
1122  * \param f See \ref mp4_meta_get_artist().
1123  * \return See \ref mp4_meta_get_artist().
1124  */
1125 char *mp4_meta_get_album(const struct mp4 *f)
1126 {
1127         return meta_find_by_name(f, "album");
1128 }
1129
1130 /**
1131  * Return the value of the comment meta tag of an mp4 file.
1132  *
1133  * \param f See \ref mp4_meta_get_artist().
1134  * \return See \ref mp4_meta_get_artist().
1135  */
1136 char *mp4_meta_get_comment(const struct mp4 *f)
1137 {
1138         return meta_find_by_name(f, "comment");
1139 }