diff options
Diffstat (limited to 'libavformat/aiffdec.c')
-rw-r--r-- | libavformat/aiffdec.c | 153 |
1 files changed, 116 insertions, 37 deletions
diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 481a92d351..7dcc85f1ed 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -2,29 +2,33 @@ * AIFF/AIFF-C demuxer * Copyright (c) 2006 Patrick Guimond * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * 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. * - * Libav is distributed in the hope that it will be useful, + * 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 Libav; if not, write to the Free Software + * 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 "libavutil/mathematics.h" #include "libavutil/dict.h" #include "avformat.h" #include "internal.h" #include "pcm.h" #include "aiff.h" +#include "isom.h" +#include "id3v2.h" +#include "mov_chan.h" #define AIFF 0 #define AIFF_C_VERSION1 0xA2805140 @@ -54,7 +58,7 @@ static int get_tag(AVIOContext *pb, uint32_t * tag) { int size; - if (pb->eof_reached) + if (avio_feof(pb)) return AVERROR(EIO); *tag = avio_rl32(pb); @@ -70,23 +74,24 @@ static int get_tag(AVIOContext *pb, uint32_t * tag) static void get_meta(AVFormatContext *s, const char *key, int size) { uint8_t *str = av_malloc(size+1); - int res; - if (!str) { - avio_skip(s->pb, size); - return; - } - - res = avio_read(s->pb, str, size); - if (res < 0) - return; + if (str) { + int res = avio_read(s->pb, str, size); + if (res < 0){ + av_free(str); + return; + } + size += (size&1)-res; + str[res] = 0; + av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + }else + size+= size&1; - str[res] = 0; - av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + avio_skip(s->pb, size); } /* Returns the number of sound data frames or negative on error */ -static unsigned int get_aiff_header(AVFormatContext *s, int size, +static int get_aiff_header(AVFormatContext *s, int size, unsigned version) { AVIOContext *pb = s->pb; @@ -94,7 +99,7 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, AIFFInputContext *aiff = s->priv_data; int exp; uint64_t val; - double sample_rate; + int sample_rate; unsigned int num_frames; if (size & 1) @@ -104,16 +109,30 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, num_frames = avio_rb32(pb); par->bits_per_coded_sample = avio_rb16(pb); - exp = avio_rb16(pb); + exp = avio_rb16(pb) - 16383 - 63; val = avio_rb64(pb); - sample_rate = ldexp(val, exp - 16383 - 63); + if (exp <-63 || exp >63) { + av_log(s, AV_LOG_ERROR, "exp %d is out of range\n", exp); + return AVERROR_INVALIDDATA; + } + if (exp >= 0) + sample_rate = val << exp; + else + sample_rate = (val + (1ULL<<(-exp-1))) >> -exp; par->sample_rate = sample_rate; size -= 18; /* get codec id for AIFF-C */ - if (version == AIFF_C_VERSION1) { + if (size < 4) { + version = AIFF; + } else if (version == AIFF_C_VERSION1) { par->codec_tag = avio_rl32(pb); par->codec_id = ff_codec_get_id(ff_codec_aiff_tags, par->codec_tag); + if (par->codec_id == AV_CODEC_ID_NONE) { + char tag[32]; + av_get_codec_tag_string(tag, sizeof(tag), par->codec_tag); + avpriv_request_sample(s, "unknown or unsupported codec tag: %s", tag); + } size -= 4; } @@ -136,17 +155,19 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, case AV_CODEC_ID_MACE3: par->block_align = 2 * par->channels; break; + case AV_CODEC_ID_ADPCM_G726LE: + par->bits_per_coded_sample = 5; + case AV_CODEC_ID_ADPCM_IMA_WS: case AV_CODEC_ID_ADPCM_G722: case AV_CODEC_ID_MACE6: + case AV_CODEC_ID_SDX2_DPCM: par->block_align = 1 * par->channels; break; case AV_CODEC_ID_GSM: par->block_align = 33; break; - case AV_CODEC_ID_QCELP: - par->block_align = 35; - break; default: + aiff->block_duration = 1; break; } if (par->block_align > 0) @@ -157,10 +178,10 @@ static unsigned int get_aiff_header(AVFormatContext *s, int size, /* Block align needs to be computed in all cases, as the definition * is specific to applications -> here we use the WAVE format definition */ if (!par->block_align) - par->block_align = (par->bits_per_coded_sample * par->channels) >> 3; + par->block_align = (av_get_bits_per_sample(par->codec_id) * par->channels) >> 3; if (aiff->block_duration) { - par->bit_rate = par->sample_rate * (par->block_align << 3) / + par->bit_rate = (int64_t)par->sample_rate * (par->block_align << 3) / aiff->block_duration; } @@ -186,13 +207,14 @@ static int aiff_probe(AVProbeData *p) /* aiff input */ static int aiff_read_header(AVFormatContext *s) { - int size, filesize; - int64_t offset = 0; + int ret, size, filesize; + int64_t offset = 0, position; uint32_t tag; unsigned version = AIFF_C_VERSION1; AVIOContext *pb = s->pb; AVStream * st; AIFFInputContext *aiff = s->priv_data; + ID3v2ExtraMeta *id3v2_extra_meta = NULL; /* check FORM header */ filesize = get_tag(pb, &tag); @@ -215,6 +237,11 @@ static int aiff_read_header(AVFormatContext *s) while (filesize > 0) { /* parse different chunks */ size = get_tag(pb, &tag); + + if (size == AVERROR_EOF && offset > 0 && st->codecpar->block_align) { + av_log(s, AV_LOG_WARNING, "header parser hit EOF\n"); + goto got_sound; + } if (size < 0) return size; @@ -229,6 +256,18 @@ static int aiff_read_header(AVFormatContext *s) if (offset > 0) // COMM is after SSND goto got_sound; break; + case MKTAG('I', 'D', '3', ' '): + position = avio_tell(pb); + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size); + if (id3v2_extra_meta) + if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) { + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + return ret; + } + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + if (position + size > avio_tell(pb)) + avio_skip(pb, position + size - avio_tell(pb)); + break; case MKTAG('F', 'V', 'E', 'R'): /* Version chunk */ version = avio_rb32(pb); break; @@ -249,7 +288,7 @@ static int aiff_read_header(AVFormatContext *s) offset = avio_rb32(pb); /* Offset of sound data */ avio_rb32(pb); /* BlockSize... don't care */ offset += avio_tell(pb); /* Compute absolute data offset */ - if (st->codecpar->block_align) /* Assume COMM already parsed */ + if (st->codecpar->block_align && !(pb->seekable & AVIO_SEEKABLE_NORMAL)) /* Assume COMM already parsed */ goto got_sound; if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) { av_log(s, AV_LOG_ERROR, "file is not seekable\n"); @@ -260,12 +299,36 @@ static int aiff_read_header(AVFormatContext *s) case MKTAG('w', 'a', 'v', 'e'): if ((uint64_t)size > (1<<30)) return -1; - st->codecpar->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!st->codecpar->extradata) + if (ff_get_extradata(s, st->codecpar, pb, size) < 0) return AVERROR(ENOMEM); - st->codecpar->extradata_size = size; - avio_read(pb, st->codecpar->extradata, size); + if ( (st->codecpar->codec_id == AV_CODEC_ID_QDMC || st->codecpar->codec_id == AV_CODEC_ID_QDM2) + && size>=12*4 && !st->codecpar->block_align) { + st->codecpar->block_align = AV_RB32(st->codecpar->extradata+11*4); + aiff->block_duration = AV_RB32(st->codecpar->extradata+9*4); + } else if (st->codecpar->codec_id == AV_CODEC_ID_QCELP) { + char rate = 0; + if (size >= 25) + rate = st->codecpar->extradata[24]; + switch (rate) { + case 'H': // RATE_HALF + st->codecpar->block_align = 17; + break; + case 'F': // RATE_FULL + default: + st->codecpar->block_align = 35; + } + aiff->block_duration = 160; + st->codecpar->bit_rate = (int64_t)st->codecpar->sample_rate * (st->codecpar->block_align << 3) / + aiff->block_duration; + } break; + case MKTAG('C','H','A','N'): + if(ff_mov_read_chan(s, pb, st, size) < 0) + return AVERROR_INVALIDDATA; + break; + case 0: + if (offset > 0 && st->codecpar->block_align) // COMM && SSND + goto got_sound; default: /* Jump */ avio_skip(pb, size); } @@ -278,7 +341,10 @@ static int aiff_read_header(AVFormatContext *s) } got_sound: - if (!st->codecpar->block_align) { + if (!st->codecpar->block_align && st->codecpar->codec_id == AV_CODEC_ID_QCELP) { + av_log(s, AV_LOG_WARNING, "qcelp without wave chunk, assuming full rate\n"); + st->codecpar->block_align = 35; + } else if (!st->codecpar->block_align) { av_log(s, AV_LOG_ERROR, "could not find COMM tag or invalid block_align value\n"); return -1; } @@ -309,16 +375,29 @@ static int aiff_read_packet(AVFormatContext *s, if (max_size <= 0) return AVERROR_EOF; + if (!st->codecpar->block_align) { + av_log(s, AV_LOG_ERROR, "block_align not set\n"); + return AVERROR_INVALIDDATA; + } + /* Now for that packet */ - if (st->codecpar->block_align >= 33) // GSM, QCLP, IMA4 + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_ADPCM_IMA_QT: + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_QDM2: + case AV_CODEC_ID_QCELP: size = st->codecpar->block_align; - else - size = (MAX_SIZE / st->codecpar->block_align) * st->codecpar->block_align; + break; + default: + size = st->codecpar->block_align ? (MAX_SIZE / st->codecpar->block_align) * st->codecpar->block_align : MAX_SIZE; + } size = FFMIN(max_size, size); res = av_get_packet(s->pb, pkt, size); if (res < 0) return res; + if (size >= st->codecpar->block_align) + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; /* Only one stream in an AIFF file */ pkt->stream_index = 0; pkt->duration = (res / st->codecpar->block_align) * aiff->block_duration; |