[svn-commits] file: branch 1.6.1 r184950 - in /branches/1.6.1: ./ channels/chan_sip.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Mar 30 09:41:23 CDT 2009


Author: file
Date: Mon Mar 30 09:41:13 2009
New Revision: 184950

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=184950
Log:
Merged revisions 184948 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/trunk

................
  r184948 | file | 2009-03-30 11:37:47 -0300 (Mon, 30 Mar 2009) | 21 lines
  
  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/
  ........
................

Modified:
    branches/1.6.1/   (props changed)
    branches/1.6.1/channels/chan_sip.c

Propchange: branches/1.6.1/
------------------------------------------------------------------------------
Binary property 'trunk-merged' - no diff available.

Modified: branches/1.6.1/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.1/channels/chan_sip.c?view=diff&rev=184950&r1=184949&r2=184950
==============================================================================
--- branches/1.6.1/channels/chan_sip.c (original)
+++ branches/1.6.1/channels/chan_sip.c Mon Mar 30 09:41:13 2009
@@ -1143,7 +1143,6 @@
 /*! \brief T38 States for a call */
 enum t38state {
 	T38_DISABLED = 0,                /*!< Not enabled */
-	T38_LOCAL_DIRECT,                /*!< Offered from local */
 	T38_LOCAL_REINVITE,              /*!< Offered from local - REINVITE */
 	T38_PEER_DIRECT,                 /*!< Offered from peer */
 	T38_PEER_REINVITE,               /*!< Offered from peer - REINVITE */
@@ -1157,6 +1156,7 @@
 	int peercapability;		/*!< Peers T38 capability */
 	int jointcapability;		/*!< Supported T38 capability at both ends */
 	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 */
@@ -1954,7 +1954,7 @@
 static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
 				struct ast_str **m_buf, struct ast_str **a_buf,
 				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 stop_media_flows(struct sip_pvt *p);
 
@@ -3509,7 +3509,6 @@
 		/* 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)) {
 			switch (p->t38.state) {
-			case T38_LOCAL_DIRECT:
 			case T38_LOCAL_REINVITE:
 			case T38_PEER_DIRECT:
 			case T38_PEER_REINVITE:
@@ -4239,6 +4238,10 @@
 	if (old == state)
 		return;
 
+	if (state == T38_PEER_DIRECT) {
+		p->t38.direct = 1;
+	}
+
 	p->t38.state = state;
 	ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
 
@@ -4591,9 +4594,6 @@
 		} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
 			/* We're replacing a call. */
 			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);
 		}
 	}
 
@@ -5343,13 +5343,10 @@
 		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);
-			res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE);
-			ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
-		}
+		}
+		ast_rtp_new_source(p->rtp);
+		res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE);
+		ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
 	}
 	sip_pvt_unlock(p);
 	return res;
@@ -5386,9 +5383,13 @@
 					p->invitestate = INV_EARLY_MEDIA;
 					transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
 					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);
+					res = ast_rtp_write(p->rtp, frame);
 				}
-				p->lastrtptx = time(NULL);
-				res = ast_rtp_write(p->rtp, frame);
 			}
 			sip_pvt_unlock(p);
 		}
@@ -5443,8 +5444,16 @@
 				we simply forget the frames if we get modem frames before the bridge is up.
 				Fax will re-transmit.
 			*/
-			if (p->udptl && ast->_state == AST_STATE_UP) 
-				res = ast_udptl_write(p->udptl, frame);
+			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);
+				}
+			}
 			sip_pvt_unlock(p);
 		}
 		break;
@@ -5862,10 +5871,6 @@
 		pbx_builtin_setvar_helper(tmp, "SIPCALLID", i->callid);
 	if (i->rtp)
 		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 */
 	for (v = i->chanvars ; v ; v = v->next)
@@ -7441,7 +7446,7 @@
 
 		/* Remote party offers T38, we need to update state */
 		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);
 		} else if (t38action == SDP_T38_INITIATE) {
 			if (p->owner && p->lastinvite) {
@@ -8527,92 +8532,6 @@
 	}
 }
 
-/*! \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 */
 static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
 				struct ast_str **m_buf, struct ast_str **a_buf,
@@ -8682,7 +8601,7 @@
     is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions 
     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 alreadysent = 0;
@@ -8691,8 +8610,10 @@
 	struct sockaddr_in vsin;
 	struct sockaddr_in tsin;
 	struct sockaddr_in dest;
+	struct sockaddr_in udptlsin;
 	struct sockaddr_in vdest = { 0, };
 	struct sockaddr_in tdest = { 0, };
+	struct sockaddr_in udptldest = { 0, };
 
 	/* SDP fields */
 	char *version = 	"v=0\r\n";		/* Protocol version */
@@ -8701,16 +8622,18 @@
 	char connection[256];				/* Connection data */
 	char *session_time = "t=0 0\r\n"; 			/* Time the session is active */
 	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_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_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_video = ast_str_alloca(1024); /* Attributes for video */
 	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 capability;
+	int capability = 0;
 	int needaudio = FALSE;
 	int needvideo = FALSE;
 	int needtext = FALSE;
@@ -8741,183 +8664,232 @@
 			p->sessionversion++;
 	}
 
-	capability = p->jointcapability;
-
-	/* XXX note, Video and Text are negated - 'true' means 'no' */
- 	ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), 
- 		p->novideo ? "True" : "False", p->notext ? "True" : "False");
-	ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
+	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;
+
+		/* XXX note, Video and Text are negated - 'true' means 'no' */
+		ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), 
+			  p->novideo ? "True" : "False", p->notext ? "True" : "False");
+		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);
-	}
+		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 */
-	if (capability & AST_FORMAT_AUDIO_MASK)
-		needaudio = TRUE;
-
-	/* Check if we need video in this call */
-	if ((capability & AST_FORMAT_VIDEO_MASK) && !p->novideo) {
-		if (p->vrtp) {
-			needvideo = TRUE;
-			ast_debug(2, "This call needs video offers!\n");
-		} else
-			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);
+		/* Check if we need audio */
+		if (capability & AST_FORMAT_AUDIO_MASK)
+			needaudio = TRUE;
+
+		/* Check if we need video in this call */
+		if ((capability & AST_FORMAT_VIDEO_MASK) && !p->novideo) {
+			if (p->vrtp) {
+				needvideo = TRUE;
+				ast_debug(2, "This call needs video offers!\n");
+			} else
+				ast_debug(2, "This call needs video offers, but there's no video support enabled!\n");
+		}
+
+		if (debug) 
+			ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port));	
+
+		/* Ok, we need video. Let's add what we need for video and set codecs.
+		   Video is handled differently than audio since we can not transcode. */
+		if (needvideo) {
+			ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
+
+			/* Build max bitrate string */
+			if (p->maxcallbitrate)
+				snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
+			if (debug) 
+				ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port));	
+		}
+
+		/* Check if we need text in this call */
+		if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) {
+			if (sipdebug_text)
+				ast_verbose("We think we can do text\n");
+			if (p->trtp) {
+				if (sipdebug_text)
+					ast_verbose("And we have a text rtp object\n");
+				needtext = TRUE;
+				ast_debug(2, "This call needs text offers! \n");
+			} else
+				ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n");
+		}
 		
-	if (debug) 
-		ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port));	
-
-	/* Ok, we need video. Let's add what we need for video and set codecs.
-	   Video is handled differently than audio since we can not transcode. */
-	if (needvideo) {
-		ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
-
-		/* Build max bitrate string */
-		if (p->maxcallbitrate)
-			snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
-		if (debug) 
-			ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port));	
-	}
-
-	/* Check if we need text in this call */
-	if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) {
-		if (sipdebug_text)
-			ast_verbose("We think we can do text\n");
-		if (p->trtp) {
+		/* Ok, we need text. Let's add what we need for text and set codecs.
+		   Text is handled differently than audio since we can not transcode. */
+		if (needtext) {
 			if (sipdebug_text)
-				ast_verbose("And we have a text rtp object\n");
-			needtext = TRUE;
-			ast_debug(2, "This call needs text offers! \n");
-		} else
-			ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n");
-	}
-		
-	/* Ok, we need text. Let's add what we need for text and set codecs.
-	   Text is handled differently than audio since we can not transcode. */
-	if (needtext) {
-		if (sipdebug_text)
-			ast_verbose("Lets set up the text sdp\n");
-		/* Determine text destination */
-		if (p->tredirip.sin_addr.s_addr) {
-			tdest.sin_addr = p->tredirip.sin_addr;
-			tdest.sin_port = p->tredirip.sin_port;
+				ast_verbose("Lets set up the text sdp\n");
+			/* Determine text destination */
+			if (p->tredirip.sin_addr.s_addr) {
+				tdest.sin_addr = p->tredirip.sin_addr;
+				tdest.sin_port = p->tredirip.sin_port;
+			} else {
+				tdest.sin_addr = p->ourip.sin_addr;
+				tdest.sin_port = tsin.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 ? */
+				ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port));	
+
+		}
+
+		/* Start building generic SDP headers */
+
+		/* We break with the "recommendation" and send our IP, in order that our
+		   peer doesn't have to ast_gethostbyname() us */
+
+		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)
+			hold = "a=recvonly\r\n";
+		else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE)
+			hold = "a=inactive\r\n";
+		else
+			hold = "a=sendrecv\r\n";
+
+		/* Now, start adding audio codecs. These are added in this order:
+		   - First what was requested by the calling channel
+		   - Then preferences in order from sip.conf device config for this peer/user
+		   - Then other codecs in capabilities, including video
+		*/
+
+		/* Prefer the audio codec we were requested to use, first, no matter what 
+		   Note that p->prefcodec can include video codecs, so mask them out
+		*/
+		if (capability & p->prefcodec) {
+			int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK;
+
+			add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
+					 &m_audio, &a_audio,
+					 debug, &min_audio_packet_size);
+			alreadysent |= codec;
+		}
+
+		/* Start by sending our preferred audio/video codecs */
+		for (x = 0; x < 32; x++) {
+			int codec;
+
+			if (!(codec = ast_codec_pref_index(&p->prefs, x)))
+				break; 
+
+			if (!(capability & codec))
+				continue;
+
+			if (alreadysent & codec)
+			continue;
+
+			add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
+					 &m_audio, &a_audio,
+					 debug, &min_audio_packet_size);
+			alreadysent |= codec;
+		}
+
+		/* Now send any other common audio and video codecs, and non-codec formats: */
+		for (x = 1; x <= (needtext ? AST_FORMAT_TEXT_MASK : (needvideo ? AST_FORMAT_VIDEO_MASK : AST_FORMAT_AUDIO_MASK)); x <<= 1) {
+			if (!(capability & x))	/* Codec not requested */
+				continue;
+
+			if (alreadysent & x)	/* Already added to SDP */
+				continue;
+
+			if (x & AST_FORMAT_AUDIO_MASK)
+				add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x),
+						 &m_audio, &a_audio, debug, &min_audio_packet_size);
+			else if (x & AST_FORMAT_VIDEO_MASK) 
+				add_vcodec_to_sdp(p, x, 90000,
+						  &m_video, &a_video, debug, &min_video_packet_size);
+			else if (x & AST_FORMAT_TEXT_MASK)
+				add_tcodec_to_sdp(p, x, 1000,
+						  &m_text, &a_text, debug, &min_text_packet_size);
+		}
+
+		/* Now add DTMF RFC2833 telephony-event as a codec */
+		for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
+			if (!(p->jointnoncodeccapability & x))
+				continue;
+
+			add_noncodec_to_sdp(p, x, 8000, &m_audio, &a_audio, debug);
+		}
+
+		ast_debug(3, "-- Done with adding codecs to SDP\n");
+
+		if (!p->owner || !ast_internal_timing_enabled(p->owner))
+			ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n");
+
+		if (min_audio_packet_size)
+			ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size);
+
+		/* XXX don't think you can have ptime for video */
+		if (min_video_packet_size)
+			ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size);
+
+		/* XXX don't think you can have ptime for text */
+		if (min_text_packet_size)
+			ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
+	}
+
+	if (add_t38) {
+		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 {
-			tdest.sin_addr = p->ourip.sin_addr;
-			tdest.sin_port = tsin.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 ? */
-			ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port));	
-
-	}
-
-	/* Start building generic SDP headers */
-
-	/* We break with the "recommendation" and send our IP, in order that our
-	   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));
-
-	if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
-		hold = "a=recvonly\r\n";
-	else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE)
-		hold = "a=inactive\r\n";
-	else
-		hold = "a=sendrecv\r\n";
-
-	/* Now, start adding audio codecs. These are added in this order:
-		- First what was requested by the calling channel
-		- Then preferences in order from sip.conf device config for this peer/user
-		- Then other codecs in capabilities, including video
-	*/
-
-	/* Prefer the audio codec we were requested to use, first, no matter what 
-		Note that p->prefcodec can include video codecs, so mask them out
-	 */
-	if (capability & p->prefcodec) {
-		int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK;
-
-		add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
-				 &m_audio, &a_audio,
-				 debug, &min_audio_packet_size);
-		alreadysent |= codec;
-	}
-
-	/* Start by sending our preferred audio/video codecs */
-	for (x = 0; x < 32; x++) {
-		int codec;
-
-		if (!(codec = ast_codec_pref_index(&p->prefs, x)))
-			break; 
-
-		if (!(capability & codec))
-			continue;
-
-		if (alreadysent & codec)
-			continue;
-
-		add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
-				 &m_audio, &a_audio,
-				 debug, &min_audio_packet_size);
-		alreadysent |= codec;
-	}
-
-	/* Now send any other common audio and video codecs, and non-codec formats: */
-	for (x = 1; x <= (needtext ? AST_FORMAT_TEXT_MASK : (needvideo ? AST_FORMAT_VIDEO_MASK : AST_FORMAT_AUDIO_MASK)); x <<= 1) {
-		if (!(capability & x))	/* Codec not requested */
-			continue;
-
-		if (alreadysent & x)	/* Already added to SDP */
-			continue;
-
-		if (x & AST_FORMAT_AUDIO_MASK)
-			add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x),
-				 &m_audio, &a_audio, debug, &min_audio_packet_size);
-		else if (x & AST_FORMAT_VIDEO_MASK) 
-			add_vcodec_to_sdp(p, x, 90000,
-				 &m_video, &a_video, debug, &min_video_packet_size);
-		else if (x & AST_FORMAT_TEXT_MASK)
-			add_tcodec_to_sdp(p, x, 1000,
-				 &m_text, &a_text, debug, &min_text_packet_size);
-	}
-
-	/* Now add DTMF RFC2833 telephony-event as a codec */
-	for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
-		if (!(p->jointnoncodeccapability & x))
-			continue;
-
-		add_noncodec_to_sdp(p, x, 8000, &m_audio, &a_audio, debug);
-	}
-
-	ast_debug(3, "-- Done with adding codecs to SDP\n");
-
-	if (!p->owner || !ast_internal_timing_enabled(p->owner))
-		ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n");
-
-	if (min_audio_packet_size)
-		ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size);
-
- 	/* XXX don't think you can have ptime for video */
-	if (min_video_packet_size)
-		ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size);
-
- 	/* XXX don't think you can have ptime for text */
- 	if (min_text_packet_size)
- 		ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
- 
+			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\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");
+	}
+
 	if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->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)
+	    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)
 		ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
 
 	if (needaudio)
@@ -8935,6 +8907,8 @@
  		len += m_video->used + a_video->used + strlen(bandwidth) + strlen(hold);
  	if (needtext) /* only if text response is appropriate */
  		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_contentLength(resp, len);
@@ -8960,6 +8934,10 @@
 		add_line(resp, a_text->str);
 		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 */
 	p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
@@ -8982,7 +8960,7 @@
 	respprep(&resp, p, msg, req);
 	if (p->udptl) {
 		ast_udptl_offered_from_local(p->udptl, 0);
-		add_t38_sdp(&resp, p);
+		add_sdp(&resp, p, 0, 0, 1);
 	} else 
 		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)
@@ -9031,8 +9009,12 @@
 			ast_debug(1, "Setting framing from config on incoming call\n");
 			ast_rtp_codec_setpref(p->rtp, &p->prefs);
 		}
-		try_suggested_sip_codec(p);	
-		add_sdp(&resp, p, oldsdp);
+		try_suggested_sip_codec(p);
+		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 
 		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)
@@ -9114,9 +9096,9 @@
 	if (p->do_history)
 		append_history(p, "ReInv", "Re-invite sent");
 	if (t38version)
-		add_t38_sdp(&req, p);
+		add_sdp(&req, p, oldsdp, FALSE, TRUE);
 	else
-		add_sdp(&req, p, oldsdp);
+		add_sdp(&req, p, oldsdp, TRUE, FALSE);
 
 	/* Use this as the basis */
 	initialize_initreq(p, &req);
@@ -9509,12 +9491,12 @@
 		ast_channel_unlock(chan);
 	}
 	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_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) 
-			add_sdp(&req, p, FALSE);
+			add_sdp(&req, p, FALSE, TRUE, FALSE);
 	} else {
 		if (!p->notify_headers) {
 			add_header_contentLength(&req, 0);
@@ -16033,20 +16015,6 @@
 
 			/* Trigger a reinvite back to audio */
 			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);
-			p->needdestroy = 1;
-			sip_alreadygone(p);
 		} else {
 			/* We can't set up this call, so give up */
 			if (p->owner && !req->ignore)




More information about the svn-commits mailing list