]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - mp4.c
mp4: Implement error checking for the write path.
[paraslash.git] / mp4.c
diff --git a/mp4.c b/mp4.c
index e2512db17de21c0fd20286860a0e6861f9542b2d..fc8c41b4af919358350812cae68033611c5d33c4 100644 (file)
--- a/mp4.c
+++ b/mp4.c
@@ -14,8 +14,9 @@
 #include "mp4.h"
 
 struct mp4_track {
-       uint16_t channelCount;
-       uint16_t sampleRate;
+       /* mp4a */
+       uint16_t channel_count;
+       uint16_t sample_rate;
 
        /* stsz */
        uint32_t stsz_sample_size;
@@ -35,7 +36,8 @@ struct mp4_track {
        uint32_t stco_entry_count;
        uint32_t *stco_chunk_offset;
 
-       uint32_t timeScale;
+       /* mdhd */
+       uint32_t time_scale;
        uint64_t duration;
 };
 
@@ -137,95 +139,49 @@ static uint8_t read_int8(struct mp4 *f, uint8_t *result)
        return ret;
 }
 
-static bool atom_compare(int8_t a1, int8_t b1, int8_t c1, int8_t d1,
-               int8_t a2, int8_t b2, int8_t c2, int8_t d2)
+#define ATOM_ITEMS \
+       ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') \
+       ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') \
+       ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') \
+       ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') \
+       ATOM_ITEM(STBL, 's', 't', 'b', 'l') \
+       ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') \
+       ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
+       ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') \
+       ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') \
+       ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') \
+       ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') \
+       ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') \
+       ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
+       ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
+       ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
+       ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
+       ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
+       ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
+       ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') \
+       ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
+       ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
+
+#define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
+enum atom {ATOM_ITEMS};
+#undef ATOM_ITEM
+
+static uint8_t atom_name_to_type(uint8_t *p)
 {
-       return a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2;
-}
-
-enum atoms {
-       /* atoms with subatoms */
-       ATOM_MOOV,
-       ATOM_TRAK,
-       ATOM_MDIA,
-       ATOM_MINF,
-       ATOM_STBL,
-       ATOM_UDTA,
-       ATOM_ILST, /* iTunes Metadata list */
-       ATOM_TITLE,
-       ATOM_ARTIST,
-       ATOM_ALBUM,
-       ATOM_DATE,
-       ATOM_COMMENT,
-
-       SUBATOMIC = 128,
-
-       /* atoms without subatoms */
-       ATOM_MDHD, /* track header */
-       ATOM_STSD, /* sample description box */
-       ATOM_STTS, /* time to sample box */
-       ATOM_STSZ, /* sample size box */
-       ATOM_STCO, /* chunk offset box */
-       ATOM_STSC, /* sample to chunk box */
-       ATOM_MP4A,
-       ATOM_META, /* iTunes Metadata box */
-       ATOM_DATA, /* iTunes Metadata data box */
-       ATOM_UNKNOWN = 255
-};
-
-#define COPYRIGHT_SYMBOL ((int8_t)0xA9)
-
-static uint8_t atom_name_to_type(int8_t a, int8_t b, int8_t c, int8_t d)
-{
-       if (a == 'm') {
-               if (atom_compare(a, b, c, d, 'm', 'o', 'o', 'v'))
-                       return ATOM_MOOV;
-               else if (atom_compare(a, b, c, d, 'm', 'i', 'n', 'f'))
-                       return ATOM_MINF;
-               else if (atom_compare(a, b, c, d, 'm', 'd', 'i', 'a'))
-                       return ATOM_MDIA;
-               else if (atom_compare(a, b, c, d, 'm', 'd', 'h', 'd'))
-                       return ATOM_MDHD;
-               else if (atom_compare(a, b, c, d, 'm', 'p', '4', 'a'))
-                       return ATOM_MP4A;
-               else if (atom_compare(a, b, c, d, 'm', 'e', 't', 'a'))
-                       return ATOM_META;
-       } else if (a == 't') {
-               if (atom_compare(a, b, c, d, 't', 'r', 'a', 'k'))
-                       return ATOM_TRAK;
-       } else if (a == 's') {
-               if (atom_compare(a, b, c, d, 's', 't', 'b', 'l'))
-                       return ATOM_STBL;
-               else if (atom_compare(a, b, c, d, 's', 't', 's', 'd'))
-                       return ATOM_STSD;
-               else if (atom_compare(a, b, c, d, 's', 't', 't', 's'))
-                       return ATOM_STTS;
-               else if (atom_compare(a, b, c, d, 's', 't', 'c', 'o'))
-                       return ATOM_STCO;
-               else if (atom_compare(a, b, c, d, 's', 't', 's', 'c'))
-                       return ATOM_STSC;
-               else if (atom_compare(a, b, c, d, 's', 't', 's', 'z'))
-                       return ATOM_STSZ;
-       } else if (a == COPYRIGHT_SYMBOL) {
-               if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'n', 'a', 'm'))
-                       return ATOM_TITLE;
-               else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'A', 'R', 'T'))
-                       return ATOM_ARTIST;
-               else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'a', 'l', 'b'))
-                       return ATOM_ALBUM;
-               else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'd', 'a', 'y'))
-                       return ATOM_DATE;
-               else if (atom_compare(a, b, c, d, COPYRIGHT_SYMBOL, 'c', 'm', 't'))
-                       return ATOM_COMMENT;
-       }
-       if (atom_compare(a, b, c, d, 'u', 'd', 't', 'a'))
-               return ATOM_UDTA;
-       else if (atom_compare(a, b, c, d, 'i', 'l', 's', 't'))
-               return ATOM_ILST;
-       else if (atom_compare(a, b, c, d, 'd', 'a', 't', 'a'))
-               return ATOM_DATA;
-       else
-               return ATOM_UNKNOWN;
+       #define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
+       #define ATOM_ITEM(_name, a, b, c, d) \
+               {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
+       static const struct {
+               const char *name;
+               uint32_t val;
+       } atom_table[] = {ATOM_ITEMS};
+       #undef ATOM_ITEM
+       uint32_t val = read_u32_be(p);
+
+       for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
+               if (val == atom_table[n].val)
+                       return n;
+       return 255;
 }
 
 /* read atom header, atom size is returned with header included. */
@@ -234,7 +190,7 @@ static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
 {
        uint32_t size;
        int ret;
-       int8_t atom_header[8];
+       uint8_t atom_header[8];
 
        ret = read_data(f, atom_header, 8);
        if (ret <= 0)
@@ -252,8 +208,7 @@ static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
                if (atom_size)
                        *atom_size = size;
        }
-       *atom_type = atom_name_to_type(atom_header[4], atom_header[5],
-               atom_header[6], atom_header[7]);
+       *atom_type = atom_name_to_type(atom_header + 4);
        return 1;
 }
 
@@ -424,7 +379,7 @@ static int read_mp4a(struct mp4 *f)
        ret = read_int32(f, NULL); /* reserved */
        if (ret <= 0)
                return ret;
-       ret = read_int16(f, &t->channelCount);
+       ret = read_int16(f, &t->channel_count);
        if (ret <= 0)
                return ret;
        ret = read_int16(f, NULL);
@@ -436,7 +391,7 @@ static int read_mp4a(struct mp4 *f)
        ret = read_int16(f, NULL);
        if (ret <= 0)
                return ret;
-       return read_int16(f, &t->sampleRate);
+       return read_int16(f, &t->sample_rate);
 }
 
 static int read_stsd(struct mp4 *f)
@@ -563,7 +518,7 @@ static int read_mdhd(struct mp4 *f)
                ret = read_int64(f, NULL); /* modification-time */
                if (ret <= 0)
                        return ret;
-               ret = read_int32(f, &t->timeScale);
+               ret = read_int32(f, &t->time_scale);
                if (ret <= 0)
                        return ret;
                ret = read_int64(f, &t->duration);
@@ -578,7 +533,7 @@ static int read_mdhd(struct mp4 *f)
                ret = read_int32(f, NULL); /* modification-time */
                if (ret <= 0)
                        return ret;
-               ret = read_int32(f, &t->timeScale);
+               ret = read_int32(f, &t->time_scale);
                if (ret <= 0)
                        return ret;
                ret = read_int32(f, &temp);
@@ -657,29 +612,6 @@ static int32_t read_meta(struct mp4 *f, uint64_t size)
        return 1;
 }
 
-static int parse_leaf_atom(struct mp4 *f, uint64_t size, uint8_t header_size,
-               uint8_t atom_type)
-{
-       uint64_t dest_position = get_position(f) + size - 8;
-       int ret = 1; /* return success for atoms we don't care about */
-
-       switch (atom_type) {
-       case ATOM_STSZ: ret = read_stsz(f); break;
-       case ATOM_STTS: ret = read_stts(f); break;
-       case ATOM_STSC: ret = read_stsc(f); break;
-       case ATOM_STCO: ret = read_stco(f); break;
-       case ATOM_STSD: ret = read_stsd(f); break;
-       case ATOM_MDHD: ret = read_mdhd(f); break;
-       case ATOM_META:
-               f->meta_offset = get_position(f) - header_size;
-               f->meta_size = size;
-               ret = read_meta(f, size);
-               break;
-       }
-       set_position(f, dest_position);
-       return ret;
-}
-
 static bool need_atom(uint8_t atom_type, bool meta_only)
 {
        /* these are needed in any case */
@@ -712,18 +644,16 @@ static bool need_atom(uint8_t atom_type, bool meta_only)
 static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
 {
        int ret;
-       uint64_t size;
-       uint8_t atom_type = 0;
-       uint64_t counted_size = 0;
-       uint8_t header_size = 0;
+       uint64_t dest, size, end = get_position(f) + total_size;
 
-       while (counted_size < total_size) {
+       for (dest = get_position(f); dest < end; set_position(f, dest)) {
+               uint8_t header_size, atom_type;
                ret = atom_read_header(f, &atom_type, &header_size, &size);
                if (ret <= 0)
                        return ret;
                if (size == 0)
                        return -1;
-               counted_size += size;
+               dest = get_position(f) + size - header_size;
                if (atom_type == ATOM_TRAK) {
                        if (f->total_tracks >= MAX_TRACKS)
                                return -1;
@@ -734,14 +664,23 @@ static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
                        f->udta_offset = get_position(f) - header_size;
                        f->udta_size = size;
                }
-               if (!need_atom(atom_type, meta_only)) {
-                       set_position(f, get_position(f) + size - header_size);
+               if (!need_atom(atom_type, meta_only))
                        continue;
-               }
-               if (atom_type < SUBATOMIC) /* atom contains subatoms */
+               switch (atom_type) {
+               case ATOM_STSZ: ret = read_stsz(f); break;
+               case ATOM_STTS: ret = read_stts(f); break;
+               case ATOM_STSC: ret = read_stsc(f); break;
+               case ATOM_STCO: ret = read_stco(f); break;
+               case ATOM_STSD: ret = read_stsd(f); break;
+               case ATOM_MDHD: ret = read_mdhd(f); break;
+               case ATOM_META:
+                       f->meta_offset = get_position(f) - header_size;
+                       f->meta_size = size;
+                       ret = read_meta(f, size);
+                       break;
+               default:
                        ret = parse_sub_atoms(f, size - header_size, meta_only);
-               else
-                       ret = parse_leaf_atom(f, size, header_size, atom_type);
+               }
                if (ret <= 0)
                        return ret;
        }
@@ -841,9 +780,9 @@ uint64_t mp4_get_duration(const struct mp4 *f)
 {
        const struct mp4_track *t = f->audio_track;
 
-       if (t->timeScale == 0)
+       if (t->time_scale == 0)
                return 0;
-       return t->duration * 1000 / t->timeScale;
+       return t->duration * 1000 / t->time_scale;
 }
 
 int mp4_set_sample_position(struct mp4 *f, int32_t sample)
@@ -882,12 +821,12 @@ int32_t mp4_get_sample_size(const struct mp4 *f, int sample)
 
 uint32_t mp4_get_sample_rate(const struct mp4 *f)
 {
-       return f->audio_track->sampleRate;
+       return f->audio_track->sample_rate;
 }
 
 uint32_t mp4_get_channel_count(const struct mp4 *f)
 {
-       return f->audio_track->channelCount ;
+       return f->audio_track->channel_count;
 }
 
 int32_t mp4_num_samples(const struct mp4 *f)
@@ -964,11 +903,6 @@ static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
        }
 }
 
-static uint32_t fix_byte_order_32(uint32_t src)
-{
-       return read_u32_be(&src);
-}
-
 static void *modify_moov(struct mp4 *f, uint32_t *out_size)
 {
        int ret;
@@ -994,7 +928,7 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        ret = read_int32(f, &tmp);
        if (ret <= 0)
                return NULL;
-       *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
+       write_u32_be(p_out, tmp + size_delta);
        p_out += 4;
        ret = read_data(f, p_out, 4);
        if (ret <= 0)
@@ -1007,7 +941,7 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        ret = read_int32(f, &tmp);
        if (ret <= 0)
                return NULL;
-       *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
+       write_u32_be(p_out, tmp + size_delta);
        p_out += 4;
        ret = read_data(f, p_out, 4);
        if (ret <= 0)
@@ -1020,7 +954,7 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        ret = read_int32(f, &tmp);
        if (ret <= 0)
                return NULL;
-       *(uint32_t *)p_out = fix_byte_order_32(tmp + size_delta);
+       write_u32_be(p_out, tmp + size_delta);
        p_out += 4;
        ret = read_data(f, p_out, 4);
        if (ret <= 0)
@@ -1036,28 +970,27 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        return out_buffer;
 }
 
-static int32_t write_data(struct mp4 *f, void *data, uint32_t size)
+static int write_data(struct mp4 *f, void *data, size_t size)
 {
-       int32_t result = 1;
-
-       result = f->cb->write(f->cb->user_data, data, size);
-
-       f->current_position += size;
-
-       return result;
-}
-
-static int32_t write_int32(struct mp4 *f, uint32_t data)
-{
-       int8_t temp[4];
-       write_u32_be(temp, data);
-       return write_data(f, temp, sizeof(temp));
+       while (size > 0) {
+               ssize_t ret = f->cb->write(f->cb->user_data, data, size);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return -ERRNO_TO_PARA_ERROR(errno);
+               }
+               f->current_position += ret;
+               size -= ret;
+       }
+       return 1;
 }
 
-int32_t mp4_meta_update(struct mp4 *f)
+int mp4_meta_update(struct mp4 *f)
 {
        void *new_moov_data;
        uint32_t new_moov_size;
+       uint8_t buf[8] = "----moov";
+       int ret;
 
        set_position(f, 0);
        new_moov_data = modify_moov(f, &new_moov_size);
@@ -1065,27 +998,26 @@ int32_t mp4_meta_update(struct mp4 *f)
                mp4_close(f);
                return 0;
        }
-       /* copy moov atom to end of the file */
        if (f->last_atom != ATOM_MOOV) {
-               char *free_data = "free";
-
-               /* rename old moov to free */
                set_position(f, f->moov_offset + 4);
-               write_data(f, free_data, 4);
-
-               set_position(f, f->file_size);
-               write_int32(f, new_moov_size + 8);
-               write_data(f, "moov", 4);
-               write_data(f, new_moov_data, new_moov_size);
-       } else {
+               ret = write_data(f, "free", 4); /* rename old moov to free */
+               if (ret < 0)
+                       goto free_moov;
+               set_position(f, f->file_size); /* write new moov atom at EOF */
+       } else /* overwrite old moov atom */
                set_position(f, f->moov_offset);
-               write_int32(f, new_moov_size + 8);
-               write_data(f, "moov", 4);
-               write_data(f, new_moov_data, new_moov_size);
-       }
-       free(new_moov_data);
+       write_u32_be(buf, new_moov_size + 8);
+       ret = write_data(f, buf, sizeof(buf));
+       if (ret < 0)
+               goto free_moov;
+       ret = write_data(f, new_moov_data, new_moov_size);
+       if (ret < 0)
+               goto free_moov;
        f->cb->truncate(f->cb->user_data);
-       return 1;
+       ret = 1;
+free_moov:
+       free(new_moov_data);
+       return ret;
 }
 
 static char *meta_find_by_name(const struct mp4 *f, const char *item)