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