yading@11: /* yading@11: * APE tag handling yading@11: * Copyright (c) 2007 Benjamin Zores yading@11: * based upon libdemac from Dave Chapman. yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavutil/dict.h" yading@11: #include "avformat.h" yading@11: #include "apetag.h" yading@11: #include "internal.h" yading@11: yading@11: #define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) yading@11: #define APE_TAG_FLAG_IS_HEADER (1 << 29) yading@11: #define APE_TAG_FLAG_IS_BINARY (1 << 1) yading@11: yading@11: static int ape_tag_read_field(AVFormatContext *s) yading@11: { yading@11: AVIOContext *pb = s->pb; yading@11: uint8_t key[1024], *value; yading@11: uint32_t size, flags; yading@11: int i, c; yading@11: yading@11: size = avio_rl32(pb); /* field size */ yading@11: flags = avio_rl32(pb); /* field flags */ yading@11: for (i = 0; i < sizeof(key) - 1; i++) { yading@11: c = avio_r8(pb); yading@11: if (c < 0x20 || c > 0x7E) yading@11: break; yading@11: else yading@11: key[i] = c; yading@11: } yading@11: key[i] = 0; yading@11: if (c != 0) { yading@11: av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key); yading@11: return -1; yading@11: } yading@11: if (size >= UINT_MAX) yading@11: return -1; yading@11: if (flags & APE_TAG_FLAG_IS_BINARY) { yading@11: uint8_t filename[1024]; yading@11: enum AVCodecID id; yading@11: AVStream *st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: size -= avio_get_str(pb, size, filename, sizeof(filename)); yading@11: if (size <= 0) { yading@11: av_log(s, AV_LOG_WARNING, "Skipping binary tag '%s'.\n", key); yading@11: return 0; yading@11: } yading@11: yading@11: av_dict_set(&st->metadata, key, filename, 0); yading@11: yading@11: if ((id = ff_guess_image2_codec(filename)) != AV_CODEC_ID_NONE) { yading@11: AVPacket pkt; yading@11: int ret; yading@11: yading@11: ret = av_get_packet(s->pb, &pkt, size); yading@11: if (ret < 0) { yading@11: av_log(s, AV_LOG_ERROR, "Error reading cover art.\n"); yading@11: return ret; yading@11: } yading@11: yading@11: st->disposition |= AV_DISPOSITION_ATTACHED_PIC; yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = id; yading@11: yading@11: st->attached_pic = pkt; yading@11: st->attached_pic.stream_index = st->index; yading@11: st->attached_pic.flags |= AV_PKT_FLAG_KEY; yading@11: } else { yading@11: st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!st->codec->extradata) yading@11: return AVERROR(ENOMEM); yading@11: if (avio_read(pb, st->codec->extradata, size) != size) { yading@11: av_freep(&st->codec->extradata); yading@11: return AVERROR(EIO); yading@11: } yading@11: st->codec->extradata_size = size; yading@11: st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; yading@11: } yading@11: } else { yading@11: value = av_malloc(size+1); yading@11: if (!value) yading@11: return AVERROR(ENOMEM); yading@11: c = avio_read(pb, value, size); yading@11: if (c < 0) { yading@11: av_free(value); yading@11: return c; yading@11: } yading@11: value[c] = 0; yading@11: av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: int64_t ff_ape_parse_tag(AVFormatContext *s) yading@11: { yading@11: AVIOContext *pb = s->pb; yading@11: int file_size = avio_size(pb); yading@11: uint32_t val, fields, tag_bytes; yading@11: uint8_t buf[8]; yading@11: int64_t tag_start; yading@11: int i; yading@11: yading@11: if (file_size < APE_TAG_FOOTER_BYTES) yading@11: return 0; yading@11: yading@11: avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); yading@11: yading@11: avio_read(pb, buf, 8); /* APETAGEX */ yading@11: if (strncmp(buf, APE_TAG_PREAMBLE, 8)) { yading@11: return 0; yading@11: } yading@11: yading@11: val = avio_rl32(pb); /* APE tag version */ yading@11: if (val > APE_TAG_VERSION) { yading@11: av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION); yading@11: return 0; yading@11: } yading@11: yading@11: tag_bytes = avio_rl32(pb); /* tag size */ yading@11: if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) { yading@11: av_log(s, AV_LOG_ERROR, "Tag size is way too big\n"); yading@11: return 0; yading@11: } yading@11: yading@11: if (tag_bytes > file_size - APE_TAG_FOOTER_BYTES) { yading@11: av_log(s, AV_LOG_ERROR, "Invalid tag size %u.\n", tag_bytes); yading@11: return 0; yading@11: } yading@11: tag_start = file_size - tag_bytes - APE_TAG_FOOTER_BYTES; yading@11: yading@11: fields = avio_rl32(pb); /* number of fields */ yading@11: if (fields > 65536) { yading@11: av_log(s, AV_LOG_ERROR, "Too many tag fields (%d)\n", fields); yading@11: return 0; yading@11: } yading@11: yading@11: val = avio_rl32(pb); /* flags */ yading@11: if (val & APE_TAG_FLAG_IS_HEADER) { yading@11: av_log(s, AV_LOG_ERROR, "APE Tag is a header\n"); yading@11: return 0; yading@11: } yading@11: yading@11: avio_seek(pb, file_size - tag_bytes, SEEK_SET); yading@11: yading@11: for (i=0; i