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