/* * APE tag handling * Copyright (c) 2007 Benjamin Zores * based upon libdemac from Dave Chapman. * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/intreadwrite.h" #include "avformat.h" #define ENABLE_DEBUG 0 #define APE_TAG_VERSION 2000 #define APE_TAG_FOOTER_BYTES 32 #define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) #define APE_TAG_FLAG_IS_HEADER (1 << 29) static int ape_tag_read_field(AVFormatContext *s) { ByteIOContext *pb = s->pb; uint8_t key[1024], value[1024]; uint32_t size, flags; int i, l, c; size = get_le32(pb); /* field size */ flags = get_le32(pb); /* field flags */ for (i = 0; i < sizeof(key) - 1; i++) { c = get_byte(pb); if (c < 0x20 || c > 0x7E) break; else key[i] = c; } key[i] = 0; if (c != 0) { av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key); return -1; } l = FFMIN(size, sizeof(value)-1); get_buffer(pb, value, l); value[l] = 0; url_fskip(pb, size-l); if (l < size) av_log(s, AV_LOG_WARNING, "Too long '%s' tag was truncated.\n", key); av_metadata_set(&s->metadata, key, value); return 0; } void ff_ape_parse_tag(AVFormatContext *s) { ByteIOContext *pb = s->pb; int file_size = url_fsize(pb); uint32_t val, fields, tag_bytes; uint8_t buf[8]; int i; if (file_size < APE_TAG_FOOTER_BYTES) return; url_fseek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); get_buffer(pb, buf, 8); /* APETAGEX */ if (strncmp(buf, "APETAGEX", 8)) { return; } val = get_le32(pb); /* APE tag version */ if (val > APE_TAG_VERSION) { av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION); return; } tag_bytes = get_le32(pb); /* tag size */ if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) { av_log(s, AV_LOG_ERROR, "Tag size is way too big\n"); return; } fields = get_le32(pb); /* number of fields */ if (fields > 65536) { av_log(s, AV_LOG_ERROR, "Too many tag fields (%d)\n", fields); return; } val = get_le32(pb); /* flags */ if (val & APE_TAG_FLAG_IS_HEADER) { av_log(s, AV_LOG_ERROR, "APE Tag is a header\n"); return; } url_fseek(pb, file_size - tag_bytes, SEEK_SET); for (i=0; ititle); av_log(s, AV_LOG_DEBUG, "author = %s\n", s->author); av_log(s, AV_LOG_DEBUG, "copyright = %s\n", s->copyright); av_log(s, AV_LOG_DEBUG, "comment = %s\n", s->comment); av_log(s, AV_LOG_DEBUG, "album = %s\n", s->album); av_log(s, AV_LOG_DEBUG, "year = %d\n", s->year); av_log(s, AV_LOG_DEBUG, "track = %d\n", s->track); av_log(s, AV_LOG_DEBUG, "genre = %s\n", s->genre); #endif }