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 */
163 /* atoms without subatoms */
164 ATOM_MDHD, /* track header */
165 ATOM_STSD, /* sample description box */
166 ATOM_STTS, /* time to sample box */
167 ATOM_STSZ, /* sample size box */
168 ATOM_STCO, /* chunk offset box */
169 ATOM_STSC, /* sample to chunk box */
171 ATOM_META, /* iTunes Metadata box */
172 ATOM_DATA, /* iTunes Metadata data box */
176 #define COPYRIGHT_SYMBOL ((int8_t)0xA9)
178 static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
181 if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
183 else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
185 else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
187 else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
189 else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
191 else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
193 } else if (a == 't') {
194 if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
196 } else if (a == 's') {
197 if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
199 else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
201 else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
203 else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
205 else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
207 else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
209 } else if (a == COPYRIGHT_SYMBOL) {
210 if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
212 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
214 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
216 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
218 else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
221 if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
223 else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
225 else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
231 /* read atom header, atom size is returned with header included. */
232 static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
233 uint8_t *header_size, uint64_t *atom_size)
237 int8_t atom_header[8];
239 ret = read_data(f, atom_header, 8);
242 size = read_u32_be(atom_header);
243 if (size == 1) { /* 64 bit atom size */
246 ret = read_int64(f, atom_size);
255 *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
256 atom_header[6], atom_header[7]);
260 static int64_t get_position(const struct mp4 *f)
262 return f->current_position;
265 static int32_t set_position(struct mp4 *f, int64_t position)
267 f->cb->seek(f->cb->user_data, position);
268 f->current_position = position;
273 static int read_stsz(struct mp4 *f)
279 if (f->total_tracks == 0)
281 t = f->track[f->total_tracks - 1];
282 ret = read_int8(f, NULL); /* version */
285 ret = read_int24(f, NULL); /* flags */
288 ret = read_int32(f, &t->stsz_sample_size);
291 ret = read_int32(f, &t->stsz_sample_count);
294 if (t->stsz_sample_size != 0)
296 t->stsz_table = para_malloc(t->stsz_sample_count * sizeof(int32_t));
297 for (i = 0; i < t->stsz_sample_count; i++) {
298 ret = read_int32(f, &t->stsz_table[i]);
305 static int read_stts(struct mp4 *f)
311 if (f->total_tracks == 0)
313 t = f->track[f->total_tracks - 1];
314 if (t->stts_entry_count)
316 ret = read_int8(f, NULL); /* version */
319 ret = read_int24(f, NULL); /* flags */
322 ret = read_int32(f, &t->stts_entry_count);
325 t->stts_sample_count = para_malloc(t->stts_entry_count
327 for (i = 0; i < t->stts_entry_count; i++) {
328 ret = read_int32(f, &t->stts_sample_count[i]);
331 ret = read_int32(f, NULL); /* sample delta */
338 static int read_stsc(struct mp4 *f)
344 if (f->total_tracks == 0)
346 t = f->track[f->total_tracks - 1];
348 ret = read_int8(f, NULL); /* version */
351 ret = read_int24(f, NULL); /* flags */
354 ret = read_int32(f, &t->stsc_entry_count);
357 t->stsc_first_chunk = para_malloc(t->stsc_entry_count * sizeof(int32_t));
358 t->stsc_samples_per_chunk = para_malloc(t->stsc_entry_count
360 for (i = 0; i < t->stsc_entry_count; i++) {
361 ret = read_int32(f, &t->stsc_first_chunk[i]);
364 ret = read_int32(f, &t->stsc_samples_per_chunk[i]);
367 ret = read_int32(f, NULL); /* sample desc index */
374 static int read_stco(struct mp4 *f)
380 if (f->total_tracks == 0)
382 t = f->track[f->total_tracks - 1];
384 ret = read_int8(f, NULL); /* version */
387 ret = read_int24(f, NULL); /* flags */
390 ret = read_int32(f, &t->stco_entry_count);
393 t->stco_chunk_offset = para_malloc(t->stco_entry_count
395 for (i = 0; i < t->stco_entry_count; i++) {
396 ret = read_int32(f, &t->stco_chunk_offset[i]);
403 static int read_mp4a(struct mp4 *f)
409 if (f->total_tracks == 0)
411 t = f->track[f->total_tracks - 1];
413 for (i = 0; i < 6; i++) {
414 ret = read_int8(f, NULL); /* reserved */
418 ret = read_int16(f, NULL); /* data_reference_index */
421 ret = read_int32(f, NULL); /* reserved */
424 ret = read_int32(f, NULL); /* reserved */
427 ret = read_int16(f, &t->channelCount);
430 ret = read_int16(f, NULL);
433 ret = read_int16(f, NULL);
436 ret = read_int16(f, NULL);
439 return read_int16(f, &t->sampleRate);
442 static int read_stsd(struct mp4 *f)
445 uint32_t i, entry_count;
448 if (f->total_tracks == 0)
450 t = f->track[f->total_tracks - 1];
451 ret = read_int8(f, NULL); /* version */
454 ret = read_int24(f, NULL); /* flags */
457 ret = read_int32(f, &entry_count);
460 for (i = 0; i < entry_count; i++) {
461 uint64_t skip = get_position(f);
463 uint8_t atom_type = 0;
464 ret = atom_read_header(f, &atom_type, NULL, &size);
468 if (!f->audio_track && atom_type == ATOM_MP4A) {
472 set_position(f, skip);
477 static const char *get_metadata_name(uint8_t atom_type)
480 case ATOM_TITLE: return "title";
481 case ATOM_ARTIST: return "artist";
482 case ATOM_ALBUM: return "album";
483 case ATOM_DATE: return "date";
484 case ATOM_COMMENT: return "comment";
485 default: return "unknown";
489 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
492 uint64_t subsize, sumsize;
501 set_position(f, destpos), sumsize += subsize
504 uint8_t header_size = 0;
505 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
508 destpos = get_position(f) + subsize - header_size;
509 if (atom_type != ATOM_DATA)
511 ret = read_int8(f, NULL); /* version */
514 ret = read_int24(f, NULL); /* flags */
517 ret = read_int32(f, NULL); /* reserved */
520 ret = -ERRNO_TO_PARA_ERROR(EINVAL);
521 if (subsize < header_size + 8 || subsize > UINT_MAX)
523 len = subsize - (header_size + 8);
525 value = para_malloc(len + 1);
526 ret = read_data(f, value, len);
532 return -ERRNO_TO_PARA_ERROR(EINVAL);
533 f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
534 * sizeof(struct mp4_tag));
535 tag = f->meta.tags + f->meta.count;
536 tag->item = para_strdup(get_metadata_name(parent));
546 static int read_mdhd(struct mp4 *f)
552 if (f->total_tracks == 0)
554 t = f->track[f->total_tracks - 1];
556 ret = read_int32(f, &version);
560 ret = read_int64(f, NULL); /* creation-time */
563 ret = read_int64(f, NULL); /* modification-time */
566 ret = read_int32(f, &t->timeScale);
569 ret = read_int64(f, &t->duration);
572 } else { //version == 0
575 ret = read_int32(f, NULL); /* creation-time */
578 ret = read_int32(f, NULL); /* modification-time */
581 ret = read_int32(f, &t->timeScale);
584 ret = read_int32(f, &temp);
587 t->duration = (temp == (uint32_t) (-1))?
588 (uint64_t) (-1) : (uint64_t) (temp);
590 ret = read_int16(f, NULL);
593 ret = read_int16(f, NULL);
599 static int32_t read_ilst(struct mp4 *f, int32_t size)
602 uint64_t sumsize = 0;
604 while (sumsize < size) {
606 uint64_t subsize, destpos;
607 uint8_t header_size = 0;
608 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
611 destpos = get_position(f) + subsize - header_size;
618 ret = parse_tag(f, atom_type, subsize - header_size);
622 set_position(f, destpos);
628 static int32_t read_meta(struct mp4 *f, uint64_t size)
631 uint64_t subsize, sumsize = 0;
633 uint8_t header_size = 0;
635 ret = read_int8(f, NULL); /* version */
638 ret = read_int24(f, NULL); /* flags */
641 while (sumsize < (size - (header_size + 4))) {
642 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
645 if (subsize <= header_size + 4)
647 if (atom_type == ATOM_ILST) {
648 f->ilst_offset = get_position(f) - header_size;
649 f->ilst_size = subsize;
650 ret = read_ilst(f, subsize - (header_size + 4));
654 set_position(f, get_position(f) + subsize - header_size);
660 static int parse_leaf_atom(struct mp4 *f, uint64_t size, uint8_t header_size,
663 uint64_t dest_position = get_position(f) + size - 8;
664 int ret = 1; /* return success for atoms we don't care about */
667 case ATOM_STSZ: ret = read_stsz(f); break;
668 case ATOM_STTS: ret = read_stts(f); break;
669 case ATOM_STSC: ret = read_stsc(f); break;
670 case ATOM_STCO: ret = read_stco(f); break;
671 case ATOM_STSD: ret = read_stsd(f); break;
672 case ATOM_MDHD: ret = read_mdhd(f); break;
674 f->meta_offset = get_position(f) - header_size;
676 ret = read_meta(f, size);
679 set_position(f, dest_position);
683 static bool need_atom(uint8_t atom_type, bool meta_only)
685 /* these are needed in any case */
696 /* meta-only opens don't need anything else */
699 /* these are only required for regular opens */
711 /* parse atoms that are sub atoms of other atoms */
712 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
716 uint8_t atom_type = 0;
717 uint64_t counted_size = 0;
718 uint8_t header_size = 0;
720 while (counted_size < total_size) {
721 ret = atom_read_header(f, &atom_type, &header_size, &size);
726 counted_size += size;
727 if (atom_type == ATOM_TRAK) {
728 if (f->total_tracks >= MAX_TRACKS)
731 f->track[f->total_tracks - 1] = para_calloc(
732 sizeof(struct mp4_track));
733 } else if (atom_type == ATOM_UDTA) {
734 f->udta_offset = get_position(f) - header_size;
737 if (!need_atom(atom_type, meta_only)) {
738 set_position(f, get_position(f) + size - header_size);
741 if (atom_type < SUBATOMIC) /* atom contains subatoms */
742 ret = parse_sub_atoms(f, size - header_size, meta_only);
744 ret = parse_leaf_atom(f, size, header_size, atom_type);
751 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
755 uint8_t atom_type, header_size;
756 struct mp4 *f = para_calloc(sizeof(*f));
759 while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
760 f->file_size += size;
761 f->last_atom = atom_type;
762 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
763 set_position(f, get_position(f) + size - header_size);
766 f->moov_offset = get_position(f) - header_size;
768 ret = parse_sub_atoms(f, size - header_size, meta_only);
787 int mp4_open_read(const struct mp4_callback *cb, struct mp4 **result)
789 return open_file(cb, false, result);
792 void mp4_close(struct mp4 *f)
796 for (i = 0; i < f->total_tracks; i++) {
798 free(f->track[i]->stsz_table);
799 free(f->track[i]->stts_sample_count);
800 free(f->track[i]->stsc_first_chunk);
801 free(f->track[i]->stsc_samples_per_chunk);
802 free(f->track[i]->stco_chunk_offset);
806 for (i = 0; i < f->meta.count; i++) {
807 free(f->meta.tags[i].item);
808 free(f->meta.tags[i].value);
814 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
817 const struct mp4_track *t = f->audio_track;
818 uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
819 int32_t chunk1, chunk1samples, n, total, i;
821 for (i = 1, total = 0; i < t->stsc_entry_count; i++, total += n) {
822 n = (fc[i] - fc[i - 1]) * spc[i - 1]; /* number of samples */
823 if (sample < total + n)
827 chunk1samples = spc[i - 1];
828 if (chunk1samples != 0)
829 *chunk = (sample - total) / chunk1samples + chunk1;
832 return total + (*chunk - chunk1) * chunk1samples;
836 * Return the number of milliseconds of the audio track.
838 * \param f As returned by \ref mp4_open_read(), must not be NULL.
840 uint64_t mp4_get_duration(const struct mp4 *f)
842 const struct mp4_track *t = f->audio_track;
844 if (t->timeScale == 0)
846 return t->duration * 1000 / t->timeScale;
849 int mp4_set_sample_position(struct mp4 *f, int32_t sample)
851 const struct mp4_track *t = f->audio_track;
852 int32_t offset, chunk, chunk_sample;
853 uint32_t n, srs; /* sample range size */
855 if (sample >= t->stsz_sample_count)
856 return -ERRNO_TO_PARA_ERROR(EINVAL);
857 chunk_sample = chunk_of_sample(f, sample, &chunk);
858 if (t->stsz_sample_size > 0)
859 srs = (sample - chunk_sample) * t->stsz_sample_size;
861 for (srs = 0, n = chunk_sample; n < sample; n++)
862 srs += t->stsz_table[n];
864 if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
865 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
866 else if (t->stco_entry_count > 0)
867 offset = t->stco_chunk_offset[chunk - 1];
870 set_position(f, offset + srs);
874 int32_t mp4_get_sample_size(const struct mp4 *f, int sample)
876 const struct mp4_track *t = f->audio_track;
878 if (t->stsz_sample_size != 0)
879 return t->stsz_sample_size;
880 return t->stsz_table[sample];
883 uint32_t mp4_get_sample_rate(const struct mp4 *f)
885 return f->audio_track->sampleRate;
888 uint32_t mp4_get_channel_count(const struct mp4 *f)
890 return f->audio_track->channelCount ;
893 int32_t mp4_num_samples(const struct mp4 *f)
895 const struct mp4_track *t = f->audio_track;
899 for (i = 0; i < t->stts_entry_count; i++)
900 total += t->stts_sample_count[i];
904 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
907 int ret = open_file(cb, true, &f);
911 if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0) {
914 return -E_MP4_MISSING_ATOM;
921 * Return the metadata of an mp4 file.
923 * \param f As returned by either \ref mp4_open_read() or \ref mp4_open_meta().
925 * The caller is allowed to add, delete or modify the entries of the returned
926 * structure in order to pass the modified version to \ref mp4_meta_update().
928 struct mp4_metadata *mp4_get_meta(struct mp4 *f)
933 /** Total length of an on-disk metadata tag. */
934 #define TAG_LEN(_len) (24 + (_len))
935 static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
937 for (unsigned n = 0; n < meta->count; n++) {
938 struct mp4_tag *tag = meta->tags + n;
939 unsigned len = strlen(tag->value);
940 const char *atom_name;
942 if (!strcasecmp(tag->item, "title"))
943 atom_name = "\xA9" "nam";
944 else if (!strcasecmp(tag->item, "artist"))
945 atom_name = "\xA9" "ART";
946 else if (!strcasecmp(tag->item, "album"))
947 atom_name = "\xA9" "alb";
948 else if (!strcasecmp(tag->item, "date"))
949 atom_name = "\xA9" "day";
950 else if (!strcasecmp(tag->item, "comment"))
951 atom_name = "\xA9" "cmt";
954 write_u32_be(out, TAG_LEN(len));
955 memcpy(out + 4, atom_name, 4);
956 write_u32_be(out + 8, 8 /* data atom header */
957 + 8 /* flags + reserved */
959 memcpy(out + 12, "data", 4);
960 write_u32_be(out + 16, 1); /* flags */
961 write_u32_be(out + 20, 0); /* reserved */
962 memcpy(out + 24, tag->value, len);
967 static uint32_t fix_byte_order_32(uint32_t src)
969 return read_u32_be(&src);
972 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
975 uint64_t total_base = f->moov_offset + 8;
976 uint32_t total_size = (uint32_t) (f->moov_size - 8);
977 uint32_t new_ilst_size = 0;
983 for (unsigned n = 0; n < f->meta.count; n++)
984 new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
985 size_delta = new_ilst_size - (f->ilst_size - 8);
986 *out_size = total_size + size_delta;
987 out_buffer = para_malloc(*out_size);
989 set_position(f, total_base);
990 ret = read_data(f, p_out, f->udta_offset - total_base);
993 p_out += f->udta_offset - total_base;
994 ret = read_int32(f, &tmp);
997 *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
999 ret = read_data(f, p_out, 4);
1003 ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
1006 p_out += f->meta_offset - f->udta_offset - 8;
1007 ret = read_int32(f, &tmp);
1010 *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
1012 ret = read_data(f, p_out, 4);
1016 ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
1019 p_out += f->ilst_offset - f->meta_offset - 8;
1020 ret = read_int32(f, &tmp);
1023 *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
1025 ret = read_data(f, p_out, 4);
1029 create_ilst(&f->meta, p_out);
1030 p_out += new_ilst_size;
1031 set_position(f, f->ilst_offset + f->ilst_size);
1032 ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
1039 static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
1043 result = f->cb->write(f->cb->user_data, data, size);
1045 f->current_position += size;
1050 static int32_t write_int32(struct mp4 *f, uint32_t data)
1053 write_u32_be(temp, data);
1054 return write_data(f, temp, sizeof(temp));
1057 int32_t mp4_meta_update(struct mp4 *f)
1059 void *new_moov_data;
1060 uint32_t new_moov_size;
1063 new_moov_data = modify_moov(f, &new_moov_size);
1064 if (!new_moov_data ) {
1068 /* copy moov atom to end of the file */
1069 if (f->last_atom != ATOM_MOOV) {
1070 char *free_data = "free";
1072 /* rename old moov to free */
1073 set_position(f, f->moov_offset + 4);
1074 write_data(f, free_data, 4);
1076 set_position(f, f->file_size);
1077 write_int32(f, new_moov_size + 8);
1078 write_data(f, "moov", 4);
1079 write_data(f, new_moov_data, new_moov_size);
1081 set_position(f, f->moov_offset);
1082 write_int32(f, new_moov_size + 8);
1083 write_data(f, "moov", 4);
1084 write_data(f, new_moov_data, new_moov_size);
1086 free(new_moov_data);
1087 f->cb->truncate(f->cb->user_data);
1091 static char *meta_find_by_name(const struct mp4 *f, const char *item)
1095 for (i = 0; i < f->meta.count; i++)
1096 if (!strcasecmp(f->meta.tags[i].item, item))
1097 return para_strdup(f->meta.tags[i].value);
1102 * Return the value of the artist meta tag of an mp4 file.
1104 * \param f Must not be NULL.
1106 * \return If the file does not contain this metadata tag, the function returns
1107 * NULL. Otherwise, a copy of the tag value is returned. The caller should free
1108 * this memory when it is no longer needed.
1110 char *mp4_meta_get_artist(const struct mp4 *f)
1112 return meta_find_by_name(f, "artist");
1116 * Return the value of the title meta tag of an mp4 file.
1118 * \param f See \ref mp4_meta_get_artist().
1119 * \return See \ref mp4_meta_get_artist().
1121 char *mp4_meta_get_title(const struct mp4 *f)
1123 return meta_find_by_name(f, "title");
1127 * Return the value of the date meta tag of an mp4 file.
1129 * \param f See \ref mp4_meta_get_artist().
1130 * \return See \ref mp4_meta_get_artist().
1132 char *mp4_meta_get_date(const struct mp4 *f)
1134 return meta_find_by_name(f, "date");
1138 * Return the value of the album meta tag of an mp4 file.
1140 * \param f See \ref mp4_meta_get_artist().
1141 * \return See \ref mp4_meta_get_artist().
1143 char *mp4_meta_get_album(const struct mp4 *f)
1145 return meta_find_by_name(f, "album");
1149 * Return the value of the comment meta tag of an mp4 file.
1151 * \param f See \ref mp4_meta_get_artist().
1152 * \return See \ref mp4_meta_get_artist().
1154 char *mp4_meta_get_comment(const struct mp4 *f)
1156 return meta_find_by_name(f, "comment");