From b237248e297760648307356efa5fcbfe16844829 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Sun, 20 Nov 2011 14:21:32 -0500 Subject: adx: calculate correct LPC coeffs Instead of using fixed coefficients, the correct way is to calculate the coefficients using the highpass cutoff frequency from the ADX stream header and the sample rate. --- libavcodec/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libavcodec/Makefile') diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 37aa8ee331..2cdcca2cfc 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -489,7 +489,7 @@ OBJS-$(CONFIG_PCM_U32LE_ENCODER) += pcm.o OBJS-$(CONFIG_PCM_ZORK_DECODER) += pcm.o OBJS-$(CONFIG_ADPCM_4XM_DECODER) += adpcm.o adpcm_data.o -OBJS-$(CONFIG_ADPCM_ADX_DECODER) += adxdec.o +OBJS-$(CONFIG_ADPCM_ADX_DECODER) += adxdec.o adx.o OBJS-$(CONFIG_ADPCM_ADX_ENCODER) += adxenc.o OBJS-$(CONFIG_ADPCM_CT_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_EA_DECODER) += adpcm.o -- cgit v1.2.3 From 27360ccc5edcaa1a37dec39127e87c461ce668b6 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Mon, 21 Nov 2011 01:49:37 -0500 Subject: adx: add an ADX parser. This simplifies the decoder so it doesn't have to process an in-packet header or handle arbitrary-sized packets. It also fixes decoding of files with large headers. --- libavcodec/Makefile | 1 + libavcodec/adx.h | 3 +- libavcodec/adx_parser.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/adxdec.c | 100 ++++++++++++++++++++-------------------------- libavcodec/allcodecs.c | 1 + libavcodec/version.h | 4 +- libavformat/segafilm.c | 1 + 7 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 libavcodec/adx_parser.c (limited to 'libavcodec/Makefile') diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 2cdcca2cfc..926056f42a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -595,6 +595,7 @@ OBJS-$(CONFIG_AAC_PARSER) += aac_parser.o aac_ac3_parser.o \ aacadtsdec.o mpeg4audio.o OBJS-$(CONFIG_AC3_PARSER) += ac3_parser.o ac3tab.o \ aac_ac3_parser.o +OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o adx.o OBJS-$(CONFIG_CAVSVIDEO_PARSER) += cavs_parser.o OBJS-$(CONFIG_DCA_PARSER) += dca_parser.o OBJS-$(CONFIG_DIRAC_PARSER) += dirac_parser.o diff --git a/libavcodec/adx.h b/libavcodec/adx.h index f68a3cb793..93d547dcb2 100644 --- a/libavcodec/adx.h +++ b/libavcodec/adx.h @@ -43,8 +43,7 @@ typedef struct { int channels; ADXChannelState prev[2]; int header_parsed; - unsigned char dec_temp[18*2]; - int in_temp; + int eof; int cutoff; int coeff[2]; } ADXContext; diff --git a/libavcodec/adx_parser.c b/libavcodec/adx_parser.c new file mode 100644 index 0000000000..6de5ad48d1 --- /dev/null +++ b/libavcodec/adx_parser.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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, + * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * ADX audio parser + * + * Reads header to extradata and splits packets into individual blocks. + */ + +#include "libavutil/intreadwrite.h" +#include "parser.h" +#include "adx.h" + +typedef struct ADXParseContext { + ParseContext pc; + int header_size; + int block_size; + int buf_pos; +} ADXParseContext; + +#define MIN_HEADER_SIZE 24 + +static int adx_parse(AVCodecParserContext *s1, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + ADXParseContext *s = s1->priv_data; + ParseContext *pc = &s->pc; + int next = END_NOT_FOUND; + + if (!avctx->extradata_size) { + int ret; + + ff_combine_frame(pc, END_NOT_FOUND, &buf, &buf_size); + + if (!s->header_size && pc->index >= MIN_HEADER_SIZE) { + if (ret = ff_adx_decode_header(avctx, pc->buffer, pc->index, + &s->header_size, NULL)) + return AVERROR_INVALIDDATA; + s->block_size = BLOCK_SIZE * avctx->channels; + } + if (s->header_size && s->header_size <= pc->index) { + avctx->extradata = av_mallocz(s->header_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + avctx->extradata_size = s->header_size; + memcpy(avctx->extradata, pc->buffer, s->header_size); + memmove(pc->buffer, pc->buffer + s->header_size, s->header_size); + pc->index -= s->header_size; + } + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + + if (pc->index - s->buf_pos >= s->block_size) { + *poutbuf = &pc->buffer[s->buf_pos]; + *poutbuf_size = s->block_size; + s->buf_pos += s->block_size; + return 0; + } + if (pc->index && s->buf_pos) { + memmove(pc->buffer, &pc->buffer[s->buf_pos], pc->index - s->buf_pos); + pc->index -= s->buf_pos; + s->buf_pos = 0; + } + if (buf_size + pc->index >= s->block_size) + next = s->block_size - pc->index; + + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0 || !buf_size) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; +} + +AVCodecParser ff_adx_parser = { + .codec_ids = { CODEC_ID_ADPCM_ADX }, + .priv_data_size = sizeof(ADXParseContext), + .parser_parse = adx_parse, + .parser_close = ff_parse_close, +}; diff --git a/libavcodec/adxdec.c b/libavcodec/adxdec.c index 0b0eac262c..ca96a904d6 100644 --- a/libavcodec/adxdec.c +++ b/libavcodec/adxdec.c @@ -35,6 +35,19 @@ static av_cold int adx_decode_init(AVCodecContext *avctx) { + ADXContext *c = avctx->priv_data; + int ret, header_size; + + if (avctx->extradata_size < 24) + return AVERROR_INVALIDDATA; + + if ((ret = ff_adx_decode_header(avctx, avctx->extradata, avctx->extradata_size, + &header_size, c->coeff)) < 0) { + av_log(avctx, AV_LOG_ERROR, "error parsing ADX header\n"); + return AVERROR_INVALIDDATA; + } + c->channels = avctx->channels; + avctx->sample_fmt = AV_SAMPLE_FMT_S16; return 0; } @@ -46,7 +59,7 @@ static av_cold int adx_decode_init(AVCodecContext *avctx) * 2nd-order LPC filter applied to it to form the output signal for a single * channel. */ -static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch) +static int adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch) { ADXChannelState *prev = &c->prev[ch]; GetBitContext gb; @@ -54,6 +67,10 @@ static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch) int i; int s0, s1, s2, d; + /* check if this is an EOF packet */ + if (scale & 0x8000) + return -1; + init_get_bits(&gb, in + 2, (BLOCK_SIZE - 2) * 8); s1 = prev->s1; s2 = prev->s2; @@ -67,84 +84,55 @@ static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch) } prev->s1 = s1; prev->s2 = s2; -} - -static int adx_decode_header(AVCodecContext *avctx, const uint8_t *buf, - int bufsize) -{ - ADXContext *c = avctx->priv_data; - int ret, header_size; - if ((ret = ff_adx_decode_header(avctx, buf, bufsize, &header_size, - c->coeff)) < 0) - return ret; - - c->channels = avctx->channels; - return header_size; + return 0; } static int adx_decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) { - const uint8_t *buf0 = avpkt->data; int buf_size = avpkt->size; ADXContext *c = avctx->priv_data; int16_t *samples = data; - const uint8_t *buf = buf0; - int rest = buf_size; - int num_blocks; - - if (!c->header_parsed) { - int hdrsize = adx_decode_header(avctx, buf, rest); - if (hdrsize < 0) { - av_log(avctx, AV_LOG_ERROR, "invalid stream header\n"); - return hdrsize; - } - c->header_parsed = 1; - buf += hdrsize; - rest -= hdrsize; + const uint8_t *buf = avpkt->data; + int num_blocks, ch; + + if (c->eof) { + *data_size = 0; + return buf_size; } /* 18 bytes of data are expanded into 32*2 bytes of audio, so guard against buffer overflows */ - num_blocks = (rest + c->in_temp) / (BLOCK_SIZE * c->channels); + num_blocks = buf_size / (BLOCK_SIZE * c->channels); if (num_blocks > *data_size / (BLOCK_SAMPLES * c->channels)) { - rest = (*data_size / (BLOCK_SAMPLES * c->channels)) * BLOCK_SIZE; - num_blocks = (rest + c->in_temp) / (BLOCK_SIZE * c->channels); + buf_size = (*data_size / (BLOCK_SAMPLES * c->channels)) * BLOCK_SIZE; + num_blocks = buf_size / (BLOCK_SIZE * c->channels); } - if (!num_blocks) { - av_log(avctx, AV_LOG_ERROR, "packet is too small\n"); + if (!buf_size || buf_size % (BLOCK_SIZE * avctx->channels)) { + if (buf_size >= 4 && (AV_RB16(buf) & 0x8000)) { + c->eof = 1; + *data_size = 0; + return avpkt->size; + } return AVERROR_INVALIDDATA; } - if (c->in_temp) { - int copysize = BLOCK_SIZE * avctx->channels - c->in_temp; - memcpy(c->dec_temp + c->in_temp, buf, copysize); - rest -= copysize; - buf += copysize; - adx_decode(c, samples, c->dec_temp, 0); - if (avctx->channels == 2) - adx_decode(c, samples + 1, c->dec_temp + BLOCK_SIZE, 1); - samples += BLOCK_SAMPLES * c->channels; - num_blocks--; - } - while (num_blocks--) { - adx_decode(c, samples, buf, 0); - if (c->channels == 2) - adx_decode(c, samples + 1, buf + BLOCK_SIZE, 1); - rest -= BLOCK_SIZE * c->channels; - buf += BLOCK_SIZE * c->channels; + for (ch = 0; ch < c->channels; ch++) { + if (adx_decode(c, samples + ch, buf, ch)) { + c->eof = 1; + buf = avpkt->data + avpkt->size; + break; + } + buf_size -= BLOCK_SIZE; + buf += BLOCK_SIZE; + } samples += BLOCK_SAMPLES * c->channels; } - c->in_temp = rest; - if (rest) { - memcpy(c->dec_temp, buf, rest); - buf += rest; - } *data_size = (uint8_t*)samples - (uint8_t*)data; - return buf - buf0; + return buf - avpkt->data; } AVCodec ff_adpcm_adx_decoder = { diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index db213a1ae0..82023ff230 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -388,6 +388,7 @@ void avcodec_register_all(void) REGISTER_PARSER (AAC, aac); REGISTER_PARSER (AAC_LATM, aac_latm); REGISTER_PARSER (AC3, ac3); + REGISTER_PARSER (ADX, adx); REGISTER_PARSER (CAVSVIDEO, cavsvideo); REGISTER_PARSER (DCA, dca); REGISTER_PARSER (DIRAC, dirac); diff --git a/libavcodec/version.h b/libavcodec/version.h index 2313fae021..0bd17817ec 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -21,8 +21,8 @@ #define AVCODEC_VERSION_H #define LIBAVCODEC_VERSION_MAJOR 53 -#define LIBAVCODEC_VERSION_MINOR 22 -#define LIBAVCODEC_VERSION_MICRO 1 +#define LIBAVCODEC_VERSION_MINOR 23 +#define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavformat/segafilm.c b/libavformat/segafilm.c index b75c8774ce..9bf81d30d4 100644 --- a/libavformat/segafilm.c +++ b/libavformat/segafilm.c @@ -172,6 +172,7 @@ static int film_read_header(AVFormatContext *s, if (film->audio_type == CODEC_ID_ADPCM_ADX) { st->codec->bits_per_coded_sample = 18 * 8 / 32; st->codec->block_align = st->codec->channels * 18; + st->need_parsing = AVSTREAM_PARSE_FULL; } else { st->codec->bits_per_coded_sample = film->audio_bits; st->codec->block_align = st->codec->channels * -- cgit v1.2.3 From a17c3c7d15a841ce330e142966b9420fb64cf299 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Mon, 21 Nov 2011 02:34:18 -0500 Subject: avformat: add CRI ADX format demuxer --- Changelog | 1 + doc/general.texi | 2 + libavcodec/Makefile | 1 + libavformat/Makefile | 1 + libavformat/adxdec.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++ libavformat/allformats.c | 1 + libavformat/version.h | 2 +- 7 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 libavformat/adxdec.c (limited to 'libavcodec/Makefile') diff --git a/Changelog b/Changelog index b9b5f26ae8..e5c4993dbe 100644 --- a/Changelog +++ b/Changelog @@ -102,6 +102,7 @@ easier to use. The changes are: - Discworld II BMV decoding support - VBLE Decoder - OS X Video Decoder Acceleration (VDA) support +- CRI ADX audio format demuxer version 0.7: diff --git a/doc/general.texi b/doc/general.texi index d2747c052c..9a5c405f85 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -67,6 +67,8 @@ library: @item Brute Force & Ignorance @tab @tab X @tab Used in the game Flash Traffic: City of Angels. @item BWF @tab X @tab X +@item CRI ADX @tab @tab X + @tab Audio-only format used in console video games. @item Discworld II BMV @tab @tab X @item Interplay C93 @tab @tab X @tab Used in the game Cyberia from Interplay. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 926056f42a..a78cd8171e 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -528,6 +528,7 @@ OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcmenc.o adpcm_data.o # libavformat dependencies OBJS-$(CONFIG_ADTS_MUXER) += mpeg4audio.o +OBJS-$(CONFIG_ADX_DEMUXER) += adx.o OBJS-$(CONFIG_CAF_DEMUXER) += mpeg4audio.o mpegaudiodata.o OBJS-$(CONFIG_DV_DEMUXER) += dvdata.o OBJS-$(CONFIG_DV_MUXER) += dvdata.o diff --git a/libavformat/Makefile b/libavformat/Makefile index 2b7588f1ad..3419148b56 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -21,6 +21,7 @@ OBJS-$(CONFIG_A64_MUXER) += a64.o OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o rawdec.o OBJS-$(CONFIG_AC3_DEMUXER) += ac3dec.o rawdec.o OBJS-$(CONFIG_AC3_MUXER) += rawenc.o +OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o riff.o pcm.o diff --git a/libavformat/adxdec.c b/libavformat/adxdec.c new file mode 100644 index 0000000000..76b3728b1e --- /dev/null +++ b/libavformat/adxdec.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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, + * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * CRI ADX demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/adx.h" +#include "avformat.h" + +#define BLOCK_SIZE 18 +#define BLOCK_SAMPLES 32 + +typedef struct ADXDemuxerContext { + int header_size; +} ADXDemuxerContext; + +static int adx_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ADXDemuxerContext *c = s->priv_data; + AVCodecContext *avctx = s->streams[0]->codec; + int ret, size; + + size = BLOCK_SIZE * avctx->channels; + + pkt->pos = avio_tell(s->pb); + pkt->stream_index = 0; + + ret = av_get_packet(s->pb, pkt, size); + if (ret != size) { + av_free_packet(pkt); + return ret < 0 ? ret : AVERROR(EIO); + } + if (AV_RB16(pkt->data) & 0x8000) { + av_free_packet(pkt); + return AVERROR_EOF; + } + pkt->size = size; + pkt->duration = 1; + pkt->pts = (pkt->pos - c->header_size) / size; + + return 0; +} + +static int adx_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + ADXDemuxerContext *c = s->priv_data; + AVCodecContext *avctx; + int ret; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avctx = s->streams[0]->codec; + + if (avio_rb16(s->pb) != 0x8000) + return AVERROR_INVALIDDATA; + c->header_size = avio_rb16(s->pb) + 4; + avio_seek(s->pb, -4, SEEK_CUR); + + avctx->extradata = av_mallocz(c->header_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + if (avio_read(s->pb, avctx->extradata, c->header_size) < c->header_size) { + av_freep(&avctx->extradata); + return AVERROR(EIO); + } + avctx->extradata_size = c->header_size; + + ret = ff_adx_decode_header(avctx, avctx->extradata, avctx->extradata_size, + &c->header_size, NULL); + if (ret) + return ret; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->value; + + av_set_pts_info(st, 64, BLOCK_SAMPLES, avctx->sample_rate); + + return 0; +} + +AVInputFormat ff_adx_demuxer = { + .name = "adx", + .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), + .priv_data_size = sizeof(ADXDemuxerContext), + .read_header = adx_read_header, + .read_packet = adx_read_packet, + .extensions = "adx", + .value = CODEC_ID_ADPCM_ADX, +}; diff --git a/libavformat/allformats.c b/libavformat/allformats.c index bee2f5f11f..573b714d33 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -52,6 +52,7 @@ void av_register_all(void) REGISTER_DEMUXER (AAC, aac); REGISTER_MUXDEMUX (AC3, ac3); REGISTER_MUXER (ADTS, adts); + REGISTER_DEMUXER (ADX, adx); REGISTER_DEMUXER (AEA, aea); REGISTER_MUXDEMUX (AIFF, aiff); REGISTER_MUXDEMUX (AMR, amr); diff --git a/libavformat/version.h b/libavformat/version.h index d56bdb7c87..37c3d5bc88 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -24,7 +24,7 @@ #include "libavutil/avutil.h" #define LIBAVFORMAT_VERSION_MAJOR 53 -#define LIBAVFORMAT_VERSION_MINOR 15 +#define LIBAVFORMAT_VERSION_MINOR 16 #define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- cgit v1.2.3