]> git.tuebingen.mpg.de Git - paraslash.git/blob - mp4.c
mp4: Don't parse the movie header any more.
[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 "portable_io.h"
12 #include "string.h"
13 #include "mp4.h"
14
15 struct mp4_track {
16         bool is_audio;
17         int32_t channelCount;
18         int32_t sampleSize;
19         uint16_t sampleRate;
20
21         /* stsd */
22         int32_t stsd_entry_count;
23
24         /* stsz */
25         int32_t stsz_sample_size;
26         int32_t stsz_sample_count;
27         int32_t *stsz_table;
28
29         /* stts */
30         int32_t stts_entry_count;
31         int32_t *stts_sample_count;
32         int32_t *stts_sample_delta;
33
34         /* stsc */
35         int32_t stsc_entry_count;
36         int32_t *stsc_first_chunk;
37         int32_t *stsc_samples_per_chunk;
38         int32_t *stsc_sample_desc_index;
39
40         /* stsc */
41         int32_t stco_entry_count;
42         int32_t *stco_chunk_offset;
43
44         /* ctts */
45         int32_t ctts_entry_count;
46         int32_t *ctts_sample_count;
47         int32_t *ctts_sample_offset;
48
49         uint32_t maxBitrate;
50         uint32_t avgBitrate;
51
52         uint32_t timeScale;
53         uint64_t duration;
54 };
55
56 #define MAX_TRACKS 1024
57
58 struct mp4 {
59         /* stream to read from */
60         struct mp4_callback *stream;
61         int64_t current_position;
62
63         uint64_t moov_offset;
64         uint64_t moov_size;
65         uint8_t last_atom;
66         uint64_t file_size;
67         uint32_t error;
68
69         /* incremental track index while reading the file */
70         int32_t total_tracks;
71
72         /* track data */
73         struct mp4_track *track[MAX_TRACKS];
74
75         /* metadata */
76         struct mp4_metadata tags;
77 };
78
79 int32_t mp4_total_tracks(const struct mp4 *f)
80 {
81         return f->total_tracks;
82 }
83
84 static int32_t read_data(struct mp4 *f, void *data, uint32_t size)
85 {
86         int32_t result = 1;
87
88         result = f->stream->read(f->stream->user_data, data, size);
89
90         if (result < size)
91                 f->stream->read_error++;
92
93         f->current_position += size;
94
95         return result;
96 }
97
98 static uint64_t read_int64(struct mp4 *f)
99 {
100         uint8_t data[8];
101
102         read_data(f, data, 8);
103         return read_u64_be(data);
104 }
105
106 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
107                 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
108 {
109         return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
110 }
111
112 enum atoms {
113         /* atoms with subatoms */
114         ATOM_MOOV = 1,
115         ATOM_TRAK = 2,
116         ATOM_EDTS = 3,
117         ATOM_MDIA = 4,
118         ATOM_MINF = 5,
119         ATOM_STBL = 6,
120         ATOM_UDTA = 7,
121         ATOM_ILST = 8, /* iTunes Metadata list */
122         ATOM_TITLE = 9,
123         ATOM_ARTIST = 10,
124         ATOM_WRITER = 11,
125         ATOM_ALBUM = 12,
126         ATOM_DATE = 13,
127         ATOM_TOOL = 14,
128         ATOM_COMMENT = 15,
129         ATOM_GENRE1 = 16,
130         ATOM_TRACK = 17,
131         ATOM_DISC = 18,
132         ATOM_COMPILATION = 19,
133         ATOM_GENRE2 = 20,
134         ATOM_TEMPO = 21,
135         ATOM_COVER = 22,
136         ATOM_DRMS = 23,
137         ATOM_SINF = 24,
138         ATOM_SCHI = 25,
139
140         SUBATOMIC = 128,
141
142         /* atoms without subatoms */
143         ATOM_FTYP = 129,
144         ATOM_MDAT = 130,
145         ATOM_MVHD = 131,
146         ATOM_TKHD = 132,
147         ATOM_TREF = 133,
148         ATOM_MDHD = 134,
149         ATOM_VMHD = 135,
150         ATOM_SMHD = 136,
151         ATOM_HMHD = 137,
152         ATOM_STSD = 138,
153         ATOM_STTS = 139,
154         ATOM_STSZ = 140,
155         ATOM_STZ2 = 141,
156         ATOM_STCO = 142,
157         ATOM_STSC = 143,
158         ATOM_MP4A = 144,
159         ATOM_MP4V = 145,
160         ATOM_MP4S = 146,
161         ATOM_ESDS = 147,
162         ATOM_META = 148, /* iTunes Metadata box */
163         ATOM_NAME = 149, /* iTunes Metadata name box */
164         ATOM_DATA = 150, /* iTunes Metadata data box */
165         ATOM_CTTS = 151,
166         ATOM_FRMA = 152,
167         ATOM_IVIV = 153,
168         ATOM_PRIV = 154,
169         ATOM_USER = 155,
170         ATOM_KEY = 156,
171         ATOM_ALBUM_ARTIST = 157,
172         ATOM_CONTENTGROUP = 158,
173         ATOM_LYRICS = 159,
174         ATOM_DESCRIPTION = 160,
175         ATOM_NETWORK = 161,
176         ATOM_SHOW = 162,
177         ATOM_EPISODENAME = 163,
178         ATOM_SORTTITLE = 164,
179         ATOM_SORTALBUM = 165,
180         ATOM_SORTARTIST = 166,
181         ATOM_SORTALBUMARTIST = 167,
182         ATOM_SORTWRITER = 168,
183         ATOM_SORTSHOW = 169,
184         ATOM_SEASON = 170,
185         ATOM_EPISODE = 171,
186         ATOM_PODCAST = 172,
187
188         ATOM_UNKNOWN = 255
189 };
190
191 #define ATOM_FREE ATOM_UNKNOWN
192 #define ATOM_SKIP ATOM_UNKNOWN
193
194 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
195
196 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
197 {
198         if (a == 'm') {
199                 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
200                         return ATOM_MOOV;
201                 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
202                         return ATOM_MINF;
203                 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
204                         return ATOM_MDIA;
205                 else if (atom_compare(a, b, c, d, 'm', 'd', 'a', 't'))
206                         return ATOM_MDAT;
207                 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
208                         return ATOM_MDHD;
209                 else if (atom_compare(a, b, c, d, 'm', 'v', 'h', 'd'))
210                         return ATOM_MVHD;
211                 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
212                         return ATOM_MP4A;
213                 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'v'))
214                         return ATOM_MP4V;
215                 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 's'))
216                         return ATOM_MP4S;
217                 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
218                         return ATOM_META;
219         } else if (a == 't') {
220                 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
221                         return ATOM_TRAK;
222                 else if (atom_compare(a, b, c, d, 't', 'k', 'h', 'd'))
223                         return ATOM_TKHD;
224                 else if (atom_compare(a, b, c, d, 't', 'r', 'e', 'f'))
225                         return ATOM_TREF;
226                 else if (atom_compare(a, b, c, d, 't', 'r', 'k', 'n'))
227                         return ATOM_TRACK;
228                 else if (atom_compare(a, b, c, d, 't', 'm', 'p', 'o'))
229                         return ATOM_TEMPO;
230                 else if (atom_compare(a, b, c, d, 't', 'v', 'n', 'n'))
231                         return ATOM_NETWORK;
232                 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'h'))
233                         return ATOM_SHOW;
234                 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 'n'))
235                         return ATOM_EPISODENAME;
236                 else if (atom_compare(a, b, c, d, 't', 'v', 's', 'n'))
237                         return ATOM_SEASON;
238                 else if (atom_compare(a, b, c, d, 't', 'v', 'e', 's'))
239                         return ATOM_EPISODE;
240         } else if (a == 's') {
241                 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
242                         return ATOM_STBL;
243                 else if (atom_compare(a, b, c, d, 's', 'm', 'h', 'd'))
244                         return ATOM_SMHD;
245                 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
246                         return ATOM_STSD;
247                 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
248                         return ATOM_STTS;
249                 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
250                         return ATOM_STCO;
251                 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
252                         return ATOM_STSC;
253                 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
254                         return ATOM_STSZ;
255                 else if (atom_compare(a, b, c, d, 's', 't', 'z', '2'))
256                         return ATOM_STZ2;
257                 else if (atom_compare(a, b, c, d, 's', 'k', 'i', 'p'))
258                         return ATOM_SKIP;
259                 else if (atom_compare(a, b, c, d, 's', 'i', 'n', 'f'))
260                         return ATOM_SINF;
261                 else if (atom_compare(a, b, c, d, 's', 'c', 'h', 'i'))
262                         return ATOM_SCHI;
263                 else if (atom_compare(a, b, c, d, 's', 'o', 'n', 'm'))
264                         return ATOM_SORTTITLE;
265                 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'l'))
266                         return ATOM_SORTALBUM;
267                 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'r'))
268                         return ATOM_SORTARTIST;
269                 else if (atom_compare(a, b, c, d, 's', 'o', 'a', 'a'))
270                         return ATOM_SORTALBUMARTIST;
271                 else if (atom_compare(a, b, c, d, 's', 'o', 'c', 'o'))
272                         return ATOM_SORTWRITER;
273                 else if (atom_compare(a, b, c, d, 's', 'o', 's', 'n'))
274                         return ATOM_SORTSHOW;
275         } else if (a == COPYRIGHT_SYMBOL) {
276                 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
277                         return ATOM_TITLE;
278                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
279                         return ATOM_ARTIST;
280                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'w', 'r', 't'))
281                         return ATOM_WRITER;
282                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
283                         return ATOM_ALBUM;
284                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
285                         return ATOM_DATE;
286                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 't', 'o', 'o'))
287                         return ATOM_TOOL;
288                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
289                         return ATOM_COMMENT;
290                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'e', 'n'))
291                         return ATOM_GENRE1;
292                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'g', 'r', 'p'))
293                         return ATOM_CONTENTGROUP;
294                 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'l', 'y', 'r'))
295                         return ATOM_LYRICS;
296         }
297
298         if (atom_compare(a, b, c, d, 'e', 'd', 't', 's'))
299                 return ATOM_EDTS;
300         else if (atom_compare(a, b, c, d, 'e', 's', 'd', 's'))
301                 return ATOM_ESDS;
302         else if (atom_compare(a, b, c, d, 'f', 't', 'y', 'p'))
303                 return ATOM_FTYP;
304         else if (atom_compare(a, b, c, d, 'f', 'r', 'e', 'e'))
305                 return ATOM_FREE;
306         else if (atom_compare(a, b, c, d, 'h', 'm', 'h', 'd'))
307                 return ATOM_HMHD;
308         else if (atom_compare(a, b, c, d, 'v', 'm', 'h', 'd'))
309                 return ATOM_VMHD;
310         else if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
311                 return ATOM_UDTA;
312         else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
313                 return ATOM_ILST;
314         else if (atom_compare(a, b, c, d, 'n', 'a', 'm', 'e'))
315                 return ATOM_NAME;
316         else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
317                 return ATOM_DATA;
318         else if (atom_compare(a, b, c, d, 'd', 'i', 's', 'k'))
319                 return ATOM_DISC;
320         else if (atom_compare(a, b, c, d, 'g', 'n', 'r', 'e'))
321                 return ATOM_GENRE2;
322         else if (atom_compare(a, b, c, d, 'c', 'o', 'v', 'r'))
323                 return ATOM_COVER;
324         else if (atom_compare(a, b, c, d, 'c', 'p', 'i', 'l'))
325                 return ATOM_COMPILATION;
326         else if (atom_compare(a, b, c, d, 'c', 't', 't', 's'))
327                 return ATOM_CTTS;
328         else if (atom_compare(a, b, c, d, 'd', 'r', 'm', 's'))
329                 return ATOM_DRMS;
330         else if (atom_compare(a, b, c, d, 'f', 'r', 'm', 'a'))
331                 return ATOM_FRMA;
332         else if (atom_compare(a, b, c, d, 'p', 'r', 'i', 'v'))
333                 return ATOM_PRIV;
334         else if (atom_compare(a, b, c, d, 'i', 'v', 'i', 'v'))
335                 return ATOM_IVIV;
336         else if (atom_compare(a, b, c, d, 'u', 's', 'e', 'r'))
337                 return ATOM_USER;
338         else if (atom_compare(a, b, c, d, 'k', 'e', 'y', ' '))
339                 return ATOM_KEY;
340         else if (atom_compare(a, b, c, d, 'a', 'A', 'R', 'T'))
341                 return ATOM_ALBUM_ARTIST;
342         else if (atom_compare(a, b, c, d, 'd', 'e', 's', 'c'))
343                 return ATOM_DESCRIPTION;
344         else if (atom_compare(a, b, c, d, 'p', 'c', 's', 't'))
345                 return ATOM_PODCAST;
346         else
347                 return ATOM_UNKNOWN;
348 }
349
350 /* read atom header, return atom size, atom size is with header included */
351 static uint64_t atom_read_header(struct mp4 *f, uint8_t * atom_type,
352                                 uint8_t * header_size)
353 {
354         uint64_t size;
355         int32_t ret;
356         int8_t atom_header[8];
357
358         ret = read_data(f, atom_header, 8);
359         if (ret != 8)
360                 return 0;
361
362         size = read_u32_be(atom_header);
363         *header_size = 8;
364
365         /* check for 64 bit atom size */
366         if (size == 1) {
367                 *header_size = 16;
368                 size = read_int64(f);
369         }
370         *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
371                 atom_header[6], atom_header[7]);
372         return size;
373 }
374
375 static int64_t get_position(const struct mp4 *f)
376 {
377         return f->current_position;
378 }
379
380 static int need_parse_when_meta_only(uint8_t atom_type)
381 {
382         switch (atom_type) {
383         case ATOM_EDTS:
384         case ATOM_DRMS:
385         case ATOM_SINF:
386         case ATOM_SCHI:
387         case ATOM_STTS:
388         case ATOM_STSZ:
389         case ATOM_STZ2:
390         case ATOM_STCO:
391         case ATOM_STSC:
392         case ATOM_FRMA:
393         case ATOM_IVIV:
394         case ATOM_PRIV:
395                 return 0;
396         default:
397                 return 1;
398         }
399 }
400
401 static int32_t set_position(struct mp4 *f, int64_t position)
402 {
403         f->stream->seek(f->stream->user_data, position);
404         f->current_position = position;
405
406         return 0;
407 }
408
409 static void track_add(struct mp4 *f)
410 {
411         f->total_tracks++;
412
413         if (f->total_tracks > MAX_TRACKS) {
414                 f->total_tracks = 0;
415                 f->error++;
416                 return;
417         }
418         f->track[f->total_tracks - 1] = para_calloc(sizeof(struct mp4_track));
419 }
420
421 static uint8_t read_char(struct mp4 *f)
422 {
423         uint8_t output;
424         read_data(f, &output, 1);
425         return output;
426 }
427
428 static uint32_t read_int24(struct mp4 *f)
429 {
430         int8_t data[4];
431
432         read_data(f, data, 3);
433         return read_u24_be(data);
434 }
435
436 static uint32_t read_int32(struct mp4 *f)
437 {
438         int8_t data[4];
439
440         read_data(f, data, 4);
441         return read_u32_be(data);
442 }
443
444 static int32_t read_stsz(struct mp4 *f)
445 {
446         int32_t i;
447         struct mp4_track *t;
448
449         if (f->total_tracks == 0)
450                 return f->error++;
451         t = f->track[f->total_tracks - 1];
452         read_char(f);   /* version */
453         read_int24(f);  /* flags */
454         t->stsz_sample_size = read_int32(f);
455         t->stsz_sample_count = read_int32(f);
456         if (t->stsz_sample_size != 0)
457                 return 0;
458         t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
459         for (i = 0; i < t->stsz_sample_count && !f->stream->read_error; i++)
460                 t->stsz_table[i] = read_int32(f);
461         return 0;
462 }
463
464 static int32_t read_stts(struct mp4 *f)
465 {
466         int32_t i;
467         struct mp4_track *t;
468
469         /* CVE-2017-9223 */
470         if (f->total_tracks == 0)
471                 return f->error++;
472         t = f->track[f->total_tracks - 1];
473         if (t->stts_entry_count)
474                 return 0;
475         read_char(f);   /* version */
476         read_int24(f);  /* flags */
477         t->stts_entry_count = read_int32(f);
478
479         t->stts_sample_count = para_malloc(t->stts_entry_count
480                 * sizeof(int32_t));
481         t->stts_sample_delta = para_malloc(t->stts_entry_count
482                 * sizeof (int32_t));
483         /* CVE-2017-9254 */
484         for (i = 0; i < t->stts_entry_count && !f->stream->read_error; i++) {
485                 t->stts_sample_count[i] = read_int32(f);
486                 t->stts_sample_delta[i] = read_int32(f);
487         }
488         return 1;
489 }
490
491 static int32_t read_ctts(struct mp4 *f)
492 {
493         int32_t i;
494         struct mp4_track *t;
495
496         if (f->total_tracks == 0)
497                 return f->error++;
498         t = f->track[f->total_tracks - 1];
499         if (t->ctts_entry_count)
500                 return 0;
501
502         read_char(f);   /* version */
503         read_int24(f);  /* flags */
504         t->ctts_entry_count = read_int32(f);
505
506         t->ctts_sample_count = para_malloc(t->ctts_entry_count
507                 * sizeof (int32_t));
508         t->ctts_sample_offset = para_malloc(t->ctts_entry_count
509                 * sizeof (int32_t));
510
511         /* CVE-2017-9257 */
512         for (i = 0; i < t->ctts_entry_count && !f->stream->read_error; i++) {
513                 t->ctts_sample_count[i] = read_int32(f);
514                 t->ctts_sample_offset[i] = read_int32(f);
515         }
516         return 1;
517 }
518
519 static int32_t read_stsc(struct mp4 *f)
520 {
521         int32_t i;
522         struct mp4_track *t;
523
524         if (f->total_tracks == 0)
525                 return f->error++;
526         t = f->track[f->total_tracks - 1];
527
528         read_char(f);   /* version */
529         read_int24(f);  /* flags */
530         t->stsc_entry_count = read_int32(f);
531         t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
532         t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
533                 * sizeof (int32_t));
534         t->stsc_sample_desc_index = para_malloc(t->stsc_entry_count *
535                 sizeof (int32_t));
536
537         /* CVE-2017-9255 */
538         for (i = 0; i < t->stsc_entry_count && !f->stream->read_error; i++) {
539                 t->stsc_first_chunk[i] = read_int32(f);
540                 t->stsc_samples_per_chunk[i] = read_int32(f);
541                 t->stsc_sample_desc_index[i] = read_int32(f);
542         }
543         return 0;
544 }
545
546 static int32_t read_stco(struct mp4 *f)
547 {
548         int32_t i;
549         struct mp4_track *t;
550
551         if (f->total_tracks == 0)
552                 return f->error++;
553         t = f->track[f->total_tracks - 1];
554
555         read_char(f);   /* version */
556         read_int24(f);  /* flags */
557         t->stco_entry_count = read_int32(f);
558         t->stco_chunk_offset = para_malloc(t->stco_entry_count
559                 * sizeof(int32_t));
560         /* CVE-2017-9256 */
561         for (i = 0; i < t->stco_entry_count && !f->stream->read_error; i++)
562                 t->stco_chunk_offset[i] = read_int32(f);
563         return 0;
564 }
565
566 static uint16_t read_int16(struct mp4 *f)
567 {
568         int8_t data[2];
569
570         read_data(f, data, 2);
571         return read_u16_be(data);
572 }
573
574 static int32_t read_mp4a(struct mp4 *f)
575 {
576         int32_t i;
577         uint8_t atom_type = 0;
578         uint8_t header_size = 0;
579         struct mp4_track *t;
580
581         if (f->total_tracks == 0)
582                 return f->error++;
583         t = f->track[f->total_tracks - 1];
584
585         for (i = 0; i < 6; i++) {
586                 read_char(f);   /* reserved */
587         }
588         /* data_reference_index */ read_int16(f);
589
590         read_int32(f);  /* reserved */
591         read_int32(f);  /* reserved */
592
593         t->channelCount = read_int16(f);
594         t->sampleSize = read_int16(f);
595
596         read_int16(f);
597         read_int16(f);
598
599         t->sampleRate = read_int16(f);
600
601         read_int16(f);
602
603         atom_read_header(f, &atom_type, &header_size);
604         return 0;
605 }
606
607 static int32_t read_stsd(struct mp4 *f)
608 {
609         int32_t i;
610         uint8_t header_size = 0;
611         struct mp4_track *t;
612
613         /* CVE-2017-9218 */
614         if (f->total_tracks == 0)
615                 return f->error++;
616         t = f->track[f->total_tracks - 1];
617
618         read_char(f);   /* version */
619         read_int24(f);  /* flags */
620
621         t->stsd_entry_count = read_int32(f);
622
623         /* CVE-2017-9253 */
624         for (i = 0; i < t->stsd_entry_count && !f->stream->read_error; i++) {
625                 uint64_t skip = get_position(f);
626                 uint64_t size;
627                 uint8_t atom_type = 0;
628                 size = atom_read_header(f, &atom_type, &header_size);
629                 skip += size;
630                 t->is_audio = atom_type == ATOM_MP4A;
631                 if (t->is_audio)
632                         read_mp4a(f);
633                 set_position(f, skip);
634         }
635
636         return 0;
637 }
638
639 static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
640                 const char *value, int32_t len)
641 {
642         if (!item || (item && !*item) || !value)
643                 return 0;
644         tags->tags = para_realloc(tags->tags,
645                 (tags->count + 1) * sizeof(struct mp4_tag));
646         tags->tags[tags->count].item = para_strdup(item);
647         tags->tags[tags->count].len = len;
648         if (len >= 0) {
649                 tags->tags[tags->count].value = para_malloc(len + 1);
650                 memcpy(tags->tags[tags->count].value, value, len);
651                 tags->tags[tags->count].value[len] = 0;
652         } else {
653                 tags->tags[tags->count].value = para_strdup(value);
654         }
655         tags->count++;
656         return 1;
657 }
658
659 static const char *ID3v1GenreList[] = {
660         "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
661         "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
662         "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
663         "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
664         "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
665         "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
666         "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
667         "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
668         "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
669         "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
670         "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
671         "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
672         "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
673         "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
674         "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
675         "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
676         "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
677         "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
678         "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
679         "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
680         "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
681         "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
682         "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
683         "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
684         "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
685         "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
686         "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
687         "Anime", "JPop", "SynthPop",
688 };
689
690 static const char *meta_index_to_genre(uint32_t idx)
691 {
692         if (idx > 0 && idx <= sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0])) {
693                 return ID3v1GenreList[idx - 1];
694         } else {
695                 return 0;
696         }
697 }
698
699 static char *read_string(struct mp4 *f, uint32_t length)
700 {
701         char *str = para_malloc(length + 1);
702         if ((uint32_t)read_data(f, str, length) != length) {
703                 free(str);
704                 str = NULL;
705         } else
706                 str[length] = 0;
707         return str;
708 }
709
710 static int32_t set_metadata_name(uint8_t atom_type, char **name)
711 {
712         static char *tag_names[] = {
713                 "unknown", "title", "artist", "writer", "album",
714                 "date", "tool", "comment", "genre", "track",
715                 "disc", "compilation", "genre", "tempo", "cover",
716                 "album_artist", "contentgroup", "lyrics", "description",
717                 "network", "show", "episodename",
718                 "sorttitle", "sortalbum", "sortartist", "sortalbumartist",
719                 "sortwriter", "sortshow",
720                 "season", "episode", "podcast"
721         };
722         uint8_t tag_idx = 0;
723
724         switch (atom_type) {
725         case ATOM_TITLE:
726                 tag_idx = 1;
727                 break;
728         case ATOM_ARTIST:
729                 tag_idx = 2;
730                 break;
731         case ATOM_WRITER:
732                 tag_idx = 3;
733                 break;
734         case ATOM_ALBUM:
735                 tag_idx = 4;
736                 break;
737         case ATOM_DATE:
738                 tag_idx = 5;
739                 break;
740         case ATOM_TOOL:
741                 tag_idx = 6;
742                 break;
743         case ATOM_COMMENT:
744                 tag_idx = 7;
745                 break;
746         case ATOM_GENRE1:
747                 tag_idx = 8;
748                 break;
749         case ATOM_TRACK:
750                 tag_idx = 9;
751                 break;
752         case ATOM_DISC:
753                 tag_idx = 10;
754                 break;
755         case ATOM_COMPILATION:
756                 tag_idx = 11;
757                 break;
758         case ATOM_GENRE2:
759                 tag_idx = 12;
760                 break;
761         case ATOM_TEMPO:
762                 tag_idx = 13;
763                 break;
764         case ATOM_COVER:
765                 tag_idx = 14;
766                 break;
767         case ATOM_ALBUM_ARTIST:
768                 tag_idx = 15;
769                 break;
770         case ATOM_CONTENTGROUP:
771                 tag_idx = 16;
772                 break;
773         case ATOM_LYRICS:
774                 tag_idx = 17;
775                 break;
776         case ATOM_DESCRIPTION:
777                 tag_idx = 18;
778                 break;
779         case ATOM_NETWORK:
780                 tag_idx = 19;
781                 break;
782         case ATOM_SHOW:
783                 tag_idx = 20;
784                 break;
785         case ATOM_EPISODENAME:
786                 tag_idx = 21;
787                 break;
788         case ATOM_SORTTITLE:
789                 tag_idx = 22;
790                 break;
791         case ATOM_SORTALBUM:
792                 tag_idx = 23;
793                 break;
794         case ATOM_SORTARTIST:
795                 tag_idx = 24;
796                 break;
797         case ATOM_SORTALBUMARTIST:
798                 tag_idx = 25;
799                 break;
800         case ATOM_SORTWRITER:
801                 tag_idx = 26;
802                 break;
803         case ATOM_SORTSHOW:
804                 tag_idx = 27;
805                 break;
806         case ATOM_SEASON:
807                 tag_idx = 28;
808                 break;
809         case ATOM_EPISODE:
810                 tag_idx = 29;
811                 break;
812         case ATOM_PODCAST:
813                 tag_idx = 30;
814                 break;
815         default:
816                 tag_idx = 0;
817                 break;
818         }
819
820         *name = para_strdup(tag_names[tag_idx]);
821         return 0;
822 }
823
824 static uint32_t min_body_size(uint8_t atom_type)
825 {
826         switch(atom_type) {
827         case ATOM_GENRE2:
828         case ATOM_TEMPO:
829                 return 10;
830         case ATOM_TRACK:
831                 return sizeof (char) /* version */
832                         + sizeof(uint8_t) * 3 /* flags */
833                         + sizeof(uint32_t) /* reserved */
834                         + sizeof(uint16_t) /* leading uint16_t */
835                         + sizeof(uint16_t) /* track */
836                         + sizeof(uint16_t); /* totaltracks */
837         case ATOM_DISC:
838                 return sizeof (char) /* version */
839                         + sizeof(uint8_t) * 3 /* flags */
840                         + sizeof(uint32_t) /* reserved */
841                         + sizeof(uint16_t) /* disc */
842                         + sizeof(uint16_t); /* totaldiscs */
843         default: assert(false);
844         }
845 }
846
847 static int32_t parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
848 {
849         uint8_t atom_type;
850         uint8_t header_size = 0;
851         uint64_t subsize, sumsize;
852         char *name = NULL;
853         char *data = NULL;
854         uint32_t done = 0;
855         uint32_t len = 0;
856         uint64_t destpos;
857
858         for (
859                 sumsize = 0;
860                 sumsize < size && !f->stream->read_error; /* CVE-2017-9222 */
861                 set_position(f, destpos), sumsize += subsize
862         ) {
863                 subsize = atom_read_header(f, &atom_type, &header_size);
864                 destpos = get_position(f) + subsize - header_size;
865                 if (done)
866                         continue;
867                 if (atom_type == ATOM_NAME) {
868                         read_char(f);   /* version */
869                         read_int24(f);  /* flags */
870                         free(name);
871                         name = read_string(f, subsize - (header_size + 4));
872                         continue;
873                 }
874                 if (atom_type != ATOM_DATA)
875                         continue;
876                 read_char(f);   /* version */
877                 read_int24(f);  /* flags */
878                 read_int32(f);  /* reserved */
879
880                 /* some need special attention */
881                 if (parent == ATOM_GENRE2 || parent == ATOM_TEMPO) {
882                         uint16_t val;
883                         if (subsize - header_size < min_body_size(parent))
884                                 continue;
885                         val = read_int16(f);
886                         if (parent == ATOM_TEMPO) {
887                                 char temp[16];
888                                 sprintf(temp, "%.5u BPM", val);
889                                 tag_add_field(&(f-> tags), "tempo",
890                                         temp, -1);
891                         } else {
892                                 const char *tmp = meta_index_to_genre(val);
893                                 if (tmp)
894                                         tag_add_field (&(f->tags),
895                                                 "genre", tmp, -1);
896                         }
897                         done = 1;
898                 } else if (parent == ATOM_TRACK || parent == ATOM_DISC) {
899                         uint16_t index, total;
900                         char temp[32];
901                         if (subsize - header_size < min_body_size(parent))
902                                 continue;
903                         read_int16(f);
904                         index = read_int16(f);
905                         total = read_int16(f);
906                         if (parent == ATOM_TRACK)
907                                 read_int16(f);
908                         sprintf(temp, "%d", index);
909                         tag_add_field(&(f->tags), parent == ATOM_TRACK?
910                                 "track" : "disc", temp, -1);
911                         if (total > 0) {
912                                 sprintf(temp, "%d", total);
913                                 tag_add_field(& (f-> tags),
914                                         parent == ATOM_TRACK?
915                                         "totaltracks" : "totaldiscs", temp, -1);
916                         }
917                         done = 1;
918                 } else {
919                         free(data);
920                         data = read_string(f, subsize - (header_size + 8));
921                         len = subsize - (header_size + 8);
922                 }
923         }
924         if (data) {
925                 if (!done) {
926                         if (name == NULL)
927                                 set_metadata_name(parent , &name);
928                         if (name)
929                                 tag_add_field(&(f->tags), name, data, len);
930                 }
931
932                 free(data);
933         }
934         free(name);
935         return 1;
936 }
937
938 static int32_t read_mdhd(struct mp4 *f)
939 {
940         uint32_t version;
941         struct mp4_track *t;
942
943         /* CVE-2017-9221 */
944         if (f->total_tracks == 0)
945                 return f->error++;
946         t = f->track[f->total_tracks - 1];
947
948         version = read_int32(f);
949         if (version == 1) {
950                 read_int64(f); //creation-time
951                 read_int64(f); //modification-time
952                 t->timeScale = read_int32(f); //timescale
953                 t->duration = read_int64(f); //duration
954         } else { //version == 0
955                 uint32_t temp;
956
957                 read_int32(f);  //creation-time
958                 read_int32(f);  //modification-time
959                 t->timeScale = read_int32(f); //timescale
960                 temp = read_int32(f);
961                 t->duration = (temp == (uint32_t) (-1))?
962                         (uint64_t) (-1) : (uint64_t) (temp);
963         }
964         read_int16(f);
965         read_int16(f);
966         return 1;
967 }
968
969 static int32_t parse_metadata(struct mp4 *f, int32_t size)
970 {
971         uint64_t subsize, sumsize = 0;
972         uint8_t atom_type;
973         uint8_t header_size = 0;
974
975         while (sumsize < size) {
976                 subsize = atom_read_header(f, &atom_type, &header_size);
977                 if (subsize == 0)
978                         break;
979                 parse_tag(f, atom_type, (uint32_t)(subsize - header_size));
980                 sumsize += subsize;
981         }
982
983         return 0;
984 }
985
986 static int32_t read_meta(struct mp4 *f, uint64_t size)
987 {
988         uint64_t subsize, sumsize = 0;
989         uint8_t atom_type;
990         uint8_t header_size = 0;
991
992         read_char(f);   /* version */
993         read_int24(f);  /* flags */
994
995         while (sumsize < (size - (header_size + 4))) {
996                 subsize = atom_read_header(f, &atom_type, &header_size);
997                 if (subsize <= header_size + 4)
998                         return 1;
999                 if (atom_type == ATOM_ILST) {
1000                         parse_metadata(f, (uint32_t) (subsize - (header_size + 4)));
1001                 } else {
1002                         set_position(f, get_position(f) + subsize - header_size);
1003                 }
1004                 sumsize += subsize;
1005         }
1006
1007         return 0;
1008 }
1009
1010 static int32_t atom_read(struct mp4 *f, int32_t size, uint8_t atom_type)
1011 {
1012         uint64_t dest_position = get_position(f) + size - 8;
1013         if (atom_type == ATOM_STSZ) {
1014                 /* sample size box */
1015                 read_stsz(f);
1016         } else if (atom_type == ATOM_STTS) {
1017                 /* time to sample box */
1018                 read_stts(f);
1019         } else if (atom_type == ATOM_CTTS) {
1020                 /* composition offset box */
1021                 read_ctts(f);
1022         } else if (atom_type == ATOM_STSC) {
1023                 /* sample to chunk box */
1024                 read_stsc(f);
1025         } else if (atom_type == ATOM_STCO) {
1026                 /* chunk offset box */
1027                 read_stco(f);
1028         } else if (atom_type == ATOM_STSD) {
1029                 /* sample description box */
1030                 read_stsd(f);
1031         } else if (atom_type == ATOM_MDHD) {
1032                 /* track header */
1033                 read_mdhd(f);
1034         } else if (atom_type == ATOM_META) {
1035                 /* iTunes Metadata box */
1036                 read_meta(f, size);
1037         }
1038
1039         set_position(f, dest_position);
1040         return 0;
1041 }
1042
1043 /* parse atoms that are sub atoms of other atoms */
1044 static int32_t parse_sub_atoms(struct mp4 *f, uint64_t total_size, int meta_only)
1045 {
1046         uint64_t size;
1047         uint8_t atom_type = 0;
1048         uint64_t counted_size = 0;
1049         uint8_t header_size = 0;
1050
1051         while (counted_size < total_size) {
1052                 size = atom_read_header(f, &atom_type, &header_size);
1053                 counted_size += size;
1054
1055                 /* check for end of file */
1056                 if (size == 0)
1057                         break;
1058
1059                 /* we're starting to read a new track, update index,
1060                  * so that all data and tables get written in the right place
1061                  */
1062                 if (atom_type == ATOM_TRAK)
1063                         track_add(f);
1064                 /* parse subatoms */
1065                 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1066                         set_position(f, get_position(f) + size - header_size);
1067                 } else if (atom_type < SUBATOMIC) {
1068                         parse_sub_atoms(f, size - header_size, meta_only);
1069                 } else {
1070                         atom_read(f, (uint32_t) size, atom_type);
1071                 }
1072         }
1073
1074         return 0;
1075 }
1076
1077 /* parse root atoms */
1078 static int32_t parse_atoms(struct mp4 *f, int meta_only)
1079 {
1080         uint64_t size;
1081         uint8_t atom_type = 0;
1082         uint8_t header_size = 0;
1083
1084         f->file_size = 0;
1085         f->stream->read_error = 0;
1086
1087         while ((size =
1088                 atom_read_header(f, &atom_type, &header_size)) != 0) {
1089                 f->file_size += size;
1090                 f->last_atom = atom_type;
1091
1092                 if (atom_type == ATOM_MOOV && size > header_size) {
1093                         f->moov_offset = get_position(f) - header_size;
1094                         f->moov_size = size;
1095                 }
1096
1097                 /* parse subatoms */
1098                 if (meta_only && !need_parse_when_meta_only(atom_type)) {
1099                         set_position(f, get_position(f) + size - header_size);
1100                 } else if (atom_type < SUBATOMIC) {
1101                         parse_sub_atoms(f, size - header_size, meta_only);
1102                 } else {
1103                         /* skip this atom */
1104                         set_position(f, get_position(f) + size - header_size);
1105                 }
1106         }
1107
1108         return 0;
1109 }
1110
1111 struct mp4 *mp4_open_read(struct mp4_callback *f)
1112 {
1113         struct mp4 *ff = para_calloc(sizeof(struct mp4));
1114
1115         ff->stream = f;
1116
1117         parse_atoms(ff, 0);
1118
1119         if (ff->error) {
1120                 free(ff);
1121                 ff = NULL;
1122         }
1123
1124         return ff;
1125 }
1126
1127 static int32_t tag_delete(struct mp4_metadata *tags)
1128 {
1129         uint32_t i;
1130
1131         for (i = 0; i < tags->count; i++) {
1132                 free(tags->tags[i].item);
1133                 free(tags->tags[i].value);
1134         }
1135         free(tags->tags);
1136         tags->tags = NULL;
1137         tags->count = 0;
1138
1139         return 0;
1140 }
1141
1142 void mp4_close(struct mp4 *ff)
1143 {
1144         int32_t i;
1145
1146         for (i = 0; i < ff->total_tracks; i++) {
1147                 if (ff->track[i]) {
1148                         free(ff->track[i]->stsz_table);
1149                         free(ff->track[i]->stts_sample_count);
1150                         free(ff->track[i]->stts_sample_delta);
1151                         free(ff->track[i]->stsc_first_chunk);
1152                         free(ff->track[i]->stsc_samples_per_chunk);
1153                         free(ff->track[i]->stsc_sample_desc_index);
1154                         free(ff->track[i]->stco_chunk_offset);
1155                         free(ff->track[i]->ctts_sample_count);
1156                         free(ff->track[i]->ctts_sample_offset);
1157                         free(ff->track[i]);
1158                 }
1159         }
1160
1161         tag_delete(&(ff->tags));
1162         free(ff);
1163 }
1164
1165 static int32_t chunk_of_sample(const struct mp4 *f, int32_t track,
1166                 int32_t sample, int32_t *chunk_sample, int32_t *chunk)
1167 {
1168         int32_t total_entries = 0;
1169         int32_t chunk2entry;
1170         int32_t chunk1, chunk2, chunk1samples, range_samples, total = 0;
1171
1172         *chunk_sample = 0;
1173         *chunk = 1;
1174         if (f->track[track] == NULL) {
1175                 return -1;
1176         }
1177
1178         total_entries = f->track[track]->stsc_entry_count;
1179
1180         chunk1 = 1;
1181         chunk1samples = 0;
1182         chunk2entry = 0;
1183
1184         do {
1185                 chunk2 = f->track[track]->stsc_first_chunk[chunk2entry];
1186                 *chunk = chunk2 - chunk1;
1187                 range_samples = *chunk * chunk1samples;
1188
1189                 if (sample < total + range_samples)
1190                         break;
1191
1192                 chunk1samples = f->track[track]->stsc_samples_per_chunk[chunk2entry];
1193                 chunk1 = chunk2;
1194
1195                 if (chunk2entry < total_entries) {
1196                         chunk2entry++;
1197                         total += range_samples;
1198                 }
1199         } while (chunk2entry < total_entries);
1200
1201         if (chunk1samples)
1202                 *chunk = (sample - total) / chunk1samples + chunk1;
1203         else
1204                 *chunk = 1;
1205
1206         *chunk_sample = total + (*chunk - chunk1) * chunk1samples;
1207
1208         return 0;
1209 }
1210
1211 static int32_t chunk_to_offset(const struct mp4 *f, int32_t track,
1212                 int32_t chunk)
1213 {
1214         const struct mp4_track *p_track = f->track[track];
1215
1216         if (p_track->stco_entry_count && (chunk > p_track->stco_entry_count)) {
1217                 return p_track->stco_chunk_offset[p_track->stco_entry_count -
1218                                                   1];
1219         } else if (p_track->stco_entry_count) {
1220                 return p_track->stco_chunk_offset[chunk - 1];
1221         } else {
1222                 return 8;
1223         }
1224
1225         return 0;
1226 }
1227
1228 static int32_t sample_range_size(const struct mp4 *f, int32_t track,
1229                 int32_t chunk_sample, int32_t sample)
1230 {
1231         int32_t i, total;
1232         const struct mp4_track *p_track = f->track[track];
1233
1234         if (p_track->stsz_sample_size) {
1235                 return (sample - chunk_sample) * p_track->stsz_sample_size;
1236         } else {
1237                 if (sample >= p_track->stsz_sample_count)
1238                         return 0;       //error
1239
1240                 for (i = chunk_sample, total = 0; i < sample; i++) {
1241                         total += p_track->stsz_table[i];
1242                 }
1243         }
1244
1245         return total;
1246 }
1247
1248 static int32_t sample_to_offset(const struct mp4 *f, int32_t track,
1249                 int32_t sample)
1250 {
1251         int32_t chunk, chunk_sample, chunk_offset1, chunk_offset2;
1252
1253         chunk_of_sample(f, track, sample, &chunk_sample, &chunk);
1254
1255         chunk_offset1 = chunk_to_offset(f, track, chunk);
1256         chunk_offset2 = chunk_offset1 + sample_range_size(f,
1257                 track, chunk_sample, sample);
1258         return chunk_offset2;
1259 }
1260
1261 /**
1262  * Return the number of milliseconds of the given track.
1263  *
1264  * \param f As returned by \ref mp4_open_read(), must not be NULL.
1265  * \param track Between zero and the value returned by \ref mp4_total_tracks().
1266  *
1267  * The function returns zero if the audio file is of zero length or contains a
1268  * corrupt track header.
1269  */
1270 uint64_t mp4_get_duration(const struct mp4 *f, int32_t track)
1271 {
1272         const struct mp4_track *t = f->track[track];
1273
1274         if (t->timeScale == 0)
1275                 return 0;
1276         return t->duration * 1000 / t->timeScale;
1277 }
1278
1279 /**
1280  * Check whether the given track number corresponds to an audio track.
1281  *
1282  * \param f See \ref mp4_get_duration().
1283  * \param track See \ref mp4_get_duration().
1284  *
1285  * Besides audio tracks, an mp4 file may contain video and system tracks. For
1286  * those the function returns false.
1287  */
1288 bool mp4_is_audio_track(const struct mp4 *f, int32_t track)
1289 {
1290         return f->track[track]->is_audio;
1291 }
1292
1293 void mp4_set_sample_position(struct mp4 *f, int32_t track, int32_t sample)
1294 {
1295         int32_t offset = sample_to_offset(f, track, sample);
1296         set_position(f, offset);
1297 }
1298
1299 int32_t mp4_get_sample_size(const struct mp4 *f, int track, int sample)
1300 {
1301         const struct mp4_track *t = f->track[track];
1302
1303         if (t->stsz_sample_size != 0)
1304                 return t->stsz_sample_size;
1305         return t->stsz_table[sample];
1306 }
1307
1308 uint32_t mp4_get_sample_rate(const struct mp4 *f, int32_t track)
1309 {
1310         return f->track[track]->sampleRate;
1311 }
1312
1313 uint32_t mp4_get_channel_count(const struct mp4 *f, int32_t track)
1314 {
1315         return f->track[track]->channelCount;
1316 }
1317
1318 int32_t mp4_num_samples(const struct mp4 *f, int32_t track)
1319 {
1320         int32_t i;
1321         int32_t total = 0;
1322
1323         for (i = 0; i < f->track[track]->stts_entry_count; i++) {
1324                 total += f->track[track]->stts_sample_count[i];
1325         }
1326         return total;
1327 }
1328
1329 struct mp4 *mp4_open_meta(struct mp4_callback *f)
1330 {
1331         struct mp4 *ff = para_calloc(sizeof(struct mp4));
1332
1333         ff->stream = f;
1334
1335         parse_atoms(ff, 1);
1336
1337         if (ff->error) {
1338                 free(ff);
1339                 ff = NULL;
1340         }
1341
1342         return ff;
1343 }
1344
1345 int32_t mp4_meta_get_num_items(const struct mp4 *f)
1346 {
1347         return f->tags.count;
1348 }
1349
1350 int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
1351                                 char **item, char **value)
1352 {
1353         if (index >= f->tags.count) {
1354                 *item = NULL;
1355                 *value = NULL;
1356                 return 0;
1357         } else {
1358                 *item = para_strdup(f->tags.tags[index].item);
1359                 *value = para_strdup(f->tags.tags[index].value);
1360                 return 1;
1361         }
1362 }
1363
1364 static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
1365                           const char *name)
1366 {
1367         uint32_t remaining = size;
1368         uint64_t atom_offset = base;
1369         for (;;) {
1370                 char atom_name[4];
1371                 uint32_t atom_size;
1372
1373                 set_position(f, atom_offset);
1374
1375                 if (remaining < 8)
1376                         break;
1377                 atom_size = read_int32(f);
1378                 if (atom_size > remaining || atom_size < 8)
1379                         break;
1380                 read_data(f, atom_name, 4);
1381
1382                 if (!memcmp(atom_name, name, 4)) {
1383                         set_position(f, atom_offset);
1384                         return 1;
1385                 }
1386
1387                 remaining -= atom_size;
1388                 atom_offset += atom_size;
1389         }
1390         return 0;
1391 }
1392
1393 static uint32_t find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
1394                 const char *name, uint32_t extraheaders, const char *name_inside)
1395 {
1396         uint64_t first_base = (uint64_t) (-1);
1397         while (find_atom(f, base, size, name))  //try to find atom <name> with atom <name_inside> in it
1398         {
1399                 uint64_t mybase = get_position(f);
1400                 uint32_t mysize = read_int32(f);
1401
1402                 if (first_base == (uint64_t) (-1))
1403                         first_base = mybase;
1404
1405                 if (mysize < 8 + extraheaders)
1406                         break;
1407
1408                 if (find_atom (f, mybase + (8 + extraheaders),
1409                                 mysize - (8 + extraheaders), name_inside)) {
1410                         set_position(f, mybase);
1411                         return 2;
1412                 }
1413                 base += mysize;
1414                 if (size <= mysize) {
1415                         size = 0;
1416                         break;
1417                 }
1418                 size -= mysize;
1419         }
1420
1421         if (first_base != (uint64_t) (-1))      //wanted atom inside not found
1422         {
1423                 set_position(f, first_base);
1424                 return 1;
1425         } else
1426                 return 0;
1427 }
1428
1429 struct membuffer {
1430         void *data;
1431         unsigned written;
1432         unsigned allocated;
1433         unsigned error;
1434 };
1435
1436 static struct membuffer *membuffer_create(void)
1437 {
1438         const unsigned initial_size = 256;
1439
1440         struct membuffer *buf = para_malloc(sizeof(*buf));
1441         buf->data = para_malloc(initial_size);
1442         buf->written = 0;
1443         buf->allocated = initial_size;
1444         buf->error = buf->data == 0 ? 1 : 0;
1445
1446         return buf;
1447 }
1448
1449 static unsigned membuffer_write(struct membuffer *buf, const void *ptr, unsigned bytes)
1450 {
1451         unsigned dest_size = buf->written + bytes;
1452
1453         if (buf->error)
1454                 return 0;
1455         if (dest_size > buf->allocated) {
1456                 do {
1457                         buf->allocated <<= 1;
1458                 } while (dest_size > buf->allocated);
1459                 buf->data = para_realloc(buf->data, buf->allocated);
1460         }
1461
1462         if (ptr)
1463                 memcpy((char *) buf->data + buf->written, ptr, bytes);
1464         buf->written += bytes;
1465         return bytes;
1466 }
1467
1468 static unsigned membuffer_write_atom_name(struct membuffer *buf, const char *data)
1469 {
1470         return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
1471 }
1472
1473 static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
1474 {
1475         uint8_t temp[2];
1476
1477         write_u16_be(temp, data);
1478         return membuffer_write(buf, temp, 2);
1479 }
1480
1481 static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
1482 {
1483         uint8_t temp[4];
1484         write_u32_be(temp, data);
1485         return membuffer_write(buf, temp, 4);
1486 }
1487
1488 static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
1489                 uint32_t index, uint32_t total)
1490 {
1491         membuffer_write_int32(buf,
1492                 8 /*atom header */  + 8 /*data atom header */  +
1493                 8 /*flags + reserved */  + 8 /*actual data */ );
1494         membuffer_write_atom_name(buf, name);
1495         membuffer_write_int32(buf,
1496                 8 /*data atom header */  +
1497                 8 /*flags + reserved */  + 8 /*actual data */ );
1498         membuffer_write_atom_name(buf, "data");
1499         membuffer_write_int32(buf, 0);  //flags
1500         membuffer_write_int32(buf, 0);  //reserved
1501         membuffer_write_int16(buf, 0);
1502         membuffer_write_int16(buf, (uint16_t) index);   //track number
1503         membuffer_write_int16(buf, (uint16_t) total);   //total tracks
1504         membuffer_write_int16(buf, 0);
1505 }
1506
1507 static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
1508                 uint16_t value)
1509 {
1510         membuffer_write_int32(buf,
1511                 8 /*atom header */  + 8 /*data atom header */  +
1512                 8 /*flags + reserved */  + 2 /*actual data */ );
1513         membuffer_write_atom_name(buf, name);
1514         membuffer_write_int32(buf,
1515                 8 /*data atom header */  +
1516                 8 /*flags + reserved */  + 2 /*actual data */ );
1517         membuffer_write_atom_name(buf, "data");
1518         membuffer_write_int32(buf, 0);  //flags
1519         membuffer_write_int32(buf, 0);  //reserved
1520         membuffer_write_int16(buf, value);      //value
1521 }
1522
1523 static uint32_t myatoi(const char *param)
1524 {
1525         return param ? atoi(param) : 0;
1526 }
1527
1528 static uint32_t meta_genre_to_index(const char *genrestr)
1529 {
1530         unsigned n;
1531         for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
1532                 if (!strcasecmp(genrestr, ID3v1GenreList[n]))
1533                         return n + 1;
1534         }
1535         return 0;
1536 }
1537
1538 struct stdmeta_entry {
1539         const char *atom;
1540         const char *name;
1541 };
1542
1543 struct stdmeta_entry stdmetas[] = {
1544         {"\xA9" "nam", "title"},
1545         {"\xA9" "ART", "artist"},
1546         {"\xA9" "wrt", "writer"},
1547         {"\xA9" "alb", "album"},
1548         {"\xA9" "day", "date"},
1549         {"\xA9" "too", "tool"},
1550         {"\xA9" "cmt", "comment"},
1551         {"cpil", "compilation"},
1552         {"covr", "cover"},
1553         {"aART", "album_artist"},
1554 };
1555
1556 static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
1557 {
1558         unsigned n;
1559         for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
1560                 if (!strcasecmp(name, stdmetas[n].name))
1561                         return stdmetas[n].atom;
1562         }
1563         return 0;
1564 }
1565
1566 static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
1567                 const char *value)
1568 {
1569         uint32_t flags = 1;
1570
1571         /* special check for compilation flag */
1572         if (strcmp(name, "cpil") == 0) {
1573                 flags = 21;
1574         }
1575
1576         membuffer_write_int32(buf,
1577                 8 /*atom header */  + 8 /*data atom header */  +
1578                 8 /*flags + reserved */  + strlen(value));
1579         membuffer_write_atom_name(buf, name);
1580         membuffer_write_int32(buf,
1581                 8 /*data atom header */  +
1582                 8 /*flags + reserved */  + strlen(value));
1583         membuffer_write_atom_name(buf, "data");
1584         membuffer_write_int32(buf, flags);      //flags
1585         membuffer_write_int32(buf, 0);  //reserved
1586         membuffer_write(buf, value, strlen(value));
1587 }
1588
1589 static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
1590                 const char *value)
1591 {
1592         membuffer_write_int32(buf,
1593                 8 /*atom header */  +
1594                 0x1C /*weirdo itunes atom */  +
1595                 12 /*name atom header */  + strlen(name) +
1596                 16 /*data atom header + flags */  + strlen(value));
1597         membuffer_write_atom_name(buf, "----");
1598         membuffer_write_int32(buf, 0x1C);       //weirdo itunes atom
1599         membuffer_write_atom_name(buf, "mean");
1600         membuffer_write_int32(buf, 0);
1601         membuffer_write(buf, "com.apple.iTunes", 16);
1602         membuffer_write_int32(buf, 12 + strlen(name));
1603         membuffer_write_atom_name(buf, "name");
1604         membuffer_write_int32(buf, 0);
1605         membuffer_write(buf, name, strlen(name));
1606         membuffer_write_int32(buf,
1607                 8 /*data atom header */  +
1608                 8 /*flags + reserved */  + strlen(value));
1609         membuffer_write_atom_name(buf, "data");
1610         membuffer_write_int32(buf, 1);  //flags
1611         membuffer_write_int32(buf, 0);  //reserved
1612         membuffer_write(buf, value, strlen(value));
1613 }
1614
1615 static unsigned membuffer_error(const struct membuffer *buf)
1616 {
1617         return buf->error;
1618 }
1619
1620 static void membuffer_free(struct membuffer *buf)
1621 {
1622         free(buf->data);
1623         free(buf);
1624 }
1625
1626 static unsigned membuffer_get_size(const struct membuffer *buf)
1627 {
1628         return buf->written;
1629 }
1630
1631 static void *membuffer_detach(struct membuffer *buf)
1632 {
1633         void *ret;
1634
1635         if (buf->error)
1636                 return 0;
1637         ret = para_realloc(buf->data, buf->written);
1638         buf->data = 0;
1639         buf->error = 1;
1640         return ret;
1641 }
1642
1643 static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
1644                 uint32_t * out_size)
1645 {
1646         struct membuffer *buf = membuffer_create();
1647         unsigned metaptr;
1648         char *mask = para_calloc(data->count);
1649         const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
1650         const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
1651         const char *genre_ptr = 0, *tempo_ptr = 0;
1652
1653         for (metaptr = 0; metaptr < data->count; metaptr++) {
1654                 struct mp4_tag *tag = &data->tags[metaptr];
1655                 if (!strcasecmp(tag->item, "tracknumber")
1656                                 || !strcasecmp(tag->item, "track")) {
1657                         if (tracknumber_ptr == 0)
1658                                 tracknumber_ptr = tag->value;
1659                         mask[metaptr] = 1;
1660                 } else if (!strcasecmp(tag->item, "totaltracks")) {
1661                         if (totaltracks_ptr == 0)
1662                                 totaltracks_ptr = tag->value;
1663                         mask[metaptr] = 1;
1664                 } else if (!strcasecmp(tag->item, "discnumber")
1665                                 || !strcasecmp(tag->item, "disc")) {
1666                         if (discnumber_ptr == 0)
1667                                 discnumber_ptr = tag->value;
1668                         mask[metaptr] = 1;
1669                 } else if (!strcasecmp(tag->item, "totaldiscs")) {
1670                         if (totaldiscs_ptr == 0)
1671                                 totaldiscs_ptr = tag->value;
1672                         mask[metaptr] = 1;
1673                 } else if (!strcasecmp(tag->item, "genre")) {
1674                         if (genre_ptr == 0)
1675                                 genre_ptr = tag->value;
1676                         mask[metaptr] = 1;
1677                 } else if (!strcasecmp(tag->item, "tempo")) {
1678                         if (tempo_ptr == 0)
1679                                 tempo_ptr = tag->value;
1680                         mask[metaptr] = 1;
1681                 }
1682         }
1683
1684         if (tracknumber_ptr)
1685                 membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
1686                          myatoi(totaltracks_ptr));
1687         if (discnumber_ptr)
1688                 membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
1689                          myatoi(totaldiscs_ptr));
1690         if (tempo_ptr)
1691                 membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
1692
1693         if (genre_ptr) {
1694                 uint32_t index = meta_genre_to_index(genre_ptr);
1695                 if (index == 0)
1696                         membuffer_write_std_tag(buf, "©gen", genre_ptr);
1697                 else
1698                         membuffer_write_int16_tag(buf, "gnre", index);
1699         }
1700         for (metaptr = 0; metaptr < data->count; metaptr++) {
1701                 struct mp4_tag *tag;
1702                 const char *std_meta_atom;
1703
1704                 if (mask[metaptr])
1705                         continue;
1706                 tag = &data->tags[metaptr];
1707                 std_meta_atom = find_standard_meta(tag->item);
1708                 if (std_meta_atom)
1709                         membuffer_write_std_tag(buf, std_meta_atom, tag->value);
1710                 else
1711                         membuffer_write_custom_tag(buf, tag->item, tag->value);
1712         }
1713         free(mask);
1714
1715         if (membuffer_error(buf)) {
1716                 membuffer_free(buf);
1717                 return 0;
1718         }
1719
1720         *out_size = membuffer_get_size(buf);
1721         *out_buffer = membuffer_detach(buf);
1722         membuffer_free(buf);
1723
1724         return 1;
1725 }
1726
1727 static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
1728                           const void *data)
1729 {
1730         membuffer_write_int32(buf, size + 8);
1731         membuffer_write_atom_name(buf, name);
1732         membuffer_write(buf, data, size);
1733 }
1734
1735 static void *membuffer_get_ptr(const struct membuffer *buf)
1736 {
1737         return buf->data;
1738 }
1739
1740 static void membuffer_set_error(struct membuffer *buf)
1741 {
1742         buf->error = 1;
1743 }
1744
1745 static unsigned membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
1746                 unsigned bytes)
1747 {
1748         unsigned oldsize;
1749         void *bufptr;
1750
1751         oldsize = membuffer_get_size(buf);
1752         if (membuffer_write(buf, 0, bytes) != bytes)
1753                 return 0;
1754
1755         bufptr = membuffer_get_ptr(buf);
1756         if (bufptr == 0)
1757                 return 0;
1758
1759         if ((unsigned)read_data(src, (char *) bufptr + oldsize, bytes) !=
1760                 bytes) {
1761                 membuffer_set_error(buf);
1762                 return 0;
1763         }
1764
1765         return bytes;
1766 }
1767
1768 static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
1769                 uint32_t * out_size)
1770 {
1771         struct membuffer *buf;
1772         uint32_t ilst_size;
1773         void *ilst_buffer;
1774
1775         if (!create_ilst(data, &ilst_buffer, &ilst_size))
1776                 return 0;
1777
1778         buf = membuffer_create();
1779
1780         membuffer_write_int32(buf, 0);
1781         membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
1782         free(ilst_buffer);
1783
1784         *out_size = membuffer_get_size(buf);
1785         *out_buffer = membuffer_detach(buf);
1786         membuffer_free(buf);
1787         return 1;
1788 }
1789
1790 static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
1791 uint32_t * out_size)
1792 {
1793         struct membuffer *buf;
1794         uint32_t meta_size;
1795         void *meta_buffer;
1796
1797         if (!create_meta(data, &meta_buffer, &meta_size))
1798                 return 0;
1799
1800         buf = membuffer_create();
1801
1802         membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
1803
1804         free(meta_buffer);
1805
1806         *out_size = membuffer_get_size(buf);
1807         *out_buffer = membuffer_detach(buf);
1808         membuffer_free(buf);
1809         return 1;
1810 }
1811
1812 static uint32_t fix_byte_order_32(uint32_t src)
1813 {
1814         return read_u32_be(&src);
1815 }
1816
1817 static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
1818                 void **out_buffer, uint32_t * out_size)
1819 {
1820         uint64_t total_base = f->moov_offset + 8;
1821         uint32_t total_size = (uint32_t) (f->moov_size - 8);
1822
1823         uint64_t udta_offset, meta_offset, ilst_offset;
1824         uint32_t udta_size, meta_size, ilst_size;
1825
1826         uint32_t new_ilst_size;
1827         void *new_ilst_buffer;
1828
1829         uint8_t *p_out;
1830         int32_t size_delta;
1831
1832         if (!find_atom_v2(f, total_base, total_size, "udta", 0, "meta")) {
1833                 struct membuffer *buf;
1834                 void *new_udta_buffer;
1835                 uint32_t new_udta_size;
1836                 if (!create_udta(data, &new_udta_buffer, &new_udta_size))
1837                         return 0;
1838
1839                 buf = membuffer_create();
1840                 set_position(f, total_base);
1841                 membuffer_transfer_from_file(buf, f, total_size);
1842
1843                 membuffer_write_atom(buf, "udta", new_udta_size,
1844                         new_udta_buffer);
1845
1846                 free(new_udta_buffer);
1847
1848                 *out_size = membuffer_get_size(buf);
1849                 *out_buffer = membuffer_detach(buf);
1850                 membuffer_free(buf);
1851                 return 1;
1852         } else {
1853                 udta_offset = get_position(f);
1854                 udta_size = read_int32(f);
1855                 if (!find_atom_v2 (f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst")) {
1856                         struct membuffer *buf;
1857                         void *new_meta_buffer;
1858                         uint32_t new_meta_size;
1859                         if (!create_meta(data, &new_meta_buffer, &new_meta_size))
1860                                 return 0;
1861
1862                         buf = membuffer_create();
1863                         set_position(f, total_base);
1864                         membuffer_transfer_from_file(buf, f,
1865                                 (uint32_t)(udta_offset - total_base));
1866
1867                         membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
1868                         membuffer_write_atom_name(buf, "udta");
1869                         membuffer_transfer_from_file(buf, f, udta_size);
1870
1871                         membuffer_write_atom(buf, "meta", new_meta_size,
1872                                 new_meta_buffer);
1873                         free(new_meta_buffer);
1874
1875                         *out_size = membuffer_get_size(buf);
1876                         *out_buffer = membuffer_detach(buf);
1877                         membuffer_free(buf);
1878                         return 1;
1879                 }
1880                 meta_offset = get_position(f);
1881                 meta_size = read_int32(f);
1882                 if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
1883                         return 0;       //shouldn't happen, find_atom_v2 above takes care of it
1884                 ilst_offset = get_position(f);
1885                 ilst_size = read_int32(f);
1886
1887                 if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
1888                         return 0;
1889
1890                 size_delta = new_ilst_size - (ilst_size - 8);
1891
1892                 *out_size = total_size + size_delta;
1893                 *out_buffer = para_malloc(*out_size);
1894                 p_out = (uint8_t *) * out_buffer;
1895
1896                 set_position(f, total_base);
1897                 read_data(f, p_out,
1898                                 (uint32_t) (udta_offset - total_base));
1899                 p_out += (uint32_t) (udta_offset - total_base);
1900                 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1901                 p_out += 4;
1902                 read_data(f, p_out, 4);
1903                 p_out += 4;
1904                 read_data(f, p_out,
1905                                 (uint32_t) (meta_offset - udta_offset - 8));
1906                 p_out += (uint32_t) (meta_offset - udta_offset - 8);
1907                 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1908                 p_out += 4;
1909                 read_data(f, p_out, 4);
1910                 p_out += 4;
1911                 read_data(f, p_out,
1912                                 (uint32_t) (ilst_offset - meta_offset - 8));
1913                 p_out += (uint32_t) (ilst_offset - meta_offset - 8);
1914                 *(uint32_t *) p_out = fix_byte_order_32(read_int32(f) + size_delta);
1915                 p_out += 4;
1916                 read_data(f, p_out, 4);
1917                 p_out += 4;
1918
1919                 memcpy(p_out, new_ilst_buffer, new_ilst_size);
1920                 p_out += new_ilst_size;
1921
1922                 set_position(f, ilst_offset + ilst_size);
1923                 read_data(f, p_out, (uint32_t) (total_size
1924                         - (ilst_offset - total_base) - ilst_size));
1925
1926                 free(new_ilst_buffer);
1927         }
1928         return 1;
1929 }
1930
1931 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1932 {
1933         int32_t result = 1;
1934
1935         result = f->stream->write(f->stream->user_data, data, size);
1936
1937         f->current_position += size;
1938
1939         return result;
1940 }
1941
1942 static int32_t write_int32(struct mp4 *f, uint32_t data)
1943 {
1944         int8_t temp[4];
1945         write_u32_be(temp, data);
1946         return write_data(f, temp, sizeof(temp));
1947 }
1948
1949 static int32_t truncate_stream(struct mp4 *f)
1950 {
1951         return f->stream->truncate(f->stream->user_data);
1952 }
1953
1954 int32_t mp4_meta_update(struct mp4_callback *f, const struct mp4_metadata *data)
1955 {
1956         void *new_moov_data;
1957         uint32_t new_moov_size;
1958
1959         struct mp4 *ff = para_calloc(sizeof(struct mp4));
1960         ff->stream = f;
1961         set_position(ff, 0);
1962
1963         parse_atoms(ff, 1);
1964
1965         if (!modify_moov(ff, data, &new_moov_data, &new_moov_size)) {
1966                 mp4_close(ff);
1967                 return 0;
1968         }
1969
1970         /* copy moov atom to end of the file */
1971         if (ff->last_atom != ATOM_MOOV) {
1972                 char *free_data = "free";
1973
1974                 /* rename old moov to free */
1975                 set_position(ff, ff->moov_offset + 4);
1976                 write_data(ff, free_data, 4);
1977
1978                 set_position(ff, ff->file_size);
1979                 write_int32(ff, new_moov_size + 8);
1980                 write_data(ff, "moov", 4);
1981                 write_data(ff, new_moov_data, new_moov_size);
1982         } else {
1983                 set_position(ff, ff->moov_offset);
1984                 write_int32(ff, new_moov_size + 8);
1985                 write_data(ff, "moov", 4);
1986                 write_data(ff, new_moov_data, new_moov_size);
1987         }
1988
1989         truncate_stream(ff);
1990
1991         mp4_close(ff);
1992         return 1;
1993 }
1994
1995 /* find a metadata item by name */
1996 /* returns 0 if item found, 1 if no such item */
1997 static int32_t meta_find_by_name(const struct mp4 *f, const char *item,
1998                 char **value)
1999 {
2000         uint32_t i;
2001
2002         for (i = 0; i < f->tags.count; i++) {
2003                 if (!strcasecmp(f->tags.tags[i].item, item)) {
2004                         *value = para_strdup(f->tags.tags[i].value);
2005                         return 1;
2006                 }
2007         }
2008
2009         *value = NULL;
2010
2011         /* not found */
2012         return 0;
2013 }
2014
2015 int32_t mp4_meta_get_artist(const struct mp4 *f, char **value)
2016 {
2017         return meta_find_by_name(f, "artist", value);
2018 }
2019
2020 int32_t mp4_meta_get_title(const struct mp4 *f, char **value)
2021 {
2022         return meta_find_by_name(f, "title", value);
2023 }
2024
2025 int32_t mp4_meta_get_date(const struct mp4 *f, char **value)
2026 {
2027         return meta_find_by_name(f, "date", value);
2028 }
2029
2030 int32_t mp4_meta_get_album(const struct mp4 *f, char **value)
2031 {
2032         return meta_find_by_name(f, "album", value);
2033 }
2034
2035 int32_t mp4_meta_get_comment(const struct mp4 *f, char **value)
2036 {
2037         return meta_find_by_name(f, "comment", value);
2038 }