summaryrefslogtreecommitdiff
path: root/modules/opus
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
commit98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch)
treeebc6ec71f44bff8c42e4eefced61948623df02fc /modules/opus
parente6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff)
baresip 0.4.10
Diffstat (limited to 'modules/opus')
-rw-r--r--modules/opus/decode.c101
-rw-r--r--modules/opus/encode.c170
-rw-r--r--modules/opus/module.mk14
-rw-r--r--modules/opus/opus.c63
-rw-r--r--modules/opus/opus.h34
-rw-r--r--modules/opus/sdp.c51
6 files changed, 433 insertions, 0 deletions
diff --git a/modules/opus/decode.c b/modules/opus/decode.c
new file mode 100644
index 0000000..f2d67b1
--- /dev/null
+++ b/modules/opus/decode.c
@@ -0,0 +1,101 @@
+/**
+ * @file opus/decode.c Opus Decode
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <opus/opus.h>
+#include "opus.h"
+
+
+struct audec_state {
+ OpusDecoder *dec;
+ unsigned ch;
+};
+
+
+static void destructor(void *arg)
+{
+ struct audec_state *ads = arg;
+
+ if (ads->dec)
+ opus_decoder_destroy(ads->dec);
+}
+
+
+int opus_decode_update(struct audec_state **adsp, const struct aucodec *ac,
+ const char *fmtp)
+{
+ struct audec_state *ads;
+ int opuserr, err = 0;
+ (void)fmtp;
+
+ if (!adsp || !ac || !ac->ch)
+ return EINVAL;
+
+ ads = *adsp;
+
+ if (ads)
+ return 0;
+
+ ads = mem_zalloc(sizeof(*ads), destructor);
+ if (!ads)
+ return ENOMEM;
+
+ ads->ch = ac->ch;
+
+ ads->dec = opus_decoder_create(ac->srate, ac->ch, &opuserr);
+ if (!ads->dec) {
+ warning("opus: decoder create: %s\n", opus_strerror(opuserr));
+ err = ENOMEM;
+ goto out;
+ }
+
+ out:
+ if (err)
+ mem_deref(ads);
+ else
+ *adsp = ads;
+
+ return err;
+}
+
+
+int opus_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc,
+ const uint8_t *buf, size_t len)
+{
+ int n;
+
+ if (!ads || !sampv || !sampc || !buf)
+ return EINVAL;
+
+ n = opus_decode(ads->dec, buf, (opus_int32)len,
+ sampv, (int)(*sampc/ads->ch), 0);
+ if (n < 0) {
+ warning("opus: decode error: %s\n", opus_strerror(n));
+ return EPROTO;
+ }
+
+ *sampc = n * ads->ch;
+
+ return 0;
+}
+
+
+int opus_decode_pkloss(struct audec_state *ads, int16_t *sampv, size_t *sampc)
+{
+ int n;
+
+ if (!ads || !sampv || !sampc)
+ return EINVAL;
+
+ n = opus_decode(ads->dec, NULL, 0, sampv, (int)(*sampc/ads->ch), 0);
+ if (n < 0)
+ return EPROTO;
+
+ *sampc = n * ads->ch;
+
+ return 0;
+}
diff --git a/modules/opus/encode.c b/modules/opus/encode.c
new file mode 100644
index 0000000..3272490
--- /dev/null
+++ b/modules/opus/encode.c
@@ -0,0 +1,170 @@
+/**
+ * @file opus/encode.c Opus Encode
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <opus/opus.h>
+#include "opus.h"
+
+
+struct auenc_state {
+ OpusEncoder *enc;
+ unsigned ch;
+};
+
+
+static void destructor(void *arg)
+{
+ struct auenc_state *aes = arg;
+
+ if (aes->enc)
+ opus_encoder_destroy(aes->enc);
+}
+
+
+static opus_int32 srate2bw(opus_int32 srate)
+{
+ if (srate >= 48000)
+ return OPUS_BANDWIDTH_FULLBAND;
+ else if (srate >= 24000)
+ return OPUS_BANDWIDTH_SUPERWIDEBAND;
+ else if (srate >= 16000)
+ return OPUS_BANDWIDTH_WIDEBAND;
+ else if (srate >= 12000)
+ return OPUS_BANDWIDTH_MEDIUMBAND;
+ else
+ return OPUS_BANDWIDTH_NARROWBAND;
+}
+
+
+#if 0
+static const char *bwname(opus_int32 bw)
+{
+ switch (bw) {
+ case OPUS_BANDWIDTH_FULLBAND: return "full";
+ case OPUS_BANDWIDTH_SUPERWIDEBAND: return "superwide";
+ case OPUS_BANDWIDTH_WIDEBAND: return "wide";
+ case OPUS_BANDWIDTH_MEDIUMBAND: return "medium";
+ case OPUS_BANDWIDTH_NARROWBAND: return "narrow";
+ default: return "???";
+ }
+}
+
+
+static const char *chname(opus_int32 ch)
+{
+ switch (ch) {
+ case OPUS_AUTO: return "auto";
+ case 1: return "mono";
+ case 2: return "stereo";
+ default: return "???";
+ }
+}
+#endif
+
+
+int opus_encode_update(struct auenc_state **aesp, const struct aucodec *ac,
+ struct auenc_param *param, const char *fmtp)
+{
+ struct auenc_state *aes;
+ struct opus_param prm;
+ opus_int32 fch, vbr;
+ (void)param;
+
+ if (!aesp || !ac || !ac->ch)
+ return EINVAL;
+
+ aes = *aesp;
+
+ if (!aes) {
+ const opus_int32 complex = 10;
+ int opuserr;
+
+ aes = mem_zalloc(sizeof(*aes), destructor);
+ if (!aes)
+ return ENOMEM;
+
+ aes->ch = ac->ch;
+
+ aes->enc = opus_encoder_create(ac->srate, ac->ch,
+ /* this has big impact on cpu */
+ OPUS_APPLICATION_AUDIO,
+ &opuserr);
+ if (!aes->enc) {
+ warning("opus: encoder create: %s\n",
+ opus_strerror(opuserr));
+ mem_deref(aes);
+ return ENOMEM;
+ }
+
+ (void)opus_encoder_ctl(aes->enc, OPUS_SET_COMPLEXITY(complex));
+
+ *aesp = aes;
+ }
+
+ prm.srate = 48000;
+ prm.bitrate = OPUS_AUTO;
+ prm.stereo = 1;
+ prm.cbr = 0;
+ prm.inband_fec = 0;
+ prm.dtx = 0;
+
+ opus_decode_fmtp(&prm, fmtp);
+
+ fch = prm.stereo ? OPUS_AUTO : 1;
+ vbr = prm.cbr ? 0 : 1;
+
+ (void)opus_encoder_ctl(aes->enc,
+ OPUS_SET_MAX_BANDWIDTH(srate2bw(prm.srate)));
+ (void)opus_encoder_ctl(aes->enc, OPUS_SET_BITRATE(prm.bitrate));
+ (void)opus_encoder_ctl(aes->enc, OPUS_SET_FORCE_CHANNELS(fch));
+ (void)opus_encoder_ctl(aes->enc, OPUS_SET_VBR(vbr));
+ (void)opus_encoder_ctl(aes->enc, OPUS_SET_INBAND_FEC(prm.inband_fec));
+ (void)opus_encoder_ctl(aes->enc, OPUS_SET_DTX(prm.dtx));
+
+
+#if 0
+ {
+ opus_int32 bw, complex;
+
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_MAX_BANDWIDTH(&bw));
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_BITRATE(&prm.bitrate));
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_FORCE_CHANNELS(&fch));
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_VBR(&vbr));
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_INBAND_FEC(&prm.inband_fec));
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_DTX(&prm.dtx));
+ (void)opus_encoder_ctl(aes->enc, OPUS_GET_COMPLEXITY(&complex));
+
+ debug("opus: encode bw=%s bitrate=%i fch=%s "
+ "vbr=%i fec=%i dtx=%i complex=%i\n",
+ bwname(bw), prm.bitrate, chname(fch),
+ vbr, prm.inband_fec, prm.dtx, complex);
+ }
+#endif
+
+ return 0;
+}
+
+
+int opus_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len,
+ const int16_t *sampv, size_t sampc)
+{
+ opus_int32 n;
+
+ if (!aes || !buf || !len || !sampv)
+ return EINVAL;
+
+ n = opus_encode(aes->enc, sampv, (int)(sampc/aes->ch),
+ buf, (opus_int32)(*len));
+ if (n < 0) {
+ warning("opus: encode error: %s\n", opus_strerror((int)n));
+ return EPROTO;
+ }
+
+ *len = n;
+
+ return 0;
+}
diff --git a/modules/opus/module.mk b/modules/opus/module.mk
new file mode 100644
index 0000000..ded0f13
--- /dev/null
+++ b/modules/opus/module.mk
@@ -0,0 +1,14 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := opus
+$(MOD)_SRCS += decode.c
+$(MOD)_SRCS += encode.c
+$(MOD)_SRCS += opus.c
+$(MOD)_SRCS += sdp.c
+$(MOD)_LFLAGS += -lopus -lm
+
+include mk/mod.mk
diff --git a/modules/opus/opus.c b/modules/opus/opus.c
new file mode 100644
index 0000000..28b24b9
--- /dev/null
+++ b/modules/opus/opus.c
@@ -0,0 +1,63 @@
+/**
+ * @file opus.c Opus Audio Codec
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <opus/opus.h>
+#include "opus.h"
+
+
+/**
+ * @defgroup opus opus
+ *
+ * The OPUS audio codec
+ *
+ * Latest supported version: libopus 1.0.0
+ *
+ * References:
+ *
+ * draft-ietf-codec-opus-10
+ * draft-spittka-payload-rtp-opus-00
+ *
+ * http://opus-codec.org/downloads/
+ */
+
+
+static struct aucodec opus = {
+ .name = "opus",
+ .srate = 48000,
+ .ch = 2,
+ .fmtp = "stereo=1;sprop-stereo=1",
+ .encupdh = opus_encode_update,
+ .ench = opus_encode_frm,
+ .decupdh = opus_decode_update,
+ .dech = opus_decode_frm,
+ .plch = opus_decode_pkloss,
+};
+
+
+static int module_init(void)
+{
+ aucodec_register(&opus);
+
+ return 0;
+}
+
+
+static int module_close(void)
+{
+ aucodec_unregister(&opus);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(opus) = {
+ "opus",
+ "audio codec",
+ module_init,
+ module_close,
+};
diff --git a/modules/opus/opus.h b/modules/opus/opus.h
new file mode 100644
index 0000000..2bed161
--- /dev/null
+++ b/modules/opus/opus.h
@@ -0,0 +1,34 @@
+/**
+ * @file opus.h Private Opus Interface
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+struct opus_param {
+ opus_int32 srate;
+ opus_int32 bitrate;
+ opus_int32 stereo;
+ opus_int32 cbr;
+ opus_int32 inband_fec;
+ opus_int32 dtx;
+};
+
+
+/* Encode */
+int opus_encode_update(struct auenc_state **aesp, const struct aucodec *ac,
+ struct auenc_param *prm, const char *fmtp);
+int opus_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len,
+ const int16_t *sampv, size_t sampc);
+
+
+/* Decode */
+int opus_decode_update(struct audec_state **adsp, const struct aucodec *ac,
+ const char *fmtp);
+int opus_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc,
+ const uint8_t *buf, size_t len);
+int opus_decode_pkloss(struct audec_state *st, int16_t *sampv, size_t *sampc);
+
+
+/* SDP */
+void opus_decode_fmtp(struct opus_param *prm, const char *fmtp);
diff --git a/modules/opus/sdp.c b/modules/opus/sdp.c
new file mode 100644
index 0000000..024c8a6
--- /dev/null
+++ b/modules/opus/sdp.c
@@ -0,0 +1,51 @@
+/**
+ * @file opus/sdp.c Opus SDP Functions
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <opus/opus.h>
+#include "opus.h"
+
+
+static void assign_if(opus_int32 *v, const struct pl *pl,
+ uint32_t min, uint32_t max)
+{
+ const uint32_t val = pl_u32(pl);
+
+ if (val < min || val > max)
+ return;
+
+ *v = val;
+}
+
+
+void opus_decode_fmtp(struct opus_param *prm, const char *fmtp)
+{
+ struct pl pl, val;
+
+ if (!prm || !fmtp)
+ return;
+
+ pl_set_str(&pl, fmtp);
+
+ if (fmt_param_get(&pl, "maxplaybackrate", &val))
+ assign_if(&prm->srate, &val, 8000, 48000);
+
+ if (fmt_param_get(&pl, "maxaveragebitrate", &val))
+ assign_if(&prm->bitrate, &val, 6000, 510000);
+
+ if (fmt_param_get(&pl, "stereo", &val))
+ assign_if(&prm->stereo, &val, 0, 1);
+
+ if (fmt_param_get(&pl, "cbr", &val))
+ assign_if(&prm->cbr, &val, 0, 1);
+
+ if (fmt_param_get(&pl, "useinbandfec", &val))
+ assign_if(&prm->inband_fec, &val, 0, 1);
+
+ if (fmt_param_get(&pl, "usedtx", &val))
+ assign_if(&prm->dtx, &val, 0, 1);
+}