]> git.tuebingen.mpg.de Git - paraslash.git/blob - mp4.c
mp4: Check for missing metadata also for regular opens.
[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 -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
79  * by EOF or 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? -E_MP4_READ : 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                 *atom_size = size;
192         }
193         *atom_type = atom_name_to_type(atom_header + 4);
194         return 1;
195 }
196
197 static off_t get_position(const struct mp4 *f)
198 {
199         return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
200 }
201
202 static void set_position(struct mp4 *f, off_t position)
203 {
204         f->cb->seek(f->cb->user_data, position, SEEK_SET);
205 }
206
207 static void skip_bytes(struct mp4 *f, off_t num_skip)
208 {
209         f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
210 }
211
212 static int read_stsz(struct mp4 *f)
213 {
214         int ret;
215         struct mp4_track *t = &f->track;
216
217         if (t->state != ATS_SEEN_MP4A || t->stsz_table)
218                 return 1;
219         skip_bytes(f, 4); /* version (1), flags (3) */
220         ret = read_int32(f, &t->stsz_sample_size);
221         if (ret <= 0)
222                 return ret;
223         ret = read_int32(f, &t->stsz_sample_count);
224         if (ret <= 0)
225                 return ret;
226         if (t->stsz_sample_size != 0)
227                 return 1;
228         t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
229         for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
230                 ret = read_int32(f, &t->stsz_table[n]);
231                 if (ret <= 0)
232                         return ret;
233         }
234         return 1;
235 }
236
237 static int read_stts(struct mp4 *f)
238 {
239         int ret;
240         struct mp4_track *t = &f->track;
241
242         if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
243                 return 1;
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 (uint32_t n = 0; n < t->stts_entry_count; n++) {
251                 ret = read_int32(f, &t->stts_sample_count[n]);
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         struct mp4_track *t = &f->track;
263
264         if (t->state != ATS_SEEN_MP4A)
265                 return 1;
266         if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
267                 return 1;
268         skip_bytes(f, 4); /* version (1), flags (3) */
269         ret = read_int32(f, &t->stsc_entry_count);
270         if (ret <= 0)
271                 return ret;
272         t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
273         t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
274                 * sizeof (int32_t));
275         for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
276                 ret = read_int32(f, &t->stsc_first_chunk[n]);
277                 if (ret <= 0)
278                         return ret;
279                 ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
280                 if (ret <= 0)
281                         return ret;
282                 skip_bytes(f, 4); /* sample desc index */
283         }
284         return 1;
285 }
286
287 static int read_stco(struct mp4 *f)
288 {
289         int ret;
290         struct mp4_track *t = &f->track;
291
292         if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
293                 return 1;
294         skip_bytes(f, 4); /* version (1), flags (3) */
295         ret = read_int32(f, &t->stco_entry_count);
296         if (ret <= 0)
297                 return ret;
298         t->stco_chunk_offset = para_malloc(t->stco_entry_count
299                 * sizeof(int32_t));
300         for (uint32_t n = 0; n < t->stco_entry_count; n++) {
301                 ret = read_int32(f, &t->stco_chunk_offset[n]);
302                 if (ret <= 0)
303                         return ret;
304         }
305         return 1;
306 }
307
308 static int read_stsd(struct mp4 *f)
309 {
310         int ret;
311         uint32_t entry_count;
312
313         if (f->track.state != ATS_INITIAL)
314                 return 1;
315         skip_bytes(f, 4); /* version (1), flags (3) */
316         ret = read_int32(f, &entry_count);
317         if (ret <= 0)
318                 return ret;
319         for (uint32_t n = 0; n < entry_count; n++) {
320                 uint64_t skip = get_position(f);
321                 uint64_t size;
322                 uint8_t atom_type = 0;
323                 ret = atom_read_header(f, &atom_type, NULL, &size);
324                 if (ret <= 0)
325                         return ret;
326                 skip += size;
327                 if (atom_type == ATOM_MP4A) {
328                         f->track.state = ATS_SEEN_MP4A;
329                         /* reserved (6), data reference index (2), reserved (8) */
330                         skip_bytes(f, 16);
331                         ret = read_int16(f, &f->track.channel_count);
332                         if (ret <= 0)
333                                 return ret;
334                         skip_bytes(f, 6);
335                         ret = read_int16(f, &f->track.sample_rate);
336                         if (ret <= 0)
337                                 return ret;
338                 }
339                 set_position(f, skip);
340         }
341         return 1;
342 }
343
344 static const char *get_metadata_name(uint8_t atom_type)
345 {
346         switch (atom_type) {
347         case ATOM_TITLE: return "title";
348         case ATOM_ARTIST: return "artist";
349         case ATOM_ALBUM: return "album";
350         case ATOM_DATE: return "date";
351         case ATOM_COMMENT: return "comment";
352         default: return "unknown";
353         }
354 }
355
356 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
357 {
358         int ret;
359         uint64_t subsize, sumsize;
360         char *value = NULL;
361         uint32_t len = 0;
362         uint64_t destpos;
363         struct mp4_tag *tag;
364
365         for (
366                 sumsize = 0;
367                 sumsize < size;
368                 set_position(f, destpos), sumsize += subsize
369         ) {
370                 uint8_t atom_type;
371                 uint8_t header_size = 0;
372                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
373                 if (ret <= 0)
374                         goto fail;
375                 destpos = get_position(f) + subsize - header_size;
376                 if (atom_type != ATOM_DATA)
377                         continue;
378                 skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
379                 ret = -E_MP4_CORRUPT;
380                 if (subsize < header_size + 8 || subsize > UINT_MAX)
381                         goto fail;
382                 len = subsize - (header_size + 8);
383                 free(value);
384                 value = para_malloc(len + 1);
385                 ret = read_data(f, value, len);
386                 if (ret <= 0)
387                         goto fail;
388                 value[len] = '\0';
389         }
390         if (!value)
391                 return -E_MP4_CORRUPT;
392         f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
393                 * sizeof(struct mp4_tag));
394         tag = f->meta.tags + f->meta.count;
395         tag->item = para_strdup(get_metadata_name(parent));
396         tag->value = value;
397         f->meta.count++;
398         return 1;
399 fail:
400         free(value);
401         return ret;
402 }
403
404 static int read_mdhd(struct mp4 *f)
405 {
406         int ret;
407         uint32_t version;
408         struct mp4_track *t = &f->track;
409
410         if (t->state != ATS_INITIAL)
411                 return 1;
412         ret = read_int32(f, &version);
413         if (ret <= 0)
414                 return ret;
415         if (version == 1) {
416                 skip_bytes(f, 16); /* creation time (8), modification time (8) */
417                 ret = read_int32(f, &t->time_scale);
418                 if (ret <= 0)
419                         return ret;
420                 ret = read_int64(f, &t->duration);
421                 if (ret <= 0)
422                         return ret;
423         } else { /* version == 0 */
424                 uint32_t temp;
425
426                 skip_bytes(f, 8); /* creation time (4), modification time (4) */
427                 ret = read_int32(f, &t->time_scale);
428                 if (ret <= 0)
429                         return ret;
430                 ret = read_int32(f, &temp);
431                 if (ret <= 0)
432                         return ret;
433                 t->duration = (temp == (uint32_t) (-1))?
434                         (uint64_t) (-1) : (uint64_t) (temp);
435         }
436         skip_bytes(f, 4);
437         return 1;
438 }
439
440 static int read_ilst(struct mp4 *f, int32_t size)
441 {
442         int ret;
443         uint64_t sumsize = 0;
444
445         while (sumsize < size) {
446                 uint8_t atom_type;
447                 uint64_t subsize, destpos;
448                 uint8_t header_size = 0;
449                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
450                 if (ret <= 0)
451                         return ret;
452                 destpos = get_position(f) + subsize - header_size;
453                 switch (atom_type) {
454                 case ATOM_ARTIST:
455                 case ATOM_TITLE:
456                 case ATOM_ALBUM:
457                 case ATOM_COMMENT:
458                 case ATOM_DATE:
459                         ret = parse_tag(f, atom_type, subsize - header_size);
460                         if (ret <= 0)
461                                 return ret;
462                 }
463                 set_position(f, destpos);
464                 sumsize += subsize;
465         }
466         return 1;
467 }
468
469 static int read_meta(struct mp4 *f, uint64_t size)
470 {
471         int ret;
472         uint64_t subsize, sumsize = 0;
473         uint8_t atom_type;
474         uint8_t header_size = 0;
475
476         skip_bytes(f, 4); /* version (1), flags (3) */
477         while (sumsize < (size - (header_size + 4))) {
478                 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
479                 if (ret <= 0)
480                         return ret;
481                 if (subsize <= header_size + 4)
482                         return 1;
483                 if (atom_type == ATOM_ILST) {
484                         f->ilst_offset = get_position(f) - header_size;
485                         f->ilst_size = subsize;
486                         ret = read_ilst(f, subsize - (header_size + 4));
487                         if (ret <= 0)
488                                 return ret;
489                 } else
490                         set_position(f, get_position(f) + subsize - header_size);
491                 sumsize += subsize;
492         }
493         return 1;
494 }
495
496 static bool need_atom(uint8_t atom_type, bool meta_only)
497 {
498         /* these are needed in any case */
499         switch (atom_type) {
500         case ATOM_STSD:
501         case ATOM_META:
502         case ATOM_TRAK:
503         case ATOM_MDIA:
504         case ATOM_MINF:
505         case ATOM_STBL:
506         case ATOM_UDTA:
507                 return true;
508         }
509         /* meta-only opens don't need anything else */
510         if (meta_only)
511                 return false;
512         /* these are only required for regular opens */
513         switch (atom_type) {
514         case ATOM_STTS:
515         case ATOM_STSZ:
516         case ATOM_STCO:
517         case ATOM_STSC:
518         case ATOM_MDHD:
519                 return true;
520         }
521         return false;
522 }
523
524 /* parse atoms that are sub atoms of other atoms */
525 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
526 {
527         int ret;
528         uint64_t dest, size, end = get_position(f) + total_size;
529
530         for (dest = get_position(f); dest < end; set_position(f, dest)) {
531                 uint8_t header_size, atom_type;
532                 ret = atom_read_header(f, &atom_type, &header_size, &size);
533                 if (ret <= 0)
534                         return ret;
535                 if (size == 0)
536                         return -E_MP4_CORRUPT;
537                 dest = get_position(f) + size - header_size;
538                 if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
539                         f->track.state = ATS_TRACK_CHANGE;
540                         continue;
541                 }
542                 if (atom_type == ATOM_UDTA) {
543                         f->udta_offset = get_position(f) - header_size;
544                         f->udta_size = size;
545                 }
546                 if (!need_atom(atom_type, meta_only))
547                         continue;
548                 switch (atom_type) {
549                 case ATOM_STSZ: ret = read_stsz(f); break;
550                 case ATOM_STTS: ret = read_stts(f); break;
551                 case ATOM_STSC: ret = read_stsc(f); break;
552                 case ATOM_STCO: ret = read_stco(f); break;
553                 case ATOM_STSD: ret = read_stsd(f); break;
554                 case ATOM_MDHD: ret = read_mdhd(f); break;
555                 case ATOM_META:
556                         f->meta_offset = get_position(f) - header_size;
557                         f->meta_size = size;
558                         ret = read_meta(f, size);
559                         break;
560                 default:
561                         ret = parse_sub_atoms(f, size - header_size, meta_only);
562                 }
563                 if (ret <= 0)
564                         return ret;
565         }
566         return 1;
567 }
568
569 void mp4_close(struct mp4 *f)
570 {
571         free(f->track.stsz_table);
572         free(f->track.stts_sample_count);
573         free(f->track.stsc_first_chunk);
574         free(f->track.stsc_samples_per_chunk);
575         free(f->track.stco_chunk_offset);
576         for (uint32_t n = 0; n < f->meta.count; n++) {
577                 free(f->meta.tags[n].item);
578                 free(f->meta.tags[n].value);
579         }
580         free(f->meta.tags);
581         free(f);
582 }
583
584 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
585 {
586         int ret;
587         uint64_t size;
588         uint8_t atom_type, header_size;
589         struct mp4 *f = para_calloc(sizeof(*f));
590
591         f->cb = cb;
592         while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
593                 f->last_atom = atom_type;
594                 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
595                         set_position(f, get_position(f) + size - header_size);
596                         continue;
597                 }
598                 f->moov_offset = get_position(f) - header_size;
599                 f->moov_size = size;
600                 ret = parse_sub_atoms(f, size - header_size, meta_only);
601                 if (ret <= 0)
602                         break;
603         }
604         if (ret < 0)
605                 goto fail;
606         ret = -E_MP4_TRACK;
607         if (f->track.channel_count == 0)
608                 goto fail;
609         ret = -E_MP4_BAD_SAMPLERATE;
610         if (f->track.sample_rate == 0)
611                 goto fail;
612         ret = -E_MP4_MISSING_ATOM;
613         if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
614                 goto fail;
615         *result = f;
616         return 1;
617 fail:
618         *result = NULL;
619         mp4_close(f);
620         return ret;
621 }
622
623 int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
624 {
625         struct mp4 *f;
626         int ret;
627
628         *result = NULL;
629         ret = open_file(cb, false, &f);
630         if (ret < 0)
631                 return ret;
632         ret = -E_MP4_BAD_SAMPLE_COUNT;
633         if (f->track.stsz_sample_count == 0)
634                 goto fail;
635         ret = -E_MP4_CORRUPT;
636         if (f->track.time_scale == 0)
637                 goto fail;
638         *result = f;
639         return 1;
640 fail:
641         mp4_close(f);
642         return ret;
643 }
644
645 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
646                 int32_t *chunk)
647 {
648         const struct mp4_track *t = &f->track;
649         uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
650         uint32_t chunk1, chunk1samples, n, total, k;
651
652         for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
653                 n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
654                 if (sample < total + n)
655                         break;
656         }
657         chunk1 = fc[k - 1];
658         chunk1samples = spc[k - 1];
659         if (chunk1samples != 0)
660                 *chunk = (sample - total) / chunk1samples + chunk1;
661         else
662                 *chunk = 1;
663         return total + (*chunk - chunk1) * chunk1samples;
664 }
665
666 /**
667  * Return the number of milliseconds of the audio track.
668  *
669  * \param f As returned by \ref mp4_open_read(), must not be NULL.
670  */
671 uint64_t mp4_get_duration(const struct mp4 *f)
672 {
673         const struct mp4_track *t = &f->track;
674
675         return t->duration * 1000 / t->time_scale;
676 }
677
678 int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
679 {
680         const struct mp4_track *t = &f->track;
681         int32_t offset, chunk, chunk_sample;
682         uint32_t n, srs; /* sample range size */
683
684         if (sample >= t->stsz_sample_count)
685                 return -ERRNO_TO_PARA_ERROR(EINVAL);
686         chunk_sample = chunk_of_sample(f, sample, &chunk);
687         if (t->stsz_sample_size > 0)
688                 srs = (sample - chunk_sample) * t->stsz_sample_size;
689         else {
690                 for (srs = 0, n = chunk_sample; n < sample; n++)
691                         srs += t->stsz_table[n];
692         }
693         if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
694                 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
695         else if (t->stco_entry_count > 0)
696                 offset = t->stco_chunk_offset[chunk - 1];
697         else
698                 offset = 8;
699         set_position(f, offset + srs);
700         return 1;
701 }
702
703 int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
704 {
705         const struct mp4_track *t = &f->track;
706
707         if (sample >= t->stsz_sample_count)
708                 return -ERRNO_TO_PARA_ERROR(EINVAL);
709         if (t->stsz_sample_size != 0)
710                 *result = t->stsz_sample_size;
711         else
712                 *result = t->stsz_table[sample];
713         return 1;
714 }
715
716 uint16_t mp4_get_sample_rate(const struct mp4 *f)
717 {
718         return f->track.sample_rate;
719 }
720
721 uint16_t mp4_get_channel_count(const struct mp4 *f)
722 {
723         return f->track.channel_count;
724 }
725
726 uint32_t mp4_num_samples(const struct mp4 *f)
727 {
728         return f->track.stsz_sample_count;
729 }
730
731 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
732 {
733         struct mp4 *f;
734         int ret = open_file(cb, true, &f);
735
736         if (ret < 0)
737                 return ret;
738         *result = f;
739         return 1;
740 }
741
742 /**
743  * Return the metadata of an mp4 file.
744  *
745  * \param f As returned by either \ref mp4_open_read() or \ref mp4_open_meta().
746  *
747  * The caller is allowed to add, delete or modify the entries of the returned
748  * structure in order to pass the modified version to \ref mp4_meta_update().
749  */
750 struct mp4_metadata *mp4_get_meta(struct mp4 *f)
751 {
752         return &f->meta;
753 }
754
755 /** Total length of an on-disk metadata tag. */
756 #define TAG_LEN(_len) (24 + (_len))
757 static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
758 {
759         for (unsigned n = 0; n < meta->count; n++) {
760                 struct mp4_tag *tag = meta->tags + n;
761                 unsigned len = strlen(tag->value);
762                 const char *atom_name;
763
764                 if (!strcasecmp(tag->item, "title"))
765                         atom_name = "\xA9" "nam";
766                 else if (!strcasecmp(tag->item, "artist"))
767                         atom_name = "\xA9" "ART";
768                 else if (!strcasecmp(tag->item, "album"))
769                         atom_name = "\xA9" "alb";
770                 else if (!strcasecmp(tag->item, "date"))
771                         atom_name = "\xA9" "day";
772                 else if (!strcasecmp(tag->item, "comment"))
773                         atom_name = "\xA9" "cmt";
774                 else
775                         assert(false);
776                 write_u32_be(out, TAG_LEN(len));
777                 memcpy(out + 4, atom_name, 4);
778                 write_u32_be(out + 8, 8 /* data atom header */
779                         + 8 /* flags + reserved */
780                         + len);
781                 memcpy(out + 12, "data", 4);
782                 write_u32_be(out + 16, 1); /* flags */
783                 write_u32_be(out + 20, 0); /* reserved */
784                 memcpy(out + 24, tag->value, len);
785                 out += TAG_LEN(len);
786         }
787 }
788
789 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
790 {
791         int ret;
792         uint64_t total_base = f->moov_offset + 8;
793         uint32_t total_size = f->moov_size - 8;
794         uint32_t new_ilst_size = 0;
795         void *out_buffer;
796         uint8_t *p_out;
797         int32_t size_delta;
798         uint32_t tmp;
799
800         for (unsigned n = 0; n < f->meta.count; n++)
801                 new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
802         size_delta = new_ilst_size - (f->ilst_size - 8);
803         *out_size = total_size + size_delta;
804         out_buffer = para_malloc(*out_size);
805         p_out = out_buffer;
806         set_position(f, total_base);
807         ret = read_data(f, p_out, f->udta_offset - total_base);
808         if (ret <= 0)
809                 return NULL;
810         p_out += f->udta_offset - total_base;
811         ret = read_int32(f, &tmp);
812         if (ret <= 0)
813                 return NULL;
814         write_u32_be(p_out, tmp + size_delta);
815         p_out += 4;
816         ret = read_data(f, p_out, 4);
817         if (ret <= 0)
818                 return NULL;
819         p_out += 4;
820         ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
821         if (ret <= 0)
822                 return NULL;
823         p_out += f->meta_offset - f->udta_offset - 8;
824         ret = read_int32(f, &tmp);
825         if (ret <= 0)
826                 return NULL;
827         write_u32_be(p_out, tmp + size_delta);
828         p_out += 4;
829         ret = read_data(f, p_out, 4);
830         if (ret <= 0)
831                 return NULL;
832         p_out += 4;
833         ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
834         if (ret <= 0)
835                 return NULL;
836         p_out += f->ilst_offset - f->meta_offset - 8;
837         ret = read_int32(f, &tmp);
838         if (ret <= 0)
839                 return NULL;
840         write_u32_be(p_out, tmp + size_delta);
841         p_out += 4;
842         ret = read_data(f, p_out, 4);
843         if (ret <= 0)
844                 return NULL;
845         p_out += 4;
846         create_ilst(&f->meta, p_out);
847         p_out += new_ilst_size;
848         set_position(f, f->ilst_offset + f->ilst_size);
849         ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
850                 - f->ilst_size);
851         if (ret <= 0)
852                 return NULL;
853         return out_buffer;
854 }
855
856 static int write_data(struct mp4 *f, void *data, size_t size)
857 {
858         while (size > 0) {
859                 ssize_t ret = f->cb->write(f->cb->user_data, data, size);
860                 if (ret < 0) {
861                         if (errno == EINTR)
862                                 continue;
863                         return -ERRNO_TO_PARA_ERROR(errno);
864                 }
865                 size -= ret;
866         }
867         return 1;
868 }
869
870 int mp4_update_meta(struct mp4 *f)
871 {
872         void *new_moov_data;
873         uint32_t new_moov_size;
874         uint8_t buf[8] = "----moov";
875         int ret;
876
877         set_position(f, 0);
878         new_moov_data = modify_moov(f, &new_moov_size);
879         if (!new_moov_data ) {
880                 mp4_close(f);
881                 return 0;
882         }
883         if (f->last_atom != ATOM_MOOV) {
884                 set_position(f, f->moov_offset + 4);
885                 ret = write_data(f, "free", 4); /* rename old moov to free */
886                 if (ret < 0)
887                         goto free_moov;
888                 /* write new moov atom at EOF */
889                 f->cb->seek(f->cb->user_data, 0, SEEK_END);
890         } else /* overwrite old moov atom */
891                 set_position(f, f->moov_offset);
892         write_u32_be(buf, new_moov_size + 8);
893         ret = write_data(f, buf, sizeof(buf));
894         if (ret < 0)
895                 goto free_moov;
896         ret = write_data(f, new_moov_data, new_moov_size);
897         if (ret < 0)
898                 goto free_moov;
899         ret = f->cb->truncate(f->cb->user_data);
900         if (ret < 0)
901                 ret = -ERRNO_TO_PARA_ERROR(errno);
902 free_moov:
903         free(new_moov_data);
904         return ret;
905 }
906
907 /**
908  * Return the value of the given tag item.
909  *
910  * \param f Must not be NULL.
911  * \param item "artist", "title", "album", "comment", or "date".
912  *
913  * \return The function always returns NULL if the given item is not in the
914  * above list. Otherwise, if the file does not contain a tag for the given
915  * item, the function also returns NULL. Otherwise a copy of the tag value is
916  * returned and the caller should free this memory when it is no longer needed.
917  */
918 char *mp4_get_tag_value(const struct mp4 *f, const char *item)
919 {
920         for (unsigned n = 0; n < f->meta.count; n++)
921                 if (!strcasecmp(f->meta.tags[n].item, item))
922                         return para_strdup(f->meta.tags[n].value);
923         return NULL;
924 }