do v21 detection instead of CED detection for the fax gateway
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@327769 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
d75b0b09d3
commit
f4348dccf2
|
@ -45,7 +45,8 @@
|
|||
|
||||
#define DSP_FAXMODE_DETECT_CNG (1 << 0)
|
||||
#define DSP_FAXMODE_DETECT_CED (1 << 1)
|
||||
#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED)
|
||||
#define DSP_FAXMODE_DETECT_V21 (1 << 2)
|
||||
#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21)
|
||||
|
||||
#define DSP_TONE_STATE_SILENCE 0
|
||||
#define DSP_TONE_STATE_RINGING 1
|
||||
|
|
147
main/dsp.c
147
main/dsp.c
|
@ -244,6 +244,20 @@ typedef struct
|
|||
|
||||
} tone_detect_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int block_size;
|
||||
goertzel_state_t tone;
|
||||
float energy; /* Accumulated energy of the current block */
|
||||
int samples_pending; /* Samples remain to complete the current block */
|
||||
|
||||
float threshold; /* Energy of the tone relative to energy from all other signals to consider a hit */
|
||||
|
||||
int hit_count;
|
||||
int miss_count;
|
||||
|
||||
} v21_detect_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
goertzel_state_t row_out[4];
|
||||
|
@ -391,6 +405,7 @@ struct ast_dsp {
|
|||
digit_detect_state_t digit_state;
|
||||
tone_detect_state_t cng_tone_state;
|
||||
tone_detect_state_t ced_tone_state;
|
||||
v21_detect_state_t v21_state;
|
||||
};
|
||||
|
||||
static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment)
|
||||
|
@ -463,10 +478,55 @@ static void ast_tone_detect_init(tone_detect_state_t *s, int freq, int duration,
|
|||
ast_debug(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required);
|
||||
}
|
||||
|
||||
static void ast_v21_detect_init(v21_detect_state_t *s, unsigned int sample_rate)
|
||||
{
|
||||
float x;
|
||||
int periods_in_block;
|
||||
|
||||
/* If we want to remove tone, it is important to have block size not
|
||||
to exceed frame size. Otherwise by the moment tone is detected it is too late
|
||||
to squelch it from previous frames. Block size is 20ms at the given sample rate.*/
|
||||
s->block_size = (20 * sample_rate) / 1000;
|
||||
|
||||
periods_in_block = s->block_size * 1850 / sample_rate;
|
||||
|
||||
/* Make sure we will have at least 5 periods at target frequency for analisys.
|
||||
This may make block larger than expected packet and will make squelching impossible
|
||||
but at least we will be detecting the tone */
|
||||
if (periods_in_block < 5)
|
||||
periods_in_block = 5;
|
||||
|
||||
/* Now calculate final block size. It will contain integer number of periods */
|
||||
s->block_size = periods_in_block * sample_rate / 1850;
|
||||
|
||||
goertzel_init(&s->tone, 1850.0, s->block_size, sample_rate);
|
||||
|
||||
s->samples_pending = s->block_size;
|
||||
s->hit_count = 0;
|
||||
s->miss_count = 0;
|
||||
s->energy = 0.0;
|
||||
|
||||
/* We want tone energy to be amp decibels above the rest of the signal (the noise).
|
||||
According to Parseval's theorem the energy computed in time domain equals to energy
|
||||
computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result)
|
||||
from the energy in the time domain we will get energy of the remaining signal (without the tone
|
||||
we are detecting). We will be checking that
|
||||
10*log(Ew / (Et - Ew)) > amp
|
||||
Calculate threshold so that we will be actually checking
|
||||
Ew > Et * threshold
|
||||
*/
|
||||
|
||||
x = pow(10.0, 16 / 10.0);
|
||||
s->threshold = x / (x + 1);
|
||||
|
||||
ast_debug(1, "Setup v21 detector, block_size=%d\n", s->block_size);
|
||||
}
|
||||
|
||||
static void ast_fax_detect_init(struct ast_dsp *s)
|
||||
{
|
||||
ast_tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB, s->sample_rate);
|
||||
ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB, s->sample_rate);
|
||||
ast_v21_detect_init(&s->v21_state, s->sample_rate);
|
||||
}
|
||||
|
||||
static void ast_dtmf_detect_init (dtmf_detect_state_t *s, unsigned int sample_rate)
|
||||
|
@ -513,6 +573,89 @@ static void ast_digit_detect_init(digit_detect_state_t *s, int mf, unsigned int
|
|||
}
|
||||
}
|
||||
|
||||
/*! \brief Detect a v21 preamble.
|
||||
* This code is derived from the tone_detect code and detects a pattern of 1850
|
||||
* Hz tone found in a v21 preamble.
|
||||
*/
|
||||
static int v21_detect(struct ast_dsp *dsp, v21_detect_state_t *s, int16_t *amp, int samples)
|
||||
{
|
||||
float tone_energy;
|
||||
int i;
|
||||
int hit = 0;
|
||||
int limit;
|
||||
int res = 0;
|
||||
int16_t *ptr;
|
||||
int start, end;
|
||||
|
||||
for (start = 0; start < samples; start = end) {
|
||||
/* Process in blocks. */
|
||||
limit = samples - start;
|
||||
if (limit > s->samples_pending) {
|
||||
limit = s->samples_pending;
|
||||
}
|
||||
end = start + limit;
|
||||
|
||||
for (i = limit, ptr = amp ; i > 0; i--, ptr++) {
|
||||
/* signed 32 bit int should be enough to suqare any possible signed 16 bit value */
|
||||
s->energy += (int32_t) *ptr * (int32_t) *ptr;
|
||||
|
||||
goertzel_sample(&s->tone, *ptr);
|
||||
}
|
||||
|
||||
s->samples_pending -= limit;
|
||||
|
||||
if (s->samples_pending) {
|
||||
/* Finished incomplete (last) block */
|
||||
break;
|
||||
}
|
||||
|
||||
tone_energy = goertzel_result(&s->tone);
|
||||
|
||||
/* Scale to make comparable */
|
||||
tone_energy *= 2.0;
|
||||
s->energy *= s->block_size;
|
||||
|
||||
ast_debug(10, "v21 1850 Ew=%.2E, Et=%.2E, s/n=%10.2f\n", tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
|
||||
|
||||
hit = 0;
|
||||
if (tone_energy > s->energy * s->threshold) {
|
||||
ast_debug(10, "Hit! count=%d; miss_count=%d\n", s->hit_count, s->miss_count);
|
||||
hit = 1;
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
if (s->hit_count == 0 || s->miss_count == 3) {
|
||||
s->hit_count++;
|
||||
} else {
|
||||
s->hit_count = 0;
|
||||
}
|
||||
|
||||
s->miss_count = 0;
|
||||
} else {
|
||||
s->miss_count++;
|
||||
if (s->miss_count > 3) {
|
||||
s->hit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->hit_count == 4) {
|
||||
ast_debug(1, "v21 preamble detected\n");
|
||||
res = 1;
|
||||
}
|
||||
|
||||
/* Reinitialise the detector for the next block */
|
||||
goertzel_reset(&s->tone);
|
||||
|
||||
/* Advance to the next block */
|
||||
s->energy = 0.0;
|
||||
s->samples_pending = s->block_size;
|
||||
|
||||
amp += limit;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp, int samples)
|
||||
{
|
||||
float tone_energy;
|
||||
|
@ -1441,6 +1584,10 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
|
|||
if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len)) {
|
||||
fax_digit = 'e';
|
||||
}
|
||||
|
||||
if ((dsp->faxmode & DSP_FAXMODE_DETECT_V21) && v21_detect(dsp, &dsp->v21_state, shortdata, len)) {
|
||||
fax_digit = 'g';
|
||||
}
|
||||
}
|
||||
|
||||
if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {
|
||||
|
|
|
@ -246,8 +246,6 @@ struct fax_gateway {
|
|||
struct ast_fax_tech_token *token;
|
||||
/*! \brief the start of our timeout counter */
|
||||
struct timeval timeout_start;
|
||||
/*! \brief the start of our ced timeout */
|
||||
struct timeval ced_timeout_start;
|
||||
/*! \brief DSP Processor */
|
||||
struct ast_dsp *chan_dsp;
|
||||
struct ast_dsp *peer_dsp;
|
||||
|
@ -255,8 +253,8 @@ struct fax_gateway {
|
|||
int framehook;
|
||||
/*! \brief bridged */
|
||||
int bridged:1;
|
||||
/*! \brief 1 if the ced tone came from chan, 0 if it came from peer */
|
||||
int ced_chan:1;
|
||||
/*! \brief 1 if a v21 preamble has been detected */
|
||||
int detected_v21:1;
|
||||
/*! \brief a flag to track the state of our negotiation */
|
||||
enum ast_t38_state t38_state;
|
||||
/*! \brief original audio formats */
|
||||
|
@ -273,7 +271,6 @@ static int fax_logger_level = -1;
|
|||
|
||||
#define RES_FAX_TIMEOUT 10000
|
||||
#define FAX_GATEWAY_TIMEOUT RES_FAX_TIMEOUT
|
||||
#define FAX_GATEWAY_CED_TIMEOUT 3000
|
||||
|
||||
/*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */
|
||||
static struct {
|
||||
|
@ -2412,10 +2409,10 @@ static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *detai
|
|||
gateway->framehook = -1;
|
||||
|
||||
ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
|
||||
ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_CED);
|
||||
ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21);
|
||||
|
||||
ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
|
||||
ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CED);
|
||||
ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21);
|
||||
|
||||
details->caps = AST_FAX_TECH_GATEWAY;
|
||||
if (!(gateway->s = fax_session_reserve(details, &gateway->token))) {
|
||||
|
@ -2503,21 +2500,17 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
|
|||
gateway->t38_state = T38_STATE_NEGOTIATING;
|
||||
gateway->timeout_start = ast_tvnow();
|
||||
|
||||
gateway->ced_timeout_start.tv_sec = 0;
|
||||
gateway->ced_timeout_start.tv_usec = 0;
|
||||
|
||||
ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name);
|
||||
return fp;
|
||||
}
|
||||
|
||||
static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
|
||||
static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
|
||||
{
|
||||
struct ast_frame *dfr = ast_frdup(f);
|
||||
struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
|
||||
struct ast_channel *other = (active == chan) ? peer : chan;
|
||||
|
||||
/* if we have already detected a CED tone, don't waste time here */
|
||||
if (!ast_tvzero(gateway->ced_timeout_start)) {
|
||||
if (gateway->detected_v21) {
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -2529,17 +2522,13 @@ static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, str
|
|||
return f;
|
||||
}
|
||||
|
||||
if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'e') {
|
||||
if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') {
|
||||
gateway->detected_v21 = 1;
|
||||
if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
|
||||
if (ast_channel_get_t38_state(active) == T38_STATE_UNKNOWN) {
|
||||
gateway->ced_timeout_start = ast_tvnow();
|
||||
gateway->ced_chan = (active == chan);
|
||||
ast_debug(1, "detected CED tone from %s; will schedule T.38 request on %s\n", active->name, other->name);
|
||||
} else {
|
||||
return fax_gateway_request_t38(gateway, chan, f);
|
||||
}
|
||||
ast_debug(1, "detected v21 preamble from %s\n", active->name);
|
||||
return fax_gateway_request_t38(gateway, chan, f);
|
||||
} else {
|
||||
ast_debug(1, "detected CED tone on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
|
||||
ast_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2593,9 +2582,6 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
|
|||
if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) {
|
||||
enum ast_t38_state state = ast_channel_get_t38_state(other);
|
||||
|
||||
gateway->ced_timeout_start.tv_sec = 0;
|
||||
gateway->ced_timeout_start.tv_usec = 0;
|
||||
|
||||
if (state == T38_STATE_UNKNOWN) {
|
||||
/* we detected a request to negotiate T.38 and the
|
||||
* other channel appears to support T.38, we'll pass
|
||||
|
@ -2635,7 +2621,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
|
|||
return &ast_null_frame;
|
||||
} else if (gateway->t38_state == T38_STATE_NEGOTIATING) {
|
||||
/* we got a request to negotiate T.38 after we already
|
||||
* sent one to the other party based on CED tone
|
||||
* sent one to the other party based on v21 preamble
|
||||
* detection. We'll just pretend we passed this request
|
||||
* through in the first place. */
|
||||
|
||||
|
@ -2643,12 +2629,12 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
|
|||
gateway->t38_state = T38_STATE_UNKNOWN;
|
||||
gateway->timeout_start = ast_tvnow();
|
||||
|
||||
ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on CED detection\n", active->name);
|
||||
ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble detection\n", active->name);
|
||||
ao2_ref(details, -1);
|
||||
return &ast_null_frame;
|
||||
} else if (gateway->t38_state == T38_STATE_NEGOTIATED) {
|
||||
/* we got a request to negotiate T.38 after we already
|
||||
* sent one to the other party based on CED tone
|
||||
* sent one to the other party based on v21 preamble
|
||||
* detection and received a response. We need to
|
||||
* respond to this and shut down the gateway. */
|
||||
|
||||
|
@ -2869,7 +2855,8 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
|
|||
|
||||
gateway->timeout_start = ast_tvnow();
|
||||
|
||||
/* we are bridged, change r/w formats to SLIN for CED detection and T.30 */
|
||||
/* we are bridged, change r/w formats to SLIN for v21 preamble
|
||||
* detection and T.30 */
|
||||
ast_format_copy(&gateway->chan_read_format, &chan->readformat);
|
||||
ast_format_copy(&gateway->chan_write_format, &chan->readformat);
|
||||
|
||||
|
@ -2944,20 +2931,10 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
|
|||
return fax_gateway_detect_t38(gateway, chan, peer, active, f);
|
||||
}
|
||||
|
||||
/* handle the ced timeout delay */
|
||||
if (!ast_tvzero(gateway->ced_timeout_start)) {
|
||||
if (ast_tvdiff_ms(ast_tvnow(), gateway->ced_timeout_start) > FAX_GATEWAY_CED_TIMEOUT) {
|
||||
if (gateway->ced_chan && chan == active) {
|
||||
return fax_gateway_request_t38(gateway, chan, f);
|
||||
} else if (!gateway->ced_chan && peer == active) {
|
||||
return fax_gateway_request_t38(gateway, chan, f);
|
||||
}
|
||||
}
|
||||
} else if (gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
|
||||
/* not in gateway mode and have not detected CED yet, listen
|
||||
* for CED */
|
||||
/* XXX this should detect a v21 preamble instead of CED */
|
||||
return fax_gateway_detect_ced(gateway, chan, peer, active, f);
|
||||
if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
|
||||
/* not in gateway mode and have not detected v21 yet, listen
|
||||
* for v21 */
|
||||
return fax_gateway_detect_v21(gateway, chan, peer, active, f);
|
||||
}
|
||||
|
||||
/* in gateway mode, gateway some packets */
|
||||
|
@ -2981,7 +2958,7 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
|
|||
}
|
||||
|
||||
/* force silence on the line if T.38 negotiation might be taking place */
|
||||
if (!ast_tvzero(gateway->ced_timeout_start) || (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED)) {
|
||||
if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) {
|
||||
if (f->frametype == AST_FRAME_VOICE && f->subclass.format.id == AST_FORMAT_SLINEAR) {
|
||||
short silence_buf[f->samples];
|
||||
struct ast_frame silence_frame = {
|
||||
|
|
Reference in New Issue