Libav

libavformat/id3v2.c

Go to the documentation of this file.
00001 /*
00002  * ID3v2 header parser
00003  * Copyright (c) 2003 Fabrice Bellard
00004  *
00005  * This file is part of FFmpeg.
00006  *
00007  * FFmpeg is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * FFmpeg is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with FFmpeg; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 #include "id3v2.h"
00023 #include "id3v1.h"
00024 #include "libavutil/avstring.h"
00025 
00026 int ff_id3v2_match(const uint8_t *buf)
00027 {
00028     return  buf[0]         ==  'I' &&
00029             buf[1]         ==  'D' &&
00030             buf[2]         ==  '3' &&
00031             buf[3]         != 0xff &&
00032             buf[4]         != 0xff &&
00033            (buf[6] & 0x80) ==    0 &&
00034            (buf[7] & 0x80) ==    0 &&
00035            (buf[8] & 0x80) ==    0 &&
00036            (buf[9] & 0x80) ==    0;
00037 }
00038 
00039 int ff_id3v2_tag_len(const uint8_t * buf)
00040 {
00041     int len = ((buf[6] & 0x7f) << 21) +
00042               ((buf[7] & 0x7f) << 14) +
00043               ((buf[8] & 0x7f) << 7) +
00044                (buf[9] & 0x7f) +
00045               ID3v2_HEADER_SIZE;
00046     if (buf[5] & 0x10)
00047         len += ID3v2_HEADER_SIZE;
00048     return len;
00049 }
00050 
00051 void ff_id3v2_read(AVFormatContext *s)
00052 {
00053     int len, ret;
00054     uint8_t buf[ID3v2_HEADER_SIZE];
00055 
00056     ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
00057     if (ret != ID3v2_HEADER_SIZE)
00058         return;
00059     if (ff_id3v2_match(buf)) {
00060         /* parse ID3v2 header */
00061         len = ((buf[6] & 0x7f) << 21) |
00062             ((buf[7] & 0x7f) << 14) |
00063             ((buf[8] & 0x7f) << 7) |
00064             (buf[9] & 0x7f);
00065         ff_id3v2_parse(s, len, buf[3], buf[5]);
00066     } else {
00067         url_fseek(s->pb, 0, SEEK_SET);
00068     }
00069 }
00070 
00071 static unsigned int get_size(ByteIOContext *s, int len)
00072 {
00073     int v = 0;
00074     while (len--)
00075         v = (v << 7) + (get_byte(s) & 0x7F);
00076     return v;
00077 }
00078 
00079 static void read_ttag(AVFormatContext *s, int taglen, const char *key)
00080 {
00081     char *q, dst[512];
00082     const char *val = NULL;
00083     int len, dstlen = sizeof(dst) - 1;
00084     unsigned genre;
00085     unsigned int (*get)(ByteIOContext*) = get_be16;
00086 
00087     dst[0] = 0;
00088     if (taglen < 1)
00089         return;
00090 
00091     taglen--; /* account for encoding type byte */
00092 
00093     switch (get_byte(s->pb)) { /* encoding type */
00094 
00095     case 0:  /* ISO-8859-1 (0 - 255 maps directly into unicode) */
00096         q = dst;
00097         while (taglen-- && q - dst < dstlen - 7) {
00098             uint8_t tmp;
00099             PUT_UTF8(get_byte(s->pb), tmp, *q++ = tmp;)
00100         }
00101         *q = 0;
00102         break;
00103 
00104     case 1:  /* UTF-16 with BOM */
00105         taglen -= 2;
00106         switch (get_be16(s->pb)) {
00107         case 0xfffe:
00108             get = get_le16;
00109         case 0xfeff:
00110             break;
00111         default:
00112             av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
00113             return;
00114         }
00115         // fall-through
00116 
00117     case 2:  /* UTF-16BE without BOM */
00118         q = dst;
00119         while (taglen > 1 && q - dst < dstlen - 7) {
00120             uint32_t ch;
00121             uint8_t tmp;
00122 
00123             GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(s->pb) : 0), break;)
00124             PUT_UTF8(ch, tmp, *q++ = tmp;)
00125         }
00126         *q = 0;
00127         break;
00128 
00129     case 3:  /* UTF-8 */
00130         len = FFMIN(taglen, dstlen);
00131         get_buffer(s->pb, dst, len);
00132         dst[len] = 0;
00133         break;
00134     default:
00135         av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
00136     }
00137 
00138     if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
00139         && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
00140         && genre <= ID3v1_GENRE_MAX)
00141         val = ff_id3v1_genre_str[genre];
00142     else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
00143         /* dst now contains two 0-terminated strings */
00144         dst[dstlen] = 0;
00145         len = strlen(dst);
00146         key = dst;
00147         val = dst + FFMIN(len + 1, dstlen);
00148     }
00149     else if (*dst)
00150         val = dst;
00151 
00152     if (val)
00153         av_metadata_set2(&s->metadata, key, val, 0);
00154 }
00155 
00156 void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
00157 {
00158     int isv34, tlen;
00159     char tag[5];
00160     int64_t next;
00161     int taghdrlen;
00162     const char *reason;
00163 
00164     switch (version) {
00165     case 2:
00166         if (flags & 0x40) {
00167             reason = "compression";
00168             goto error;
00169         }
00170         isv34 = 0;
00171         taghdrlen = 6;
00172         break;
00173 
00174     case 3:
00175     case 4:
00176         isv34 = 1;
00177         taghdrlen = 10;
00178         break;
00179 
00180     default:
00181         reason = "version";
00182         goto error;
00183     }
00184 
00185     if (flags & 0x80) {
00186         reason = "unsynchronization";
00187         goto error;
00188     }
00189 
00190     if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
00191         int extlen = get_size(s->pb, 4);
00192         if (version == 4)
00193             extlen -= 4;     // in v2.4 the length includes the length field we just read
00194 
00195         if (extlen < 0) {
00196             reason = "invalid extended header length";
00197             goto error;
00198         }
00199         url_fskip(s->pb, extlen);
00200     }
00201 
00202     while (len >= taghdrlen) {
00203         if (isv34) {
00204             get_buffer(s->pb, tag, 4);
00205             tag[4] = 0;
00206             if(version==3){
00207                 tlen = get_be32(s->pb);
00208             }else
00209                 tlen = get_size(s->pb, 4);
00210             get_be16(s->pb); /* flags */
00211         } else {
00212             get_buffer(s->pb, tag, 3);
00213             tag[3] = 0;
00214             tlen = get_be24(s->pb);
00215         }
00216         len -= taghdrlen + tlen;
00217 
00218         if (len < 0)
00219             break;
00220 
00221         next = url_ftell(s->pb) + tlen;
00222 
00223         if (tag[0] == 'T')
00224             read_ttag(s, tlen, tag);
00225         else if (!tag[0]) {
00226             if (tag[1])
00227                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
00228             url_fskip(s->pb, len);
00229             break;
00230         }
00231         /* Skip to end of tag */
00232         url_fseek(s->pb, next, SEEK_SET);
00233     }
00234 
00235     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
00236         url_fskip(s->pb, 10);
00237     return;
00238 
00239   error:
00240     av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
00241     url_fskip(s->pb, len);
00242 }
00243 
00244 const AVMetadataConv ff_id3v2_metadata_conv[] = {
00245     { "TALB", "album"},
00246     { "TAL",  "album"},
00247     { "TCOM", "composer"},
00248     { "TCON", "genre"},
00249     { "TCO",  "genre"},
00250     { "TCOP", "copyright"},
00251     { "TDRL", "date"},
00252     { "TDRC", "date"},
00253     { "TENC", "encoded_by"},
00254     { "TEN",  "encoded_by"},
00255     { "TIT2", "title"},
00256     { "TT2",  "title"},
00257     { "TLAN", "language"},
00258     { "TPE1", "artist"},
00259     { "TP1",  "artist"},
00260     { "TPE2", "album_artist"},
00261     { "TP2",  "album_artist"},
00262     { "TPE3", "performer"},
00263     { "TP3",  "performer"},
00264     { "TPOS", "disc"},
00265     { "TPUB", "publisher"},
00266     { "TRCK", "track"},
00267     { "TRK",  "track"},
00268     { "TSOA", "album-sort"},
00269     { "TSOP", "artist-sort"},
00270     { "TSOT", "title-sort"},
00271     { "TSSE", "encoder"},
00272     { 0 }
00273 };
00274 
00275 const char ff_id3v2_tags[][4] = {
00276    "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC",
00277    "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3",
00278    "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY",
00279    "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB",
00280    "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST",
00281    { 0 },
00282 };