diff options
Diffstat (limited to 'modules/ilbc/ilbc.c')
-rw-r--r-- | modules/ilbc/ilbc.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/modules/ilbc/ilbc.c b/modules/ilbc/ilbc.c new file mode 100644 index 0000000..549be4d --- /dev/null +++ b/modules/ilbc/ilbc.c @@ -0,0 +1,354 @@ +/** + * @file ilbc.c Internet Low Bit Rate Codec (iLBC) audio codec + * + * Copyright (C) 2010 Creytiv.com + */ +#include <re.h> +#include <baresip.h> +#include <iLBC_define.h> +#include <iLBC_decode.h> +#include <iLBC_encode.h> + + +/* + * This module implements the iLBC audio codec as defined in: + * + * RFC 3951 Internet Low Bit Rate Codec (iLBC) + * RFC 3952 RTP Payload Format for iLBC Speech + * + * The iLBC source code is not included here, but can be downloaded from + * http://ilbcfreeware.org/ + * + * You can also use the source distributed by the Freeswitch project, + * see www.freeswitch.org, and then freeswitch/libs/codec/ilbc. + * Or you can look in the asterisk source code ... + * + * mode=20 15.20 kbit/s 160samp 38bytes + * mode=30 13.33 kbit/s 240samp 50bytes + */ + +enum { + DEFAULT_MODE = 20, /* 20ms or 30ms */ + USE_ENHANCER = 1 +}; + +struct auenc_state { + iLBC_Enc_Inst_t enc; + int mode; + uint32_t enc_bytes; +}; + +struct audec_state { + iLBC_Dec_Inst_t dec; + int mode; + uint32_t nsamp; + size_t dec_bytes; +}; + + +static char ilbc_fmtp[32]; + + +static void set_encoder_mode(struct auenc_state *st, int mode) +{ + if (st->mode == mode) + return; + + info("ilbc: set iLBC encoder mode %dms\n", mode); + + st->mode = mode; + + switch (mode) { + + case 20: + st->enc_bytes = NO_OF_BYTES_20MS; + break; + + case 30: + st->enc_bytes = NO_OF_BYTES_30MS; + break; + + default: + warning("ilbc: unknown encoder mode %d\n", mode); + return; + } + + st->enc_bytes = initEncode(&st->enc, mode); +} + + +static void set_decoder_mode(struct audec_state *st, int mode) +{ + if (st->mode == mode) + return; + + info("ilbc: set iLBC decoder mode %dms\n", mode); + + st->mode = mode; + + switch (mode) { + + case 20: + st->nsamp = BLOCKL_20MS; + break; + + case 30: + st->nsamp = BLOCKL_30MS; + break; + + default: + warning("ilbc: unknown decoder mode %d\n", mode); + return; + } + + st->nsamp = initDecode(&st->dec, mode, USE_ENHANCER); +} + + +static void encoder_fmtp_decode(struct auenc_state *st, const char *fmtp) +{ + struct pl mode; + + if (!fmtp) + return; + + if (re_regex(fmtp, strlen(fmtp), "mode=[0-9]+", &mode)) + return; + + set_encoder_mode(st, pl_u32(&mode)); +} + + +static void decoder_fmtp_decode(struct audec_state *st, const char *fmtp) +{ + struct pl mode; + + if (!fmtp) + return; + + if (re_regex(fmtp, strlen(fmtp), "mode=[0-9]+", &mode)) + return; + + set_decoder_mode(st, pl_u32(&mode)); +} + + +static void encode_destructor(void *arg) +{ + struct auenc_state *st = arg; + (void)st; +} + + +static void decode_destructor(void *arg) +{ + struct audec_state *st = arg; + (void)st; +} + + +static int check_ptime(const struct auenc_param *prm) +{ + if (!prm) + return 0; + + switch (prm->ptime) { + + case 20: + case 30: + return 0; + + default: + warning("ilbc: invalid ptime %u ms\n", prm->ptime); + return EINVAL; + } +} + + +static int encode_update(struct auenc_state **aesp, const struct aucodec *ac, + struct auenc_param *prm, const char *fmtp) +{ + struct auenc_state *st; + int err = 0; + + if (!aesp || !ac || !prm) + return EINVAL; + if (check_ptime(prm)) + return EINVAL; + if (*aesp) + return 0; + + st = mem_zalloc(sizeof(*st), encode_destructor); + if (!st) + return ENOMEM; + + set_encoder_mode(st, DEFAULT_MODE); + + if (str_isset(fmtp)) + encoder_fmtp_decode(st, fmtp); + + /* update parameters after SDP was decoded */ + if (prm) { + prm->ptime = st->mode; + } + + if (err) + mem_deref(st); + else + *aesp = st; + + return err; +} + + +static int decode_update(struct audec_state **adsp, + const struct aucodec *ac, const char *fmtp) +{ + struct audec_state *st; + int err = 0; + (void)fmtp; + + if (!adsp || !ac) + return EINVAL; + if (*adsp) + return 0; + + st = mem_zalloc(sizeof(*st), decode_destructor); + if (!st) + return ENOMEM; + + set_decoder_mode(st, DEFAULT_MODE); + + if (str_isset(fmtp)) + decoder_fmtp_decode(st, fmtp); + + if (err) + mem_deref(st); + else + *adsp = st; + + return err; +} + + +static int encode(struct auenc_state *st, uint8_t *buf, + size_t *len, const int16_t *sampv, size_t sampc) +{ + float float_buf[sampc]; + uint32_t i; + + /* Make sure there is enough space */ + if (*len < st->enc_bytes) { + warning("ilbc: encode: buffer is too small (%u bytes)\n", + *len); + return ENOMEM; + } + + /* Convert from 16-bit samples to float */ + for (i=0; i<sampc; i++) { + const int16_t v = sampv[i]; + float_buf[i] = (float)v; + } + + iLBC_encode(buf, /* (o) encoded data bits iLBC */ + float_buf, /* (o) speech vector to encode */ + &st->enc); /* (i/o) the general encoder state */ + + *len = st->enc_bytes; + + return 0; +} + + +static int do_dec(struct audec_state *st, int16_t *sampv, size_t *sampc, + const uint8_t *buf, size_t len) +{ + float float_buf[st->nsamp]; + const int mode = len ? 1 : 0; + uint32_t i; + + /* Make sure there is enough space in the buffer */ + if (*sampc < st->nsamp) + return ENOMEM; + + iLBC_decode(float_buf, /* (o) decoded signal block */ + (uint8_t *)buf, /* (i) encoded signal bits */ + &st->dec, /* (i/o) the decoder state structure */ + mode); /* (i) 0: bad packet, PLC, 1: normal */ + + /* Convert from float to 16-bit samples */ + for (i=0; i<st->nsamp; i++) { + sampv[i] = (int16_t)float_buf[i]; + } + + *sampc = st->nsamp; + + return 0; +} + + +static int decode(struct audec_state *st, int16_t *sampv, + size_t *sampc, const uint8_t *buf, size_t len) +{ + /* Try to detect mode */ + if (st->dec_bytes != len) { + + st->dec_bytes = len; + + switch (st->dec_bytes) { + + case NO_OF_BYTES_20MS: + set_decoder_mode(st, 20); + break; + + case NO_OF_BYTES_30MS: + set_decoder_mode(st, 30); + break; + + default: + warning("ilbc: decode: expect %u, got %u\n", + st->dec_bytes, len); + return EINVAL; + } + } + + return do_dec(st, sampv, sampc, buf, len); +} + + +static int pkloss(struct audec_state *st, int16_t *sampv, size_t *sampc) +{ + return do_dec(st, sampv, sampc, NULL, 0); +} + + +static struct aucodec ilbc = { + LE_INIT, 0, "iLBC", 8000, 1, ilbc_fmtp, + encode_update, encode, decode_update, decode, pkloss, 0, 0 +}; + + +static int module_init(void) +{ + (void)re_snprintf(ilbc_fmtp, sizeof(ilbc_fmtp), + "mode=%d", DEFAULT_MODE); + + aucodec_register(&ilbc); + return 0; +} + + +static int module_close(void) +{ + aucodec_unregister(&ilbc); + return 0; +} + + +/** Module exports */ +EXPORT_SYM const struct mod_export DECL_EXPORTS(ilbc) = { + "ilbc", + "codec", + module_init, + module_close +}; |