Merged revisions 184947 via svnmerge from

https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r184947 | file | 2009-03-30 11:35:47 -0300 (Mon, 30 Mar 2009) | 14 lines
  
  Improve our handling of T38 in the initial INVITE from a device.
  
  We now answer with matching media streams to what is requested. If an INVITE
  is received with both a T38 and RTP media stream this means we answer with both.
  For any outgoing calls created as a result of this inbound one no T38 is requested
  in the initial INVITE. Instead if we start receiving udptl packets we trigger a
  reinvite on the outbound side.
  
  (closes issue #12437)
  Reported by: marsosa
  Tested by: pinga-fogo, okrief, file, afu
  
  Review: http://reviewboard.digium.com/r/208/
........


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@184948 65c4cc65-6c06-0410-ace0-fbb531ad65f3
certified/1.8.6
Joshua Colp 17 years ago
parent 3ec3742a97
commit aa056be678

@ -1456,7 +1456,6 @@ static int sipdebug_text;
/*! \brief T38 States for a call */ /*! \brief T38 States for a call */
enum t38state { enum t38state {
T38_DISABLED = 0, /*!< Not enabled */ T38_DISABLED = 0, /*!< Not enabled */
T38_LOCAL_DIRECT, /*!< Offered from local */
T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */
T38_PEER_DIRECT, /*!< Offered from peer */ T38_PEER_DIRECT, /*!< Offered from peer */
T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */
@ -1470,6 +1469,7 @@ 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 */
@ -2294,7 +2294,7 @@ static void add_codec_to_sdp(const struct sip_pvt *p, int codec,
static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
struct ast_str **m_buf, struct ast_str **a_buf, struct ast_str **m_buf, struct ast_str **a_buf,
int debug); int debug);
static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp); static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38);
static void do_setnat(struct sip_pvt *p, int natflags); static void do_setnat(struct sip_pvt *p, int natflags);
static void stop_media_flows(struct sip_pvt *p); static void stop_media_flows(struct sip_pvt *p);
@ -3897,7 +3897,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */ /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
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_DIRECT:
case T38_LOCAL_REINVITE: case T38_LOCAL_REINVITE:
case T38_PEER_DIRECT: case T38_PEER_DIRECT:
case T38_PEER_REINVITE: case T38_PEER_REINVITE:
@ -4619,6 +4618,10 @@ static void change_t38_state(struct sip_pvt *p, int state)
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>");
@ -4998,9 +5001,6 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
/* We're replacing a call. */ /* We're replacing a call. */
p->options->replaces = ast_var_value(current); p->options->replaces = ast_var_value(current);
} else if (!strcasecmp(ast_var_name(current), "T38CALL")) {
p->t38.state = T38_LOCAL_DIRECT;
ast_debug(1, "T38State change to %d on channel %s\n", p->t38.state, ast->name);
} }
} }
@ -5770,16 +5770,10 @@ 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);
res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
} else {
ast_rtp_new_source(p->rtp); ast_rtp_new_source(p->rtp);
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);
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
} }
}
sip_pvt_unlock(p); sip_pvt_unlock(p);
return res; return res;
} }
@ -5815,10 +5809,14 @@ 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) {
change_t38_state(p, T38_DISABLED);
transmit_reinvite_with_sdp(p, FALSE, FALSE);
} else {
p->lastrtptx = time(NULL); p->lastrtptx = time(NULL);
res = ast_rtp_write(p->rtp, frame); res = ast_rtp_write(p->rtp, frame);
} }
}
sip_pvt_unlock(p); sip_pvt_unlock(p);
} }
break; break;
@ -5872,8 +5870,16 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
we simply forget the frames if we get modem frames before the bridge is up. we simply forget the frames if we get modem frames before the bridge is up.
Fax will re-transmit. Fax will re-transmit.
*/ */
if (p->udptl && ast->_state == AST_STATE_UP) if (ast->_state == AST_STATE_UP) {
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state == T38_DISABLED) {
if (!p->pendinginvite) {
change_t38_state(p, T38_LOCAL_REINVITE);
transmit_reinvite_with_sdp(p, TRUE, FALSE);
}
} else if (p->udptl && p->t38.state == T38_ENABLED) {
res = ast_udptl_write(p->udptl, frame); res = ast_udptl_write(p->udptl, frame);
}
}
sip_pvt_unlock(p); sip_pvt_unlock(p);
} }
break; break;
@ -6295,10 +6301,6 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
if (i->rtp) if (i->rtp)
ast_jb_configure(tmp, &global_jbconf); ast_jb_configure(tmp, &global_jbconf);
/* If the INVITE contains T.38 SDP information set the proper channel variable so a created outgoing call will also have T.38 */
if (i->udptl && i->t38.state == T38_PEER_DIRECT)
pbx_builtin_setvar_helper(tmp, "_T38CALL", "1");
/* Set channel variables for this call from configuration */ /* Set channel variables for this call from configuration */
for (v = i->chanvars ; v ; v = v->next) { for (v = i->chanvars ; v ; v = v->next) {
char valuebuf[1024]; char valuebuf[1024];
@ -8010,7 +8012,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
/* Remote party offers T38, we need to update state */ /* Remote party offers T38, we need to update state */
if (t38action == SDP_T38_ACCEPT) { if (t38action == SDP_T38_ACCEPT) {
if (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE) if (p->t38.state == T38_LOCAL_REINVITE)
change_t38_state(p, T38_ENABLED); change_t38_state(p, T38_ENABLED);
} else if (t38action == SDP_T38_INITIATE) { } else if (t38action == SDP_T38_INITIATE) {
if (p->owner && p->lastinvite) { if (p->owner && p->lastinvite) {
@ -9127,92 +9129,6 @@ static int t38_get_rate(int t38cap)
} }
} }
/*! \brief Add T.38 Session Description Protocol message */
static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p)
{
int len = 0;
int x = 0;
struct sockaddr_in udptlsin;
struct ast_str *m_modem = ast_str_alloca(1024);
struct ast_str *a_modem = ast_str_alloca(1024);
struct sockaddr_in udptldest = { 0, };
int debug;
debug = sip_debug_test_pvt(p);
len = 0;
if (!p->udptl) {
ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n");
return -1;
}
if (!p->sessionid) {
p->sessionid = (int)ast_random();
p->sessionversion = p->sessionid;
} else
p->sessionversion++;
/* Our T.38 end is */
ast_udptl_get_us(p->udptl, &udptlsin);
/* Determine T.38 UDPTL destination */
if (p->udptlredirip.sin_addr.s_addr) {
udptldest.sin_port = p->udptlredirip.sin_port;
udptldest.sin_addr = p->udptlredirip.sin_addr;
} else {
udptldest.sin_addr = p->ourip.sin_addr;
udptldest.sin_port = udptlsin.sin_port;
}
if (debug)
ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port));
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
if (debug) {
ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
p->t38.capability,
p->t38.peercapability,
p->t38.jointcapability);
}
ast_str_append(&m_modem, 0, "v=0\r\n");
ast_str_append(&m_modem, 0, "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner , p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr));
ast_str_append(&m_modem, 0, "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
ast_str_append(&m_modem, 0, "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr));
ast_str_append(&m_modem, 0, "t=0 0\r\n");
ast_str_append(&m_modem, 0, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n");
if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n");
if ((x = t38_get_rate(p->t38.jointcapability)))
ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", x);
if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL)
ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n");
if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR)
ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n");
if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG)
ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n");
ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
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=T38FaxMaxDatagram:%d\r\n", x);
if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
len = m_modem->used + a_modem->used;
add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len);
add_line(resp, m_modem->str);
add_line(resp, a_modem->str);
/* Update lastrtprx when we send our SDP */
p->lastrtprx = p->lastrtptx = time(NULL);
return 0;
}
/*! \brief Add RFC 2833 DTMF offer to SDP */ /*! \brief Add RFC 2833 DTMF offer to SDP */
static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
struct ast_str **m_buf, struct ast_str **a_buf, struct ast_str **m_buf, struct ast_str **a_buf,
@ -9275,7 +9191,7 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo,
is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions
without modifying the media session in any way. without modifying the media session in any way.
*/ */
static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp) static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38)
{ {
int len = 0; int len = 0;
int alreadysent = 0; int alreadysent = 0;
@ -9284,8 +9200,10 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
struct sockaddr_in vsin; struct sockaddr_in vsin;
struct sockaddr_in tsin; struct sockaddr_in tsin;
struct sockaddr_in dest; struct sockaddr_in dest;
struct sockaddr_in udptlsin;
struct sockaddr_in vdest = { 0, }; struct sockaddr_in vdest = { 0, };
struct sockaddr_in tdest = { 0, }; struct sockaddr_in tdest = { 0, };
struct sockaddr_in udptldest = { 0, };
/* SDP fields */ /* SDP fields */
char *version = "v=0\r\n"; /* Protocol version */ char *version = "v=0\r\n"; /* Protocol version */
@ -9294,16 +9212,18 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
char connection[256]; /* Connection data */ char connection[256]; /* Connection data */
char *session_time = "t=0 0\r\n"; /* Time the session is active */ char *session_time = "t=0 0\r\n"; /* Time the session is active */
char bandwidth[256] = ""; /* Max bitrate */ char bandwidth[256] = ""; /* Max bitrate */
char *hold; char *hold = "";
struct ast_str *m_audio = ast_str_alloca(256); /* Media declaration line for audio */ struct ast_str *m_audio = ast_str_alloca(256); /* Media declaration line for audio */
struct ast_str *m_video = ast_str_alloca(256); /* Media declaration line for video */ struct ast_str *m_video = ast_str_alloca(256); /* Media declaration line for video */
struct ast_str *m_text = ast_str_alloca(256); /* Media declaration line for text */ struct ast_str *m_text = ast_str_alloca(256); /* Media declaration line for text */
struct ast_str *m_modem = ast_str_alloca(256); /* Media declaration line for modem */
struct ast_str *a_audio = ast_str_alloca(1024); /* Attributes for audio */ struct ast_str *a_audio = ast_str_alloca(1024); /* Attributes for audio */
struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */ struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */
struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */ struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */
struct ast_str *a_modem = ast_str_alloca(1024); /* Attributes for modem */
int x; int x;
int capability; int capability = 0;
int needaudio = FALSE; int needaudio = FALSE;
int needvideo = FALSE; int needvideo = FALSE;
int needtext = FALSE; int needtext = FALSE;
@ -9334,6 +9254,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
p->sessionversion++; p->sessionversion++;
} }
get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest);
snprintf(owner, sizeof(owner), "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner, p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
if (add_audio) {
capability = p->jointcapability; capability = p->jointcapability;
/* XXX note, Video and Text are negated - 'true' means 'no' */ /* XXX note, Video and Text are negated - 'true' means 'no' */
@ -9361,9 +9287,6 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
ast_debug(2, "This call needs video offers, but there's no video support enabled!\n"); ast_debug(2, "This call needs video offers, but there's no video support enabled!\n");
} }
/* Get our media addresses */
get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest);
if (debug) if (debug)
ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port));
@ -9406,7 +9329,6 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
tdest.sin_port = tsin.sin_port; tdest.sin_port = tsin.sin_port;
} }
ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port)); ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port));
if (debug) /* XXX should I use tdest below ? */ if (debug) /* XXX should I use tdest below ? */
ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port)); ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port));
@ -9417,8 +9339,6 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
/* We break with the "recommendation" and send our IP, in order that our /* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */ peer doesn't have to ast_gethostbyname() us */
snprintf(owner, sizeof(owner), "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner, p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port)); ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
@ -9505,6 +9425,55 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 || m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 ||
a_audio->len - a_audio->used < 2 || a_video->len - a_video->used < 2) a_audio->len - a_audio->used < 2 || a_video->len - a_video->used < 2)
ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n"); ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
}
if (add_t38) {
/* Our T.38 end is */
ast_udptl_get_us(p->udptl, &udptlsin);
/* Determine T.38 UDPTL destination */
if (p->udptlredirip.sin_addr.s_addr) {
udptldest.sin_port = p->udptlredirip.sin_port;
udptldest.sin_addr = p->udptlredirip.sin_addr;
} else {
udptldest.sin_addr = p->ourip.sin_addr;
udptldest.sin_port = udptlsin.sin_port;
}
if (debug)
ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port));
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
if (debug) {
ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
p->t38.capability,
p->t38.peercapability,
p->t38.jointcapability);
}
ast_str_append(&m_modem, 0, "m=image %d udptl t38", ntohs(udptldest.sin_port));
if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n");
if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n");
if ((x = t38_get_rate(p->t38.jointcapability)))
ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", x);
if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL)
ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n");
if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR)
ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n");
if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG)
ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n");
ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
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=T38FaxMaxDatagram:%d\r\n", x);
if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
}
if (needaudio) if (needaudio)
ast_str_append(&m_audio, 0, "\r\n"); ast_str_append(&m_audio, 0, "\r\n");
@ -9512,6 +9481,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
ast_str_append(&m_video, 0, "\r\n"); ast_str_append(&m_video, 0, "\r\n");
if (needtext) if (needtext)
ast_str_append(&m_text, 0, "\r\n"); ast_str_append(&m_text, 0, "\r\n");
if (add_t38)
ast_str_append(&m_modem, 0, "\r\n");
len = strlen(version) + strlen(subject) + strlen(owner) + len = strlen(version) + strlen(subject) + strlen(owner) +
strlen(connection) + strlen(session_time); strlen(connection) + strlen(session_time);
@ -9521,6 +9492,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
len += m_video->used + a_video->used + strlen(bandwidth) + strlen(hold); len += m_video->used + a_video->used + strlen(bandwidth) + strlen(hold);
if (needtext) /* only if text response is appropriate */ if (needtext) /* only if text response is appropriate */
len += m_text->used + a_text->used + strlen(hold); len += m_text->used + a_text->used + strlen(hold);
if (add_t38)
len += m_modem->used + a_modem->used;
add_header(resp, "Content-Type", "application/sdp"); add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len); add_header_contentLength(resp, len);
@ -9546,6 +9519,10 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
add_line(resp, a_text->str); add_line(resp, a_text->str);
add_line(resp, hold); /* Repeat hold for the text stream */ add_line(resp, hold); /* Repeat hold for the text stream */
} }
if (add_t38) {
add_line(resp, m_modem->str);
add_line(resp, a_modem->str);
}
/* Update lastrtprx when we send our SDP */ /* Update lastrtprx when we send our SDP */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
@ -9568,7 +9545,7 @@ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct s
respprep(&resp, p, msg, req); respprep(&resp, p, msg, req);
if (p->udptl) { if (p->udptl) {
ast_udptl_offered_from_local(p->udptl, 0); ast_udptl_offered_from_local(p->udptl, 0);
add_t38_sdp(&resp, p); add_sdp(&resp, p, 0, 0, 1);
} else } else
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid); ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
if (retrans && !p->pendinginvite) if (retrans && !p->pendinginvite)
@ -9618,7 +9595,11 @@ 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);
add_sdp(&resp, p, oldsdp); if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) {
add_sdp(&resp, p, oldsdp, TRUE, TRUE);
} else {
add_sdp(&resp, p, oldsdp, TRUE, FALSE);
}
} else } else
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid); ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
if (reliable && !p->pendinginvite) if (reliable && !p->pendinginvite)
@ -9700,9 +9681,9 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
if (p->do_history) if (p->do_history)
append_history(p, "ReInv", "Re-invite sent"); append_history(p, "ReInv", "Re-invite sent");
if (t38version) if (t38version)
add_t38_sdp(&req, p); add_sdp(&req, p, oldsdp, FALSE, TRUE);
else else
add_sdp(&req, p, oldsdp); add_sdp(&req, p, oldsdp, TRUE, FALSE);
/* Use this as the basis */ /* Use this as the basis */
initialize_initreq(p, &req); initialize_initreq(p, &req);
@ -10105,12 +10086,12 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
ast_channel_unlock(chan); ast_channel_unlock(chan);
} }
if (sdp) { if (sdp) {
if (p->udptl && (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE)) { if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
ast_udptl_offered_from_local(p->udptl, 1); ast_udptl_offered_from_local(p->udptl, 1);
ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
add_t38_sdp(&req, p); add_sdp(&req, p, FALSE, FALSE, TRUE);
} else if (p->rtp) } else if (p->rtp)
add_sdp(&req, p, FALSE); add_sdp(&req, p, FALSE, TRUE, FALSE);
} else { } else {
if (!p->notify_headers) { if (!p->notify_headers) {
add_header_contentLength(&req, 0); add_header_contentLength(&req, 0);
@ -16799,20 +16780,6 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
/* Trigger a reinvite back to audio */ /* Trigger a reinvite back to audio */
transmit_reinvite_with_sdp(p, FALSE, FALSE); transmit_reinvite_with_sdp(p, FALSE, FALSE);
} else if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
/* We tried to send T.38 out in an initial INVITE and the remote side rejected it,
right now we can't fall back to audio so totally abort.
*/
/* Try to reset RTP timers */
ast_rtp_set_rtptimers_onhold(p->rtp);
ast_log(LOG_ERROR, "Got error on T.38 initial invite. Bailing out.\n");
change_t38_state(p, T38_DISABLED);
/* The dialog is now terminated */
if (p->owner && !req->ignore)
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
pvt_set_needdestroy(p, "got error on T.38 initial invite");
sip_alreadygone(p);
} else { } else {
/* We can't set up this call, so give up */ /* We can't set up this call, so give up */
if (p->owner && !req->ignore) if (p->owner && !req->ignore)

Loading…
Cancel
Save