mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-17 15:29:05 +00:00
Make T.38 switchover in ReceiveFAX synchronous.
In receive mode, if the channel that ReceiveFAX is running on supports T.38, we should *always* attempt to switch T.38, rather than listening for an incoming CNG tone and only triggering on that. The channel may be using a low-bitrate codec that distorts the CNG tone, the sending FAX endpoint may not send CNG at all, or there could be a variety of other reasons that we don't detect it, but in all those cases if T.38 is available we certainly want to use it. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@209256 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
132
apps/app_fax.c
132
apps/app_fax.c
@@ -366,12 +366,78 @@ static int transmit_audio(fax_session *s)
|
|||||||
int original_write_fmt = AST_FORMAT_SLINEAR;
|
int original_write_fmt = AST_FORMAT_SLINEAR;
|
||||||
fax_state_t fax;
|
fax_state_t fax;
|
||||||
t30_state_t *t30state;
|
t30_state_t *t30state;
|
||||||
struct ast_dsp *dsp = NULL;
|
|
||||||
int detect_tone = 0;
|
|
||||||
struct ast_frame *inf = NULL;
|
struct ast_frame *inf = NULL;
|
||||||
struct ast_frame *fr;
|
|
||||||
int last_state = 0;
|
int last_state = 0;
|
||||||
struct timeval now, start, state_change;
|
struct timeval now, start, state_change;
|
||||||
|
enum ast_t38_state t38_state;
|
||||||
|
struct ast_control_t38_parameters t38_parameters = { .version = 0,
|
||||||
|
.max_ifp = 800,
|
||||||
|
.rate = AST_T38_RATE_9600,
|
||||||
|
.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
|
||||||
|
.fill_bit_removal = 1,
|
||||||
|
.transcoding_mmr = 1,
|
||||||
|
.transcoding_jbig = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* if in receive mode, try to use T.38 */
|
||||||
|
if (!s->direction) {
|
||||||
|
/* check if we are already in T.38 mode (unlikely), or if we can request
|
||||||
|
* a switch... if so, request it now and wait for the result, rather
|
||||||
|
* than starting an audio FAX session that will have to be cancelled
|
||||||
|
*/
|
||||||
|
if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
|
||||||
|
return 1;
|
||||||
|
} else if ((t38_state != T38_STATE_UNAVAILABLE) &&
|
||||||
|
(t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
|
||||||
|
(ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
|
||||||
|
/* wait up to five seconds for negotiation to complete */
|
||||||
|
unsigned int timeout = 5000;
|
||||||
|
int ms;
|
||||||
|
|
||||||
|
ast_log(LOG_NOTICE, "Negotiating T.38 for receive on %s\n", s->chan->name);
|
||||||
|
while (timeout > 0) {
|
||||||
|
ms = ast_waitfor(s->chan, 1000);
|
||||||
|
if (ms < 0) {
|
||||||
|
ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!ms) {
|
||||||
|
/* nothing happened */
|
||||||
|
if (timeout > 0) {
|
||||||
|
timeout -= 1000;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(inf = ast_read(s->chan))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((inf->frametype == AST_FRAME_CONTROL) &&
|
||||||
|
(inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
|
||||||
|
(inf->datalen == sizeof(t38_parameters))) {
|
||||||
|
struct ast_control_t38_parameters *parameters = inf->data.ptr;
|
||||||
|
|
||||||
|
switch (parameters->request_response) {
|
||||||
|
case AST_T38_NEGOTIATED:
|
||||||
|
ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", s->chan->name);
|
||||||
|
ast_free(inf);
|
||||||
|
return 1;
|
||||||
|
case AST_T38_REFUSED:
|
||||||
|
ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ast_frfree(inf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ast_frfree(inf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if SPANDSP_RELEASE_DATE >= 20080725
|
#if SPANDSP_RELEASE_DATE >= 20080725
|
||||||
/* for spandsp shaphots 0.0.6 and higher */
|
/* for spandsp shaphots 0.0.6 and higher */
|
||||||
@@ -415,18 +481,6 @@ static int transmit_audio(fax_session *s)
|
|||||||
|
|
||||||
t30_set_phase_e_handler(t30state, phase_e_handler, s);
|
t30_set_phase_e_handler(t30state, phase_e_handler, s);
|
||||||
|
|
||||||
if (s->t38state == T38_STATE_UNAVAILABLE) {
|
|
||||||
ast_debug(1, "T38 is unavailable on %s\n", s->chan->name);
|
|
||||||
} else if (!s->direction) {
|
|
||||||
/* We are receiving side and this means we are the side which should
|
|
||||||
request T38 when the fax is detected. Use DSP to detect fax tone */
|
|
||||||
ast_debug(1, "Setting up CNG detection on %s\n", s->chan->name);
|
|
||||||
dsp = ast_dsp_new();
|
|
||||||
ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
|
|
||||||
ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG);
|
|
||||||
detect_tone = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = state_change = ast_tvnow();
|
start = state_change = ast_tvnow();
|
||||||
|
|
||||||
ast_activate_generator(s->chan, &generator, &fax);
|
ast_activate_generator(s->chan, &generator, &fax);
|
||||||
@@ -461,32 +515,6 @@ static int transmit_audio(fax_session *s)
|
|||||||
|
|
||||||
ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
|
ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
|
||||||
|
|
||||||
/* Detect fax tone */
|
|
||||||
if (detect_tone && inf->frametype == AST_FRAME_VOICE) {
|
|
||||||
/* Duplicate frame because ast_dsp_process may free the frame passed */
|
|
||||||
fr = ast_frdup(inf);
|
|
||||||
|
|
||||||
/* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */
|
|
||||||
fr = ast_dsp_process(NULL, dsp, fr);
|
|
||||||
if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') {
|
|
||||||
struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_NEGOTIATE,
|
|
||||||
.version = 0,
|
|
||||||
.max_ifp = 800,
|
|
||||||
.rate = AST_T38_RATE_9600,
|
|
||||||
.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
|
|
||||||
.fill_bit_removal = 1,
|
|
||||||
.transcoding_mmr = 1,
|
|
||||||
.transcoding_jbig = 1,
|
|
||||||
};
|
|
||||||
ast_debug(1, "Fax tone detected. Requesting T38\n");
|
|
||||||
ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters));
|
|
||||||
detect_tone = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_frfree(fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Check the frame type. Format also must be checked because there is a chance
|
/* Check the frame type. Format also must be checked because there is a chance
|
||||||
that a frame in old format was already queued before we set channel format
|
that a frame in old format was already queued before we set channel format
|
||||||
to slinear so it will still be received by ast_read */
|
to slinear so it will still be received by ast_read */
|
||||||
@@ -501,8 +529,10 @@ static int transmit_audio(fax_session *s)
|
|||||||
state_change = ast_tvnow();
|
state_change = ast_tvnow();
|
||||||
last_state = t30state->state;
|
last_state = t30state->state;
|
||||||
}
|
}
|
||||||
} else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
|
} else if ((inf->frametype == AST_FRAME_CONTROL) &&
|
||||||
|
(inf->subclass == AST_CONTROL_T38_PARAMETERS)) {
|
||||||
struct ast_control_t38_parameters *parameters = inf->data.ptr;
|
struct ast_control_t38_parameters *parameters = inf->data.ptr;
|
||||||
|
|
||||||
if (parameters->request_response == AST_T38_NEGOTIATED) {
|
if (parameters->request_response == AST_T38_NEGOTIATED) {
|
||||||
/* T38 switchover completed */
|
/* T38 switchover completed */
|
||||||
s->t38parameters = *parameters;
|
s->t38parameters = *parameters;
|
||||||
@@ -510,20 +540,13 @@ static int transmit_audio(fax_session *s)
|
|||||||
res = 1;
|
res = 1;
|
||||||
break;
|
break;
|
||||||
} else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
|
} else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
|
||||||
struct ast_control_t38_parameters our_parameters = { .request_response = AST_T38_NEGOTIATED,
|
t38_parameters.request_response = AST_T38_NEGOTIATED;
|
||||||
.version = 0,
|
|
||||||
.max_ifp = 800,
|
|
||||||
.rate = AST_T38_RATE_9600,
|
|
||||||
.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
|
|
||||||
.fill_bit_removal = 1,
|
|
||||||
.transcoding_mmr = 1,
|
|
||||||
.transcoding_jbig = 1,
|
|
||||||
};
|
|
||||||
ast_debug(1, "T38 request received, accepting\n");
|
ast_debug(1, "T38 request received, accepting\n");
|
||||||
/* Complete T38 switchover */
|
/* Complete T38 switchover */
|
||||||
ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters));
|
ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
|
||||||
/* Do not break audio loop, wait until channel driver finally acks switchover
|
/* Do not break audio loop, wait until channel driver finally acks switchover
|
||||||
with AST_T38_NEGOTIATED */
|
* with AST_T38_NEGOTIATED
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,9 +558,6 @@ static int transmit_audio(fax_session *s)
|
|||||||
if (inf)
|
if (inf)
|
||||||
ast_frfree(inf);
|
ast_frfree(inf);
|
||||||
|
|
||||||
if (dsp)
|
|
||||||
ast_dsp_free(dsp);
|
|
||||||
|
|
||||||
ast_deactivate_generator(s->chan);
|
ast_deactivate_generator(s->chan);
|
||||||
|
|
||||||
/* If we are switching to T38, remove phase E handler. Otherwise it will be executed
|
/* If we are switching to T38, remove phase E handler. Otherwise it will be executed
|
||||||
|
|||||||
Reference in New Issue
Block a user