Merged revisions 203699 via svnmerge from

https://origsvn.digium.com/svn/asterisk/trunk

........
  r203699 | file | 2009-06-26 16:27:24 -0300 (Fri, 26 Jun 2009) | 2 lines
  
  Improve T.38 negotiation by exchanging session parameters between application and channel.
........


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.6.0@203701 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.0
Joshua Colp 16 years ago
parent 7f0af9cf29
commit fc33f7b57e

@ -116,7 +116,7 @@ typedef struct {
int direction; /* Fax direction: 0 - receiving, 1 - sending */ int direction; /* Fax direction: 0 - receiving, 1 - sending */
int caller_mode; int caller_mode;
char *file_name; char *file_name;
struct ast_control_t38_parameters t38parameters;
volatile int finished; volatile int finished;
} fax_session; } fax_session;
@ -327,7 +327,6 @@ static int transmit_audio(fax_session *s)
struct ast_frame *fr; 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_control_t38 t38control;
#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 */
@ -411,9 +410,16 @@ static int transmit_audio(fax_session *s)
/* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */ /* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */
fr = ast_dsp_process(NULL, dsp, fr); fr = ast_dsp_process(NULL, dsp, fr);
if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') { 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_datagram = 400,
.rate = AST_T38_RATE_9600,
.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF,
.fill_bit_removal = 1,
.transcoding_mmr = 1,
};
ast_debug(1, "Fax tone detected. Requesting T38\n"); ast_debug(1, "Fax tone detected. Requesting T38\n");
t38control = AST_T38_REQUEST_NEGOTIATE; ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
ast_indicate_data(s->chan, AST_CONTROL_T38, &t38control, sizeof(t38control));
detect_tone = 0; detect_tone = 0;
} }
@ -438,11 +444,11 @@ 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 && } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
inf->datalen == sizeof(enum ast_control_t38)) { struct ast_control_t38_parameters *parameters = inf->data;
t38control =*((enum ast_control_t38 *) inf->data); if (parameters->request_response == AST_T38_NEGOTIATED) {
if (t38control == AST_T38_NEGOTIATED) {
/* T38 switchover completed */ /* T38 switchover completed */
s->t38parameters = *parameters;
ast_debug(1, "T38 negotiated, finishing audio loop\n"); ast_debug(1, "T38 negotiated, finishing audio loop\n");
res = 1; res = 1;
break; break;
@ -503,8 +509,6 @@ static int transmit_t38(fax_session *s)
struct ast_frame *inf = NULL; struct ast_frame *inf = NULL;
int last_state = 0; int last_state = 0;
struct timeval now, start, state_change, last_frame; struct timeval now, start, state_change, last_frame;
enum ast_control_t38 t38control;
t30_state_t *t30state; t30_state_t *t30state;
t38_core_state_t *t38state; t38_core_state_t *t38state;
@ -525,6 +529,17 @@ static int transmit_t38(fax_session *s)
return -1; return -1;
} }
t38_set_max_datagram_size(t38state, s->t38parameters.max_datagram);
if (s->t38parameters.fill_bit_removal) {
t38_set_fill_bit_removal(t38state, TRUE);
}
if (s->t38parameters.transcoding_mmr) {
t38_set_mmr_transcoding(t38state, TRUE);
} else if (s->t38parameters.transcoding_jbig) {
t38_set_jbig_transcoding(t38state, TRUE);
}
/* Setup logging */ /* Setup logging */
set_logging(&t38.logging); set_logging(&t38.logging);
set_logging(&t30state->logging); set_logging(&t30state->logging);
@ -568,12 +583,9 @@ static int transmit_t38(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 && } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
inf->datalen == sizeof(enum ast_control_t38)) { struct ast_control_t38_parameters *parameters = inf->data;
if (parameters->request_response == AST_T38_TERMINATED || parameters->request_response == AST_T38_REFUSED) {
t38control = *((enum ast_control_t38 *) inf->data);
if (t38control == AST_T38_TERMINATED || t38control == AST_T38_REFUSED) {
ast_debug(1, "T38 down, terminating\n"); ast_debug(1, "T38 down, terminating\n");
res = -1; res = -1;
break; break;
@ -664,7 +676,7 @@ static int sndfax_exec(struct ast_channel *chan, void *data)
{ {
int res = 0; int res = 0;
char *parse; char *parse;
fax_session session; fax_session session = { 0, };
AST_DECLARE_APP_ARGS(args, AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(file_name); AST_APP_ARG(file_name);

@ -984,9 +984,9 @@ struct sip_auth {
#define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */ #define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */
#define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< GDP: T38 Fax Passthrough Support */ #define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< GDP: T38 Fax Passthrough Support */
#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T38 Fax Passthrough Support */ #define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T38 Fax Passthrough Support (no error correction) */
#define SIP_PAGE2_T38SUPPORT_RTP (2 << 20) /*!< GDP: T38 Fax Passthrough Support (not implemented) */ #define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T38 Fax Passthrough Support (FEC error correction) */
#define SIP_PAGE2_T38SUPPORT_TCP (4 << 20) /*!< GDP: T38 Fax Passthrough Support (not implemented) */ #define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (4 << 20) /*!< GDP: T38 Fax Passthrough Support (redundancy error correction) */
#define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */ #define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */
#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */ #define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */
@ -1060,7 +1060,6 @@ static int sipdebug_text;
enum t38state { enum t38state {
T38_DISABLED = 0, /*!< Not enabled */ T38_DISABLED = 0, /*!< Not enabled */
T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */
T38_PEER_DIRECT, /*!< Offered from peer */
T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */
T38_ENABLED /*!< Negotiated (enabled) */ T38_ENABLED /*!< Negotiated (enabled) */
}; };
@ -1072,7 +1071,6 @@ struct t38properties {
int peercapability; /*!< Peers T38 capability */ int peercapability; /*!< Peers T38 capability */
int jointcapability; /*!< Supported T38 capability at both ends */ int jointcapability; /*!< Supported T38 capability at both ends */
enum t38state state; /*!< T.38 state */ enum t38state state; /*!< T.38 state */
unsigned int direct:1; /*!< Whether the T38 came from the initial invite or not */
}; };
/*! \brief Parameters to know status of transfer */ /*! \brief Parameters to know status of transfer */
@ -3291,7 +3289,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) { if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) {
switch (p->t38.state) { switch (p->t38.state) {
case T38_LOCAL_REINVITE: case T38_LOCAL_REINVITE:
case T38_PEER_DIRECT:
case T38_PEER_REINVITE: case T38_PEER_REINVITE:
state = T38_STATE_NEGOTIATING; state = T38_STATE_NEGOTIATING;
break; break;
@ -4032,21 +4029,62 @@ static void do_setnat(struct sip_pvt *p, int natflags)
} }
} }
/*! \brief Helper function which interprets T.38 capabilities and fills a parameters structure in */
static void fill_t38_parameters(int capabilities, struct ast_control_t38_parameters *parameters, struct sip_pvt *p)
{
if (capabilities & T38FAX_VERSION_0) {
parameters->version = 0;
} else if (capabilities & T38FAX_VERSION_1) {
parameters->version = 1;
}
if (capabilities & T38FAX_RATE_14400) {
parameters->rate = AST_T38_RATE_14400;
} else if (capabilities & T38FAX_RATE_12000) {
parameters->rate = AST_T38_RATE_12000;
} else if (capabilities & T38FAX_RATE_9600) {
parameters->rate = AST_T38_RATE_9600;
} else if (capabilities & T38FAX_RATE_7200) {
parameters->rate = AST_T38_RATE_7200;
} else if (capabilities & T38FAX_RATE_4800) {
parameters->rate = AST_T38_RATE_4800;
} else if (capabilities & T38FAX_RATE_2400) {
parameters->rate = AST_T38_RATE_2400;
}
if (capabilities & T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF) {
parameters->rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF;
} else if (capabilities & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) {
parameters->rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
}
if (capabilities & T38FAX_FILL_BIT_REMOVAL) {
parameters->fill_bit_removal = 1;
}
if (capabilities & T38FAX_TRANSCODING_MMR) {
parameters->transcoding_mmr = 1;
}
if (capabilities & T38FAX_TRANSCODING_JBIG) {
parameters->transcoding_jbig = 1;
}
parameters->max_datagram = ast_udptl_get_far_max_datagram(p->udptl);
}
/*! \brief Change the T38 state on a SIP dialog */ /*! \brief Change the T38 state on a SIP dialog */
static void change_t38_state(struct sip_pvt *p, int state) static void change_t38_state(struct sip_pvt *p, int state)
{ {
int old = p->t38.state; int old = p->t38.state;
struct ast_channel *chan = p->owner; struct ast_channel *chan = p->owner;
enum ast_control_t38 message = 0; enum ast_control_t38 message = 0;
struct ast_control_t38_parameters parameters = { 0, };
/* Don't bother changing if we are already in the state wanted */ /* Don't bother changing if we are already in the state wanted */
if (old == state) if (old == state)
return; return;
if (state == T38_PEER_DIRECT) {
p->t38.direct = 1;
}
p->t38.state = state; p->t38.state = state;
ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>"); ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
@ -4055,16 +4093,20 @@ static void change_t38_state(struct sip_pvt *p, int state)
return; return;
/* Given the state requested and old state determine what control frame we want to queue up */ /* Given the state requested and old state determine what control frame we want to queue up */
if (state == T38_PEER_REINVITE) if (state == T38_PEER_REINVITE) {
message = AST_T38_REQUEST_NEGOTIATE; message = parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
else if (state == T38_ENABLED) fill_t38_parameters(p->t38.peercapability, &parameters, p);
message = AST_T38_NEGOTIATED; } else if (state == T38_ENABLED) {
else if (state == T38_DISABLED && old == T38_ENABLED) message = parameters.request_response = AST_T38_NEGOTIATED;
message = AST_T38_TERMINATED; fill_t38_parameters(p->t38.jointcapability, &parameters, p);
} else if (state == T38_DISABLED && old == T38_ENABLED)
message = parameters.request_response = AST_T38_TERMINATED;
else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE) else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE)
message = AST_T38_REFUSED; message = parameters.request_response = AST_T38_REFUSED;
/* Woot we got a message, create a control frame and send it on! */ /* Woot we got a message, create a control frame and send it on! */
if (parameters.request_response)
ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
if (message) if (message)
ast_queue_control_data(chan, AST_CONTROL_T38, &message, sizeof(message)); ast_queue_control_data(chan, AST_CONTROL_T38, &message, sizeof(message));
} }
@ -4074,12 +4116,16 @@ static void set_t38_capabilities(struct sip_pvt *p)
{ {
p->t38.capability = global_t38_capability; p->t38.capability = global_t38_capability;
if (p->udptl) { if (p->udptl) {
if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC ) if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
p->t38.capability |= T38FAX_UDP_EC_FEC; ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY; p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE ) } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
p->t38.capability |= T38FAX_UDP_EC_FEC;
} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
p->t38.capability |= T38FAX_UDP_EC_NONE; p->t38.capability |= T38FAX_UDP_EC_NONE;
}
p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF; p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
} }
} }
@ -5162,9 +5208,6 @@ static int sip_answer(struct ast_channel *ast)
ast_setstate(ast, AST_STATE_UP); ast_setstate(ast, AST_STATE_UP);
ast_debug(1, "SIP answering channel: %s\n", ast->name); ast_debug(1, "SIP answering channel: %s\n", ast->name);
if (p->t38.state == T38_PEER_DIRECT) {
change_t38_state(p, T38_ENABLED);
}
ast_rtp_new_source(p->rtp); ast_rtp_new_source(p->rtp);
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE); res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE);
@ -5204,7 +5247,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
p->invitestate = INV_EARLY_MEDIA; p->invitestate = INV_EARLY_MEDIA;
transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
} else if (p->t38.state == T38_ENABLED && !p->t38.direct) { } else if (p->t38.state == T38_ENABLED) {
change_t38_state(p, T38_DISABLED); change_t38_state(p, T38_DISABLED);
transmit_reinvite_with_sdp(p, FALSE, FALSE); transmit_reinvite_with_sdp(p, FALSE, FALSE);
} else { } else {
@ -5390,6 +5433,90 @@ static int sip_transfer(struct ast_channel *ast, const char *dest)
return res; return res;
} }
/*! \brief Helper function which updates T.38 capability information and triggers a reinvite */
static void interpret_t38_parameters(struct sip_pvt *p, enum ast_control_t38 request_response, const struct ast_control_t38_parameters *parameters)
{
if (parameters) {
if (!parameters->version) {
p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_0;
} else if (parameters->version == 1) {
p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_1;
}
if (parameters->rate == AST_T38_RATE_14400) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
} else if (parameters->rate == AST_T38_RATE_12000) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
} else if (parameters->rate == AST_T38_RATE_9600) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
} else if (parameters->rate == AST_T38_RATE_7200) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
} else if (parameters->rate == AST_T38_RATE_4800) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
} else if (parameters->rate == AST_T38_RATE_2400) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_2400;
}
if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
} else if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_LOCAL_TCF) {
p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
}
if (parameters->fill_bit_removal) {
p->t38.capability = p->t38.jointcapability |= T38FAX_FILL_BIT_REMOVAL;
} else {
p->t38.capability = p->t38.jointcapability &= ~T38FAX_FILL_BIT_REMOVAL;
}
if (parameters->transcoding_mmr) {
p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_MMR;
} else {
p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_MMR;
}
if (parameters->transcoding_jbig) {
p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_JBIG;
} else {
p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_JBIG;
}
if (p->udptl && request_response == AST_T38_REQUEST_NEGOTIATE) {
ast_udptl_set_local_max_datagram(p->udptl, parameters->max_datagram ? parameters->max_datagram : 400);
}
}
switch (request_response) {
case AST_T38_NEGOTIATED:
case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */
if (p->t38.state == T38_PEER_REINVITE) {
AST_SCHED_DEL(sched, p->t38id);
change_t38_state(p, T38_ENABLED);
transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
} else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) {
change_t38_state(p, T38_LOCAL_REINVITE);
if (!p->pendinginvite) {
transmit_reinvite_with_sdp(p, TRUE, FALSE);
} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
}
}
break;
case AST_T38_TERMINATED:
case AST_T38_REFUSED:
case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */
if (p->t38.state == T38_PEER_REINVITE) {
AST_SCHED_DEL(sched, p->t38id);
change_t38_state(p, T38_DISABLED);
transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
} else if (p->t38.state == T38_ENABLED)
transmit_reinvite_with_sdp(p, FALSE, FALSE);
break;
default:
break;
}
}
/*! \brief Play indication to user /*! \brief Play indication to user
* With SIP a lot of indications is sent as messages, letting the device play * With SIP a lot of indications is sent as messages, letting the device play
the indication - busy signal, congestion etc the indication - busy signal, congestion etc
@ -5478,35 +5605,15 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
if (datalen != sizeof(enum ast_control_t38)) { if (datalen != sizeof(enum ast_control_t38)) {
ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. Expected %d, got %d\n", (int)sizeof(enum ast_control_t38), (int)datalen); ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. Expected %d, got %d\n", (int)sizeof(enum ast_control_t38), (int)datalen);
} else { } else {
switch (*((enum ast_control_t38 *) data)) { interpret_t38_parameters(p, *((enum ast_control_t38 *) data), NULL);
case AST_T38_NEGOTIATED:
case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */
if (p->t38.state == T38_PEER_REINVITE) {
AST_SCHED_DEL(sched, p->t38id);
change_t38_state(p, T38_ENABLED);
transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
} else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) {
change_t38_state(p, T38_LOCAL_REINVITE);
if (!p->pendinginvite) {
transmit_reinvite_with_sdp(p, TRUE, FALSE);
} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
}
} }
break; break;
case AST_T38_TERMINATED: case AST_CONTROL_T38_PARAMETERS:
case AST_T38_REFUSED: if (datalen != sizeof(struct ast_control_t38_parameters)) {
case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */ ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int)sizeof(struct ast_control_t38_parameters), (int)datalen);
if (p->t38.state == T38_PEER_REINVITE) { } else {
AST_SCHED_DEL(sched, p->t38id); const struct ast_control_t38_parameters *parameters = data;
change_t38_state(p, T38_DISABLED); interpret_t38_parameters(p, parameters->request_response, parameters);
transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
} else if (p->t38.state == T38_ENABLED)
transmit_reinvite_with_sdp(p, FALSE, FALSE);
break;
default:
break;
}
} }
break; break;
case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCUPDATE:
@ -5913,7 +6020,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) { if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
f = ast_dsp_process(p->owner, p->vad, f); f = ast_dsp_process(p->owner, p->vad, f);
if (f && f->frametype == AST_FRAME_DTMF) { if (f && f->frametype == AST_FRAME_DTMF) {
if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') { if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && f->subclass == 'f') {
ast_debug(1, "Fax CNG detected on %s\n", ast->name); ast_debug(1, "Fax CNG detected on %s\n", ast->name);
*faxdetect = 1; *faxdetect = 1;
} else { } else {
@ -5938,7 +6045,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
/* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */ /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */
/* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */ /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */
if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) { if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
if (!p->pendinginvite) { if (!p->pendinginvite) {
ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n", ast->name); ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n", ast->name);
@ -7226,7 +7333,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
found = 1; found = 1;
ast_debug(3, "FaxMaxDatagram: %d\n", x); ast_debug(3, "FaxMaxDatagram: %d\n", x);
ast_udptl_set_far_max_datagram(p->udptl, x); ast_udptl_set_far_max_datagram(p->udptl, x);
if (!ast_udptl_get_local_max_datagram(p->udptl)) {
ast_udptl_set_local_max_datagram(p->udptl, x); ast_udptl_set_local_max_datagram(p->udptl, x);
}
} else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) { } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) {
found = 1; found = 1;
if(sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1) { if(sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1) {
@ -7298,8 +7407,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
} else if (t38action == SDP_T38_INITIATE) { } else if (t38action == SDP_T38_INITIATE) {
if (p->owner && p->lastinvite) { if (p->owner && p->lastinvite) {
change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */ change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
} else {
change_t38_state(p, T38_PEER_DIRECT); /* T38 Offered directly from peer in first invite */
} }
} }
} else { } else {
@ -8522,13 +8629,6 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
p->novideo ? "True" : "False", p->notext ? "True" : "False"); p->novideo ? "True" : "False", p->notext ? "True" : "False");
ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) {
ast_str_append(&m_audio, 0, " %d", 191);
ast_str_append(&a_audio, 0, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000);
}
#endif
/* Check if we need audio */ /* Check if we need audio */
if (capability & AST_FORMAT_AUDIO_MASK) if (capability & AST_FORMAT_AUDIO_MASK)
needaudio = TRUE; needaudio = TRUE;
@ -8728,8 +8828,10 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
x = ast_udptl_get_local_max_datagram(p->udptl); x = ast_udptl_get_local_max_datagram(p->udptl);
ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n", x); ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n", x);
ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", x); ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", x);
if (p->t38.jointcapability != T38FAX_UDP_EC_NONE) if (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY)
ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC"); ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n");
else if (p->t38.jointcapability & T38FAX_UDP_EC_FEC)
ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPFEC\r\n");
} }
if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 || if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
@ -8848,7 +8950,7 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
ast_rtp_codec_setpref(p->rtp, &p->prefs); ast_rtp_codec_setpref(p->rtp, &p->prefs);
} }
try_suggested_sip_codec(p); try_suggested_sip_codec(p);
if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) { if (p->t38.state == T38_ENABLED) {
add_sdp(&resp, p, oldsdp, TRUE, TRUE); add_sdp(&resp, p, oldsdp, TRUE, TRUE);
} else { } else {
add_sdp(&resp, p, oldsdp, TRUE, FALSE); add_sdp(&resp, p, oldsdp, TRUE, FALSE);
@ -12078,6 +12180,11 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) {
set_t38_capabilities(p);
p->t38.jointcapability = p->t38.capability;
}
/* Copy SIP extensions profile to peer */ /* Copy SIP extensions profile to peer */
/* XXX is this correct before a successful auth ? */ /* XXX is this correct before a successful auth ? */
if (p->sipoptions) if (p->sipoptions)
@ -13332,11 +13439,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE))); ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
ast_cli(fd, " Nat : %s\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT))); ast_cli(fd, " Nat : %s\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT)));
ast_cli(fd, " ACL : %s\n", cli_yesno(peer->ha != NULL)); ast_cli(fd, " ACL : %s\n", cli_yesno(peer->ha != NULL));
ast_cli(fd, " T38 pt UDPTL : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL))); ast_cli(fd, " T38 pt UDPTL : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
ast_cli(fd, " T38 pt RTP : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_RTP)));
ast_cli(fd, " T38 pt TCP : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_TCP)));
#endif
ast_cli(fd, " CanReinvite : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE))); ast_cli(fd, " CanReinvite : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE)));
ast_cli(fd, " PromiscRedir : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR))); ast_cli(fd, " PromiscRedir : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)));
ast_cli(fd, " User=Phone : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE))); ast_cli(fd, " User=Phone : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)));
@ -13722,11 +13825,8 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli(a->fd, " Record SIP history: %s\n", recordhistory ? "On" : "Off"); ast_cli(a->fd, " Record SIP history: %s\n", recordhistory ? "On" : "Off");
ast_cli(a->fd, " Call Events: %s\n", global_callevents ? "On" : "Off"); ast_cli(a->fd, " Call Events: %s\n", global_callevents ? "On" : "Off");
ast_cli(a->fd, " T38 fax pt UDPTL: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_UDPTL))); ast_cli(a->fd, " T38 fax pt UDPTL: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT)));
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
ast_cli(a->fd, " T38 fax pt RTP: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_RTP)));
ast_cli(a->fd, " T38 fax pt TCP: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_TCP)));
#endif
if (!realtimepeers && !realtimeusers && !realtimeregs) if (!realtimepeers && !realtimeusers && !realtimeregs)
ast_cli(a->fd, " SIP realtime: Disabled\n" ); ast_cli(a->fd, " SIP realtime: Disabled\n" );
else else
@ -17529,6 +17629,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && !p->udptl && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) { if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && !p->udptl && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) {
set_t38_capabilities(p); set_t38_capabilities(p);
p->t38.jointcapability = p->t38.capability; p->t38.jointcapability = p->t38.capability;
set_t38_capabilities(p);
p->t38.jointcapability = p->t38.capability;
} }
/* We have a succesful authentication, process the SDP portion if there is one */ /* We have a succesful authentication, process the SDP portion if there is one */
@ -20640,16 +20742,24 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
ast_set_flag(&mask[1], SIP_PAGE2_IGNORESDPVERSION); ast_set_flag(&mask[1], SIP_PAGE2_IGNORESDPVERSION);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_IGNORESDPVERSION); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_IGNORESDPVERSION);
} else if (!strcasecmp(v->name, "t38pt_udptl")) { } else if (!strcasecmp(v->name, "t38pt_udptl")) {
ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL); char buf[16], *word, *next = buf;
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL);
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT);
} else if (!strcasecmp(v->name, "t38pt_rtp")) {
ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP); ast_copy_string(buf, v->value, sizeof(buf));
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP);
} else if (!strcasecmp(v->name, "t38pt_tcp")) { while ((word = strsep(&next, ","))) {
ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP); if (ast_true(word) || !strcasecmp(word, "fec")) {
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP); ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
#endif ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_FEC);
} else if (!strcasecmp(word, "redundancy")) {
ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY);
} else if (!strcasecmp(word, "none")) {
ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
}
}
} else if (!strcasecmp(v->name, "rfc2833compensate")) { } else if (!strcasecmp(v->name, "rfc2833compensate")) {
ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE); ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);

@ -392,10 +392,12 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; both parties have T38 support enabled in their Asterisk configuration ; both parties have T38 support enabled in their Asterisk configuration
; This has to be enabled in the general section for all devices to work. You can then ; This has to be enabled in the general section for all devices to work. You can then
; disable it on a per device basis. ; disable it on a per device basis.
; T.38 faxing only works in SIP to SIP calls. It defaults to off.
; ;
; T.38 faxing only works in SIP to SIP calls, with no local or agent channel being used. ; t38pt_udptl = yes ; Enables T.38 with FEC error correction.
; ; t38pt_udptl = yes,fec ; Enables T.38 with FEC error correction.
; t38pt_udptl = yes ; Default false ; t38pt_udptl = yes,redundancy ; Enables T.38 with redundancy error correction.
; t38pt_udptl = yes,none ; Enables T.38 with no error correction.
; ;
;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------ ;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------
; Asterisk can register as a SIP user agent to a SIP proxy (provider) ; Asterisk can register as a SIP user agent to a SIP proxy (provider)

@ -313,6 +313,7 @@ enum ast_control_frame_type {
AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */ AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */
AST_CONTROL_T38 = 19, /*!< T38 state change request/notification */ AST_CONTROL_T38 = 19, /*!< T38 state change request/notification */
AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */ AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */
AST_CONTROL_T38_PARAMETERS = 24, /*!< T38 state change request/notification with parameters */
}; };
enum ast_control_t38 { enum ast_control_t38 {
@ -323,6 +324,31 @@ enum ast_control_t38 {
AST_T38_REFUSED /*!< T38 refused for some reason (usually rejected by remote end) */ AST_T38_REFUSED /*!< T38 refused for some reason (usually rejected by remote end) */
}; };
enum ast_control_t38_rate {
AST_T38_RATE_2400 = 0,
AST_T38_RATE_4800,
AST_T38_RATE_7200,
AST_T38_RATE_9600,
AST_T38_RATE_12000,
AST_T38_RATE_14400,
};
enum ast_control_t38_rate_management {
AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF = 0,
AST_T38_RATE_MANAGEMENT_LOCAL_TCF,
};
struct ast_control_t38_parameters {
enum ast_control_t38 request_response; /*!< Request or response of the T38 control frame */
unsigned int version; /*!< Supported T.38 version */
unsigned int max_datagram; /*!< Maximum datagram size supported */
enum ast_control_t38_rate rate; /*!< Maximum fax rate supported */
enum ast_control_t38_rate_management rate_management; /*!< Rate management setting */
unsigned int fill_bit_removal:1; /*!< Set if fill bit removal should be used */
unsigned int transcoding_mmr:1; /*!< Set if MMR transcoding should be used */
unsigned int transcoding_jbig:1; /*!< Set if JBIG transcoding should be used */
};
#define AST_SMOOTHER_FLAG_G729 (1 << 0) #define AST_SMOOTHER_FLAG_G729 (1 << 0)
#define AST_SMOOTHER_FLAG_BE (1 << 1) #define AST_SMOOTHER_FLAG_BE (1 << 1)

@ -3046,6 +3046,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
case AST_CONTROL_ANSWER: case AST_CONTROL_ANSWER:
case AST_CONTROL_HANGUP: case AST_CONTROL_HANGUP:
case AST_CONTROL_T38: case AST_CONTROL_T38:
case AST_CONTROL_T38_PARAMETERS:
return 0; return 0;
case AST_CONTROL_CONGESTION: case AST_CONTROL_CONGESTION:
@ -3142,6 +3143,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
case AST_CONTROL_HOLD: case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD: case AST_CONTROL_UNHOLD:
case AST_CONTROL_T38: case AST_CONTROL_T38:
case AST_CONTROL_T38_PARAMETERS:
/* Nothing left to do for these. */ /* Nothing left to do for these. */
res = 0; res = 0;
break; break;
@ -4752,6 +4754,7 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
case AST_CONTROL_VIDUPDATE: case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCUPDATE:
case AST_CONTROL_T38: case AST_CONTROL_T38:
case AST_CONTROL_T38_PARAMETERS:
ast_indicate_data(other, f->subclass, f->data, f->datalen); ast_indicate_data(other, f->subclass, f->data, f->datalen);
if (jb_in_use) { if (jb_in_use) {
ast_jb_empty_and_reset(c0, c1); ast_jb_empty_and_reset(c0, c1);

@ -851,6 +851,25 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
} }
snprintf(subclass, sizeof(subclass), "T38/%s", message); snprintf(subclass, sizeof(subclass), "T38/%s", message);
break; break;
case AST_CONTROL_T38_PARAMETERS:
if (f->datalen != sizeof(struct ast_control_t38_parameters *)) {
message = "Invalid";
} else {
struct ast_control_t38_parameters *parameters = f->data;
enum ast_control_t38 state = parameters->request_response;
if (state == AST_T38_REQUEST_NEGOTIATE)
message = "Negotiation Requested";
else if (state == AST_T38_REQUEST_TERMINATE)
message = "Negotiation Request Terminated";
else if (state == AST_T38_NEGOTIATED)
message = "Negotiated";
else if (state == AST_T38_TERMINATED)
message = "Terminated";
else if (state == AST_T38_REFUSED)
message = "Refused";
}
snprintf(subclass, sizeof(subclass), "T38_Parameters/%s", message);
break;
case -1: case -1:
strcpy(subclass, "Stop generators"); strcpy(subclass, "Stop generators");
break; break;

@ -3494,7 +3494,8 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct
(fr->subclass == AST_CONTROL_UNHOLD) || (fr->subclass == AST_CONTROL_UNHOLD) ||
(fr->subclass == AST_CONTROL_VIDUPDATE) || (fr->subclass == AST_CONTROL_VIDUPDATE) ||
(fr->subclass == AST_CONTROL_T38) || (fr->subclass == AST_CONTROL_T38) ||
(fr->subclass == AST_CONTROL_SRCUPDATE)) { (fr->subclass == AST_CONTROL_SRCUPDATE) ||
(fr->subclass == AST_CONTROL_T38_PARAMETERS)) {
if (fr->subclass == AST_CONTROL_HOLD) { if (fr->subclass == AST_CONTROL_HOLD) {
/* If we someone went on hold we want the other side to reinvite back to us */ /* If we someone went on hold we want the other side to reinvite back to us */
if (who == c0) if (who == c0)
@ -3734,7 +3735,8 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast
(fr->subclass == AST_CONTROL_UNHOLD) || (fr->subclass == AST_CONTROL_UNHOLD) ||
(fr->subclass == AST_CONTROL_VIDUPDATE) || (fr->subclass == AST_CONTROL_VIDUPDATE) ||
(fr->subclass == AST_CONTROL_T38) || (fr->subclass == AST_CONTROL_T38) ||
(fr->subclass == AST_CONTROL_SRCUPDATE)) { (fr->subclass == AST_CONTROL_SRCUPDATE) ||
(fr->subclass == AST_CONTROL_T38_PARAMETERS)) {
/* If we are going on hold, then break callback mode and P2P bridging */ /* If we are going on hold, then break callback mode and P2P bridging */
if (fr->subclass == AST_CONTROL_HOLD) { if (fr->subclass == AST_CONTROL_HOLD) {
if (p0_callback) if (p0_callback)

Loading…
Cancel
Save