LCOV - code coverage report
Current view: top level - libavformat - apetag.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 100 131 76.3 %
Date: 2017-12-16 21:16:39 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :  * APE tag handling
       3             :  * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org>
       4             :  *  based upon libdemac from Dave Chapman.
       5             :  *
       6             :  * This file is part of FFmpeg.
       7             :  *
       8             :  * FFmpeg is free software; you can redistribute it and/or
       9             :  * modify it under the terms of the GNU Lesser General Public
      10             :  * License as published by the Free Software Foundation; either
      11             :  * version 2.1 of the License, or (at your option) any later version.
      12             :  *
      13             :  * FFmpeg is distributed in the hope that it will be useful,
      14             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :  * Lesser General Public License for more details.
      17             :  *
      18             :  * You should have received a copy of the GNU Lesser General Public
      19             :  * License along with FFmpeg; if not, write to the Free Software
      20             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      21             :  */
      22             : 
      23             : #include <inttypes.h>
      24             : 
      25             : #include "libavutil/intreadwrite.h"
      26             : #include "libavutil/dict.h"
      27             : #include "avformat.h"
      28             : #include "avio_internal.h"
      29             : #include "apetag.h"
      30             : #include "internal.h"
      31             : 
      32             : #define APE_TAG_FLAG_CONTAINS_HEADER  (1 << 31)
      33             : #define APE_TAG_FLAG_LACKS_FOOTER     (1 << 30)
      34             : #define APE_TAG_FLAG_IS_HEADER        (1 << 29)
      35             : #define APE_TAG_FLAG_IS_BINARY        (1 << 1)
      36             : 
      37          28 : static int ape_tag_read_field(AVFormatContext *s)
      38             : {
      39          28 :     AVIOContext *pb = s->pb;
      40             :     uint8_t key[1024], *value;
      41             :     int64_t size, flags;
      42             :     int i, c;
      43             : 
      44          28 :     size = avio_rl32(pb);  /* field size */
      45          28 :     flags = avio_rl32(pb); /* field flags */
      46         199 :     for (i = 0; i < sizeof(key) - 1; i++) {
      47         199 :         c = avio_r8(pb);
      48         199 :         if (c < 0x20 || c > 0x7E)
      49             :             break;
      50             :         else
      51         171 :             key[i] = c;
      52             :     }
      53          28 :     key[i] = 0;
      54          28 :     if (c != 0) {
      55           0 :         av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key);
      56           0 :         return -1;
      57             :     }
      58          28 :     if (size > INT32_MAX - AV_INPUT_BUFFER_PADDING_SIZE) {
      59           0 :         av_log(s, AV_LOG_ERROR, "APE tag size too large.\n");
      60           0 :         return AVERROR_INVALIDDATA;
      61             :     }
      62          28 :     if (flags & APE_TAG_FLAG_IS_BINARY) {
      63             :         uint8_t filename[1024];
      64             :         enum AVCodecID id;
      65             :         int ret;
      66           2 :         AVStream *st = avformat_new_stream(s, NULL);
      67           2 :         if (!st)
      68           0 :             return AVERROR(ENOMEM);
      69             : 
      70           2 :         ret = avio_get_str(pb, size, filename, sizeof(filename));
      71           2 :         if (ret < 0)
      72           0 :             return ret;
      73           2 :         if (size <= ret) {
      74           0 :             av_log(s, AV_LOG_WARNING, "Skipping binary tag '%s'.\n", key);
      75           0 :             return 0;
      76             :         }
      77           2 :         size -= ret;
      78             : 
      79           2 :         av_dict_set(&st->metadata, key, filename, 0);
      80             : 
      81           2 :         if ((id = ff_guess_image2_codec(filename)) != AV_CODEC_ID_NONE) {
      82             :             AVPacket pkt;
      83             :             int ret;
      84             : 
      85           2 :             ret = av_get_packet(s->pb, &pkt, size);
      86           2 :             if (ret < 0) {
      87           0 :                 av_log(s, AV_LOG_ERROR, "Error reading cover art.\n");
      88           0 :                 return ret;
      89             :             }
      90             : 
      91           2 :             st->disposition      |= AV_DISPOSITION_ATTACHED_PIC;
      92           2 :             st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
      93           2 :             st->codecpar->codec_id   = id;
      94             : 
      95           2 :             st->attached_pic              = pkt;
      96           2 :             st->attached_pic.stream_index = st->index;
      97           2 :             st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
      98             :         } else {
      99           0 :             if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0)
     100           0 :                 return AVERROR(ENOMEM);
     101           0 :             st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
     102             :         }
     103             :     } else {
     104          26 :         value = av_malloc(size+1);
     105          26 :         if (!value)
     106           0 :             return AVERROR(ENOMEM);
     107          26 :         c = avio_read(pb, value, size);
     108          26 :         if (c < 0) {
     109           0 :             av_free(value);
     110           0 :             return c;
     111             :         }
     112          26 :         value[c] = 0;
     113          26 :         av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL);
     114             :     }
     115          28 :     return 0;
     116             : }
     117             : 
     118          82 : int64_t ff_ape_parse_tag(AVFormatContext *s)
     119             : {
     120          82 :     AVIOContext *pb = s->pb;
     121          82 :     int64_t file_size = avio_size(pb);
     122             :     uint32_t val, fields, tag_bytes;
     123             :     uint8_t buf[8];
     124             :     int64_t tag_start;
     125             :     int i;
     126             : 
     127          82 :     if (file_size < APE_TAG_FOOTER_BYTES)
     128           0 :         return 0;
     129             : 
     130          82 :     avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET);
     131             : 
     132          82 :     avio_read(pb, buf, 8);     /* APETAGEX */
     133          82 :     if (strncmp(buf, APE_TAG_PREAMBLE, 8)) {
     134          74 :         return 0;
     135             :     }
     136             : 
     137           8 :     val = avio_rl32(pb);       /* APE tag version */
     138           8 :     if (val > APE_TAG_VERSION) {
     139           0 :         av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION);
     140           0 :         return 0;
     141             :     }
     142             : 
     143           8 :     tag_bytes = avio_rl32(pb); /* tag size */
     144           8 :     if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) {
     145           0 :         av_log(s, AV_LOG_ERROR, "Tag size is way too big\n");
     146           0 :         return 0;
     147             :     }
     148             : 
     149           8 :     if (tag_bytes > file_size - APE_TAG_FOOTER_BYTES) {
     150           0 :         av_log(s, AV_LOG_ERROR, "Invalid tag size %"PRIu32".\n", tag_bytes);
     151           0 :         return 0;
     152             :     }
     153             : 
     154           8 :     fields = avio_rl32(pb);    /* number of fields */
     155           8 :     if (fields > 65536) {
     156           0 :         av_log(s, AV_LOG_ERROR, "Too many tag fields (%"PRIu32")\n", fields);
     157           0 :         return 0;
     158             :     }
     159             : 
     160           8 :     val = avio_rl32(pb);       /* flags */
     161           8 :     if (val & APE_TAG_FLAG_IS_HEADER) {
     162           0 :         av_log(s, AV_LOG_ERROR, "APE Tag is a header\n");
     163           0 :         return 0;
     164             :     }
     165             : 
     166           8 :     avio_seek(pb, file_size - tag_bytes, SEEK_SET);
     167             : 
     168           8 :     if (val & APE_TAG_FLAG_CONTAINS_HEADER)
     169           8 :         tag_bytes += APE_TAG_HEADER_BYTES;
     170             : 
     171           8 :     tag_start = file_size - tag_bytes;
     172             : 
     173          36 :     for (i=0; i<fields; i++)
     174          28 :         if (ape_tag_read_field(s) < 0) break;
     175             : 
     176           8 :     return tag_start;
     177             : }
     178             : 
     179           2 : static int string_is_ascii(const uint8_t *str)
     180             : {
     181           2 :     while (*str && *str >= 0x20 && *str <= 0x7e ) str++;
     182           2 :     return !*str;
     183             : }
     184             : 
     185           4 : int ff_ape_write_tag(AVFormatContext *s)
     186             : {
     187           4 :     AVDictionaryEntry *e = NULL;
     188           4 :     int size, ret, count = 0;
     189           4 :     AVIOContext *dyn_bc = NULL;
     190           4 :     uint8_t *dyn_buf = NULL;
     191             : 
     192           4 :     if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
     193           0 :         goto end;
     194             : 
     195           4 :     ff_standardize_creation_time(s);
     196          10 :     while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) {
     197             :         int val_len;
     198             : 
     199           2 :         if (!string_is_ascii(e->key)) {
     200           0 :             av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n");
     201           0 :             continue;
     202             :         }
     203             : 
     204           2 :         val_len = strlen(e->value);
     205           2 :         avio_wl32(dyn_bc, val_len);            // value length
     206           2 :         avio_wl32(dyn_bc, 0);                  // item flags
     207           2 :         avio_put_str(dyn_bc, e->key);          // key
     208           2 :         avio_write(dyn_bc, e->value, val_len); // value
     209           2 :         count++;
     210             :     }
     211           4 :     if (!count)
     212           2 :         goto end;
     213             : 
     214           2 :     size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
     215           2 :     if (size <= 0)
     216           0 :         goto end;
     217           2 :     size += APE_TAG_FOOTER_BYTES;
     218             : 
     219             :     // header
     220           2 :     avio_write(s->pb, "APETAGEX", 8);   // id
     221           2 :     avio_wl32(s->pb, APE_TAG_VERSION);  // version
     222           2 :     avio_wl32(s->pb, size);
     223           2 :     avio_wl32(s->pb, count);
     224             : 
     225             :     // flags
     226           2 :     avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_IS_HEADER);
     227           2 :     ffio_fill(s->pb, 0, 8);             // reserved
     228             : 
     229           2 :     avio_write(s->pb, dyn_buf, size - APE_TAG_FOOTER_BYTES);
     230             : 
     231             :     // footer
     232           2 :     avio_write(s->pb, "APETAGEX", 8);   // id
     233           2 :     avio_wl32(s->pb, APE_TAG_VERSION);  // version
     234           2 :     avio_wl32(s->pb, size);             // size
     235           2 :     avio_wl32(s->pb, count);            // tag count
     236             : 
     237             :     // flags
     238           2 :     avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER);
     239           2 :     ffio_fill(s->pb, 0, 8);             // reserved
     240             : 
     241           4 : end:
     242           4 :     if (dyn_bc && !dyn_buf)
     243           2 :         avio_close_dyn_buf(dyn_bc, &dyn_buf);
     244           4 :     av_freep(&dyn_buf);
     245             : 
     246           4 :     return ret;
     247             : }

Generated by: LCOV version 1.13