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