• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavformat/id3v2.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2003 Fabrice Bellard
00003  *
00004  * This file is part of FFmpeg.
00005  *
00006  * FFmpeg is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * FFmpeg is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with FFmpeg; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 
00029 #include "id3v2.h"
00030 #include "id3v1.h"
00031 #include "libavutil/avstring.h"
00032 #include "libavutil/intreadwrite.h"
00033 #include "libavutil/dict.h"
00034 #include "avio_internal.h"
00035 
00036 int ff_id3v2_match(const uint8_t *buf, const char * magic)
00037 {
00038     return  buf[0]         == magic[0] &&
00039             buf[1]         == magic[1] &&
00040             buf[2]         == magic[2] &&
00041             buf[3]         != 0xff &&
00042             buf[4]         != 0xff &&
00043            (buf[6] & 0x80) ==    0 &&
00044            (buf[7] & 0x80) ==    0 &&
00045            (buf[8] & 0x80) ==    0 &&
00046            (buf[9] & 0x80) ==    0;
00047 }
00048 
00049 int ff_id3v2_tag_len(const uint8_t * buf)
00050 {
00051     int len = ((buf[6] & 0x7f) << 21) +
00052               ((buf[7] & 0x7f) << 14) +
00053               ((buf[8] & 0x7f) << 7) +
00054                (buf[9] & 0x7f) +
00055               ID3v2_HEADER_SIZE;
00056     if (buf[5] & 0x10)
00057         len += ID3v2_HEADER_SIZE;
00058     return len;
00059 }
00060 
00061 static unsigned int get_size(AVIOContext *s, int len)
00062 {
00063     int v = 0;
00064     while (len--)
00065         v = (v << 7) + (avio_r8(s) & 0x7F);
00066     return v;
00067 }
00068 
00069 static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
00070 {
00071     char *q, dst[512];
00072     const char *val = NULL;
00073     int len, dstlen = sizeof(dst) - 1;
00074     unsigned genre;
00075     unsigned int (*get)(AVIOContext*) = avio_rb16;
00076 
00077     dst[0] = 0;
00078     if (taglen < 1)
00079         return;
00080 
00081     taglen--; /* account for encoding type byte */
00082 
00083     switch (avio_r8(pb)) { /* encoding type */
00084 
00085     case ID3v2_ENCODING_ISO8859:
00086         q = dst;
00087         while (taglen-- && q - dst < dstlen - 7) {
00088             uint8_t tmp;
00089             PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
00090         }
00091         *q = 0;
00092         break;
00093 
00094     case ID3v2_ENCODING_UTF16BOM:
00095         taglen -= 2;
00096         switch (avio_rb16(pb)) {
00097         case 0xfffe:
00098             get = avio_rl16;
00099         case 0xfeff:
00100             break;
00101         default:
00102             av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
00103             return;
00104         }
00105         // fall-through
00106 
00107     case ID3v2_ENCODING_UTF16BE:
00108         q = dst;
00109         while (taglen > 1 && q - dst < dstlen - 7) {
00110             uint32_t ch;
00111             uint8_t tmp;
00112 
00113             GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
00114             PUT_UTF8(ch, tmp, *q++ = tmp;)
00115         }
00116         *q = 0;
00117         break;
00118 
00119     case ID3v2_ENCODING_UTF8:
00120         len = FFMIN(taglen, dstlen);
00121         avio_read(pb, dst, len);
00122         dst[len] = 0;
00123         break;
00124     default:
00125         av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
00126     }
00127 
00128     if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
00129         && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
00130         && genre <= ID3v1_GENRE_MAX)
00131         val = ff_id3v1_genre_str[genre];
00132     else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
00133         /* dst now contains two 0-terminated strings */
00134         dst[dstlen] = 0;
00135         len = strlen(dst);
00136         key = dst;
00137         val = dst + FFMIN(len + 1, dstlen);
00138     }
00139     else if (*dst)
00140         val = dst;
00141 
00142     if (val)
00143         av_dict_set(&s->metadata, key, val, AV_DICT_DONT_OVERWRITE);
00144 }
00145 
00146 static int is_number(const char *str)
00147 {
00148     while (*str >= '0' && *str <= '9') str++;
00149     return !*str;
00150 }
00151 
00152 static AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag)
00153 {
00154     AVDictionaryEntry *t;
00155     if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
00156         strlen(t->value) == 4 && is_number(t->value))
00157         return t;
00158     return NULL;
00159 }
00160 
00161 static void merge_date(AVDictionary **m)
00162 {
00163     AVDictionaryEntry *t;
00164     char date[17] = {0};      // YYYY-MM-DD hh:mm
00165 
00166     if (!(t = get_date_tag(*m, "TYER")) &&
00167         !(t = get_date_tag(*m, "TYE")))
00168         return;
00169     av_strlcpy(date, t->value, 5);
00170     av_dict_set(m, "TYER", NULL, 0);
00171     av_dict_set(m, "TYE",  NULL, 0);
00172 
00173     if (!(t = get_date_tag(*m, "TDAT")) &&
00174         !(t = get_date_tag(*m, "TDA")))
00175         goto finish;
00176     snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
00177     av_dict_set(m, "TDAT", NULL, 0);
00178     av_dict_set(m, "TDA",  NULL, 0);
00179 
00180     if (!(t = get_date_tag(*m, "TIME")) &&
00181         !(t = get_date_tag(*m, "TIM")))
00182         goto finish;
00183     snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
00184     av_dict_set(m, "TIME", NULL, 0);
00185     av_dict_set(m, "TIM",  NULL, 0);
00186 
00187 finish:
00188     if (date[0])
00189         av_dict_set(m, "date", date, 0);
00190 }
00191 
00192 static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
00193 {
00194     int isv34, unsync;
00195     unsigned tlen;
00196     char tag[5];
00197     int64_t next, end = avio_tell(s->pb) + len;
00198     int taghdrlen;
00199     const char *reason = NULL;
00200     AVIOContext pb;
00201     unsigned char *buffer = NULL;
00202     int buffer_size = 0;
00203 
00204     switch (version) {
00205     case 2:
00206         if (flags & 0x40) {
00207             reason = "compression";
00208             goto error;
00209         }
00210         isv34 = 0;
00211         taghdrlen = 6;
00212         break;
00213 
00214     case 3:
00215     case 4:
00216         isv34 = 1;
00217         taghdrlen = 10;
00218         break;
00219 
00220     default:
00221         reason = "version";
00222         goto error;
00223     }
00224 
00225     unsync = flags & 0x80;
00226 
00227     if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
00228         int extlen = get_size(s->pb, 4);
00229         if (version == 4)
00230             extlen -= 4;     // in v2.4 the length includes the length field we just read
00231 
00232         if (extlen < 0) {
00233             reason = "invalid extended header length";
00234             goto error;
00235         }
00236         avio_skip(s->pb, extlen);
00237     }
00238 
00239     while (len >= taghdrlen) {
00240         unsigned int tflags = 0;
00241         int tunsync = 0;
00242 
00243         if (isv34) {
00244             avio_read(s->pb, tag, 4);
00245             tag[4] = 0;
00246             if(version==3){
00247                 tlen = avio_rb32(s->pb);
00248             }else
00249                 tlen = get_size(s->pb, 4);
00250             tflags = avio_rb16(s->pb);
00251             tunsync = tflags & ID3v2_FLAG_UNSYNCH;
00252         } else {
00253             avio_read(s->pb, tag, 3);
00254             tag[3] = 0;
00255             tlen = avio_rb24(s->pb);
00256         }
00257         if (tlen > (1<<28) || !tlen)
00258             break;
00259         len -= taghdrlen + tlen;
00260 
00261         if (len < 0)
00262             break;
00263 
00264         next = avio_tell(s->pb) + tlen;
00265 
00266         if (tflags & ID3v2_FLAG_DATALEN) {
00267             if (tlen < 4)
00268                 break;
00269             avio_rb32(s->pb);
00270             tlen -= 4;
00271         }
00272 
00273         if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
00274             av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
00275             avio_skip(s->pb, tlen);
00276         } else if (tag[0] == 'T') {
00277             if (unsync || tunsync) {
00278                 int i, j;
00279                 av_fast_malloc(&buffer, &buffer_size, tlen);
00280                 if (!buffer) {
00281                     av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
00282                     goto seek;
00283                 }
00284                 for (i = 0, j = 0; i < tlen; i++, j++) {
00285                     buffer[j] = avio_r8(s->pb);
00286                     if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
00287                         /* Unsynchronised byte, skip it */
00288                         j--;
00289                     }
00290                 }
00291                 ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
00292                 read_ttag(s, &pb, j, tag);
00293             } else {
00294                 read_ttag(s, s->pb, tlen, tag);
00295             }
00296         }
00297         else if (!tag[0]) {
00298             if (tag[1])
00299                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
00300             avio_skip(s->pb, tlen);
00301             break;
00302         }
00303         /* Skip to end of tag */
00304 seek:
00305         avio_seek(s->pb, next, SEEK_SET);
00306     }
00307 
00308     if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
00309         end += 10;
00310 
00311   error:
00312     if (reason)
00313         av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
00314     avio_seek(s->pb, end, SEEK_SET);
00315     av_free(buffer);
00316     return;
00317 }
00318 
00319 void ff_id3v2_read(AVFormatContext *s, const char *magic)
00320 {
00321     int len, ret;
00322     uint8_t buf[ID3v2_HEADER_SIZE];
00323     int     found_header;
00324     int64_t off;
00325 
00326     do {
00327         /* save the current offset in case there's nothing to read/skip */
00328         off = avio_tell(s->pb);
00329         ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
00330         if (ret != ID3v2_HEADER_SIZE)
00331             break;
00332             found_header = ff_id3v2_match(buf, magic);
00333             if (found_header) {
00334             /* parse ID3v2 header */
00335             len = ((buf[6] & 0x7f) << 21) |
00336                   ((buf[7] & 0x7f) << 14) |
00337                   ((buf[8] & 0x7f) << 7) |
00338                    (buf[9] & 0x7f);
00339             ff_id3v2_parse(s, len, buf[3], buf[5]);
00340         } else {
00341             avio_seek(s->pb, off, SEEK_SET);
00342         }
00343     } while (found_header);
00344     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
00345     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
00346     ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
00347     merge_date(&s->metadata);
00348 }
00349 
00350 const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
00351     { "TALB", "album"},
00352     { "TCOM", "composer"},
00353     { "TCON", "genre"},
00354     { "TCOP", "copyright"},
00355     { "TENC", "encoded_by"},
00356     { "TIT2", "title"},
00357     { "TLAN", "language"},
00358     { "TPE1", "artist"},
00359     { "TPE2", "album_artist"},
00360     { "TPE3", "performer"},
00361     { "TPOS", "disc"},
00362     { "TPUB", "publisher"},
00363     { "TRCK", "track"},
00364     { "TSSE", "encoder"},
00365     { 0 }
00366 };
00367 
00368 const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
00369     { "TDRL", "date"},
00370     { "TDRC", "date"},
00371     { "TDEN", "creation_time"},
00372     { "TSOA", "album-sort"},
00373     { "TSOP", "artist-sort"},
00374     { "TSOT", "title-sort"},
00375     { 0 }
00376 };
00377 
00378 const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
00379     { "TAL",  "album"},
00380     { "TCO",  "genre"},
00381     { "TT2",  "title"},
00382     { "TEN",  "encoded_by"},
00383     { "TP1",  "artist"},
00384     { "TP2",  "album_artist"},
00385     { "TP3",  "performer"},
00386     { "TRK",  "track"},
00387     { 0 }
00388 };
00389 
00390 
00391 const char ff_id3v2_tags[][4] = {
00392    "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
00393    "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
00394    "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
00395    "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
00396    { 0 },
00397 };
00398 
00399 const char ff_id3v2_4_tags[][4] = {
00400    "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
00401    "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
00402    { 0 },
00403 };
00404 
00405 const char ff_id3v2_3_tags[][4] = {
00406    "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
00407    { 0 },
00408 };

Generated on Fri Feb 22 2013 07:24:31 for FFmpeg by  doxygen 1.7.1