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