]> git.tuebingen.mpg.de Git - paraslash.git/blob - mp4.c
Remove unused error code MOOD_SYNTAX.
[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 /** \file mp4.c Paraslash's internal mp4 parser. */
9
10 /*
11  * This is a stripped down version of the former mp4ff library which used to be
12  * part of the faad decoder project but was removed from the faad code base in
13  * 2017. The original code has been cleaned up substantially and the public API
14  * has been documented. See the git commit log for details.
15  */
16
17 #include <regex.h>
18
19 #include "para.h"
20 #include "error.h"
21 #include "portable_io.h"
22 #include "string.h"
23 #include "mp4.h"
24
25 /**
26  * The three states of the mp4 parser. The parser only loads the audio specific
27  * values and tables when it is in the second state.
28  */
29 enum audio_track_state {
30         /** We haven't encountered an mp4a atom so far. */
31         ATS_INITIAL,
32         /** We have seen an mp4a atom but no subsequent trak atom yet. */
33         ATS_SEEN_MP4A,
34         /** A trak atom was seen *after* the mp4a atom. */
35         ATS_TRACK_CHANGE,
36 };
37
38 struct mp4_track {
39         /* determines which atoms we still need to parse. */
40         enum audio_track_state state;
41
42         /* mp4a */
43         uint16_t channel_count;
44         uint16_t sample_rate;
45
46         /* stsz */
47         uint32_t stsz_sample_size;
48         uint32_t stsz_sample_count;
49         uint32_t *stsz_table;
50
51         /* stts */
52         uint32_t stts_entry_count;
53         uint32_t *stts_sample_count;
54
55         /* stsc */
56         uint32_t stsc_entry_count;
57         uint32_t *stsc_first_chunk;
58         uint32_t *stsc_samples_per_chunk;
59
60         /* stsc */
61         uint32_t stco_entry_count;
62         uint32_t *stco_chunk_offset;
63
64         /* mdhd */
65         uint32_t time_scale;
66         uint64_t duration;
67 };
68
69 struct mp4 {
70         const struct mp4_callback *cb;
71
72         uint64_t moov_offset;
73         uint64_t moov_size;
74         uint64_t meta_offset;
75         uint32_t meta_size;
76         uint64_t ilst_offset;
77         uint32_t ilst_size;
78         uint64_t udta_offset;
79         uint32_t udta_size;
80
81         uint8_t last_atom;
82         struct mp4_track track;
83         struct mp4_metadata meta;
84 };
85
86 /*
87  * Returns -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
88  * by EOF or read errors are treated as errors.
89  */
90 static int read_data(struct mp4 *f, void *data, size_t size)
91 {
92         while (size > 0) {
93                 ssize_t ret = f->cb->read(f->cb->user_data, data, size);
94                 if (ret < 0 && errno == EINTR)
95                         continue;
96                 /* regard EAGAIN as an error as reads should be blocking. */
97                 if (ret <= 0)
98                         return ret < 0? -E_MP4_READ : 0;
99                 size -= ret;
100         }
101         return 1;
102 }
103
104 static int read_int64(struct mp4 *f, uint64_t *result)
105 {
106         uint8_t data[8];
107         int ret = read_data(f, data, 8);
108
109         if (ret > 0)
110                 *result = read_u64_be(data);
111         return ret;
112 }
113
114 static int read_int32(struct mp4 *f, uint32_t *result)
115 {
116         uint8_t data[4];
117         int ret = read_data(f, data, 4);
118
119         if (ret > 0)
120                 *result = read_u32_be(data);
121         return ret;
122 }
123
124 static int read_int16(struct mp4 *f, uint16_t *result)
125 {
126         uint8_t data[2];
127         int ret = read_data(f, data, 2);
128
129         if (ret > 0)
130                 *result = read_u16_be(data);
131         return ret;
132 }
133
134 /** A macro defining the atoms we care about. It gets expanded twice. */
135 #define ATOM_ITEMS \
136         ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
137         ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
138         ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
139         ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
140         ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
141         ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
142         ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
143         ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
144         ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
145         ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
146         ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
147         ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
148         ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
149         ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
150         ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
151         ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
152         ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
153         ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
154         ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
155         ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
156         ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
157
158 /** For the C enumeration we concatenate ATOM_ with the first argument. */
159 #define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
160 /** The enumeration of interesting atoms. */
161 enum atom {ATOM_ITEMS};
162 #undef ATOM_ITEM
163
164 /** A cpp version of read_u32_be(). */
165 #define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
166
167 static uint8_t atom_name_to_type(uint8_t *p)
168 {
169         /** Expands to an instance of the following unnamed structure. */
170         #define ATOM_ITEM(_name, a, b, c, d) \
171                 {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
172         static const struct {
173                 const char *name;
174                 uint32_t val;
175         } atom_table[] = {ATOM_ITEMS};
176         #undef ATOM_ITEM
177         uint32_t val = read_u32_be(p);
178
179         for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
180                 if (val == atom_table[n].val)
181                         return n;
182         return 255;
183 }
184
185 /* read atom header, atom size is returned with header included. */
186 static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
187                 uint8_t *header_size, uint64_t *atom_size)
188 {
189         uint32_t size;
190         int ret;
191         uint8_t atom_header[8];
192
193         ret = read_data(f, atom_header, 8);
194         if (ret <= 0)
195                 return ret;
196         size = read_u32_be(atom_header);
197         if (size == 1) { /* 64 bit atom size */
198                 if (header_size)
199                         *header_size = 16;
200                 ret = read_int64(f, atom_size);
201                 if (ret <= 0)
202                         return ret;
203         } else {
204                 if (header_size)
205                         *header_size = 8;
206                 *atom_size = size;
207         }
208         *atom_type = atom_name_to_type(atom_header + 4);
209         return 1;
210 }
211
212 static off_t get_position(const struct mp4 *f)
213 {
214         return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
215 }
216
217 static void set_position(struct mp4 *f, off_t position)
218 {
219         f->cb->seek(f->cb->user_data, position, SEEK_SET);
220 }
221
222 static void skip_bytes(struct mp4 *f, off_t num_skip)
223 {
224         f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
225 }
226
227 static int read_stsz(struct mp4 *f)
228 {
229         int ret;
230         struct mp4_track *t = &f->track;
231
232         if (t->state != ATS_SEEN_MP4A || t->stsz_table)
233                 return 1;
234         skip_bytes(f, 4); /* version (1), flags (3) */
235         ret = read_int32(f, &t->stsz_sample_size);
236         if (ret <= 0)
237                 return ret;
238         ret = read_int32(f, &t->stsz_sample_count);
239         if (ret <= 0)
240                 return ret;
241         if (t->stsz_sample_size != 0)
242                 return 1;
243         t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
244         for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
245                 ret = read_int32(f, &t->stsz_table[n]);
246                 if (ret <= 0)
247                         return ret;
248         }
249         return 1;
250 }
251
252 static int read_stts(struct mp4 *f)
253 {
254         int ret;
255         struct mp4_track *t = &f->track;
256
257         if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
258                 return 1;
259         skip_bytes(f, 4); /* version (1), flags (3) */
260         ret = read_int32(f, &t->stts_entry_count);
261         if (ret <= 0)
262                 return ret;
263         t->stts_sample_count = para_malloc(t->stts_entry_count
264                 * sizeof(int32_t));
265         for (uint32_t n = 0; n < t->stts_entry_count; n++) {
266                 ret = read_int32(f, &t->stts_sample_count[n]);
267                 if (ret <= 0)
268                         return ret;
269                 skip_bytes(f, 4); /* sample delta */
270         }
271         return 1;
272 }
273
274 static int read_stsc(struct mp4 *f)
275 {
276         int ret;
277         struct mp4_track *t = &f->track;
278
279         if (t->state != ATS_SEEN_MP4A)
280                 return 1;
281         if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
282                 return 1;
283         skip_bytes(f, 4); /* version (1), flags (3) */
284         ret = read_int32(f, &t->stsc_entry_count);
285         if (ret <= 0)
286                 return ret;
287         t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
288         t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
289                 * sizeof (int32_t));
290         for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
291                 ret = read_int32(f, &t->stsc_first_chunk[n]);
292                 if (ret <= 0)
293                         return ret;
294                 ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
295                 if (ret <= 0)
296                         return ret;
297                 skip_bytes(f, 4); /* sample desc index */
298         }
299         return 1;
300 }
301
302 static int read_stco(struct mp4 *f)
303 {
304         int ret;
305         struct mp4_track *t = &f->track;
306
307         if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
308                 return 1;
309         skip_bytes(f, 4); /* version (1), flags (3) */
310         ret = read_int32(f, &t->stco_entry_count);
311         if (ret <= 0)
312                 return ret;
313         t->stco_chunk_offset = para_malloc(t->stco_entry_count
314                 * sizeof(int32_t));
315         for (uint32_t n = 0; n < t->stco_entry_count; n++) {
316                 ret = read_int32(f, &t->stco_chunk_offset[n]);
317                 if (ret <= 0)
318                         return ret;
319         }
320         return 1;
321 }
322
323 static int read_stsd(struct mp4 *f)
324 {
325         int ret;
326         uint32_t entry_count;
327
328         if (f->track.state != ATS_INITIAL)
329                 return 1;
330         skip_bytes(f, 4); /* version (1), flags (3) */
331         ret = read_int32(f, &entry_count);
332         if (ret <= 0)
333                 return ret;
334         for (uint32_t n = 0; n < entry_count; n++) {
335                 uint64_t skip = get_position(f);
336                 uint64_t size;
337                 uint8_t atom_type = 0;
338                 ret = atom_read_header(f, &atom_type, NULL, &size);
339                 if (ret <= 0)
340                         return ret;
341                 skip += size;
342                 if (atom_type == ATOM_MP4A) {
343                         f->track.state = ATS_SEEN_MP4A;
344                         /* reserved (6), data reference index (2), reserved (8) */
345                         skip_bytes(f, 16);
346                         ret = read_int16(f, &f->track.channel_count);
347                         if (ret <= 0)
348                                 return ret;
349                         skip_bytes(f, 6);
350                         ret = read_int16(f, &f->track.sample_rate);
351                         if (ret <= 0)
352                                 return ret;
353                 }
354                 set_position(f, skip);
355         }
356         return 1;
357 }
358
359 static const char *get_metadata_name(uint8_t atom_type)
360 {
361         switch (atom_type) {
362         case ATOM_TITLE: return "title";
363         case ATOM_ARTIST: return "artist";
364         case ATOM_ALBUM: return "album";
365         case ATOM_DATE: return "date";
366         case ATOM_COMMENT: return "comment";
367         default: return "unknown";
368         }
369 }
370
371 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
372 {
373         int ret;
374         uint64_t subsize, sumsize;
375         char *value = NULL;
376         uint32_t len = 0;
377         uint64_t destpos;
378         struct mp4_tag *tag;
379
380         for (
381                 sumsize = 0;
382                 sumsize < size;
383                 set_position(f, destpos), sumsize += subsize
384         ) {
385                 uint8_t atom_type;
386                 uint8_t header_size = 0;
387                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
388                 if (ret <= 0)
389                         goto fail;
390                 destpos = get_position(f) + subsize - header_size;
391                 if (atom_type != ATOM_DATA)
392                         continue;
393                 skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
394                 ret = -E_MP4_CORRUPT;
395                 if (subsize < header_size + 8 || subsize > UINT_MAX)
396                         goto fail;
397                 len = subsize - (header_size + 8);
398                 free(value);
399                 value = para_malloc(len + 1);
400                 ret = read_data(f, value, len);
401                 if (ret <= 0)
402                         goto fail;
403                 value[len] = '\0';
404         }
405         if (!value)
406                 return -E_MP4_CORRUPT;
407         f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
408                 * sizeof(struct mp4_tag));
409         tag = f->meta.tags + f->meta.count;
410         tag->item = para_strdup(get_metadata_name(parent));
411         tag->value = value;
412         f->meta.count++;
413         return 1;
414 fail:
415         free(value);
416         return ret;
417 }
418
419 static int read_mdhd(struct mp4 *f)
420 {
421         int ret;
422         uint32_t version;
423         struct mp4_track *t = &f->track;
424
425         if (t->state != ATS_INITIAL)
426                 return 1;
427         ret = read_int32(f, &version);
428         if (ret <= 0)
429                 return ret;
430         if (version == 1) {
431                 skip_bytes(f, 16); /* creation time (8), modification time (8) */
432                 ret = read_int32(f, &t->time_scale);
433                 if (ret <= 0)
434                         return ret;
435                 ret = read_int64(f, &t->duration);
436                 if (ret <= 0)
437                         return ret;
438         } else { /* version == 0 */
439                 uint32_t temp;
440
441                 skip_bytes(f, 8); /* creation time (4), modification time (4) */
442                 ret = read_int32(f, &t->time_scale);
443                 if (ret <= 0)
444                         return ret;
445                 ret = read_int32(f, &temp);
446                 if (ret <= 0)
447                         return ret;
448                 t->duration = (temp == (uint32_t) (-1))?
449                         (uint64_t) (-1) : (uint64_t) (temp);
450         }
451         skip_bytes(f, 4);
452         return 1;
453 }
454
455 static int read_ilst(struct mp4 *f, int32_t size)
456 {
457         int ret;
458         uint64_t sumsize = 0;
459
460         while (sumsize < size) {
461                 uint8_t atom_type;
462                 uint64_t subsize, destpos;
463                 uint8_t header_size = 0;
464                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
465                 if (ret <= 0)
466                         return ret;
467                 destpos = get_position(f) + subsize - header_size;
468                 switch (atom_type) {
469                 case ATOM_ARTIST:
470                 case ATOM_TITLE:
471                 case ATOM_ALBUM:
472                 case ATOM_COMMENT:
473                 case ATOM_DATE:
474                         ret = parse_tag(f, atom_type, subsize - header_size);
475                         if (ret <= 0)
476                                 return ret;
477                 }
478                 set_position(f, destpos);
479                 sumsize += subsize;
480         }
481         return 1;
482 }
483
484 static int read_meta(struct mp4 *f, uint64_t size)
485 {
486         int ret;
487         uint64_t subsize, sumsize = 0;
488         uint8_t atom_type;
489         uint8_t header_size = 0;
490
491         skip_bytes(f, 4); /* version (1), flags (3) */
492         while (sumsize < (size - (header_size + 4))) {
493                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
494                 if (ret <= 0)
495                         return ret;
496                 if (subsize <= header_size + 4)
497                         return 1;
498                 if (atom_type == ATOM_ILST) {
499                         f->ilst_offset = get_position(f) - header_size;
500                         f->ilst_size = subsize;
501                         ret = read_ilst(f, subsize - (header_size + 4));
502                         if (ret <= 0)
503                                 return ret;
504                 } else
505                         set_position(f, get_position(f) + subsize - header_size);
506                 sumsize += subsize;
507         }
508         return 1;
509 }
510
511 static bool need_atom(uint8_t atom_type, bool meta_only)
512 {
513         /* these are needed in any case */
514         switch (atom_type) {
515         case ATOM_STSD:
516         case ATOM_META:
517         case ATOM_TRAK:
518         case ATOM_MDIA:
519         case ATOM_MINF:
520         case ATOM_STBL:
521         case ATOM_UDTA:
522                 return true;
523         }
524         /* meta-only opens don't need anything else */
525         if (meta_only)
526                 return false;
527         /* these are only required for regular opens */
528         switch (atom_type) {
529         case ATOM_STTS:
530         case ATOM_STSZ:
531         case ATOM_STCO:
532         case ATOM_STSC:
533         case ATOM_MDHD:
534                 return true;
535         }
536         return false;
537 }
538
539 /* parse atoms that are sub atoms of other atoms */
540 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
541 {
542         int ret;
543         uint64_t dest, size, end = get_position(f) + total_size;
544
545         for (dest = get_position(f); dest < end; set_position(f, dest)) {
546                 uint8_t header_size, atom_type;
547                 ret = atom_read_header(f, &atom_type, &header_size, &size);
548                 if (ret <= 0)
549                         return ret;
550                 if (size == 0)
551                         return -E_MP4_CORRUPT;
552                 dest = get_position(f) + size - header_size;
553                 if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
554                         f->track.state = ATS_TRACK_CHANGE;
555                         continue;
556                 }
557                 if (atom_type == ATOM_UDTA) {
558                         f->udta_offset = get_position(f) - header_size;
559                         f->udta_size = size;
560                 }
561                 if (!need_atom(atom_type, meta_only))
562                         continue;
563                 switch (atom_type) {
564                 case ATOM_STSZ: ret = read_stsz(f); break;
565                 case ATOM_STTS: ret = read_stts(f); break;
566                 case ATOM_STSC: ret = read_stsc(f); break;
567                 case ATOM_STCO: ret = read_stco(f); break;
568                 case ATOM_STSD: ret = read_stsd(f); break;
569                 case ATOM_MDHD: ret = read_mdhd(f); break;
570                 case ATOM_META:
571                         f->meta_offset = get_position(f) - header_size;
572                         f->meta_size = size;
573                         ret = read_meta(f, size);
574                         break;
575                 default:
576                         ret = parse_sub_atoms(f, size - header_size, meta_only);
577                 }
578                 if (ret <= 0)
579                         return ret;
580         }
581         return 1;
582 }
583
584 /**
585  * Deallocate all resources associated with an mp4 file handle.
586  *
587  * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
588  *
589  * This frees the metadata items and various tables which were allocated when
590  * the file was opened. The given file handle must not be NULL.
591  */
592 void mp4_close(struct mp4 *f)
593 {
594         free(f->track.stsz_table);
595         free(f->track.stts_sample_count);
596         free(f->track.stsc_first_chunk);
597         free(f->track.stsc_samples_per_chunk);
598         free(f->track.stco_chunk_offset);
599         for (uint32_t n = 0; n < f->meta.count; n++) {
600                 free(f->meta.tags[n].item);
601                 free(f->meta.tags[n].value);
602         }
603         free(f->meta.tags);
604         free(f);
605 }
606
607 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
608 {
609         int ret;
610         uint64_t size;
611         uint8_t atom_type, header_size;
612         struct mp4 *f = para_calloc(sizeof(*f));
613
614         f->cb = cb;
615         while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
616                 f->last_atom = atom_type;
617                 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
618                         set_position(f, get_position(f) + size - header_size);
619                         continue;
620                 }
621                 f->moov_offset = get_position(f) - header_size;
622                 f->moov_size = size;
623                 ret = parse_sub_atoms(f, size - header_size, meta_only);
624                 if (ret <= 0)
625                         break;
626         }
627         if (ret < 0)
628                 goto fail;
629         ret = -E_MP4_TRACK;
630         if (f->track.channel_count == 0)
631                 goto fail;
632         ret = -E_MP4_BAD_SAMPLERATE;
633         if (f->track.sample_rate == 0)
634                 goto fail;
635         ret = -E_MP4_MISSING_ATOM;
636         if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
637                 goto fail;
638         *result = f;
639         return 1;
640 fail:
641         *result = NULL;
642         mp4_close(f);
643         return ret;
644 }
645
646 /**
647  * Read the audio track and the metadata of an mp4 file.
648  *
649  * \param cb Only the ->read() and ->seek() methods need to be supplied.
650  * \param result Initialized to a non-NULL pointer iff the function succeeds.
651  *
652  * This detects and parses the first audio track and the metadata information
653  * of the mp4 file. Various error checks are performed after the mp4 atoms have
654  * been parsed successfully.
655  *
656  * This function does not modify the file. However, if the caller intents to
657  * update the metadata later, the ->write() and ->truncate() methods must be
658  * supplied in the callback structure.
659  *
660  * \return Standard. Several errors are possible.
661  *
662  * \sa \ref mp4_open_meta().
663  */
664 int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
665 {
666         struct mp4 *f;
667         int ret;
668
669         *result = NULL;
670         ret = open_file(cb, false, &f);
671         if (ret < 0)
672                 return ret;
673         ret = -E_MP4_BAD_SAMPLE_COUNT;
674         if (f->track.stsz_sample_count == 0)
675                 goto fail;
676         ret = -E_MP4_CORRUPT;
677         if (f->track.time_scale == 0)
678                 goto fail;
679         *result = f;
680         return 1;
681 fail:
682         mp4_close(f);
683         return ret;
684 }
685
686 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
687                 int32_t *chunk)
688 {
689         const struct mp4_track *t = &f->track;
690         uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
691         uint32_t chunk1, chunk1samples, n, total, k;
692
693         for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
694                 n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
695                 if (sample < total + n)
696                         break;
697         }
698         chunk1 = fc[k - 1];
699         chunk1samples = spc[k - 1];
700         if (chunk1samples != 0)
701                 *chunk = (sample - total) / chunk1samples + chunk1;
702         else
703                 *chunk = 1;
704         return total + (*chunk - chunk1) * chunk1samples;
705 }
706
707 /**
708  * Compute the duration of an mp4 file.
709  *
710  * \param f See \ref mp4_close().
711  *
712  * \return The number of milliseconds of the audio track. This function never
713  * fails.
714  */
715 uint64_t mp4_get_duration(const struct mp4 *f)
716 {
717         const struct mp4_track *t = &f->track;
718
719         return t->duration * 1000 / t->time_scale;
720 }
721
722 /**
723  * Reposition the read/write file offset.
724  *
725  * \param f See \ref mp4_close().
726  * \param sample The number of the sample to reposition to.
727  *
728  * The given sample number must be within range, i.e., strictly less than the
729  * value returned by \ref mp4_num_samples().
730  *
731  * \return Standard. The only possible error is an invalid sample number.
732  */
733 int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
734 {
735         const struct mp4_track *t = &f->track;
736         int32_t offset, chunk, chunk_sample;
737         uint32_t n, srs; /* sample range size */
738
739         if (sample >= t->stsz_sample_count)
740                 return -ERRNO_TO_PARA_ERROR(EINVAL);
741         chunk_sample = chunk_of_sample(f, sample, &chunk);
742         if (t->stsz_sample_size > 0)
743                 srs = (sample - chunk_sample) * t->stsz_sample_size;
744         else {
745                 for (srs = 0, n = chunk_sample; n < sample; n++)
746                         srs += t->stsz_table[n];
747         }
748         if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
749                 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
750         else if (t->stco_entry_count > 0)
751                 offset = t->stco_chunk_offset[chunk - 1];
752         else
753                 offset = 8;
754         set_position(f, offset + srs);
755         return 1;
756 }
757
758 /**
759  * Look up and return the size of the given sample in the stsz table.
760  *
761  * \param f See \ref mp4_close().
762  * \param sample The sample number of interest.
763  * \param result Sample size is returned here.
764  *
765  * For the sample argument the restriction mentioned in the documentation of
766  * \ref mp4_set_sample_position() applies as well.
767  *
768  * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
769  * only possible error.
770  */
771 int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
772 {
773         const struct mp4_track *t = &f->track;
774
775         if (sample >= t->stsz_sample_count)
776                 return -ERRNO_TO_PARA_ERROR(EINVAL);
777         if (t->stsz_sample_size != 0)
778                 *result = t->stsz_sample_size;
779         else
780                 *result = t->stsz_table[sample];
781         return 1;
782 }
783
784 /**
785  * Return the sample rate stored in the stsd atom.
786  *
787  * \param f See \ref mp4_close().
788  *
789  * The sample rate is a property of the audio track of the mp4 file and is thus
790  * independent of the sample number.
791  *
792  * \return The function always returns a positive value because the open
793  * operation fails if the sample rate happens to be zero. A typical value is
794  * 44100.
795  */
796 uint16_t mp4_get_sample_rate(const struct mp4 *f)
797 {
798         return f->track.sample_rate;
799 }
800
801 /**
802  * Return the number of channels of the audio track.
803  *
804  * \param f See \ref mp4_close().
805  *
806  * \return The returned channel count is guaranteed to be positive because the
807  * open operation fails if the mp4a atom is missing or contains a zero channel
808  * count.
809  */
810 uint16_t mp4_get_channel_count(const struct mp4 *f)
811 {
812         return f->track.channel_count;
813 }
814
815 /**
816  * Return the number of samples of the audio track.
817  *
818  * \param f See \ref mp4_close().
819  *
820  * \return The sample count is read from the stsz atom during open.
821  */
822 uint32_t mp4_num_samples(const struct mp4 *f)
823 {
824         return f->track.stsz_sample_count;
825 }
826
827 /**
828  * Open an mp4 file in metadata-only mode.
829  *
830  * \param cb See \ref mp4_open().
831  * \param result See \ref mp4_open().
832  *
833  * This is similar to \ref mp4_open() but is cheaper because it only parses the
834  * metadata of the mp4 file. The only functions that can subsequently be called
835  * with the file handle returned here are \ref mp4_get_meta() and \ref
836  * mp4_update_meta().
837  *
838  * \return Standard.
839  *
840  * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
841  * this function as well.
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         *result = f;
851         return 1;
852 }
853
854 /**
855  * Return the metadata of an mp4 file.
856  *
857  * \param f See \ref mp4_close().
858  *
859  * The caller is allowed to add, delete or modify the entries of the returned
860  * structure with the intention to pass the modified version to \ref
861  * mp4_update_meta().
862  *
863  * \return This never returns NULL, even if the file contains no metadata tag
864  * items. However, the meta count will be zero and the ->tags pointer NULL in
865  * this case.
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 = 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                 size -= ret;
983         }
984         return 1;
985 }
986
987 /**
988  * Write back the modified metadata items to the mp4 file.
989  *
990  * This is the only public function which modifies the contents of an mp4 file.
991  * This is achieved by calling the ->write() and ->truncate() methods of the
992  * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
993  *
994  * \param f See \ref mp4_close().
995  *
996  * The modified metadata structure does not need to be supplied to this
997  * function because it is part of the mp4 structure.
998  *
999  * \return Standard.
1000  */
1001 int mp4_update_meta(struct mp4 *f)
1002 {
1003         void *new_moov_data;
1004         uint32_t new_moov_size;
1005         uint8_t buf[8] = "----moov";
1006         int ret;
1007
1008         set_position(f, 0);
1009         new_moov_data = modify_moov(f, &new_moov_size);
1010         if (!new_moov_data ) {
1011                 mp4_close(f);
1012                 return 0;
1013         }
1014         if (f->last_atom != ATOM_MOOV) {
1015                 set_position(f, f->moov_offset + 4);
1016                 ret = write_data(f, "free", 4); /* rename old moov to free */
1017                 if (ret < 0)
1018                         goto free_moov;
1019                 /* write new moov atom at EOF */
1020                 f->cb->seek(f->cb->user_data, 0, SEEK_END);
1021         } else /* overwrite old moov atom */
1022                 set_position(f, f->moov_offset);
1023         write_u32_be(buf, new_moov_size + 8);
1024         ret = write_data(f, buf, sizeof(buf));
1025         if (ret < 0)
1026                 goto free_moov;
1027         ret = write_data(f, new_moov_data, new_moov_size);
1028         if (ret < 0)
1029                 goto free_moov;
1030         ret = f->cb->truncate(f->cb->user_data);
1031         if (ret < 0)
1032                 ret = -ERRNO_TO_PARA_ERROR(errno);
1033 free_moov:
1034         free(new_moov_data);
1035         return ret;
1036 }
1037
1038 /**
1039  * Return the value of the given tag item.
1040  *
1041  * \param f See \ref mp4_close().
1042  * \param item "artist", "title", "album", "comment", or "date".
1043  *
1044  * \return The function returns NULL if the given item is not in the above
1045  * list. Otherwise, if the file does not contain a tag for the given item, the
1046  * function also returns NULL. Otherwise a copy of the tag value is returned
1047  * and the caller should free this memory when it is no longer needed.
1048  */
1049 char *mp4_get_tag_value(const struct mp4 *f, const char *item)
1050 {
1051         for (unsigned n = 0; n < f->meta.count; n++)
1052                 if (!strcasecmp(f->meta.tags[n].item, item))
1053                         return para_strdup(f->meta.tags[n].value);
1054         return NULL;
1055 }