/* -*- linux-c -*- Copyright (C) 2007 Tom Szilagyi 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., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: metadata.c 1085 2009-12-18 17:22:50Z peterszilagyi $ */ #include #include #include #include #include "common.h" #include "i18n.h" #include "utils.h" #include "options.h" #include "metadata.h" #include "metadata_api.h" extern options_t options; /* the data model */ struct { int type; char * name; /* name displayed to the user */ char * parse_fmt; /* format string for conversion from int/float types */ char * render_fmt; /* format string for conversion to int/float types */ int tags; /* tags this frame can be present in */ struct { int tag; int flags; char * name; /* name as it is written in tag */ } const emb_names[META_N_TAGS]; } const meta_model[] = { {META_FIELD_TITLE, X_("Title"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2 | META_TAG_MPEGSTREAM, {{META_TAG_APE, 0, "Title"}, {META_TAG_OXC, 0, "title"}, {META_TAG_ID3v1, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "Title"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TIT2"}, {META_TAG_MPEGSTREAM, 0, "Title"}}}, {META_FIELD_ARTIST, X_("Artist"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2 | META_TAG_MPEGSTREAM, {{META_TAG_APE, 0, "Artist"}, {META_TAG_OXC, 0, "artist"}, {META_TAG_ID3v1, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "Artist"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TPE1"}, {META_TAG_MPEGSTREAM, 0, "Artist"}}}, {META_FIELD_ALBUM, X_("Album"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2 | META_TAG_MPEGSTREAM, {{META_TAG_APE, 0, "Album"}, {META_TAG_OXC, 0, "album"}, {META_TAG_ID3v1, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "Album"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TALB"}, {META_TAG_MPEGSTREAM, 0, "Album"}}}, {META_FIELD_DATE, X_("Date"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2, {{META_TAG_APE, META_FIELD_UNIQUE, "Year"}, {META_TAG_OXC, META_FIELD_UNIQUE, "date"}, {META_TAG_ID3v1, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "Year"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TDRC"}}}, {META_FIELD_GENRE, X_("Genre"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2, {{META_TAG_APE, META_FIELD_UNIQUE, "Genre"}, {META_TAG_OXC, META_FIELD_UNIQUE, "genre"}, {META_TAG_ID3v1, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "Genre"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TCON"}}}, {META_FIELD_TRACKNO, X_("Track No."), "%d", "%02d", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2, {{META_TAG_APE, META_FIELD_UNIQUE, "Track"}, {META_TAG_OXC, META_FIELD_UNIQUE, "tracknumber"}, {META_TAG_ID3v1, META_FIELD_UNIQUE, "Track"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TRCK"}}}, {META_FIELD_COMMENT, X_("Comment"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v1 | META_TAG_ID3v2, {{META_TAG_APE, 0, "Comment"}, {META_TAG_OXC, 0, "comment"}, {META_TAG_ID3v1, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "Comment"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "COMM"}}}, {META_FIELD_DISC, X_("Disc"), "%d", "%d", META_TAG_APE | META_TAG_OXC, {{META_TAG_APE, 0, "Disc"}, {META_TAG_OXC, 0, "disc"}}}, {META_FIELD_PERFORMER, X_("Performer"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "performer"}}}, {META_FIELD_DESCRIPTION, X_("Description"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "description"}}}, {META_FIELD_ORGANIZATION, X_("Organization"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "organization"}}}, {META_FIELD_LOCATION, X_("Location"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "location"}}}, {META_FIELD_CONTACT, X_("Contact"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "contact"}}}, {META_FIELD_LICENSE, X_("License"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "license"}}}, {META_FIELD_COPYRIGHT, X_("Copyright"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v2, {{META_TAG_APE, 0, "Copyright"}, {META_TAG_OXC, 0, "copyright"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TCOP"}}}, {META_FIELD_ISRC, X_("ISRC"), "", "", META_TAG_APE | META_TAG_OXC | META_TAG_ID3v2, {{META_TAG_APE, 0, "ISRC"}, {META_TAG_OXC, 0, "isrc"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TSRC"}}}, {META_FIELD_VERSION, X_("Version"), "", "", META_TAG_OXC, {{META_TAG_OXC, 0, "Version"}}}, {META_FIELD_SUBTITLE, X_("Subtitle"), "", "", META_TAG_APE | META_TAG_ID3v2, {{META_TAG_APE, 0, "Subtitle"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TIT3"}}}, {META_FIELD_DEBUT_ALBUM, X_("Debut Album"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Debut album"}}}, {META_FIELD_PUBLISHER, X_("Publisher"), "", "", META_TAG_APE | META_TAG_ID3v2, {{META_TAG_APE, 0, "Publisher"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TPUB"}}}, {META_FIELD_CONDUCTOR, X_("Conductor"), "", "", META_TAG_APE | META_TAG_ID3v2, {{META_TAG_APE, 0, "Conductor"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TCON"}}}, {META_FIELD_COMPOSER, X_("Composer"), "", "", META_TAG_APE | META_TAG_ID3v2, {{META_TAG_APE, 0, "Composer"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TCOM"}}}, {META_FIELD_PRIGHT, X_("Publication Right"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Publicationright"}}}, {META_FIELD_FILE, X_("File"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "File"}}}, {META_FIELD_EAN_UPC, X_("EAN/UPC"), "%d", "%d", META_TAG_APE, {{META_TAG_APE, 0, "EAN/UPC"}}}, {META_FIELD_ISBN, X_("ISBN"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "ISBN"}}}, {META_FIELD_CATALOG, X_("Catalog Number"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Catalog"}}}, {META_FIELD_LC, X_("Label Code"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "LC"}}}, {META_FIELD_RECORD_DATE, X_("Record Date"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Record Date"}}}, {META_FIELD_RECORD_LOC, X_("Record Location"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Record Location"}}}, {META_FIELD_MEDIA, X_("Media"), "", "", META_TAG_APE | META_TAG_ID3v2, {{META_TAG_APE, 0, "Media"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TMED"}}}, {META_FIELD_INDEX, X_("Index"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Index"}}}, {META_FIELD_RELATED, X_("Related"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Related"}}}, {META_FIELD_ABSTRACT, X_("Abstract"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Abstract"}}}, {META_FIELD_LANGUAGE, X_("Language"), "", "", META_TAG_APE | META_TAG_ID3v2, {{META_TAG_APE, 0, "Language"}, {META_TAG_ID3v2, META_FIELD_UNIQUE, "TLAN"}}}, {META_FIELD_BIBLIOGRAPHY, X_("Bibliography"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Bibliography"}}}, {META_FIELD_INTROPLAY, X_("Introplay"), "", "", META_TAG_APE, {{META_TAG_APE, 0, "Introplay"}}}, {META_FIELD_TBPM, X_("BPM"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TBPM"}}}, {META_FIELD_TDEN, X_("Encoding Time"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TDEN"}}}, {META_FIELD_TDLY, X_("Playlist Delay"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TDLY"}}}, {META_FIELD_TDOR, X_("Original Release Time"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TDOR"}}}, {META_FIELD_TDRL, X_("Release Time"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TDRL"}}}, {META_FIELD_TDTG, X_("Tagging Time"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TDTG"}}}, {META_FIELD_TENC, X_("Encoded by"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TENC"}}}, {META_FIELD_T_E_X_T, X_("Lyricist/Text Writer"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TEXT"}}}, {META_FIELD_TFLT, X_("File Type"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TFLT"}}}, {META_FIELD_TIPL, X_("Involved People"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TIPL"}}}, {META_FIELD_TIT1, X_("Content Group"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TIT1"}}}, {META_FIELD_TKEY, X_("Initial key"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TKEY"}}}, {META_FIELD_TLEN, X_("Length"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TLEN"}}}, {META_FIELD_TMCL, X_("Musician Credits"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TMCL"}}}, {META_FIELD_TMOO, X_("Mood"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TMOO"}}}, {META_FIELD_TOAL, X_("Original Album"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TOAL"}}}, {META_FIELD_TOFN, X_("Original Filename"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TOFN"}}}, {META_FIELD_TOLY, X_("Original Lyricist"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TOLY"}}}, {META_FIELD_TOPE, X_("Original Artist"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TOPE"}}}, {META_FIELD_TOWN, X_("File Owner"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TOWN"}}}, {META_FIELD_TPE2, X_("Band/Orchestra"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TPE2"}}}, {META_FIELD_TPE4, X_("Interpreted/Remixed"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TPE4"}}}, {META_FIELD_TPOS, X_("Part Of A Set"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TPOS"}}}, {META_FIELD_TPRO, X_("Produced"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TPRO"}}}, {META_FIELD_TRSN, X_("Internet Radio Station Name"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TRSN"}}}, {META_FIELD_TRSO, X_("Internet Radio Station Owner"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TRSO"}}}, {META_FIELD_TSOA, X_("Album Sort Order"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TSOA"}}}, {META_FIELD_TSOP, X_("Performer Sort Order"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TSOP"}}}, {META_FIELD_TSOT, X_("Title Sort Order"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TSOT"}}}, {META_FIELD_TSSE, X_("Software"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TSSE"}}}, {META_FIELD_TSST, X_("Set Subtitle"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "TSST"}}}, {META_FIELD_TXXX, X_("User Defined Text"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, 0, "TXXX"}}}, {META_FIELD_WCOM, X_("Commercial Information"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WCOM"}}}, {META_FIELD_WCOP, X_("Copyright/Legal Information"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WCOP"}}}, {META_FIELD_WOAF, X_("Official Audio File Website"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WOAF"}}}, {META_FIELD_WOAR, X_("Official Artist Website"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WOAR"}}}, {META_FIELD_WOAS, X_("Official Audio Source Website"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WOAS"}}}, {META_FIELD_WORS, X_("Official Radio Station Website"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WORS"}}}, {META_FIELD_WPAY, X_("Payment"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WPAY"}}}, {META_FIELD_WPUB, X_("Publisher's Official Website"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "WPUB"}}}, {META_FIELD_WXXX, X_("User Defined URL"), "", "", META_TAG_ID3v2, {{META_TAG_ID3v2, 0, "WXXX"}}}, {META_FIELD_VENDOR, X_("Vendor"), "", "", META_TAG_OXC, {{META_TAG_OXC, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "vendor"}}}, {META_FIELD_RG_REFLOUDNESS, X_("ReplayGain Reference Loudness"), "%f", "%02.1f dB", META_TAG_APE | META_TAG_OXC, {{META_TAG_APE, META_FIELD_UNIQUE, "Replaygain_reference_loudness"}, {META_TAG_OXC, META_FIELD_UNIQUE, "replaygain_reference_loudness"}}}, {META_FIELD_RG_TRACK_GAIN, X_("ReplayGain Track Gain"), "%f", "%1.2f dB", META_TAG_APE | META_TAG_OXC | META_TAG_MPC_RGDATA, {{META_TAG_APE, META_FIELD_UNIQUE, "Replaygain_track_gain"}, {META_TAG_OXC, META_FIELD_UNIQUE, "replaygain_track_gain"}, {META_TAG_MPC_RGDATA, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "track_gain"}}}, {META_FIELD_RG_TRACK_PEAK, X_("ReplayGain Track Peak"), "%f", "%f", META_TAG_APE | META_TAG_OXC | META_TAG_MPC_RGDATA, {{META_TAG_APE, META_FIELD_UNIQUE, "Replaygain_track_peak"}, {META_TAG_OXC, META_FIELD_UNIQUE, "replaygain_track_peak"}, {META_TAG_MPC_RGDATA, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "track_peak"}}}, {META_FIELD_RG_ALBUM_GAIN, X_("ReplayGain Album Gain"), "%f dB", "%1.2f dB", META_TAG_APE | META_TAG_OXC | META_TAG_MPC_RGDATA, {{META_TAG_APE, META_FIELD_UNIQUE, "Replaygain_album_gain"}, {META_TAG_OXC, META_FIELD_UNIQUE, "replaygain_album_gain"}, {META_TAG_MPC_RGDATA, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "album_gain"}}}, {META_FIELD_RG_ALBUM_PEAK, X_("ReplayGain Album Peak"), "%f", "%f", META_TAG_APE | META_TAG_OXC | META_TAG_MPC_RGDATA, {{META_TAG_APE, META_FIELD_UNIQUE, "Replaygain_album_peak"}, {META_TAG_OXC, META_FIELD_UNIQUE, "replaygain_album_peak"}, {META_TAG_MPC_RGDATA, META_FIELD_UNIQUE | META_FIELD_MANDATORY, "album_peak"}}}, {META_FIELD_ICY_NAME, X_("Icy-Name"), "", "", META_TAG_GEN_STREAM, {{META_TAG_GEN_STREAM, 0, "Icy-Name"}}}, {META_FIELD_ICY_DESCR, X_("Icy-Description"), "", "", META_TAG_GEN_STREAM, {{META_TAG_GEN_STREAM, 0, "Icy-Description"}}}, {META_FIELD_ICY_GENRE, X_("Icy-Genre"), "", "", META_TAG_GEN_STREAM, {{META_TAG_GEN_STREAM, 0, "Icy-Genre"}}}, {META_FIELD_RVA2, X_("RVA"), "%f dB", "%2.2f dB", META_TAG_ID3v2, {{META_TAG_ID3v2, META_FIELD_UNIQUE, "RVA2"}}}, {META_FIELD_APIC, X_("Attached Picture"), "", "", META_TAG_ID3v2 | META_TAG_APE | META_TAG_FLAC_APIC, {{META_TAG_ID3v2, 0, "APIC"}, {META_TAG_APE, 0, "Cover Art"}, {META_TAG_FLAC_APIC, 0, "APIC"}}}, {META_FIELD_GEOB, X_("Binary Object"), "", "", 0, {}}, {-1}}; /* data model functions */ char * meta_get_tagname(int tag) { switch (tag) { case META_TAG_NULL: return _("NULL"); case META_TAG_ID3v1: return _("ID3v1"); case META_TAG_ID3v2: return _("ID3v2"); case META_TAG_APE: return _("APE"); case META_TAG_OXC: return _("Ogg Xiph Comments"); case META_TAG_FLAC_APIC: return _("FLAC Pictures"); case META_TAG_MPC_RGDATA: return _("Musepack ReplayGain"); case META_TAG_GEN_STREAM: return _("Generic StreamMeta"); case META_TAG_MPEGSTREAM: return _("MPEG StreamMeta"); case META_TAG_MODINFO: return _("Module info"); default: return _("Unknown"); } } int meta_tag_from_name(char * name) { int i = 1; if (strcmp(name, meta_get_tagname(0)) == 0) return 0; while (1) { if (strcmp(name, meta_get_tagname(i)) == 0) { return i; } i <<= 1; } fprintf(stderr, "meta_tag_from_name: programmer error\n"); return -1; } int meta_get_fieldname(int type, char ** str) { int i; for (i = 0; meta_model[i].type != -1; i++) { if (type == meta_model[i].type) { *str = _(meta_model[i].name); return 1; } } return 0; } int meta_get_fieldname_embedded(int tag, int type, char ** str) { int i; for (i = 0; meta_model[i].type != -1; i++) { if (type == meta_model[i].type) { int j; for (j = 0; j < META_N_TAGS; j++) { if (tag == meta_model[i].emb_names[j].tag) { *str = meta_model[i].emb_names[j].name; return 1; } } return 0; } } return 0; } char * meta_get_field_parsefmt(int type) { int i; for (i = 0; meta_model[i].type != -1; i++) { if (type == meta_model[i].type) { return meta_model[i].parse_fmt; } } if (META_FIELD_INT(type)) { return "%d"; } if (META_FIELD_FLOAT(type)) { return "%f"; } return ""; } char * meta_get_field_renderfmt(int type) { int i; for (i = 0; meta_model[i].type != -1; i++) { if (type == meta_model[i].type) { return meta_model[i].render_fmt; } } if (META_FIELD_INT(type)) { return "%d"; } if (META_FIELD_FLOAT(type)) { return "%f"; } return ""; } int meta_frame_type_from_name(char * name) { int i; for (i = 0; meta_model[i].type != -1; i++) { if (strcmp(name, _(meta_model[i].name)) == 0) { return meta_model[i].type; } } return META_FIELD_OTHER; } int meta_frame_type_from_embedded_name(int tag, char * name) { int i; for (i = 0; meta_model[i].type != -1; i++) { int j; for (j = 0; j < META_N_TAGS; j++) { if ((tag == meta_model[i].emb_names[j].tag) && (strcasecmp(name, meta_model[i].emb_names[j].name) == 0)) { return meta_model[i].type; } } } return META_FIELD_OTHER; } GSList * meta_get_possible_fields(int tag) { GSList * list = NULL; int i; for (i = 0; meta_model[i].type != -1; i++) { if ((tag & meta_model[i].tags) != 0) { list = g_slist_append(list, GINT_TO_POINTER(meta_model[i].type)); } } return list; } int meta_get_default_flags(int tag, int type) { int i; for (i = 0; meta_model[i].type != -1; i++) { if (type == meta_model[i].type) { int j; for (j = 0; j < META_N_TAGS; j++) { if (tag == meta_model[i].emb_names[j].tag) { return meta_model[i].emb_names[j].flags; } } return 0; } } return 0; } meta_frame_t * metadata_find_companion_frame(metadata_t * meta, meta_frame_t * frame) { meta_frame_t * f; int tag = META_TAG_MAX; while (tag > 0) { if (tag != frame->tag) { f = metadata_get_frame_by_tag_and_type(meta, tag, frame->type, NULL); if (f) { return f; } } tag >>= 1; } return NULL; } /* search for frames other than the given frame itself that are of the same type and copy their contents */ void metadata_clone_frame(metadata_t * meta, meta_frame_t * frame) { meta_frame_t * companion = metadata_find_companion_frame(meta, frame); if (companion == NULL) { return; } if (companion->field_name != NULL) { if (frame->field_name != NULL) { free(frame->field_name); } frame->field_name = strdup(companion->field_name); } if (companion->field_val != NULL) { frame->field_val = strdup(companion->field_val); } frame->int_val = companion->int_val; frame->float_val = companion->float_val; if (companion->length > 0) { frame->length = companion->length; frame->data = (unsigned char *)malloc(frame->length); if (frame->data == NULL) { fprintf(stderr, "metadata_clone_frame: malloc error\n"); return; } memcpy(frame->data, companion->data, frame->length); } } void metadata_add_mandatory_frames(metadata_t * meta, int tag) { int i; for (i = 0; meta_model[i].type != -1; i++) { int j; for (j = 0; j < META_N_TAGS; j++) { if ((tag == meta_model[i].emb_names[j].tag) && ((meta_model[i].emb_names[j].flags & META_FIELD_MANDATORY) != 0)) { meta_frame_t * frame = meta_frame_new(); char * str; frame->tag = tag; frame->type = meta_model[i].type; frame->flags = meta_get_default_flags(tag, frame->type); if (meta_get_fieldname(frame->type, &str)) { if (options.metaedit_auto_clone) { metadata_clone_frame(meta, frame); } if (frame->field_name == NULL) { frame->field_name = strdup(str); } } else { fprintf(stderr, "metadata_add_mandatory_frames: programmer error\n"); meta_frame_free(frame); return; } if (frame->field_val == NULL) { frame->field_val = strdup(""); } metadata_add_frame(meta, frame); } } } } /* object methods */ metadata_t * metadata_new(void) { metadata_t * meta = NULL; if ((meta = calloc(1, sizeof(metadata_t))) == NULL) { fprintf(stderr, "metadata.c: metadata_new() failed: calloc error\n"); return NULL; } return meta; } void metadata_free(metadata_t * meta) { meta_frame_t * p; meta_frame_t * q; if (meta->root == NULL) { free(meta); return; } p = meta->root; while (p != NULL) { q = p->next; meta_frame_free(p); p = q; } free(meta); } meta_frame_t * meta_frame_new(void) { meta_frame_t * meta_frame = NULL; if ((meta_frame = calloc(1, sizeof(meta_frame_t))) == NULL) { fprintf(stderr, "metadata.c: meta_frame_new() failed: calloc error\n"); return NULL; } return meta_frame; } void meta_frame_free(meta_frame_t * meta_frame) { if (meta_frame->field_name != NULL) free(meta_frame->field_name); if (meta_frame->field_val != NULL) free(meta_frame->field_val); if (meta_frame->data != NULL) free(meta_frame->data); free(meta_frame); } void metadata_add_frame(metadata_t * meta, meta_frame_t * frame) { frame->next = NULL; if (meta->root == NULL) { meta->root = frame; } else { meta_frame_t * prev = meta->root; while (prev->next != NULL) { prev = prev->next; } prev->next = frame; } } /* take frame out of meta; does not free frame! */ void metadata_remove_frame(metadata_t * meta, meta_frame_t * frame) { meta_frame_t * prev; if (meta->root == frame) { meta->root = frame->next; return; } prev = meta->root; while (prev->next != frame) { prev = prev->next; } prev->next = frame->next; } /* helper functions */ /* Search for frame of given type. Passing NULL as root will search from * the beginning of the frame list. Passing the previous return value of * this function as root allows getting multiple frames of the same type. * Returns NULL if there are no (more) frames of the specified type. */ meta_frame_t * metadata_get_frame_by_type(metadata_t * meta, int type, meta_frame_t * root) { meta_frame_t * frame; if (meta == NULL) { return NULL; } if (root == NULL) { frame = meta->root; } else { frame = root->next; } if (frame == NULL) { return NULL; } while (frame->type != type) { frame = frame->next; if (frame == NULL) { return NULL; } } return frame; } /* Search for frame belonging to tag. Passing NULL as root will search from * the beginning of the frame list. Passing the previous return value of * this function as root allows getting multiple frames of the same tag. * Returns NULL if there are no (more) frames of the specified tag. */ meta_frame_t * metadata_get_frame_by_tag(metadata_t * meta, int tag, meta_frame_t * root) { meta_frame_t * frame; if (root == NULL) { frame = meta->root; } else { frame = root->next; } if (frame == NULL) { return NULL; } while (frame->tag != tag) { frame = frame->next; if (frame == NULL) { return NULL; } } return frame; } /* Search for frame of given type, belonging to tag. Passing NULL as * root will search from the beginning of the frame list. Passing the * previous return value of this function as root allows getting * multiple frames of the same tag. Returns NULL if there are no * (more) frames of the specified tag. */ meta_frame_t * metadata_get_frame_by_tag_and_type(metadata_t * meta, int tag, int type, meta_frame_t * root) { meta_frame_t * frame; if (meta == NULL) { return NULL; } if (root == NULL) { frame = meta->root; } else { frame = root->next; } if (frame == NULL) { return NULL; } while (frame->tag != tag || frame->type != type) { frame = frame->next; if (frame == NULL) { return NULL; } } return frame; } meta_frame_t * metadata_add_frame_from_keyval(metadata_t * meta, int tag, char * key, char * val) { meta_frame_t * frame = meta_frame_new(); char * str; char * parsefmt; frame->tag = tag; frame->type = meta_frame_type_from_embedded_name(tag, key); if (meta_get_fieldname(frame->type, &str)) { frame->field_name = strdup(str); } else { frame->field_name = strdup(key); } frame->field_val = strdup(val); frame->flags = meta_get_default_flags(tag, frame->type); parsefmt = meta_get_field_parsefmt(frame->type); if (META_FIELD_INT(frame->type)) { if (sscanf(val, parsefmt, &frame->int_val) < 1) { fprintf(stderr, "warning: couldn't parse integer value from '%s' using format string '%s'\n", val, parsefmt); } } else if (META_FIELD_FLOAT(frame->type)) { if (sscanf(val, parsefmt, &frame->float_val) < 1) { fprintf(stderr, "warning: couldn't parse float value from '%s' using format string '%s'\n", val, parsefmt); } } metadata_add_frame(meta, frame); return frame; } metadata_t * metadata_from_mpeg_stream_data(char * str) { metadata_t * meta = metadata_new(); char * s; meta->writable = 0; s = strtok(str, ";"); while (s) { char key[MAXLEN]; char val[MAXLEN]; char c; int k, n = 0; for (k = 0; ((c = s[n]) != '\0') && (c != '=') && (k < MAXLEN-1); k++) { key[k] = c; ++n; } key[k] = '\0'; ++n; if (strstr(key, "Stream") == key) { memmove(key, key+6, sizeof(key)-6); } if (str[n] == '\'') { ++n; } for (k = 0; ((c = s[n]) != '\0') && (c != '\'') && (k < MAXLEN-1); k++) { val[k] = c; ++n; } val[k] = '\0'; metadata_add_frame_from_keyval(meta, META_TAG_MPEGSTREAM, key, val); s = strtok(NULL, ";"); } return meta; } metadata_t * metadata_clone(metadata_t * meta, int tags) { metadata_t * out = metadata_new(); int tag = META_TAG_MAX; if (meta == NULL) { return NULL; } if (out == NULL) { return NULL; } /* iterate on possible output tags */ while (tag) { int i; if ((tags & tag) == 0) { tag >>= 1; continue; } /* iterate on frame types supported by this tag */ for (i = 0; meta_model[i].type != -1; i++) { meta_frame_t * frame; int type = meta_model[i].type; if ((tag & meta_model[i].tags) == 0) { continue; } if (type == META_FIELD_VENDOR) { continue; } /* for each frame type, iterate on tags that might have it in the input meta in pref. order and copy all of them */ frame = metadata_pref_frame_by_type(meta, type, NULL); while (frame != NULL) { meta_frame_t * fout = meta_frame_new(); fout->tag = tag; fout->type = type; if (frame->field_name) { fout->field_name = strdup(frame->field_name); } if (frame->field_val) { fout->field_val = strdup(frame->field_val); } fout->int_val = frame->int_val; fout->float_val = frame->float_val; fout->length = frame->length; if (fout->length > 0) { fout->data = malloc(fout->length); if (fout->data == NULL) { fprintf(stderr, "metadata_clone: malloc error\n"); return NULL; } memcpy(fout->data, frame->data, fout->length); } metadata_add_frame(out, fout); frame = metadata_pref_frame_by_type(meta, type, frame); } } tag >>= 1; } return out; } /* low-level utils */ u_int32_t meta_read_int32(unsigned char * buf) { return ((((u_int32_t)buf[0])) | (((u_int32_t)buf[1]) << 8) | (((u_int32_t)buf[2]) << 16) | (((u_int32_t)buf[3]) << 24)); } u_int64_t meta_read_int64(unsigned char * buf) { return ((((u_int64_t)buf[0])) | (((u_int64_t)buf[1]) << 8) | (((u_int64_t)buf[2]) << 16) | (((u_int64_t)buf[3]) << 24) | (((u_int64_t)buf[4]) << 32) | (((u_int64_t)buf[5]) << 40) | (((u_int64_t)buf[6]) << 48) | (((u_int64_t)buf[7]) << 56)); } void meta_write_int32(u_int32_t val, unsigned char * buf) { buf[0] = ((val & 0xff)); buf[1] = ((val >> 8) & 0xff); buf[2] = ((val >> 16) & 0xff); buf[3] = ((val >> 24) & 0xff); } void meta_write_int64(u_int64_t val, unsigned char * buf) { buf[0] = ((val & 0xff)); buf[1] = ((val >> 8) & 0xff); buf[2] = ((val >> 16) & 0xff); buf[3] = ((val >> 24) & 0xff); buf[4] = ((val >> 32) & 0xff); buf[5] = ((val >> 40) & 0xff); buf[6] = ((val >> 48) & 0xff); buf[7] = ((val >> 56) & 0xff); } /* debug functions */ /* void metadata_dump(metadata_t * meta) { meta_frame_t * frame = meta->root; printf("\nMetadata block dump, writable = %d, valid_tags = %d\n", meta->writable, meta->valid_tags); while (frame) { meta_dump_frame(frame); frame = frame->next; } } void meta_dump_frame(meta_frame_t * frame) { printf(" Tag %2d Type %4d F=0x%04x '%s' '%s' ", frame->tag, frame->type, frame->flags, frame->field_name, frame->field_val); printf("int %d float %f ptr %p len %d\n", frame->int_val, frame->float_val, frame->data, frame->length); } */