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