Based on patch by Matti Hamalainen (mhamalai<mot>students<punkt>oamk<punkt>) Originally committed as revision 19629 to svn://svn.ffmpeg.org/ffmpeg/trunktags/v0.6
| @@ -17,7 +17,7 @@ OBJS-$(CONFIG_AIFF_MUXER) += aiff.o riff.o | |||
| OBJS-$(CONFIG_AMR_DEMUXER) += amr.o | |||
| OBJS-$(CONFIG_AMR_MUXER) += amr.o | |||
| OBJS-$(CONFIG_APC_DEMUXER) += apc.o | |||
| OBJS-$(CONFIG_APE_DEMUXER) += ape.o | |||
| OBJS-$(CONFIG_APE_DEMUXER) += ape.o apetag.o | |||
| OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o riff.o avlanguage.o | |||
| OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o riff.o | |||
| OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o | |||
| @@ -24,6 +24,7 @@ | |||
| #include "libavutil/intreadwrite.h" | |||
| #include "avformat.h" | |||
| #include "apetag.h" | |||
| #define ENABLE_DEBUG 0 | |||
| @@ -42,12 +43,6 @@ | |||
| #define APE_EXTRADATA_SIZE 6 | |||
| /* APE tags */ | |||
| #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) | |||
| typedef struct { | |||
| int64_t pos; | |||
| int nblocks; | |||
| @@ -91,97 +86,6 @@ typedef struct { | |||
| uint32_t *seektable; | |||
| } APEContext; | |||
| 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; | |||
| } | |||
| static void 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; i<fields; i++) | |||
| if (ape_tag_read_field(s) < 0) break; | |||
| #if ENABLE_DEBUG | |||
| av_log(s, AV_LOG_DEBUG, "\nAPE Tags:\n\n"); | |||
| av_log(s, AV_LOG_DEBUG, "title = %s\n", s->title); | |||
| 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 | |||
| } | |||
| static int ape_probe(AVProbeData * p) | |||
| { | |||
| if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ') | |||
| @@ -384,7 +288,7 @@ static int ape_read_header(AVFormatContext * s, AVFormatParameters * ap) | |||
| /* try to read APE tags */ | |||
| if (!url_is_streamed(pb)) { | |||
| ape_parse_tag(s); | |||
| ff_ape_parse_tag(s); | |||
| url_fseek(pb, 0, SEEK_SET); | |||
| } | |||
| @@ -0,0 +1,122 @@ | |||
| /* | |||
| * APE tag handling | |||
| * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> | |||
| * 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; i<fields; i++) | |||
| if (ape_tag_read_field(s) < 0) break; | |||
| #if ENABLE_DEBUG | |||
| av_log(s, AV_LOG_DEBUG, "\nAPE Tags:\n\n"); | |||
| av_log(s, AV_LOG_DEBUG, "title = %s\n", s->title); | |||
| 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 | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| /* | |||
| * APE tag handling | |||
| * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> | |||
| * 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 | |||
| */ | |||
| #ifndef AVFORMAT_APETAG_H | |||
| #define AVFORMAT_APETAG_H | |||
| #include "avformat.h" | |||
| /** | |||
| * Read and parse an APE tag | |||
| */ | |||
| void ff_ape_parse_tag(AVFormatContext *s); | |||
| #endif /* AVFORMAT_ID3V2_H */ | |||