Merged revisions 184947 via svnmerge from
authorJoshua Colp <jcolp@digium.com>
Mon, 30 Mar 2009 14:37:47 +0000 (14:37 +0000)
committerJoshua Colp <jcolp@digium.com>
Mon, 30 Mar 2009 14:37:47 +0000 (14:37 +0000)
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

channels/chan_sip.c

index c9e63ad..514d0aa 100644 (file)
@@ -1456,7 +1456,6 @@ static int sipdebug_text;
 /*! \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 */
@@ -1470,6 +1469,7 @@ struct t38properties {
        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 */
@@ -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,
                                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);
 
@@ -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 */
                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:
@@ -4619,6 +4618,10 @@ static void change_t38_state(struct sip_pvt *p, int state)
        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>");
 
@@ -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")) {
                        /* 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);
                }
        }
 
@@ -5770,15 +5770,9 @@ static int sip_answer(struct ast_channel *ast)
 
                ast_setstate(ast, AST_STATE_UP);
                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;
@@ -5815,9 +5809,13 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
                                        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);
                }
@@ -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.
                                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;
@@ -6295,10 +6301,6 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
        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) {
                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 */
                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) {
@@ -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 */
 static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
                                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 
     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;
@@ -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 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 */
@@ -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 *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;
@@ -9334,177 +9254,226 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                        p->sessionversion++;
        }
 
-       capability = p->jointcapability;
+       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));
 
-       /* 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));
+       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 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");
-       }
+               /* 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);
-               
-       if (debug) 
-               ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port)); 
+               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));
+               /* 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));        
-       }
+                       /* 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) {
+               /* Check if we need text in this call */
+               if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) {
                        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");
-       }
+                               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");
+               }
                
-       /* 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;
-               } else {
-                       tdest.sin_addr = p->ourip.sin_addr;
-                       tdest.sin_port = tsin.sin_port;
+               /* 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;
+                       } 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)); 
+
                }
-               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 */
 
-       /* Start building generic SDP headers */
+               ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
 
-       /* We break with the "recommendation" and send our IP, in order that our
-          peer doesn't have to ast_gethostbyname() us */
+               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";
 
-       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));
+               /* 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
+               */
 
-       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";
+               /* 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;
 
-       /* 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
-       */
+                       add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size);
+                       alreadysent |= codec;
+               }
 
-       /* 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;
+               /* Start by sending our preferred audio/video codecs */
+               for (x = 0; x < 32; x++) {
+                       int codec;
 
-               add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size);
-               alreadysent |= codec;
-       }
+                       if (!(codec = ast_codec_pref_index(&p->prefs, x)))
+                               break; 
 
-       /* Start by sending our preferred audio/video codecs */
-       for (x = 0; x < 32; x++) {
-               int codec;
+                       if (!(capability & codec))
+                               continue;
 
-               if (!(codec = ast_codec_pref_index(&p->prefs, x)))
-                       break; 
+                       if (alreadysent & codec)
+                               continue;
 
-               if (!(capability & codec))
-                       continue;
+                       add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size);
+                       alreadysent |= codec;
+               }
 
-               if (alreadysent & codec)
-                       continue;
+               /* 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;
 
-               add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size);
-               alreadysent |= codec;
-       }
+                       if (alreadysent & x)    /* Already added to SDP */
+                               continue;
 
-       /* 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 (x & AST_FORMAT_AUDIO_MASK)
+                               add_codec_to_sdp(p, x, &m_audio, &a_audio, debug, &min_audio_packet_size);
+                       else if (x & AST_FORMAT_VIDEO_MASK)
+                               add_vcodec_to_sdp(p, x, &m_video, &a_video, debug, &min_video_packet_size);
+                       else if (x & AST_FORMAT_TEXT_MASK)
+                               add_tcodec_to_sdp(p, x, &m_text, &a_text, debug, &min_text_packet_size);
+               }
 
-               if (alreadysent & x)    /* Already added to SDP */
-                       continue;
+               /* Now add DTMF RFC2833 telephony-event as a codec */
+               for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
+                       if (!(p->jointnoncodeccapability & x))
+                               continue;
 
-               if (x & AST_FORMAT_AUDIO_MASK)
-                       add_codec_to_sdp(p, x, &m_audio, &a_audio, debug, &min_audio_packet_size);
-               else if (x & AST_FORMAT_VIDEO_MASK)
-                       add_vcodec_to_sdp(p, x, &m_video, &a_video, debug, &min_video_packet_size);
-               else if (x & AST_FORMAT_TEXT_MASK)
-                       add_tcodec_to_sdp(p, x, &m_text, &a_text, debug, &min_text_packet_size);
-       }
+                       add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug);
+               }
 
-       /* Now add DTMF RFC2833 telephony-event as a codec */
-       for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
-               if (!(p->jointnoncodeccapability & x))
-                       continue;
+               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);
 
-               add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug);
+               /* 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 (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)
+                       ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
        }
 
-       ast_debug(3, "-- Done with adding codecs to SDP\n");
+       if (add_t38) {
+               /* Our T.38 end is */
+               ast_udptl_get_us(p->udptl, &udptlsin);
 
-       if (!p->owner || !ast_internal_timing_enabled(p->owner))
-               ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n");
+               /* 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 (min_audio_packet_size)
-               ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size);
+               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));
 
-       /* 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);
+               /* We break with the "recommendation" and send our IP, in order that our
+                  peer doesn't have to ast_gethostbyname() us */
 
-       /* 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 (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)
-               ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
+               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)
                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");
        if (needtext)
                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) +
                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);
        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);
@@ -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, 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 ? */
@@ -9568,7 +9545,7 @@ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct s
        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)
@@ -9617,8 +9594,12 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
                        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)
@@ -9700,9 +9681,9 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
        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);
@@ -10105,12 +10086,12 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
                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);
@@ -16799,20 +16780,6 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
 
                        /* 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);
-                       pvt_set_needdestroy(p, "got error on T.38 initial invite");
-                       sip_alreadygone(p);
                } else {
                        /* We can't set up this call, so give up */
                        if (p->owner && !req->ignore)