+ milliseconds = mp4_get_duration(c->mp4);
+ afhi->seconds_total = milliseconds / 1000;
+ ms2tv(milliseconds / afhi->chunks_total, &afhi->chunk_tv);
+ if (aac_afh_get_chunk(0, c, &buf, &len) >= 0)
+ numbytes -= buf - map;
+ afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
+ aac_afh_get_taginfo(c->mp4, &afhi->tags);
+ ret = 1;
+ aac_afh_close(c);
+ return ret;
+}
+
+static ssize_t aac_afh_meta_read_cb(void *user_data, void *dest, size_t want)
+{
+ int fd = *(int *)user_data;
+ return read(fd, dest, want);
+}
+
+static off_t aac_afh_meta_seek_cb(void *user_data, off_t offset, int whence)
+{
+ int fd = *(int *)user_data;
+ off_t ret = lseek(fd, offset, whence);
+
+ assert(ret != (off_t)-1);
+ return ret;
+}
+
+static ssize_t aac_afh_meta_write_cb(void *user_data, void *dest, size_t count)
+{
+ int fd = *(int *)user_data;
+ return write(fd, dest, count);
+}
+
+static int aac_afh_meta_truncate_cb(void *user_data)
+{
+ int fd = *(int *)user_data;
+ off_t offset = lseek(fd, 0, SEEK_CUR);
+ return ftruncate(fd, offset);
+}
+
+static void replace_or_add_tag(const char *item, const char *value,
+ struct mp4_metadata *meta)
+{
+ uint32_t n;
+ struct mp4_tag *t;
+
+ for (n = 0; n < meta->count; n++) {
+ t = meta->tags + n;
+ if (strcasecmp(t->item, item))
+ continue;
+ free(t->value);
+ t->value = para_strdup(value);
+ return;
+ }
+ /* item not found, add new tag */
+ meta->tags = para_realloc(meta->tags, (meta->count + 1)
+ * sizeof(struct mp4_tag));
+ t = meta->tags + meta->count;
+ t->item = para_strdup(item);
+ t->value = para_strdup(value);
+ meta->count++;