2 * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
3 * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
12 #include "portable_io.h"
17 uint16_t channelCount;
21 uint32_t stsz_sample_size;
22 uint32_t stsz_sample_count;
26 uint32_t stts_entry_count;
27 uint32_t *stts_sample_count;
30 uint32_t stsc_entry_count;
31 uint32_t *stsc_first_chunk;
32 uint32_t *stsc_samples_per_chunk;
35 uint32_t stco_entry_count;
36 uint32_t *stco_chunk_offset;
42 #define MAX_TRACKS 1024
45 const struct mp4_callback *cb;
46 int64_t current_position;
60 /* incremental track index while reading the file */
63 struct mp4_track *track[MAX_TRACKS];
64 /* the first audio track found */
65 struct mp4_track *audio_track;
68 struct mp4_metadata meta;
72 * Returns -1, 0, or 1 on errors/EOF/success. Partial reads followed by EOF or
73 * read errors are treated as errors.
75 static int read_data(struct mp4 *f, void *data, size_t size)
78 ssize_t ret = f->cb->read(f->cb->user_data, data, size);
79 if (ret < 0 && errno == EINTR)
81 /* regard EAGAIN as an error as reads should be blocking. */
83 return ret < 0? -1 : 0;
84 f->current_position += ret;
90 static int read_int64(struct mp4 *f, uint64_t *result)
93 int ret = read_data(f, data, 8);
95 if (ret > 0 && result)
96 *result = read_u64_be(data);
100 static int read_int32(struct mp4 *f, uint32_t *result)
103 int ret = read_data(f, data, 4);
105 if (ret > 0 && result)
106 *result = read_u32_be(data);
110 static int read_int24(struct mp4 *f, uint32_t *result)
113 int ret = read_data(f, data, 3);
115 if (ret > 0 && result)
116 *result = read_u24_be(data);
120 static int read_int16(struct mp4 *f, uint16_t *result)
123 int ret = read_data(f, data, 2);
125 if (ret > 0 && result)
126 *result = read_u16_be(data);
130 static uint8_t read_int8(struct mp4 *f, uint8_t *result)
133 int ret = read_data(f, data, 1);
135 if (ret > 0 && result)
140 static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
141 int8_t a2, int8_t b2, int8_t c2, int8_t d2)
143 return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
147 /* atoms with subatoms */
154 ATOM_ILST, /* iTunes Metadata list */
161 /* atoms without subatoms */
162 ATOM_MDHD, /* track header */
163 ATOM_STSD, /* sample description box */
164 ATOM_STTS, /* time to sample box */
165 ATOM_STSZ, /* sample size box */
166 ATOM_STCO, /* chunk offset box */
167 ATOM_STSC, /* sample to chunk box */
169 ATOM_META, /* iTunes Metadata box */
170 ATOM_DATA, /* iTunes Metadata data box */
174 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
176 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
179 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
181 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
183 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
185 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
187 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
189 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
191 } else if (a == 't') {
192 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
194 } else if (a == 's') {
195 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
197 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
199 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
201 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
203 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
205 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
207 } else if (a == COPYRIGHT_SYMBOL) {
208 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
210 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
212 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
214 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
216 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
219 if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
221 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
223 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
229 /* read atom header, atom size is returned with header included. */
230 static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
231 uint8_t *header_size, uint64_t *atom_size)
235 int8_t atom_header[8];
237 ret = read_data(f, atom_header, 8);
240 size = read_u32_be(atom_header);
241 if (size == 1) { /* 64 bit atom size */
244 ret = read_int64(f, atom_size);
253 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
254 atom_header[6], atom_header[7]);
258 static int64_t get_position(const struct mp4 *f)
260 return f->current_position;
263 static int32_t set_position(struct mp4 *f, int64_t position)
265 f->cb->seek(f->cb->user_data, position);
266 f->current_position = position;
271 static int read_stsz(struct mp4 *f)
277 if (f->total_tracks == 0)
279 t = f->track[f->total_tracks - 1];
280 ret = read_int8(f, NULL); /* version */
283 ret = read_int24(f, NULL); /* flags */
286 ret = read_int32(f, &t->stsz_sample_size);
289 ret = read_int32(f, &t->stsz_sample_count);
292 if (t->stsz_sample_size != 0)
294 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
295 for (i = 0; i < t->stsz_sample_count; i++) {
296 ret = read_int32(f, &t->stsz_table[i]);
303 static int read_stts(struct mp4 *f)
309 if (f->total_tracks == 0)
311 t = f->track[f->total_tracks - 1];
312 if (t->stts_entry_count)
314 ret = read_int8(f, NULL); /* version */
317 ret = read_int24(f, NULL); /* flags */
320 ret = read_int32(f, &t->stts_entry_count);
323 t->stts_sample_count = para_malloc(t->stts_entry_count
325 for (i = 0; i < t->stts_entry_count; i++) {
326 ret = read_int32(f, &t->stts_sample_count[i]);
329 ret = read_int32(f, NULL); /* sample delta */
336 static int read_stsc(struct mp4 *f)
342 if (f->total_tracks == 0)
344 t = f->track[f->total_tracks - 1];
346 ret = read_int8(f, NULL); /* version */
349 ret = read_int24(f, NULL); /* flags */
352 ret = read_int32(f, &t->stsc_entry_count);
355 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
356 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
358 for (i = 0; i < t->stsc_entry_count; i++) {
359 ret = read_int32(f, &t->stsc_first_chunk[i]);
362 ret = read_int32(f, &t->stsc_samples_per_chunk[i]);
365 ret = read_int32(f, NULL); /* sample desc index */
372 static int read_stco(struct mp4 *f)
378 if (f->total_tracks == 0)
380 t = f->track[f->total_tracks - 1];
382 ret = read_int8(f, NULL); /* version */
385 ret = read_int24(f, NULL); /* flags */
388 ret = read_int32(f, &t->stco_entry_count);
391 t->stco_chunk_offset = para_malloc(t->stco_entry_count
393 for (i = 0; i < t->stco_entry_count; i++) {
394 ret = read_int32(f, &t->stco_chunk_offset[i]);
401 static int read_mp4a(struct mp4 *f)
407 if (f->total_tracks == 0)
409 t = f->track[f->total_tracks - 1];
411 for (i = 0; i < 6; i++) {
412 ret = read_int8(f, NULL); /* reserved */
416 ret = read_int16(f, NULL); /* data_reference_index */
419 ret = read_int32(f, NULL); /* reserved */
422 ret = read_int32(f, NULL); /* reserved */
425 ret = read_int16(f, &t->channelCount);
428 ret = read_int16(f, NULL);
431 ret = read_int16(f, NULL);
434 ret = read_int16(f, NULL);
437 return read_int16(f, &t->sampleRate);
440 static int read_stsd(struct mp4 *f)
443 uint32_t i, entry_count;
446 if (f->total_tracks == 0)
448 t = f->track[f->total_tracks - 1];
449 ret = read_int8(f, NULL); /* version */
452 ret = read_int24(f, NULL); /* flags */
455 ret = read_int32(f, &entry_count);
458 for (i = 0; i < entry_count; i++) {
459 uint64_t skip = get_position(f);
461 uint8_t atom_type = 0;
462 ret = atom_read_header(f, &atom_type, NULL, &size);
466 if (!f->audio_track && atom_type == ATOM_MP4A) {
470 set_position(f, skip);
475 static const char *get_metadata_name(uint8_t atom_type)
478 case ATOM_TITLE: return "title";
479 case ATOM_ARTIST: return "artist";
480 case ATOM_ALBUM: return "album";
481 case ATOM_DATE: return "date";
482 case ATOM_COMMENT: return "comment";
483 default: return "unknown";
487 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
490 uint64_t subsize, sumsize;
499 set_position(f, destpos), sumsize += subsize
502 uint8_t header_size = 0;
503 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
506 destpos = get_position(f) + subsize - header_size;
507 if (atom_type != ATOM_DATA)
509 ret = read_int8(f, NULL); /* version */
512 ret = read_int24(f, NULL); /* flags */
515 ret = read_int32(f, NULL); /* reserved */
518 ret = -ERRNO_TO_PARA_ERROR(EINVAL);
519 if (subsize < header_size + 8 || subsize > UINT_MAX)
521 len = subsize - (header_size + 8);
523 value = para_malloc(len + 1);
524 ret = read_data(f, value, len);
530 return -ERRNO_TO_PARA_ERROR(EINVAL);
531 f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
532 * sizeof(struct mp4_tag));
533 tag = f->meta.tags + f->meta.count;
534 tag->item = para_strdup(get_metadata_name(parent));
544 static int read_mdhd(struct mp4 *f)
550 if (f->total_tracks == 0)
552 t = f->track[f->total_tracks - 1];
554 ret = read_int32(f, &version);
558 ret = read_int64(f, NULL); /* creation-time */
561 ret = read_int64(f, NULL); /* modification-time */
564 ret = read_int32(f, &t->timeScale);
567 ret = read_int64(f, &t->duration);
570 } else { //version == 0
573 ret = read_int32(f, NULL); /* creation-time */
576 ret = read_int32(f, NULL); /* modification-time */
579 ret = read_int32(f, &t->timeScale);
582 ret = read_int32(f, &temp);
585 t->duration = (temp == (uint32_t) (-1))?
586 (uint64_t) (-1) : (uint64_t) (temp);
588 ret = read_int16(f, NULL);
591 ret = read_int16(f, NULL);
597 static int32_t read_ilst(struct mp4 *f, int32_t size)
600 uint64_t sumsize = 0;
602 while (sumsize < size) {
604 uint64_t subsize, destpos;
605 uint8_t header_size = 0;
606 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
609 destpos = get_position(f) + subsize - header_size;
616 ret = parse_tag(f, atom_type, subsize - header_size);
620 set_position(f, destpos);
626 static int32_t read_meta(struct mp4 *f, uint64_t size)
629 uint64_t subsize, sumsize = 0;
631 uint8_t header_size = 0;
633 ret = read_int8(f, NULL); /* version */
636 ret = read_int24(f, NULL); /* flags */
639 while (sumsize < (size - (header_size + 4))) {
640 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
643 if (subsize <= header_size + 4)
645 if (atom_type == ATOM_ILST) {
646 f->ilst_offset = get_position(f) - header_size;
647 f->ilst_size = subsize;
648 ret = read_ilst(f, subsize - (header_size + 4));
652 set_position(f, get_position(f) + subsize - header_size);
658 static bool need_atom(uint8_t atom_type, bool meta_only)
660 /* these are needed in any case */
671 /* meta-only opens don't need anything else */
674 /* these are only required for regular opens */
686 /* parse atoms that are sub atoms of other atoms */
687 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
690 uint64_t dest, size, end = get_position(f) + total_size;
692 for (dest = get_position(f); dest < end; set_position(f, dest)) {
693 uint8_t header_size, atom_type;
694 ret = atom_read_header(f, &atom_type, &header_size, &size);
699 dest = get_position(f) + size - header_size;
700 if (atom_type == ATOM_TRAK) {
701 if (f->total_tracks >= MAX_TRACKS)
704 f->track[f->total_tracks - 1] = para_calloc(
705 sizeof(struct mp4_track));
706 } else if (atom_type == ATOM_UDTA) {
707 f->udta_offset = get_position(f) - header_size;
710 if (!need_atom(atom_type, meta_only))
713 case ATOM_STSZ: ret = read_stsz(f); break;
714 case ATOM_STTS: ret = read_stts(f); break;
715 case ATOM_STSC: ret = read_stsc(f); break;
716 case ATOM_STCO: ret = read_stco(f); break;
717 case ATOM_STSD: ret = read_stsd(f); break;
718 case ATOM_MDHD: ret = read_mdhd(f); break;
720 f->meta_offset = get_position(f) - header_size;
722 ret = read_meta(f, size);
725 ret = parse_sub_atoms(f, size - header_size, meta_only);
733 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
737 uint8_t atom_type, header_size;
738 struct mp4 *f = para_calloc(sizeof(*f));
741 while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
742 f->file_size += size;
743 f->last_atom = atom_type;
744 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
745 set_position(f, get_position(f) + size - header_size);
748 f->moov_offset = get_position(f) - header_size;
750 ret = parse_sub_atoms(f, size - header_size, meta_only);
769 int mp4_open_read(const struct mp4_callback *cb, struct mp4 **result)
771 return open_file(cb, false, result);
774 void mp4_close(struct mp4 *f)
778 for (i = 0; i < f->total_tracks; i++) {
780 free(f->track[i]->stsz_table);
781 free(f->track[i]->stts_sample_count);
782 free(f->track[i]->stsc_first_chunk);
783 free(f->track[i]->stsc_samples_per_chunk);
784 free(f->track[i]->stco_chunk_offset);
788 for (i = 0; i < f->meta.count; i++) {
789 free(f->meta.tags[i].item);
790 free(f->meta.tags[i].value);
796 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
799 const struct mp4_track *t = f->audio_track;
800 uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
801 int32_t chunk1, chunk1samples, n, total, i;
803 for (i = 1, total = 0; i < t->stsc_entry_count; i++, total += n) {
804 n = (fc[i] - fc[i - 1]) * spc[i - 1]; /* number of samples */
805 if (sample < total + n)
809 chunk1samples = spc[i - 1];
810 if (chunk1samples != 0)
811 *chunk = (sample - total) / chunk1samples + chunk1;
814 return total + (*chunk - chunk1) * chunk1samples;
818 * Return the number of milliseconds of the audio track.
820 * \param f As returned by \ref mp4_open_read(), must not be NULL.
822 uint64_t mp4_get_duration(const struct mp4 *f)
824 const struct mp4_track *t = f->audio_track;
826 if (t->timeScale == 0)
828 return t->duration * 1000 / t->timeScale;
831 int mp4_set_sample_position(struct mp4 *f, int32_t sample)
833 const struct mp4_track *t = f->audio_track;
834 int32_t offset, chunk, chunk_sample;
835 uint32_t n, srs; /* sample range size */
837 if (sample >= t->stsz_sample_count)
838 return -ERRNO_TO_PARA_ERROR(EINVAL);
839 chunk_sample = chunk_of_sample(f, sample, &chunk);
840 if (t->stsz_sample_size > 0)
841 srs = (sample - chunk_sample) * t->stsz_sample_size;
843 for (srs = 0, n = chunk_sample; n < sample; n++)
844 srs += t->stsz_table[n];
846 if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
847 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
848 else if (t->stco_entry_count > 0)
849 offset = t->stco_chunk_offset[chunk - 1];
852 set_position(f, offset + srs);
856 int32_t mp4_get_sample_size(const struct mp4 *f, int sample)
858 const struct mp4_track *t = f->audio_track;
860 if (t->stsz_sample_size != 0)
861 return t->stsz_sample_size;
862 return t->stsz_table[sample];
865 uint32_t mp4_get_sample_rate(const struct mp4 *f)
867 return f->audio_track->sampleRate;
870 uint32_t mp4_get_channel_count(const struct mp4 *f)
872 return f->audio_track->channelCount ;
875 int32_t mp4_num_samples(const struct mp4 *f)
877 const struct mp4_track *t = f->audio_track;
881 for (i = 0; i < t->stts_entry_count; i++)
882 total += t->stts_sample_count[i];
886 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
889 int ret = open_file(cb, true, &f);
893 if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0) {
896 return -E_MP4_MISSING_ATOM;
903 * Return the metadata of an mp4 file.
905 * \param f As returned by either \ref mp4_open_read() or \ref mp4_open_meta().
907 * The caller is allowed to add, delete or modify the entries of the returned
908 * structure in order to pass the modified version to \ref mp4_meta_update().
910 struct mp4_metadata *mp4_get_meta(struct mp4 *f)
915 /** Total length of an on-disk metadata tag. */
916 #define TAG_LEN(_len) (24 + (_len))
917 static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
919 for (unsigned n = 0; n < meta->count; n++) {
920 struct mp4_tag *tag = meta->tags + n;
921 unsigned len = strlen(tag->value);
922 const char *atom_name;
924 if (!strcasecmp(tag->item, "title"))
925 atom_name = "\xA9" "nam";
926 else if (!strcasecmp(tag->item, "artist"))
927 atom_name = "\xA9" "ART";
928 else if (!strcasecmp(tag->item, "album"))
929 atom_name = "\xA9" "alb";
930 else if (!strcasecmp(tag->item, "date"))
931 atom_name = "\xA9" "day";
932 else if (!strcasecmp(tag->item, "comment"))
933 atom_name = "\xA9" "cmt";
936 write_u32_be(out, TAG_LEN(len));
937 memcpy(out + 4, atom_name, 4);
938 write_u32_be(out + 8, 8 /* data atom header */
939 + 8 /* flags + reserved */
941 memcpy(out + 12, "data", 4);
942 write_u32_be(out + 16, 1); /* flags */
943 write_u32_be(out + 20, 0); /* reserved */
944 memcpy(out + 24, tag->value, len);
949 static uint32_t fix_byte_order_32(uint32_t src)
951 return read_u32_be(&src);
954 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
957 uint64_t total_base = f->moov_offset + 8;
958 uint32_t total_size = (uint32_t) (f->moov_size - 8);
959 uint32_t new_ilst_size = 0;
965 for (unsigned n = 0; n < f->meta.count; n++)
966 new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
967 size_delta = new_ilst_size - (f->ilst_size - 8);
968 *out_size = total_size + size_delta;
969 out_buffer = para_malloc(*out_size);
971 set_position(f, total_base);
972 ret = read_data(f, p_out, f->udta_offset - total_base);
975 p_out += f->udta_offset - total_base;
976 ret = read_int32(f, &tmp);
979 *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
981 ret = read_data(f, p_out, 4);
985 ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
988 p_out += f->meta_offset - f->udta_offset - 8;
989 ret = read_int32(f, &tmp);
992 *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
994 ret = read_data(f, p_out, 4);
998 ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
1001 p_out += f->ilst_offset - f->meta_offset - 8;
1002 ret = read_int32(f, &tmp);
1005 *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
1007 ret = read_data(f, p_out, 4);
1011 create_ilst(&f->meta, p_out);
1012 p_out += new_ilst_size;
1013 set_position(f, f->ilst_offset + f->ilst_size);
1014 ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
1021 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1025 result = f->cb->write(f->cb->user_data, data, size);
1027 f->current_position += size;
1032 static int32_t write_int32(struct mp4 *f, uint32_t data)
1035 write_u32_be(temp, data);
1036 return write_data(f, temp, sizeof(temp));
1039 int32_t mp4_meta_update(struct mp4 *f)
1041 void *new_moov_data;
1042 uint32_t new_moov_size;
1045 new_moov_data = modify_moov(f, &new_moov_size);
1046 if (!new_moov_data ) {
1050 /* copy moov atom to end of the file */
1051 if (f->last_atom != ATOM_MOOV) {
1052 char *free_data = "free";
1054 /* rename old moov to free */
1055 set_position(f, f->moov_offset + 4);
1056 write_data(f, free_data, 4);
1058 set_position(f, f->file_size);
1059 write_int32(f, new_moov_size + 8);
1060 write_data(f, "moov", 4);
1061 write_data(f, new_moov_data, new_moov_size);
1063 set_position(f, f->moov_offset);
1064 write_int32(f, new_moov_size + 8);
1065 write_data(f, "moov", 4);
1066 write_data(f, new_moov_data, new_moov_size);
1068 free(new_moov_data);
1069 f->cb->truncate(f->cb->user_data);
1073 static char *meta_find_by_name(const struct mp4 *f, const char *item)
1077 for (i = 0; i < f->meta.count; i++)
1078 if (!strcasecmp(f->meta.tags[i].item, item))
1079 return para_strdup(f->meta.tags[i].value);
1084 * Return the value of the artist meta tag of an mp4 file.
1086 * \param f Must not be NULL.
1088 * \return If the file does not contain this metadata tag, the function returns
1089 * NULL. Otherwise, a copy of the tag value is returned. The caller should free
1090 * this memory when it is no longer needed.
1092 char *mp4_meta_get_artist(const struct mp4 *f)
1094 return meta_find_by_name(f, "artist");
1098 * Return the value of the title meta tag of an mp4 file.
1100 * \param f See \ref mp4_meta_get_artist().
1101 * \return See \ref mp4_meta_get_artist().
1103 char *mp4_meta_get_title(const struct mp4 *f)
1105 return meta_find_by_name(f, "title");
1109 * Return the value of the date meta tag of an mp4 file.
1111 * \param f See \ref mp4_meta_get_artist().
1112 * \return See \ref mp4_meta_get_artist().
1114 char *mp4_meta_get_date(const struct mp4 *f)
1116 return meta_find_by_name(f, "date");
1120 * Return the value of the album meta tag of an mp4 file.
1122 * \param f See \ref mp4_meta_get_artist().
1123 * \return See \ref mp4_meta_get_artist().
1125 char *mp4_meta_get_album(const struct mp4 *f)
1127 return meta_find_by_name(f, "album");
1131 * Return the value of the comment meta tag of an mp4 file.
1133 * \param f See \ref mp4_meta_get_artist().
1134 * \return See \ref mp4_meta_get_artist().
1136 char *mp4_meta_get_comment(const struct mp4 *f)
1138 return meta_find_by_name(f, "comment");