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 "libavutil/intreadwrite.h" 24#include "avformat.h" 25#include "apetag.h" 26 27#define ENABLE_DEBUG 0 28 29#define APE_TAG_VERSION 2000 30#define APE_TAG_FOOTER_BYTES 32 31#define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) 32#define APE_TAG_FLAG_IS_HEADER (1 << 29) 33 34static int ape_tag_read_field(AVFormatContext *s) 35{ 36 ByteIOContext *pb = s->pb; 37 uint8_t key[1024], *value; 38 uint32_t size, flags; 39 int i, c; 40 41 size = get_le32(pb); /* field size */ 42 flags = get_le32(pb); /* field flags */ 43 for (i = 0; i < sizeof(key) - 1; i++) { 44 c = get_byte(pb); 45 if (c < 0x20 || c > 0x7E) 46 break; 47 else 48 key[i] = c; 49 } 50 key[i] = 0; 51 if (c != 0) { 52 av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key); 53 return -1; 54 } 55 if (size >= UINT_MAX) 56 return -1; 57 value = av_malloc(size+1); 58 if (!value) 59 return AVERROR(ENOMEM); 60 get_buffer(pb, value, size); 61 value[size] = 0; 62 av_metadata_set2(&s->metadata, key, value, AV_METADATA_DONT_STRDUP_VAL); 63 return 0; 64} 65 66void ff_ape_parse_tag(AVFormatContext *s) 67{ 68 ByteIOContext *pb = s->pb; 69 int file_size = url_fsize(pb); 70 uint32_t val, fields, tag_bytes; 71 uint8_t buf[8]; 72 int i; 73 74 if (file_size < APE_TAG_FOOTER_BYTES) 75 return; 76 77 url_fseek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); 78 79 get_buffer(pb, buf, 8); /* APETAGEX */ 80 if (strncmp(buf, "APETAGEX", 8)) { 81 return; 82 } 83 84 val = get_le32(pb); /* APE tag version */ 85 if (val > APE_TAG_VERSION) { 86 av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION); 87 return; 88 } 89 90 tag_bytes = get_le32(pb); /* tag size */ 91 if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) { 92 av_log(s, AV_LOG_ERROR, "Tag size is way too big\n"); 93 return; 94 } 95 96 fields = get_le32(pb); /* number of fields */ 97 if (fields > 65536) { 98 av_log(s, AV_LOG_ERROR, "Too many tag fields (%d)\n", fields); 99 return; 100 } 101 102 val = get_le32(pb); /* flags */ 103 if (val & APE_TAG_FLAG_IS_HEADER) { 104 av_log(s, AV_LOG_ERROR, "APE Tag is a header\n"); 105 return; 106 } 107 108 url_fseek(pb, file_size - tag_bytes, SEEK_SET); 109 110 for (i=0; i<fields; i++) 111 if (ape_tag_read_field(s) < 0) break; 112} 113