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