From cbb23cdbd8ad4a17340d453e750cdbb7be324664 Mon Sep 17 00:00:00 2001 From: nu774 Date: Fri, 15 Feb 2013 22:26:17 +0900 Subject: [PATCH] add --tag-from-json --- MSVC/fdkaac.vcxproj | 4 + Makefile.am | 2 + README | 50 ++++ src/main.c | 221 +++------------ src/metadata.c | 378 ++++++++++++++++++++++++++ src/metadata.h | 28 ++ src/parson.c | 647 ++++++++++++++++++++++++++++++++++++++++++++ src/parson.h | 100 +++++++ version.h | 2 +- 9 files changed, 1241 insertions(+), 191 deletions(-) create mode 100644 src/metadata.c create mode 100644 src/metadata.h create mode 100644 src/parson.c create mode 100644 src/parson.h diff --git a/MSVC/fdkaac.vcxproj b/MSVC/fdkaac.vcxproj index 71ec9b6..a2d139d 100644 --- a/MSVC/fdkaac.vcxproj +++ b/MSVC/fdkaac.vcxproj @@ -100,6 +100,8 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ + + @@ -110,6 +112,8 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ + + diff --git a/Makefile.am b/Makefile.am index b69e467..62d3c0c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,8 @@ fdkaac_SOURCES = \ src/lpcm.c \ src/m4af.c \ src/main.c \ + src/metadata.c \ + src/parson.c \ src/progress.c \ src/wav_reader.c diff --git a/README b/README index 007af3a..4bd55b3 100644 --- a/README +++ b/README @@ -50,3 +50,53 @@ Therefore, only use UTF-8 (without BOM) when setting text tags by this option. On the other hand, --tag / --long-tag (and other command line arguments) are converted from locale character encoding to UTF-8 on Posix environment. On Windows, command line arguments are always treated as Unicode. + +Tagging using JSON +------------------ +With --tag-from-json, fdkaac can read JSON file and set tags from it. +By default, tags are assumed to be in the root object(dictionary) like this: + +{ + "title": "No Expectations", + "artist": "The Rolling Stones", + "album": "Beggars Banquet", + "track": 2 +} + +In this case, you can simply specify the filename like: +--tag-from-json=/path/to/json + +If the object containing tags is placed somewhere else, you can optionally +specify the path of the object with dotted notation. + +{ + "format" : { + "filename" : "Middle Curse.flac", + "nb_streams" : 1, + "format_name" : "flac", + "format_long_name" : "raw FLAC", + "start_time" : "N/A", + "duration" : "216.146667", + "size" : "11851007.000000", + "bit_rate" : "438628.000000", + "tags" : { + "ALBUM" : "Scary World Theory", + "ARTIST" : "Lali Puna", + "DATE" : "2001", + "DISCID" : "9208CC0A", + "TITLE" : "Middle Curse", + "TRACKTOTAL" : "10", + "track" : "2" + } + } +} + +In this example, tags are placed under the object "format.tags". +("format" is a child of the root, and "tags" is a child of the "format"). +In this case, you can say: +--tag-from-json=/path/to/json?format.tags + +For your information, ffprobe of ffmpeg project (or avprobe of libav) can +output media information/metadata in json format like this. + +Note that not all tags can be read/written this way. diff --git a/src/main.c b/src/main.c index 8649b0a..59f2b97 100644 --- a/src/main.c +++ b/src/main.c @@ -38,6 +38,7 @@ #include "m4af.h" #include "progress.h" #include "version.h" +#include "metadata.h" #define PROGNAME "fdkaac" @@ -181,17 +182,18 @@ PROGNAME " %s\n" " --tag-from-file :\n" " Same as above, but value is read from file.\n" " --long-tag : Set arbitrary tag as iTunes custom metadata.\n" +" --tag-from-json \n" +" Read tags from JSON. By default, tags are\n" +" assumed to be direct children of the root\n" +" object(dictionary).\n" +" Optionally, position of the dictionary\n" +" that contains tags can be specified with\n" +" dotted notation.\n" +" Example:\n" +" --tag-from-json /path/to/json?format.tags\n" , fdkaac_version); } -typedef struct aacenc_tag_entry_t { - uint32_t tag; - const char *name; - const char *data; - uint32_t data_size; - int is_file_name; -} aacenc_tag_entry_t; - typedef struct aacenc_param_ex_t { AACENC_PARAMS @@ -205,33 +207,10 @@ typedef struct aacenc_param_ex_t { unsigned raw_rate; const char *raw_format; - aacenc_tag_entry_t *tag_table; - unsigned tag_count; - unsigned tag_table_capacity; -} aacenc_param_ex_t; + aacenc_tag_param_t tags; -static -void param_add_itmf_entry(aacenc_param_ex_t *params, uint32_t tag, - const char *key, const char *value, uint32_t size, - int is_file_name) -{ - aacenc_tag_entry_t *entry; - if (params->tag_count == params->tag_table_capacity) { - unsigned newsize = params->tag_table_capacity; - newsize = newsize ? newsize * 2 : 1; - params->tag_table = - realloc(params->tag_table, newsize * sizeof(aacenc_tag_entry_t)); - params->tag_table_capacity = newsize; - } - entry = params->tag_table + params->tag_count; - entry->tag = tag; - if (tag == M4AF_FOURCC('-','-','-','-')) - entry->name = key; - entry->data = value; - entry->data_size = size; - entry->is_file_name = is_file_name; - params->tag_count++; -} + char *json_filename; +} aacenc_param_ex_t; static int parse_options(int argc, char **argv, aacenc_param_ex_t *params) @@ -245,6 +224,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) #define OPT_SHORT_TAG M4AF_FOURCC('s','t','a','g') #define OPT_SHORT_TAG_FILE M4AF_FOURCC('s','t','g','f') #define OPT_LONG_TAG M4AF_FOURCC('l','t','a','g') +#define OPT_TAG_FROM_JSON M4AF_FOURCC('t','f','j','s') static struct option long_options[] = { { "help", no_argument, 0, 'h' }, @@ -282,6 +262,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) { "tag", required_argument, 0, OPT_SHORT_TAG }, { "tag-from-file", required_argument, 0, OPT_SHORT_TAG_FILE }, { "long-tag", required_argument, 0, OPT_LONG_TAG }, + { "tag-from-json", required_argument, 0, OPT_TAG_FROM_JSON }, { 0, 0, 0, 0 }, }; params->afterburner = 1; @@ -395,7 +376,8 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) case M4AF_TAG_TRACK: case M4AF_TAG_DISK: case M4AF_TAG_TEMPO: - param_add_itmf_entry(params, ch, 0, optarg, strlen(optarg), 0); + aacenc_param_add_itmf_entry(¶ms->tags, ch, 0, optarg, + strlen(optarg), 0); break; case OPT_SHORT_TAG: case OPT_SHORT_TAG_FILE: @@ -428,10 +410,14 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) for (; *optarg; ++optarg) fcc = ((fcc << 8) | (*optarg & 0xff)); } - param_add_itmf_entry(params, fcc, optarg, val, strlen(val), - ch == OPT_SHORT_TAG_FILE); + aacenc_param_add_itmf_entry(¶ms->tags, fcc, optarg, + val, strlen(val), + ch == OPT_SHORT_TAG_FILE); } break; + case OPT_TAG_FROM_JSON: + params->json_filename = optarg; + break; default: return usage(), -1; } @@ -538,155 +524,6 @@ END: return rc; } -static -char *load_tag_from_file(const char *path, uint32_t *data_size) -{ - FILE *fp = 0; - char *data = 0; - int64_t size; - - if ((fp = aacenc_fopen(path, "rb")) == NULL) { - aacenc_fprintf(stderr, "WARNING: %s: %s\n", path, strerror(errno)); - goto END; - } - fseeko(fp, 0, SEEK_END); - size = ftello(fp); - if (size > 5*1024*1024) { - aacenc_fprintf(stderr, "WARNING: %s: size too large\n", path); - goto END; - } - fseeko(fp, 0, SEEK_SET); - data = malloc(size + 1); - if (data) fread(data, 1, size, fp); - data[size] = 0; - *data_size = (uint32_t)size; -END: - if (fp) fclose(fp); - return data; -} - -static -void put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag) -{ - unsigned m, n = 0; - const char *data = tag->data; - uint32_t data_size = tag->data_size; - char *file_contents = 0; - - if (tag->is_file_name) { - data = file_contents = load_tag_from_file(tag->data, &data_size); - if (!data) return; - } - switch (tag->tag) { - case M4AF_TAG_TRACK: - if (sscanf(data, "%u/%u", &m, &n) >= 1) - m4af_add_itmf_track_tag(m4af, m, n); - break; - case M4AF_TAG_DISK: - if (sscanf(data, "%u/%u", &m, &n) >= 1) - m4af_add_itmf_disk_tag(m4af, m, n); - break; - case M4AF_TAG_GENRE_ID3: - if (sscanf(data, "%u", &n) == 1) - m4af_add_itmf_genre_tag(m4af, n); - break; - case M4AF_TAG_TEMPO: - if (sscanf(data, "%u", &n) == 1) - m4af_add_itmf_int16_tag(m4af, tag->tag, n); - break; - case M4AF_TAG_COMPILATION: - case M4AF_FOURCC('a','k','I','D'): - case M4AF_FOURCC('h','d','v','d'): - case M4AF_FOURCC('p','c','s','t'): - case M4AF_FOURCC('p','g','a','p'): - case M4AF_FOURCC('r','t','n','g'): - case M4AF_FOURCC('s','t','i','k'): - if (sscanf(data, "%u", &n) == 1) - m4af_add_itmf_int8_tag(m4af, tag->tag, n); - break; - case M4AF_FOURCC('a','t','I','D'): - case M4AF_FOURCC('c','m','I','D'): - case M4AF_FOURCC('c','n','I','D'): - case M4AF_FOURCC('g','e','I','D'): - case M4AF_FOURCC('s','f','I','D'): - case M4AF_FOURCC('t','v','s','n'): - case M4AF_FOURCC('t','v','s','s'): - if (sscanf(data, "%u", &n) == 1) - m4af_add_itmf_int32_tag(m4af, tag->tag, n); - break; - case M4AF_FOURCC('p','l','I','D'): - { - int64_t qn; - if (sscanf(data, "%" SCNd64, &qn) == 1) - m4af_add_itmf_int64_tag(m4af, tag->tag, qn); - break; - } - case M4AF_TAG_ARTWORK: - { - int data_type = 0; - if (!memcmp(data, "GIF", 3)) - data_type = M4AF_GIF; - else if (!memcmp(data, "\xff\xd8\xff", 3)) - data_type = M4AF_JPEG; - else if (!memcmp(data, "\x89PNG", 4)) - data_type = M4AF_PNG; - if (data_type) - m4af_add_itmf_short_tag(m4af, tag->tag, data_type, - data, data_size); - break; - } - case M4AF_FOURCC('-','-','-','-'): - { - char *u8 = aacenc_to_utf8(data); - m4af_add_itmf_long_tag(m4af, tag->name, u8); - free(u8); - break; - } - case M4AF_TAG_TITLE: - case M4AF_TAG_ARTIST: - case M4AF_TAG_ALBUM: - case M4AF_TAG_GENRE: - case M4AF_TAG_DATE: - case M4AF_TAG_COMPOSER: - case M4AF_TAG_GROUPING: - case M4AF_TAG_COMMENT: - case M4AF_TAG_LYRICS: - case M4AF_TAG_TOOL: - case M4AF_TAG_ALBUM_ARTIST: - case M4AF_TAG_DESCRIPTION: - case M4AF_TAG_LONG_DESCRIPTION: - case M4AF_TAG_COPYRIGHT: - case M4AF_FOURCC('a','p','I','D'): - case M4AF_FOURCC('c','a','t','g'): - case M4AF_FOURCC('k','e','y','w'): - case M4AF_FOURCC('p','u','r','d'): - case M4AF_FOURCC('p','u','r','l'): - case M4AF_FOURCC('s','o','a','a'): - case M4AF_FOURCC('s','o','a','l'): - case M4AF_FOURCC('s','o','a','r'): - case M4AF_FOURCC('s','o','c','o'): - case M4AF_FOURCC('s','o','n','m'): - case M4AF_FOURCC('s','o','s','n'): - case M4AF_FOURCC('t','v','e','n'): - case M4AF_FOURCC('t','v','n','n'): - case M4AF_FOURCC('t','v','s','h'): - case M4AF_FOURCC('x','i','d',' '): - case M4AF_FOURCC('\xa9','e','n','c'): - case M4AF_FOURCC('\xa9','s','t','3'): - { - char *u8 = aacenc_to_utf8(data); - m4af_add_itmf_string_tag(m4af, tag->tag, u8); - free(u8); - break; - } - default: - fprintf(stderr, "WARNING: unknown/unsupported tag: %c%c%c%c\n", - tag->tag >> 24, (tag->tag >> 16) & 0xff, - (tag->tag >> 8) & 0xff, tag->tag & 0xff); - } - if (file_contents) free(file_contents); -} - static void put_tool_tag(m4af_ctx_t *m4af, const aacenc_param_ex_t *params, HANDLE_AACENCODER encoder) @@ -720,9 +557,13 @@ int finalize_m4a(m4af_ctx_t *m4af, const aacenc_param_ex_t *params, HANDLE_AACENCODER encoder) { unsigned i; - aacenc_tag_entry_t *tag = params->tag_table; - for (i = 0; i < params->tag_count; ++i, ++tag) - put_tag_entry(m4af, tag); + aacenc_tag_entry_t *tag = params->tags.tag_table; + + if (params->json_filename) + aacenc_put_tags_from_json(m4af, params->json_filename); + + for (i = 0; i < params->tags.tag_count; ++i, ++tag) + aacenc_put_tag_entry(m4af, tag); put_tool_tag(m4af, params, encoder); @@ -901,7 +742,7 @@ END: if (ofp) fclose(ofp); if (encoder) aacEncClose(&encoder); if (output_filename) free(output_filename); - if (params.tag_table) free(params.tag_table); + if (params.tags.tag_table) free(params.tags.tag_table); return result; } diff --git a/src/metadata.c b/src/metadata.c new file mode 100644 index 0000000..c84b0f4 --- /dev/null +++ b/src/metadata.c @@ -0,0 +1,378 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif +#if HAVE_STDINT_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#elif defined(_MSC_VER) +# define SCNd64 "I64d" +#endif +#include +#include +#include +#include +#include +#include "m4af.h" +#include "metadata.h" +#include "compat.h" +#include "parson.h" + +typedef struct tag_key_mapping_t { + const char *name; + uint32_t fcc; +} tag_key_mapping_t; + +enum { + TAG_TOTAL_DISCS = 1, + TAG_TOTAL_TRACKS = 2 +}; + +static tag_key_mapping_t tag_mapping_table[] = { + { "album", M4AF_TAG_ALBUM }, + { "albumartist", M4AF_TAG_ALBUM_ARTIST }, + { "albumartistsort", M4AF_FOURCC('s','o','a','a') }, + { "albumartistsortorder", M4AF_FOURCC('s','o','a','a') }, + { "albumsort", M4AF_FOURCC('s','o','a','l') }, + { "albumsortorder", M4AF_FOURCC('s','o','a','l') }, + { "artist", M4AF_TAG_ARTIST }, + { "artistsort", M4AF_FOURCC('s','o','a','r') }, + { "artistsortorder", M4AF_FOURCC('s','o','a','r') }, + { "band", M4AF_TAG_ALBUM_ARTIST }, + { "bpm", M4AF_TAG_TEMPO }, + { "comment", M4AF_TAG_COMMENT }, + { "compilation", M4AF_TAG_COMPILATION }, + { "composer", M4AF_TAG_COMPOSER }, + { "composersort", M4AF_FOURCC('s','o','c','o') }, + { "composersortorder", M4AF_FOURCC('s','o','c','o') }, + { "contentgroup", M4AF_TAG_GROUPING }, + { "copyright", M4AF_TAG_COPYRIGHT }, + { "date", M4AF_TAG_DATE }, + { "disc", M4AF_TAG_DISK }, + { "disctotal", TAG_TOTAL_DISCS }, + { "discnumber", M4AF_TAG_DISK }, + { "genre", M4AF_TAG_GENRE }, + { "grouping", M4AF_TAG_GROUPING }, + { "itunescompilation", M4AF_TAG_COMPILATION }, + { "lyrics", M4AF_TAG_LYRICS }, + { "title", M4AF_TAG_TITLE }, + { "titlesort", M4AF_FOURCC('s','o','n','m') }, + { "titlesortorder", M4AF_FOURCC('s','o','n','m') }, + { "totaldiscs", TAG_TOTAL_DISCS }, + { "totaltracks", TAG_TOTAL_TRACKS }, + { "track", M4AF_TAG_TRACK }, + { "tracknumber", M4AF_TAG_TRACK }, + { "tracktotal", TAG_TOTAL_TRACKS }, + { "unsyncedlyrics", M4AF_TAG_LYRICS }, + { "year", M4AF_TAG_DATE }, +}; + +static +int tag_key_comparator(const void *k, const void *v) +{ + return strcmp((const char *)k, ((tag_key_mapping_t*)v)->name); +} + +static +uint32_t get_tag_fcc_from_name(const char *name) +{ + char *name_p = 0, *p; + const tag_key_mapping_t *ent; + + name_p = malloc(strlen(name) + 1); + for (p = name_p; *name; ++name) { + unsigned char c = *name; + if (c != ' ' && c != '-' && c != '_') + *p++ = tolower(c); + } + *p = 0; + ent = bsearch(name_p, tag_mapping_table, + sizeof(tag_mapping_table) / sizeof(tag_mapping_table[0]), + sizeof(tag_mapping_table[0]), + tag_key_comparator); + free(name_p); + return ent ? ent->fcc : 0; +} + +char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size) +{ + FILE *fp = 0; + char *data = 0; + int64_t size; + + if ((fp = aacenc_fopen(path, "rb")) == NULL) { + aacenc_fprintf(stderr, "WARNING: %s: %s\n", path, strerror(errno)); + goto END; + } + fseeko(fp, 0, SEEK_END); + size = ftello(fp); + if (size > 5*1024*1024) { + aacenc_fprintf(stderr, "WARNING: %s: size too large\n", path); + goto END; + } + fseeko(fp, 0, SEEK_SET); + data = malloc(size + 1); + if (data) fread(data, 1, size, fp); + data[size] = 0; + *data_size = (uint32_t)size; +END: + if (fp) fclose(fp); + return data; +} + +void aacenc_param_add_itmf_entry(aacenc_tag_param_t *params, uint32_t tag, + const char *key, const char *value, + uint32_t size, int is_file_name) +{ + aacenc_tag_entry_t *entry; + + if (!is_file_name && !size) + return; + if (params->tag_count == params->tag_table_capacity) { + unsigned newsize = params->tag_table_capacity; + newsize = newsize ? newsize * 2 : 1; + params->tag_table = + realloc(params->tag_table, newsize * sizeof(aacenc_tag_entry_t)); + params->tag_table_capacity = newsize; + } + entry = params->tag_table + params->tag_count; + entry->tag = tag; + if (tag == M4AF_FOURCC('-','-','-','-')) + entry->name = key; + entry->data = value; + entry->data_size = size; + entry->is_file_name = is_file_name; + params->tag_count++; +} + +static +void tag_put_number_pair(m4af_ctx_t *m4af, uint32_t fcc, + const char *snumber, const char *stotal) +{ + unsigned number = 0, total = 0; + char buf[128]; + aacenc_tag_entry_t entry = { 0 }; + + if (snumber) sscanf(snumber, "%u", &number); + if (stotal) sscanf(stotal, "%u", &total); + if (number) { + if (total) sprintf(buf, "%u/%u", number, total); + else sprintf(buf, "%u", number); + entry.tag = fcc; + entry.data = buf; + entry.data_size = strlen(buf); + aacenc_put_tag_entry(m4af, &entry); + } +} + +void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename) +{ + char *data = 0; + JSON_Value *json = 0; + JSON_Object *root; + size_t i, nelts; + uint32_t data_size; + char *json_dot_path; + char *filename = 0; + char *disc = 0; + char *track = 0; + char *total_discs = 0; + char *total_tracks = 0; + aacenc_tag_entry_t entry = { 0 }; + + filename = strdup(json_filename); + if ((json_dot_path = strchr(filename, '?')) != 0) + *json_dot_path++ = '\0'; + + if (!(data = aacenc_load_tag_from_file(filename, &data_size))) + goto DONE; + if (!(json = json_parse_string(data))) { + aacenc_fprintf(stderr, "WARNING: failed to parse JSON\n"); + goto DONE; + } + root = json_value_get_object(json); + if (json_dot_path) { + if (!(root = json_object_dotget_object(root, json_dot_path))) { + aacenc_fprintf(stderr, "WARNING: %s not found in JSON\n", + json_dot_path); + goto DONE; + } + } + nelts = json_object_get_count(root); + for (i = 0; i < nelts; ++i) { + char buf[256]; + const char *key = 0; + const char *val = 0; + uint32_t fcc = 0; + JSON_Value_Type type; + + key = json_object_get_name(root, i); + type = json_value_get_type(json_object_get_value(root, key)); + if (type == JSONString) + val = json_object_get_string(root, key); + else if (type == JSONNumber) { + double num = json_object_get_number(root, key); + sprintf(buf, "%g", num); + val = buf; + } else if (type == JSONBoolean) { + int n = json_object_get_boolean(root, key); + sprintf(buf, "%d", n); + val = buf; + } + fcc = get_tag_fcc_from_name(key); + if (!val || !fcc) + continue; + + switch (fcc) { + case TAG_TOTAL_DISCS: + total_discs = strdup(val); break; + case TAG_TOTAL_TRACKS: + total_tracks = strdup(val); break; + case M4AF_TAG_DISK: + disc = strdup(val); break; + case M4AF_TAG_TRACK: + track = strdup(val); break; + default: + { + entry.tag = fcc; + entry.data = val; + entry.data_size = strlen(val); + aacenc_put_tag_entry(m4af, &entry); + } + } + } + tag_put_number_pair(m4af, M4AF_TAG_TRACK, track, total_tracks); + tag_put_number_pair(m4af, M4AF_TAG_DISK, disc, total_discs); +DONE: + if (track) free(track); + if (disc) free(disc); + if (total_tracks) free(total_tracks); + if (total_discs) free(total_discs); + if (data) free(data); + if (filename) free(filename); + if (json) json_value_free(json); +} + +void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag) +{ + unsigned m, n = 0; + const char *data = tag->data; + uint32_t data_size = tag->data_size; + char *file_contents = 0; + + if (tag->is_file_name) { + data = file_contents = aacenc_load_tag_from_file(tag->data, &data_size); + if (!data) return; + } + switch (tag->tag) { + case M4AF_TAG_TRACK: + if (sscanf(data, "%u/%u", &m, &n) >= 1) + m4af_add_itmf_track_tag(m4af, m, n); + break; + case M4AF_TAG_DISK: + if (sscanf(data, "%u/%u", &m, &n) >= 1) + m4af_add_itmf_disk_tag(m4af, m, n); + break; + case M4AF_TAG_GENRE_ID3: + if (sscanf(data, "%u", &n) == 1) + m4af_add_itmf_genre_tag(m4af, n); + break; + case M4AF_TAG_TEMPO: + if (sscanf(data, "%u", &n) == 1) + m4af_add_itmf_int16_tag(m4af, tag->tag, n); + break; + case M4AF_TAG_COMPILATION: + case M4AF_FOURCC('a','k','I','D'): + case M4AF_FOURCC('h','d','v','d'): + case M4AF_FOURCC('p','c','s','t'): + case M4AF_FOURCC('p','g','a','p'): + case M4AF_FOURCC('r','t','n','g'): + case M4AF_FOURCC('s','t','i','k'): + if (sscanf(data, "%u", &n) == 1) + m4af_add_itmf_int8_tag(m4af, tag->tag, n); + break; + case M4AF_FOURCC('a','t','I','D'): + case M4AF_FOURCC('c','m','I','D'): + case M4AF_FOURCC('c','n','I','D'): + case M4AF_FOURCC('g','e','I','D'): + case M4AF_FOURCC('s','f','I','D'): + case M4AF_FOURCC('t','v','s','n'): + case M4AF_FOURCC('t','v','s','s'): + if (sscanf(data, "%u", &n) == 1) + m4af_add_itmf_int32_tag(m4af, tag->tag, n); + break; + case M4AF_FOURCC('p','l','I','D'): + { + int64_t qn; + if (sscanf(data, "%" SCNd64, &qn) == 1) + m4af_add_itmf_int64_tag(m4af, tag->tag, qn); + break; + } + case M4AF_TAG_ARTWORK: + { + int data_type = 0; + if (!memcmp(data, "GIF", 3)) + data_type = M4AF_GIF; + else if (!memcmp(data, "\xff\xd8\xff", 3)) + data_type = M4AF_JPEG; + else if (!memcmp(data, "\x89PNG", 4)) + data_type = M4AF_PNG; + if (data_type) + m4af_add_itmf_short_tag(m4af, tag->tag, data_type, + data, data_size); + break; + } + case M4AF_FOURCC('-','-','-','-'): + { + char *u8 = aacenc_to_utf8(data); + m4af_add_itmf_long_tag(m4af, tag->name, u8); + free(u8); + break; + } + case M4AF_TAG_TITLE: + case M4AF_TAG_ARTIST: + case M4AF_TAG_ALBUM: + case M4AF_TAG_GENRE: + case M4AF_TAG_DATE: + case M4AF_TAG_COMPOSER: + case M4AF_TAG_GROUPING: + case M4AF_TAG_COMMENT: + case M4AF_TAG_LYRICS: + case M4AF_TAG_TOOL: + case M4AF_TAG_ALBUM_ARTIST: + case M4AF_TAG_DESCRIPTION: + case M4AF_TAG_LONG_DESCRIPTION: + case M4AF_TAG_COPYRIGHT: + case M4AF_FOURCC('a','p','I','D'): + case M4AF_FOURCC('c','a','t','g'): + case M4AF_FOURCC('k','e','y','w'): + case M4AF_FOURCC('p','u','r','d'): + case M4AF_FOURCC('p','u','r','l'): + case M4AF_FOURCC('s','o','a','a'): + case M4AF_FOURCC('s','o','a','l'): + case M4AF_FOURCC('s','o','a','r'): + case M4AF_FOURCC('s','o','c','o'): + case M4AF_FOURCC('s','o','n','m'): + case M4AF_FOURCC('s','o','s','n'): + case M4AF_FOURCC('t','v','e','n'): + case M4AF_FOURCC('t','v','n','n'): + case M4AF_FOURCC('t','v','s','h'): + case M4AF_FOURCC('x','i','d',' '): + case M4AF_FOURCC('\xa9','e','n','c'): + case M4AF_FOURCC('\xa9','s','t','3'): + { + char *u8 = aacenc_to_utf8(data); + m4af_add_itmf_string_tag(m4af, tag->tag, u8); + free(u8); + break; + } + default: + fprintf(stderr, "WARNING: unknown/unsupported tag: %c%c%c%c\n", + tag->tag >> 24, (tag->tag >> 16) & 0xff, + (tag->tag >> 8) & 0xff, tag->tag & 0xff); + } + if (file_contents) free(file_contents); +} + + diff --git a/src/metadata.h b/src/metadata.h new file mode 100644 index 0000000..66a8762 --- /dev/null +++ b/src/metadata.h @@ -0,0 +1,28 @@ +#ifndef METADATA_H +#define METADATA_H + +typedef struct aacenc_tag_entry_t { + uint32_t tag; + const char *name; + const char *data; + uint32_t data_size; + int is_file_name; +} aacenc_tag_entry_t; + +typedef struct aacenc_tag_param_t { + aacenc_tag_entry_t *tag_table; + unsigned tag_count; + unsigned tag_table_capacity; +} aacenc_tag_param_t; + +char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size); + +void aacenc_param_add_itmf_entry(aacenc_tag_param_t *params, uint32_t tag, + const char *key, const char *value, + uint32_t size, int is_file_name); + +void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename); + +void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag); + +#endif diff --git a/src/parson.c b/src/parson.c new file mode 100644 index 0000000..6e01a87 --- /dev/null +++ b/src/parson.c @@ -0,0 +1,647 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "parson.h" + +#include +#include +#include +#include + +#define ERROR 0 +#define SUCCESS 1 +#define STARTING_CAPACITY 15 +#define ARRAY_MAX_CAPACITY 122880 /* 15*(2^13) */ +#define OBJECT_MAX_CAPACITY 960 /* 15*(2^6) */ +#define MAX_NESTING 19 +#define sizeof_token(a) (sizeof(a) - 1) +#define skip_char(str) ((*str)++) +#define skip_whitespaces(str) while (isspace(**string)) { skip_char(string); } +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define parson_malloc(a) malloc(a) +#define parson_free(a) free((void*)a) +#define parson_realloc(a, b) realloc(a, b) + +/* Type definitions */ +typedef union json_value_value { + const char *string; + double number; + JSON_Object *object; + JSON_Array *array; + int boolean; + int null; +} JSON_Value_Value; + +struct json_value_t { + JSON_Value_Type type; + JSON_Value_Value value; +}; + +struct json_object_t { + const char **names; + JSON_Value **values; + size_t count; + size_t capacity; +}; + +struct json_array_t { + JSON_Value **items; + size_t count; + size_t capacity; +}; + +/* Various */ +static int try_realloc(void **ptr, size_t new_size); +static char * parson_strndup(const char *string, size_t n); +static int is_utf(const unsigned char *string); +static int is_decimal(const char *string, size_t length); + +/* JSON Object */ +static JSON_Object * json_object_init(void); +static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value); +static int json_object_resize(JSON_Object *object, size_t capacity); +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); +static void json_object_free(JSON_Object *object); + +/* JSON Array */ +static JSON_Array * json_array_init(void); +static int json_array_add(JSON_Array *array, JSON_Value *value); +static int json_array_resize(JSON_Array *array, size_t capacity); +static void json_array_free(JSON_Array *array); + +/* JSON Value */ +static JSON_Value * json_value_init_object(void); +static JSON_Value * json_value_init_array(void); +static JSON_Value * json_value_init_string(const char *string); +static JSON_Value * json_value_init_number(double number); +static JSON_Value * json_value_init_boolean(int boolean); +static JSON_Value * json_value_init_null(void); + +/* Parser */ +static void skip_quotes(const char **string); +static const char * get_processed_string(const char **string); +static JSON_Value * parse_object_value(const char **string, size_t nesting); +static JSON_Value * parse_array_value(const char **string, size_t nesting); +static JSON_Value * parse_string_value(const char **string); +static JSON_Value * parse_boolean_value(const char **string); +static JSON_Value * parse_number_value(const char **string); +static JSON_Value * parse_null_value(const char **string); +static JSON_Value * parse_value(const char **string, size_t nesting); + +/* Various */ +static int try_realloc(void **ptr, size_t new_size) { + void *reallocated_ptr = parson_realloc(*ptr, new_size); + if (!reallocated_ptr) { return ERROR; } + *ptr = reallocated_ptr; + return SUCCESS; +} + +static char * parson_strndup(const char *string, size_t n) { + char *output_string = (char*)parson_malloc(n + 1); + if (!output_string) { return NULL; } + output_string[n] = '\0'; + strncpy(output_string, string, n); + return output_string; +} + +static int is_utf(const unsigned char *s) { + return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]); +} + +static int is_decimal(const char *string, size_t length) { + if (length > 1 && string[0] == '0' && string[1] != '.') { return 0; } + if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { return 0; } + while (length--) { if (strchr("xX", string[length])) { return 0; } } + return 1; +} + +/* JSON Object */ +static JSON_Object * json_object_init(void) { + JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); + if (!new_obj) { return NULL; } + new_obj->names = (const char**)NULL; + new_obj->values = (JSON_Value**)NULL; + new_obj->capacity = 0; + new_obj->count = 0; + return new_obj; +} + +static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { + size_t index; + if (object->count >= object->capacity) { + size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY); + if (new_capacity > OBJECT_MAX_CAPACITY) { return ERROR; } + if (json_object_resize(object, new_capacity) == ERROR) { return ERROR; } + } + if (json_object_get_value(object, name) != NULL) { return ERROR; } + index = object->count; + object->names[index] = parson_strndup(name, strlen(name)); + if (!object->names[index]) { return ERROR; } + object->values[index] = value; + object->count++; + return SUCCESS; +} + +static int json_object_resize(JSON_Object *object, size_t capacity) { + if (try_realloc((void**)&object->names, capacity * sizeof(char*)) == ERROR) { return ERROR; } + if (try_realloc((void**)&object->values, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; } + object->capacity = capacity; + return SUCCESS; +} + +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) { + size_t i, name_length; + for (i = 0; i < json_object_get_count(object); i++) { + name_length = strlen(object->names[i]); + if (name_length != n) { continue; } + if (strncmp(object->names[i], name, n) == 0) { return object->values[i]; } + } + return NULL; +} + +static void json_object_free(JSON_Object *object) { + while(object->count--) { + parson_free(object->names[object->count]); + json_value_free(object->values[object->count]); + } + parson_free(object->names); + parson_free(object->values); + parson_free(object); +} + +/* JSON Array */ +static JSON_Array * json_array_init(void) { + JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); + if (!new_array) { return NULL; } + new_array->items = (JSON_Value**)NULL; + new_array->capacity = 0; + new_array->count = 0; + return new_array; +} + +static int json_array_add(JSON_Array *array, JSON_Value *value) { + if (array->count >= array->capacity) { + size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); + if (new_capacity > ARRAY_MAX_CAPACITY) { return ERROR; } + if (!json_array_resize(array, new_capacity)) { return ERROR; } + } + array->items[array->count] = value; + array->count++; + return SUCCESS; +} + +static int json_array_resize(JSON_Array *array, size_t capacity) { + if (try_realloc((void**)&array->items, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; } + array->capacity = capacity; + return SUCCESS; +} + +static void json_array_free(JSON_Array *array) { + while (array->count--) { json_value_free(array->items[array->count]); } + parson_free(array->items); + parson_free(array); +} + +/* JSON Value */ +static JSON_Value * json_value_init_object(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { return NULL; } + new_value->type = JSONObject; + new_value->value.object = json_object_init(); + if (!new_value->value.object) { parson_free(new_value); return NULL; } + return new_value; +} + +static JSON_Value * json_value_init_array(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { return NULL; } + new_value->type = JSONArray; + new_value->value.array = json_array_init(); + if (!new_value->value.array) { parson_free(new_value); return NULL; } + return new_value; +} + +static JSON_Value * json_value_init_string(const char *string) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { return NULL; } + new_value->type = JSONString; + new_value->value.string = string; + return new_value; +} + +static JSON_Value * json_value_init_number(double number) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { return NULL; } + new_value->type = JSONNumber; + new_value->value.number = number; + return new_value; +} + +static JSON_Value * json_value_init_boolean(int boolean) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { return NULL; } + new_value->type = JSONBoolean; + new_value->value.boolean = boolean; + return new_value; +} + +static JSON_Value * json_value_init_null(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { return NULL; } + new_value->type = JSONNull; + return new_value; +} + +/* Parser */ +static void skip_quotes(const char **string) { + skip_char(string); + while (**string != '\"') { + if (**string == '\0') { return; } + if (**string == '\\') { skip_char(string); if (**string == '\0') { return; }} + skip_char(string); + } + skip_char(string); +} + +/* Returns contents of a string inside double quotes and parses escaped + characters inside. + Example: "\u006Corem ipsum" -> lorem ipsum */ +static const char * get_processed_string(const char **string) { + const char *string_start = *string; + char *output, *processed_ptr, *unprocessed_ptr, current_char; + unsigned int utf_val; + skip_quotes(string); + if (**string == '\0') { return NULL; } + output = parson_strndup(string_start + 1, *string - string_start - 2); + if (!output) { return NULL; } + processed_ptr = unprocessed_ptr = output; + while (*unprocessed_ptr) { + current_char = *unprocessed_ptr; + if (current_char == '\\') { + unprocessed_ptr++; + current_char = *unprocessed_ptr; + switch (current_char) { + case '\"': case '\\': case '/': break; + case 'b': current_char = '\b'; break; + case 'f': current_char = '\f'; break; + case 'n': current_char = '\n'; break; + case 'r': current_char = '\r'; break; + case 't': current_char = '\t'; break; + case 'u': + unprocessed_ptr++; + if (!is_utf((const unsigned char*)unprocessed_ptr) || + sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) { + parson_free(output); return NULL; + } + if (utf_val < 0x80) { + current_char = utf_val; + } else if (utf_val < 0x800) { + *processed_ptr++ = (utf_val >> 6) | 0xC0; + current_char = ((utf_val | 0x80) & 0xBF); + } else { + *processed_ptr++ = (utf_val >> 12) | 0xE0; + *processed_ptr++ = (((utf_val >> 6) | 0x80) & 0xBF); + current_char = ((utf_val | 0x80) & 0xBF); + } + unprocessed_ptr += 3; + break; + default: + parson_free(output); + return NULL; + break; + } + } else if ((unsigned char)current_char < 0x20) { /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */ + parson_free(output); + return NULL; + } + *processed_ptr = current_char; + processed_ptr++; + unprocessed_ptr++; + } + *processed_ptr = '\0'; + if (try_realloc((void**)&output, strlen(output) + 1) == ERROR) { return NULL; } + return output; +} + +static JSON_Value * parse_value(const char **string, size_t nesting) { + if (nesting > MAX_NESTING) { return NULL; } + skip_whitespaces(string); + switch (**string) { + case '{': + return parse_object_value(string, nesting + 1); + case '[': + return parse_array_value(string, nesting + 1); + case '\"': + return parse_string_value(string); + case 'f': case 't': + return parse_boolean_value(string); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return parse_number_value(string); + case 'n': + return parse_null_value(string); + default: + return NULL; + } +} + +static JSON_Value * parse_object_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_object(), *new_value = NULL; + JSON_Object *output_object = json_value_get_object(output_value); + const char *new_key = NULL; + if (!output_value) { return NULL; } + skip_char(string); + skip_whitespaces(string); + if (**string == '}') { skip_char(string); return output_value; } /* empty object */ + while (**string != '\0') { + new_key = get_processed_string(string); + skip_whitespaces(string); + if (!new_key || **string != ':') { + json_value_free(output_value); + return NULL; + } + skip_char(string); + new_value = parse_value(string, nesting); + if (!new_value) { + parson_free(new_key); + json_value_free(output_value); + return NULL; + } + if(!json_object_add(output_object, new_key, new_value)) { + parson_free(new_key); + parson_free(new_value); + json_value_free(output_value); + return NULL; + } + parson_free(new_key); + skip_whitespaces(string); + if (**string != ',') { break; } + skip_char(string); + skip_whitespaces(string); + } + skip_whitespaces(string); + if (**string != '}' || /* Trim object after parsing is over */ + json_object_resize(output_object, json_object_get_count(output_object)) == ERROR) { + json_value_free(output_value); + return NULL; + } + skip_char(string); + return output_value; +} + +static JSON_Value * parse_array_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL; + JSON_Array *output_array = json_value_get_array(output_value); + if (!output_value) { return NULL; } + skip_char(string); + skip_whitespaces(string); + if (**string == ']') { /* empty array */ + skip_char(string); + return output_value; + } + while (**string != '\0') { + new_array_value = parse_value(string, nesting); + if (!new_array_value) { + json_value_free(output_value); + return NULL; + } + if(json_array_add(output_array, new_array_value) == ERROR) { + parson_free(new_array_value); + json_value_free(output_value); + return NULL; + } + skip_whitespaces(string); + if (**string != ',') { break; } + skip_char(string); + skip_whitespaces(string); + } + skip_whitespaces(string); + if (**string != ']' || /* Trim array after parsing is over */ + json_array_resize(output_array, json_array_get_count(output_array)) == ERROR) { + json_value_free(output_value); + return NULL; + } + skip_char(string); + return output_value; +} + +static JSON_Value * parse_string_value(const char **string) { + const char *new_string = get_processed_string(string); + if (!new_string) { return NULL; } + return json_value_init_string(new_string); +} + +static JSON_Value * parse_boolean_value(const char **string) { + size_t true_token_size = sizeof_token("true"); + size_t false_token_size = sizeof_token("false"); + if (strncmp("true", *string, true_token_size) == 0) { + *string += true_token_size; + return json_value_init_boolean(1); + } else if (strncmp("false", *string, false_token_size) == 0) { + *string += false_token_size; + return json_value_init_boolean(0); + } + return NULL; +} + +static JSON_Value * parse_number_value(const char **string) { + char *end; + double number = strtod(*string, &end); + JSON_Value *output_value; + if (is_decimal(*string, end - *string)) { + *string = end; + output_value = json_value_init_number(number); + } else { + output_value = NULL; + } + return output_value; +} + +static JSON_Value * parse_null_value(const char **string) { + size_t token_size = sizeof_token("null"); + if (strncmp("null", *string, token_size) == 0) { + *string += token_size; + return json_value_init_null(); + } + return NULL; +} + +/* Parser API */ +JSON_Value * json_parse_file(const char *filename) { + FILE *fp = fopen(filename, "r"); + size_t file_size; + char *file_contents; + JSON_Value *output_value; + if (!fp) { return NULL; } + fseek(fp, 0L, SEEK_END); + file_size = ftell(fp); + rewind(fp); + file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); + if (!file_contents) { fclose(fp); return NULL; } + fread(file_contents, file_size, 1, fp); + fclose(fp); + file_contents[file_size] = '\0'; + output_value = json_parse_string(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_string(const char *string) { + if (!string || (*string != '{' && *string != '[')) { return NULL; } + return parse_value((const char**)&string, 0); +} + +/* JSON Object API */ +JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) { + return json_object_nget_value(object, name, strlen(name)); +} + +const char * json_object_get_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_get_value(object, name)); +} + +double json_object_get_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_get_value(object, name)); +} + +JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_get_value(object, name)); +} + +JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_get_value(object, name)); +} + +int json_object_get_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_get_value(object, name)); +} + +JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) { + const char *dot_position = strchr(name, '.'); + if (!dot_position) { return json_object_get_value(object, name); } + object = json_value_get_object(json_object_nget_value(object, name, dot_position - name)); + return json_object_dotget_value(object, dot_position + 1); +} + +const char * json_object_dotget_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_dotget_value(object, name)); +} + +double json_object_dotget_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_dotget_value(object, name)); +} + +JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_dotget_value(object, name)); +} + +JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_dotget_value(object, name)); +} + +int json_object_dotget_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_dotget_value(object, name)); +} + +size_t json_object_get_count(const JSON_Object *object) { + return object ? object->count : 0; +} + +const char * json_object_get_name(const JSON_Object *object, size_t index) { + if (index >= json_object_get_count(object)) { return NULL; } + return object->names[index]; +} + +/* JSON Array API */ +JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) { + if (index >= json_array_get_count(array)) { return NULL; } + return array->items[index]; +} + +const char * json_array_get_string(const JSON_Array *array, size_t index) { + return json_value_get_string(json_array_get_value(array, index)); +} + +double json_array_get_number(const JSON_Array *array, size_t index) { + return json_value_get_number(json_array_get_value(array, index)); +} + +JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) { + return json_value_get_object(json_array_get_value(array, index)); +} + +JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) { + return json_value_get_array(json_array_get_value(array, index)); +} + +int json_array_get_boolean(const JSON_Array *array, size_t index) { + return json_value_get_boolean(json_array_get_value(array, index)); +} + +size_t json_array_get_count(const JSON_Array *array) { + return array ? array->count : 0; +} + +/* JSON Value API */ +JSON_Value_Type json_value_get_type(const JSON_Value *value) { + return value ? value->type : JSONError; +} + +JSON_Object * json_value_get_object(const JSON_Value *value) { + return json_value_get_type(value) == JSONObject ? value->value.object : NULL; +} + +JSON_Array * json_value_get_array(const JSON_Value *value) { + return json_value_get_type(value) == JSONArray ? value->value.array : NULL; +} + +const char * json_value_get_string(const JSON_Value *value) { + return json_value_get_type(value) == JSONString ? value->value.string : NULL; +} + +double json_value_get_number(const JSON_Value *value) { + return json_value_get_type(value) == JSONNumber ? value->value.number : 0; +} + +int json_value_get_boolean(const JSON_Value *value) { + return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; +} + +void json_value_free(JSON_Value *value) { + switch (json_value_get_type(value)) { + case JSONObject: + json_object_free(value->value.object); + break; + case JSONString: + if (value->value.string) { parson_free(value->value.string); } + break; + case JSONArray: + json_array_free(value->value.array); + break; + default: + break; + } + parson_free(value); +} diff --git a/src/parson.h b/src/parson.h new file mode 100644 index 0000000..00728b1 --- /dev/null +++ b/src/parson.h @@ -0,0 +1,100 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef parson_parson_h +#define parson_parson_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include /* size_t */ + +/* Types and enums */ +typedef struct json_object_t JSON_Object; +typedef struct json_array_t JSON_Array; +typedef struct json_value_t JSON_Value; + +typedef enum json_value_type { + JSONError = 0, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +} JSON_Value_Type; + +/* Parses first JSON value in a file, returns NULL in case of error */ +JSON_Value * json_parse_file(const char *filename); + +/* Parses first JSON value in a string, returns NULL in case of error */ +JSON_Value * json_parse_string(const char *string); + +/* JSON Object */ +JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); +const char * json_object_get_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); +double json_object_get_number (const JSON_Object *object, const char *name); +int json_object_get_boolean(const JSON_Object *object, const char *name); + +/* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ +JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); +const char * json_object_dotget_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); +double json_object_dotget_number (const JSON_Object *object, const char *name); +int json_object_dotget_boolean(const JSON_Object *object, const char *name); + +/* Functions to get available names */ +size_t json_object_get_count(const JSON_Object *object); +const char * json_object_get_name (const JSON_Object *object, size_t index); + +/* JSON Array */ +JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); +const char * json_array_get_string (const JSON_Array *array, size_t index); +JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); +JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); +double json_array_get_number (const JSON_Array *array, size_t index); +int json_array_get_boolean(const JSON_Array *array, size_t index); +size_t json_array_get_count (const JSON_Array *array); + +/* JSON Value */ +JSON_Value_Type json_value_get_type (const JSON_Value *value); +JSON_Object * json_value_get_object (const JSON_Value *value); +JSON_Array * json_value_get_array (const JSON_Value *value); +const char * json_value_get_string (const JSON_Value *value); +double json_value_get_number (const JSON_Value *value); +int json_value_get_boolean(const JSON_Value *value); +void json_value_free (JSON_Value *value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/version.h b/version.h index 2efe25b..1e3bbdc 100644 --- a/version.h +++ b/version.h @@ -1,4 +1,4 @@ #ifndef VERSION_H #define VERSION_H -const char *fdkaac_version = "0.1.6"; +const char *fdkaac_version = "0.1.7"; #endif -- 2.30.2