diff options
author | Martin Storsjö <martin@martin.st> | 2012-10-23 00:02:41 +0300 |
---|---|---|
committer | Martin Storsjö <martin@martin.st> | 2012-10-29 22:00:43 +0200 |
commit | 9b500b8f6c9806f3979f9d1fb874b7f4a802c656 (patch) | |
tree | 88427520e9cc033bbe6e6c9e506856e440cf3dd1 /libavcodec/cngenc.c | |
parent | 8b25a20efbf4ca261bcd3327a385330eca775ec6 (diff) | |
download | ffmpeg-9b500b8f6c9806f3979f9d1fb874b7f4a802c656.tar.gz |
avcodec: Add a RFC 3389 comfort noise codec
This isn't too useful as a normal codec, but can be used in
voip style applications. The decoder updates the noise
generator parameters when a packet is given to it for decoding,
but if called with an empty packet, it generates more noise
according to the last parameters.
Signed-off-by: Martin Storsjö <martin@martin.st>
Diffstat (limited to 'libavcodec/cngenc.c')
-rw-r--r-- | libavcodec/cngenc.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/libavcodec/cngenc.c b/libavcodec/cngenc.c new file mode 100644 index 0000000000..1e3f8f0c8d --- /dev/null +++ b/libavcodec/cngenc.c @@ -0,0 +1,116 @@ +/* + * RFC 3389 comfort noise generator + * Copyright (c) 2012 Martin Storsjo + * + * 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 + */ + +#include <math.h> + +#include "libavutil/common.h" +#include "avcodec.h" +#include "internal.h" +#include "lpc.h" + +typedef struct CNGContext { + LPCContext lpc; + int order; + int32_t *samples32; + double *ref_coef; +} CNGContext; + +static av_cold int cng_encode_close(AVCodecContext *avctx) +{ + CNGContext *p = avctx->priv_data; + ff_lpc_end(&p->lpc); + av_free(p->samples32); + av_free(p->ref_coef); + return 0; +} + +static av_cold int cng_encode_init(AVCodecContext *avctx) +{ + CNGContext *p = avctx->priv_data; + int ret; + + if (avctx->channels != 1) { + av_log(avctx, AV_LOG_ERROR, "Only mono supported\n"); + return AVERROR(EINVAL); + } + + avctx->frame_size = 640; + p->order = 10; + if ((ret = ff_lpc_init(&p->lpc, avctx->frame_size, p->order, FF_LPC_TYPE_LEVINSON)) < 0) + return ret; + p->samples32 = av_malloc(avctx->frame_size * sizeof(*p->samples32)); + p->ref_coef = av_malloc(p->order * sizeof(*p->ref_coef)); + if (!p->samples32 || !p->ref_coef) { + cng_encode_close(avctx); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int cng_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr) +{ + CNGContext *p = avctx->priv_data; + int ret, i; + double energy = 0; + int qdbov; + int16_t *samples = (int16_t*) frame->data[0]; + + if ((ret = ff_alloc_packet(avpkt, 1 + p->order))) { + av_log(avctx, AV_LOG_ERROR, "Error getting output packet\n"); + return ret; + } + + for (i = 0; i < frame->nb_samples; i++) { + p->samples32[i] = samples[i]; + energy += samples[i] * samples[i]; + } + energy /= frame->nb_samples; + if (energy > 0) { + double dbov = 10 * log10(energy / 1081109975); + qdbov = av_clip(-floor(dbov), 0, 127); + } else { + qdbov = 127; + } + ret = ff_lpc_calc_ref_coefs(&p->lpc, p->samples32, p->order, p->ref_coef); + avpkt->data[0] = qdbov; + for (i = 0; i < p->order; i++) + avpkt->data[1 + i] = p->ref_coef[i] * 127 + 127; + + *got_packet_ptr = 1; + avpkt->size = 1 + p->order; + + return 0; +} + +AVCodec ff_comfortnoise_encoder = { + .name = "comfortnoise", + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_COMFORT_NOISE, + .priv_data_size = sizeof(CNGContext), + .init = cng_encode_init, + .encode2 = cng_encode_frame, + .close = cng_encode_close, + .long_name = NULL_IF_CONFIG_SMALL("RFC 3389 comfort noise generator"), + .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, + AV_SAMPLE_FMT_NONE }, +}; |