diff options
-rw-r--r-- | include/asterisk/dsp.h | 5 | ||||
-rw-r--r-- | include/asterisk/res_fax.h | 4 | ||||
-rw-r--r-- | main/dsp.c | 148 | ||||
-rw-r--r-- | res/res_fax.c | 193 | ||||
-rw-r--r-- | res/res_fax_spandsp.c | 85 |
5 files changed, 207 insertions, 228 deletions
diff --git a/include/asterisk/dsp.h b/include/asterisk/dsp.h index f82f08a94..16262c05d 100644 --- a/include/asterisk/dsp.h +++ b/include/asterisk/dsp.h @@ -45,9 +45,8 @@ #define DSP_FAXMODE_DETECT_CNG (1 << 0) #define DSP_FAXMODE_DETECT_CED (1 << 1) -#define DSP_FAXMODE_DETECT_V21 (1 << 2) -#define DSP_FAXMODE_DETECT_SQUELCH (1 << 3) -#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21) +#define DSP_FAXMODE_DETECT_SQUELCH (1 << 2) +#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED) #define DSP_TONE_STATE_SILENCE 0 #define DSP_TONE_STATE_RINGING 1 diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h index ba20944e4..b397bd445 100644 --- a/include/asterisk/res_fax.h +++ b/include/asterisk/res_fax.h @@ -44,6 +44,8 @@ enum ast_fax_capabilities { AST_FAX_TECH_MULTI_DOC = (1 << 4), /*! T.38 - T.30 Gateway */ AST_FAX_TECH_GATEWAY = (1 << 5), + /*! V21 detection is supported */ + AST_FAX_TECH_V21_DETECT = (1 << 6), }; /*! \brief fax modem capabilities */ @@ -160,6 +162,8 @@ struct ast_fax_session_details { uint32_t send_cng:1; /*! send a T.38 reinvite */ uint32_t request_t38:1; + /*! a V.21 preamble was detected */ + uint32_t v21_detected:1; }; } option; /*! override the minimum transmission rate with a channel variable */ diff --git a/main/dsp.c b/main/dsp.c index 91eea6c9d..0f8955314 100644 --- a/main/dsp.c +++ b/main/dsp.c @@ -246,20 +246,6 @@ typedef struct 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]; goertzel_state_t col_out[4]; int hits_to_begin; /* How many successive hits needed to consider begin of a digit */ @@ -405,7 +391,6 @@ 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) @@ -479,56 +464,10 @@ 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); if (s->faxmode & DSP_FAXMODE_DETECT_SQUELCH) { s->cng_tone_state.squelch = 1; s->ced_tone_state.squelch = 1; @@ -580,89 +519,6 @@ 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->miss_count == 3 || (s->hit_count == 1 && s->miss_count == 2)) { - s->hit_count++; - } else { - s->hit_count = 1; - } - - 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; @@ -1599,10 +1455,6 @@ 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)) { diff --git a/res/res_fax.c b/res/res_fax.c index ec2c98b82..7ee4cceff 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -249,13 +249,12 @@ struct ast_fax_debug_info { struct fax_gateway { /*! \brief FAX Session */ struct ast_fax_session *s; + struct ast_fax_session *peer_v21_session; + struct ast_fax_session *chan_v21_session; /*! \brief reserved fax session token */ struct ast_fax_tech_token *token; /*! \brief the start of our timeout counter */ struct timeval timeout_start; - /*! \brief DSP Processor */ - struct ast_dsp *chan_dsp; - struct ast_dsp *peer_dsp; /*! \brief framehook used in gateway mode */ int framehook; /*! \brief bridged */ @@ -336,7 +335,7 @@ static struct { enum ast_fax_modems modems; uint32_t statusevents:1; uint32_t ecm:1; - unsigned int minrate; + unsigned int minrate; unsigned int maxrate; } general_options; @@ -371,7 +370,7 @@ struct manager_event_info { }; static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame) -{ +{ struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c; int dspsilence; unsigned int last_consec_frames, last_consec_ms; @@ -403,7 +402,7 @@ static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned in history->consec_ms += (frame->samples / 8); } -static void destroy_callback(void *data) +static void destroy_callback(void *data) { if (data) { ao2_ref(data, -1); @@ -421,9 +420,9 @@ static struct ast_fax_session_details *find_details(struct ast_channel *chan) struct ast_fax_session_details *details; struct ast_datastore *datastore; - ast_channel_lock(chan); + ast_channel_lock(chan); if (!(datastore = ast_channel_datastore_find(chan, &fax_datastore, NULL))) { - ast_channel_unlock(chan); + ast_channel_unlock(chan); return NULL; } if (!(details = datastore->data)) { @@ -431,8 +430,8 @@ static struct ast_fax_session_details *find_details(struct ast_channel *chan) ast_channel_unlock(chan); return NULL; } - ao2_ref(details, 1); - ast_channel_unlock(chan); + ao2_ref(details, 1); + ast_channel_unlock(chan); return details; } @@ -442,11 +441,11 @@ static void destroy_session_details(void *details) { struct ast_fax_session_details *d = details; struct ast_fax_document *doc; - + while ((doc = AST_LIST_REMOVE_HEAD(&d->documents, next))) { ast_free(doc); } - ast_string_field_free_memory(d); + ast_string_field_free_memory(d); } /*! \brief create a FAX session details structure */ @@ -457,7 +456,7 @@ static struct ast_fax_session_details *session_details_new(void) if (!(d = ao2_alloc(sizeof(*d), destroy_session_details))) { return NULL; } - + if (ast_string_field_init(d, 512)) { ao2_ref(d, -1); return NULL; @@ -512,7 +511,7 @@ static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, co } /*! \brief returns a reference counted details structure from the channel's fax datastore. If the datastore - * does not exist it will be created */ + * does not exist it will be created */ static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan) { struct ast_fax_session_details *details; @@ -556,7 +555,7 @@ unsigned int ast_fax_minrate(void) } static int update_modem_bits(enum ast_fax_modems *bits, const char *value) -{ +{ char *m[5], *tok, *v = (char *)value; int i = 0, j; @@ -636,7 +635,13 @@ static char *ast_fax_caps_to_str(enum ast_fax_capabilities caps, char *buf, size ast_build_string(&buf, &size, "GATEWAY"); first = 0; } - + if (caps & AST_FAX_TECH_V21_DETECT) { + if (!first) { + ast_build_string(&buf, &size, ","); + } + ast_build_string(&buf, &size, "V21"); + first = 0; + } return out; } @@ -748,7 +753,7 @@ void ast_fax_tech_unregister(struct ast_fax_tech *tech) ast_module_unref(ast_module_info->self); ast_free(fax); ast_verb(4, "Unregistered FAX module type '%s'\n", tech->type); - break; + break; } AST_RWLIST_TRAVERSE_SAFE_END; AST_RWLIST_UNLOCK(&faxmodules); @@ -856,7 +861,7 @@ static void destroy_session(void *session) } ao2_ref(s->details, -1); } - + if (s->debug_info) { ast_dsp_free(s->debug_info->dsp); ast_free(s->debug_info); @@ -996,7 +1001,7 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d return NULL; } ast_dsp_set_threshold(s->debug_info->dsp, 128); - } + } if (!(s->channame = ast_strdup(chan->name))) { fax_session_release(s, token); @@ -1311,7 +1316,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det } ast_channel_lock(chan); - /* update session details */ + /* update session details */ if (ast_strlen_zero(details->headerinfo) && (tempvar = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"))) { ast_string_field_set(details, headerinfo, tempvar); } @@ -1409,7 +1414,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det (frame->datalen == sizeof(t38_parameters))) { unsigned int was_t38 = t38negotiated; struct ast_control_t38_parameters *parameters = frame->data.ptr; - + switch (parameters->request_response) { case AST_T38_REQUEST_NEGOTIATE: /* the other end has requested a switch to T.38, so reply that we are willing, if we can @@ -1435,15 +1440,15 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det ast_smoother_free(fax->smoother); fax->smoother = NULL; } - + report_fax_status(chan, details, "T.38 Negotiated"); - + ast_verb(3, "Channel '%s' switched to T.38 FAX session '%d'.\n", chan->name, fax->id); } } else if ((frame->frametype == expected_frametype) && (!memcmp(&frame->subclass, &expected_framesubclass, sizeof(frame->subclass)))) { struct ast_frame *f; - + if (fax->smoother) { /* push the frame into a smoother */ if (ast_smoother_feed(fax->smoother, frame) < 0) { @@ -1805,7 +1810,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ao2_ref(details, -1); return -1; } - + ast_atomic_fetchadd_int(&faxregistry.fax_rx_attempts, 1); pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel Problems"); @@ -1909,7 +1914,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) get_manager_event_info(chan, &info); manager_event(EVENT_FLAG_CALL, - "ReceiveFAX", + "ReceiveFAX", "Channel: %s\r\n" "Context: %s\r\n" "Exten: %s\r\n" @@ -2265,7 +2270,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ao2_ref(details, -1); return -1; } - + /* check for unsupported FAX application options */ if (ast_test_flag(&opts, OPT_CALLERMODE) || ast_test_flag(&opts, OPT_CALLEDMODE)) { ast_string_field_set(details, error, "INVALID_ARGUMENTS"); @@ -2407,7 +2412,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_channel_lock(chan); get_manager_event_info(chan, &info); manager_event(EVENT_FLAG_CALL, - "SendFAX", + "SendFAX", "Channel: %s\r\n" "Context: %s\r\n" "Exten: %s\r\n" @@ -2439,20 +2444,34 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) return (!channel_alive) ? -1 : 0; } -/*! \brief destroy a FAX gateway session structure */ -static void destroy_gateway(void *data) +/*! \brief destroy the v21 detection parts of a fax gateway session */ +static void destroy_v21_sessions(struct fax_gateway *gateway) { - struct fax_gateway *gateway = data; + if (gateway->chan_v21_session) { + ao2_lock(faxregistry.container); + ao2_unlink(faxregistry.container, gateway->chan_v21_session); + ao2_unlock(faxregistry.container); - if (gateway->chan_dsp) { - ast_dsp_free(gateway->chan_dsp); - gateway->chan_dsp = NULL; + ao2_ref(gateway->chan_v21_session, -1); + gateway->chan_v21_session = NULL; } - if (gateway->peer_dsp) { - ast_dsp_free(gateway->peer_dsp); - gateway->peer_dsp = NULL; + if (gateway->peer_v21_session) { + ao2_lock(faxregistry.container); + ao2_unlink(faxregistry.container, gateway->peer_v21_session); + ao2_unlock(faxregistry.container); + + ao2_ref(gateway->peer_v21_session, -1); + gateway->peer_v21_session = NULL; } +} + +/*! \brief destroy a FAX gateway session structure */ +static void destroy_gateway(void *data) +{ + struct fax_gateway *gateway = data; + + destroy_v21_sessions(gateway); if (gateway->s) { fax_session_release(gateway->s, gateway->token); @@ -2468,35 +2487,38 @@ static void destroy_gateway(void *data) } /*! \brief Create a new fax gateway object. + * \param chan the channel the gateway object will be attached to * \param details the fax session details * \return NULL or a fax gateway object */ -static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *details) +static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details) { struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway); + struct ast_fax_session_details *v21_details; if (!gateway) { return NULL; } - gateway->chan_dsp = ast_dsp_new(); - if (!gateway->chan_dsp) { + if (!(v21_details = session_details_new())) { ao2_ref(gateway, -1); return NULL; } - gateway->peer_dsp = ast_dsp_new(); - if (!gateway->peer_dsp) { + v21_details->caps = AST_FAX_TECH_V21_DETECT; + if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) { + ao2_ref(v21_details, -1); ao2_ref(gateway, -1); return NULL; } - gateway->framehook = -1; - - ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT); - ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21); + if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) { + ao2_ref(v21_details, -1); + ao2_ref(gateway, -1); + return NULL; + } + ao2_ref(v21_details, -1); - ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT); - ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21); + gateway->framehook = -1; details->caps = AST_FAX_TECH_GATEWAY; if (details->gateway_timeout && !(gateway->s = fax_session_reserve(details, &gateway->token))) { @@ -2591,24 +2613,20 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st 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; + struct ast_fax_session *active_v21_session = (active == chan) ? gateway->chan_v21_session : gateway->peer_v21_session; - if (gateway->detected_v21) { + if (!active_v21_session || gateway->detected_v21) { return f; } - if (!dfr) { - return f; - } - - if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) { - return f; + if (active_v21_session->tech->write(active_v21_session, f) == 0 && + active_v21_session->details->option.v21_detected) { + gateway->detected_v21 = 1; } - if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') { - gateway->detected_v21 = 1; + if (gateway->detected_v21) { + destroy_v21_sessions(gateway); if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) { ast_debug(1, "detected v21 preamble from %s\n", active->name); return fax_gateway_request_t38(gateway, chan, f); @@ -2617,7 +2635,6 @@ static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, str } } - ast_frfree(dfr); return f; } @@ -3120,7 +3137,7 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d set_channel_variables(chan, details); /* set up the frame hook*/ - gateway = fax_gateway_new(details); + gateway = fax_gateway_new(chan, details); if (!gateway) { ast_string_field_set(details, result, "FAILED"); ast_string_field_set(details, resultstr, "error initializing gateway session"); @@ -3468,7 +3485,7 @@ static char *cli_fax_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_ switch (cmd) { case CLI_INIT: e->command = "fax set debug {on|off}"; - e->usage = + e->usage = "Usage: fax set debug { on | off }\n" " Enable/Disable FAX debugging on new FAX sessions. The basic FAX debugging will result in\n" " additional events sent to manager sessions with 'call' class permissions. When\n" @@ -3499,11 +3516,11 @@ static char *cli_fax_show_capabilities(struct ast_cli_entry *e, int cmd, struct { struct fax_module *fax; unsigned int num_modules = 0; - + switch (cmd) { case CLI_INIT: e->command = "fax show capabilities"; - e->usage = + e->usage = "Usage: fax show capabilities\n" " Shows the capabilities of the registered FAX technology modules\n"; return NULL; @@ -3594,12 +3611,12 @@ static char *cli_fax_show_session(struct ast_cli_entry *e, int cmd, struct ast_c return CLI_SUCCESS; } - + /*! \brief display fax stats */ static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct fax_module *fax; - + switch (cmd) { case CLI_INIT: e->command = "fax show stats"; @@ -3628,6 +3645,36 @@ static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli return CLI_SUCCESS; } +static const char *cli_session_type(struct ast_fax_session *s) +{ + if (s->details->caps & AST_FAX_TECH_AUDIO) { + return "G.711"; + } + if (s->details->caps & AST_FAX_TECH_T38) { + return "T.38"; + } + + return "none"; +} + +static const char *cli_session_operation(struct ast_fax_session *s) +{ + if (s->details->caps & AST_FAX_TECH_GATEWAY) { + return "gateway"; + } + if (s->details->caps & AST_FAX_TECH_SEND) { + return "send"; + } + if (s->details->caps & AST_FAX_TECH_RECEIVE) { + return "receive"; + } + if (s->details->caps & AST_FAX_TECH_V21_DETECT) { + return "V.21"; + } + + return "none"; +} + /*! \brief display fax sessions */ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { @@ -3658,10 +3705,8 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_ ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n", s->channame, s->tech->type, s->id, - (s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38", - (s->details->caps & AST_FAX_TECH_GATEWAY) - ? "gateway" - : (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", + cli_session_type(s), + cli_session_operation(s), ast_fax_state_to_str(s->state), S_OR(filenames, "")); ast_free(filenames); @@ -3693,9 +3738,9 @@ static int set_config(const char *config_file) struct ast_flags config_flags = { 0 }; char modems[128] = ""; - /* set defaults */ + /* set defaults */ general_options.minrate = RES_FAX_MINRATE; - general_options.maxrate = RES_FAX_MAXRATE; + general_options.maxrate = RES_FAX_MAXRATE; general_options.statusevents = RES_FAX_STATUSEVENTS; general_options.modems = RES_FAX_MODEM; general_options.ecm = AST_FAX_OPTFLAG_TRUE; @@ -3979,7 +4024,7 @@ struct ast_custom_function acf_faxopt = { static int unload_module(void) { ast_cli_unregister_multiple(fax_cli, ARRAY_LEN(fax_cli)); - + if (ast_custom_function_unregister(&acf_faxopt) < 0) { ast_log(LOG_WARNING, "failed to unregister function '%s'\n", acf_faxopt.name); } @@ -4012,7 +4057,7 @@ static int load_module(void) if (!(faxregistry.container = ao2_container_alloc(FAX_MAXBUCKETS, session_hash_cb, session_cmp_cb))) { return AST_MODULE_LOAD_DECLINE; } - + if (set_config(config) < 0) { ast_log(LOG_ERROR, "failed to load configuration file '%s'\n", config); ao2_ref(faxregistry.container, -1); diff --git a/res/res_fax_spandsp.c b/res/res_fax_spandsp.c index 81711bb40..7f4fbfc67 100644 --- a/res/res_fax_spandsp.c +++ b/res/res_fax_spandsp.c @@ -80,6 +80,9 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s); static int spandsp_fax_gateway_start(struct ast_fax_session *s); static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f); static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s); +static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f); +static void spandsp_v21_cleanup(struct ast_fax_session *s); +static void spandsp_v21_tone(void *data, int code, int level, int delay); static char *spandsp_fax_cli_show_capabilities(int fd); static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd); @@ -98,7 +101,9 @@ static struct ast_fax_tech spandsp_fax_tech = { */ .version = "pre-20090220", #endif - .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY, + .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND + | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY + | AST_FAX_TECH_V21_DETECT, .new_session = spandsp_fax_new, .destroy_session = spandsp_fax_destroy, .read = spandsp_fax_read, @@ -150,8 +155,12 @@ struct spandsp_pvt { struct ast_timer *timer; AST_LIST_HEAD(frame_queue, ast_frame) read_frames; + + int v21_detected; + modem_connect_tones_rx_state_t *tone_state; }; +static int spandsp_v21_new(struct spandsp_pvt *p); static void session_destroy(struct spandsp_pvt *p); static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count); static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code); @@ -450,6 +459,20 @@ static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *deta t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); } +static int spandsp_v21_new(struct spandsp_pvt *p) +{ + /* XXX Here we use MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE even though + * we don't care about CED tones. Using MODEM_CONNECT_TONES_PREAMBLE + * doesn't seem to work right all the time. + */ + p->tone_state = modem_connect_tones_rx_init(NULL, MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE, spandsp_v21_tone, p); + if (!p->tone_state) { + return -1; + } + + return 0; +} + /*! \brief create an instance of the spandsp tech_pvt for a fax session */ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token) { @@ -461,6 +484,15 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke goto e_return; } + if (s->details->caps & AST_FAX_TECH_V21_DETECT) { + if (spandsp_v21_new(p)) { + ast_log(LOG_ERROR, "Cannot initialize the spandsp private v21 technology structure.\n"); + goto e_return; + } + s->state = AST_FAX_STATE_ACTIVE; + return p; + } + if (s->details->caps & AST_FAX_TECH_GATEWAY) { s->state = AST_FAX_STATE_INITIALIZED; return p; @@ -513,6 +545,11 @@ e_return: return NULL; } +static void spandsp_v21_cleanup(struct ast_fax_session *s) { + struct spandsp_pvt *p = s->tech_pvt; + modem_connect_tones_rx_free(p->tone_state); +} + /*! \brief Destroy a spandsp fax session. */ static void spandsp_fax_destroy(struct ast_fax_session *s) @@ -521,6 +558,8 @@ static void spandsp_fax_destroy(struct ast_fax_session *s) if (s->details->caps & AST_FAX_TECH_GATEWAY) { spandsp_fax_gateway_cleanup(s); + } else if (s->details->caps & AST_FAX_TECH_V21_DETECT) { + spandsp_v21_cleanup(s); } else { session_destroy(p); } @@ -571,6 +610,36 @@ static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s) return &ast_null_frame; } +static void spandsp_v21_tone(void *data, int code, int level, int delay) +{ + struct spandsp_pvt *p = data; + + if (code == MODEM_CONNECT_TONES_FAX_PREAMBLE) { + p->v21_detected = 1; + } +} + +static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f) { + struct spandsp_pvt *p = s->tech_pvt; + + if (p->v21_detected) { + return 0; + } + + /*invalid frame*/ + if (!f->data.ptr || !f->datalen) { + return -1; + } + + modem_connect_tones_rx(p->tone_state, f->data.ptr, f->samples); + + if (p->v21_detected) { + s->details->option.v21_detected = 1; + } + + return 0; +} + /*! \brief Write a frame to the spandsp fax stack. * \param s a fax session * \param f the frame to write @@ -585,6 +654,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame * { struct spandsp_pvt *p = s->tech_pvt; + if (s->details->caps & AST_FAX_TECH_V21_DETECT) { + return spandsp_v21_detect(s, f); + } + if (s->details->caps & AST_FAX_TECH_GATEWAY) { return spandsp_fax_gateway_process(s, f); } @@ -906,10 +979,10 @@ static char *spandsp_fax_cli_show_capabilities(int fd) /*! \brief */ static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd) { - struct spandsp_pvt *p = s->tech_pvt; - ao2_lock(s); if (s->details->caps & AST_FAX_TECH_GATEWAY) { + struct spandsp_pvt *p = s->tech_pvt; + ast_cli(fd, "%-22s : %d\n", "session", s->id); ast_cli(fd, "%-22s : %s\n", "operation", "Gateway"); ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state)); @@ -920,7 +993,13 @@ static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd) ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate); ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1); } + } else if (s->details->caps & AST_FAX_TECH_V21_DETECT) { + ast_cli(fd, "%-22s : %d\n", "session", s->id); + ast_cli(fd, "%-22s : %s\n", "operation", "V.21 Detect"); + ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state)); } else { + struct spandsp_pvt *p = s->tech_pvt; + ast_cli(fd, "%-22s : %d\n", "session", s->id); ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit"); ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state)); |