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_CONTAINS_FOOTER (1 << 30) 34#define APE_TAG_FLAG_IS_HEADER (1 << 29) 35#define APE_TAG_FLAG_IS_BINARY (1 << 1) 36 37static int ape_tag_read_field(AVFormatContext *s) 38{ 39 AVIOContext *pb = s->pb; 40 uint8_t key[1024], *value; 41 uint32_t size, flags; 42 int i, c; 43 44 size = avio_rl32(pb); /* field size */ 45 flags = avio_rl32(pb); /* field flags */ 46 for (i = 0; i < sizeof(key) - 1; i++) { 47 c = avio_r8(pb); 48 if (c < 0x20 || c > 0x7E) 49 break; 50 else 51 key[i] = c; 52 } 53 key[i] = 0; 54 if (c != 0) { 55 av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key); 56 return -1; 57 } 58 if (size > INT32_MAX - FF_INPUT_BUFFER_PADDING_SIZE) { 59 av_log(s, AV_LOG_ERROR, "APE tag size too large.\n"); 60 return AVERROR_INVALIDDATA; 61 } 62 if (flags & APE_TAG_FLAG_IS_BINARY) { 63 uint8_t filename[1024]; 64 enum AVCodecID id; 65 AVStream *st = avformat_new_stream(s, NULL); 66 if (!st) 67 return AVERROR(ENOMEM); 68 69 size -= avio_get_str(pb, size, filename, sizeof(filename)); 70 if (size <= 0) { 71 av_log(s, AV_LOG_WARNING, "Skipping binary tag '%s'.\n", key); 72 return 0; 73 } 74 75 av_dict_set(&st->metadata, key, filename, 0); 76 77 if ((id = ff_guess_image2_codec(filename)) != AV_CODEC_ID_NONE) { 78 AVPacket pkt; 79 int ret; 80 81 ret = av_get_packet(s->pb, &pkt, size); 82 if (ret < 0) { 83 av_log(s, AV_LOG_ERROR, "Error reading cover art.\n"); 84 return ret; 85 } 86 87 st->disposition |= AV_DISPOSITION_ATTACHED_PIC; 88 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 89 st->codec->codec_id = id; 90 91 st->attached_pic = pkt; 92 st->attached_pic.stream_index = st->index; 93 st->attached_pic.flags |= AV_PKT_FLAG_KEY; 94 } else { 95 if (ff_get_extradata(st->codec, s->pb, size) < 0) 96 return AVERROR(ENOMEM); 97 st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; 98 } 99 } else { 100 value = av_malloc(size+1); 101 if (!value) 102 return AVERROR(ENOMEM); 103 c = avio_read(pb, value, size); 104 if (c < 0) { 105 av_free(value); 106 return c; 107 } 108 value[c] = 0; 109 av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); 110 } 111 return 0; 112} 113 114int64_t ff_ape_parse_tag(AVFormatContext *s) 115{ 116 AVIOContext *pb = s->pb; 117 int64_t file_size = avio_size(pb); 118 uint32_t val, fields, tag_bytes; 119 uint8_t buf[8]; 120 int64_t tag_start; 121 int i; 122 123 if (file_size < APE_TAG_FOOTER_BYTES) 124 return 0; 125 126 avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); 127 128 avio_read(pb, buf, 8); /* APETAGEX */ 129 if (strncmp(buf, APE_TAG_PREAMBLE, 8)) { 130 return 0; 131 } 132 133 val = avio_rl32(pb); /* APE tag version */ 134 if (val > APE_TAG_VERSION) { 135 av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION); 136 return 0; 137 } 138 139 tag_bytes = avio_rl32(pb); /* tag size */ 140 if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) { 141 av_log(s, AV_LOG_ERROR, "Tag size is way too big\n"); 142 return 0; 143 } 144 145 if (tag_bytes > file_size - APE_TAG_FOOTER_BYTES) { 146 av_log(s, AV_LOG_ERROR, "Invalid tag size %"PRIu32".\n", tag_bytes); 147 return 0; 148 } 149 tag_start = file_size - tag_bytes - APE_TAG_FOOTER_BYTES; 150 151 fields = avio_rl32(pb); /* number of fields */ 152 if (fields > 65536) { 153 av_log(s, AV_LOG_ERROR, "Too many tag fields (%"PRIu32")\n", fields); 154 return 0; 155 } 156 157 val = avio_rl32(pb); /* flags */ 158 if (val & APE_TAG_FLAG_IS_HEADER) { 159 av_log(s, AV_LOG_ERROR, "APE Tag is a header\n"); 160 return 0; 161 } 162 163 avio_seek(pb, file_size - tag_bytes, SEEK_SET); 164 165 for (i=0; i<fields; i++) 166 if (ape_tag_read_field(s) < 0) break; 167 168 return tag_start; 169} 170 171static int string_is_ascii(const uint8_t *str) 172{ 173 while (*str && *str >= 0x20 && *str <= 0x7e ) str++; 174 return !*str; 175} 176 177int ff_ape_write_tag(AVFormatContext *s) 178{ 179 AVDictionaryEntry *e = NULL; 180 int size, ret, count = 0; 181 AVIOContext *dyn_bc = NULL; 182 uint8_t *dyn_buf = NULL; 183 184 if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0) 185 goto end; 186 187 // flags 188 avio_wl32(dyn_bc, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER | 189 APE_TAG_FLAG_IS_HEADER); 190 ffio_fill(dyn_bc, 0, 8); // reserved 191 192 while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) { 193 int val_len; 194 195 if (!string_is_ascii(e->key)) { 196 av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n"); 197 continue; 198 } 199 200 val_len = strlen(e->value); 201 avio_wl32(dyn_bc, val_len); // value length 202 avio_wl32(dyn_bc, 0); // item flags 203 avio_put_str(dyn_bc, e->key); // key 204 avio_write(dyn_bc, e->value, val_len); // value 205 count++; 206 } 207 if (!count) 208 goto end; 209 210 size = avio_close_dyn_buf(dyn_bc, &dyn_buf); 211 if (size <= 0) 212 goto end; 213 size += 20; 214 215 // header 216 avio_write(s->pb, "APETAGEX", 8); // id 217 avio_wl32(s->pb, APE_TAG_VERSION); // version 218 avio_wl32(s->pb, size); 219 avio_wl32(s->pb, count); 220 221 avio_write(s->pb, dyn_buf, size - 20); 222 223 // footer 224 avio_write(s->pb, "APETAGEX", 8); // id 225 avio_wl32(s->pb, APE_TAG_VERSION); // version 226 avio_wl32(s->pb, size); // size 227 avio_wl32(s->pb, count); // tag count 228 229 // flags 230 avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER); 231 ffio_fill(s->pb, 0, 8); // reserved 232 233end: 234 if (dyn_bc && !dyn_buf) 235 avio_close_dyn_buf(dyn_bc, &dyn_buf); 236 av_freep(&dyn_buf); 237 238 return ret; 239} 240