avcodec: Add a RFC 3389 comfort noise codec
authorMartin Storsjö <martin@martin.st>
Mon, 22 Oct 2012 21:02:41 +0000 (00:02 +0300)
committerMartin Storsjö <martin@martin.st>
Mon, 29 Oct 2012 20:00:43 +0000 (22:00 +0200)
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>
configure
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/cngdec.c [new file with mode: 0644]
libavcodec/cngenc.c [new file with mode: 0644]
libavcodec/codec_desc.c
libavcodec/version.h

index 1a006be..2abc465 100755 (executable)
--- a/configure
+++ b/configure
@@ -1440,6 +1440,7 @@ atrac3_decoder_select="mdct"
 binkaudio_dct_decoder_select="mdct rdft dct sinewin"
 binkaudio_rdft_decoder_select="mdct rdft sinewin"
 cavs_decoder_select="golomb mpegvideo"
+comfortnoise_encoder_select="lpc"
 cook_decoder_select="mdct sinewin"
 cscd_decoder_select="lzo"
 cscd_decoder_suggest="zlib"
index d8c853a..4d14aea 100644 (file)
@@ -129,6 +129,8 @@ OBJS-$(CONFIG_CLJR_DECODER)            += cljr.o
 OBJS-$(CONFIG_CLJR_ENCODER)            += cljr.o
 OBJS-$(CONFIG_CLLC_DECODER)            += cllc.o
 OBJS-$(CONFIG_COOK_DECODER)            += cook.o
+OBJS-$(CONFIG_COMFORTNOISE_DECODER)    += cngdec.o celp_filters.o
+OBJS-$(CONFIG_COMFORTNOISE_ENCODER)    += cngenc.o
 OBJS-$(CONFIG_CSCD_DECODER)            += cscd.o
 OBJS-$(CONFIG_CYUV_DECODER)            += cyuv.o
 OBJS-$(CONFIG_DCA_DECODER)             += dcadec.o dca.o dcadsp.o      \
index b175fbb..e5fb351 100644 (file)
@@ -91,6 +91,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER (CINEPAK, cinepak);
     REGISTER_ENCDEC  (CLJR, cljr);
     REGISTER_DECODER (CLLC, cllc);
+    REGISTER_ENCDEC  (COMFORTNOISE, comfortnoise);
     REGISTER_DECODER (CSCD, cscd);
     REGISTER_DECODER (CYUV, cyuv);
     REGISTER_DECODER (DFA, dfa);
index c505a92..d6a4e4d 100644 (file)
@@ -406,6 +406,7 @@ enum AVCodecID {
     AV_CODEC_ID_IAC,
     AV_CODEC_ID_ILBC,
     AV_CODEC_ID_OPUS,
+    AV_CODEC_ID_COMFORT_NOISE,
 
     /* subtitle codecs */
     AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing at the start of subtitle codecs.
diff --git a/libavcodec/cngdec.c b/libavcodec/cngdec.c
new file mode 100644 (file)
index 0000000..c22fd55
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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 "celp_filters.h"
+#include "libavutil/lfg.h"
+
+typedef struct CNGContext {
+    AVFrame avframe;
+    float *refl_coef, *target_refl_coef;
+    float *lpc_coef;
+    int order;
+    int energy, target_energy;
+    float *filter_out;
+    float *excitation;
+    AVLFG lfg;
+} CNGContext;
+
+static av_cold int cng_decode_close(AVCodecContext *avctx)
+{
+    CNGContext *p = avctx->priv_data;
+    av_free(p->refl_coef);
+    av_free(p->target_refl_coef);
+    av_free(p->lpc_coef);
+    av_free(p->filter_out);
+    av_free(p->excitation);
+    return 0;
+}
+
+static av_cold int cng_decode_init(AVCodecContext *avctx)
+{
+    CNGContext *p = avctx->priv_data;
+
+    avctx->sample_fmt  = AV_SAMPLE_FMT_S16;
+    avctx->channels    = 1;
+    avctx->sample_rate = 8000;
+
+    avcodec_get_frame_defaults(&p->avframe);
+    avctx->coded_frame  = &p->avframe;
+    p->order            = 12;
+    avctx->frame_size   = 640;
+    p->refl_coef        = av_mallocz(p->order * sizeof(*p->refl_coef));
+    p->target_refl_coef = av_mallocz(p->order * sizeof(*p->target_refl_coef));
+    p->lpc_coef         = av_mallocz(p->order * sizeof(*p->lpc_coef));
+    p->filter_out       = av_mallocz((avctx->frame_size + p->order) *
+                                     sizeof(*p->filter_out));
+    p->excitation       = av_mallocz(avctx->frame_size * sizeof(*p->excitation));
+    if (!p->refl_coef || !p->target_refl_coef || !p->lpc_coef ||
+        !p->filter_out || !p->excitation) {
+        cng_decode_close(avctx);
+        return AVERROR(ENOMEM);
+    }
+
+    av_lfg_init(&p->lfg, 0);
+
+    return 0;
+}
+
+static void make_lpc_coefs(float *lpc, const float *refl, int order)
+{
+    float buf[100];
+    float *next, *cur;
+    int m, i;
+    next = buf;
+    cur  = lpc;
+    for (m = 0; m < order; m++) {
+        next[m] = refl[m];
+        for (i = 0; i < m; i++)
+            next[i] = cur[i] + refl[m] * cur[m - i - 1];
+        FFSWAP(float*, next, cur);
+    }
+    if (cur != lpc)
+        memcpy(lpc, cur, sizeof(*lpc) * order);
+}
+
+static int cng_decode_frame(AVCodecContext *avctx, void *data,
+                              int *got_frame_ptr, AVPacket *avpkt)
+{
+
+    CNGContext *p = avctx->priv_data;
+    int buf_size  = avpkt->size;
+    int ret, i;
+    int16_t *buf_out;
+    float e = 1.0;
+    float scaling;
+
+    if (avpkt->size) {
+        float dbov = -avpkt->data[0] / 10.0;
+        p->target_energy = 1081109975 * pow(10, dbov) * 0.75;
+        memset(p->target_refl_coef, 0, sizeof(p->refl_coef));
+        for (i = 0; i < FFMIN(avpkt->size - 1, p->order); i++) {
+            p->target_refl_coef[i] = (avpkt->data[1 + i] - 127) / 128.0;
+        }
+        make_lpc_coefs(p->lpc_coef, p->refl_coef, p->order);
+    }
+
+    p->energy = p->energy / 2 + p->target_energy / 2;
+    for (i = 0; i < p->order; i++)
+        p->refl_coef[i] = 0.6 *p->refl_coef[i] + 0.4 * p->target_refl_coef[i];
+
+    for (i = 0; i < p->order; i++)
+        e *= 1.0 - p->refl_coef[i]*p->refl_coef[i];
+
+    scaling = sqrt(e * p->energy / 1081109975);
+    for (i = 0; i < avctx->frame_size; i++) {
+        int r = (av_lfg_get(&p->lfg) & 0xffff) - 0x8000;
+        p->excitation[i] = scaling * r;
+    }
+    ff_celp_lp_synthesis_filterf(p->filter_out + p->order, p->lpc_coef,
+                                 p->excitation, avctx->frame_size, p->order);
+
+    p->avframe.nb_samples = avctx->frame_size;
+    if ((ret = avctx->get_buffer(avctx, &p->avframe)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+        return ret;
+    }
+    buf_out = (int16_t *)p->avframe.data[0];
+    for (i = 0; i < avctx->frame_size; i++)
+        buf_out[i] = p->filter_out[i + p->order];
+    memcpy(p->filter_out, p->filter_out + avctx->frame_size,
+           p->order * sizeof(*p->filter_out));
+
+    *got_frame_ptr   = 1;
+    *(AVFrame *)data = p->avframe;
+
+    return buf_size;
+}
+
+AVCodec ff_comfortnoise_decoder = {
+    .name           = "comfortnoise",
+    .type           = AVMEDIA_TYPE_AUDIO,
+    .id             = AV_CODEC_ID_COMFORT_NOISE,
+    .priv_data_size = sizeof(CNGContext),
+    .init           = cng_decode_init,
+    .decode         = cng_decode_frame,
+    .close          = cng_decode_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 },
+    .capabilities   = CODEC_CAP_DELAY | CODEC_CAP_DR1,
+};
diff --git a/libavcodec/cngenc.c b/libavcodec/cngenc.c
new file mode 100644 (file)
index 0000000..1e3f8f0
--- /dev/null
@@ -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 },
+};
index 818a35e..a8ff314 100644 (file)
@@ -2112,6 +2112,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Opus (Opus Interactive Audio Codec)"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_COMFORT_NOISE,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "comfortnoise",
+        .long_name = NULL_IF_CONFIG_SMALL("RFC 3389 Comfort Noise"),
+        .props     = AV_CODEC_PROP_LOSSY,
+    },
 
     /* subtitle codecs */
     {
index b702f4b..5ee7c7c 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 #define LIBAVCODEC_VERSION_MAJOR 54
-#define LIBAVCODEC_VERSION_MINOR 31
+#define LIBAVCODEC_VERSION_MINOR 32
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \