2 * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
3 * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
8 /** \file mp4.c Paraslash's internal mp4 parser. */
11 * This is a stripped down version of the former mp4ff library which used to be
12 * part of the faad decoder project but was removed from the faad code base in
13 * 2017. The original code has been cleaned up substantially and the public API
14 * has been documented. See the git commit log for details.
21 #include "portable_io.h"
26 * The three states of the mp4 parser. The parser only loads the audio specific
27 * values and tables when it is in the second state.
29 enum audio_track_state {
30 /** We haven't encountered an mp4a atom so far. */
32 /** We have seen an mp4a atom but no subsequent trak atom yet. */
34 /** A trak atom was seen *after* the mp4a atom. */
39 /* determines which atoms we still need to parse. */
40 enum audio_track_state state;
43 uint16_t channel_count;
47 uint32_t stsz_sample_size;
48 uint32_t stsz_sample_count;
52 uint32_t stts_entry_count;
53 uint32_t *stts_sample_count;
56 uint32_t stsc_entry_count;
57 uint32_t *stsc_first_chunk;
58 uint32_t *stsc_samples_per_chunk;
61 uint32_t stco_entry_count;
62 uint32_t *stco_chunk_offset;
70 const struct mp4_callback *cb;
82 struct mp4_track track;
83 struct mp4_metadata meta;
87 * Returns -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
88 * by EOF or read errors are treated as errors.
90 static int read_data(struct mp4 *f, void *data, size_t size)
93 ssize_t ret = f->cb->read(f->cb->user_data, data, size);
94 if (ret < 0 && errno == EINTR)
96 /* regard EAGAIN as an error as reads should be blocking. */
98 return ret < 0? -E_MP4_READ : 0;
104 static int read_int64(struct mp4 *f, uint64_t *result)
107 int ret = read_data(f, data, 8);
110 *result = read_u64_be(data);
114 static int read_int32(struct mp4 *f, uint32_t *result)
117 int ret = read_data(f, data, 4);
120 *result = read_u32_be(data);
124 static int read_int16(struct mp4 *f, uint16_t *result)
127 int ret = read_data(f, data, 2);
130 *result = read_u16_be(data);
134 /** \cond atom_items */
135 /* A macro defining the atoms we care about. It gets expanded twice. */
137 ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
138 ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
139 ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
140 ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
141 ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
142 ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
143 ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
144 ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
145 ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
146 ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
147 ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
148 ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
149 ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
150 ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
151 ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
152 ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
153 ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
154 ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
155 ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
156 ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
157 ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
159 /** \endcond atom_items */
161 /** For the C enumeration we concatenate ATOM_ with the first argument. */
162 #define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
163 /** The enumeration of interesting atoms. */
164 enum atom {ATOM_ITEMS};
167 /** A cpp version of read_u32_be(). */
168 #define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
170 static uint8_t atom_name_to_type(uint8_t *p)
172 /** Expands to an instance of the following unnamed structure. */
173 #define ATOM_ITEM(_name, a, b, c, d) \
174 {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
175 static const struct {
178 } atom_table[] = {ATOM_ITEMS};
180 uint32_t val = read_u32_be(p);
182 for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
183 if (val == atom_table[n].val)
188 /* read atom header, atom size is returned with header included. */
189 static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
190 uint8_t *header_size, uint64_t *atom_size)
194 uint8_t atom_header[8];
196 ret = read_data(f, atom_header, 8);
199 size = read_u32_be(atom_header);
200 if (size == 1) { /* 64 bit atom size */
203 ret = read_int64(f, atom_size);
211 *atom_type = atom_name_to_type(atom_header + 4);
215 static off_t get_position(const struct mp4 *f)
217 return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
220 static void set_position(struct mp4 *f, off_t position)
222 f->cb->seek(f->cb->user_data, position, SEEK_SET);
225 static void skip_bytes(struct mp4 *f, off_t num_skip)
227 f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
230 static int read_stsz(struct mp4 *f)
233 struct mp4_track *t = &f->track;
235 if (t->state != ATS_SEEN_MP4A || t->stsz_table)
237 skip_bytes(f, 4); /* version (1), flags (3) */
238 ret = read_int32(f, &t->stsz_sample_size);
241 ret = read_int32(f, &t->stsz_sample_count);
244 if (t->stsz_sample_size != 0)
246 t->stsz_table = arr_alloc(t->stsz_sample_count, sizeof(int32_t));
247 for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
248 ret = read_int32(f, &t->stsz_table[n]);
255 static int read_stts(struct mp4 *f)
258 struct mp4_track *t = &f->track;
260 if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
262 skip_bytes(f, 4); /* version (1), flags (3) */
263 ret = read_int32(f, &t->stts_entry_count);
266 t->stts_sample_count = arr_alloc(t->stts_entry_count, sizeof(int32_t));
267 for (uint32_t n = 0; n < t->stts_entry_count; n++) {
268 ret = read_int32(f, &t->stts_sample_count[n]);
271 skip_bytes(f, 4); /* sample delta */
276 static int read_stsc(struct mp4 *f)
279 struct mp4_track *t = &f->track;
281 if (t->state != ATS_SEEN_MP4A)
283 if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
285 skip_bytes(f, 4); /* version (1), flags (3) */
286 ret = read_int32(f, &t->stsc_entry_count);
289 t->stsc_first_chunk = arr_alloc(t->stsc_entry_count, sizeof(int32_t));
290 t->stsc_samples_per_chunk = arr_alloc(t->stsc_entry_count,
292 for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
293 ret = read_int32(f, &t->stsc_first_chunk[n]);
296 ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
299 skip_bytes(f, 4); /* sample desc index */
304 static int read_stco(struct mp4 *f)
307 struct mp4_track *t = &f->track;
309 if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
311 skip_bytes(f, 4); /* version (1), flags (3) */
312 ret = read_int32(f, &t->stco_entry_count);
315 t->stco_chunk_offset = arr_alloc(t->stco_entry_count, sizeof(int32_t));
316 for (uint32_t n = 0; n < t->stco_entry_count; n++) {
317 ret = read_int32(f, &t->stco_chunk_offset[n]);
324 static int read_stsd(struct mp4 *f)
327 uint32_t entry_count;
329 if (f->track.state != ATS_INITIAL)
331 skip_bytes(f, 4); /* version (1), flags (3) */
332 ret = read_int32(f, &entry_count);
335 for (uint32_t n = 0; n < entry_count; n++) {
336 uint64_t skip = get_position(f);
338 uint8_t atom_type = 0;
339 ret = atom_read_header(f, &atom_type, NULL, &size);
343 if (atom_type == ATOM_MP4A) {
344 f->track.state = ATS_SEEN_MP4A;
345 /* reserved (6), data reference index (2), reserved (8) */
347 ret = read_int16(f, &f->track.channel_count);
351 ret = read_int16(f, &f->track.sample_rate);
355 set_position(f, skip);
360 static const char *get_metadata_name(uint8_t atom_type)
363 case ATOM_TITLE: return "title";
364 case ATOM_ARTIST: return "artist";
365 case ATOM_ALBUM: return "album";
366 case ATOM_DATE: return "date";
367 case ATOM_COMMENT: return "comment";
368 default: return "unknown";
372 static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
375 uint64_t subsize, sumsize;
384 set_position(f, destpos), sumsize += subsize
387 uint8_t header_size = 0;
388 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
391 destpos = get_position(f) + subsize - header_size;
392 if (atom_type != ATOM_DATA)
394 skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
395 ret = -E_MP4_CORRUPT;
396 if (subsize < header_size + 8 || subsize > UINT_MAX)
398 len = subsize - (header_size + 8);
400 value = alloc(len + 1);
401 ret = read_data(f, value, len);
407 return -E_MP4_CORRUPT;
408 f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
409 * sizeof(struct mp4_tag));
410 tag = f->meta.tags + f->meta.count;
411 tag->item = para_strdup(get_metadata_name(parent));
420 static int read_mdhd(struct mp4 *f)
424 struct mp4_track *t = &f->track;
426 if (t->state != ATS_INITIAL)
428 ret = read_int32(f, &version);
432 skip_bytes(f, 16); /* creation time (8), modification time (8) */
433 ret = read_int32(f, &t->time_scale);
436 ret = read_int64(f, &t->duration);
439 } else { /* version == 0 */
442 skip_bytes(f, 8); /* creation time (4), modification time (4) */
443 ret = read_int32(f, &t->time_scale);
446 ret = read_int32(f, &temp);
449 t->duration = (temp == (uint32_t) (-1))?
450 (uint64_t) (-1) : (uint64_t) (temp);
456 static int read_ilst(struct mp4 *f, int32_t size)
459 uint64_t sumsize = 0;
461 while (sumsize < size) {
463 uint64_t subsize, destpos;
464 uint8_t header_size = 0;
465 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
468 destpos = get_position(f) + subsize - header_size;
475 ret = parse_tag(f, atom_type, subsize - header_size);
479 set_position(f, destpos);
485 static int read_meta(struct mp4 *f, uint64_t size)
488 uint64_t subsize, sumsize = 0;
490 uint8_t header_size = 0;
492 skip_bytes(f, 4); /* version (1), flags (3) */
493 while (sumsize < (size - (header_size + 4))) {
494 ret = atom_read_header(f, &atom_type, &header_size, &subsize);
497 if (subsize <= header_size + 4)
499 if (atom_type == ATOM_ILST) {
500 f->ilst_offset = get_position(f) - header_size;
501 f->ilst_size = subsize;
502 ret = read_ilst(f, subsize - (header_size + 4));
506 set_position(f, get_position(f) + subsize - header_size);
512 static bool need_atom(uint8_t atom_type, bool meta_only)
514 /* these are needed in any case */
525 /* meta-only opens don't need anything else */
528 /* these are only required for regular opens */
540 /* parse atoms that are sub atoms of other atoms */
541 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
544 uint64_t dest, size, end = get_position(f) + total_size;
546 for (dest = get_position(f); dest < end; set_position(f, dest)) {
547 uint8_t header_size, atom_type;
548 ret = atom_read_header(f, &atom_type, &header_size, &size);
552 return -E_MP4_CORRUPT;
553 dest = get_position(f) + size - header_size;
554 if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
555 f->track.state = ATS_TRACK_CHANGE;
558 if (atom_type == ATOM_UDTA) {
559 f->udta_offset = get_position(f) - header_size;
562 if (!need_atom(atom_type, meta_only))
565 case ATOM_STSZ: ret = read_stsz(f); break;
566 case ATOM_STTS: ret = read_stts(f); break;
567 case ATOM_STSC: ret = read_stsc(f); break;
568 case ATOM_STCO: ret = read_stco(f); break;
569 case ATOM_STSD: ret = read_stsd(f); break;
570 case ATOM_MDHD: ret = read_mdhd(f); break;
572 f->meta_offset = get_position(f) - header_size;
574 ret = read_meta(f, size);
577 ret = parse_sub_atoms(f, size - header_size, meta_only);
586 * Deallocate all resources associated with an mp4 file handle.
588 * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
590 * This frees the metadata items and various tables which were allocated when
591 * the file was opened. The given file handle must not be NULL.
593 void mp4_close(struct mp4 *f)
595 free(f->track.stsz_table);
596 free(f->track.stts_sample_count);
597 free(f->track.stsc_first_chunk);
598 free(f->track.stsc_samples_per_chunk);
599 free(f->track.stco_chunk_offset);
600 for (uint32_t n = 0; n < f->meta.count; n++) {
601 free(f->meta.tags[n].item);
602 free(f->meta.tags[n].value);
608 static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
612 uint8_t atom_type, header_size;
613 struct mp4 *f = zalloc(sizeof(*f));
616 while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
617 f->last_atom = atom_type;
618 if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
619 set_position(f, get_position(f) + size - header_size);
622 f->moov_offset = get_position(f) - header_size;
624 ret = parse_sub_atoms(f, size - header_size, meta_only);
631 if (f->track.channel_count == 0)
633 ret = -E_MP4_BAD_SAMPLERATE;
634 if (f->track.sample_rate == 0)
636 ret = -E_MP4_MISSING_ATOM;
637 if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
648 * Read the audio track and the metadata of an mp4 file.
650 * \param cb Only the ->read() and ->seek() methods need to be supplied.
651 * \param result Initialized to a non-NULL pointer iff the function succeeds.
653 * This detects and parses the first audio track and the metadata information
654 * of the mp4 file. Various error checks are performed after the mp4 atoms have
655 * been parsed successfully.
657 * This function does not modify the file. However, if the caller intents to
658 * update the metadata later, the ->write() and ->truncate() methods must be
659 * supplied in the callback structure.
661 * \return Standard. Several errors are possible.
663 * \sa \ref mp4_open_meta().
665 int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
671 ret = open_file(cb, false, &f);
674 ret = -E_MP4_BAD_SAMPLE_COUNT;
675 if (f->track.stsz_sample_count == 0)
677 ret = -E_MP4_CORRUPT;
678 if (f->track.time_scale == 0)
687 static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
690 const struct mp4_track *t = &f->track;
691 uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
692 uint32_t chunk1, chunk1samples, n, total, k;
694 for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
695 n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
696 if (sample < total + n)
700 chunk1samples = spc[k - 1];
701 if (chunk1samples != 0)
702 *chunk = (sample - total) / chunk1samples + chunk1;
705 return total + (*chunk - chunk1) * chunk1samples;
709 * Compute the duration of an mp4 file.
711 * \param f See \ref mp4_close().
713 * \return The number of milliseconds of the audio track. This function never
716 uint64_t mp4_get_duration(const struct mp4 *f)
718 const struct mp4_track *t = &f->track;
720 return t->duration * 1000 / t->time_scale;
724 * Reposition the read/write file offset.
726 * \param f See \ref mp4_close().
727 * \param sample The number of the sample to reposition to.
729 * The given sample number must be within range, i.e., strictly less than the
730 * value returned by \ref mp4_num_samples().
732 * \return Standard. The only possible error is an invalid sample number.
734 int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
736 const struct mp4_track *t = &f->track;
737 int32_t offset, chunk, chunk_sample;
738 uint32_t n, srs; /* sample range size */
740 if (sample >= t->stsz_sample_count)
741 return -ERRNO_TO_PARA_ERROR(EINVAL);
742 chunk_sample = chunk_of_sample(f, sample, &chunk);
743 if (t->stsz_sample_size > 0)
744 srs = (sample - chunk_sample) * t->stsz_sample_size;
746 for (srs = 0, n = chunk_sample; n < sample; n++)
747 srs += t->stsz_table[n];
749 if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
750 offset = t->stco_chunk_offset[t->stco_entry_count - 1];
751 else if (t->stco_entry_count > 0)
752 offset = t->stco_chunk_offset[chunk - 1];
755 set_position(f, offset + srs);
760 * Look up and return the size of the given sample in the stsz table.
762 * \param f See \ref mp4_close().
763 * \param sample The sample number of interest.
764 * \param result Sample size is returned here.
766 * For the sample argument the restriction mentioned in the documentation of
767 * \ref mp4_set_sample_position() applies as well.
769 * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
770 * only possible error.
772 int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
774 const struct mp4_track *t = &f->track;
776 if (sample >= t->stsz_sample_count)
777 return -ERRNO_TO_PARA_ERROR(EINVAL);
778 if (t->stsz_sample_size != 0)
779 *result = t->stsz_sample_size;
781 *result = t->stsz_table[sample];
786 * Return the sample rate stored in the stsd atom.
788 * \param f See \ref mp4_close().
790 * The sample rate is a property of the audio track of the mp4 file and is thus
791 * independent of the sample number.
793 * \return The function always returns a positive value because the open
794 * operation fails if the sample rate happens to be zero. A typical value is
797 uint16_t mp4_get_sample_rate(const struct mp4 *f)
799 return f->track.sample_rate;
803 * Return the number of channels of the audio track.
805 * \param f See \ref mp4_close().
807 * \return The returned channel count is guaranteed to be positive because the
808 * open operation fails if the mp4a atom is missing or contains a zero channel
811 uint16_t mp4_get_channel_count(const struct mp4 *f)
813 return f->track.channel_count;
817 * Return the number of samples of the audio track.
819 * \param f See \ref mp4_close().
821 * \return The sample count is read from the stsz atom during open.
823 uint32_t mp4_num_samples(const struct mp4 *f)
825 return f->track.stsz_sample_count;
829 * Open an mp4 file in metadata-only mode.
831 * \param cb See \ref mp4_open().
832 * \param result See \ref mp4_open().
834 * This is similar to \ref mp4_open() but is cheaper because it only parses the
835 * metadata of the mp4 file. The only functions that can subsequently be called
836 * with the file handle returned here are \ref mp4_get_meta() and \ref
841 * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
842 * this function as well.
844 int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
847 int ret = open_file(cb, true, &f);
856 * Return the metadata of an mp4 file.
858 * \param f See \ref mp4_close().
860 * The caller is allowed to add, delete or modify the entries of the returned
861 * structure with the intention to pass the modified version to \ref
864 * \return This never returns NULL, even if the file contains no metadata tag
865 * items. However, the meta count will be zero and the ->tags pointer NULL in
868 struct mp4_metadata *mp4_get_meta(struct mp4 *f)
873 /** Total length of an on-disk metadata tag. */
874 #define TAG_LEN(_len) (24 + (_len))
875 static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
877 for (unsigned n = 0; n < meta->count; n++) {
878 struct mp4_tag *tag = meta->tags + n;
879 unsigned len = strlen(tag->value);
880 const char *atom_name;
882 if (!strcasecmp(tag->item, "title"))
883 atom_name = "\xA9" "nam";
884 else if (!strcasecmp(tag->item, "artist"))
885 atom_name = "\xA9" "ART";
886 else if (!strcasecmp(tag->item, "album"))
887 atom_name = "\xA9" "alb";
888 else if (!strcasecmp(tag->item, "date"))
889 atom_name = "\xA9" "day";
890 else if (!strcasecmp(tag->item, "comment"))
891 atom_name = "\xA9" "cmt";
894 write_u32_be(out, TAG_LEN(len));
895 memcpy(out + 4, atom_name, 4);
896 write_u32_be(out + 8, 8 /* data atom header */
897 + 8 /* flags + reserved */
899 memcpy(out + 12, "data", 4);
900 write_u32_be(out + 16, 1); /* flags */
901 write_u32_be(out + 20, 0); /* reserved */
902 memcpy(out + 24, tag->value, len);
907 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
910 uint64_t total_base = f->moov_offset + 8;
911 uint32_t total_size = f->moov_size - 8;
912 uint32_t new_ilst_size = 0;
918 for (unsigned n = 0; n < f->meta.count; n++)
919 new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
920 size_delta = new_ilst_size - (f->ilst_size - 8);
921 *out_size = total_size + size_delta;
922 out_buffer = alloc(*out_size);
924 set_position(f, total_base);
925 ret = read_data(f, p_out, f->udta_offset - total_base);
928 p_out += f->udta_offset - total_base;
929 ret = read_int32(f, &tmp);
932 write_u32_be(p_out, tmp + size_delta);
934 ret = read_data(f, p_out, 4);
938 ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
941 p_out += f->meta_offset - f->udta_offset - 8;
942 ret = read_int32(f, &tmp);
945 write_u32_be(p_out, tmp + size_delta);
947 ret = read_data(f, p_out, 4);
951 ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
954 p_out += f->ilst_offset - f->meta_offset - 8;
955 ret = read_int32(f, &tmp);
958 write_u32_be(p_out, tmp + size_delta);
960 ret = read_data(f, p_out, 4);
964 create_ilst(&f->meta, p_out);
965 p_out += new_ilst_size;
966 set_position(f, f->ilst_offset + f->ilst_size);
967 ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
974 static int write_data(struct mp4 *f, void *data, size_t size)
977 ssize_t ret = f->cb->write(f->cb->user_data, data, size);
981 return -ERRNO_TO_PARA_ERROR(errno);
989 * Write back the modified metadata items to the mp4 file.
991 * This is the only public function which modifies the contents of an mp4 file.
992 * This is achieved by calling the ->write() and ->truncate() methods of the
993 * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
995 * \param f See \ref mp4_close().
997 * The modified metadata structure does not need to be supplied to this
998 * function because it is part of the mp4 structure.
1002 int mp4_update_meta(struct mp4 *f)
1004 void *new_moov_data;
1005 uint32_t new_moov_size;
1006 uint8_t buf[8] = "----moov";
1010 new_moov_data = modify_moov(f, &new_moov_size);
1011 if (!new_moov_data ) {
1015 if (f->last_atom != ATOM_MOOV) {
1016 set_position(f, f->moov_offset + 4);
1017 ret = write_data(f, "free", 4); /* rename old moov to free */
1020 /* write new moov atom at EOF */
1021 f->cb->seek(f->cb->user_data, 0, SEEK_END);
1022 } else /* overwrite old moov atom */
1023 set_position(f, f->moov_offset);
1024 write_u32_be(buf, new_moov_size + 8);
1025 ret = write_data(f, buf, sizeof(buf));
1028 ret = write_data(f, new_moov_data, new_moov_size);
1031 ret = f->cb->truncate(f->cb->user_data);
1033 ret = -ERRNO_TO_PARA_ERROR(errno);
1035 free(new_moov_data);
1040 * Return the value of the given tag item.
1042 * \param f See \ref mp4_close().
1043 * \param item "artist", "title", "album", "comment", or "date".
1045 * \return The function returns NULL if the given item is not in the above
1046 * list. Otherwise, if the file does not contain a tag for the given item, the
1047 * function also returns NULL. Otherwise a copy of the tag value is returned
1048 * and the caller should free this memory when it is no longer needed.
1050 __malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item)
1052 for (unsigned n = 0; n < f->meta.count; n++)
1053 if (!strcasecmp(f->meta.tags[n].item, item))
1054 return para_strdup(f->meta.tags[n].value);