| 1 | /* |
| 2 | * ID3v1 header parser |
| 3 | * Copyright (c) 2003 Fabrice Bellard |
| 4 | * |
| 5 | * This file is part of FFmpeg. |
| 6 | * |
| 7 | * FFmpeg is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * FFmpeg is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with FFmpeg; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ |
| 21 | |
| 22 | #include "id3v1.h" |
| 23 | #include "libavcodec/avcodec.h" |
| 24 | #include "libavutil/dict.h" |
| 25 | |
| 26 | /* See Genre List at http://id3.org/id3v2.3.0 */ |
| 27 | const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { |
| 28 | [0] = "Blues", |
| 29 | [1] = "Classic Rock", |
| 30 | [2] = "Country", |
| 31 | [3] = "Dance", |
| 32 | [4] = "Disco", |
| 33 | [5] = "Funk", |
| 34 | [6] = "Grunge", |
| 35 | [7] = "Hip-Hop", |
| 36 | [8] = "Jazz", |
| 37 | [9] = "Metal", |
| 38 | [10] = "New Age", |
| 39 | [11] = "Oldies", |
| 40 | [12] = "Other", |
| 41 | [13] = "Pop", |
| 42 | [14] = "R&B", |
| 43 | [15] = "Rap", |
| 44 | [16] = "Reggae", |
| 45 | [17] = "Rock", |
| 46 | [18] = "Techno", |
| 47 | [19] = "Industrial", |
| 48 | [20] = "Alternative", |
| 49 | [21] = "Ska", |
| 50 | [22] = "Death Metal", |
| 51 | [23] = "Pranks", |
| 52 | [24] = "Soundtrack", |
| 53 | [25] = "Euro-Techno", |
| 54 | [26] = "Ambient", |
| 55 | [27] = "Trip-Hop", |
| 56 | [28] = "Vocal", |
| 57 | [29] = "Jazz+Funk", |
| 58 | [30] = "Fusion", |
| 59 | [31] = "Trance", |
| 60 | [32] = "Classical", |
| 61 | [33] = "Instrumental", |
| 62 | [34] = "Acid", |
| 63 | [35] = "House", |
| 64 | [36] = "Game", |
| 65 | [37] = "Sound Clip", |
| 66 | [38] = "Gospel", |
| 67 | [39] = "Noise", |
| 68 | [40] = "AlternRock", |
| 69 | [41] = "Bass", |
| 70 | [42] = "Soul", |
| 71 | [43] = "Punk", |
| 72 | [44] = "Space", |
| 73 | [45] = "Meditative", |
| 74 | [46] = "Instrumental Pop", |
| 75 | [47] = "Instrumental Rock", |
| 76 | [48] = "Ethnic", |
| 77 | [49] = "Gothic", |
| 78 | [50] = "Darkwave", |
| 79 | [51] = "Techno-Industrial", |
| 80 | [52] = "Electronic", |
| 81 | [53] = "Pop-Folk", |
| 82 | [54] = "Eurodance", |
| 83 | [55] = "Dream", |
| 84 | [56] = "Southern Rock", |
| 85 | [57] = "Comedy", |
| 86 | [58] = "Cult", |
| 87 | [59] = "Gangsta", |
| 88 | [60] = "Top 40", |
| 89 | [61] = "Christian Rap", |
| 90 | [62] = "Pop/Funk", |
| 91 | [63] = "Jungle", |
| 92 | [64] = "Native American", |
| 93 | [65] = "Cabaret", |
| 94 | [66] = "New Wave", |
| 95 | [67] = "Psychadelic", /* sic, the misspelling is used in the specification */ |
| 96 | [68] = "Rave", |
| 97 | [69] = "Showtunes", |
| 98 | [70] = "Trailer", |
| 99 | [71] = "Lo-Fi", |
| 100 | [72] = "Tribal", |
| 101 | [73] = "Acid Punk", |
| 102 | [74] = "Acid Jazz", |
| 103 | [75] = "Polka", |
| 104 | [76] = "Retro", |
| 105 | [77] = "Musical", |
| 106 | [78] = "Rock & Roll", |
| 107 | [79] = "Hard Rock", |
| 108 | [80] = "Folk", |
| 109 | [81] = "Folk-Rock", |
| 110 | [82] = "National Folk", |
| 111 | [83] = "Swing", |
| 112 | [84] = "Fast Fusion", |
| 113 | [85] = "Bebob", |
| 114 | [86] = "Latin", |
| 115 | [87] = "Revival", |
| 116 | [88] = "Celtic", |
| 117 | [89] = "Bluegrass", |
| 118 | [90] = "Avantgarde", |
| 119 | [91] = "Gothic Rock", |
| 120 | [92] = "Progressive Rock", |
| 121 | [93] = "Psychedelic Rock", |
| 122 | [94] = "Symphonic Rock", |
| 123 | [95] = "Slow Rock", |
| 124 | [96] = "Big Band", |
| 125 | [97] = "Chorus", |
| 126 | [98] = "Easy Listening", |
| 127 | [99] = "Acoustic", |
| 128 | [100] = "Humour", |
| 129 | [101] = "Speech", |
| 130 | [102] = "Chanson", |
| 131 | [103] = "Opera", |
| 132 | [104] = "Chamber Music", |
| 133 | [105] = "Sonata", |
| 134 | [106] = "Symphony", |
| 135 | [107] = "Booty Bass", |
| 136 | [108] = "Primus", |
| 137 | [109] = "Porn Groove", |
| 138 | [110] = "Satire", |
| 139 | [111] = "Slow Jam", |
| 140 | [112] = "Club", |
| 141 | [113] = "Tango", |
| 142 | [114] = "Samba", |
| 143 | [115] = "Folklore", |
| 144 | [116] = "Ballad", |
| 145 | [117] = "Power Ballad", |
| 146 | [118] = "Rhythmic Soul", |
| 147 | [119] = "Freestyle", |
| 148 | [120] = "Duet", |
| 149 | [121] = "Punk Rock", |
| 150 | [122] = "Drum Solo", |
| 151 | [123] = "A capella", |
| 152 | [124] = "Euro-House", |
| 153 | [125] = "Dance Hall", |
| 154 | [126] = "Goa", |
| 155 | [127] = "Drum & Bass", |
| 156 | [128] = "Club-House", |
| 157 | [129] = "Hardcore", |
| 158 | [130] = "Terror", |
| 159 | [131] = "Indie", |
| 160 | [132] = "BritPop", |
| 161 | [133] = "Negerpunk", |
| 162 | [134] = "Polsk Punk", |
| 163 | [135] = "Beat", |
| 164 | [136] = "Christian Gangsta", |
| 165 | [137] = "Heavy Metal", |
| 166 | [138] = "Black Metal", |
| 167 | [139] = "Crossover", |
| 168 | [140] = "Contemporary Christian", |
| 169 | [141] = "Christian Rock", |
| 170 | [142] = "Merengue", |
| 171 | [143] = "Salsa", |
| 172 | [144] = "Thrash Metal", |
| 173 | [145] = "Anime", |
| 174 | [146] = "JPop", |
| 175 | [147] = "SynthPop", |
| 176 | }; |
| 177 | |
| 178 | static void get_string(AVFormatContext *s, const char *key, |
| 179 | const uint8_t *buf, int buf_size) |
| 180 | { |
| 181 | int i, c; |
| 182 | char *q, str[512]; |
| 183 | |
| 184 | q = str; |
| 185 | for(i = 0; i < buf_size; i++) { |
| 186 | c = buf[i]; |
| 187 | if (c == '\0') |
| 188 | break; |
| 189 | if ((q - str) >= sizeof(str) - 1) |
| 190 | break; |
| 191 | *q++ = c; |
| 192 | } |
| 193 | *q = '\0'; |
| 194 | |
| 195 | if (*str) |
| 196 | av_dict_set(&s->metadata, key, str, 0); |
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Parse an ID3v1 tag |
| 201 | * |
| 202 | * @param buf ID3v1_TAG_SIZE long buffer containing the tag |
| 203 | */ |
| 204 | static int parse_tag(AVFormatContext *s, const uint8_t *buf) |
| 205 | { |
| 206 | int genre; |
| 207 | |
| 208 | if (!(buf[0] == 'T' && |
| 209 | buf[1] == 'A' && |
| 210 | buf[2] == 'G')) |
| 211 | return -1; |
| 212 | get_string(s, "title", buf + 3, 30); |
| 213 | get_string(s, "artist", buf + 33, 30); |
| 214 | get_string(s, "album", buf + 63, 30); |
| 215 | get_string(s, "date", buf + 93, 4); |
| 216 | get_string(s, "comment", buf + 97, 30); |
| 217 | if (buf[125] == 0 && buf[126] != 0) { |
| 218 | av_dict_set_int(&s->metadata, "track", buf[126], 0); |
| 219 | } |
| 220 | genre = buf[127]; |
| 221 | if (genre <= ID3v1_GENRE_MAX) |
| 222 | av_dict_set(&s->metadata, "genre", ff_id3v1_genre_str[genre], 0); |
| 223 | return 0; |
| 224 | } |
| 225 | |
| 226 | void ff_id3v1_read(AVFormatContext *s) |
| 227 | { |
| 228 | int ret; |
| 229 | uint8_t buf[ID3v1_TAG_SIZE]; |
| 230 | int64_t filesize, position = avio_tell(s->pb); |
| 231 | |
| 232 | if (s->pb->seekable) { |
| 233 | /* XXX: change that */ |
| 234 | filesize = avio_size(s->pb); |
| 235 | if (filesize > 128) { |
| 236 | avio_seek(s->pb, filesize - 128, SEEK_SET); |
| 237 | ret = avio_read(s->pb, buf, ID3v1_TAG_SIZE); |
| 238 | if (ret == ID3v1_TAG_SIZE) { |
| 239 | parse_tag(s, buf); |
| 240 | } |
| 241 | avio_seek(s->pb, position, SEEK_SET); |
| 242 | } |
| 243 | } |
| 244 | } |