Commit 40e1002c authored by johan's avatar johan

Add generic PLC and PLC for g722

parent d1e6c619
......@@ -118,7 +118,9 @@ set(VOIP_SOURCE_FILES_C
audiofilters/flowcontrol.c
audiofilters/g711.c
audiofilters/g711.h
audiofilters/genericplc.h
audiofilters/genericplc.c
audiofilters/msgenericplc.c
audiofilters/l16.c
audiofilters/msfileplayer.c
audiofilters/msfilerec.c
......
......@@ -142,7 +142,9 @@ libmediastreamer_voip_la_SOURCES+= audiofilters/alaw.c \
utils/g722_encode.c \
audiofilters/msg722.c \
audiofilters/l16.c \
audiofilters/genericplc.h \
audiofilters/genericplc.c \
audiofilters/msgenericplc.c \
audiofilters/msfileplayer.c \
audiofilters/msfilerec.c \
audiofilters/waveheader.h \
......
This diff is collapsed.
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2015 Belledonne Communications, Grenoble
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef genericplc_h
#define genericplc_h
/* define transition duration in ms when starting/ending a comfort noise period - it introduces an equivalent delay */
#define TRANSITION_DELAY 5
/* 2/<min frequency we want to be able to reproduce> gives then length in seconds */
#define PLC_BUFFER_LEN 2/40
/* define in ms the maximum duration of PLC(after wich the output will be 0), and after how long we start decreasing the output volume to reach 0 at MAX_PLC_LEN */
#define PLC_DECREASE_START 100
#define MAX_PLC_LEN 150
/* length in ms of the incoming signal used to adjust energy of PLC generated signal */
#define ENERGY_ADJUSTMENT_WINDOW 10
#define ENERGY_ATTENUATION 0.85
typedef struct {
unsigned char *continuity_buffer; /**< buffer used to store a small set of future samples */
uint16_t plc_buffer_len; /**< The buffer len in bytes*/
unsigned char *plc_buffer; /**< buffer to store the previous frames used to generate plc */
float *hamming_window; /**< store the hamming window to apply to signal before fft */
int16_t *plc_out_buffer; /**< buffer to store the frames generated by plc, same length than previous frames one */
uint16_t plc_index; /**< index of plc_out_buffer to the next samples */
uint16_t plc_samples_used; /**< number of plc frames already used */
void *fft_to_frequency_context; /**< context for the FFT */
void *fft_to_time_context; /**< context for the inverse FFT */
int sample_rate; /**< sample rate of the audio signal */
}plc_context_t;
plc_context_t *generic_plc_create_context(int sample_rate);
void generic_plc_destroy_context(plc_context_t *context);
void generic_plc_transition_mix(int16_t *inout_buffer, int16_t *continuity_buffer, size_t fading_sample_nbr);
void generic_plc_fftbf(plc_context_t *context, int16_t *input_buffer, int16_t *output_buffer, size_t input_buffer_len);
void generic_plc_generate_samples(plc_context_t *context, int16_t *data, size_t sample_nbr);
void generic_plc_update_plc_buffer(plc_context_t *context, unsigned char *data, size_t data_len);
void generic_plc_update_continuity_buffer(plc_context_t *context, unsigned char *data, size_t data_len);
#endif /* genericplc_h */
......@@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include "genericplc.h"
#ifdef HAVE_SPANDSP
......@@ -66,8 +68,15 @@ static void scale_down(int16_t *samples, int count){
static void scale_up(int16_t *samples, int count){
int i;
for (i=0;i<count;++i)
samples[i]=samples[i]<<1;
for (i=0;i<count;++i) {
if (samples[i]>INT16_MAX/2) {
samples[i] = INT16_MAX;
} else if (samples[i]<INT16_MIN/2) {
samples[i] = INT16_MIN;
} else {
samples[i]=samples[i]<<1;
}
}
}
static void enc_process(MSFilter *f)
......@@ -181,50 +190,118 @@ MSFilterDesc ms_g722_enc_desc={
#endif
struct DecState {
g722_decode_state_t *state;
g722_decode_state_t *dec_state;
g722_encode_state_t *enc_state;
plc_context_t *plc_context;
MSConcealerContext* concealer;
};
static void dec_init(MSFilter *f){
struct DecState *s=ms_new0(struct DecState,1);
f->data=s;
s->state = g722_decode_init(NULL, 64000, 0);
s->dec_state = g722_decode_init(NULL, 64000, 0);
s->enc_state = g722_encode_init(NULL, 64000, 0);
s->concealer = ms_concealer_context_new(UINT32_MAX);
s->plc_context = generic_plc_create_context(16000); /* g722 has a sample rate of 16kHz */
};
static void dec_uninit(MSFilter *f)
{
struct DecState *s=(struct DecState*)f->data;
g722_decode_release(s->state);
g722_decode_release(s->dec_state);
g722_encode_release(s->enc_state);
generic_plc_destroy_context(s->plc_context);
ms_concealer_context_destroy(s->concealer);
ms_free(f->data);
f->data = 0;
};
static void dec_process(MSFilter *f)
{
static void dec_process(MSFilter *f) {
struct DecState *s=(struct DecState*)f->data;
mblk_t *im;
mblk_t *om;
while((im=ms_queue_get(f->inputs[0]))) {
int payloadlen = (int)(im->b_wptr - im->b_rptr);
size_t msg_size = msgdsize(im);
int declen;
uint8_t *reencoded_buffer = ms_malloc0(msg_size);
om=allocb(payloadlen*4,0);
om=allocb(msg_size*4,0);
mblk_meta_copy(im, om);
if ((declen = g722_decode(s->state,(int16_t *)om->b_wptr, im->b_rptr, payloadlen))<0) {
if ((declen = g722_decode(s->dec_state,(int16_t *)om->b_wptr, im->b_rptr, msg_size))<0) {
ms_warning("g722_decode error!");
freemsg(om);
} else {
ms_concealer_inc_sample_time(s->concealer, f->ticker->time, declen/16, TRUE); /* increase concealer context by sample nbr decoded/sample rate (in ms) */
/* update local encoder state with received data, re-encoded buffer is just discarded */
g722_encode(s->enc_state, reencoded_buffer, (int16_t *)om->b_wptr, declen);
scale_up((int16_t *)om->b_wptr,declen);
om->b_wptr += declen*2;
ms_queue_put(f->outputs[0],om);
/* Store decoded data in plc_buffer */
generic_plc_update_plc_buffer(s->plc_context, om->b_wptr, declen*sizeof(int16_t));
/* introduce delay (TRANSITION_DELAY ms) */
generic_plc_update_continuity_buffer(s->plc_context, om->b_wptr, declen*sizeof(int16_t));
if (s->plc_context->plc_samples_used!=0) {
/* we were doing PLC, now resuming with normal audio, continuity buffer is twice the transition delay lengths,
* the second half is untouched by the update function and contains transition data generated by PLC */
generic_plc_transition_mix(((int16_t *)(om->b_wptr))+16*TRANSITION_DELAY, ((int16_t *)(s->plc_context->continuity_buffer))+16*TRANSITION_DELAY, 16*TRANSITION_DELAY);
}
}
s->plc_context->plc_index=0;
s->plc_context->plc_samples_used=0;
om->b_wptr += declen*sizeof(int16_t);
ms_queue_put(f->outputs[0],om);
ms_free(reencoded_buffer);
freemsg(im);
}
/* PLC needed ?*/
if (ms_concealer_context_is_concealement_required(s->concealer, f->ticker->time)) {
unsigned int buff_size = 16*sizeof(int16_t)*f->ticker->interval; /* size in bytes of signal to be generated */
unsigned char *reencoded_buffer = ms_malloc0(buff_size);
unsigned char *decoded_buffer = ms_malloc0(buff_size);
int k;
ms_concealer_inc_sample_time(s->concealer, f->ticker->time, f->ticker->interval, FALSE);
om = allocb(buff_size, 0);
mblk_set_plc_flag(om, 1);
generic_plc_generate_samples(s->plc_context, (int16_t *)om->b_wptr, buff_size/sizeof(int16_t));
/* store the generated samples into plc_buffer */
generic_plc_update_plc_buffer(s->plc_context, om->b_wptr, buff_size);
memcpy(decoded_buffer, om->b_wptr+TRANSITION_DELAY*16*sizeof(int16_t), buff_size-TRANSITION_DELAY*16*sizeof(int16_t));
memcpy(decoded_buffer+buff_size-TRANSITION_DELAY*16*sizeof(int16_t), s->plc_context->continuity_buffer, TRANSITION_DELAY*16*sizeof(int16_t));
/* update local encoder/decoder state with plc generated data, re-encoded buffer is just discarded */
scale_down((int16_t *)decoded_buffer, 16*f->ticker->interval);
k = g722_encode(s->enc_state, reencoded_buffer, (int16_t *)decoded_buffer, 16*f->ticker->interval);
k = g722_decode(s->dec_state,(int16_t *)decoded_buffer, reencoded_buffer, k);
ms_free(reencoded_buffer);
ms_free(decoded_buffer);
om->b_wptr += buff_size;
ms_queue_put(f->outputs[0], om);
}
};
static int ms_g722_dec_have_plc(MSFilter *f, void *arg) {
*((int *)arg) = 1;
return 0;
}
static MSFilterMethod dec_methods[]={
{ MS_FILTER_GET_SAMPLE_RATE, get_sr },
{ MS_AUDIO_DECODER_HAVE_PLC, ms_g722_dec_have_plc },
{ 0 , NULL }
};
......@@ -243,7 +320,8 @@ MSFilterDesc ms_g722_dec_desc={
dec_process,
NULL,
dec_uninit,
dec_methods
dec_methods,
MS_FILTER_IS_PUMP
};
#else
......@@ -259,6 +337,7 @@ MSFilterDesc ms_g722_dec_desc={
.init = dec_init,
.process = dec_process,
.uninit = dec_uninit,
.flags = MS_FILTER_IS_PUMP,
.methods = dec_methods
};
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2011 Yann Diorcet(yann.diorcet@linphone.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdint.h>
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/mscodecutils.h>
#include <mediastreamer2/msticker.h>
#include "mediastreamer2/msgenericplc.h"
#include "genericplc.h"
#ifdef HAVE_G729B
#include "bcg729/decoder.h"
#endif
/*filter common method*/
typedef struct {
MSConcealerContext* concealer;
int rate;
int nchannels;
MSCngData cng_data;
bool_t cng_set;
bool_t cng_running;
#ifdef HAVE_G729B
bcg729DecoderChannelContextStruct *decoderChannelContext;
#endif
plc_context_t *plc_context;
} generic_plc_struct;
const static unsigned int MAX_PLC_COUNT = UINT32_MAX;
static void generic_plc_init(MSFilter *f) {
generic_plc_struct *mgps = (generic_plc_struct*) ms_new0(generic_plc_struct, 1);
#ifdef HAVE_G729B
mgps->decoderChannelContext = initBcg729DecoderChannel(); /* initialize bcg729 decoder for CNG */
#endif
mgps->concealer = ms_concealer_context_new(MAX_PLC_COUNT);
mgps->nchannels = 1;
f->data = mgps;
}
static void generic_plc_preprocess(MSFilter *f) {
generic_plc_struct *mgps=(generic_plc_struct*)f->data;
mgps->plc_context = generic_plc_create_context(mgps->rate);
}
static void generic_plc_process(MSFilter *f) {
generic_plc_struct *mgps=(generic_plc_struct*)f->data;
plc_context_t *plc_context = mgps->plc_context;
mblk_t *m;
while((m=ms_queue_get(f->inputs[0]))!=NULL){
int transitionBufferSize = mgps->rate*sizeof(int16_t)*TRANSITION_DELAY/1000;
size_t msg_size = msgdsize(m);
unsigned int time = (unsigned int)((1000*msg_size)/(mgps->rate*sizeof(int16_t)*mgps->nchannels));
ms_concealer_inc_sample_time(mgps->concealer, f->ticker->time, time, TRUE);
/* Store current msg in plc_buffer */
generic_plc_update_plc_buffer(plc_context, m->b_rptr, msg_size);
/* introduce delay (TRANSITION_DELAY ms) */
generic_plc_update_continuity_buffer(plc_context, m->b_rptr, msg_size);
if (mgps->cng_running){
/*we were doing CNG, now resuming with normal audio*/
int16_t continuity_buffer[80];
#ifdef HAVE_G729B
bcg729Decoder(mgps->decoderChannelContext, NULL, 0, 1, 1, 1, continuity_buffer);
#else
memset (continuity_buffer, 0, 80*sizeof(int16_t));
#endif
memcpy(m->b_rptr, continuity_buffer, transitionBufferSize);
generic_plc_transition_mix((int16_t *)(m->b_rptr+transitionBufferSize), continuity_buffer, mgps->rate*TRANSITION_DELAY/1000);
mgps->cng_running=FALSE;
mgps->cng_set=FALSE;
}
if (plc_context->plc_samples_used!=0) {
/*we were doing PLC, now resuming with normal audio, continuity buffer is twice the transition delay lengths,
* the second half is untouched by the update function and contains transition data generated by PLC */
generic_plc_transition_mix((int16_t *)(m->b_rptr+transitionBufferSize), (int16_t *)(plc_context->continuity_buffer+transitionBufferSize), mgps->rate*TRANSITION_DELAY/1000);
}
plc_context->plc_index=0;
plc_context->plc_samples_used=0;
ms_queue_put(f->outputs[0], m);
}
if (ms_concealer_context_is_concealement_required(mgps->concealer, f->ticker->time)) {
unsigned int buff_size = mgps->rate*sizeof(int16_t)*mgps->nchannels*f->ticker->interval/1000;
#ifdef HAVE_G729B
m = allocb(buff_size, 0);
/* Transmitted CNG data is in mgps->cng_data : give it to bcg729 decoder -> output in m->b_wptr */
if (mgps->cng_set) { /* received some CNG data */
mgps->cng_set=FALSE; /* reset flag */
mgps->cng_running=TRUE;
bcg729Decoder(mgps->decoderChannelContext, mgps->cng_data.data, mgps->cng_data.datasize, 0, 1, 1, (int16_t *)(m->b_wptr));
mblk_set_cng_flag(m, 1);
generic_plc_transition_mix((int16_t *)m->b_wptr, (int16_t *)mgps->continuity_buffer, mgps->rate*TRANSITION_DELAY/1000);
/* TODO: if ticker->interval is not 10 ms which is also G729 frame length, we must generate untransmitted frame CNG until we reach the requested data amount */
} else if (mgps->cng_running) { /* missing frame but CNG is ongoing: shall be an untransmitted frame */
bcg729Decoder(mgps->decoderChannelContext, NULL, 0, 1, 1, 1, (int16_t *)(m->b_wptr));
mblk_set_cng_flag(m, 1);
} else { /* plc */
mblk_set_plc_flag(m, 1);
generic_plc_generate_samples(plc_context, (int16_t *)m->b_wptr, buff_size/sizeof(int16_t));
/* store the generated samples into plc_buffer */
generic_plc_update_plc_buffer(plc_context, m->b_wptr, buff_size);
//memset(m->b_wptr, 0, buff_size);
}
#else
m = allocb(buff_size, 0);
if (mgps->cng_set){
mgps->cng_set=FALSE; /* reset flag */
mgps->cng_running=TRUE;
mblk_set_cng_flag(m, 1);
/*TODO do something with the buffer*/
memset(m->b_wptr, 0, buff_size);
} else if (mgps->cng_running) { /* missing frame but CNG is ongoing: shall be an untransmitted frame */
memset(m->b_wptr, 0, buff_size);
mblk_set_cng_flag(m, 1);
}else{ /* plc */
mblk_set_plc_flag(m, 1);
generic_plc_generate_samples(plc_context, (int16_t *)m->b_wptr, buff_size/sizeof(int16_t));
/* store the generated samples into plc_buffer */
generic_plc_update_plc_buffer(plc_context, m->b_wptr, buff_size);
//memset(m->b_wptr, 0, buff_size);
}
#endif
m->b_wptr += buff_size;
ms_queue_put(f->outputs[0], m);
ms_concealer_inc_sample_time(mgps->concealer, f->ticker->time, f->ticker->interval, FALSE);
}
}
static void generic_plc_unit(MSFilter *f) {
generic_plc_struct *mgps = (generic_plc_struct*) f->data;
generic_plc_destroy_context(mgps->plc_context);
ms_concealer_context_destroy(mgps->concealer);
#ifdef HAVE_G729B
closeBcg729DecoderChannel(mgps->decoderChannelContext);
#endif
ms_free(mgps);
}
static int generic_plc_get_sr(MSFilter *f, void *arg){
generic_plc_struct *s=(generic_plc_struct*)f->data;
((int*)arg)[0]=s->rate;
return 0;
}
static int generic_plc_set_sr(MSFilter *f, void *arg){
generic_plc_struct *s=(generic_plc_struct*)f->data;
s->rate=((int*)arg)[0];
return 0;
}
static int generic_plc_set_nchannels(MSFilter *f, void *arg) {
generic_plc_struct *s = (generic_plc_struct *)f->data;
s->nchannels = *(int *)arg;
return 0;
}
static int generic_plc_set_cn(MSFilter *f, void *arg) {
generic_plc_struct *s = (generic_plc_struct *)f->data;
memcpy(&s->cng_data,arg, sizeof(MSCngData));
s->cng_set=TRUE;
return 0;
}
static MSFilterMethod generic_plc_methods[] = {
{ MS_FILTER_SET_SAMPLE_RATE , generic_plc_set_sr },
{ MS_FILTER_GET_SAMPLE_RATE , generic_plc_get_sr },
{ MS_FILTER_SET_NCHANNELS , generic_plc_set_nchannels },
{ MS_GENERIC_PLC_SET_CN , generic_plc_set_cn },
{ 0 , NULL }
};
#ifdef _MSC_VER
MSFilterDesc ms_genericplc_desc= {
MS_GENERIC_PLC_ID,
"MSGenericPLC",
N_("Generic PLC."),
MS_FILTER_OTHER,
NULL,
1,
1,
generic_plc_init,
generic_plc_preprocess,
generic_plc_process,
NULL,
generic_plc_unit,
generic_plc_methods,
MS_FILTER_IS_PUMP
};
#else
MSFilterDesc ms_genericplc_desc = {
.id = MS_GENERIC_PLC_ID,
.name = "MSGenericPLC",
.text = N_("Generic PLC."),
.category = MS_FILTER_OTHER,
.ninputs = 1,
.noutputs = 1,
.init = generic_plc_init,
.preprocess = generic_plc_preprocess,
.process = generic_plc_process,
.uninit = generic_plc_unit,
.flags = MS_FILTER_IS_PUMP,
.methods = generic_plc_methods
};
#endif
MS_FILTER_DESC_EXPORT(ms_genericplc_desc)
......@@ -145,6 +145,7 @@ static void vad_dtx_process(MSFilter *f){
ms_filter_notify(f, MS_VAD_DTX_VOICE, NULL);
}
}
ms_queue_put(f->outputs[0],m);
}
#endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment