From f025588bb6008600195184b733800f768ed8e025 Mon Sep 17 00:00:00 2001 From: Vitor Sessak Date: Mon, 7 May 2007 08:55:27 +0000 Subject: [PATCH] RoQ audio encoder patch by Vitor vitor1001 gmail com Originally committed as revision 8922 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 2 +- libavcodec/avcodec.h | 1 + libavcodec/roqaudioenc.c | 177 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 libavcodec/roqaudioenc.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index f7d85b81ec..8cc3a01db8 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -125,6 +125,7 @@ OBJS-$(CONFIG_RA_144_DECODER) += ra144.o OBJS-$(CONFIG_RA_288_DECODER) += ra288.o OBJS-$(CONFIG_ROQ_DECODER) += roqvideo.o OBJS-$(CONFIG_ROQ_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_ROQ_DPCM_ENCODER) += roqaudioenc.o OBJS-$(CONFIG_RPZA_DECODER) += rpza.o OBJS-$(CONFIG_RV10_DECODER) += rv10.o OBJS-$(CONFIG_RV10_ENCODER) += rv10.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 28820654e1..e593a6e8f1 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -226,7 +226,7 @@ void avcodec_register_all(void) /* dpcm codecs */ REGISTER_DECODER(INTERPLAY_DPCM, interplay_dpcm); - REGISTER_DECODER(ROQ_DPCM, roq_dpcm); + REGISTER_ENCDEC (ROQ_DPCM, roq_dpcm); REGISTER_DECODER(SOL_DPCM, sol_dpcm); REGISTER_DECODER(XAN_DPCM, xan_dpcm); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index dee12c245b..29b15c7213 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -2225,6 +2225,7 @@ extern AVCodec pgm_encoder; extern AVCodec pgmyuv_encoder; extern AVCodec png_encoder; extern AVCodec ppm_encoder; +extern AVCodec roq_dpcm_encoder; extern AVCodec rv10_encoder; extern AVCodec rv20_encoder; extern AVCodec sgi_encoder; diff --git a/libavcodec/roqaudioenc.c b/libavcodec/roqaudioenc.c new file mode 100644 index 0000000000..f76079e1d1 --- /dev/null +++ b/libavcodec/roqaudioenc.c @@ -0,0 +1,177 @@ +/* + * RoQ audio encoder + * + * Copyright (c) 2005 Eric Lasota + * Based on RoQ specs (c)2001 Tim Ferguson + * + * 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 "avcodec.h" +#include "bytestream.h" + +#define ROQ_FIRST_FRAME_SIZE (735*8) +#define ROQ_FRAME_SIZE 735 + + +#define MAX_DPCM (127*127) +static unsigned char dpcmValues[MAX_DPCM]; + + +typedef struct +{ + short lastSample[2]; +} ROQDPCMContext_t; + +static void roq_dpcm_table_init(void) +{ + int i; + + /* Create a table of quick DPCM values */ + for (i=0; imid); + } +} + +static int roq_dpcm_encode_init(AVCodecContext *avctx) +{ + ROQDPCMContext_t *context = avctx->priv_data; + + if (avctx->channels > 2) { + av_log(avctx, AV_LOG_ERROR, "Audio must be mono or stereo\n"); + return -1; + } + if (avctx->sample_rate != 22050) { + av_log(avctx, AV_LOG_ERROR, "Audio must be 22050 Hz\n"); + return -1; + } + if (avctx->sample_fmt != SAMPLE_FMT_S16) { + av_log(avctx, AV_LOG_ERROR, "Audio must be signed 16-bit\n"); + return -1; + } + + roq_dpcm_table_init(); + + avctx->frame_size = ROQ_FIRST_FRAME_SIZE; + + context->lastSample[0] = context->lastSample[1] = 0; + + avctx->coded_frame= avcodec_alloc_frame(); + avctx->coded_frame->key_frame= 1; + + return 0; +} + +static unsigned char dpcm_predict(short *previous, short current) +{ + int diff; + int negative; + int result; + int predicted; + + diff = current - *previous; + + negative = diff<0; + diff = FFABS(diff); + + if (diff >= MAX_DPCM) + result = 127; + else + result = dpcmValues[diff]; + + /* See if this overflows */ + retry: + diff = result*result; + if (negative) + diff = -diff; + predicted = *previous + diff; + + /* If it overflows, back off a step */ + if (predicted > 32767 || predicted < -32768) { + result--; + goto retry; + } + + /* Add the sign bit */ + result |= negative << 7; //if (negative) result |= 128; + + *previous = predicted; + + return result; +} + +static int roq_dpcm_encode_frame(AVCodecContext *avctx, + unsigned char *frame, int buf_size, void *data) +{ + int i, samples, stereo, ch; + short *in; + unsigned char *out; + + ROQDPCMContext_t *context = avctx->priv_data; + + stereo = (avctx->channels == 2); + + if (stereo) { + context->lastSample[0] &= 0xFF00; + context->lastSample[1] &= 0xFF00; + } + + out = frame; + in = data; + + bytestream_put_byte(&out, stereo ? 0x21 : 0x20); + bytestream_put_byte(&out, 0x10); + bytestream_put_le32(&out, avctx->frame_size*avctx->channels); + + if (stereo) { + bytestream_put_byte(&out, (context->lastSample[1])>>8); + bytestream_put_byte(&out, (context->lastSample[0])>>8); + } else + bytestream_put_le16(&out, context->lastSample[0]); + + /* Write the actual samples */ + samples = avctx->frame_size; + for (i=0; ichannels; ch++) + *out++ = dpcm_predict(&context->lastSample[ch], *in++); + + /* Use smaller frames from now on */ + avctx->frame_size = ROQ_FRAME_SIZE; + + /* Return the result size */ + return out - frame; +} + +static int roq_dpcm_encode_close(AVCodecContext *avctx) +{ + av_freep(&avctx->coded_frame); + + return 0; +} + +AVCodec roq_dpcm_encoder = { + "roq_dpcm", + CODEC_TYPE_AUDIO, + CODEC_ID_ROQ_DPCM, + sizeof(ROQDPCMContext_t), + roq_dpcm_encode_init, + roq_dpcm_encode_frame, + roq_dpcm_encode_close, + NULL, +};