shithub: aacdec

Download patch

ref: 593d9c3caed1588271c91984308efaa2f22c6039
parent: 4f469f5ce50621af6b7f40cb89095216dd5fb4d0
author: menno <menno>
date: Sat Nov 22 10:38:31 EST 2003

tagging files

--- /dev/null
+++ b/common/mp4ff/mp4ffint.h
@@ -1,0 +1,307 @@
+/*
+** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**
+** Any non-GPL usage of this software or parts of this software is strictly
+** forbidden.
+**
+** Commercial non-GPL licensing of this software is possible.
+** For more info contact Ahead Software through Mpeg4AAClicense@nero.com.
+**
+** $Id: mp4ffint.h,v 1.1 2003/11/22 15:38:31 menno Exp $
+**/
+
+#ifndef MP4FF_INTERNAL_H
+#define MP4FF_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define MAX_TRACKS 1024
+#define TRACK_UNKNOWN 0
+#define TRACK_AUDIO   1
+#define TRACK_VIDEO   2
+#define TRACK_SYSTEM  3
+
+
+#define SUBATOMIC 128
+
+/* atoms without subatoms */
+#define ATOM_FTYP 129
+#define ATOM_MDAT 130
+#define ATOM_MVHD 131
+#define ATOM_TKHD 132
+#define ATOM_TREF 133
+#define ATOM_MDHD 134
+#define ATOM_VMHD 135
+#define ATOM_SMHD 136
+#define ATOM_HMHD 137
+#define ATOM_STSD 138
+#define ATOM_STTS 139
+#define ATOM_STSZ 140
+#define ATOM_STZ2 141
+#define ATOM_STCO 142
+#define ATOM_STSC 143
+#define ATOM_MP4A 144
+#define ATOM_MP4V 145
+#define ATOM_MP4S 146
+#define ATOM_ESDS 147
+#define ATOM_META 148 /* iTunes Metadata box */
+#define ATOM_NAME 149 /* iTunes Metadata name box */
+#define ATOM_DATA 150 /* iTunes Metadata data box */
+
+#define ATOM_UNKNOWN 255
+#define ATOM_FREE ATOM_UNKNOWN
+#define ATOM_SKIP ATOM_UNKNOWN
+
+/* atoms with subatoms */
+#define ATOM_MOOV 1
+#define ATOM_TRAK 2
+#define ATOM_EDTS 3
+#define ATOM_MDIA 4
+#define ATOM_MINF 5
+#define ATOM_STBL 6
+#define ATOM_UDTA 7
+#define ATOM_ILST 8 /* iTunes Metadata list */
+#define ATOM_TITLE 9
+#define ATOM_ARTIST 10
+#define ATOM_WRITER 11
+#define ATOM_ALBUM 12
+#define ATOM_DATE 13
+#define ATOM_TOOL 14
+#define ATOM_COMMENT 15
+#define ATOM_GENRE1 16
+#define ATOM_TRACK 17
+#define ATOM_DISC 18
+#define ATOM_COMPILATION 19
+#define ATOM_GENRE2 20
+#define ATOM_TEMPO 21
+
+
+#ifdef _WIN32
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#ifdef HAVE_CONFIG_H
+#include "../../config.h"
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#define u_int8_t uint8_t
+#define u_int16_t uint16_t
+#define u_int32_t uint32_t
+#define u_int64_t uint64_t
+#else
+typedef unsigned long long uint64_t;
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+typedef unsigned char uint8_t;
+typedef long long int64_t;
+typedef long int32_t;
+typedef short int16_t;
+typedef char int8_t;
+#endif
+#endif
+#endif
+
+/* metadata tag structure */
+typedef struct
+{
+    char *item;
+    char *value;
+} mp4ff_tag_t;
+
+/* metadata list structure */
+typedef struct
+{
+    mp4ff_tag_t *tags;
+    uint32_t count;
+} mp4ff_metadata_t;
+
+
+/* file callback structure */
+typedef struct
+{
+    int32_t (*read)(void *udata, void *buffer, int32_t length);
+    int32_t (*seek)(void *udata, int32_t position);
+    void *user_data;
+} mp4ff_callback_t;
+
+typedef struct
+{
+    int32_t type;
+    int32_t channelCount;
+    int32_t sampleSize;
+    uint16_t sampleRate;
+
+    /* stsd */
+    int32_t stsd_entry_count;
+
+    /* stsz */
+	int32_t stsz_sample_size;
+	int32_t stsz_sample_count;
+    int32_t *stsz_table;
+
+    /* stts */
+	int32_t stts_entry_count;
+    int32_t *stts_sample_count;
+    int32_t *stts_sample_delta;
+
+    /* stsc */
+	int32_t stsc_entry_count;
+    int32_t *stsc_first_chunk;
+    int32_t *stsc_samples_per_chunk;
+    int32_t *stsc_sample_desc_index;
+
+    /* stsc */
+	int32_t stco_entry_count;
+    int32_t *stco_chunk_offset;
+
+    /* esde */
+    uint8_t *decoderConfig;
+    int32_t decoderConfigLen;
+
+} mp4ff_track_t;
+
+/* mp4 main file structure */
+typedef struct
+{
+    /* stream to read from */
+	mp4ff_callback_t *stream;
+    int32_t current_position;
+
+    int32_t moov_read;
+    int32_t moov_offset;
+    int32_t moov_size;
+    int32_t mdat_read;
+    int32_t mdat_offset;
+    int32_t mdat_size;
+
+    /* mvhd */
+    int32_t time_scale;
+    int32_t duration;
+
+    /* incremental track index while reading the file */
+    int32_t total_tracks;
+
+    /* track data */
+    mp4ff_track_t *track[MAX_TRACKS];
+
+    /* metadata */
+    mp4ff_metadata_t tags;
+} mp4ff_t;
+
+
+
+
+/* mp4util.c */
+int32_t mp4ff_read_data(mp4ff_t *f, int8_t *data, const int32_t size);
+uint32_t mp4ff_read_int32(mp4ff_t *f);
+uint32_t mp4ff_read_int24(mp4ff_t *f);
+uint16_t mp4ff_read_int16(mp4ff_t *f);
+uint8_t mp4ff_read_char(mp4ff_t *f);
+uint32_t mp4ff_read_mp4_descr_length(mp4ff_t *f);
+int32_t mp4ff_position(const mp4ff_t *f);
+int32_t mp4ff_set_position(mp4ff_t *f, const int32_t position);
+
+/* mp4atom.c */
+static int32_t mp4ff_atom_get_size(const int8_t *data);
+static int32_t mp4ff_atom_compare(const int8_t a1, const int8_t b1, const int8_t c1, const int8_t d1,
+                                  const int8_t a2, const int8_t b2, const int8_t c2, const int8_t d2);
+static uint8_t mp4ff_atom_name_to_type(const int8_t a, const int8_t b, const int8_t c, const int8_t d);
+int32_t mp4ff_atom_read_header(mp4ff_t *f, uint8_t *atom_type);
+static int32_t mp4ff_read_stsz(mp4ff_t *f);
+static int32_t mp4ff_read_esds(mp4ff_t *f);
+static int32_t mp4ff_read_mp4a(mp4ff_t *f);
+static int32_t mp4ff_read_stsd(mp4ff_t *f);
+static int32_t mp4ff_read_stsc(mp4ff_t *f);
+static int32_t mp4ff_read_stco(mp4ff_t *f);
+static int32_t mp4ff_read_stts(mp4ff_t *f);
+static int32_t mp4ff_read_meta(mp4ff_t *f, const int32_t size);
+int32_t mp4ff_atom_read(mp4ff_t *f, const int32_t size, const uint8_t atom_type);
+
+/* mp4sample.c */
+static int32_t mp4ff_chunk_of_sample(const mp4ff_t *f, const int32_t track, const int32_t sample,
+                                     int32_t *chunk_sample, int32_t *chunk);
+static int32_t mp4ff_chunk_to_offset(const mp4ff_t *f, const int32_t track, const int32_t chunk);
+static int32_t mp4ff_sample_range_size(const mp4ff_t *f, const int32_t track,
+                                       const int32_t chunk_sample, const int32_t sample);
+static int32_t mp4ff_sample_to_offset(const mp4ff_t *f, const int32_t track, const int32_t sample);
+int32_t mp4ff_audio_frame_size(const mp4ff_t *f, const int32_t track, const int32_t sample);
+int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
+
+/* mp4meta.c */
+static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value);
+static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value);
+static int32_t GenreToString(char** GenreStr, const uint16_t genre);
+static int32_t StringToGenre(const char* GenreStr);
+static int32_t TrackToString(char** str, const uint16_t track, const uint16_t totalTracks);
+static int32_t StringToTrack(const char *str, uint16_t *track, uint16_t *totalTracks);
+static int32_t TempoToString(char** str, const uint16_t tempo);
+static int32_t mp4ff_set_metadata_name(mp4ff_t *f, const uint8_t atom_type, char **name);
+static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size);
+static int32_t mp4ff_meta_find_by_name(const mp4ff_t *f, const char *item, char **value);
+int32_t mp4ff_parse_metadata(mp4ff_t *f, const int32_t size);
+int32_t mp4ff_tag_delete(mp4ff_metadata_t *tags);
+int32_t mp4ff_meta_get_num_items(const mp4ff_t *f);
+int32_t mp4ff_meta_get_by_index(const mp4ff_t *f, uint32_t index,
+                            char **item, char **value);
+int32_t mp4ff_meta_get_title(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_artist(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_writer(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_album(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_date(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_tool(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_comment(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_genre(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_track(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_disc(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_compilation(const mp4ff_t *f, char **value);
+int32_t mp4ff_meta_get_tempo(const mp4ff_t *f, char **value);
+
+/* mp4ff.c */
+mp4ff_t *mp4ff_open_read(mp4ff_callback_t *f);
+void mp4ff_close(mp4ff_t *ff);
+static void mp4ff_track_add(mp4ff_t *f);
+static int32_t parse_sub_atoms(mp4ff_t *f, const int32_t total_size);
+static int32_t parse_atoms(mp4ff_t *f);
+int32_t mp4ff_get_sample_duration(const mp4ff_t *f, const int32_t track, const int32_t sample);
+int32_t mp4ff_read_sample(mp4ff_t *f, const int32_t track, const int32_t sample,
+                          uint8_t **audio_buffer,  uint32_t *bytes);
+int32_t mp4ff_get_decoder_config(const mp4ff_t *f, const int32_t track,
+                                 uint8_t** ppBuf, uint32_t* pBufSize);
+int32_t mp4ff_total_tracks(const mp4ff_t *f);
+int32_t mp4ff_time_scale(const mp4ff_t *f, const int32_t track);
+int32_t mp4ff_num_samples(const mp4ff_t *f, const int32_t track);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/common/mp4ff/mp4meta.c
@@ -1,0 +1,436 @@
+/*
+** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**
+** Any non-GPL usage of this software or parts of this software is strictly
+** forbidden.
+**
+** Commercial non-GPL licensing of this software is possible.
+** For more info contact Ahead Software through Mpeg4AAClicense@nero.com.
+**
+** $Id: mp4meta.c,v 1.1 2003/11/22 15:38:31 menno Exp $
+**/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "mp4ffint.h"
+
+static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value)
+{
+    void *backup = (void *)tags->tags;
+
+    if (!item || (item && !*item) || !value) return 0;
+
+    tags->tags = (mp4ff_tag_t*)realloc(tags->tags, (tags->count+1) * sizeof(mp4ff_tag_t));
+    if (!tags->tags)
+    {
+        if (backup) free(backup);
+        return 0;
+    } else {
+        int i_len = strlen(item);
+        int v_len = strlen(value);
+
+        tags->tags[tags->count].item = (char *)malloc(i_len+1);
+        tags->tags[tags->count].value = (char *)malloc(v_len+1);
+
+        if (!tags->tags[tags->count].item || !tags->tags[tags->count].value)
+        {
+            if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item);
+            if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value);
+            tags->tags[tags->count].item = NULL;
+            tags->tags[tags->count].value = NULL;
+            return 0;
+        }
+
+        memcpy(tags->tags[tags->count].item, item, i_len);
+        memcpy(tags->tags[tags->count].value, value, v_len);
+        tags->tags[tags->count].item[i_len] = '\0';
+        tags->tags[tags->count].value[v_len] = '\0';
+
+        tags->count++;
+        return 1;
+    }
+}
+
+static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value)
+{
+    unsigned int i;
+
+    if (!item || (item && !*item) || !value) return 0;
+
+    for (i = 0; i < tags->count; i++)
+    {
+        if (!stricmp(tags->tags[i].item, item))
+        {
+            void *backup = (void *)tags->tags[i].value;
+            int v_len = strlen(value);
+
+            tags->tags[i].value = (char *)realloc(tags->tags[i].value, v_len+1);
+            if (!tags->tags[i].value)
+            {
+                if (backup) free(backup);
+                return 0;
+            }
+
+            memcpy(tags->tags[i].value, value, v_len);
+            tags->tags[i].value[v_len] = '\0';
+
+            return 1;
+        }
+    }
+
+    return mp4ff_tag_add_field(tags, item, value);
+}
+
+int32_t mp4ff_tag_delete(mp4ff_metadata_t *tags)
+{
+    uint32_t i;
+
+    for (i = 0; i < tags->count; i++)
+    {
+        if (tags->tags[i].item) free(tags->tags[i].item);
+        if (tags->tags[i].value) free(tags->tags[i].value);
+    }
+
+    if (tags->tags) free(tags->tags);
+
+    tags->tags = NULL;
+    tags->count = 0;
+
+    return 0;
+}
+
+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 int32_t GenreToString(char** GenreStr, const uint16_t genre)
+{
+    if (genre > 0 && genre <= sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList))
+    {
+        *GenreStr = (char*)malloc((strlen(ID3v1GenreList[genre-1])+1)*sizeof(char));
+        memset(*GenreStr, 0, (strlen(ID3v1GenreList[genre-1])+1)*sizeof(char));
+        strcpy(*GenreStr, ID3v1GenreList[genre-1]);
+        return 0;
+    }
+
+    *GenreStr = NULL;
+    return 1;
+}
+
+static int32_t StringToGenre(const char* GenreStr)
+{
+    int32_t i;
+
+    for (i = 0; i < sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList); i++)
+    {
+        if (stricmp(GenreStr, ID3v1GenreList[i]) == 0)
+            return i+1;
+    }
+    return 0;
+}
+
+static int32_t TrackToString(char** str, const uint16_t track, const uint16_t totalTracks)
+{
+    *str = malloc(12);
+    sprintf(*str, "%.5u of %.5u", track, totalTracks);
+
+    return 0;
+}
+
+static int32_t StringToTrack(const char *str, uint16_t *track, uint16_t *totalTracks)
+{
+    sscanf(str, "%.5u of %.5u", &track, &totalTracks);
+
+    return 0;
+}
+
+static int32_t TempoToString(char** str, const uint16_t tempo)
+{
+    *str = malloc(12);
+    sprintf(*str, "%.5u BPM", tempo);
+
+    return 0;
+}
+
+static int32_t mp4ff_set_metadata_name(mp4ff_t *f, const uint8_t atom_type, char **name)
+{
+    static char *tag_names[] = {
+        "unknown", "title", "artist", "writer", "album",
+        "date", "tool", "comment", "genre", "track",
+        "disc", "compilation", "genre", "tempo"
+    };
+    uint8_t tag_idx = 0;
+    uint16_t size;
+
+    switch (atom_type)
+    {
+    case ATOM_TITLE: tag_idx = 1; break;
+    case ATOM_ARTIST: tag_idx = 2; break;
+    case ATOM_WRITER: tag_idx = 3; break;
+    case ATOM_ALBUM: tag_idx = 4; break;
+    case ATOM_DATE: tag_idx = 5; break;
+    case ATOM_TOOL: tag_idx = 6; break;
+    case ATOM_COMMENT: tag_idx = 7; break;
+    case ATOM_GENRE1: tag_idx = 8; break;
+    case ATOM_TRACK: tag_idx = 9; break;
+    case ATOM_DISC: tag_idx = 10; break;
+    case ATOM_COMPILATION: tag_idx = 11; break;
+    case ATOM_GENRE2: tag_idx = 12; break;
+    case ATOM_TEMPO: tag_idx = 13; break;
+    default: tag_idx = 0; break;
+    }
+
+    size = strlen(tag_names[tag_idx])+1;
+    *name = malloc(size+1);
+    memset(*name, 0, size+1);
+    memcpy(*name, tag_names[tag_idx], size);
+
+    return 0;
+}
+
+static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size)
+{
+    uint8_t atom_type;
+    int32_t subsize, sumsize = 0;
+    char *name = NULL;
+    char *data = NULL;
+
+    while (sumsize < (size-8))
+    {
+        subsize = mp4ff_atom_read_header(f, &atom_type);
+        if (atom_type == ATOM_DATA)
+        {
+            mp4ff_read_char(f); /* version */
+            mp4ff_read_int24(f); /* flags */
+            mp4ff_read_int32(f); /* reserved */
+            data = malloc(subsize-16+1);
+            mp4ff_read_data(f, data, subsize-16);
+            data[subsize-16] = '\0';
+
+            /* some need special attention */
+            if (parent_atom_type == ATOM_GENRE2 || parent_atom_type == ATOM_TEMPO)
+            {
+                uint16_t val = 0;
+                char *tmp;
+                tmp = data;
+
+                val = (uint16_t)(tmp[1]);
+                val += (uint16_t)(tmp[0]<<8);
+
+                if (data)
+                {
+                    free(data);
+                    data = NULL;
+                }
+                if (parent_atom_type == ATOM_TEMPO)
+                    TempoToString(&data, val);
+                else
+                    GenreToString(&data, val);
+            } else if (parent_atom_type == ATOM_TRACK || parent_atom_type == ATOM_DISC) {
+                uint16_t val1 = 0, val2 = 0;
+                char *tmp;
+                tmp = data;
+
+                val1 = (uint16_t)(tmp[3]);
+                val1 += (uint16_t)(tmp[2]<<8);
+                val2 = (uint16_t)(tmp[5]);
+                val2 += (uint16_t)(tmp[4]<<8);
+
+                if (data)
+                {
+                    free(data);
+                    data = NULL;
+                }
+                TrackToString(&data, val1, val2);
+            }
+        } else if (atom_type == ATOM_NAME) {
+            mp4ff_read_char(f); /* version */
+            mp4ff_read_int24(f); /* flags */
+            name = malloc(subsize-12+1);
+            mp4ff_read_data(f, name, subsize-12);
+            name[subsize-12] = '\0';
+        } else {
+            mp4ff_set_position(f, mp4ff_position(f)+subsize-8);
+        }
+        sumsize += subsize;
+    }
+
+    if (name == NULL)
+    {
+        mp4ff_set_metadata_name(f, parent_atom_type, &name);
+    }
+
+    mp4ff_tag_set_field(&(f->tags), (const char*)name, (const char*)data);
+
+    return 0;
+}
+
+int32_t mp4ff_parse_metadata(mp4ff_t *f, const int32_t size)
+{
+    int32_t subsize, sumsize = 0;
+    uint8_t atom_type;
+
+    while (sumsize < (size-12))
+    {
+        subsize = mp4ff_atom_read_header(f, &atom_type);
+        mp4ff_parse_tag(f, atom_type, subsize);
+        sumsize += subsize;
+    }
+
+    return 0;
+}
+
+/* find a metadata item by name */
+/* returns 0 if item found, 1 if no such item */
+static int32_t mp4ff_meta_find_by_name(const mp4ff_t *f, const char *item, char **value)
+{
+    uint32_t i;
+
+    for (i = 0; i < f->tags.count; i++)
+    {
+        if (!stricmp(f->tags.tags[i].item, item))
+        {
+            int v_len = strlen(f->tags.tags[i].value);
+
+            *value = (char*)malloc(v_len + 1);
+            memcpy(*value, f->tags.tags[i].value, v_len);
+            (*value)[v_len] = '\0';
+
+            return 0;
+        }
+    }
+
+    *value = NULL;
+
+    /* not found */
+    return 1;
+}
+
+int32_t mp4ff_meta_get_num_items(const mp4ff_t *f)
+{
+    return f->tags.count;
+}
+
+int32_t mp4ff_meta_get_by_index(const mp4ff_t *f, uint32_t index,
+                                char **item, char **value)
+{
+    if (index >= f->tags.count)
+    {
+        *item = NULL;
+        *value = NULL;
+        return 1;
+    } else {
+        int i_len = strlen(f->tags.tags[index].item);
+        int v_len = strlen(f->tags.tags[index].value);
+
+        *item = (char*)malloc(i_len + 1);
+        memcpy(*item, f->tags.tags[index].item, i_len);
+        (*item)[i_len] = '\0';
+        *value = (char*)malloc(v_len + 1);
+        memcpy(*value, f->tags.tags[index].value, v_len);
+        (*value)[v_len] = '\0';
+    }
+
+    return 0;
+}
+
+int32_t mp4ff_meta_get_title(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "title", value);
+}
+
+int32_t mp4ff_meta_get_artist(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "artist", value);
+}
+
+int32_t mp4ff_meta_get_writer(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "writer", value);
+}
+
+int32_t mp4ff_meta_get_album(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "album", value);
+}
+
+int32_t mp4ff_meta_get_date(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "date", value);
+}
+
+int32_t mp4ff_meta_get_tool(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "tool", value);
+}
+
+int32_t mp4ff_meta_get_comment(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "comment", value);
+}
+
+int32_t mp4ff_meta_get_genre(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "genre", value);
+}
+
+int32_t mp4ff_meta_get_track(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "track", value);
+}
+
+int32_t mp4ff_meta_get_disc(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "disc", value);
+}
+
+int32_t mp4ff_meta_get_compilation(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "compilation", value);
+}
+
+int32_t mp4ff_meta_get_tempo(const mp4ff_t *f, char **value)
+{
+    return mp4ff_meta_find_by_name(f, "tempo", value);
+}