]> git.tuebingen.mpg.de Git - paraslash.git/blob - mp4.c
prevent doxygen from generating invalid html.
[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 /** \cond atom_items */
135 /* A macro defining the atoms we care about. It gets expanded twice. */
136 #define ATOM_ITEMS \
137         ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
138         ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
139         ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
140         ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
141         ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
142         ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
143         ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
144         ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
145         ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
146         ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
147         ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
148         ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
149         ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
150         ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
151         ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
152         ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
153         ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
154         ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
155         ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
156         ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
157         ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
158
159 /** \endcond atom_items */
160
161 /** For the C enumeration we concatenate ATOM_ with the first argument. */
162 #define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
163 /** The enumeration of interesting atoms. */
164 enum atom {ATOM_ITEMS};
165 #undef ATOM_ITEM
166
167 /** A cpp version of read_u32_be(). */
168 #define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
169
170 static uint8_t atom_name_to_type(uint8_t *p)
171 {
172         /** Expands to an instance of the following unnamed structure. */
173         #define ATOM_ITEM(_name, a, b, c, d) \
174                 {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
175         static const struct {
176                 const char *name;
177                 uint32_t val;
178         } atom_table[] = {ATOM_ITEMS};
179         #undef ATOM_ITEM
180         uint32_t val = read_u32_be(p);
181
182         for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
183                 if (val == atom_table[n].val)
184                         return n;
185         return 255;
186 }
187
188 /* read atom header, atom size is returned with header included. */
189 static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
190                 uint8_t *header_size, uint64_t *atom_size)
191 {
192         uint32_t size;
193         int ret;
194         uint8_t atom_header[8];
195
196         ret = read_data(f, atom_header, 8);
197         if (ret <= 0)
198                 return ret;
199         size = read_u32_be(atom_header);
200         if (size == 1) { /* 64 bit atom size */
201                 if (header_size)
202                         *header_size = 16;
203                 ret = read_int64(f, atom_size);
204                 if (ret <= 0)
205                         return ret;
206         } else {
207                 if (header_size)
208                         *header_size = 8;
209                 *atom_size = size;
210         }
211         *atom_type = atom_name_to_type(atom_header + 4);
212         return 1;
213 }
214
215 static off_t get_position(const struct mp4 *f)
216 {
217         return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
218 }
219
220 static void set_position(struct mp4 *f, off_t position)
221 {
222         f->cb->seek(f->cb->user_data, position, SEEK_SET);
223 }
224
225 static void skip_bytes(struct mp4 *f, off_t num_skip)
226 {
227         f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
228 }
229
230 static int read_stsz(struct mp4 *f)
231 {
232         int ret;
233         struct mp4_track *t = &f->track;
234
235         if (t->state != ATS_SEEN_MP4A || t->stsz_table)
236                 return 1;
237         skip_bytes(f, 4); /* version (1), flags (3) */
238         ret = read_int32(f, &t->stsz_sample_size);
239         if (ret <= 0)
240                 return ret;
241         ret = read_int32(f, &t->stsz_sample_count);
242         if (ret <= 0)
243                 return ret;
244         if (t->stsz_sample_size != 0)
245                 return 1;
246         t->stsz_table = arr_alloc(t->stsz_sample_count, sizeof(int32_t));
247         for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
248                 ret = read_int32(f, &t->stsz_table[n]);
249                 if (ret <= 0)
250                         return ret;
251         }
252         return 1;
253 }
254
255 static int read_stts(struct mp4 *f)
256 {
257         int ret;
258         struct mp4_track *t = &f->track;
259
260         if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
261                 return 1;
262         skip_bytes(f, 4); /* version (1), flags (3) */
263         ret = read_int32(f, &t->stts_entry_count);
264         if (ret <= 0)
265                 return ret;
266         t->stts_sample_count = arr_alloc(t->stts_entry_count, sizeof(int32_t));
267         for (uint32_t n = 0; n < t->stts_entry_count; n++) {
268                 ret = read_int32(f, &t->stts_sample_count[n]);
269                 if (ret <= 0)
270                         return ret;
271                 skip_bytes(f, 4); /* sample delta */
272         }
273         return 1;
274 }
275
276 static int read_stsc(struct mp4 *f)
277 {
278         int ret;
279         struct mp4_track *t = &f->track;
280
281         if (t->state != ATS_SEEN_MP4A)
282                 return 1;
283         if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
284                 return 1;
285         skip_bytes(f, 4); /* version (1), flags (3) */
286         ret = read_int32(f, &t->stsc_entry_count);
287         if (ret <= 0)
288                 return ret;
289         t->stsc_first_chunk = arr_alloc(t->stsc_entry_count, sizeof(int32_t));
290         t->stsc_samples_per_chunk = arr_alloc(t->stsc_entry_count,
291                 sizeof (int32_t));
292         for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
293                 ret = read_int32(f, &t->stsc_first_chunk[n]);
294                 if (ret <= 0)
295                         return ret;
296                 ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
297                 if (ret <= 0)
298                         return ret;
299                 skip_bytes(f, 4); /* sample desc index */
300         }
301         return 1;
302 }
303
304 static int read_stco(struct mp4 *f)
305 {
306         int ret;
307         struct mp4_track *t = &f->track;
308
309         if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
310                 return 1;
311         skip_bytes(f, 4); /* version (1), flags (3) */
312         ret = read_int32(f, &t->stco_entry_count);
313         if (ret <= 0)
314                 return ret;
315         t->stco_chunk_offset = arr_alloc(t->stco_entry_count, sizeof(int32_t));
316         for (uint32_t n = 0; n < t->stco_entry_count; n++) {
317                 ret = read_int32(f, &t->stco_chunk_offset[n]);
318                 if (ret <= 0)
319                         return ret;
320         }
321         return 1;
322 }
323
324 static int read_stsd(struct mp4 *f)
325 {
326         int ret;
327         uint32_t entry_count;
328
329         if (f->track.state != ATS_INITIAL)
330                 return 1;
331         skip_bytes(f, 4); /* version (1), flags (3) */
332         ret = read_int32(f, &entry_count);
333         if (ret <= 0)
334                 return ret;
335         for (uint32_t n = 0; n < entry_count; n++) {
336                 uint64_t skip = get_position(f);
337                 uint64_t size;
338                 uint8_t atom_type = 0;
339                 ret = atom_read_header(f, &atom_type, NULL, &size);
340                 if (ret <= 0)
341                         return ret;
342                 skip += size;
343                 if (atom_type == ATOM_MP4A) {
344                         f->track.state = ATS_SEEN_MP4A;
345                         /* reserved (6), data reference index (2), reserved (8) */
346                         skip_bytes(f, 16);
347                         ret = read_int16(f, &f->track.channel_count);
348                         if (ret <= 0)
349                                 return ret;
350                         skip_bytes(f, 6);
351                         ret = read_int16(f, &f->track.sample_rate);
352                         if (ret <= 0)
353                                 return ret;
354                 }
355                 set_position(f, skip);
356         }
357         return 1;
358 }
359
360 static const char *get_metadata_name(uint8_t atom_type)
361 {
362         switch (atom_type) {
363         case ATOM_TITLE: return "title";
364         case ATOM_ARTIST: return "artist";
365         case ATOM_ALBUM: return "album";
366         case ATOM_DATE: return "date";
367         case ATOM_COMMENT: return "comment";
368         default: return "unknown";
369         }
370 }
371
372 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
373 {
374         int ret;
375         uint64_t subsize, sumsize;
376         char *value = NULL;
377         uint32_t len = 0;
378         uint64_t destpos;
379         struct mp4_tag *tag;
380
381         for (
382                 sumsize = 0;
383                 sumsize < size;
384                 set_position(f, destpos), sumsize += subsize
385         ) {
386                 uint8_t atom_type;
387                 uint8_t header_size = 0;
388                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
389                 if (ret <= 0)
390                         goto fail;
391                 destpos = get_position(f) + subsize - header_size;
392                 if (atom_type != ATOM_DATA)
393                         continue;
394                 skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
395                 ret = -E_MP4_CORRUPT;
396                 if (subsize < header_size + 8 || subsize > UINT_MAX)
397                         goto fail;
398                 len = subsize - (header_size + 8);
399                 free(value);
400                 value = alloc(len + 1);
401                 ret = read_data(f, value, len);
402                 if (ret <= 0)
403                         goto fail;
404                 value[len] = '\0';
405         }
406         if (!value)
407                 return -E_MP4_CORRUPT;
408         f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
409                 * sizeof(struct mp4_tag));
410         tag = f->meta.tags + f->meta.count;
411         tag->item = para_strdup(get_metadata_name(parent));
412         tag->value = value;
413         f->meta.count++;
414         return 1;
415 fail:
416         free(value);
417         return ret;
418 }
419
420 static int read_mdhd(struct mp4 *f)
421 {
422         int ret;
423         uint32_t version;
424         struct mp4_track *t = &f->track;
425
426         if (t->state != ATS_INITIAL)
427                 return 1;
428         ret = read_int32(f, &version);
429         if (ret <= 0)
430                 return ret;
431         if (version == 1) {
432                 skip_bytes(f, 16); /* creation time (8), modification time (8) */
433                 ret = read_int32(f, &t->time_scale);
434                 if (ret <= 0)
435                         return ret;
436                 ret = read_int64(f, &t->duration);
437                 if (ret <= 0)
438                         return ret;
439         } else { /* version == 0 */
440                 uint32_t temp;
441
442                 skip_bytes(f, 8); /* creation time (4), modification time (4) */
443                 ret = read_int32(f, &t->time_scale);
444                 if (ret <= 0)
445                         return ret;
446                 ret = read_int32(f, &temp);
447                 if (ret <= 0)
448                         return ret;
449                 t->duration = (temp == (uint32_t) (-1))?
450                         (uint64_t) (-1) : (uint64_t) (temp);
451         }
452         skip_bytes(f, 4);
453         return 1;
454 }
455
456 static int read_ilst(struct mp4 *f, int32_t size)
457 {
458         int ret;
459         uint64_t sumsize = 0;
460
461         while (sumsize < size) {
462                 uint8_t atom_type;
463                 uint64_t subsize, destpos;
464                 uint8_t header_size = 0;
465                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
466                 if (ret <= 0)
467                         return ret;
468                 destpos = get_position(f) + subsize - header_size;
469                 switch (atom_type) {
470                 case ATOM_ARTIST:
471                 case ATOM_TITLE:
472                 case ATOM_ALBUM:
473                 case ATOM_COMMENT:
474                 case ATOM_DATE:
475                         ret = parse_tag(f, atom_type, subsize - header_size);
476                         if (ret <= 0)
477                                 return ret;
478                 }
479                 set_position(f, destpos);
480                 sumsize += subsize;
481         }
482         return 1;
483 }
484
485 static int read_meta(struct mp4 *f, uint64_t size)
486 {
487         int ret;
488         uint64_t subsize, sumsize = 0;
489         uint8_t atom_type;
490         uint8_t header_size = 0;
491
492         skip_bytes(f, 4); /* version (1), flags (3) */
493         while (sumsize < (size - (header_size + 4))) {
494                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
495                 if (ret <= 0)
496                         return ret;
497                 if (subsize <= header_size + 4)
498                         return 1;
499                 if (atom_type == ATOM_ILST) {
500                         f->ilst_offset = get_position(f) - header_size;
501                         f->ilst_size = subsize;
502                         ret = read_ilst(f, subsize - (header_size + 4));
503                         if (ret <= 0)
504                                 return ret;
505                 } else
506                         set_position(f, get_position(f) + subsize - header_size);
507                 sumsize += subsize;
508         }
509         return 1;
510 }
511
512 static bool need_atom(uint8_t atom_type, bool meta_only)
513 {
514         /* these are needed in any case */
515         switch (atom_type) {
516         case ATOM_STSD:
517         case ATOM_META:
518         case ATOM_TRAK:
519         case ATOM_MDIA:
520         case ATOM_MINF:
521         case ATOM_STBL:
522         case ATOM_UDTA:
523                 return true;
524         }
525         /* meta-only opens don't need anything else */
526         if (meta_only)
527                 return false;
528         /* these are only required for regular opens */
529         switch (atom_type) {
530         case ATOM_STTS:
531         case ATOM_STSZ:
532         case ATOM_STCO:
533         case ATOM_STSC:
534         case ATOM_MDHD:
535                 return true;
536         }
537         return false;
538 }
539
540 /* parse atoms that are sub atoms of other atoms */
541 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
542 {
543         int ret;
544         uint64_t dest, size, end = get_position(f) + total_size;
545
546         for (dest = get_position(f); dest < end; set_position(f, dest)) {
547                 uint8_t header_size, atom_type;
548                 ret = atom_read_header(f, &atom_type, &header_size, &size);
549                 if (ret <= 0)
550                         return ret;
551                 if (size == 0)
552                         return -E_MP4_CORRUPT;
553                 dest = get_position(f) + size - header_size;
554                 if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
555                         f->track.state = ATS_TRACK_CHANGE;
556                         continue;
557                 }
558                 if (atom_type == ATOM_UDTA) {
559                         f->udta_offset = get_position(f) - header_size;
560                         f->udta_size = size;
561                 }
562                 if (!need_atom(atom_type, meta_only))
563                         continue;
564                 switch (atom_type) {
565                 case ATOM_STSZ: ret = read_stsz(f); break;
566                 case ATOM_STTS: ret = read_stts(f); break;
567                 case ATOM_STSC: ret = read_stsc(f); break;
568                 case ATOM_STCO: ret = read_stco(f); break;
569                 case ATOM_STSD: ret = read_stsd(f); break;
570                 case ATOM_MDHD: ret = read_mdhd(f); break;
571                 case ATOM_META:
572                         f->meta_offset = get_position(f) - header_size;
573                         f->meta_size = size;
574                         ret = read_meta(f, size);
575                         break;
576                 default:
577                         ret = parse_sub_atoms(f, size - header_size, meta_only);
578                 }
579                 if (ret <= 0)
580                         return ret;
581         }
582         return 1;
583 }
584
585 /**
586  * Deallocate all resources associated with an mp4 file handle.
587  *
588  * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
589  *
590  * This frees the metadata items and various tables which were allocated when
591  * the file was opened. The given file handle must not be NULL.
592  */
593 void mp4_close(struct mp4 *f)
594 {
595         free(f->track.stsz_table);
596         free(f->track.stts_sample_count);
597         free(f->track.stsc_first_chunk);
598         free(f->track.stsc_samples_per_chunk);
599         free(f->track.stco_chunk_offset);
600         for (uint32_t n = 0; n < f->meta.count; n++) {
601                 free(f->meta.tags[n].item);
602                 free(f->meta.tags[n].value);
603         }
604         free(f->meta.tags);
605         free(f);
606 }
607
608 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
609 {
610         int ret;
611         uint64_t size;
612         uint8_t atom_type, header_size;
613         struct mp4 *f = zalloc(sizeof(*f));
614
615         f->cb = cb;
616         while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
617                 f->last_atom = atom_type;
618                 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
619                         set_position(f, get_position(f) + size - header_size);
620                         continue;
621                 }
622                 f->moov_offset = get_position(f) - header_size;
623                 f->moov_size = size;
624                 ret = parse_sub_atoms(f, size - header_size, meta_only);
625                 if (ret <= 0)
626                         break;
627         }
628         if (ret < 0)
629                 goto fail;
630         ret = -E_MP4_TRACK;
631         if (f->track.channel_count == 0)
632                 goto fail;
633         ret = -E_MP4_BAD_SAMPLERATE;
634         if (f->track.sample_rate == 0)
635                 goto fail;
636         ret = -E_MP4_MISSING_ATOM;
637         if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
638                 goto fail;
639         *result = f;
640         return 1;
641 fail:
642         *result = NULL;
643         mp4_close(f);
644         return ret;
645 }
646
647 /**
648  * Read the audio track and the metadata of an mp4 file.
649  *
650  * \param cb Only the ->read() and ->seek() methods need to be supplied.
651  * \param result Initialized to a non-NULL pointer iff the function succeeds.
652  *
653  * This detects and parses the first audio track and the metadata information
654  * of the mp4 file. Various error checks are performed after the mp4 atoms have
655  * been parsed successfully.
656  *
657  * This function does not modify the file. However, if the caller intents to
658  * update the metadata later, the ->write() and ->truncate() methods must be
659  * supplied in the callback structure.
660  *
661  * \return Standard. Several errors are possible.
662  *
663  * \sa \ref mp4_open_meta().
664  */
665 int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
666 {
667         struct mp4 *f;
668         int ret;
669
670         *result = NULL;
671         ret = open_file(cb, false, &f);
672         if (ret < 0)
673                 return ret;
674         ret = -E_MP4_BAD_SAMPLE_COUNT;
675         if (f->track.stsz_sample_count == 0)
676                 goto fail;
677         ret = -E_MP4_CORRUPT;
678         if (f->track.time_scale == 0)
679                 goto fail;
680         *result = f;
681         return 1;
682 fail:
683         mp4_close(f);
684         return ret;
685 }
686
687 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
688                 int32_t *chunk)
689 {
690         const struct mp4_track *t = &f->track;
691         uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
692         uint32_t chunk1, chunk1samples, n, total, k;
693
694         for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
695                 n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
696                 if (sample < total + n)
697                         break;
698         }
699         chunk1 = fc[k - 1];
700         chunk1samples = spc[k - 1];
701         if (chunk1samples != 0)
702                 *chunk = (sample - total) / chunk1samples + chunk1;
703         else
704                 *chunk = 1;
705         return total + (*chunk - chunk1) * chunk1samples;
706 }
707
708 /**
709  * Compute the duration of an mp4 file.
710  *
711  * \param f See \ref mp4_close().
712  *
713  * \return The number of milliseconds of the audio track. This function never
714  * fails.
715  */
716 uint64_t mp4_get_duration(const struct mp4 *f)
717 {
718         const struct mp4_track *t = &f->track;
719
720         return t->duration * 1000 / t->time_scale;
721 }
722
723 /**
724  * Reposition the read/write file offset.
725  *
726  * \param f See \ref mp4_close().
727  * \param sample The number of the sample to reposition to.
728  *
729  * The given sample number must be within range, i.e., strictly less than the
730  * value returned by \ref mp4_num_samples().
731  *
732  * \return Standard. The only possible error is an invalid sample number.
733  */
734 int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
735 {
736         const struct mp4_track *t = &f->track;
737         int32_t offset, chunk, chunk_sample;
738         uint32_t n, srs; /* sample range size */
739
740         if (sample >= t->stsz_sample_count)
741                 return -ERRNO_TO_PARA_ERROR(EINVAL);
742         chunk_sample = chunk_of_sample(f, sample, &chunk);
743         if (t->stsz_sample_size > 0)
744                 srs = (sample - chunk_sample) * t->stsz_sample_size;
745         else {
746                 for (srs = 0, n = chunk_sample; n < sample; n++)
747                         srs += t->stsz_table[n];
748         }
749         if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
750                 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
751         else if (t->stco_entry_count > 0)
752                 offset = t->stco_chunk_offset[chunk - 1];
753         else
754                 offset = 8;
755         set_position(f, offset + srs);
756         return 1;
757 }
758
759 /**
760  * Look up and return the size of the given sample in the stsz table.
761  *
762  * \param f See \ref mp4_close().
763  * \param sample The sample number of interest.
764  * \param result Sample size is returned here.
765  *
766  * For the sample argument the restriction mentioned in the documentation of
767  * \ref mp4_set_sample_position() applies as well.
768  *
769  * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
770  * only possible error.
771  */
772 int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
773 {
774         const struct mp4_track *t = &f->track;
775
776         if (sample >= t->stsz_sample_count)
777                 return -ERRNO_TO_PARA_ERROR(EINVAL);
778         if (t->stsz_sample_size != 0)
779                 *result = t->stsz_sample_size;
780         else
781                 *result = t->stsz_table[sample];
782         return 1;
783 }
784
785 /**
786  * Return the sample rate stored in the stsd atom.
787  *
788  * \param f See \ref mp4_close().
789  *
790  * The sample rate is a property of the audio track of the mp4 file and is thus
791  * independent of the sample number.
792  *
793  * \return The function always returns a positive value because the open
794  * operation fails if the sample rate happens to be zero. A typical value is
795  * 44100.
796  */
797 uint16_t mp4_get_sample_rate(const struct mp4 *f)
798 {
799         return f->track.sample_rate;
800 }
801
802 /**
803  * Return the number of channels of the audio track.
804  *
805  * \param f See \ref mp4_close().
806  *
807  * \return The returned channel count is guaranteed to be positive because the
808  * open operation fails if the mp4a atom is missing or contains a zero channel
809  * count.
810  */
811 uint16_t mp4_get_channel_count(const struct mp4 *f)
812 {
813         return f->track.channel_count;
814 }
815
816 /**
817  * Return the number of samples of the audio track.
818  *
819  * \param f See \ref mp4_close().
820  *
821  * \return The sample count is read from the stsz atom during open.
822  */
823 uint32_t mp4_num_samples(const struct mp4 *f)
824 {
825         return f->track.stsz_sample_count;
826 }
827
828 /**
829  * Open an mp4 file in metadata-only mode.
830  *
831  * \param cb See \ref mp4_open().
832  * \param result See \ref mp4_open().
833  *
834  * This is similar to \ref mp4_open() but is cheaper because it only parses the
835  * metadata of the mp4 file. The only functions that can subsequently be called
836  * with the file handle returned here are \ref mp4_get_meta() and \ref
837  * mp4_update_meta().
838  *
839  * \return Standard.
840  *
841  * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
842  * this function as well.
843  */
844 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
845 {
846         struct mp4 *f;
847         int ret = open_file(cb, true, &f);
848
849         if (ret < 0)
850                 return ret;
851         *result = f;
852         return 1;
853 }
854
855 /**
856  * Return the metadata of an mp4 file.
857  *
858  * \param f See \ref mp4_close().
859  *
860  * The caller is allowed to add, delete or modify the entries of the returned
861  * structure with the intention to pass the modified version to \ref
862  * mp4_update_meta().
863  *
864  * \return This never returns NULL, even if the file contains no metadata tag
865  * items. However, the meta count will be zero and the ->tags pointer NULL in
866  * this case.
867  */
868 struct mp4_metadata *mp4_get_meta(struct mp4 *f)
869 {
870         return &f->meta;
871 }
872
873 /** Total length of an on-disk metadata tag. */
874 #define TAG_LEN(_len) (24 + (_len))
875 static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
876 {
877         for (unsigned n = 0; n < meta->count; n++) {
878                 struct mp4_tag *tag = meta->tags + n;
879                 unsigned len = strlen(tag->value);
880                 const char *atom_name;
881
882                 if (!strcasecmp(tag->item, "title"))
883                         atom_name = "\xA9" "nam";
884                 else if (!strcasecmp(tag->item, "artist"))
885                         atom_name = "\xA9" "ART";
886                 else if (!strcasecmp(tag->item, "album"))
887                         atom_name = "\xA9" "alb";
888                 else if (!strcasecmp(tag->item, "date"))
889                         atom_name = "\xA9" "day";
890                 else if (!strcasecmp(tag->item, "comment"))
891                         atom_name = "\xA9" "cmt";
892                 else
893                         assert(false);
894                 write_u32_be(out, TAG_LEN(len));
895                 memcpy(out + 4, atom_name, 4);
896                 write_u32_be(out + 8, 8 /* data atom header */
897                         + 8 /* flags + reserved */
898                         + len);
899                 memcpy(out + 12, "data", 4);
900                 write_u32_be(out + 16, 1); /* flags */
901                 write_u32_be(out + 20, 0); /* reserved */
902                 memcpy(out + 24, tag->value, len);
903                 out += TAG_LEN(len);
904         }
905 }
906
907 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
908 {
909         int ret;
910         uint64_t total_base = f->moov_offset + 8;
911         uint32_t total_size = f->moov_size - 8;
912         uint32_t new_ilst_size = 0;
913         void *out_buffer;
914         uint8_t *p_out;
915         int32_t size_delta;
916         uint32_t tmp;
917
918         for (unsigned n = 0; n < f->meta.count; n++)
919                 new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
920         size_delta = new_ilst_size - (f->ilst_size - 8);
921         *out_size = total_size + size_delta;
922         out_buffer = alloc(*out_size);
923         p_out = out_buffer;
924         set_position(f, total_base);
925         ret = read_data(f, p_out, f->udta_offset - total_base);
926         if (ret <= 0)
927                 return NULL;
928         p_out += f->udta_offset - total_base;
929         ret = read_int32(f, &tmp);
930         if (ret <= 0)
931                 return NULL;
932         write_u32_be(p_out, tmp + size_delta);
933         p_out += 4;
934         ret = read_data(f, p_out, 4);
935         if (ret <= 0)
936                 return NULL;
937         p_out += 4;
938         ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
939         if (ret <= 0)
940                 return NULL;
941         p_out += f->meta_offset - f->udta_offset - 8;
942         ret = read_int32(f, &tmp);
943         if (ret <= 0)
944                 return NULL;
945         write_u32_be(p_out, tmp + size_delta);
946         p_out += 4;
947         ret = read_data(f, p_out, 4);
948         if (ret <= 0)
949                 return NULL;
950         p_out += 4;
951         ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
952         if (ret <= 0)
953                 return NULL;
954         p_out += f->ilst_offset - f->meta_offset - 8;
955         ret = read_int32(f, &tmp);
956         if (ret <= 0)
957                 return NULL;
958         write_u32_be(p_out, tmp + size_delta);
959         p_out += 4;
960         ret = read_data(f, p_out, 4);
961         if (ret <= 0)
962                 return NULL;
963         p_out += 4;
964         create_ilst(&f->meta, p_out);
965         p_out += new_ilst_size;
966         set_position(f, f->ilst_offset + f->ilst_size);
967         ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
968                 - f->ilst_size);
969         if (ret <= 0)
970                 return NULL;
971         return out_buffer;
972 }
973
974 static int write_data(struct mp4 *f, void *data, size_t size)
975 {
976         while (size > 0) {
977                 ssize_t ret = f->cb->write(f->cb->user_data, data, size);
978                 if (ret < 0) {
979                         if (errno == EINTR)
980                                 continue;
981                         return -ERRNO_TO_PARA_ERROR(errno);
982                 }
983                 size -= ret;
984         }
985         return 1;
986 }
987
988 /**
989  * Write back the modified metadata items to the mp4 file.
990  *
991  * This is the only public function which modifies the contents of an mp4 file.
992  * This is achieved by calling the ->write() and ->truncate() methods of the
993  * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
994  *
995  * \param f See \ref mp4_close().
996  *
997  * The modified metadata structure does not need to be supplied to this
998  * function because it is part of the mp4 structure.
999  *
1000  * \return Standard.
1001  */
1002 int mp4_update_meta(struct mp4 *f)
1003 {
1004         void *new_moov_data;
1005         uint32_t new_moov_size;
1006         uint8_t buf[8] = "----moov";
1007         int ret;
1008
1009         set_position(f, 0);
1010         new_moov_data = modify_moov(f, &new_moov_size);
1011         if (!new_moov_data ) {
1012                 mp4_close(f);
1013                 return 0;
1014         }
1015         if (f->last_atom != ATOM_MOOV) {
1016                 set_position(f, f->moov_offset + 4);
1017                 ret = write_data(f, "free", 4); /* rename old moov to free */
1018                 if (ret < 0)
1019                         goto free_moov;
1020                 /* write new moov atom at EOF */
1021                 f->cb->seek(f->cb->user_data, 0, SEEK_END);
1022         } else /* overwrite old moov atom */
1023                 set_position(f, f->moov_offset);
1024         write_u32_be(buf, new_moov_size + 8);
1025         ret = write_data(f, buf, sizeof(buf));
1026         if (ret < 0)
1027                 goto free_moov;
1028         ret = write_data(f, new_moov_data, new_moov_size);
1029         if (ret < 0)
1030                 goto free_moov;
1031         ret = f->cb->truncate(f->cb->user_data);
1032         if (ret < 0)
1033                 ret = -ERRNO_TO_PARA_ERROR(errno);
1034 free_moov:
1035         free(new_moov_data);
1036         return ret;
1037 }
1038
1039 /**
1040  * Return the value of the given tag item.
1041  *
1042  * \param f See \ref mp4_close().
1043  * \param item "artist", "title", "album", "comment", or "date".
1044  *
1045  * \return The function returns NULL if the given item is not in the above
1046  * list. Otherwise, if the file does not contain a tag for the given item, the
1047  * function also returns NULL. Otherwise a copy of the tag value is returned
1048  * and the caller should free this memory when it is no longer needed.
1049  */
1050 __malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item)
1051 {
1052         for (unsigned n = 0; n < f->meta.count; n++)
1053                 if (!strcasecmp(f->meta.tags[n].item, item))
1054                         return para_strdup(f->meta.tags[n].value);
1055         return NULL;
1056 }