[asterisk-commits] branch oej/t38passthrough r11815 - in /team/oej/t38passthrough: ./ channels/

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sat Mar 4 07:50:10 MST 2006


Author: oej
Date: Sat Mar  4 08:50:07 2006
New Revision: 11815

URL: http://svn.digium.com/view/asterisk?rev=11815&view=rev
Log:
Adding patch trunk-t38-pre3.diff (mattf) from issue 5090 (steveu)

Modified:
    team/oej/t38passthrough/   (props changed)
    team/oej/t38passthrough/channels/chan_sip.c

Propchange: team/oej/t38passthrough/
------------------------------------------------------------------------------
    svnmerge-integrated = /trunk:1-11812

Modified: team/oej/t38passthrough/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/team/oej/t38passthrough/channels/chan_sip.c?rev=11815&r1=11814&r2=11815&view=diff
==============================================================================
--- team/oej/t38passthrough/channels/chan_sip.c (original)
+++ team/oej/t38passthrough/channels/chan_sip.c Sat Mar  4 08:50:07 2006
@@ -72,6 +72,9 @@
 #include "asterisk/sched.h"
 #include "asterisk/io.h"
 #include "asterisk/rtp.h"
+#if defined(T38_SUPPORT)
+#include "asterisk/udptl.h"
+#endif
 #include "asterisk/acl.h"
 #include "asterisk/manager.h"
 #include "asterisk/callerid.h"
@@ -444,8 +447,44 @@
 static int sip_reloading = FALSE;			/*!< Flag for avoiding multiple reloads at the same time */
 static enum channelreloadreason sip_reloadreason;	/*!< Reason for last reload/load of configuration */
 
+#if defined(T38_SUPPORT)
+/* T.38 set of flags */
+#define T38FAX_FILL_BIT_REMOVAL			(1 << 0) 	/*!< Default: 0 (unset)*/
+#define T38FAX_TRANSCODING_MMR			(1 << 1)	/*!< Default: 0 (unset)*/
+#define T38FAX_TRANSCODING_JBIG			(1 << 2)	/*!< Default: 0 (unset)*/
+/* Rate management */
+#define T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF	(0 << 3)
+#define T38FAX_RATE_MANAGEMENT_LOCAL_TCF	(1 << 3)	/*!< Unset for transferedTCF (UDPTL), set for localTCF (TPKT) */
+/* UDP Error correction */
+#define T38FAX_UDP_EC_NONE			(0 << 4)	/*!< two bits, if unset NO t38UDPEC field in T38 SDP*/
+#define T38FAX_UDP_EC_FEC			(1 << 4)	/*!< Set for t38UDPFEC */
+#define T38FAX_UDP_EC_REDUNDANCY		(2 << 4)	/*!< Set for t38UDPRedundancy */
+/* T38 Spec version */
+#define T38FAX_VERSION				(3 << 6)	/*!< two bits, 2 values so far, up to 4 values max */ 
+#define T38FAX_VERSION_0			(0 << 6)	/*!< Version 0 */ 
+#define T38FAX_VERSION_1			(1 << 6)	/*!< Version 1 */
+/* Maximum Fax Rate */
+#define T38FAX_RATE_2400			(1 << 8)	/*!< 2400 bps t38FaxRate */
+#define T38FAX_RATE_4800			(1 << 9)	/*!< 4800 bps t38FaxRate */
+#define T38FAX_RATE_7200			(1 << 10)	/*!< 7200 bps t38FaxRate */
+#define T38FAX_RATE_9600			(1 << 11)	/*!< 9600 bps t38FaxRate */
+#define T38FAX_RATE_12000			(1 << 12)	/*!< 12000 bps t38FaxRate */
+#define T38FAX_RATE_14400			(1 << 13)	/*!< 14400 bps t38FaxRate */
+#endif
+
+/*! \brief Codecs that we support by default: */
 static struct sched_context *sched;	/*!< The scheduling context */
 static struct io_context *io;		/*!< The IO context */
+
+#if defined(T38_SUPPORT)
+static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600; /* This is default: NO MMR and JBIG trancoding, NO fill bit removal, transfered TCF, UDP FEC, Version 0 and 9600 max fax rate */
+#endif
+
+#if defined(T38_SUPPORT)
+static int t38udptlsupport = 0;
+static int t38rtpsupport = 0;
+static int t38tcpsupport = 0;
+#endif
 
 #define DEC_CALL_LIMIT	0
 #define INC_CALL_LIMIT	1
@@ -666,6 +705,12 @@
 	int peercapability;			/*!< Supported peer capability */
 	int prefcodec;				/*!< Preferred codec (outbound only) */
 	int noncodeccapability;
+#if defined(T38_SUPPORT)
+	int t38capability;			/*!< Our T38 capability */
+	int t38peercapability;			/*!< Peers T38 capability */
+	int t38jointcapability;			/*!< Supported T38 capability at both ends */
+	int t38state;				/*!< T.38 state : 0 not enabled, 1 offered from local - direct, 2 - offered from local - reinvite, 3 - offered from peer - direct, 4 offered from peer - reinvite, 5 negotiated (enabled) */
+#endif
 	int callingpres;			/*!< Calling presentation */
 	int authtries;				/*!< Times we've tried to authenticate */
 	int expiry;				/*!< How long we take to expire */
@@ -676,6 +721,9 @@
 	struct sockaddr_in sa;			/*!< Our peer */
 	struct sockaddr_in redirip;		/*!< Where our RTP should be going if not to us */
 	struct sockaddr_in vredirip;		/*!< Where our Video RTP should be going if not to us */
+#if defined(T38_SUPPORT)
+	struct sockaddr_in udptlredirip;	/*!< Where our T.38 UDPTL should be going if not to us */
+#endif
 	int redircodecs;			/*!< Redirect codecs */
 	struct sockaddr_in recv;		/*!< Received as */
 	struct in_addr ourip;			/*!< Our IP */
@@ -714,6 +762,9 @@
 	struct sip_registry *registry;		/*!< If this is a REGISTER dialog, to which registry */
 	struct ast_rtp *rtp;			/*!< RTP Session */
 	struct ast_rtp *vrtp;			/*!< Video RTP session */
+#if defined(T38_SUPPORT)
+	struct ast_udptl *udptl;		/*!< T.38 UDPTL session */
+#endif
 	struct sip_pkt *packets;		/*!< Packets scheduled for re-transmission */
 	struct sip_history_head *history;	/*!< History of this SIP dialog */
 	struct ast_variable *chanvars;		/*!< Channel variables to set for call */
@@ -910,12 +961,18 @@
 /*---------------------------- Forward declarations of functions in chan_sip.c */
 static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req);
 static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
+#if defined(T38_SUPPORT)
+static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
+#endif
 static int transmit_response_with_unsupported(struct sip_pvt *p, char *msg, struct sip_request *req, char *unsupported);
 static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, struct sip_request *req, const char *rand, int reliable, const char *header, int stale);
 static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch);
 static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch);
 static int transmit_invite(struct sip_pvt *p, int sipmethod, int sendsdp, int init);
 static int transmit_reinvite_with_sdp(struct sip_pvt *p);
+#if defined(T38_SUPPORT)
+static int transmit_reinvite_with_t38_sdp(struct sip_pvt *p);
+#endif
 static int transmit_info_with_digit(struct sip_pvt *p, char digit);
 static int transmit_info_with_vidupdate(struct sip_pvt *p);
 static int transmit_message_with_text(struct sip_pvt *p, const char *text);
@@ -969,7 +1026,10 @@
 static void set_peer_defaults(struct sip_peer *peer);
 static struct sip_peer *temp_peer(const char *name);
 
-
+#if defined(T38_SUPPORT)
+static int sip_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo,struct ast_channel **rc, int timeoutms); /* Function to bridge to SIP channels if NOT T.38 enabled */
+static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite); /* T38 negotiation helper function */
+#endif
 /*----- RTP interface functions */
 static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active);
 static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan);
@@ -994,7 +1054,11 @@
 	.transfer = sip_transfer,
 	.fixup = sip_fixup,
 	.send_digit = sip_senddigit,
-	.bridge = ast_rtp_bridge,
+#if defined(T38_SUPPORT)
+	.bridge = sip_bridge,
+#else
+ 	.bridge = ast_rtp_bridge,
+#endif
 	.send_text = sip_sendtext,
 };
 
@@ -1909,6 +1973,20 @@
 	ast_copy_flags(r, peer, SIP_FLAGS_TO_COPY);
 	r->capability = peer->capability;
 	r->prefs = peer->prefs;
+#if defined(T38_SUPPORT)
+	r->t38capability = global_t38_capability;
+	if (r->udptl) {
+		if ( ast_udptl_get_error_correction_scheme(r->udptl) == UDPTL_ERROR_CORRECTION_FEC )
+			r->t38capability |= T38FAX_UDP_EC_FEC;
+		else if ( ast_udptl_get_error_correction_scheme(r->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
+			r->t38capability |= T38FAX_UDP_EC_REDUNDANCY;			
+		else if (  ast_udptl_get_error_correction_scheme(r->udptl) == UDPTL_ERROR_CORRECTION_NONE )
+			r->t38capability |= T38FAX_UDP_EC_NONE;
+		r->t38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
+		ast_log(LOG_DEBUG,"Our T38 capability (%d)\n", r->t38capability);
+	}
+	r->t38jointcapability = r->t38capability;
+#endif
 	if (r->rtp) {
 		if (option_debug)
 			ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(r, SIP_NAT) & SIP_NAT_ROUTE));
@@ -1919,6 +1997,12 @@
 			ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(r, SIP_NAT) & SIP_NAT_ROUTE));
 		ast_rtp_setnat(r->vrtp, (ast_test_flag(r, SIP_NAT) & SIP_NAT_ROUTE));
 	}
+#if defined(T38_SUPPORT)
+	if (r->udptl) {
+		ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %d\n", (ast_test_flag(r, SIP_NAT) & SIP_NAT_ROUTE));
+		ast_udptl_setnat(r->udptl, (ast_test_flag(r, SIP_NAT) & SIP_NAT_ROUTE));
+	}
+#endif
 	ast_string_field_set(r, peername, peer->username);
 	ast_string_field_set(r, authname, peer->username);
 	ast_string_field_set(r, username, peer->username);
@@ -2089,6 +2173,12 @@
 		} else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
 			/* Check whether there is a variable with a name starting with SIPADDHEADER */
 			p->options->addsipheaders = 1;
+#if defined(T38_SUPPORT)
+		} else if (!strncasecmp(ast_var_name(current), "T38CALL", strlen("T38CALL"))) {
+			/* Check whether there is a variable with a name starting with T38CALL */
+			p->t38state = 1;
+			ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n",p->t38state, ast->name);
+#endif
 		}
 
 		
@@ -2118,6 +2208,10 @@
 	if ( res != -1 ) {
 		p->callingpres = ast->cid.cid_pres;
 		p->jointcapability = p->capability;
+#if defined(T38_SUPPORT)
+		p->t38jointcapability = p->t38capability;
+		ast_log(LOG_DEBUG,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38capability, p->t38jointcapability);
+#endif
 		transmit_invite(p, SIP_INVITE, 1, 2);
 		if (p->maxtime) {
 			/* Initialize auto-congest time */
@@ -2181,6 +2275,11 @@
 	if (p->vrtp) {
 		ast_rtp_destroy(p->vrtp);
 	}
+#if defined(T38_SUPPORT)
+	if (p->udptl) {
+		ast_udptl_destroy(p->udptl);
+	}
+#endif
 	if (p->route) {
 		free_old_route(p->route);
 		p->route = NULL;
@@ -2633,7 +2732,14 @@
 		ast_setstate(ast, AST_STATE_UP);
 		if (option_debug)
 			ast_log(LOG_DEBUG, "SIP answering channel: %s\n", ast->name);
-		res = transmit_response_with_sdp(p, "200 OK", &p->initreq, 1);
+#if defined(T38_SUPPORT)
+		if (p->t38state == 3) {
+			p->t38state=5;
+			ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n",p->t38state, ast->name);
+			res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, 1);
+		} else
+#endif
+ 		res = transmit_response_with_sdp(p, "200 OK", &p->initreq, 1);
 	}
 	ast_mutex_unlock(&p->lock);
 	return res;
@@ -2684,6 +2790,21 @@
 	case AST_FRAME_IMAGE:
 		return 0;
 		break;
+#if defined(T38_SUPPORT)
+	case AST_FRAME_MODEM:
+		if (p) {
+			ast_mutex_lock(&p->lock);
+			if (p->udptl) {
+				if ((ast->_state != AST_STATE_UP) && !ast_test_flag(p, SIP_PROGRESS_SENT) && !ast_test_flag(p, SIP_OUTGOING)) {
+					transmit_response_with_t38_sdp(p, "183 Session Progress", &p->initreq, 0);
+					ast_set_flag(p, SIP_PROGRESS_SENT);	
+				}
+				res = ast_udptl_write(p->udptl, frame);
+			}
+			ast_mutex_unlock(&p->lock);
+		}
+		break;
+#endif
 	default: 
 		ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
 		return 0;
@@ -2836,7 +2957,24 @@
 	return res;
 }
 
-
+#ifdef T38_SUPPORT
+static int sip_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo,struct ast_channel **rc, int timeoutms) {
+
+     /* Because attempt to do a native RTP bridge between peers happens before T38 re-invites
+        and that one time only, and at that moment neither peers have T38 enabled, this will
+        lead to the native RTP bridge always (if canreinvite is set to yes). 
+	Since this is not good for T38 bridging, we have to disable native bridging entirely if 
+	t38 support is enabled - not good, but working. 
+	XXX: It would be better to have user/peer configuration flag for t38support. :XXX
+     */
+     if (!t38udptlsupport) {
+	    return ast_rtp_bridge(c0,c1,flags,fo,rc,timeoutms);
+     } else {
+	    ast_log(LOG_NOTICE, "T38 UDPTL support enabled native RTP bridging disabled\n");
+	    return AST_BRIDGE_FAILED_NOWARN;
+     }
+}
+#endif
 
 /*! \brief Initiate a call in the SIP channel
 	called from sip_request_call (calls from the pbx ) */
@@ -2880,7 +3018,11 @@
 
 	if (ast_test_flag(i, SIP_DTMF) ==  SIP_DTMF_INBAND) {
 		i->vad = ast_dsp_new();
-		ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
+#if defined(T38_SUPPORT)
+		ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT | DSP_FEATURE_FAX_DETECT);
+#else
+ 		ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
+#endif
 		if (global_relaxdtmf)
 			ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
 	}
@@ -2892,6 +3034,11 @@
 		tmp->fds[2] = ast_rtp_fd(i->vrtp);
 		tmp->fds[3] = ast_rtcp_fd(i->vrtp);
 	}
+#if defined(T38_SUPPORT)
+	if (i->udptl) {
+    		tmp->fds[4] = ast_udptl_fd(i->udptl);
+	}
+#endif
 	if (state == AST_STATE_RING)
 		tmp->rings = 1;
 	tmp->adsicpe = AST_ADSI_UNAVAILABLE;
@@ -3055,7 +3202,11 @@
 }
 
 /*! \brief Read RTP from network */
+#if defined(T38_SUPPORT)
+static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect)
+#else
 static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p)
+#endif
 {
 	/* Retrieve audio/etc from channel.  Assumes p->lock is already held. */
 	struct ast_frame *f;
@@ -3078,6 +3229,11 @@
 	case 3:
 		f = ast_rtcp_read(p->vrtp);	/* RTCP Control Channel for video */
 		break;
+#if defined(T38_SUPPORT)
+	case 4:
+		f = ast_udptl_read(p->udptl);	/* UDPTL for T.38 */
+		break;
+#endif
 	default:
 		f = &ast_null_frame;
 	}
@@ -3097,8 +3253,19 @@
 			}
 			if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
 				f = ast_dsp_process(p->owner, p->vad, f);
-				if (option_debug && f && (f->frametype == AST_FRAME_DTMF)) 
-					ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass);
+				if (f && (f->frametype == AST_FRAME_DTMF)) { 
+#if defined(T38_SUPPORT)
+					if (t38udptlsupport && f->subclass == 'f')  {
+						/* Fax tone */
+						if (option_debug)
+							ast_log(LOG_DEBUG, "Fax CNG detected on %s\n", ast->name);
+
+						*faxdetect = 1;
+					}
+#endif
+					if (option_debug)
+						ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass);
+				}
 			}
 		}
 	}
@@ -3110,11 +3277,36 @@
 {
 	struct ast_frame *fr;
 	struct sip_pvt *p = ast->tech_pvt;
-
+#if defined(T38_SUPPORT)
+	int faxdetected = 0;
+#endif
 	ast_mutex_lock(&p->lock);
+#if defined(T38_SUPPORT)
+	fr = sip_rtp_read(ast, p, &faxdetected);
+#else
 	fr = sip_rtp_read(ast, p);
+#endif
 	time(&p->lastrtprx);
 	ast_mutex_unlock(&p->lock);
+#if defined(T38_SUPPORT)
+	/* 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 than it is responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preabmle */
+	if (faxdetected  && t38udptlsupport && (p->t38state == 0) && !(ast_bridged_channel(ast))) {
+		if (!ast_test_flag(p, SIP_GOTREFER)) {
+			if (!p->pendinginvite) {
+				if (option_debug > 2)
+					ast_log(LOG_DEBUG, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name);
+				p->t38state = 2;
+				transmit_reinvite_with_t38_sdp(p);
+				ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s",p->t38state,ast->name);
+			}
+		} else if (!ast_test_flag(p, SIP_PENDINGBYE)) {
+				if (option_debug > 2)
+					ast_log(LOG_DEBUG, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n",ast->name);
+				ast_set_flag(p, SIP_NEEDREINVITE);
+			}	
+	}
+#endif
 	return fr;
 }
 
@@ -3207,6 +3399,10 @@
 		p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
 		if (global_videosupport)
 			p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+#if defined(T38_SUPPORT)
+		if (t38udptlsupport)
+			p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
+#endif
 		if (!p->rtp || (global_videosupport && !p->vrtp)) {
 			ast_log(LOG_WARNING, "Unable to create RTP audio %s session: %s\n", global_videosupport ? "and video" : "", strerror(errno));
 			ast_mutex_destroy(&p->lock);
@@ -3220,6 +3416,10 @@
 		ast_rtp_settos(p->rtp, global_tos);
 		if (p->vrtp)
 			ast_rtp_settos(p->vrtp, global_tos);
+#if defined(T38_SUPPORT)
+		if (p->udptl)
+			ast_udptl_settos(p->udptl, global_tos);
+#endif
 		p->rtptimeout = global_rtptimeout;
 		p->rtpholdtimeout = global_rtpholdtimeout;
 		p->rtpkeepalive = global_rtpkeepalive;
@@ -3233,6 +3433,10 @@
 			ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 		if (p->vrtp)
 			ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
+#if defined(T38_SUPPORT)
+		if (p->udptl)
+			ast_udptl_setnat(p->udptl, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
+#endif
 	}
 
 	if (p->method != SIP_REGISTER)
@@ -3248,6 +3452,19 @@
 	p->capability = global_capability;
 	if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(p, SIP_DTMF) == SIP_DTMF_AUTO))
 		p->noncodeccapability |= AST_RTP_DTMF;
+#if defined(T38_SUPPORT)
+	if (p->udptl) {
+	    p->t38capability = global_t38_capability;
+	    if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY)
+		    p->t38capability |= T38FAX_UDP_EC_REDUNDANCY;
+	    else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC)
+		    p->t38capability |= T38FAX_UDP_EC_FEC;
+	    else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE)
+		    p->t38capability |= T38FAX_UDP_EC_NONE;
+	    p->t38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
+	    p->t38jointcapability = p->t38capability;
+	}	
+#endif
 	ast_string_field_set(p, context, default_context);
 
 	/* Add to active dialog list */
@@ -3542,6 +3759,12 @@
 	int len = -1;
 	int portno = -1;
 	int vportno = -1;
+#if defined(T38_SUPPORT)
+	int udptlportno = -1;
+	int peert38capability = 0;
+	char s[256];
+	int old = 0;
+#endif        
 	int peercapability, peernoncodeccapability;
 	int vpeercapability=0, vpeernoncodeccapability=0;
 	struct sockaddr_in sin;
@@ -3609,6 +3832,22 @@
 				codecs = ast_skip_blanks(codecs + len);
 			}
 		}
+#if defined(T38_SUPPORT)
+		if (p->udptl && t38udptlsupport && (sscanf(m, "image %d udptl t38 %n", &x, &len) == 1)) {
+			if (debug)
+				ast_verbose("Got T.38 offer in SDP\n");
+			found = 1;
+			udptlportno = x;
+			
+			if (p->owner && p->lastinvite) {
+				p->t38state = 4; /* T38 Offered in re-invite from remote party */
+				ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n",p->t38state,p->owner ? p->owner->name : "<none>" );
+			} else {
+				p->t38state = 3; /* T38 Offered directly from peer in first invite */
+				ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n",p->t38state,p->owner ? p->owner->name : "<none>");
+			}				
+		}
+#endif
 		if (p->vrtp)
 			ast_rtp_pt_clear(p->vrtp);  /* Must be cleared in case no m=video line exists */
 
@@ -3632,7 +3871,11 @@
 		if (!found )
 			ast_log(LOG_WARNING, "Unknown SDP media type in offer: %s\n", m);
 	}
+#if defined(T38_SUPPORT)
+	if (portno == -1 && vportno == -1 && udptlportno == -1) {
+#else
 	if (portno == -1 && vportno == -1) {
+#endif
 		/* No acceptable offer found in SDP */
 		return -2;
 	}
@@ -3686,6 +3929,17 @@
 			ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
 		}
 	}
+#if defined(T38_SUPPORT)
+	/* Setup UDPTL port number */
+	sin.sin_port = htons(udptlportno);
+	if (p->udptl && t38udptlsupport && sin.sin_port) {
+		ast_udptl_set_peer(p->udptl, &sin);
+		if (debug) {
+			ast_verbose("Peer T.38 UDPTL is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
+			ast_log(LOG_DEBUG,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
+		}
+	}
+#endif
 
 	/* Next, scan through each "a=rtpmap:" line, noting each
 	 * specified RTP payload type (with corresponding MIME subtype):
@@ -3708,6 +3962,116 @@
 		if (p->vrtp)
 			ast_rtp_set_rtpmap_type(p->vrtp, codec, "video", mimeSubtype);
 	}
+#if defined(T38_SUPPORT)
+	if (udptlportno != -1) {
+		/* Scan trough the a= lines for T38 attributes and set apropriate fileds */
+		sdpLineNum_iterator_init(&iterator);
+		old = 0;
+		int found = 0;
+		while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
+			if (old && (iterator-old != 1))
+			    break;
+			old = iterator;
+			
+			if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"MaxBufferSize:%d\n",x);
+			}
+			if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"T38MaxBitRate: %d\n",x);
+				switch (x) {
+				    case 14400:
+					peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+					break;
+				    case 12000:
+					peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+					break;
+				    case 9600:
+					peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+					break;
+				    case 7200:
+					peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+					break;
+				    case 4800:
+					peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
+					break;
+				    case 2400:
+					peert38capability |= T38FAX_RATE_2400;
+					break;
+				}
+			}
+			if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"FaxVerison: %d\n",x);
+				if (x == 0)
+					peert38capability |= T38FAX_VERSION_0;
+				else if (x == 1)
+					peert38capability |= T38FAX_VERSION_1;
+			}
+			if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"FaxMaxDatagram: %d\n",x);
+				ast_udptl_set_far_max_datagram(p->udptl, x);
+				ast_udptl_set_local_max_datagram(p->udptl, x);
+			}
+			if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"FillBitRemoval: %d\n",x);
+				if (x == 1)
+					peert38capability |= T38FAX_FILL_BIT_REMOVAL;
+			}
+			if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"Transcoding MMR: %d\n",x);
+				if (x == 1)
+					peert38capability |= T38FAX_TRANSCODING_MMR;
+			}
+			if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"Transcoding JBIG: %d\n",x);
+				if (x == 1)
+					peert38capability |= T38FAX_TRANSCODING_JBIG;
+			}
+			if ((sscanf(a, "T38FaxRateManagement:%s", s) == 1)) {
+			    	found = 1;
+				ast_log(LOG_DEBUG,"RateMangement: %s\n", s);
+				if (!strcasecmp(s, "localTCF"))
+					peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
+				else if (!strcasecmp(s, "transferedTCF"))
+					peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
+			}
+			if ((sscanf(a, "T38FaxUdpEC:%s", s) == 1)) {
+				found = 1;
+				ast_log(LOG_DEBUG,"UDP EC: %s\n", s);
+				if (!strcasecmp(s, "t38UDPRedundancy")) {
+					peert38capability |= T38FAX_UDP_EC_REDUNDANCY;
+					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
+				} else if (!strcasecmp(s, "t38UDPFEC")) {
+					peert38capability |= T38FAX_UDP_EC_FEC;
+					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
+				} else {
+					peert38capability |= T38FAX_UDP_EC_NONE;
+					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
+				}
+			}
+		}
+		if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */ 
+			p->t38peercapability = peert38capability;
+			p->t38jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */
+			peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400); /* Mask speeds only */ 
+			p->t38jointcapability |= (peert38capability & p->t38capability); /* Put the lower of our's and peer's speed */
+		}
+		if (debug)
+			ast_log(LOG_DEBUG,"Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n",
+				    p->t38capability, 
+				    p->t38peercapability,
+				    p->t38jointcapability);
+	} else {
+		p->t38state = 0;
+		ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n",p->t38state,p->owner ? p->owner->name : "<none>");
+	}
+#endif
 
 	/* Now gather all of the codecs that were asked for: */
 	ast_rtp_get_current_formats(p->rtp,
@@ -4452,6 +4816,139 @@
 		/* Indicate we support DTMF and FLASH... */
 		ast_build_string(a_buf, a_size, "a=fmtp:%d 0-16\r\n", rtp_code);
 }
+#if defined(T38_SUPPORT)
+/*--- t38_get_rate: Get Max T.38 Transmision rate from T38 capabilities ---*/
+int t38_get_rate(int t38cap)
+{
+    int maxrate = (t38cap & (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400));
+    if (maxrate & T38FAX_RATE_14400) {
+		ast_log(LOG_DEBUG, "T38MaxFaxRate 14400 found\n");
+		return 14400;
+    } else if (maxrate & T38FAX_RATE_12000) {
+		ast_log(LOG_DEBUG, "T38MaxFaxRate 12000 found\n");
+		return 12000;
+    } else if (maxrate & T38FAX_RATE_9600) {
+		ast_log(LOG_DEBUG, "T38MaxFaxRate 9600 found\n");
+		return 9600;
+    } else if (maxrate & T38FAX_RATE_7200) {
+		ast_log(LOG_DEBUG, "T38MaxFaxRate 7200 found\n");
+		return 7200;
+    } else if (maxrate & T38FAX_RATE_4800) {
+		ast_log(LOG_DEBUG, "T38MaxFaxRate 4800 found\n");
+		return 4800;
+    } else if (maxrate & T38FAX_RATE_2400) {
+		ast_log(LOG_DEBUG, "T38MaxFaxRate 2400 found\n");
+		return 2400;
+    } else {
+		ast_log(LOG_DEBUG, "Strange, T38MaxFaxRate NOT found in peers T38 SDP.\n");
+		return 0;
+    }
+}
+
+/*--- add_t38_sdp: 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;
+	char v[256] = "";
+	char s[256] = "";
+	char o[256] = "";
+	char c[256] = "";
+	char t[256] = "";
+	char m_modem[256];
+	char a_modem[1024];
+	char *m_modem_next = m_modem;
+	size_t m_modem_left = sizeof(m_modem);
+	char *a_modem_next = a_modem;
+	size_t a_modem_left = sizeof(a_modem);
+	char iabuf[INET_ADDRSTRLEN];
+	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 = getpid();
+		p->sessionversion = p->sessionid;
+	} else
+		p->sessionversion++;
+
+	/* Our T.38 end is */
+	if (p->udptl)
+		ast_udptl_get_us(p->udptl, &udptlsin);
+
+	/* Determine T.38 UDPTL destination */
+	if (p->udptl) {
+		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;
+			udptldest.sin_port = udptlsin.sin_port;
+		}
+	}
+
+	if (debug){
+		if (p->udptl)
+			ast_verbose("T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), 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_log(LOG_DEBUG, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
+			p->t38capability,
+			p->t38peercapability,
+			p->t38jointcapability);	
+	}
+	snprintf(v, sizeof(v), "v=0\r\n");
+	snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(iabuf, sizeof(iabuf), udptldest.sin_addr));
+	snprintf(s, sizeof(s), "s=session\r\n");
+	snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(iabuf, sizeof(iabuf), udptldest.sin_addr));
+	snprintf(t, sizeof(t), "t=0 0\r\n");
+	ast_build_string(&m_modem_next, &m_modem_left, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
+
+	if ((p->t38jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
+		ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVerison:0\r\n");
+	if ((p->t38jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
+		ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVerison:1\r\n");
+	if ((x = t38_get_rate(p->t38jointcapability))) 
+		ast_build_string(&a_modem_next, &a_modem_left, "a=T38MaxBitRate:%d\r\n",x);
+	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxFillBitRemoval:%d\r\n", (p->t38jointcapability & T38FAX_FILL_BIT_REMOVAL) ? 1 : 0);
+	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingMMR:%d\r\n", (p->t38jointcapability & T38FAX_TRANSCODING_MMR) ? 1 : 0);
+	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingJBIG:%d\r\n", (p->t38jointcapability & T38FAX_TRANSCODING_JBIG) ? 1 : 0);
+	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxRateManagement:%s\r\n", (p->t38jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferedTCF");
+	x = ast_udptl_get_local_max_datagram(p->udptl);
+	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxBuffer:%d\r\n",x);
+	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxDatagram:%d\r\n",x);
+	if (p->t38jointcapability != T38FAX_UDP_EC_NONE)
+		ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxUdpEC:%s\r\n", (p->t38jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
+	if (p->udptl)
+		len = strlen(m_modem) + strlen(a_modem);
+	add_header(resp, "Content-Type", "application/sdp");
+	add_header_contentLength(resp, len);
+	add_line(resp, v);
+	add_line(resp, o);
+	add_line(resp, s);
+	add_line(resp, c);
+	add_line(resp, t);
+	add_line(resp, m_modem);
+	add_line(resp, a_modem);
+	
+	/* Update lastrtprx when we send our SDP */
+	time(&p->lastrtprx);
+	time(&p->lastrtptx);
+
+	return 0;
+}
+#endif
 
 /*! \brief Add Session Description Protocol message */
 static int add_sdp(struct sip_request *resp, struct sip_pvt *p)
@@ -4555,6 +5052,17 @@
 					 debug);
 		alreadysent |= p->prefcodec;
 	}
+#if defined(T38_SUPPORT)
+	if (t38rtpsupport) {
+    		/* TODO: Improve this */
+		len = snprintf(a_audio_next, a_audio_left, " %d", 191);
+    		a_audio_next += len;
+    		a_audio_left -= len;
+		len = snprintf(a_audio_next, a_audio_left, "a=rtpmap:%d %s/8000\r\n", 191, "t38");
+    		a_audio_next += len;
+    		a_audio_left -= len;
+	}
+#endif
 
 	/* Start by sending our preferred codecs */
 	for (x = 0; x < 32; x++) {
@@ -4675,6 +5183,27 @@
 	}
 	return send_response(p, &resp, retrans, seqno);
 }
+
+#if defined(T38_SUPPORT)
+/*--- transmit_response_with_t38_sdp: Used for 200 OK and 183 early media ---*/
+static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans)
+{
+	struct sip_request resp;
+	int seqno;
+	if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
+		ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
+		return -1;
+	}
+	respprep(&resp, p, msg, req);
+	if (p->udptl) {
+		ast_udptl_offered_from_local(p->udptl, 0);
+    		add_t38_sdp(&resp, p);
+	} else {
+		ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
+	}
+	return send_response(p, &resp, retrans, seqno);
+}
+#endif
 
 /*! \brief Parse first line of incoming SIP request */
 static int determine_firstline_parts( struct sip_request *req ) 
@@ -4752,6 +5281,37 @@
 	ast_set_flag(p, SIP_OUTGOING);
 	return send_request(p, &req, 1, p->ocseq);
 }
+
+#if defined(T38_SUPPORT)
+/*--- transmit_reinvite_with_t38_sdp: Transmit reinvite with T38 SDP ---*/
+/* 	A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
+	INVITE that opened the SIP dialogue 
+	We reinvite so that the T38 processing can take place.
+	SIP Signalling stays with * in the path.
+*/
+static int transmit_reinvite_with_t38_sdp(struct sip_pvt *p)
+{
+	struct sip_request req;
+	if (ast_test_flag(p, SIP_REINVITE_UPDATE))
+		reqprep(&req, p, SIP_UPDATE, 0, 1);
+	else 
+		reqprep(&req, p, SIP_INVITE, 0, 1);
+	
+	add_header(&req, "Allow", ALLOWED_METHODS);
+	if (sipdebug)
+		add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)");
+	ast_udptl_offered_from_local(p->udptl, 1);
+	add_t38_sdp(&req, p);
+	/* Use this as the basis */
+	copy_request(&p->initreq, &req);
+	parse_request(&p->initreq);
+	if (sip_debug_test_pvt(p))
+		ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+	p->lastinvite = p->ocseq;
+	ast_set_flag(p, SIP_OUTGOING);
+	return send_request(p, &req, 1, p->ocseq);
+}
+#endif
 
 /*! \brief Check Contact: URI of SIP message */
 static void extract_uri(struct sip_pvt *p, struct sip_request *req)
@@ -5077,7 +5637,15 @@
 			}
 		}
 	}
+#if defined(T38_SUPPORT)
+	if (sdp && (p->udptl) && (p->t38state == 1)) {
+		ast_udptl_offered_from_local(p->udptl, 1);
+		ast_log(LOG_DEBUG, "T38 is in state %d on channel %s\n",p->t38state, p->owner ? p->owner->name : "<none>");
+		add_t38_sdp(&req, p);
+	} else if (sdp && p->rtp) {
+#else
 	if (sdp && p->rtp) {
+#endif
 		add_sdp(&req, p);
 	} else {
 		add_header_contentLength(&req, 0);
@@ -7160,6 +7728,12 @@
 				ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 			ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 		}
+#if defined(T38_SUPPORT)
+		if (p->udptl) {
+			ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
+			ast_udptl_setnat(p->udptl, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
+		}
+#endif
 		if (!(res = check_auth(p, req, user->name, user->secret, user->md5secret, sipmethod, uri, reliable, ignore))) {
 			sip_cancel_destroy(p);
 			ast_copy_flags(p, user, SIP_FLAGS_TO_COPY);
@@ -7202,6 +7776,10 @@
 				p->noncodeccapability |= AST_RTP_DTMF;
 			else
 				p->noncodeccapability &= ~AST_RTP_DTMF;
+#if defined(T38_SUPPORT)
+			if (p->t38peercapability)
+				p->t38jointcapability &= p->t38peercapability;
+#endif
 		}
 		if (user && debug)
 			ast_verbose("Found user '%s'\n", user->name);
@@ -7256,6 +7834,12 @@
 				ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 				ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
 			}
+#if defined(T38_SUPPORT)
+			if (p->udptl) {
+				ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
+				ast_udptl_setnat(p->udptl, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE));
+			}
+#endif
 			ast_string_field_set(p, peersecret, peer->secret);
 			ast_string_field_set(p, peermd5secret, peer->md5secret);
 			ast_string_field_set(p, subscribecontext, peer->subscribecontext);
@@ -7319,6 +7903,10 @@
 					p->noncodeccapability |= AST_RTP_DTMF;
 				else
 					p->noncodeccapability &= ~AST_RTP_DTMF;
+#if defined(T38_SUPPORT)
+				if (p->t38peercapability)
+					p->t38jointcapability &= p->t38peercapability;
+#endif
 			}
 			ASTOBJ_UNREF(peer, sip_destroy_peer);
 		} else { 
@@ -9608,6 +10196,49 @@
 			build_route(p, req, 1);
 		}
 		
+#if defined(T38_SUPPORT)
+		if (p->owner && (p->owner->_state == AST_STATE_UP)) { /* if this is a re-invite */ 
+			struct ast_channel *bridgepeer = NULL;
+			struct sip_pvt *bridgepvt = NULL;
+			bridgepeer = ast_bridged_channel(p->owner);
+			if (!bridgepeer->tech) {
+				ast_log(LOG_WARNING, "Ooooh.. no tech!  That's REALLY bad\n");
+				break;
+			}
+			if (!strcasecmp(bridgepeer->tech->type,"SIP")) {
+				bridgepvt = (struct sip_pvt*)(bridgepeer->tech_pvt);
+				if (bridgepvt->udptl) {
+					if (p->t38state == 4) { 
+						/* This is 200 OK to re-invite where T38 was offered on channel so we need to send 200 OK with T38 the other side of the bridge */
+						/* Send response with T38 SDP to the other side of the bridge */
+						sip_handle_t38_reinvite(bridgepeer,p,0);
+					} else if (p->t38state == 0 && bridgepeer && (bridgepvt->t38state == 5)) { /* This is case of RTP re-invite after T38 session */
+						ast_log(LOG_WARNING, "RTP re-inivte after T38 session not handled yet !\n");
+						/* Insted of this we should somehow re-invite the other side of the bridge to RTP */
+						ast_set_flag(p, SIP_NEEDDESTROY);
+					}
+				} else {
+						ast_log(LOG_WARNING, "Strange... The other side of the bridge don't have udptl struct\n");
+						ast_mutex_lock(&bridgepvt->lock);
+						bridgepvt->t38state = 0;
+						ast_mutex_unlock(&bridgepvt->lock);
+						ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n",bridgepvt->t38state, bridgepeer->tech->type);
+						p->t38state = 0;
+						ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n",p->t38state, p->owner ? p->owner->name : "<none>");
+				}
+			} else {
+					/* Other side is not a SIP channel */ 
+					ast_log(LOG_WARNING, "Strange... The other side of the bridge is not a SIP channel\n");
+					p->t38state = 0;
+					ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n",p->t38state, p->owner ? p->owner->name : "<none>");
+			}
+		}
+		if ((p->t38state == 2) || (p->t38state == 1)) {
+			/* If there was T38 reinvite and we are supposed to answer with 200 OK than this should set us to T38 negotiated mode */
+			p->t38state = 5;
+			ast_log(LOG_DEBUG, "T38 changed state to %d on channel %s\n", p->t38state, p->owner ? p->owner->name : "<none>");
+		}
+#endif
 		if (!ignore && p->owner) {
 			if (p->owner->_state != AST_STATE_UP) {
 #ifdef OSP_SUPPORT	
@@ -10002,6 +10633,12 @@
 					/* Immediately stop VRTP */
 					ast_rtp_stop(p->vrtp);
 				}
+#if defined(T38_SUPPORT)
+				if (p->udptl) {
+					/* Immediately stop T.38 UDPTL */
+					ast_udptl_stop(p->udptl);
+				}
+#endif
 				/* XXX Locking issues?? XXX */
 				switch(resp) {
 				case 300: /* Multiple Choices */
@@ -10573,7 +11210,76 @@
 			transmit_response(p, "180 Ringing", req);
 			break;
 		case AST_STATE_UP:
-			transmit_response_with_sdp(p, "200 OK", req, 1);
+#if defined(T38_SUPPORT)
+			if (p->t38state == 4) {
+	    		    struct ast_channel *bridgepeer = NULL;
+			    struct sip_pvt *bridgepvt = NULL;
+			    if ((bridgepeer=ast_bridged_channel(p->owner))) {
+					/* We have a bridge, and this is re-invite to switchover to T38 so we send re-invite with T38 SDP, to other side of bridge*/
+					/*! XXX: we should also check here does the other side supports t38 at all !!! XXX */  
+					if (!strcasecmp(bridgepeer->tech->type,"SIP")) { /* If we are bridged to SIP channel */
+						bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
+						if (!(bridgepvt->t38state)) {
+						    if (bridgepvt->udptl) { /* If everything is OK with other side's udptl struct */
+							    /* Send re-invite to the bridged channel */ 
+							    sip_handle_t38_reinvite(bridgepeer,p,1);
+						    } else { /* Something is wrong with peers udptl struct */
+							    ast_log(LOG_WARNING, "Strange... The other side of the bridge don't have udptl struct\n");
+							    ast_mutex_lock(&bridgepvt->lock);
+							    bridgepvt->t38state = 0;
+							    ast_mutex_unlock(&bridgepvt->lock);
+							    ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n",bridgepvt->t38state, bridgepeer->name);
+							    p->t38state = 0;
+							    ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n",p->t38state, p->owner ? p->owner->name : "<none>");
+	    						    if (ignore)
+								    transmit_response(p, "415 Unsupported Media Type", req);
+							    else
+								    transmit_response_reliable(p, "415 Unsupported Media Type", req, 1);

[... 266 lines stripped ...]


More information about the asterisk-commits mailing list