]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - mp4.c
mp4: Assume udta, meta and ilst are always present.
[paraslash.git] / mp4.c
diff --git a/mp4.c b/mp4.c
index 92be4c766724b471c7d31036b41b3408f3e36406..1d8af6cb20853c33a4aff5d151eb143951bd5a90 100644 (file)
--- a/mp4.c
+++ b/mp4.c
@@ -47,6 +47,13 @@ struct mp4 {
 
        uint64_t moov_offset;
        uint64_t moov_size;
+       uint64_t meta_offset;
+       uint32_t meta_size;
+       uint64_t ilst_offset;
+       uint32_t ilst_size;
+       uint64_t udta_offset;
+       uint32_t udta_size;
+
        uint8_t last_atom;
        uint64_t file_size;
 
@@ -791,6 +798,8 @@ static int32_t read_meta(struct mp4 *f, uint64_t size)
                if (subsize <= header_size + 4)
                        return 1;
                if (atom_type == ATOM_ILST) {
+                       f->ilst_offset = get_position(f) - header_size;
+                       f->ilst_size = subsize;
                        ret = read_ilst(f, subsize - (header_size + 4));
                        if (ret <= 0)
                                return ret;
@@ -801,7 +810,8 @@ 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 atom_type)
+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 */
@@ -813,7 +823,11 @@ static int parse_leaf_atom(struct mp4 *f, uint64_t size, uint8_t atom_type)
        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: ret = read_meta(f, size); 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;
@@ -829,6 +843,7 @@ static bool need_atom(uint8_t atom_type, bool meta_only)
        case ATOM_MDIA:
        case ATOM_MINF:
        case ATOM_STBL:
+       case ATOM_UDTA:
                return true;
        }
        /* meta-only opens don't need anything else */
@@ -841,7 +856,6 @@ static bool need_atom(uint8_t atom_type, bool meta_only)
        case ATOM_STCO:
        case ATOM_STSC:
        case ATOM_MDHD:
-       case ATOM_UDTA:
                return true;
        }
        return false;
@@ -869,6 +883,9 @@ static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
                        f->total_tracks++;
                        f->track[f->total_tracks - 1] = para_calloc(
                                sizeof(struct mp4_track));
+               } else if (atom_type == ATOM_UDTA) {
+                       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);
@@ -877,22 +894,21 @@ static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
                if (atom_type < SUBATOMIC) /* atom contains subatoms */
                        ret = parse_sub_atoms(f, size - header_size, meta_only);
                else
-                       ret = parse_leaf_atom(f, size, atom_type);
+                       ret = parse_leaf_atom(f, size, header_size, atom_type);
                if (ret <= 0)
                        return ret;
        }
        return 1;
 }
 
-static int parse_root_atoms(struct mp4 *f, bool meta_only)
+static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
 {
        int ret;
        uint64_t size;
-       uint8_t atom_type = 0;
-       uint8_t header_size = 0;
-
-       f->file_size = 0;
+       uint8_t atom_type, header_size;
+       struct mp4 *f = para_calloc(sizeof(*f));
 
+       f->cb = cb;
        while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
                f->file_size += size;
                f->last_atom = atom_type;
@@ -906,25 +922,24 @@ static int parse_root_atoms(struct mp4 *f, bool meta_only)
                if (ret <= 0)
                        break;
        }
-       if (ret < 0)
-               return ret;
+       if (ret < 0) {
+               ret = -E_MP4_OPEN;
+               goto fail;
+       }
+       ret = -E_MP4_TRACK;
        if (!f->audio_track)
-               return -E_MP4_TRACK;
+               goto fail;
+       *result = f;
+       return 1;
+fail:
+       *result = NULL;
+       free(f);
        return ret;
 }
 
-struct mp4 *mp4_open_read(const struct mp4_callback *cb)
+int mp4_open_read(const struct mp4_callback *cb, struct mp4 **result)
 {
-       int ret;
-       struct mp4 *f = para_calloc(sizeof(struct mp4));
-
-       f->cb = cb;
-       ret = parse_root_atoms(f, false);
-       if (ret < 0) {
-               free(f);
-               return NULL;
-       }
-       return f;
+       return open_file(cb, false, result);
 }
 
 void mp4_close(struct mp4 *f)
@@ -1039,18 +1054,20 @@ int32_t mp4_num_samples(const struct mp4 *f)
        return total;
 }
 
-struct mp4 *mp4_open_meta(const struct mp4_callback *cb)
+int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
 {
-       int ret;
-       struct mp4 *f = para_calloc(sizeof(struct mp4));
+       struct mp4 *f;
+       int ret = open_file(cb, true, &f);
 
-       f->cb = cb;
-       ret = parse_root_atoms(f, true);
-       if (ret < 0) {
-               free(f);
-               return NULL;
+       if (ret < 0)
+               return ret;
+       if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0) {
+               mp4_close(f);
+               *result = NULL;
+               return -E_MP4_MISSING_ATOM;
        }
-       return f;
+       *result = f;
+       return 1;
 }
 
 /**
@@ -1066,83 +1083,6 @@ struct mp4_metadata *mp4_get_meta(struct mp4 *f)
        return &f->meta;
 }
 
-static int find_atom(struct mp4 *f, uint64_t base, uint32_t size,
-       const char *name)
-{
-       uint32_t remaining = size;
-       uint64_t atom_offset = base;
-
-       for (;;) {
-               int ret;
-               char atom_name[4];
-               uint32_t atom_size;
-
-               set_position(f, atom_offset);
-
-               if (remaining < 8)
-                       return -1;
-               ret = read_int32(f, &atom_size);
-               if (ret <= 0)
-                       return ret;
-               if (atom_size > remaining || atom_size < 8)
-                       return -1;
-               ret = read_data(f, atom_name, 4);
-               if (ret <= 0)
-                       return ret;
-               if (!memcmp(atom_name, name, 4)) {
-                       set_position(f, atom_offset);
-                       return 1;
-               }
-               remaining -= atom_size;
-               atom_offset += atom_size;
-       }
-}
-
-/*
- * Try to find atom <name> with atom <name_inside> in it. Besides -1/0/1 for
- * error, EOF and success, this function may return 2 to indicate that the
- * desired atoms were not found.
- */
-static int find_atom_v2(struct mp4 *f, uint64_t base, uint32_t size,
-               const char *name, uint32_t extraheaders, const char *name_inside)
-{
-       uint64_t first_base = (uint64_t) (-1);
-
-       for (;;) {
-               uint64_t mybase;
-               uint32_t mysize;
-               int ret = find_atom(f, base, size, name);
-
-               if (ret <= 0)
-                       return ret;
-               mybase = get_position(f);
-               ret = read_int32(f, &mysize);
-               if (ret <= 0)
-                       return ret;
-               if (first_base == (uint64_t) (-1))
-                       first_base = mybase;
-
-               if (mysize < 8 + extraheaders)
-                       break;
-
-               if (find_atom (f, mybase + (8 + extraheaders),
-                               mysize - (8 + extraheaders), name_inside)) {
-                       set_position(f, mybase);
-                       return 1;
-               }
-               base += mysize;
-               if (size <= mysize)
-                       break;
-               size -= mysize;
-       }
-       if (first_base != (uint64_t)(-1)) {
-               set_position(f, first_base);
-               return 1;
-       }
-       /* wanted atom inside not found */
-       return 2;
-}
-
 struct membuffer {
        void *data;
        unsigned written;
@@ -1187,24 +1127,6 @@ static void membuffer_write_int32(struct membuffer *buf, uint32_t data)
        membuffer_write(buf, temp, 4);
 }
 
-static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
-               const char *value)
-{
-       uint32_t len = strlen(value);
-       membuffer_write_int32(buf, 8 /* atom header */
-               + 8 /* data atom header */
-               + 8 /* flags + reserved */
-               + len);
-       membuffer_write_atom_name(buf, name);
-       membuffer_write_int32(buf, 8 /* data atom header */
-               + 8 /* flags + reserved */
-               + len);
-       membuffer_write_atom_name(buf, "data");
-       membuffer_write_int32(buf, 1);  /* flags */
-       membuffer_write_int32(buf, 0);  /* reserved */
-       membuffer_write(buf, value, len);
-}
-
 static unsigned membuffer_get_size(const struct membuffer *buf)
 {
        return buf->written;
@@ -1224,7 +1146,9 @@ static void *create_ilst(const struct mp4_metadata *meta, uint32_t *out_size)
 
        for (n = 0; n < meta->count; n++) {
                struct mp4_tag *tag = meta->tags + n;
+               unsigned len = strlen(tag->value);
                const char *atom_name;
+
                if (!strcasecmp(tag->item, "title"))
                        atom_name = "\xA9" "nam";
                else if (!strcasecmp(tag->item, "artist"))
@@ -1237,81 +1161,23 @@ static void *create_ilst(const struct mp4_metadata *meta, uint32_t *out_size)
                        atom_name = "\xA9" "cmt";
                else
                        assert(false);
-               membuffer_write_std_tag(buf, atom_name, tag->value);
+               membuffer_write_int32(buf, 8 /* atom header */
+                       + 8 /* data atom header */
+                       + 8 /* flags + reserved */
+                       + len);
+               membuffer_write_atom_name(buf, atom_name);
+               membuffer_write_int32(buf, 8 /* data atom header */
+                       + 8 /* flags + reserved */
+                       + len);
+               membuffer_write_atom_name(buf, "data");
+               membuffer_write_int32(buf, 1);  /* flags */
+               membuffer_write_int32(buf, 0);  /* reserved */
+               membuffer_write(buf, tag->value, len);
        }
        *out_size = membuffer_get_size(buf);
        return membuffer_detach(buf);
 }
 
-static void membuffer_write_atom(struct membuffer *buf, const char *name, unsigned size,
-                         const void *data)
-{
-       membuffer_write_int32(buf, size + 8);
-       membuffer_write_atom_name(buf, name);
-       membuffer_write(buf, data, size);
-}
-
-static void *membuffer_get_ptr(const struct membuffer *buf)
-{
-       return buf->data;
-}
-
-static bool membuffer_transfer_from_file(struct membuffer *buf, struct mp4 *src,
-               unsigned bytes)
-{
-       unsigned oldsize = membuffer_get_size(buf);
-       char *bufptr;
-
-       membuffer_write(buf, 0, bytes);
-       bufptr = membuffer_get_ptr(buf);
-       if (read_data(src, bufptr + oldsize, bytes) != 1) {
-               free(buf->data);
-               free(buf);
-               return false;
-       }
-       return true;
-}
-
-static uint32_t create_meta(const struct mp4_metadata *meta, void **out_buffer,
-               uint32_t * out_size)
-{
-       struct membuffer *buf;
-       uint32_t ilst_size;
-       void *ilst_buffer;
-
-       ilst_buffer = create_ilst(meta, &ilst_size);
-
-       buf = membuffer_create();
-       membuffer_write_int32(buf, 0);
-       membuffer_write_atom(buf, "ilst", ilst_size, ilst_buffer);
-       free(ilst_buffer);
-
-       *out_size = membuffer_get_size(buf);
-       *out_buffer = membuffer_detach(buf);
-       return 1;
-}
-
-static uint32_t create_udta(const struct mp4_metadata *meta, void **out_buffer,
-uint32_t * out_size)
-{
-       struct membuffer *buf;
-       uint32_t meta_size;
-       void *meta_buffer;
-
-       if (!create_meta(meta, &meta_buffer, &meta_size))
-               return 0;
-
-       buf = membuffer_create();
-
-       membuffer_write_atom(buf, "meta", meta_size, meta_buffer);
-
-       free(meta_buffer);
-
-       *out_size = membuffer_get_size(buf);
-       *out_buffer = membuffer_detach(buf);
-       return 1;
-}
-
 static uint32_t fix_byte_order_32(uint32_t src)
 {
        return read_u32_be(&src);
@@ -1322,95 +1188,22 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        int ret;
        uint64_t total_base = f->moov_offset + 8;
        uint32_t total_size = (uint32_t) (f->moov_size - 8);
-       uint64_t udta_offset, meta_offset, ilst_offset;
-       uint32_t udta_size, meta_size, ilst_size;
        uint32_t new_ilst_size;
        void *new_ilst_buffer, *out_buffer;
        uint8_t *p_out;
        int32_t size_delta;
        uint32_t tmp;
 
-       ret = find_atom_v2(f, total_base, total_size, "udta", 0, "meta");
-       if (ret <= 0)
-               return NULL;
-       if (ret == 2) {
-               struct membuffer *buf;
-               void *new_udta_buffer;
-               uint32_t new_udta_size;
-               if (!create_udta(&f->meta, &new_udta_buffer, &new_udta_size))
-                       return NULL;
-
-               buf = membuffer_create();
-               set_position(f, total_base);
-               if (!membuffer_transfer_from_file(buf, f, total_size)) {
-                       free(new_udta_buffer);
-                       return NULL;
-               }
-               membuffer_write_atom(buf, "udta", new_udta_size,
-                       new_udta_buffer);
-
-               free(new_udta_buffer);
-
-               *out_size = membuffer_get_size(buf);
-               return membuffer_detach(buf);
-       }
-       udta_offset = get_position(f);
-       ret = read_int32(f, &udta_size);
-       if (ret <= 0)
-               return NULL;
-       ret = find_atom_v2(f, udta_offset + 8, udta_size - 8, "meta", 4, "ilst");
-       if (ret <= 0)
-               return NULL;
-       if (ret == 2) {
-               struct membuffer *buf;
-               void *new_meta_buffer;
-               uint32_t new_meta_size;
-
-               if (!create_meta(&f->meta, &new_meta_buffer, &new_meta_size))
-                       return NULL;
-
-               buf = membuffer_create();
-               set_position(f, total_base);
-               if (!membuffer_transfer_from_file(buf, f,
-                               udta_offset - total_base)) {
-                       free(new_meta_buffer);
-                       return NULL;
-               }
-
-               membuffer_write_int32(buf, udta_size + 8 + new_meta_size);
-               membuffer_write_atom_name(buf, "udta");
-               if (!membuffer_transfer_from_file(buf, f, udta_size)) {
-                       free(new_meta_buffer);
-                       return NULL;
-               }
-               membuffer_write_atom(buf, "meta", new_meta_size,
-                       new_meta_buffer);
-               free(new_meta_buffer);
-
-               *out_size = membuffer_get_size(buf);
-               return membuffer_detach(buf);
-       }
-       meta_offset = get_position(f);
-       ret = read_int32(f, &meta_size);
-       if (ret <= 0)
-               return NULL;
-       /* shouldn't happen, find_atom_v2 above takes care of it */
-       if (!find_atom(f, meta_offset + 12, meta_size - 12, "ilst"))
-               return NULL;
-       ilst_offset = get_position(f);
-       ret = read_int32(f, &ilst_size);
-       if (ret <= 0)
-               return NULL;
        new_ilst_buffer = create_ilst(&f->meta, &new_ilst_size);
-       size_delta = new_ilst_size - (ilst_size - 8);
+       size_delta = new_ilst_size - (f->ilst_size - 8);
        *out_size = total_size + size_delta;
        out_buffer = para_malloc(*out_size);
        p_out = out_buffer;
        set_position(f, total_base);
-       ret = read_data(f, p_out, udta_offset - total_base);
+       ret = read_data(f, p_out, f->udta_offset - total_base);
        if (ret <= 0)
                return NULL;
-       p_out += (uint32_t) (udta_offset - total_base);
+       p_out += f->udta_offset - total_base;
        ret = read_int32(f, &tmp);
        if (ret <= 0)
                return NULL;
@@ -1420,10 +1213,10 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        if (ret <= 0)
                return NULL;
        p_out += 4;
-       ret = read_data(f, p_out, meta_offset - udta_offset - 8);
+       ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
        if (ret <= 0)
                return NULL;
-       p_out += (uint32_t) (meta_offset - udta_offset - 8);
+       p_out += f->meta_offset - f->udta_offset - 8;
        ret = read_int32(f, &tmp);
        if (ret <= 0)
                return NULL;
@@ -1433,10 +1226,10 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        if (ret <= 0)
                return NULL;
        p_out += 4;
-       ret = read_data(f, p_out, ilst_offset - meta_offset - 8);
+       ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
        if (ret <= 0)
                return NULL;
-       p_out += (uint32_t) (ilst_offset - meta_offset - 8);
+       p_out += f->ilst_offset - f->meta_offset - 8;
        ret = read_int32(f, &tmp);
        if (ret <= 0)
                return NULL;
@@ -1448,9 +1241,9 @@ static void *modify_moov(struct mp4 *f, uint32_t *out_size)
        p_out += 4;
        memcpy(p_out, new_ilst_buffer, new_ilst_size);
        p_out += new_ilst_size;
-       set_position(f, ilst_offset + ilst_size);
-       ret = read_data(f, p_out, total_size
-               - (ilst_offset - total_base) - ilst_size);
+       set_position(f, f->ilst_offset + f->ilst_size);
+       ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
+               - f->ilst_size);
        if (ret <= 0)
                return NULL;
        free(new_ilst_buffer);