struct mp4_track *track[MAX_TRACKS];
/* metadata */
- struct mp4_metadata tags;
+ struct mp4_metadata meta;
};
int32_t mp4_total_tracks(const struct mp4 *f)
return 0;
}
-static int32_t tag_add_field(struct mp4_metadata *tags, const char *item,
+static int32_t tag_add_field(struct mp4_metadata *meta, const char *item,
const char *value, int32_t len)
{
- tags->tags = para_realloc(tags->tags,
- (tags->count + 1) * sizeof(struct mp4_tag));
- tags->tags[tags->count].item = para_strdup(item);
- tags->tags[tags->count].len = len;
+ meta->tags = para_realloc(meta->tags,
+ (meta->count + 1) * sizeof(struct mp4_tag));
+ meta->tags[meta->count].item = para_strdup(item);
+ meta->tags[meta->count].len = len;
if (len >= 0) {
- tags->tags[tags->count].value = para_malloc(len + 1);
- memcpy(tags->tags[tags->count].value, value, len);
- tags->tags[tags->count].value[len] = 0;
+ meta->tags[meta->count].value = para_malloc(len + 1);
+ memcpy(meta->tags[meta->count].value, value, len);
+ meta->tags[meta->count].value[len] = 0;
} else {
- tags->tags[tags->count].value = para_strdup(value);
+ meta->tags[meta->count].value = para_strdup(value);
}
- tags->count++;
+ meta->count++;
return 1;
}
-static const char *ID3v1GenreList[] = {
- "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
- "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
- "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
- "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
- "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal",
- "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid",
- "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
- "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
- "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
- "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
- "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
- "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
- "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
- "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
- "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk",
- "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic",
- "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
- "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
- "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
- "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
- "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
- "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
- "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House",
- "Dance Hall", "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
- "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
- "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
- "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
- "Anime", "JPop", "SynthPop",
-};
-
static char *read_string(struct mp4 *f, uint32_t length)
{
char *str = para_malloc(length + 1);
}
if (!data)
return;
- tag_add_field(&f->tags, get_metadata_name(parent), data, len);
+ tag_add_field(&f->meta, get_metadata_name(parent), data, len);
free(data);
}
return f;
}
-static int32_t tag_delete(struct mp4_metadata *tags)
+static int32_t tag_delete(struct mp4_metadata *meta)
{
uint32_t i;
- for (i = 0; i < tags->count; i++) {
- free(tags->tags[i].item);
- free(tags->tags[i].value);
+ for (i = 0; i < meta->count; i++) {
+ free(meta->tags[i].item);
+ free(meta->tags[i].value);
}
- free(tags->tags);
- tags->tags = NULL;
- tags->count = 0;
+ free(meta->tags);
+ meta->tags = NULL;
+ meta->count = 0;
return 0;
}
}
}
- tag_delete(&(f->tags));
+ tag_delete(&(f->meta));
free(f);
}
return f;
}
-int32_t mp4_meta_get_num_items(const struct mp4 *f)
-{
- return f->tags.count;
-}
-
-int32_t mp4_meta_get_by_index(const struct mp4 *f, uint32_t index,
- char **item, char **value)
+/**
+ * Return the metadata of an mp4 file.
+ *
+ * \param f As returned by either \ref mp4_open_read() or \ref mp4_open_meta().
+ *
+ * The caller is allowed to add, delete or modify the entries of the returned
+ * structure in order to pass the modified version to \ref mp4_meta_update().
+ */
+struct mp4_metadata *mp4_get_meta(struct mp4 *f)
{
- if (index >= f->tags.count) {
- *item = NULL;
- *value = NULL;
- return 0;
- } else {
- *item = para_strdup(f->tags.tags[index].item);
- *value = para_strdup(f->tags.tags[index].value);
- return 1;
- }
+ return &f->meta;
}
static uint32_t find_atom(struct mp4 *f, uint64_t base, uint32_t size,
return membuffer_write(buf, data, 4) == 4 ? 1 : 0;
}
-static unsigned membuffer_write_int16(struct membuffer *buf, uint16_t data)
-{
- uint8_t temp[2];
-
- write_u16_be(temp, data);
- return membuffer_write(buf, temp, 2);
-}
-
static unsigned membuffer_write_int32(struct membuffer *buf, uint32_t data)
{
uint8_t temp[4];
return membuffer_write(buf, temp, 4);
}
-static void membuffer_write_track_tag(struct membuffer *buf, const char *name,
- uint32_t index, uint32_t total)
-{
- membuffer_write_int32(buf,
- 8 /*atom header */ + 8 /*data atom header */ +
- 8 /*flags + reserved */ + 8 /*actual data */ );
- membuffer_write_atom_name(buf, name);
- membuffer_write_int32(buf,
- 8 /*data atom header */ +
- 8 /*flags + reserved */ + 8 /*actual data */ );
- membuffer_write_atom_name(buf, "data");
- membuffer_write_int32(buf, 0); //flags
- membuffer_write_int32(buf, 0); //reserved
- membuffer_write_int16(buf, 0);
- membuffer_write_int16(buf, (uint16_t) index); //track number
- membuffer_write_int16(buf, (uint16_t) total); //total tracks
- membuffer_write_int16(buf, 0);
-}
-
-static void membuffer_write_int16_tag(struct membuffer *buf, const char *name,
- uint16_t value)
-{
- membuffer_write_int32(buf,
- 8 /*atom header */ + 8 /*data atom header */ +
- 8 /*flags + reserved */ + 2 /*actual data */ );
- membuffer_write_atom_name(buf, name);
- membuffer_write_int32(buf,
- 8 /*data atom header */ +
- 8 /*flags + reserved */ + 2 /*actual data */ );
- membuffer_write_atom_name(buf, "data");
- membuffer_write_int32(buf, 0); //flags
- membuffer_write_int32(buf, 0); //reserved
- membuffer_write_int16(buf, value); //value
-}
-
-static uint32_t myatoi(const char *param)
-{
- return param ? atoi(param) : 0;
-}
-
-static uint32_t meta_genre_to_index(const char *genrestr)
-{
- unsigned n;
- for (n = 0; n < sizeof (ID3v1GenreList) / sizeof (ID3v1GenreList[0]); n++) {
- if (!strcasecmp(genrestr, ID3v1GenreList[n]))
- return n + 1;
- }
- return 0;
-}
-
-struct stdmeta_entry {
- const char *atom;
- const char *name;
-};
-
-struct stdmeta_entry stdmetas[] = {
- {"\xA9" "nam", "title"},
- {"\xA9" "ART", "artist"},
- {"\xA9" "wrt", "writer"},
- {"\xA9" "alb", "album"},
- {"\xA9" "day", "date"},
- {"\xA9" "too", "tool"},
- {"\xA9" "cmt", "comment"},
- {"cpil", "compilation"},
- {"covr", "cover"},
- {"aART", "album_artist"},
-};
-
-static const char *find_standard_meta(const char *name) //returns atom name if found, 0 if not
-{
- unsigned n;
- for (n = 0; n < sizeof (stdmetas) / sizeof (stdmetas[0]); n++) {
- if (!strcasecmp(name, stdmetas[n].name))
- return stdmetas[n].atom;
- }
- return 0;
-}
-
static void membuffer_write_std_tag(struct membuffer *buf, const char *name,
const char *value)
{
- uint32_t flags = 1;
-
- /* special check for compilation flag */
- if (strcmp(name, "cpil") == 0) {
- flags = 21;
- }
-
- membuffer_write_int32(buf,
- 8 /*atom header */ + 8 /*data atom header */ +
- 8 /*flags + reserved */ + strlen(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 */ + strlen(value));
- membuffer_write_atom_name(buf, "data");
- membuffer_write_int32(buf, flags); //flags
- membuffer_write_int32(buf, 0); //reserved
- membuffer_write(buf, value, strlen(value));
-}
-
-static void membuffer_write_custom_tag(struct membuffer *buf, const char *name,
- const char *value)
-{
- membuffer_write_int32(buf,
- 8 /*atom header */ +
- 0x1C /*weirdo itunes atom */ +
- 12 /*name atom header */ + strlen(name) +
- 16 /*data atom header + flags */ + strlen(value));
- membuffer_write_atom_name(buf, "----");
- membuffer_write_int32(buf, 0x1C); //weirdo itunes atom
- membuffer_write_atom_name(buf, "mean");
- membuffer_write_int32(buf, 0);
- membuffer_write(buf, "com.apple.iTunes", 16);
- membuffer_write_int32(buf, 12 + strlen(name));
- membuffer_write_atom_name(buf, "name");
- membuffer_write_int32(buf, 0);
- membuffer_write(buf, name, strlen(name));
- membuffer_write_int32(buf,
- 8 /*data atom header */ +
- 8 /*flags + reserved */ + strlen(value));
+ 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, strlen(value));
+ membuffer_write_int32(buf, 1); /* flags */
+ membuffer_write_int32(buf, 0); /* reserved */
+ membuffer_write(buf, value, len);
}
static unsigned membuffer_error(const struct membuffer *buf)
return ret;
}
-static uint32_t create_ilst(const struct mp4_metadata *data, void **out_buffer,
+struct stdmeta_entry {
+ const char *atom;
+ const char *name;
+};
+
+static const char *find_standard_meta(const char *name)
+{
+ const struct stdmeta_entry stdmetas[] = {
+ {"\xA9" "nam", "title"},
+ {"\xA9" "ART", "artist"},
+ {"\xA9" "alb", "album"},
+ {"\xA9" "day", "date"},
+ {"\xA9" "cmt", "comment"},
+ };
+
+ for (unsigned n = 0; n < ARRAY_SIZE(stdmetas); n++)
+ if (!strcasecmp(name, stdmetas[n].name))
+ return stdmetas[n].atom;
+ return NULL;
+}
+
+static uint32_t create_ilst(const struct mp4_metadata *meta, void **out_buffer,
uint32_t * out_size)
{
struct membuffer *buf = membuffer_create();
unsigned metaptr;
- char *mask = para_calloc(data->count);
- const char *tracknumber_ptr = 0, *totaltracks_ptr = 0;
- const char *discnumber_ptr = 0, *totaldiscs_ptr = 0;
- const char *genre_ptr = 0, *tempo_ptr = 0;
-
- for (metaptr = 0; metaptr < data->count; metaptr++) {
- struct mp4_tag *tag = &data->tags[metaptr];
- if (!strcasecmp(tag->item, "tracknumber")
- || !strcasecmp(tag->item, "track")) {
- if (tracknumber_ptr == 0)
- tracknumber_ptr = tag->value;
- mask[metaptr] = 1;
- } else if (!strcasecmp(tag->item, "totaltracks")) {
- if (totaltracks_ptr == 0)
- totaltracks_ptr = tag->value;
- mask[metaptr] = 1;
- } else if (!strcasecmp(tag->item, "discnumber")
- || !strcasecmp(tag->item, "disc")) {
- if (discnumber_ptr == 0)
- discnumber_ptr = tag->value;
- mask[metaptr] = 1;
- } else if (!strcasecmp(tag->item, "totaldiscs")) {
- if (totaldiscs_ptr == 0)
- totaldiscs_ptr = tag->value;
- mask[metaptr] = 1;
- } else if (!strcasecmp(tag->item, "genre")) {
- if (genre_ptr == 0)
- genre_ptr = tag->value;
- mask[metaptr] = 1;
- } else if (!strcasecmp(tag->item, "tempo")) {
- if (tempo_ptr == 0)
- tempo_ptr = tag->value;
- mask[metaptr] = 1;
- }
- }
-
- if (tracknumber_ptr)
- membuffer_write_track_tag(buf, "trkn", myatoi(tracknumber_ptr),
- myatoi(totaltracks_ptr));
- if (discnumber_ptr)
- membuffer_write_track_tag(buf, "disk", myatoi(discnumber_ptr),
- myatoi(totaldiscs_ptr));
- if (tempo_ptr)
- membuffer_write_int16_tag(buf, "tmpo", myatoi(tempo_ptr));
-
- if (genre_ptr) {
- uint32_t index = meta_genre_to_index(genre_ptr);
- if (index == 0)
- membuffer_write_std_tag(buf, "©gen", genre_ptr);
- else
- membuffer_write_int16_tag(buf, "gnre", index);
- }
- for (metaptr = 0; metaptr < data->count; metaptr++) {
- struct mp4_tag *tag;
- const char *std_meta_atom;
- if (mask[metaptr])
- continue;
- tag = &data->tags[metaptr];
- std_meta_atom = find_standard_meta(tag->item);
+ for (metaptr = 0; metaptr < meta->count; metaptr++) {
+ struct mp4_tag *tag = meta->tags + metaptr;
+ const char *std_meta_atom = find_standard_meta(tag->item);
if (std_meta_atom)
membuffer_write_std_tag(buf, std_meta_atom, tag->value);
else
- membuffer_write_custom_tag(buf, tag->item, tag->value);
+ PARA_ERROR_LOG("invalid tag item: %s\n", tag->item);
}
- free(mask);
-
if (membuffer_error(buf)) {
membuffer_free(buf);
return 0;
return bytes;
}
-static uint32_t create_meta(const struct mp4_metadata *data, void **out_buffer,
+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;
- if (!create_ilst(data, &ilst_buffer, &ilst_size))
+ if (!create_ilst(meta, &ilst_buffer, &ilst_size))
return 0;
buf = membuffer_create();
return 1;
}
-static uint32_t create_udta(const struct mp4_metadata *data, void **out_buffer,
+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(data, &meta_buffer, &meta_size))
+ if (!create_meta(meta, &meta_buffer, &meta_size))
return 0;
buf = membuffer_create();
return read_u32_be(&src);
}
-static uint32_t modify_moov(struct mp4 *f, const struct mp4_metadata *data,
- void **out_buffer, uint32_t * out_size)
+static uint32_t modify_moov(struct mp4 *f, void **out_buffer,
+ uint32_t *out_size)
{
uint64_t total_base = f->moov_offset + 8;
uint32_t total_size = (uint32_t) (f->moov_size - 8);
struct membuffer *buf;
void *new_udta_buffer;
uint32_t new_udta_size;
- if (!create_udta(data, &new_udta_buffer, &new_udta_size))
+ if (!create_udta(&f->meta, &new_udta_buffer, &new_udta_size))
return 0;
buf = membuffer_create();
struct membuffer *buf;
void *new_meta_buffer;
uint32_t new_meta_size;
- if (!create_meta(data, &new_meta_buffer, &new_meta_size))
+
+ if (!create_meta(&f->meta, &new_meta_buffer, &new_meta_size))
return 0;
buf = membuffer_create();
ilst_offset = get_position(f);
ilst_size = read_int32(f);
- if (!create_ilst(data, &new_ilst_buffer, &new_ilst_size))
+ if (!create_ilst(&f->meta, &new_ilst_buffer, &new_ilst_size))
return 0;
size_delta = new_ilst_size - (ilst_size - 8);
return write_data(f, temp, sizeof(temp));
}
-int32_t mp4_meta_update(struct mp4 *f, struct mp4_metadata *meta)
+int32_t mp4_meta_update(struct mp4 *f)
{
void *new_moov_data;
uint32_t new_moov_size;
- tag_delete(&f->tags);
- f->tags = *meta;
set_position(f, 0);
- if (!modify_moov(f, meta, &new_moov_data, &new_moov_size)) {
+ if (!modify_moov(f, &new_moov_data, &new_moov_size)) {
mp4_close(f);
return 0;
}
-
/* copy moov atom to end of the file */
if (f->last_atom != ATOM_MOOV) {
char *free_data = "free";
{
uint32_t i;
- for (i = 0; i < f->tags.count; i++)
- if (!strcasecmp(f->tags.tags[i].item, item))
- return para_strdup(f->tags.tags[i].value);
+ for (i = 0; i < f->meta.count; i++)
+ if (!strcasecmp(f->meta.tags[i].item, item))
+ return para_strdup(f->meta.tags[i].value);
return NULL;
}