summaryrefslogtreecommitdiff
path: root/modules/mpa
diff options
context:
space:
mode:
authorChristian Hoene <christian.hoene@symonics.com>2016-04-22 16:08:07 +0200
committerChristian Hoene <christian.hoene@symonics.com>2016-04-22 16:08:07 +0200
commitcfbdb19d57a87c39a1f9f2613f7c9f572502cd5f (patch)
treea9bdf245e7043e46a4209bf1744ea1ff11e0bb8e /modules/mpa
parent488fa550651037a7e7f7ff21a4a11369ab3afec2 (diff)
First version with MPA coding
Diffstat (limited to 'modules/mpa')
-rw-r--r--modules/mpa/decode.c136
-rw-r--r--modules/mpa/encode.c141
-rw-r--r--modules/mpa/module.mk14
-rw-r--r--modules/mpa/module.mk~14
-rw-r--r--modules/mpa/mpa.c189
-rw-r--r--modules/mpa/mpa.h32
-rw-r--r--modules/mpa/sdp.c54
7 files changed, 580 insertions, 0 deletions
diff --git a/modules/mpa/decode.c b/modules/mpa/decode.c
new file mode 100644
index 0000000..11ab8b4
--- /dev/null
+++ b/modules/mpa/decode.c
@@ -0,0 +1,136 @@
+/**
+ * @file mpa/decode.c mpa Decode
+ *
+ * Copyright (C) 2016 Symonics GmbH
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <mpg123.h>
+#include "mpa.h"
+
+
+struct audec_state {
+ mpg123_handle *dec;
+};
+
+
+static void destructor(void *arg)
+{
+ struct audec_state *ads = arg;
+
+ mpg123_close(ads->dec);
+ mpg123_delete(ads->dec);
+}
+
+
+int mpa_decode_update(struct audec_state **adsp, const struct aucodec *ac,
+ const char *fmtp)
+{
+ struct audec_state *ads;
+ int mpaerr, err=0;
+
+ if (!adsp || !ac || !ac->ch)
+ return EINVAL;
+
+ ads = *adsp;
+
+ if (ads)
+ return 0;
+
+ ads = mem_zalloc(sizeof(*ads), destructor);
+ if (!ads)
+ return ENOMEM;
+
+ mpg123_delete(ads->dec);
+ ads->dec = mpg123_new(NULL,&mpaerr);
+ if (!ads->dec) {
+ warning("mpa: decoder create: %s\n", mpg123_plain_strerror(mpaerr));
+ err = ENOMEM;
+ goto out;
+ }
+
+ mpaerr = mpg123_param(ads->dec, MPG123_VERBOSE, 4, 4.);
+ if(mpaerr != MPG123_OK) {
+ error("MPA libmpg123 param error %s", mpg123_plain_strerror(mpaerr));
+ return EINVAL;
+ }
+
+
+ mpaerr = mpg123_format(ads->dec, 48000 /*ac->srate*/, 2 /*ac->ch*/, MPG123_ENC_SIGNED_16);
+ if(mpaerr != MPG123_OK) {
+ error("MPA libmpg123 format error %s", mpg123_plain_strerror(mpaerr));
+ return EINVAL;
+ }
+
+ mpaerr = mpg123_open_feed(ads->dec);
+ if(mpaerr != MPG123_OK) {
+ error("MPA libmpg123 open feed error %s", mpg123_plain_strerror(mpaerr));
+ return EINVAL;
+ }
+
+
+ out:
+ if (err)
+ mem_deref(ads);
+ else
+ *adsp = ads;
+
+ return err;
+}
+
+
+int mpa_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc,
+ const uint8_t *buf, size_t len)
+{
+ int mpaerr, channels, encoding;
+ long samplerate;
+ size_t n;
+ uint32_t header;
+
+ if (!ads || !sampv || !sampc || !buf)
+ return EINVAL;
+
+ if(len<=4)
+ return EINVAL;
+ header = *(uint32_t*)buf;
+ if(header != 0) {
+ error("MPA header is not zero %08X\n", header);
+ return EPROTO;
+ }
+
+
+ mpaerr = mpg123_decode(ads->dec, buf+4, len-4, (unsigned char*)sampv, *sampc*2, &n);
+ if(mpaerr == MPG123_NEW_FORMAT) {
+ mpg123_getformat(ads->dec, &samplerate, &channels, &encoding);
+ info("MPA libmpg123 format change %d %d %04X\n",samplerate,channels,encoding);
+ }
+ else if(mpaerr == MPG123_NEED_MORE)
+ return 0;
+ else if(mpaerr != MPG123_OK) {
+ error("MPA libmpg123 feed error %d %s", mpaerr, mpg123_plain_strerror(mpaerr));
+ return EPROTO;
+ }
+
+// warning("mpa decode %d %d %d\n",*sampc,len,n);
+ *sampc = n / 2;
+
+ return 0;
+}
+
+int mpa_decode_pkloss(struct audec_state *ads, int16_t *sampv, size_t *sampc)
+{
+ if (!ads || !sampv || !sampc)
+ return EINVAL;
+
+ warning("mpa packet loss %d\n",*sampc);
+// 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/mpa/encode.c b/modules/mpa/encode.c
new file mode 100644
index 0000000..1195844
--- /dev/null
+++ b/modules/mpa/encode.c
@@ -0,0 +1,141 @@
+/**
+ * @file mpa/encode.c mpa Encode
+ *
+ * Copyright (C) 2016 Symonics GmbH
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <twolame.h>
+#include <string.h>
+#include <speex/speex_resampler.h>
+#include "mpa.h"
+
+
+struct auenc_state {
+ twolame_options *enc;
+ int channels;
+ SpeexResamplerState *resampler;
+};
+
+
+static void destructor(void *arg)
+{
+ struct auenc_state *aes = arg;
+
+ if (aes->enc)
+ twolame_close(&aes->enc);
+}
+
+int mpa_encode_update(struct auenc_state **aesp, const struct aucodec *ac,
+ struct auenc_param *param, const char *fmtp)
+{
+ struct auenc_state *aes;
+ struct mpa_param prm;
+ const struct aucodec *auc = aucodec_find("MPA", 90000, 1);
+ int mpares;
+
+ (void)param;
+
+ if (!aesp || !ac || !ac->ch)
+ return EINVAL;
+
+ aes = *aesp;
+
+ if (!aes) {
+ aes = mem_zalloc(sizeof(*aes), destructor);
+ aes->enc = twolame_init();
+ if (!aes->enc) {
+ warning("mpa: encoder create failed");
+ mem_deref(aes);
+ return ENOMEM;
+ }
+ aes->channels = auc->ch;
+ *aesp = aes;
+ }
+
+ prm.samplerate = 32000;
+ prm.bitrate = 128000;
+ prm.layer = 2;
+ prm.mode = STEREO;
+ mpa_decode_fmtp(&prm, fmtp);
+
+ mpares = 0;
+ mpares |= twolame_set_verbosity(aes->enc, 5);
+ mpares |= twolame_set_mode(aes->enc, prm.mode == SINGLE_CHANNEL ? TWOLAME_MONO :
+ prm.mode == DUAL_CHANNEL ? TWOLAME_DUAL_CHANNEL :
+ prm.mode == JOINT_STEREO ? TWOLAME_JOINT_STEREO :
+ prm.mode == STEREO ? TWOLAME_STEREO : TWOLAME_AUTO_MODE);
+ mpares |= twolame_set_version(aes->enc, prm.samplerate < 32000 ? TWOLAME_MPEG2 : TWOLAME_MPEG1);
+ mpares |= twolame_set_bitrate(aes->enc, prm.bitrate/1000);
+ mpares |= twolame_set_in_samplerate(aes->enc, prm.samplerate);
+ mpares |= twolame_set_out_samplerate(aes->enc, prm.samplerate);
+ mpares |= twolame_set_num_channels(aes->enc, 2);
+ if(mpares!=0) {
+ warning("mpa: encoder set failed\n");
+ return EINVAL;
+ }
+
+ twolame_print_config(aes->enc);
+
+ mpares = twolame_init_params(aes->enc);
+ if(mpares!=0) {
+ warning("mpa: encoder init params failed\n");
+ return EINVAL;
+ }
+
+ if(prm.samplerate != 48000) {
+ aes->resampler = speex_resampler_init(2, 48000, prm.samplerate, 3, &mpares);
+ if(mpares!=RESAMPLER_ERR_SUCCESS) {
+ warning("mpa: resampler init failed %d\n",mpares);
+ return EINVAL;
+ }
+
+ }
+ else
+ aes->resampler = NULL;
+ return 0;
+}
+
+
+int mpa_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len,
+ const int16_t *sampv, size_t sampc)
+{
+ int n;
+ spx_uint32_t ds_len,in_len;
+ static int16_t ds[1920];
+
+ if (!aes || !buf || !len || !sampv)
+ return EINVAL;
+
+ if(aes->resampler) {
+ in_len = sampc/2;
+ n=speex_resampler_process_interleaved_int(aes->resampler, sampv, &in_len, ds, &ds_len);
+ if (n!=RESAMPLER_ERR_SUCCESS || in_len != sampc/2) {
+ warning("mpa: downsample error: %s\n", strerror(n));
+ return EPROTO;
+ }
+ n = twolame_encode_buffer_interleaved(aes->enc, ds, ds_len/2,
+ buf+4, (*len)-4);
+ warning("mpa encode %d %d %d %d %d\n",ds_len,sampc,aes->channels,*len,n);
+ }
+ else
+ n = twolame_encode_buffer_interleaved(aes->enc, sampv, (int)(sampc/2),
+ buf+4, (*len)-4);
+
+ if (n < 0) {
+ warning("mpa: encode error: %s\n", strerror((int)n));
+ return EPROTO;
+ }
+
+ if(n > 0) {
+ *(uint32_t*)buf = 0;
+ *len = n+4;
+ }
+ else
+ *len = 0;
+
+// warning("mpa encode %d %d %d %d\n",sampc,aes->channels,*len,n);
+ return 0;
+}
+
diff --git a/modules/mpa/module.mk b/modules/mpa/module.mk
new file mode 100644
index 0000000..b060f12
--- /dev/null
+++ b/modules/mpa/module.mk
@@ -0,0 +1,14 @@
+#
+# module.mk
+#
+# Copyright (C) 2016 Symonics GmbH
+#
+
+MOD := mpa
+$(MOD)_SRCS += mpa.c
+$(MOD)_SRCS += decode.c
+$(MOD)_SRCS += sdp.c
+$(MOD)_SRCS += encode.c
+$(MOD)_LFLAGS += -ltwolame -lmpg123 -lspeexdsp -lm
+
+include mk/mod.mk
diff --git a/modules/mpa/module.mk~ b/modules/mpa/module.mk~
new file mode 100644
index 0000000..9b4b98c
--- /dev/null
+++ b/modules/mpa/module.mk~
@@ -0,0 +1,14 @@
+#
+# module.mk
+#
+# Copyright (C) 2016 Symonics GmbH
+#
+
+MOD := mpa
+$(MOD)_SRCS += mpa.c
+$(MOD)_SRCS += decode.c
+$(MOD)_SRCS += sdp.c
+$(MOD)_SRCS += encode.c
+$(MOD)_LFLAGS += -ltwolame -lmpg123 -lm
+
+include mk/mod.mk
diff --git a/modules/mpa/mpa.c b/modules/mpa/mpa.c
new file mode 100644
index 0000000..cf0a273
--- /dev/null
+++ b/modules/mpa/mpa.c
@@ -0,0 +1,189 @@
+/**
+ * @file mpa.c mpa Audio Codec
+ *
+ * Copyright (C) 2016 Symonics GmbH
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <ctype.h>
+#include <string.h>
+#include "mpa.h"
+#include <mpg123.h>
+
+/**
+ * @defgroup mpa mpa
+ *
+ * The mpa audio codec
+ *
+ * Supported version:
+ * libmpg123 1.16.0 or later
+ * libtwolame 0.3.13 or later
+ *
+ * References:
+ *
+ * RFC 2250 RTP Payload Format for the mpa Speech and Audio Codec
+ *
+ */
+
+/*
+4.1.17. Registration of MIME media type audio/MPA
+
+ MIME media type name: audio
+
+ MIME subtype name: MPA (MPEG audio)
+
+ Required parameters: None
+
+ Optional parameters:
+ layer: which layer of MPEG audio encoding; permissible values
+ are 1, 2, 3.
+
+ samplerate: the rate at which audio is sampled. MPEG-1 audio
+ supports sampling rates of 32, 44.1, and 48 kHz; MPEG-2
+ supports sampling rates of 16, 22.05 and 24 kHz. This parameter
+ is separate from the RTP timestamp clock rate which is always
+ 90000 Hz for MPA.
+
+ mode: permissible values are "stereo", "joint_stereo",
+ "single_channel", "dual_channel". The "channels" parameter
+ does not apply to MPA. It is undefined to put a number of
+ channels in the SDP rtpmap attribute for MPA.
+
+ bitrate: the data rate for the audio bit stream.
+
+ ptime: RECOMMENDED duration of each packet in milliseconds.
+
+ maxptime: maximum duration of each packet in milliseconds.
+
+ Parameters which are omitted are left to the encoder to choose
+ based on the session bandwidth, configuration information, or
+ other constraints. The selected layer as well as the sampling
+ rate and mode are indicated in the payload so receivers can
+ process the data without these parameters being specified
+ externally.
+
+ Encoding considerations:
+ This type is only defined for transfer via RTP [RFC 3550].
+
+ Security considerations: See Section 5 of RFC 3555
+
+ Interoperability considerations: none
+
+ Published specification: RFC 3551
+
+ Applications which use this media type:
+ Audio and video streaming and conferencing tools.
+
+*/
+
+
+static struct aucodec mpa = {
+ .pt = NULL,
+ .name = "MPA",
+ .srate = 90000,
+ .ch = 1,
+ .fmtp = "",
+ .encupdh = mpa_encode_update,
+ .ench = mpa_encode_frm,
+ .decupdh = mpa_decode_update,
+ .dech = mpa_decode_frm,
+ .plch = mpa_decode_pkloss,
+};
+
+
+static int module_init(void)
+{
+ struct conf *conf = conf_cur();
+ uint32_t value;
+ static char fmtp[256];
+ static char mode[30];
+ int res;
+
+ /** generate fmtp string based on config file */
+
+ strcpy(mode,mpa.fmtp);
+
+ if (0 == conf_get_u32(conf, "mpa_bitrate", &value)) {
+ (void)re_snprintf(fmtp+strlen(fmtp), sizeof(fmtp)-strlen(fmtp),
+ ";bitrate=%d",
+ value);
+ }
+ if (0 == conf_get_u32(conf, "mpa_layer", &value)) {
+ if(value<1 || value>4) {
+ error("MPA layer 1, 2 or 3 are allowed.");
+ return -1;
+ }
+ (void)re_snprintf(fmtp+strlen(fmtp), sizeof(fmtp)-strlen(fmtp),
+ ";layer=%d",
+ value);
+ }
+ if (0 == conf_get_u32(conf, "mpa_samplerate", &value)) {
+ switch(value) {
+ case 32000:
+ case 44100:
+ case 48000:
+ case 16000:
+ case 22050:
+ case 24000:
+ break;
+ default:
+ error("MPA samplerates of 16, 22.05, 24, 32, 44.1, and 48 kHz are allowed.");
+ return -1;
+ }
+ (void)re_snprintf(fmtp+strlen(fmtp), sizeof(fmtp)-strlen(fmtp),
+ ";samplerate=%d",
+ value);
+ }
+ if (0 == conf_get_str(conf, "mpa_mode", mode, sizeof(mode))) {
+ char *p = mode;
+ while(*p) {
+ *p = tolower(*p);
+ p++;
+ }
+
+ if(strcmp(mode,"stereo") && strcmp(mode,"joint_stereo") && strcmp(mode,"single_channel") && strcmp(mode,"dual_channel")) {
+ error("MPA mode: Permissible values are stereo, joint_stereo, single_channel, dual_channel");
+ return -1;
+ }
+
+
+ (void)re_snprintf(fmtp+strlen(fmtp), sizeof(fmtp)-strlen(fmtp),
+ ";mode=%s",
+ mode);
+ }
+
+ if(fmtp[0]==';')
+ mpa.fmtp = fmtp+1;
+ else
+ mpa.fmtp = fmtp;
+
+ /* init decoder library */
+ res = mpg123_init();
+ if(res != MPG123_OK) {
+ error("MPA libmpg123 init error %s", mpg123_plain_strerror(res));
+ return -1;
+ }
+
+ aucodec_register(&mpa);
+
+ return 0;
+}
+
+
+static int module_close(void)
+{
+ aucodec_unregister(&mpa);
+
+ mpg123_exit();
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(mpa) = {
+ "MPA",
+ "audio codec",
+ module_init,
+ module_close,
+};
diff --git a/modules/mpa/mpa.h b/modules/mpa/mpa.h
new file mode 100644
index 0000000..f9c2934
--- /dev/null
+++ b/modules/mpa/mpa.h
@@ -0,0 +1,32 @@
+/**
+ * @file mpa.h Private mpa Interface
+ *
+ * Copyright (C) 2016 Symonics GmbH
+ */
+
+
+struct mpa_param {
+ unsigned samplerate;
+ unsigned bitrate;
+ unsigned layer;
+ enum { AUTO=0, STEREO, JOINT_STEREO, SINGLE_CHANNEL, DUAL_CHANNEL } mode;
+};
+
+
+/* Encode */
+int mpa_encode_update(struct auenc_state **aesp, const struct aucodec *ac,
+ struct auenc_param *prm, const char *fmtp);
+int mpa_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len,
+ const int16_t *sampv, size_t sampc);
+
+
+/* Decode */
+int mpa_decode_update(struct audec_state **adsp, const struct aucodec *ac,
+ const char *fmtp);
+int mpa_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc,
+ const uint8_t *buf, size_t len);
+int mpa_decode_pkloss(struct audec_state *st, int16_t *sampv, size_t *sampc);
+
+
+/* SDP */
+void mpa_decode_fmtp(struct mpa_param *prm, const char *fmtp);
diff --git a/modules/mpa/sdp.c b/modules/mpa/sdp.c
new file mode 100644
index 0000000..0e4d811
--- /dev/null
+++ b/modules/mpa/sdp.c
@@ -0,0 +1,54 @@
+/**
+ * @file mpa/sdp.c mpa SDP Functions
+ *
+ * Copyright (C) 2016 Symonics GmbH
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include <string.h>
+#include "mpa.h"
+
+
+static void assign_if(uint32_t *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 mpa_decode_fmtp(struct mpa_param *prm, const char *fmtp)
+{
+ struct pl pl, val;
+
+ if (!prm || !fmtp)
+ return;
+
+ pl_set_str(&pl, fmtp);
+
+ if (fmt_param_get(&pl, "bitrate", &val))
+ assign_if(&prm->bitrate, &val, 32000, 512000);
+
+ if (fmt_param_get(&pl, "samplerate", &val))
+ assign_if(&prm->samplerate, &val, 16000, 48000);
+
+ if (fmt_param_get(&pl, "layer", &val))
+ assign_if(&prm->layer, &val, 1, 3);
+
+ if (fmt_param_get(&pl, "mode", &val)) {
+
+ if(!strncmp("stereo",pl.p,pl.l))
+ prm->mode = STEREO;
+ else if(!strncmp("joint_stereo",pl.p,pl.l))
+ prm->mode = JOINT_STEREO;
+ else if(!strncmp("single_channel",pl.p,pl.l))
+ prm->mode = SINGLE_CHANNEL;
+ else if(!strncmp("dual_channel",pl.p,pl.l))
+ prm->mode = DUAL_CHANNEL;
+ }
+}