codecs: Add support for WebRTC iLBC 2.0.
[asterisk/asterisk.git] / codecs / codec_ilbc.c
index 4914392..536d680 100644 (file)
 /*! \file
  *
  * \brief Translate between signed linear and Internet Low Bitrate Codec
- * 
+ *
  * \ingroup codecs
  */
 
 /*** MODULEINFO
-       <defaultenabled>no</defaultenabled>
+       <use>ilbc</use>
+       <support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/translate.h"
+#include "asterisk/codec.h"             /* for AST_MEDIA_TYPE_AUDIO */
+#include "asterisk/format.h"            /* for ast_format_get_attribute_data */
+#include "asterisk/frame.h"             /* for ast_frame, etc */
+#include "asterisk/linkedlists.h"       /* for AST_LIST_NEXT, etc */
+#include "asterisk/logger.h"            /* for ast_log, ast_debug, etc */
 #include "asterisk/module.h"
-#include "asterisk/utils.h"
-
+#include "asterisk/translate.h"         /* for ast_trans_pvt, etc */
+
+#ifdef ILBC_WEBRTC
+#include <ilbc.h>
+typedef uint16_t ilbc_bytes;
+typedef int16_t  ilbc_block;
+#define BUF_TYPE i16
+#else
 #include "ilbc/iLBC_encode.h"
 #include "ilbc/iLBC_decode.h"
+typedef unsigned char ilbc_bytes;
+typedef float         ilbc_block;
+#define BUF_TYPE uc
+#endif
 
-/* Sample frame data */
-#include "slin_ilbc_ex.h"
-#include "ilbc_slin_ex.h"
+#include "asterisk/ilbc.h"
 
-#define USE_ILBC_ENHANCER      0
-#define ILBC_MS                        30
-/* #define ILBC_MS                     20 */
+#define USE_ILBC_ENHANCER 0
+#define BUFFER_SAMPLES    8000
 
-#define        ILBC_FRAME_LEN  50      /* apparently... */
-#define        ILBC_SAMPLES    240     /* 30ms at 8000 hz */
-#define        BUFFER_SAMPLES  8000
+/* Sample frame data */
+#include "asterisk/slin.h"
+#include "ex_ilbc.h"
 
 struct ilbc_coder_pvt {
        iLBC_Enc_Inst_t enc;
        iLBC_Dec_Inst_t dec;
        /* Enough to store a full second */
        int16_t buf[BUFFER_SAMPLES];
+       int16_t inited;
 };
 
 static int lintoilbc_new(struct ast_trans_pvt *pvt)
 {
        struct ilbc_coder_pvt *tmp = pvt->pvt;
+       struct ilbc_attr *attr = pvt->explicit_dst ? ast_format_get_attribute_data(pvt->explicit_dst) : NULL;
+       const unsigned int mode = attr ? attr->mode : 30;
 
-       initEncode(&tmp->enc, ILBC_MS);
+       initEncode(&tmp->enc, mode);
 
        return 0;
 }
@@ -72,73 +85,62 @@ static int ilbctolin_new(struct ast_trans_pvt *pvt)
 {
        struct ilbc_coder_pvt *tmp = pvt->pvt;
 
-       initDecode(&tmp->dec, ILBC_MS, USE_ILBC_ENHANCER);
+       tmp->inited = 0; /* we do not know the iLBC mode, yet */
 
        return 0;
 }
 
-static struct ast_frame *lintoilbc_sample(void)
-{
-       static struct ast_frame f;
-       f.frametype = AST_FRAME_VOICE;
-       f.subclass = AST_FORMAT_SLINEAR;
-       f.datalen = sizeof(slin_ilbc_ex);
-       f.samples = sizeof(slin_ilbc_ex)/2;
-       f.mallocd = 0;
-       f.offset = 0;
-       f.src = __PRETTY_FUNCTION__;
-       f.data.ptr = slin_ilbc_ex;
-       return &f;
-}
-
-static struct ast_frame *ilbctolin_sample(void)
-{
-       static struct ast_frame f;
-       f.frametype = AST_FRAME_VOICE;
-       f.subclass = AST_FORMAT_ILBC;
-       f.datalen = sizeof(ilbc_slin_ex);
-       /* All frames are 30 ms long */
-       f.samples = ILBC_SAMPLES;
-       f.mallocd = 0;
-       f.offset = 0;
-       f.src = __PRETTY_FUNCTION__;
-       f.data.ptr = ilbc_slin_ex;
-       return &f;
-}
-
 /*! \brief decode a frame and store in outbuf */
 static int ilbctolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 {
        struct ilbc_coder_pvt *tmp = pvt->pvt;
+       struct ilbc_attr *attr = ast_format_get_attribute_data(f->subclass.format);
+       const unsigned int mode = attr ? attr->mode : 30;
+       const unsigned int sample_rate = pvt->t->dst_codec.sample_rate;
+       const unsigned int samples_per_frame = mode * sample_rate / 1000;
+       const unsigned int octets_per_frame = (mode == 20) ? 38 : 50;
+
        int plc_mode = 1; /* 1 = normal data, 0 = plc */
        /* Assuming there's space left, decode into the current buffer at
           the tail location.  Read in as many frames as there are */
        int x,i;
+       int datalen = f->datalen;
        int16_t *dst = pvt->outbuf.i16;
-       float tmpf[ILBC_SAMPLES];
+       ilbc_block tmpf[samples_per_frame];
+
+       if (!f->data.ptr && datalen) {
+               ast_debug(1, "issue 16070, ILIB ERROR. data = NULL datalen = %d src = %s\n", datalen, f->src ? f->src : "no src set");
+               f->datalen = 0;
+               datalen = 0;
+       }
 
-       if (f->datalen == 0) { /* native PLC, set fake f->datalen and clear plc_mode */
-               f->datalen = ILBC_FRAME_LEN;
-               f->samples = ILBC_SAMPLES;
+       if (datalen == 0) { /* native PLC, set fake datalen and clear plc_mode */
+               datalen = octets_per_frame;
+               f->samples = samples_per_frame;
                plc_mode = 0;   /* do native plc */
-               pvt->samples += ILBC_SAMPLES;
+               pvt->samples += samples_per_frame;
        }
 
-       if (f->datalen % ILBC_FRAME_LEN) {
-               ast_log(LOG_WARNING, "Huh?  An ilbc frame that isn't a multiple of 50 bytes long from %s (%d)?\n", f->src, f->datalen);
+       if (datalen % octets_per_frame) {
+               ast_log(LOG_WARNING, "Huh?  An ilbc frame that isn't a multiple of %u bytes long from %s (%d)?\n", octets_per_frame, f->src, datalen);
                return -1;
        }
-       
-       for (x=0; x < f->datalen ; x += ILBC_FRAME_LEN) {
-               if (pvt->samples + ILBC_SAMPLES > BUFFER_SAMPLES) {     
+
+       if (!tmp->inited) {
+               initDecode(&tmp->dec, mode, USE_ILBC_ENHANCER);
+               tmp->inited = 1;
+       }
+
+       for (x = 0; x < datalen; x += octets_per_frame) {
+               if (pvt->samples + samples_per_frame > BUFFER_SAMPLES) {
                        ast_log(LOG_WARNING, "Out of buffer space\n");
                        return -1;
-               }               
+               }
                iLBC_decode(tmpf, plc_mode ? f->data.ptr + x : NULL, &tmp->dec, plc_mode);
-               for ( i=0; i < ILBC_SAMPLES; i++)
+               for (i = 0; i < samples_per_frame; i++)
                        dst[pvt->samples + i] = tmpf[i];
-               pvt->samples += ILBC_SAMPLES;
-               pvt->datalen += 2*ILBC_SAMPLES;
+               pvt->samples += samples_per_frame;
+               pvt->datalen += samples_per_frame * 2;
        }
        return 0;
 }
@@ -161,55 +163,89 @@ static int lintoilbc_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 static struct ast_frame *lintoilbc_frameout(struct ast_trans_pvt *pvt)
 {
        struct ilbc_coder_pvt *tmp = pvt->pvt;
-       int datalen = 0;
-       int samples = 0;
-
-       /* We can't work on anything less than a frame in size */
-       if (pvt->samples < ILBC_SAMPLES)
-               return NULL;
-       while (pvt->samples >= ILBC_SAMPLES) {
-               float tmpf[ILBC_SAMPLES];
+       struct ast_frame *result = NULL;
+       struct ast_frame *last = NULL;
+       int samples = 0; /* output samples */
+
+       struct ilbc_attr *attr = ast_format_get_attribute_data(pvt->f.subclass.format);
+       const unsigned int mode = attr ? attr->mode : 30;
+       const unsigned int sample_rate = pvt->t->dst_codec.sample_rate;
+       const unsigned int samples_per_frame = mode * sample_rate / 1000;
+       const unsigned int octets_per_frame = (mode == 20) ? 38 : 50;
+
+       while (pvt->samples >= samples_per_frame) {
+               struct ast_frame *current;
+               ilbc_block tmpf[samples_per_frame];
                int i;
 
                /* Encode a frame of data */
-               for (i = 0 ; i < ILBC_SAMPLES ; i++)
+               for (i = 0; i < samples_per_frame; i++)
                        tmpf[i] = tmp->buf[samples + i];
-               iLBC_encode( pvt->outbuf.uc + datalen, tmpf, &tmp->enc);
-
-               datalen += ILBC_FRAME_LEN;
-               samples += ILBC_SAMPLES;
-               pvt->samples -= ILBC_SAMPLES;
+               iLBC_encode((ilbc_bytes *) pvt->outbuf.BUF_TYPE, tmpf, &tmp->enc);
+
+               samples += samples_per_frame;
+               pvt->samples -= samples_per_frame;
+
+               current = ast_trans_frameout(pvt, octets_per_frame, samples_per_frame);
+               if (!current) {
+                       continue;
+               } else if (last) {
+                       AST_LIST_NEXT(last, frame_list) = current;
+               } else {
+                       result = current;
+               }
+               last = current;
        }
 
        /* Move the data at the end of the buffer to the front */
-       if (pvt->samples)
+       if (samples) {
                memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+       }
 
-       return ast_trans_frameout(pvt, datalen, samples);
+       return result;
 }
 
 static struct ast_translator ilbctolin = {
-       .name = "ilbctolin", 
-       .srcfmt = AST_FORMAT_ILBC,
-       .dstfmt = AST_FORMAT_SLINEAR,
+       .name = "ilbctolin",
+       .src_codec = {
+               .name = "ilbc",
+               .type = AST_MEDIA_TYPE_AUDIO,
+               .sample_rate = 8000,
+       },
+       .dst_codec = {
+               .name = "slin",
+               .type = AST_MEDIA_TYPE_AUDIO,
+               .sample_rate = 8000,
+       },
+       .format = "slin",
        .newpvt = ilbctolin_new,
        .framein = ilbctolin_framein,
-       .sample = ilbctolin_sample,
+       .sample = ilbc_sample,
        .desc_size = sizeof(struct ilbc_coder_pvt),
        .buf_size = BUFFER_SAMPLES * 2,
        .native_plc = 1,
 };
 
 static struct ast_translator lintoilbc = {
-       .name = "lintoilbc", 
-       .srcfmt = AST_FORMAT_SLINEAR,
-       .dstfmt = AST_FORMAT_ILBC,
+       .name = "lintoilbc",
+       .src_codec = {
+               .name = "slin",
+               .type = AST_MEDIA_TYPE_AUDIO,
+               .sample_rate = 8000,
+       },
+       .dst_codec = {
+               .name = "ilbc",
+               .type = AST_MEDIA_TYPE_AUDIO,
+               .sample_rate = 8000,
+       },
+       .format = "ilbc",
        .newpvt = lintoilbc_new,
        .framein = lintoilbc_framein,
        .frameout = lintoilbc_frameout,
-       .sample = lintoilbc_sample,
+       .sample = slin8_sample,
        .desc_size = sizeof(struct ilbc_coder_pvt),
-       .buf_size = (BUFFER_SAMPLES * ILBC_FRAME_LEN + ILBC_SAMPLES - 1) / ILBC_SAMPLES,
+       /* frame len (38 bytes), frame size (160 samples), ceil (+ 160 - 1) */
+       .buf_size = (BUFFER_SAMPLES * 38 + 160 - 1) / 160,
 };
 
 static int unload_module(void)
@@ -227,12 +263,13 @@ static int load_module(void)
        int res;
 
        res = ast_register_translator(&ilbctolin);
-       if (!res) 
-               res=ast_register_translator(&lintoilbc);
-       else
-               ast_unregister_translator(&ilbctolin);
-       if (res)
-               return AST_MODULE_LOAD_FAILURE;
+       res |= ast_register_translator(&lintoilbc);
+
+       if (res) {
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
        return AST_MODULE_LOAD_SUCCESS;
 }