LCOV - code coverage report
Current view: top level - libavformat - id3v2.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 409 669 61.1 %
Date: 2018-05-20 11:54:08 Functions: 27 32 84.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2003 Fabrice Bellard
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public
       8             :  * License as published by the Free Software Foundation; either
       9             :  * version 2.1 of the License, or (at your option) any later version.
      10             :  *
      11             :  * FFmpeg is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * ID3v2 header parser
      24             :  *
      25             :  * Specifications available at:
      26             :  * http://id3.org/Developer_Information
      27             :  */
      28             : 
      29             : #include "config.h"
      30             : 
      31             : #if CONFIG_ZLIB
      32             : #include <zlib.h>
      33             : #endif
      34             : 
      35             : #include "libavutil/avstring.h"
      36             : #include "libavutil/bprint.h"
      37             : #include "libavutil/dict.h"
      38             : #include "libavutil/intreadwrite.h"
      39             : #include "avio_internal.h"
      40             : #include "internal.h"
      41             : #include "id3v1.h"
      42             : #include "id3v2.h"
      43             : 
      44             : const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
      45             :     { "TALB", "album"        },
      46             :     { "TCOM", "composer"     },
      47             :     { "TCON", "genre"        },
      48             :     { "TCOP", "copyright"    },
      49             :     { "TENC", "encoded_by"   },
      50             :     { "TIT2", "title"        },
      51             :     { "TLAN", "language"     },
      52             :     { "TPE1", "artist"       },
      53             :     { "TPE2", "album_artist" },
      54             :     { "TPE3", "performer"    },
      55             :     { "TPOS", "disc"         },
      56             :     { "TPUB", "publisher"    },
      57             :     { "TRCK", "track"        },
      58             :     { "TSSE", "encoder"      },
      59             :     { "USLT", "lyrics"       },
      60             :     { 0 }
      61             : };
      62             : 
      63             : const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
      64             :     { "TCMP", "compilation"   },
      65             :     { "TDRC", "date"          },
      66             :     { "TDRL", "date"          },
      67             :     { "TDEN", "creation_time" },
      68             :     { "TSOA", "album-sort"    },
      69             :     { "TSOP", "artist-sort"   },
      70             :     { "TSOT", "title-sort"    },
      71             :     { 0 }
      72             : };
      73             : 
      74             : static const AVMetadataConv id3v2_2_metadata_conv[] = {
      75             :     { "TAL", "album"        },
      76             :     { "TCO", "genre"        },
      77             :     { "TCP", "compilation"  },
      78             :     { "TT2", "title"        },
      79             :     { "TEN", "encoded_by"   },
      80             :     { "TP1", "artist"       },
      81             :     { "TP2", "album_artist" },
      82             :     { "TP3", "performer"    },
      83             :     { "TRK", "track"        },
      84             :     { 0 }
      85             : };
      86             : 
      87             : const char ff_id3v2_tags[][4] = {
      88             :     "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
      89             :     "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
      90             :     "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
      91             :     "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
      92             :     { 0 },
      93             : };
      94             : 
      95             : const char ff_id3v2_4_tags[][4] = {
      96             :     "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
      97             :     "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
      98             :     { 0 },
      99             : };
     100             : 
     101             : const char ff_id3v2_3_tags[][4] = {
     102             :     "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
     103             :     { 0 },
     104             : };
     105             : 
     106             : const char * const ff_id3v2_picture_types[21] = {
     107             :     "Other",
     108             :     "32x32 pixels 'file icon'",
     109             :     "Other file icon",
     110             :     "Cover (front)",
     111             :     "Cover (back)",
     112             :     "Leaflet page",
     113             :     "Media (e.g. label side of CD)",
     114             :     "Lead artist/lead performer/soloist",
     115             :     "Artist/performer",
     116             :     "Conductor",
     117             :     "Band/Orchestra",
     118             :     "Composer",
     119             :     "Lyricist/text writer",
     120             :     "Recording Location",
     121             :     "During recording",
     122             :     "During performance",
     123             :     "Movie/video screen capture",
     124             :     "A bright coloured fish",
     125             :     "Illustration",
     126             :     "Band/artist logotype",
     127             :     "Publisher/Studio logotype",
     128             : };
     129             : 
     130             : const CodecMime ff_id3v2_mime_tags[] = {
     131             :     { "image/gif",  AV_CODEC_ID_GIF   },
     132             :     { "image/jpeg", AV_CODEC_ID_MJPEG },
     133             :     { "image/jpg",  AV_CODEC_ID_MJPEG },
     134             :     { "image/png",  AV_CODEC_ID_PNG   },
     135             :     { "image/tiff", AV_CODEC_ID_TIFF  },
     136             :     { "image/bmp",  AV_CODEC_ID_BMP   },
     137             :     { "JPG",        AV_CODEC_ID_MJPEG }, /* ID3v2.2  */
     138             :     { "PNG",        AV_CODEC_ID_PNG   }, /* ID3v2.2  */
     139             :     { "",           AV_CODEC_ID_NONE  },
     140             : };
     141             : 
     142       28139 : int ff_id3v2_match(const uint8_t *buf, const char *magic)
     143             : {
     144       28336 :     return  buf[0]         == magic[0] &&
     145         258 :             buf[1]         == magic[1] &&
     146         122 :             buf[2]         == magic[2] &&
     147         122 :             buf[3]         != 0xff     &&
     148         122 :             buf[4]         != 0xff     &&
     149         122 :            (buf[6] & 0x80) == 0        &&
     150         122 :            (buf[7] & 0x80) == 0        &&
     151       28261 :            (buf[8] & 0x80) == 0        &&
     152          61 :            (buf[9] & 0x80) == 0;
     153             : }
     154             : 
     155          40 : int ff_id3v2_tag_len(const uint8_t *buf)
     156             : {
     157         120 :     int len = ((buf[6] & 0x7f) << 21) +
     158          80 :               ((buf[7] & 0x7f) << 14) +
     159          80 :               ((buf[8] & 0x7f) << 7) +
     160          40 :               (buf[9] & 0x7f) +
     161             :               ID3v2_HEADER_SIZE;
     162          40 :     if (buf[5] & 0x10)
     163           0 :         len += ID3v2_HEADER_SIZE;
     164          40 :     return len;
     165             : }
     166             : 
     167           0 : static unsigned int get_size(AVIOContext *s, int len)
     168             : {
     169           0 :     int v = 0;
     170           0 :     while (len--)
     171           0 :         v = (v << 7) + (avio_r8(s) & 0x7F);
     172           0 :     return v;
     173             : }
     174             : 
     175           1 : static unsigned int size_to_syncsafe(unsigned int size)
     176             : {
     177           2 :     return (((size) & (0x7f <<  0)) >> 0) +
     178           2 :            (((size) & (0x7f <<  8)) >> 1) +
     179           2 :            (((size) & (0x7f << 16)) >> 2) +
     180           1 :            (((size) & (0x7f << 24)) >> 3);
     181             : }
     182             : 
     183             : /* No real verification, only check that the tag consists of
     184             :  * a combination of capital alpha-numerical characters */
     185           0 : static int is_tag(const char *buf, unsigned int len)
     186             : {
     187           0 :     if (!len)
     188           0 :         return 0;
     189             : 
     190           0 :     while (len--)
     191           0 :         if ((buf[len] < 'A' ||
     192           0 :              buf[len] > 'Z') &&
     193           0 :             (buf[len] < '0' ||
     194           0 :              buf[len] > '9'))
     195           0 :             return 0;
     196             : 
     197           0 :     return 1;
     198             : }
     199             : 
     200             : /**
     201             :  * Return 1 if the tag of length len at the given offset is valid, 0 if not, -1 on error
     202             :  */
     203           0 : static int check_tag(AVIOContext *s, int offset, unsigned int len)
     204             : {
     205             :     char tag[4];
     206             : 
     207           0 :     if (len > 4 ||
     208           0 :         avio_seek(s, offset, SEEK_SET) < 0 ||
     209           0 :         avio_read(s, tag, len) < (int)len)
     210           0 :         return -1;
     211           0 :     else if (!AV_RB32(tag) || is_tag(tag, len))
     212           0 :         return 1;
     213             : 
     214           0 :     return 0;
     215             : }
     216             : 
     217             : /**
     218             :  * Free GEOB type extra metadata.
     219             :  */
     220           2 : static void free_geobtag(void *obj)
     221             : {
     222           2 :     ID3v2ExtraMetaGEOB *geob = obj;
     223           2 :     av_freep(&geob->mime_type);
     224           2 :     av_freep(&geob->file_name);
     225           2 :     av_freep(&geob->description);
     226           2 :     av_freep(&geob->data);
     227           2 :     av_free(geob);
     228           2 : }
     229             : 
     230             : /**
     231             :  * Decode characters to UTF-8 according to encoding type. The decoded buffer is
     232             :  * always null terminated. Stop reading when either *maxread bytes are read from
     233             :  * pb or U+0000 character is found.
     234             :  *
     235             :  * @param dst Pointer where the address of the buffer with the decoded bytes is
     236             :  * stored. Buffer must be freed by caller.
     237             :  * @param maxread Pointer to maximum number of characters to read from the
     238             :  * AVIOContext. After execution the value is decremented by the number of bytes
     239             :  * actually read.
     240             :  * @returns 0 if no error occurred, dst is uninitialized on error
     241             :  */
     242          87 : static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
     243             :                       uint8_t **dst, int *maxread)
     244             : {
     245             :     int ret;
     246             :     uint8_t tmp;
     247          87 :     uint32_t ch = 1;
     248          87 :     int left = *maxread;
     249          87 :     unsigned int (*get)(AVIOContext*) = avio_rb16;
     250             :     AVIOContext *dynbuf;
     251             : 
     252          87 :     if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) {
     253           0 :         av_log(s, AV_LOG_ERROR, "Error opening memory stream\n");
     254           0 :         return ret;
     255             :     }
     256             : 
     257          87 :     switch (encoding) {
     258          43 :     case ID3v2_ENCODING_ISO8859:
     259         596 :         while (left && ch) {
     260         510 :             ch = avio_r8(pb);
     261         510 :             PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
     262         510 :             left--;
     263             :         }
     264          43 :         break;
     265             : 
     266           0 :     case ID3v2_ENCODING_UTF16BOM:
     267           0 :         if ((left -= 2) < 0) {
     268           0 :             av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n");
     269           0 :             ffio_free_dyn_buf(&dynbuf);
     270           0 :             *dst = NULL;
     271           0 :             return AVERROR_INVALIDDATA;
     272             :         }
     273           0 :         switch (avio_rb16(pb)) {
     274           0 :         case 0xfffe:
     275           0 :             get = avio_rl16;
     276           0 :         case 0xfeff:
     277           0 :             break;
     278           0 :         default:
     279           0 :             av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n");
     280           0 :             ffio_free_dyn_buf(&dynbuf);
     281           0 :             *dst = NULL;
     282           0 :             *maxread = left;
     283           0 :             return AVERROR_INVALIDDATA;
     284             :         }
     285             :         // fall-through
     286             : 
     287          18 :     case ID3v2_ENCODING_UTF16BE:
     288         161 :         while ((left > 1) && ch) {
     289         125 :             GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
     290         125 :             PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
     291             :         }
     292          18 :         if (left < 0)
     293           0 :             left += 2;  /* did not read last char from pb */
     294          18 :         break;
     295             : 
     296          26 :     case ID3v2_ENCODING_UTF8:
     297         336 :         while (left && ch) {
     298         284 :             ch = avio_r8(pb);
     299         284 :             avio_w8(dynbuf, ch);
     300         284 :             left--;
     301             :         }
     302          26 :         break;
     303           0 :     default:
     304           0 :         av_log(s, AV_LOG_WARNING, "Unknown encoding\n");
     305             :     }
     306             : 
     307          87 :     if (ch)
     308          22 :         avio_w8(dynbuf, 0);
     309             : 
     310          87 :     avio_close_dyn_buf(dynbuf, dst);
     311          87 :     *maxread = left;
     312             : 
     313          87 :     return 0;
     314             : }
     315             : 
     316             : /**
     317             :  * Parse a text tag.
     318             :  */
     319          53 : static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen,
     320             :                       AVDictionary **metadata, const char *key)
     321             : {
     322             :     uint8_t *dst;
     323          53 :     int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
     324             :     unsigned genre;
     325             : 
     326          53 :     if (taglen < 1)
     327           0 :         return;
     328             : 
     329          53 :     encoding = avio_r8(pb);
     330          53 :     taglen--; /* account for encoding type byte */
     331             : 
     332          53 :     if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
     333           0 :         av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
     334           0 :         return;
     335             :     }
     336             : 
     337          57 :     if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))                         &&
     338           8 :         (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) &&
     339           0 :         genre <= ID3v1_GENRE_MAX) {
     340           0 :         av_freep(&dst);
     341           0 :         dst = av_strdup(ff_id3v1_genre_str[genre]);
     342          53 :     } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
     343             :         /* dst now contains the key, need to get value */
     344           3 :         key = dst;
     345           3 :         if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
     346           0 :             av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
     347           0 :             av_freep(&key);
     348           0 :             return;
     349             :         }
     350           3 :         dict_flags |= AV_DICT_DONT_STRDUP_KEY;
     351          50 :     } else if (!*dst)
     352           4 :         av_freep(&dst);
     353             : 
     354          53 :     if (dst)
     355          49 :         av_dict_set(metadata, key, dst, dict_flags);
     356             : }
     357             : 
     358           1 : static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen,
     359             :                       AVDictionary **metadata)
     360             : {
     361             :     uint8_t lang[4];
     362           1 :     uint8_t *descriptor = NULL; // 'Content descriptor'
     363           1 :     uint8_t *text = NULL;
     364           1 :     char *key = NULL;
     365             :     int encoding;
     366           1 :     int ok = 0;
     367             : 
     368           1 :     if (taglen < 1)
     369           0 :         goto error;
     370             : 
     371           1 :     encoding = avio_r8(pb);
     372           1 :     taglen--;
     373             : 
     374           1 :     if (avio_read(pb, lang, 3) < 3)
     375           0 :         goto error;
     376           1 :     lang[3] = '\0';
     377           1 :     taglen -= 3;
     378             : 
     379           1 :     if (decode_str(s, pb, encoding, &descriptor, &taglen) < 0)
     380           0 :         goto error;
     381             : 
     382           1 :     if (decode_str(s, pb, encoding, &text, &taglen) < 0)
     383           0 :         goto error;
     384             : 
     385             :     // FFmpeg does not support hierarchical metadata, so concatenate the keys.
     386           1 :     key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "",
     387           1 :                                        descriptor[0] ? "-" : "",
     388             :                                        lang);
     389           1 :     if (!key)
     390           0 :         goto error;
     391             : 
     392           1 :     av_dict_set(metadata, key, text, 0);
     393             : 
     394           1 :     ok = 1;
     395           1 : error:
     396           1 :     if (!ok)
     397           0 :         av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n");
     398           1 :     av_free(descriptor);
     399           1 :     av_free(text);
     400           1 :     av_free(key);
     401           1 : }
     402             : 
     403             : /**
     404             :  * Parse a comment tag.
     405             :  */
     406           7 : static void read_comment(AVFormatContext *s, AVIOContext *pb, int taglen,
     407             :                       AVDictionary **metadata)
     408             : {
     409           7 :     const char *key = "comment";
     410             :     uint8_t *dst;
     411           7 :     int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL;
     412             :     av_unused int language;
     413             : 
     414           7 :     if (taglen < 4)
     415           0 :         return;
     416             : 
     417           7 :     encoding = avio_r8(pb);
     418           7 :     language = avio_rl24(pb);
     419           7 :     taglen -= 4;
     420             : 
     421           7 :     if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
     422           0 :         av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
     423           0 :         return;
     424             :     }
     425             : 
     426           7 :     if (dst && !*dst)
     427           2 :         av_freep(&dst);
     428             : 
     429           7 :     if (dst) {
     430           5 :         key = (const char *) dst;
     431           5 :         dict_flags |= AV_DICT_DONT_STRDUP_KEY;
     432             :     }
     433             : 
     434           7 :     if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
     435           0 :         av_log(s, AV_LOG_ERROR, "Error reading comment frame, skipped\n");
     436           0 :         if (dict_flags & AV_DICT_DONT_STRDUP_KEY)
     437           0 :             av_freep((void*)&key);
     438           0 :         return;
     439             :     }
     440             : 
     441           7 :     if (dst)
     442           7 :         av_dict_set(metadata, key, (const char *) dst, dict_flags);
     443             : }
     444             : 
     445             : /**
     446             :  * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
     447             :  */
     448           2 : static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
     449             :                          const char *tag, ID3v2ExtraMeta **extra_meta,
     450             :                          int isv34)
     451             : {
     452           2 :     ID3v2ExtraMetaGEOB *geob_data = NULL;
     453           2 :     ID3v2ExtraMeta *new_extra     = NULL;
     454             :     char encoding;
     455             :     unsigned int len;
     456             : 
     457           2 :     if (taglen < 1)
     458           0 :         return;
     459             : 
     460           2 :     geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
     461           2 :     if (!geob_data) {
     462           0 :         av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
     463             :                sizeof(ID3v2ExtraMetaGEOB));
     464           0 :         return;
     465             :     }
     466             : 
     467           2 :     new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
     468           2 :     if (!new_extra) {
     469           0 :         av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
     470             :                sizeof(ID3v2ExtraMeta));
     471           0 :         goto fail;
     472             :     }
     473             : 
     474             :     /* read encoding type byte */
     475           2 :     encoding = avio_r8(pb);
     476           2 :     taglen--;
     477             : 
     478             :     /* read MIME type (always ISO-8859) */
     479           2 :     if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type,
     480           2 :                    &taglen) < 0 ||
     481           2 :         taglen <= 0)
     482             :         goto fail;
     483             : 
     484             :     /* read file name */
     485           4 :     if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0 ||
     486           2 :         taglen <= 0)
     487             :         goto fail;
     488             : 
     489             :     /* read content description */
     490           4 :     if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0 ||
     491           2 :         taglen < 0)
     492             :         goto fail;
     493             : 
     494           2 :     if (taglen) {
     495             :         /* save encapsulated binary data */
     496           2 :         geob_data->data = av_malloc(taglen);
     497           2 :         if (!geob_data->data) {
     498           0 :             av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen);
     499           0 :             goto fail;
     500             :         }
     501           2 :         if ((len = avio_read(pb, geob_data->data, taglen)) < taglen)
     502           0 :             av_log(s, AV_LOG_WARNING,
     503             :                    "Error reading GEOB frame, data truncated.\n");
     504           2 :         geob_data->datasize = len;
     505             :     } else {
     506           0 :         geob_data->data     = NULL;
     507           0 :         geob_data->datasize = 0;
     508             :     }
     509             : 
     510             :     /* add data to the list */
     511           2 :     new_extra->tag  = "GEOB";
     512           2 :     new_extra->data = geob_data;
     513           2 :     new_extra->next = *extra_meta;
     514           2 :     *extra_meta     = new_extra;
     515             : 
     516           2 :     return;
     517             : 
     518           0 : fail:
     519           0 :     av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag);
     520           0 :     free_geobtag(geob_data);
     521           0 :     av_free(new_extra);
     522           0 :     return;
     523             : }
     524             : 
     525           1 : static int is_number(const char *str)
     526             : {
     527           6 :     while (*str >= '0' && *str <= '9')
     528           4 :         str++;
     529           1 :     return !*str;
     530             : }
     531             : 
     532        7445 : static AVDictionaryEntry *get_date_tag(AVDictionary *m, const char *tag)
     533             : {
     534             :     AVDictionaryEntry *t;
     535        7446 :     if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) &&
     536           2 :         strlen(t->value) == 4 && is_number(t->value))
     537           1 :         return t;
     538        7444 :     return NULL;
     539             : }
     540             : 
     541        3722 : static void merge_date(AVDictionary **m)
     542             : {
     543             :     AVDictionaryEntry *t;
     544        3722 :     char date[17] = { 0 };      // YYYY-MM-DD hh:mm
     545             : 
     546        7443 :     if (!(t = get_date_tag(*m, "TYER")) &&
     547        3721 :         !(t = get_date_tag(*m, "TYE")))
     548        3721 :         return;
     549           1 :     av_strlcpy(date, t->value, 5);
     550           1 :     av_dict_set(m, "TYER", NULL, 0);
     551           1 :     av_dict_set(m, "TYE", NULL, 0);
     552             : 
     553           2 :     if (!(t = get_date_tag(*m, "TDAT")) &&
     554           1 :         !(t = get_date_tag(*m, "TDA")))
     555           1 :         goto finish;
     556           0 :     snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
     557           0 :     av_dict_set(m, "TDAT", NULL, 0);
     558           0 :     av_dict_set(m, "TDA", NULL, 0);
     559             : 
     560           0 :     if (!(t = get_date_tag(*m, "TIME")) &&
     561           0 :         !(t = get_date_tag(*m, "TIM")))
     562           0 :         goto finish;
     563           0 :     snprintf(date + 10, sizeof(date) - 10,
     564           0 :              " %.2s:%.2s", t->value, t->value + 2);
     565           0 :     av_dict_set(m, "TIME", NULL, 0);
     566           0 :     av_dict_set(m, "TIM", NULL, 0);
     567             : 
     568           1 : finish:
     569           1 :     if (date[0])
     570           1 :         av_dict_set(m, "date", date, 0);
     571             : }
     572             : 
     573           2 : static void free_apic(void *obj)
     574             : {
     575           2 :     ID3v2ExtraMetaAPIC *apic = obj;
     576           2 :     av_buffer_unref(&apic->buf);
     577           2 :     av_freep(&apic->description);
     578           2 :     av_freep(&apic);
     579           2 : }
     580             : 
     581           2 : static void rstrip_spaces(char *buf)
     582             : {
     583           2 :     size_t len = strlen(buf);
     584           4 :     while (len > 0 && buf[len - 1] == ' ')
     585           0 :         buf[--len] = 0;
     586           2 : }
     587             : 
     588           2 : static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
     589             :                       const char *tag, ID3v2ExtraMeta **extra_meta,
     590             :                       int isv34)
     591             : {
     592             :     int enc, pic_type;
     593             :     char mimetype[64];
     594           2 :     const CodecMime *mime     = ff_id3v2_mime_tags;
     595           2 :     enum AVCodecID id         = AV_CODEC_ID_NONE;
     596           2 :     ID3v2ExtraMetaAPIC *apic  = NULL;
     597           2 :     ID3v2ExtraMeta *new_extra = NULL;
     598           2 :     int64_t end               = avio_tell(pb) + taglen;
     599             : 
     600           2 :     if (taglen <= 4 || (!isv34 && taglen <= 6))
     601             :         goto fail;
     602             : 
     603           2 :     new_extra = av_mallocz(sizeof(*new_extra));
     604           2 :     apic      = av_mallocz(sizeof(*apic));
     605           4 :     if (!new_extra || !apic)
     606             :         goto fail;
     607             : 
     608           2 :     enc = avio_r8(pb);
     609           2 :     taglen--;
     610             : 
     611             :     /* mimetype */
     612           2 :     if (isv34) {
     613           2 :         taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype));
     614             :     } else {
     615           0 :         avio_read(pb, mimetype, 3);
     616           0 :         mimetype[3] = 0;
     617           0 :         taglen    -= 3;
     618             :     }
     619             : 
     620           7 :     while (mime->id != AV_CODEC_ID_NONE) {
     621           5 :         if (!av_strncasecmp(mime->str, mimetype, sizeof(mimetype))) {
     622           2 :             id = mime->id;
     623           2 :             break;
     624             :         }
     625           3 :         mime++;
     626             :     }
     627           2 :     if (id == AV_CODEC_ID_NONE) {
     628           0 :         av_log(s, AV_LOG_WARNING,
     629             :                "Unknown attached picture mimetype: %s, skipping.\n", mimetype);
     630           0 :         goto fail;
     631             :     }
     632           2 :     apic->id = id;
     633             : 
     634             :     /* picture type */
     635           2 :     pic_type = avio_r8(pb);
     636           2 :     taglen--;
     637           2 :     if (pic_type < 0 || pic_type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) {
     638           0 :         av_log(s, AV_LOG_WARNING, "Unknown attached picture type %d.\n",
     639             :                pic_type);
     640           0 :         pic_type = 0;
     641             :     }
     642           2 :     apic->type = ff_id3v2_picture_types[pic_type];
     643             : 
     644             :     /* description and picture data */
     645           2 :     if (decode_str(s, pb, enc, &apic->description, &taglen) < 0) {
     646           0 :         av_log(s, AV_LOG_ERROR,
     647             :                "Error decoding attached picture description.\n");
     648           0 :         goto fail;
     649             :     }
     650             : 
     651           2 :     apic->buf = av_buffer_alloc(taglen + AV_INPUT_BUFFER_PADDING_SIZE);
     652           6 :     if (!apic->buf || !taglen || avio_read(pb, apic->buf->data, taglen) != taglen)
     653             :         goto fail;
     654           2 :     memset(apic->buf->data + taglen, 0, AV_INPUT_BUFFER_PADDING_SIZE);
     655             : 
     656           2 :     new_extra->tag  = "APIC";
     657           2 :     new_extra->data = apic;
     658           2 :     new_extra->next = *extra_meta;
     659           2 :     *extra_meta     = new_extra;
     660             : 
     661             :     // The description must be unique, and some ID3v2 tag writers add spaces
     662             :     // to write several APIC entries with the same description.
     663           2 :     rstrip_spaces(apic->description);
     664             : 
     665           2 :     return;
     666             : 
     667           0 : fail:
     668           0 :     if (apic)
     669           0 :         free_apic(apic);
     670           0 :     av_freep(&new_extra);
     671           0 :     avio_seek(pb, end, SEEK_SET);
     672             : }
     673             : 
     674           0 : static void free_chapter(void *obj)
     675             : {
     676           0 :     ID3v2ExtraMetaCHAP *chap = obj;
     677           0 :     av_freep(&chap->element_id);
     678           0 :     av_dict_free(&chap->meta);
     679           0 :     av_freep(&chap);
     680           0 : }
     681             : 
     682           0 : static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const char *ttag, ID3v2ExtraMeta **extra_meta, int isv34)
     683             : {
     684             :     int taglen;
     685             :     char tag[5];
     686           0 :     ID3v2ExtraMeta *new_extra = NULL;
     687           0 :     ID3v2ExtraMetaCHAP *chap  = NULL;
     688             : 
     689           0 :     new_extra = av_mallocz(sizeof(*new_extra));
     690           0 :     chap      = av_mallocz(sizeof(*chap));
     691             : 
     692           0 :     if (!new_extra || !chap)
     693             :         goto fail;
     694             : 
     695           0 :     if (decode_str(s, pb, 0, &chap->element_id, &len) < 0)
     696           0 :         goto fail;
     697             : 
     698           0 :     if (len < 16)
     699           0 :         goto fail;
     700             : 
     701           0 :     chap->start = avio_rb32(pb);
     702           0 :     chap->end   = avio_rb32(pb);
     703           0 :     avio_skip(pb, 8);
     704             : 
     705           0 :     len -= 16;
     706           0 :     while (len > 10) {
     707           0 :         if (avio_read(pb, tag, 4) < 4)
     708           0 :             goto fail;
     709           0 :         tag[4] = 0;
     710           0 :         taglen = avio_rb32(pb);
     711           0 :         avio_skip(pb, 2);
     712           0 :         len -= 10;
     713           0 :         if (taglen < 0 || taglen > len)
     714             :             goto fail;
     715           0 :         if (tag[0] == 'T')
     716           0 :             read_ttag(s, pb, taglen, &chap->meta, tag);
     717             :         else
     718           0 :             avio_skip(pb, taglen);
     719           0 :         len -= taglen;
     720             :     }
     721             : 
     722           0 :     ff_metadata_conv(&chap->meta, NULL, ff_id3v2_34_metadata_conv);
     723           0 :     ff_metadata_conv(&chap->meta, NULL, ff_id3v2_4_metadata_conv);
     724             : 
     725           0 :     new_extra->tag  = "CHAP";
     726           0 :     new_extra->data = chap;
     727           0 :     new_extra->next = *extra_meta;
     728           0 :     *extra_meta     = new_extra;
     729             : 
     730           0 :     return;
     731             : 
     732           0 : fail:
     733           0 :     if (chap)
     734           0 :         free_chapter(chap);
     735           0 :     av_freep(&new_extra);
     736             : }
     737             : 
     738           7 : static void free_priv(void *obj)
     739             : {
     740           7 :     ID3v2ExtraMetaPRIV *priv = obj;
     741           7 :     av_freep(&priv->owner);
     742           7 :     av_freep(&priv->data);
     743           7 :     av_freep(&priv);
     744           7 : }
     745             : 
     746           7 : static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen,
     747             :                       const char *tag, ID3v2ExtraMeta **extra_meta, int isv34)
     748             : {
     749             :     ID3v2ExtraMeta *meta;
     750             :     ID3v2ExtraMetaPRIV *priv;
     751             : 
     752           7 :     meta = av_mallocz(sizeof(*meta));
     753           7 :     priv = av_mallocz(sizeof(*priv));
     754             : 
     755           7 :     if (!meta || !priv)
     756             :         goto fail;
     757             : 
     758           7 :     if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0)
     759           0 :         goto fail;
     760             : 
     761           7 :     priv->data = av_malloc(taglen);
     762           7 :     if (!priv->data)
     763           0 :         goto fail;
     764             : 
     765           7 :     priv->datasize = taglen;
     766             : 
     767           7 :     if (avio_read(pb, priv->data, priv->datasize) != priv->datasize)
     768           0 :         goto fail;
     769             : 
     770           7 :     meta->tag   = "PRIV";
     771           7 :     meta->data  = priv;
     772           7 :     meta->next  = *extra_meta;
     773           7 :     *extra_meta = meta;
     774             : 
     775           7 :     return;
     776             : 
     777           0 : fail:
     778           0 :     if (priv)
     779           0 :         free_priv(priv);
     780           0 :     av_freep(&meta);
     781             : }
     782             : 
     783             : typedef struct ID3v2EMFunc {
     784             :     const char *tag3;
     785             :     const char *tag4;
     786             :     void (*read)(AVFormatContext *s, AVIOContext *pb, int taglen,
     787             :                  const char *tag, ID3v2ExtraMeta **extra_meta,
     788             :                  int isv34);
     789             :     void (*free)(void *obj);
     790             : } ID3v2EMFunc;
     791             : 
     792             : static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
     793             :     { "GEO", "GEOB", read_geobtag, free_geobtag },
     794             :     { "PIC", "APIC", read_apic,    free_apic    },
     795             :     { "CHAP","CHAP", read_chapter, free_chapter },
     796             :     { "PRIV","PRIV", read_priv,    free_priv    },
     797             :     { NULL }
     798             : };
     799             : 
     800             : /**
     801             :  * Get the corresponding ID3v2EMFunc struct for a tag.
     802             :  * @param isv34 Determines if v2.2 or v2.3/4 strings are used
     803             :  * @return A pointer to the ID3v2EMFunc struct if found, NULL otherwise.
     804             :  */
     805          22 : static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
     806             : {
     807          22 :     int i = 0;
     808          90 :     while (id3v2_extra_meta_funcs[i].tag3) {
     809          68 :         if (tag && !memcmp(tag,
     810             :                     (isv34 ? id3v2_extra_meta_funcs[i].tag4 :
     811             :                              id3v2_extra_meta_funcs[i].tag3),
     812             :                     (isv34 ? 4 : 3)))
     813          22 :             return &id3v2_extra_meta_funcs[i];
     814          46 :         i++;
     815             :     }
     816           0 :     return NULL;
     817             : }
     818             : 
     819          21 : static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
     820             :                         AVFormatContext *s, int len, uint8_t version,
     821             :                         uint8_t flags, ID3v2ExtraMeta **extra_meta)
     822             : {
     823             :     int isv34, unsync;
     824             :     unsigned tlen;
     825             :     char tag[5];
     826          21 :     int64_t next, end = avio_tell(pb) + len;
     827             :     int taghdrlen;
     828          21 :     const char *reason = NULL;
     829             :     AVIOContext pb_local;
     830             :     AVIOContext *pbx;
     831          21 :     unsigned char *buffer = NULL;
     832          21 :     int buffer_size       = 0;
     833          21 :     const ID3v2EMFunc *extra_func = NULL;
     834          21 :     unsigned char *uncompressed_buffer = NULL;
     835          21 :     av_unused int uncompressed_buffer_size = 0;
     836             :     const char *comm_frame;
     837             : 
     838          21 :     av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len);
     839             : 
     840          21 :     switch (version) {
     841           0 :     case 2:
     842           0 :         if (flags & 0x40) {
     843           0 :             reason = "compression";
     844           0 :             goto error;
     845             :         }
     846           0 :         isv34     = 0;
     847           0 :         taghdrlen = 6;
     848           0 :         comm_frame = "COM";
     849           0 :         break;
     850             : 
     851          21 :     case 3:
     852             :     case 4:
     853          21 :         isv34     = 1;
     854          21 :         taghdrlen = 10;
     855          21 :         comm_frame = "COMM";
     856          21 :         break;
     857             : 
     858           0 :     default:
     859           0 :         reason = "version";
     860           0 :         goto error;
     861             :     }
     862             : 
     863          21 :     unsync = flags & 0x80;
     864             : 
     865          21 :     if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
     866           0 :         int extlen = get_size(pb, 4);
     867           0 :         if (version == 4)
     868             :             /* In v2.4 the length includes the length field we just read. */
     869           0 :             extlen -= 4;
     870             : 
     871           0 :         if (extlen < 0) {
     872           0 :             reason = "invalid extended header length";
     873           0 :             goto error;
     874             :         }
     875           0 :         avio_skip(pb, extlen);
     876           0 :         len -= extlen + 4;
     877           0 :         if (len < 0) {
     878           0 :             reason = "extended header too long.";
     879           0 :             goto error;
     880             :         }
     881             :     }
     882             : 
     883        1621 :     while (len >= taghdrlen) {
     884        1579 :         unsigned int tflags = 0;
     885        1579 :         int tunsync         = 0;
     886        1579 :         int tcomp           = 0;
     887        1579 :         int tencr           = 0;
     888             :         unsigned long av_unused dlen;
     889             : 
     890        1579 :         if (isv34) {
     891        1579 :             if (avio_read(pb, tag, 4) < 4)
     892           0 :                 break;
     893        1579 :             tag[4] = 0;
     894        1579 :             if (version == 3) {
     895         844 :                 tlen = avio_rb32(pb);
     896             :             } else {
     897             :                 /* some encoders incorrectly uses v3 sizes instead of syncsafe ones
     898             :                  * so check the next tag to see which one to use */
     899         735 :                 tlen = avio_rb32(pb);
     900         735 :                 if (tlen > 0x7f) {
     901           1 :                     if (tlen < len) {
     902           0 :                         int64_t cur = avio_tell(pb);
     903             : 
     904           0 :                         if (ffio_ensure_seekback(pb, 2 /* tflags */ + tlen + 4 /* next tag */))
     905           0 :                             break;
     906             : 
     907           0 :                         if (check_tag(pb, cur + 2 + size_to_syncsafe(tlen), 4) == 1)
     908           0 :                             tlen = size_to_syncsafe(tlen);
     909           0 :                         else if (check_tag(pb, cur + 2 + tlen, 4) != 1)
     910           0 :                             break;
     911           0 :                         avio_seek(pb, cur, SEEK_SET);
     912             :                     } else
     913           1 :                         tlen = size_to_syncsafe(tlen);
     914             :                 }
     915             :             }
     916        1579 :             tflags  = avio_rb16(pb);
     917        1579 :             tunsync = tflags & ID3v2_FLAG_UNSYNCH;
     918             :         } else {
     919           0 :             if (avio_read(pb, tag, 3) < 3)
     920           0 :                 break;
     921           0 :             tag[3] = 0;
     922           0 :             tlen   = avio_rb24(pb);
     923             :         }
     924        1579 :         if (tlen > (1<<28))
     925           0 :             break;
     926        1579 :         len -= taghdrlen + tlen;
     927             : 
     928        1579 :         if (len < 0)
     929           0 :             break;
     930             : 
     931        1579 :         next = avio_tell(pb) + tlen;
     932             : 
     933        1579 :         if (!tlen) {
     934        1507 :             if (tag[0])
     935           3 :                 av_log(s, AV_LOG_DEBUG, "Invalid empty frame %s, skipping.\n",
     936             :                        tag);
     937        1507 :             continue;
     938             :         }
     939             : 
     940          72 :         if (tflags & ID3v2_FLAG_DATALEN) {
     941           0 :             if (tlen < 4)
     942           0 :                 break;
     943           0 :             dlen = avio_rb32(pb);
     944           0 :             tlen -= 4;
     945             :         } else
     946          72 :             dlen = tlen;
     947             : 
     948          72 :         tcomp = tflags & ID3v2_FLAG_COMPRESSION;
     949          72 :         tencr = tflags & ID3v2_FLAG_ENCRYPTION;
     950             : 
     951             :         /* skip encrypted tags and, if no zlib, compressed tags */
     952          72 :         if (tencr || (!CONFIG_ZLIB && tcomp)) {
     953             :             const char *type;
     954           0 :             if (!tcomp)
     955           0 :                 type = "encrypted";
     956           0 :             else if (!tencr)
     957           0 :                 type = "compressed";
     958             :             else
     959           0 :                 type = "encrypted and compressed";
     960             : 
     961           0 :             av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
     962           0 :             avio_skip(pb, tlen);
     963             :         /* check for text tag or supported special meta tag */
     964          91 :         } else if (tag[0] == 'T' ||
     965          37 :                    !memcmp(tag, "USLT", 4) ||
     966          29 :                    !strcmp(tag, comm_frame) ||
     967          11 :                    (extra_meta &&
     968             :                     (extra_func = get_extra_meta_func(tag, isv34)))) {
     969          72 :             pbx = pb;
     970             : 
     971          72 :             if (unsync || tunsync || tcomp) {
     972           7 :                 av_fast_malloc(&buffer, &buffer_size, tlen);
     973           7 :                 if (!buffer) {
     974           0 :                     av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen);
     975           0 :                     goto seek;
     976             :                 }
     977             :             }
     978          72 :             if (unsync || tunsync) {
     979           7 :                 uint8_t *b = buffer;
     980           7 :                 uint8_t *t = buffer;
     981           7 :                 uint8_t *end = t + tlen;
     982             : 
     983           7 :                 if (avio_read(pb, buffer, tlen) != tlen) {
     984           0 :                     av_log(s, AV_LOG_ERROR, "Failed to read tag data\n");
     985           0 :                     goto seek;
     986             :                 }
     987             : 
     988         125 :                 while (t != end) {
     989         111 :                     *b++ = *t++;
     990         111 :                     if (t != end && t[-1] == 0xff && !t[0])
     991           0 :                         t++;
     992             :                 }
     993             : 
     994           7 :                 ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL,
     995             :                                   NULL);
     996           7 :                 tlen = b - buffer;
     997           7 :                 pbx  = &pb_local; // read from sync buffer
     998             :             }
     999             : 
    1000             : #if CONFIG_ZLIB
    1001          72 :                 if (tcomp) {
    1002             :                     int err;
    1003             : 
    1004           0 :                     av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen);
    1005             : 
    1006           0 :                     av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen);
    1007           0 :                     if (!uncompressed_buffer) {
    1008           0 :                         av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen);
    1009           0 :                         goto seek;
    1010             :                     }
    1011             : 
    1012           0 :                     if (!(unsync || tunsync)) {
    1013           0 :                         err = avio_read(pb, buffer, tlen);
    1014           0 :                         if (err < 0) {
    1015           0 :                             av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
    1016           0 :                             goto seek;
    1017             :                         }
    1018           0 :                         tlen = err;
    1019             :                     }
    1020             : 
    1021           0 :                     err = uncompress(uncompressed_buffer, &dlen, buffer, tlen);
    1022           0 :                     if (err != Z_OK) {
    1023           0 :                         av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
    1024           0 :                         goto seek;
    1025             :                     }
    1026           0 :                     ffio_init_context(&pb_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL);
    1027           0 :                     tlen = dlen;
    1028           0 :                     pbx = &pb_local; // read from sync buffer
    1029             :                 }
    1030             : #endif
    1031         144 :             if (tag[0] == 'T')
    1032             :                 /* parse text tag */
    1033          53 :                 read_ttag(s, pbx, tlen, metadata, tag);
    1034          19 :             else if (!memcmp(tag, "USLT", 4))
    1035           1 :                 read_uslt(s, pbx, tlen, metadata);
    1036          18 :             else if (!strcmp(tag, comm_frame))
    1037           7 :                 read_comment(s, pbx, tlen, metadata);
    1038             :             else
    1039             :                 /* parse special meta tag */
    1040          11 :                 extra_func->read(s, pbx, tlen, tag, extra_meta, isv34);
    1041           0 :         } else if (!tag[0]) {
    1042           0 :             if (tag[1])
    1043           0 :                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n");
    1044           0 :             avio_skip(pb, tlen);
    1045           0 :             break;
    1046             :         }
    1047             :         /* Skip to end of tag */
    1048          72 : seek:
    1049          72 :         avio_seek(pb, next, SEEK_SET);
    1050             :     }
    1051             : 
    1052             :     /* Footer preset, always 10 bytes, skip over it */
    1053          36 :     if (version == 4 && flags & 0x10)
    1054           0 :         end += 10;
    1055             : 
    1056          42 : error:
    1057          21 :     if (reason)
    1058           0 :         av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n",
    1059             :                version, reason);
    1060          21 :     avio_seek(pb, end, SEEK_SET);
    1061          21 :     av_free(buffer);
    1062          21 :     av_free(uncompressed_buffer);
    1063          21 :     return;
    1064             : }
    1065             : 
    1066        3722 : static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
    1067             :                                 AVFormatContext *s, const char *magic,
    1068             :                                 ID3v2ExtraMeta **extra_meta, int64_t max_search_size)
    1069             : {
    1070             :     int len, ret;
    1071             :     uint8_t buf[ID3v2_HEADER_SIZE];
    1072             :     int found_header;
    1073             :     int64_t start, off;
    1074             : 
    1075        3722 :     if (max_search_size && max_search_size < ID3v2_HEADER_SIZE)
    1076           0 :         return;
    1077             : 
    1078        3722 :     start = avio_tell(pb);
    1079             :     do {
    1080             :         /* save the current offset in case there's nothing to read/skip */
    1081        3743 :         off = avio_tell(pb);
    1082        3743 :         if (max_search_size && off - start >= max_search_size - ID3v2_HEADER_SIZE) {
    1083           1 :             avio_seek(pb, off, SEEK_SET);
    1084           1 :             break;
    1085             :         }
    1086             : 
    1087        3742 :         ret = ffio_ensure_seekback(pb, ID3v2_HEADER_SIZE);
    1088        3742 :         if (ret >= 0)
    1089        3742 :             ret = avio_read(pb, buf, ID3v2_HEADER_SIZE);
    1090        3742 :         if (ret != ID3v2_HEADER_SIZE) {
    1091           3 :             avio_seek(pb, off, SEEK_SET);
    1092           3 :             break;
    1093             :         }
    1094        3739 :         found_header = ff_id3v2_match(buf, magic);
    1095        3739 :         if (found_header) {
    1096             :             /* parse ID3v2 header */
    1097          63 :             len = ((buf[6] & 0x7f) << 21) |
    1098          42 :                   ((buf[7] & 0x7f) << 14) |
    1099          21 :                   ((buf[8] & 0x7f) << 7) |
    1100          21 :                    (buf[9] & 0x7f);
    1101          21 :             id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta);
    1102             :         } else {
    1103        3718 :             avio_seek(pb, off, SEEK_SET);
    1104             :         }
    1105        3739 :     } while (found_header);
    1106        3722 :     ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv);
    1107        3722 :     ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv);
    1108        3722 :     ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv);
    1109        3722 :     merge_date(metadata);
    1110             : }
    1111             : 
    1112        3718 : void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata,
    1113             :                         const char *magic, ID3v2ExtraMeta **extra_meta)
    1114             : {
    1115        3718 :     id3v2_read_internal(pb, metadata, NULL, magic, extra_meta, 0);
    1116        3718 : }
    1117             : 
    1118           4 : void ff_id3v2_read(AVFormatContext *s, const char *magic,
    1119             :                    ID3v2ExtraMeta **extra_meta, unsigned int max_search_size)
    1120             : {
    1121           4 :     id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta, max_search_size);
    1122           4 : }
    1123             : 
    1124        5937 : void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
    1125             : {
    1126        5937 :     ID3v2ExtraMeta *current = *extra_meta, *next;
    1127             :     const ID3v2EMFunc *extra_func;
    1128             : 
    1129       11885 :     while (current) {
    1130          11 :         if ((extra_func = get_extra_meta_func(current->tag, 1)))
    1131          11 :             extra_func->free(current->data);
    1132          11 :         next = current->next;
    1133          11 :         av_freep(&current);
    1134          11 :         current = next;
    1135             :     }
    1136             : 
    1137        5937 :     *extra_meta = NULL;
    1138        5937 : }
    1139             : 
    1140           5 : int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
    1141             : {
    1142             :     ID3v2ExtraMeta *cur;
    1143             : 
    1144          11 :     for (cur = *extra_meta; cur; cur = cur->next) {
    1145             :         ID3v2ExtraMetaAPIC *apic;
    1146             :         AVStream *st;
    1147             : 
    1148           6 :         if (strcmp(cur->tag, "APIC"))
    1149           4 :             continue;
    1150           2 :         apic = cur->data;
    1151             : 
    1152           2 :         if (!(st = avformat_new_stream(s, NULL)))
    1153           0 :             return AVERROR(ENOMEM);
    1154             : 
    1155           2 :         st->disposition      |= AV_DISPOSITION_ATTACHED_PIC;
    1156           2 :         st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    1157           2 :         st->codecpar->codec_id   = apic->id;
    1158             : 
    1159           2 :         if (AV_RB64(apic->buf->data) == 0x89504e470d0a1a0a)
    1160           0 :             st->codecpar->codec_id = AV_CODEC_ID_PNG;
    1161             : 
    1162           2 :         if (apic->description[0])
    1163           0 :             av_dict_set(&st->metadata, "title", apic->description, 0);
    1164             : 
    1165           2 :         av_dict_set(&st->metadata, "comment", apic->type, 0);
    1166             : 
    1167           2 :         av_init_packet(&st->attached_pic);
    1168           2 :         st->attached_pic.buf          = apic->buf;
    1169           2 :         st->attached_pic.data         = apic->buf->data;
    1170           2 :         st->attached_pic.size         = apic->buf->size - AV_INPUT_BUFFER_PADDING_SIZE;
    1171           2 :         st->attached_pic.stream_index = st->index;
    1172           2 :         st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
    1173             : 
    1174           2 :         apic->buf = NULL;
    1175             :     }
    1176             : 
    1177           5 :     return 0;
    1178             : }
    1179             : 
    1180           8 : int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
    1181             : {
    1182           8 :     int ret = 0;
    1183             :     ID3v2ExtraMeta *cur;
    1184           8 :     AVRational time_base = {1, 1000};
    1185           8 :     ID3v2ExtraMetaCHAP **chapters = NULL;
    1186           8 :     int num_chapters = 0;
    1187             :     int i;
    1188             : 
    1189             :     // since extra_meta is a linked list where elements are prepended,
    1190             :     // we need to reverse the order of chapters
    1191          16 :     for (cur = *extra_meta; cur; cur = cur->next) {
    1192             :         ID3v2ExtraMetaCHAP *chap;
    1193             : 
    1194           8 :         if (strcmp(cur->tag, "CHAP"))
    1195           8 :             continue;
    1196           0 :         chap = cur->data;
    1197             : 
    1198           0 :         if ((ret = av_dynarray_add_nofree(&chapters, &num_chapters, chap)) < 0)
    1199           0 :             goto end;
    1200             :     }
    1201             : 
    1202           8 :     for (i = 0; i < (num_chapters / 2); i++) {
    1203             :         ID3v2ExtraMetaCHAP *right;
    1204             :         int right_index;
    1205             : 
    1206           0 :         right_index = (num_chapters - 1) - i;
    1207           0 :         right = chapters[right_index];
    1208             : 
    1209           0 :         chapters[right_index] = chapters[i];
    1210           0 :         chapters[i] = right;
    1211             :     }
    1212             : 
    1213           8 :     for (i = 0; i < num_chapters; i++) {
    1214             :         ID3v2ExtraMetaCHAP *chap;
    1215             :         AVChapter *chapter;
    1216             : 
    1217           0 :         chap = chapters[i];
    1218           0 :         chapter = avpriv_new_chapter(s, i, time_base, chap->start, chap->end, chap->element_id);
    1219           0 :         if (!chapter)
    1220           0 :             continue;
    1221             : 
    1222           0 :         if ((ret = av_dict_copy(&chapter->metadata, chap->meta, 0)) < 0)
    1223           0 :             goto end;
    1224             :     }
    1225             : 
    1226           8 : end:
    1227           8 :     av_freep(&chapters);
    1228           8 :     return ret;
    1229             : }
    1230             : 
    1231           7 : int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_meta)
    1232             : {
    1233             :     ID3v2ExtraMeta *cur;
    1234           7 :     int dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL;
    1235             : 
    1236          15 :     for (cur = *extra_meta; cur; cur = cur->next) {
    1237           8 :         if (!strcmp(cur->tag, "PRIV")) {
    1238           7 :             ID3v2ExtraMetaPRIV *priv = cur->data;
    1239             :             AVBPrint bprint;
    1240             :             char *escaped, *key;
    1241             :             int i, ret;
    1242             : 
    1243           7 :             if ((key = av_asprintf(ID3v2_PRIV_METADATA_PREFIX "%s", priv->owner)) == NULL) {
    1244           0 :                 return AVERROR(ENOMEM);
    1245             :             }
    1246             : 
    1247           7 :             av_bprint_init(&bprint, priv->datasize + 1, AV_BPRINT_SIZE_UNLIMITED);
    1248             : 
    1249          69 :             for (i = 0; i < priv->datasize; i++) {
    1250          62 :                 if (priv->data[i] < 32 || priv->data[i] > 126 || priv->data[i] == '\\') {
    1251          35 :                     av_bprintf(&bprint, "\\x%02x", priv->data[i]);
    1252             :                 } else {
    1253          27 :                     av_bprint_chars(&bprint, priv->data[i], 1);
    1254             :                 }
    1255             :             }
    1256             : 
    1257           7 :             if ((ret = av_bprint_finalize(&bprint, &escaped)) < 0) {
    1258           0 :                 av_free(key);
    1259           0 :                 return ret;
    1260             :             }
    1261             : 
    1262           7 :             if ((ret = av_dict_set(metadata, key, escaped, dict_flags)) < 0) {
    1263           0 :                 av_free(key);
    1264           0 :                 av_free(escaped);
    1265           0 :                 return ret;
    1266             :             }
    1267             :         }
    1268             :     }
    1269             : 
    1270           7 :     return 0;
    1271             : }
    1272             : 
    1273           4 : int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
    1274             : {
    1275           4 :     return ff_id3v2_parse_priv_dict(&s->metadata, extra_meta);
    1276             : }

Generated by: LCOV version 1.13