| OLD | NEW |
| 1 /* | 1 /* |
| 2 * ID3v2 header parser | 2 * ID3v2 header parser |
| 3 * Copyright (c) 2003 Fabrice Bellard | 3 * Copyright (c) 2003 Fabrice Bellard |
| 4 * | 4 * |
| 5 * This file is part of FFmpeg. | 5 * This file is part of FFmpeg. |
| 6 * | 6 * |
| 7 * FFmpeg is free software; you can redistribute it and/or | 7 * FFmpeg is free software; you can redistribute it and/or |
| 8 * modify it under the terms of the GNU Lesser General Public | 8 * modify it under the terms of the GNU Lesser General Public |
| 9 * License as published by the Free Software Foundation; either | 9 * License as published by the Free Software Foundation; either |
| 10 * version 2.1 of the License, or (at your option) any later version. | 10 * version 2.1 of the License, or (at your option) any later version. |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 70 } | 70 } |
| 71 | 71 |
| 72 static unsigned int get_size(ByteIOContext *s, int len) | 72 static unsigned int get_size(ByteIOContext *s, int len) |
| 73 { | 73 { |
| 74 int v = 0; | 74 int v = 0; |
| 75 while (len--) | 75 while (len--) |
| 76 v = (v << 7) + (get_byte(s) & 0x7F); | 76 v = (v << 7) + (get_byte(s) & 0x7F); |
| 77 return v; | 77 return v; |
| 78 } | 78 } |
| 79 | 79 |
| 80 static void read_ttag(AVFormatContext *s, int taglen, const char *key) | 80 static void read_ttag(AVFormatContext *s, ByteIOContext *pb, int taglen, const c
har *key) |
| 81 { | 81 { |
| 82 char *q, dst[512]; | 82 char *q, dst[512]; |
| 83 const char *val = NULL; | 83 const char *val = NULL; |
| 84 int len, dstlen = sizeof(dst) - 1; | 84 int len, dstlen = sizeof(dst) - 1; |
| 85 unsigned genre; | 85 unsigned genre; |
| 86 unsigned int (*get)(ByteIOContext*) = get_be16; | 86 unsigned int (*get)(ByteIOContext*) = get_be16; |
| 87 | 87 |
| 88 dst[0] = 0; | 88 dst[0] = 0; |
| 89 if (taglen < 1) | 89 if (taglen < 1) |
| 90 return; | 90 return; |
| 91 | 91 |
| 92 taglen--; /* account for encoding type byte */ | 92 taglen--; /* account for encoding type byte */ |
| 93 | 93 |
| 94 switch (get_byte(s->pb)) { /* encoding type */ | 94 switch (get_byte(pb)) { /* encoding type */ |
| 95 | 95 |
| 96 case 0: /* ISO-8859-1 (0 - 255 maps directly into unicode) */ | 96 case 0: /* ISO-8859-1 (0 - 255 maps directly into unicode) */ |
| 97 q = dst; | 97 q = dst; |
| 98 while (taglen-- && q - dst < dstlen - 7) { | 98 while (taglen-- && q - dst < dstlen - 7) { |
| 99 uint8_t tmp; | 99 uint8_t tmp; |
| 100 PUT_UTF8(get_byte(s->pb), tmp, *q++ = tmp;) | 100 PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;) |
| 101 } | 101 } |
| 102 *q = 0; | 102 *q = 0; |
| 103 break; | 103 break; |
| 104 | 104 |
| 105 case 1: /* UTF-16 with BOM */ | 105 case 1: /* UTF-16 with BOM */ |
| 106 taglen -= 2; | 106 taglen -= 2; |
| 107 switch (get_be16(s->pb)) { | 107 switch (get_be16(pb)) { |
| 108 case 0xfffe: | 108 case 0xfffe: |
| 109 get = get_le16; | 109 get = get_le16; |
| 110 case 0xfeff: | 110 case 0xfeff: |
| 111 break; | 111 break; |
| 112 default: | 112 default: |
| 113 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key); | 113 av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key); |
| 114 return; | 114 return; |
| 115 } | 115 } |
| 116 // fall-through | 116 // fall-through |
| 117 | 117 |
| 118 case 2: /* UTF-16BE without BOM */ | 118 case 2: /* UTF-16BE without BOM */ |
| 119 q = dst; | 119 q = dst; |
| 120 while (taglen > 1 && q - dst < dstlen - 7) { | 120 while (taglen > 1 && q - dst < dstlen - 7) { |
| 121 uint32_t ch; | 121 uint32_t ch; |
| 122 uint8_t tmp; | 122 uint8_t tmp; |
| 123 | 123 |
| 124 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(s->pb) : 0), break;) | 124 GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;) |
| 125 PUT_UTF8(ch, tmp, *q++ = tmp;) | 125 PUT_UTF8(ch, tmp, *q++ = tmp;) |
| 126 } | 126 } |
| 127 *q = 0; | 127 *q = 0; |
| 128 break; | 128 break; |
| 129 | 129 |
| 130 case 3: /* UTF-8 */ | 130 case 3: /* UTF-8 */ |
| 131 len = FFMIN(taglen, dstlen); | 131 len = FFMIN(taglen, dstlen); |
| 132 get_buffer(s->pb, dst, len); | 132 get_buffer(pb, dst, len); |
| 133 dst[len] = 0; | 133 dst[len] = 0; |
| 134 break; | 134 break; |
| 135 default: | 135 default: |
| 136 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key); | 136 av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key); |
| 137 } | 137 } |
| 138 | 138 |
| 139 if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) | 139 if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) |
| 140 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) | 140 && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) |
| 141 && genre <= ID3v1_GENRE_MAX) | 141 && genre <= ID3v1_GENRE_MAX) |
| 142 val = ff_id3v1_genre_str[genre]; | 142 val = ff_id3v1_genre_str[genre]; |
| 143 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { | 143 else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { |
| 144 /* dst now contains two 0-terminated strings */ | 144 /* dst now contains two 0-terminated strings */ |
| 145 dst[dstlen] = 0; | 145 dst[dstlen] = 0; |
| 146 len = strlen(dst); | 146 len = strlen(dst); |
| 147 key = dst; | 147 key = dst; |
| 148 val = dst + FFMIN(len + 1, dstlen); | 148 val = dst + FFMIN(len + 1, dstlen); |
| 149 } | 149 } |
| 150 else if (*dst) | 150 else if (*dst) |
| 151 val = dst; | 151 val = dst; |
| 152 | 152 |
| 153 if (val) | 153 if (val) |
| 154 av_metadata_set2(&s->metadata, key, val, 0); | 154 av_metadata_set2(&s->metadata, key, val, 0); |
| 155 } | 155 } |
| 156 | 156 |
| 157 void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) | 157 void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags) |
| 158 { | 158 { |
| 159 int isv34, tlen; | 159 int isv34, tlen, unsync; |
| 160 char tag[5]; | 160 char tag[5]; |
| 161 int64_t next; | 161 int64_t next; |
| 162 int taghdrlen; | 162 int taghdrlen; |
| 163 const char *reason; | 163 const char *reason; |
| 164 ByteIOContext pb; |
| 165 unsigned char *buffer = NULL; |
| 166 int buffer_size = 0; |
| 164 | 167 |
| 165 switch (version) { | 168 switch (version) { |
| 166 case 2: | 169 case 2: |
| 167 if (flags & 0x40) { | 170 if (flags & 0x40) { |
| 168 reason = "compression"; | 171 reason = "compression"; |
| 169 goto error; | 172 goto error; |
| 170 } | 173 } |
| 171 isv34 = 0; | 174 isv34 = 0; |
| 172 taghdrlen = 6; | 175 taghdrlen = 6; |
| 173 break; | 176 break; |
| 174 | 177 |
| 175 case 3: | 178 case 3: |
| 176 case 4: | 179 case 4: |
| 177 isv34 = 1; | 180 isv34 = 1; |
| 178 taghdrlen = 10; | 181 taghdrlen = 10; |
| 179 break; | 182 break; |
| 180 | 183 |
| 181 default: | 184 default: |
| 182 reason = "version"; | 185 reason = "version"; |
| 183 goto error; | 186 goto error; |
| 184 } | 187 } |
| 185 | 188 |
| 186 if (flags & 0x80) { | 189 unsync = flags & 0x80; |
| 187 reason = "unsynchronization"; | |
| 188 goto error; | |
| 189 } | |
| 190 | 190 |
| 191 if (isv34 && flags & 0x40) /* Extended header present, just skip over it */ | 191 if (isv34 && flags & 0x40) /* Extended header present, just skip over it */ |
| 192 url_fskip(s->pb, get_size(s->pb, 4)); | 192 url_fskip(s->pb, get_size(s->pb, 4)); |
| 193 | 193 |
| 194 while (len >= taghdrlen) { | 194 while (len >= taghdrlen) { |
| 195 unsigned int tflags; |
| 196 int tunsync = 0; |
| 197 |
| 195 if (isv34) { | 198 if (isv34) { |
| 196 get_buffer(s->pb, tag, 4); | 199 get_buffer(s->pb, tag, 4); |
| 197 tag[4] = 0; | 200 tag[4] = 0; |
| 198 if(version==3){ | 201 if(version==3){ |
| 199 tlen = get_be32(s->pb); | 202 tlen = get_be32(s->pb); |
| 200 }else | 203 }else |
| 201 tlen = get_size(s->pb, 4); | 204 tlen = get_size(s->pb, 4); |
| 202 get_be16(s->pb); /* flags */ | 205 tflags = get_be16(s->pb); |
| 206 tunsync = tflags & 0x02; |
| 203 } else { | 207 } else { |
| 204 get_buffer(s->pb, tag, 3); | 208 get_buffer(s->pb, tag, 3); |
| 205 tag[3] = 0; | 209 tag[3] = 0; |
| 206 tlen = get_be24(s->pb); | 210 tlen = get_be24(s->pb); |
| 207 } | 211 } |
| 208 len -= taghdrlen + tlen; | 212 len -= taghdrlen + tlen; |
| 209 | 213 |
| 210 if (len < 0) | 214 if (len < 0) |
| 211 break; | 215 break; |
| 212 | 216 |
| 213 next = url_ftell(s->pb) + tlen; | 217 next = url_ftell(s->pb) + tlen; |
| 214 | 218 |
| 215 if (tag[0] == 'T') | 219 if (tag[0] == 'T') { |
| 216 read_ttag(s, tlen, tag); | 220 if (unsync || tunsync) { |
| 221 int i, j; |
| 222 av_fast_malloc(&buffer, &buffer_size, tlen); |
| 223 for (i = 0, j = 0; i < tlen; i++, j++) { |
| 224 buffer[j] = get_byte(s->pb); |
| 225 if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) { |
| 226 /* Unsynchronised byte, skip it */ |
| 227 j--; |
| 228 } |
| 229 } |
| 230 init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL); |
| 231 read_ttag(s, &pb, j, tag); |
| 232 } else { |
| 233 read_ttag(s, s->pb, tlen, tag); |
| 234 } |
| 235 } |
| 217 else if (!tag[0]) { | 236 else if (!tag[0]) { |
| 218 if (tag[1]) | 237 if (tag[1]) |
| 219 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); | 238 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding"); |
| 220 url_fskip(s->pb, tlen); | 239 url_fskip(s->pb, tlen); |
| 221 break; | 240 break; |
| 222 } | 241 } |
| 223 /* Skip to end of tag */ | 242 /* Skip to end of tag */ |
| 224 url_fseek(s->pb, next, SEEK_SET); | 243 url_fseek(s->pb, next, SEEK_SET); |
| 225 } | 244 } |
| 226 | 245 |
| 227 if (len > 0) { | 246 if (len > 0) { |
| 228 /* Skip padding */ | 247 /* Skip padding */ |
| 229 url_fskip(s->pb, len); | 248 url_fskip(s->pb, len); |
| 230 } | 249 } |
| 231 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip ov
er it */ | 250 if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip ov
er it */ |
| 232 url_fskip(s->pb, 10); | 251 url_fskip(s->pb, 10); |
| 252 |
| 253 av_free(buffer); |
| 233 return; | 254 return; |
| 234 | 255 |
| 235 error: | 256 error: |
| 236 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version,
reason); | 257 av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version,
reason); |
| 237 url_fskip(s->pb, len); | 258 url_fskip(s->pb, len); |
| 259 av_free(buffer); |
| 238 } | 260 } |
| 239 | 261 |
| 240 const AVMetadataConv ff_id3v2_metadata_conv[] = { | 262 const AVMetadataConv ff_id3v2_metadata_conv[] = { |
| 241 { "TALB", "album"}, | 263 { "TALB", "album"}, |
| 242 { "TAL", "album"}, | 264 { "TAL", "album"}, |
| 243 { "TCOM", "composer"}, | 265 { "TCOM", "composer"}, |
| 244 { "TCON", "genre"}, | 266 { "TCON", "genre"}, |
| 245 { "TCO", "genre"}, | 267 { "TCO", "genre"}, |
| 246 { "TCOP", "copyright"}, | 268 { "TCOP", "copyright"}, |
| 247 { "TDRL", "date"}, | 269 { "TDRL", "date"}, |
| (...skipping 21 matching lines...) Expand all Loading... |
| 269 }; | 291 }; |
| 270 | 292 |
| 271 const char ff_id3v2_tags[][4] = { | 293 const char ff_id3v2_tags[][4] = { |
| 272 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", | 294 "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", |
| 273 "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", | 295 "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", |
| 274 "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", | 296 "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", |
| 275 "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", | 297 "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", |
| 276 "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", | 298 "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", |
| 277 { 0 }, | 299 { 0 }, |
| 278 }; | 300 }; |
| OLD | NEW |