00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/mathematics.h"
00023 #include "libavutil/dict.h"
00024 #include "avformat.h"
00025 #include "internal.h"
00026 #include "pcm.h"
00027 #include "aiff.h"
00028 #include "isom.h"
00029
00030 #define AIFF 0
00031 #define AIFF_C_VERSION1 0xA2805140
00032
00033 typedef struct {
00034 int64_t data_end;
00035 } AIFFInputContext;
00036
00037 static enum CodecID aiff_codec_get_id(int bps)
00038 {
00039 if (bps <= 8)
00040 return CODEC_ID_PCM_S8;
00041 if (bps <= 16)
00042 return CODEC_ID_PCM_S16BE;
00043 if (bps <= 24)
00044 return CODEC_ID_PCM_S24BE;
00045 if (bps <= 32)
00046 return CODEC_ID_PCM_S32BE;
00047
00048
00049 return CODEC_ID_NONE;
00050 }
00051
00052
00053 static int get_tag(AVIOContext *pb, uint32_t * tag)
00054 {
00055 int size;
00056
00057 if (url_feof(pb))
00058 return AVERROR(EIO);
00059
00060 *tag = avio_rl32(pb);
00061 size = avio_rb32(pb);
00062
00063 if (size < 0)
00064 size = 0x7fffffff;
00065
00066 return size;
00067 }
00068
00069
00070 static void get_meta(AVFormatContext *s, const char *key, int size)
00071 {
00072 uint8_t *str = av_malloc(size+1);
00073
00074 if (str) {
00075 int res = avio_read(s->pb, str, size);
00076 if (res < 0){
00077 av_free(str);
00078 return;
00079 }
00080 size += (size&1)-res;
00081 str[res] = 0;
00082 av_dict_set(&s->metadata, key, str, AV_METADATA_DONT_STRDUP_VAL);
00083 }else
00084 size+= size&1;
00085
00086 avio_skip(s->pb, size);
00087 }
00088
00089
00090 static unsigned int get_aiff_header(AVIOContext *pb, AVCodecContext *codec,
00091 int size, unsigned version)
00092 {
00093 int exp;
00094 uint64_t val;
00095 double sample_rate;
00096 unsigned int num_frames;
00097
00098 if (size & 1)
00099 size++;
00100 codec->codec_type = AVMEDIA_TYPE_AUDIO;
00101 codec->channels = avio_rb16(pb);
00102 num_frames = avio_rb32(pb);
00103 codec->bits_per_coded_sample = avio_rb16(pb);
00104
00105 exp = avio_rb16(pb);
00106 val = avio_rb64(pb);
00107 sample_rate = ldexp(val, exp - 16383 - 63);
00108 codec->sample_rate = sample_rate;
00109 size -= 18;
00110
00111
00112 if (version == AIFF_C_VERSION1) {
00113 codec->codec_tag = avio_rl32(pb);
00114 codec->codec_id = ff_codec_get_id(ff_codec_aiff_tags, codec->codec_tag);
00115
00116 switch (codec->codec_id) {
00117 case CODEC_ID_PCM_S16BE:
00118 codec->codec_id = aiff_codec_get_id(codec->bits_per_coded_sample);
00119 codec->bits_per_coded_sample = av_get_bits_per_sample(codec->codec_id);
00120 break;
00121 case CODEC_ID_ADPCM_IMA_QT:
00122 codec->block_align = 34*codec->channels;
00123 codec->frame_size = 64;
00124 break;
00125 case CODEC_ID_MACE3:
00126 codec->block_align = 2*codec->channels;
00127 codec->frame_size = 6;
00128 break;
00129 case CODEC_ID_MACE6:
00130 codec->block_align = 1*codec->channels;
00131 codec->frame_size = 6;
00132 break;
00133 case CODEC_ID_GSM:
00134 codec->block_align = 33;
00135 codec->frame_size = 160;
00136 break;
00137 case CODEC_ID_QCELP:
00138 codec->block_align = 35;
00139 codec->frame_size= 160;
00140 break;
00141 default:
00142 break;
00143 }
00144 size -= 4;
00145 } else {
00146
00147 codec->codec_id = aiff_codec_get_id(codec->bits_per_coded_sample);
00148 codec->bits_per_coded_sample = av_get_bits_per_sample(codec->codec_id);
00149 }
00150
00151
00152
00153 if (!codec->block_align)
00154 codec->block_align = (codec->bits_per_coded_sample * codec->channels) >> 3;
00155
00156 codec->bit_rate = (codec->frame_size ? codec->sample_rate/codec->frame_size :
00157 codec->sample_rate) * (codec->block_align << 3);
00158
00159
00160 if (size)
00161 avio_skip(pb, size);
00162
00163 return num_frames;
00164 }
00165
00166 static int aiff_probe(AVProbeData *p)
00167 {
00168
00169 if (p->buf[0] == 'F' && p->buf[1] == 'O' &&
00170 p->buf[2] == 'R' && p->buf[3] == 'M' &&
00171 p->buf[8] == 'A' && p->buf[9] == 'I' &&
00172 p->buf[10] == 'F' && (p->buf[11] == 'F' || p->buf[11] == 'C'))
00173 return AVPROBE_SCORE_MAX;
00174 else
00175 return 0;
00176 }
00177
00178
00179 static int aiff_read_header(AVFormatContext *s,
00180 AVFormatParameters *ap)
00181 {
00182 int size, filesize;
00183 int64_t offset = 0;
00184 uint32_t tag;
00185 unsigned version = AIFF_C_VERSION1;
00186 AVIOContext *pb = s->pb;
00187 AVStream * st;
00188 AIFFInputContext *aiff = s->priv_data;
00189
00190
00191 filesize = get_tag(pb, &tag);
00192 if (filesize < 0 || tag != MKTAG('F', 'O', 'R', 'M'))
00193 return AVERROR_INVALIDDATA;
00194
00195
00196 tag = avio_rl32(pb);
00197 if (tag == MKTAG('A', 'I', 'F', 'F'))
00198 version = AIFF;
00199 else if (tag != MKTAG('A', 'I', 'F', 'C'))
00200 return AVERROR_INVALIDDATA;
00201
00202 filesize -= 4;
00203
00204 st = avformat_new_stream(s, NULL);
00205 if (!st)
00206 return AVERROR(ENOMEM);
00207
00208 while (filesize > 0) {
00209
00210 size = get_tag(pb, &tag);
00211 if (size < 0)
00212 return size;
00213
00214 filesize -= size + 8;
00215
00216 switch (tag) {
00217 case MKTAG('C', 'O', 'M', 'M'):
00218
00219 st->nb_frames = get_aiff_header(pb, st->codec, size, version);
00220 if (st->nb_frames < 0)
00221 return st->nb_frames;
00222 if (offset > 0)
00223 goto got_sound;
00224 break;
00225 case MKTAG('F', 'V', 'E', 'R'):
00226 version = avio_rb32(pb);
00227 break;
00228 case MKTAG('N', 'A', 'M', 'E'):
00229 get_meta(s, "title" , size);
00230 break;
00231 case MKTAG('A', 'U', 'T', 'H'):
00232 get_meta(s, "author" , size);
00233 break;
00234 case MKTAG('(', 'c', ')', ' '):
00235 get_meta(s, "copyright", size);
00236 break;
00237 case MKTAG('A', 'N', 'N', 'O'):
00238 get_meta(s, "comment" , size);
00239 break;
00240 case MKTAG('S', 'S', 'N', 'D'):
00241 aiff->data_end = avio_tell(pb) + size;
00242 offset = avio_rb32(pb);
00243 avio_rb32(pb);
00244 offset += avio_tell(pb);
00245 if (st->codec->block_align)
00246 goto got_sound;
00247 if (!pb->seekable) {
00248 av_log(s, AV_LOG_ERROR, "file is not seekable\n");
00249 return -1;
00250 }
00251 avio_skip(pb, size - 8);
00252 break;
00253 case MKTAG('w', 'a', 'v', 'e'):
00254 if ((uint64_t)size > (1<<30))
00255 return -1;
00256 st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
00257 if (!st->codec->extradata)
00258 return AVERROR(ENOMEM);
00259 st->codec->extradata_size = size;
00260 avio_read(pb, st->codec->extradata, size);
00261 break;
00262 case MKTAG('C','H','A','N'):
00263 if (size < 12)
00264 return AVERROR_INVALIDDATA;
00265 ff_mov_read_chan(s, size, st->codec);
00266 break;
00267 default:
00268 if (size & 1)
00269 size++;
00270 avio_skip(pb, size);
00271 }
00272 }
00273
00274 got_sound:
00275 if (!st->codec->block_align) {
00276 av_log(s, AV_LOG_ERROR, "could not find COMM tag or invalid block_align value\n");
00277 return -1;
00278 }
00279
00280
00281 avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
00282 st->start_time = 0;
00283 st->duration = st->codec->frame_size ?
00284 st->nb_frames * st->codec->frame_size : st->nb_frames;
00285
00286
00287 avio_seek(pb, offset, SEEK_SET);
00288
00289 return 0;
00290 }
00291
00292 #define MAX_SIZE 4096
00293
00294 static int aiff_read_packet(AVFormatContext *s,
00295 AVPacket *pkt)
00296 {
00297 AVStream *st = s->streams[0];
00298 AIFFInputContext *aiff = s->priv_data;
00299 int64_t max_size;
00300 int res, size;
00301
00302
00303 max_size = aiff->data_end - avio_tell(s->pb);
00304 if (max_size <= 0)
00305 return AVERROR_EOF;
00306
00307
00308 if (st->codec->block_align >= 33)
00309 size = st->codec->block_align;
00310 else
00311 size = (MAX_SIZE / st->codec->block_align) * st->codec->block_align;
00312 size = FFMIN(max_size, size);
00313 res = av_get_packet(s->pb, pkt, size);
00314 if (res < 0)
00315 return res;
00316
00317
00318 pkt->stream_index = 0;
00319 return 0;
00320 }
00321
00322 AVInputFormat ff_aiff_demuxer = {
00323 .name = "aiff",
00324 .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"),
00325 .priv_data_size = sizeof(AIFFInputContext),
00326 .read_probe = aiff_probe,
00327 .read_header = aiff_read_header,
00328 .read_packet = aiff_read_packet,
00329 .read_seek = pcm_read_seek,
00330 .codec_tag= (const AVCodecTag* const []){ff_codec_aiff_tags, 0},
00331 };