Improve T.38 negotiation by exchanging session parameters between application and...
authorJoshua Colp <jcolp@digium.com>
Fri, 26 Jun 2009 19:27:24 +0000 (19:27 +0000)
committerJoshua Colp <jcolp@digium.com>
Fri, 26 Jun 2009 19:27:24 +0000 (19:27 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@203699 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_fax.c
channels/chan_sip.c
configs/sip.conf.sample
include/asterisk/frame.h
main/channel.c
main/frame.c
main/rtp_engine.c

index 951580d..7c35d84 100644 (file)
@@ -161,7 +161,7 @@ typedef struct {
        int direction;                  /* Fax direction: 0 - receiving, 1 - sending */
        int caller_mode;
        char *file_name;
-       
+       struct ast_control_t38_parameters t38parameters;
        volatile int finished;
 } fax_session;
 
@@ -372,7 +372,6 @@ static int transmit_audio(fax_session *s)
        struct ast_frame *fr;
        int last_state = 0;
        struct timeval now, start, state_change;
-       enum ast_control_t38 t38control;
 
 #if SPANDSP_RELEASE_DATE >= 20080725
         /* for spandsp shaphots 0.0.6 and higher */
@@ -456,9 +455,16 @@ static int transmit_audio(fax_session *s)
                        /* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */
                        fr = ast_dsp_process(NULL, dsp, fr);
                        if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') {
+                               struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_NEGOTIATE,
+                                                                                .version = 0,
+                                                                                .max_datagram = 400,
+                                                                                .rate = AST_T38_RATE_9600,
+                                                                                .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF,
+                                                                                .fill_bit_removal = 1,
+                                                                                .transcoding_mmr = 1,
+                               };
                                ast_debug(1, "Fax tone detected. Requesting T38\n");
-                               t38control = AST_T38_REQUEST_NEGOTIATE;
-                               ast_indicate_data(s->chan, AST_CONTROL_T38, &t38control, sizeof(t38control));
+                               ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
                                detect_tone = 0;
                        }
 
@@ -483,11 +489,11 @@ static int transmit_audio(fax_session *s)
                                state_change = ast_tvnow();
                                last_state = t30state->state;
                        }
-               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
-                               inf->datalen == sizeof(enum ast_control_t38)) {
-                       t38control =*((enum ast_control_t38 *) inf->data.ptr);
-                       if (t38control == AST_T38_NEGOTIATED) {
+               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
+                       struct ast_control_t38_parameters *parameters = inf->data.ptr;
+                       if (parameters->request_response == AST_T38_NEGOTIATED) {
                                /* T38 switchover completed */
+                               s->t38parameters = *parameters;
                                ast_debug(1, "T38 negotiated, finishing audio loop\n");
                                res = 1;
                                break;
@@ -548,8 +554,6 @@ static int transmit_t38(fax_session *s)
        struct ast_frame *inf = NULL;
        int last_state = 0;
        struct timeval now, start, state_change, last_frame;
-       enum ast_control_t38 t38control;
-
        t30_state_t *t30state;
        t38_core_state_t *t38state;
 
@@ -570,6 +574,17 @@ static int transmit_t38(fax_session *s)
                return -1;
        }
 
+       t38_set_max_datagram_size(t38state, s->t38parameters.max_datagram);
+
+       if (s->t38parameters.fill_bit_removal) {
+               t38_set_fill_bit_removal(t38state, TRUE);
+       }
+       if (s->t38parameters.transcoding_mmr) {
+               t38_set_mmr_transcoding(t38state, TRUE);
+       } else if (s->t38parameters.transcoding_jbig) {
+               t38_set_jbig_transcoding(t38state, TRUE);
+       }
+
        /* Setup logging */
        set_logging(&t38.logging);
        set_logging(&t30state->logging);
@@ -613,12 +628,9 @@ static int transmit_t38(fax_session *s)
                                state_change = ast_tvnow();
                                last_state = t30state->state;
                        }
-               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
-                               inf->datalen == sizeof(enum ast_control_t38)) {
-
-                       t38control = *((enum ast_control_t38 *) inf->data.ptr);
-
-                       if (t38control == AST_T38_TERMINATED || t38control == AST_T38_REFUSED) {
+               } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
+                       struct ast_control_t38_parameters *parameters = inf->data.ptr;
+                       if (parameters->request_response == AST_T38_TERMINATED || parameters->request_response == AST_T38_REFUSED) {
                                ast_debug(1, "T38 down, terminating\n");
                                res = -1;
                                break;
@@ -709,7 +721,7 @@ static int sndfax_exec(struct ast_channel *chan, const char *data)
 {
        int res = 0;
        char *parse;
-       fax_session session;
+       fax_session session = { 0, };
        char restore_digit_detect = 0;
 
        AST_DECLARE_APP_ARGS(args,
index 00042eb..ecd04a6 100644 (file)
@@ -1512,10 +1512,10 @@ struct sip_auth {
 #define SIP_PAGE2_SUBSCRIBEMWIONLY     (1 << 18)       /*!< GP: Only issue MWI notification if subscribed to */
 #define SIP_PAGE2_IGNORESDPVERSION     (1 << 19)       /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */
 
-#define SIP_PAGE2_T38SUPPORT           (7 << 20)       /*!< GDP: T38 Fax Passthrough Support */
-#define SIP_PAGE2_T38SUPPORT_UDPTL     (1 << 20)       /*!< GDP: T38 Fax Passthrough Support */
-#define SIP_PAGE2_T38SUPPORT_RTP       (2 << 20)       /*!< GDP: T38 Fax Passthrough Support (not implemented) */
-#define SIP_PAGE2_T38SUPPORT_TCP       (4 << 20)       /*!< GDP: T38 Fax Passthrough Support (not implemented) */
+#define SIP_PAGE2_T38SUPPORT                   (7 << 20)       /*!< GDP: T38 Fax Passthrough Support */
+#define SIP_PAGE2_T38SUPPORT_UDPTL             (1 << 20)       /*!< GDP: T38 Fax Passthrough Support (no error correction) */
+#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC         (2 << 20)       /*!< GDP: T38 Fax Passthrough Support (FEC error correction) */
+#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY  (4 << 20)       /*!< GDP: T38 Fax Passthrough Support (redundancy error correction) */
 
 #define SIP_PAGE2_CALL_ONHOLD          (3 << 23)       /*!< D: Call hold states: */
 #define SIP_PAGE2_CALL_ONHOLD_ACTIVE    (1 << 23)       /*!< D: Active hold */
@@ -1593,7 +1593,6 @@ static int sipdebug_text;
 enum t38state {
        T38_DISABLED = 0,                /*!< Not enabled */
        T38_LOCAL_REINVITE,              /*!< Offered from local - REINVITE */
-       T38_PEER_DIRECT,                 /*!< Offered from peer */
        T38_PEER_REINVITE,               /*!< Offered from peer - REINVITE */
        T38_ENABLED                      /*!< Negotiated (enabled) */
 };
@@ -1605,7 +1604,6 @@ 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 */
@@ -4162,7 +4160,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
                if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) {
                        switch (p->t38.state) {
                        case T38_LOCAL_REINVITE:
-                       case T38_PEER_DIRECT:
                        case T38_PEER_REINVITE:
                                state = T38_STATE_NEGOTIATING;
                                break;
@@ -4888,21 +4885,62 @@ static void do_setnat(struct sip_pvt *p, int natflags)
        }
 }
 
+/*! \brief Helper function which interprets T.38 capabilities and fills a parameters structure in */
+static void fill_t38_parameters(int capabilities, struct ast_control_t38_parameters *parameters, struct sip_pvt *p)
+{
+       if (capabilities & T38FAX_VERSION_0) {
+               parameters->version = 0;
+       } else if (capabilities & T38FAX_VERSION_1) {
+               parameters->version = 1;
+       }
+
+       if (capabilities & T38FAX_RATE_14400) {
+               parameters->rate = AST_T38_RATE_14400;
+       } else if (capabilities & T38FAX_RATE_12000) {
+               parameters->rate = AST_T38_RATE_12000;
+       } else if (capabilities & T38FAX_RATE_9600) {
+               parameters->rate = AST_T38_RATE_9600;
+       } else if (capabilities & T38FAX_RATE_7200) {
+               parameters->rate = AST_T38_RATE_7200;
+       } else if (capabilities & T38FAX_RATE_4800) {
+               parameters->rate = AST_T38_RATE_4800;
+       } else if (capabilities & T38FAX_RATE_2400) {
+               parameters->rate = AST_T38_RATE_2400;
+       }
+
+       if (capabilities & T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF) {
+               parameters->rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF;
+       } else if (capabilities & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) {
+               parameters->rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
+       }
+
+       if (capabilities & T38FAX_FILL_BIT_REMOVAL) {
+               parameters->fill_bit_removal = 1;
+       }
+
+       if (capabilities & T38FAX_TRANSCODING_MMR) {
+               parameters->transcoding_mmr = 1;
+       }
+
+       if (capabilities & T38FAX_TRANSCODING_JBIG) {
+               parameters->transcoding_jbig = 1;
+       }
+
+       parameters->max_datagram = ast_udptl_get_far_max_datagram(p->udptl);
+}
+
 /*! \brief Change the T38 state on a SIP dialog */
 static void change_t38_state(struct sip_pvt *p, int state)
 {
        int old = p->t38.state;
        struct ast_channel *chan = p->owner;
        enum ast_control_t38 message = 0;
+       struct ast_control_t38_parameters parameters = { 0, };
 
        /* Don't bother changing if we are already in the state wanted */
        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>");
 
@@ -4911,16 +4949,20 @@ static void change_t38_state(struct sip_pvt *p, int state)
                return;
 
        /* Given the state requested and old state determine what control frame we want to queue up */
-       if (state == T38_PEER_REINVITE)
-               message = AST_T38_REQUEST_NEGOTIATE;
-       else if (state == T38_ENABLED)
-               message = AST_T38_NEGOTIATED;
-       else if (state == T38_DISABLED && old == T38_ENABLED)
-               message = AST_T38_TERMINATED;
+       if (state == T38_PEER_REINVITE) {
+               message = parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
+               fill_t38_parameters(p->t38.peercapability, &parameters, p);
+       } else if (state == T38_ENABLED) {
+               message = parameters.request_response = AST_T38_NEGOTIATED;
+               fill_t38_parameters(p->t38.jointcapability, &parameters, p);
+       } else if (state == T38_DISABLED && old == T38_ENABLED)
+               message = parameters.request_response = AST_T38_TERMINATED;
        else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE)
-               message = AST_T38_REFUSED;
+               message = parameters.request_response = AST_T38_REFUSED;
 
        /* Woot we got a message, create a control frame and send it on! */
+       if (parameters.request_response)
+               ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
        if (message)
                ast_queue_control_data(chan, AST_CONTROL_T38, &message, sizeof(message));
 
@@ -4952,12 +4994,16 @@ static void set_t38_capabilities(struct sip_pvt *p)
 {
        p->t38.capability = global_t38_capability;
        if (p->udptl) {
-               if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC )
-                       p->t38.capability |= T38FAX_UDP_EC_FEC;
-               else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
+               if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
+                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
                        p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
-               else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE )
+               } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
+                       ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
+                       p->t38.capability |= T38FAX_UDP_EC_FEC;
+               } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
+                       ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
                        p->t38.capability |= T38FAX_UDP_EC_NONE;
+               }
                p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
        }
 }
@@ -6135,9 +6181,6 @@ 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);
-               }
                ast_rtp_instance_new_source(p->rtp);
                res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE, TRUE);
                ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
@@ -6177,7 +6220,7 @@ 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, FALSE);
                                        ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
-                               } else if (p->t38.state == T38_ENABLED && !p->t38.direct) {
+                               } else if (p->t38.state == T38_ENABLED) {
                                        change_t38_state(p, T38_DISABLED);
                                        transmit_reinvite_with_sdp(p, FALSE, FALSE);
                                } else {
@@ -6375,6 +6418,90 @@ static int sip_transfer(struct ast_channel *ast, const char *dest)
        return res;
 }
 
+/*! \brief Helper function which updates T.38 capability information and triggers a reinvite */
+static void interpret_t38_parameters(struct sip_pvt *p, enum ast_control_t38 request_response, const struct ast_control_t38_parameters *parameters)
+{
+       if (parameters) {
+               if (!parameters->version) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_0;
+               } else if (parameters->version == 1) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_1;
+               }
+
+               if (parameters->rate == AST_T38_RATE_14400) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+               } else if (parameters->rate == AST_T38_RATE_12000) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+               } else if (parameters->rate == AST_T38_RATE_9600) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+               } else if (parameters->rate == AST_T38_RATE_7200) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+               } else if (parameters->rate == AST_T38_RATE_4800) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
+               } else if (parameters->rate == AST_T38_RATE_2400) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_2400;
+               }
+
+               if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
+               } else if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_LOCAL_TCF) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
+               }
+
+               if (parameters->fill_bit_removal) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_FILL_BIT_REMOVAL;
+               } else {
+                       p->t38.capability = p->t38.jointcapability &= ~T38FAX_FILL_BIT_REMOVAL;
+               }
+
+               if (parameters->transcoding_mmr) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_MMR;
+               } else {
+                       p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_MMR;
+               }
+
+               if (parameters->transcoding_jbig) {
+                       p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_JBIG;
+               } else {
+                       p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_JBIG;
+               }
+
+               if (p->udptl && request_response == AST_T38_REQUEST_NEGOTIATE) {
+                       ast_udptl_set_local_max_datagram(p->udptl, parameters->max_datagram ? parameters->max_datagram : 400);
+               }
+       }
+
+       switch (request_response) {
+       case AST_T38_NEGOTIATED:
+       case AST_T38_REQUEST_NEGOTIATE:         /* Request T38 */
+               if (p->t38.state == T38_PEER_REINVITE) {
+                       AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
+                       change_t38_state(p, T38_ENABLED);
+                       transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
+               } else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) {
+                       change_t38_state(p, T38_LOCAL_REINVITE);
+                       if (!p->pendinginvite) {
+                               transmit_reinvite_with_sdp(p, TRUE, FALSE);
+                       } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
+                               ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
+                       }
+               }
+               break;
+       case AST_T38_TERMINATED:
+       case AST_T38_REFUSED:
+       case AST_T38_REQUEST_TERMINATE:         /* Shutdown T38 */
+               if (p->t38.state == T38_PEER_REINVITE) {
+                       AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
+                       change_t38_state(p, T38_DISABLED);
+                       transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
+               } else if (p->t38.state == T38_ENABLED)
+                       transmit_reinvite_with_sdp(p, FALSE, FALSE);
+               break;
+       default:
+               break;
+       }
+}
+
 /*! \brief Play indication to user 
  * With SIP a lot of indications is sent as messages, letting the device play
    the indication - busy signal, congestion etc 
@@ -6463,35 +6590,15 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
                if (datalen != sizeof(enum ast_control_t38)) {
                        ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. Expected %d, got %d\n", (int)sizeof(enum ast_control_t38), (int)datalen);
                } else {
-                       switch (*((enum ast_control_t38 *) data)) {
-                       case AST_T38_NEGOTIATED:
-                       case AST_T38_REQUEST_NEGOTIATE:         /* Request T38 */
-                               if (p->t38.state == T38_PEER_REINVITE) {
-                                       AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
-                                       change_t38_state(p, T38_ENABLED);
-                                       transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
-                               } else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) {
-                                       change_t38_state(p, T38_LOCAL_REINVITE);
-                                       if (!p->pendinginvite) {
-                                               transmit_reinvite_with_sdp(p, TRUE, FALSE);
-                                       } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
-                                               ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
-                                       }
-                               }
-                               break;
-                       case AST_T38_TERMINATED:
-                       case AST_T38_REFUSED:
-                       case AST_T38_REQUEST_TERMINATE:         /* Shutdown T38 */
-                               if (p->t38.state == T38_PEER_REINVITE) {
-                                       AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
-                                       change_t38_state(p, T38_DISABLED);
-                                       transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
-                               } else if (p->t38.state == T38_ENABLED)
-                                       transmit_reinvite_with_sdp(p, FALSE, FALSE);
-                               break;
-                       default:
-                               break;
-                       }
+                       interpret_t38_parameters(p, *((enum ast_control_t38 *) data), NULL);
+               }
+               break;
+       case AST_CONTROL_T38_PARAMETERS:
+               if (datalen != sizeof(struct ast_control_t38_parameters)) {
+                       ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int)sizeof(struct ast_control_t38_parameters), (int)datalen);
+               } else {
+                       const struct ast_control_t38_parameters *parameters = data;
+                       interpret_t38_parameters(p, parameters->request_response, parameters);
                }
                break;
        case AST_CONTROL_SRCUPDATE:
@@ -6915,7 +7022,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
        if (f && p->dsp) {
                f = ast_dsp_process(p->owner, p->dsp, f);
                if (f && f->frametype == AST_FRAME_DTMF) {
-                       if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') {
+                       if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && f->subclass == 'f') {
                                ast_debug(1, "Fax CNG detected on %s\n", ast->name);
                                *faxdetect = 1;
                        } else {
@@ -6940,7 +7047,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
 
        /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */
        /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */
-       if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
+       if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
                if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
                        if (!p->pendinginvite) {
                                ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n", ast->name);
@@ -8449,7 +8556,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                found = 1;
                                ast_debug(3, "FaxMaxDatagram: %d\n", x);
                                ast_udptl_set_far_max_datagram(p->udptl, x);
-                               ast_udptl_set_local_max_datagram(p->udptl, x);
+                               if (!ast_udptl_get_local_max_datagram(p->udptl)) {
+                                       ast_udptl_set_local_max_datagram(p->udptl, x);
+                               }
                        } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) {
                                found = 1;
                                if(sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1) {
@@ -8521,8 +8630,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                } else if (t38action == SDP_T38_INITIATE) {
                        if (p->owner && p->lastinvite) {
                                change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
-                       } else {
-                               change_t38_state(p, T38_PEER_DIRECT); /* T38 Offered directly from peer in first invite */
                        }
                }
        } else {
@@ -9870,13 +9977,6 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                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);
-               }
-#endif
 
                /* Check if we need audio */
                if (capability & AST_FORMAT_AUDIO_MASK)
@@ -10075,8 +10175,10 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                x = ast_udptl_get_local_max_datagram(p->udptl);
                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 (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY)
+                       ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n");
+               else if (p->t38.jointcapability & T38FAX_UDP_EC_FEC)
+                       ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPFEC\r\n");
        }
 
        if (needaudio)
@@ -10202,7 +10304,7 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
                        ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs);
                }
                try_suggested_sip_codec(p);
-               if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) {
+               if (p->t38.state == T38_ENABLED) {
                        add_sdp(&resp, p, oldsdp, TRUE, TRUE);
                } else {
                        add_sdp(&resp, p, oldsdp, TRUE, FALSE);
@@ -13952,6 +14054,11 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
        ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
        ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
 
+       if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) {
+               set_t38_capabilities(p);
+               p->t38.jointcapability = p->t38.capability;
+       }
+
        /* Copy SIP extensions profile to peer */
        /* XXX is this correct before a successful auth ? */
        if (p->sipoptions)
@@ -15390,11 +15497,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                ast_cli(fd, "  Insecure     : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
                ast_cli(fd, "  Nat          : %s\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT)));
                ast_cli(fd, "  ACL          : %s\n", cli_yesno(peer->ha != NULL));
-               ast_cli(fd, "  T38 pt UDPTL : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL)));
-#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
-               ast_cli(fd, "  T38 pt RTP   : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_RTP)));
-               ast_cli(fd, "  T38 pt TCP   : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_TCP)));
-#endif
+               ast_cli(fd, "  T38 pt UDPTL : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
                ast_cli(fd, "  CanReinvite  : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE)));
                ast_cli(fd, "  PromiscRedir : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)));
                ast_cli(fd, "  User=Phone   : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)));
@@ -15948,11 +16051,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
        ast_cli(a->fd, "  Call Events:            %s\n", sip_cfg.callevents ? "On" : "Off");
        ast_cli(a->fd, "  Auth. Failure Events:   %s\n", global_authfailureevents ? "On" : "Off");
 
-       ast_cli(a->fd, "  T38 fax pt UDPTL:       %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_UDPTL)));
-#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
-       ast_cli(a->fd, "  T38 fax pt RTP:         %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_RTP)));
-       ast_cli(a->fd, "  T38 fax pt TCP:         %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_TCP)));
-#endif
+       ast_cli(a->fd, "  T38 fax pt UDPTL:       %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT)));
        if (!realtimepeers && !realtimeregs)
                ast_cli(a->fd, "  SIP realtime:           Disabled\n" );
        else
@@ -20236,6 +20335,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && !p->udptl && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) {
                        set_t38_capabilities(p);
                        p->t38.jointcapability = p->t38.capability;
+                       set_t38_capabilities(p);
+                       p->t38.jointcapability = p->t38.capability;
                }
 
                /* We have a succesful authentication, process the SDP portion if there is one */
@@ -23587,16 +23688,24 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
                ast_set_flag(&mask[1], SIP_PAGE2_FAX_DETECT);
                ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_FAX_DETECT);
        } else if (!strcasecmp(v->name, "t38pt_udptl")) {
-               ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL);
-               ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL);
-#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
-       } else if (!strcasecmp(v->name, "t38pt_rtp")) {
-               ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP);
-               ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP);
-       } else if (!strcasecmp(v->name, "t38pt_tcp")) {
-               ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP);
-               ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP);
-#endif
+               char buf[16], *word, *next = buf;
+
+               ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT);
+
+               ast_copy_string(buf, v->value, sizeof(buf));
+
+               while ((word = strsep(&next, ","))) {
+                       if (ast_true(word) || !strcasecmp(word, "fec")) {
+                               ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
+                               ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_FEC);
+                       } else if (!strcasecmp(word, "redundancy")) {
+                               ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
+                               ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY);
+                       } else if (!strcasecmp(word, "none")) {
+                               ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
+                               ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
+                       }
+               }
        } else if (!strcasecmp(v->name, "rfc2833compensate")) {
                ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
                ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
index 08089d9..245f871 100644 (file)
@@ -463,11 +463,14 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ; This has to be enabled in the general section for all devices to work. You can then
 ; disable it on a per device basis.
 ;
-; T.38 faxing only works in SIP to SIP calls, with no local or agent channel being used.
+; T.38 faxing only works in SIP to SIP calls. It defaults to off.
 ;
-; t38pt_udptl = yes            ; Default false
+; t38pt_udptl = yes            ; Enables T.38 with FEC error correction.
+; t38pt_udptl = yes,fec        ; Enables T.38 with FEC error correction.
+; t38pt_udptl = yes,redundancy ; Enables T.38 with redundancy error correction.
+; t38pt_udptl = yes,none       ; Enables T.38 with no error correction.
 ;
-; Fax Detect will cause the SIP channel to jump to the 'fax' extension (if it exists)
+; Faxs Detect will cause the SIP channel to jump to the 'fax' extension (if it exists)
 ; after T.38 is successfully negotiated.
 ;
 ; faxdetect = yes              ; Default false
index 8b79a5e..56d76d9 100644 (file)
@@ -323,7 +323,8 @@ enum ast_control_frame_type {
        AST_CONTROL_SRCUPDATE = 20,     /*!< Indicate source of media has changed */
        AST_CONTROL_TRANSFER = 21,      /*!< Indicate status of a transfer request */
        AST_CONTROL_CONNECTED_LINE = 22,/*!< Indicate connected line has changed */
-       AST_CONTROL_REDIRECTING = 23    /*!< Indicate redirecting id has changed */
+       AST_CONTROL_REDIRECTING = 23,    /*!< Indicate redirecting id has changed */
+       AST_CONTROL_T38_PARAMETERS = 24, /*! T38 state change request/notification with parameters */
 };
 
 enum ast_control_t38 {
@@ -334,6 +335,31 @@ enum ast_control_t38 {
        AST_T38_REFUSED                 /*!< T38 refused for some reason (usually rejected by remote end) */
 };
 
+enum ast_control_t38_rate {
+       AST_T38_RATE_2400 = 0,
+       AST_T38_RATE_4800,
+       AST_T38_RATE_7200,
+       AST_T38_RATE_9600,
+       AST_T38_RATE_12000,
+       AST_T38_RATE_14400,
+};
+
+enum ast_control_t38_rate_management {
+       AST_T38_RATE_MANAGEMENT_TRANSFERED_TCF = 0,
+       AST_T38_RATE_MANAGEMENT_LOCAL_TCF,
+};
+
+struct ast_control_t38_parameters {
+       enum ast_control_t38 request_response;                /*!< Request or response of the T38 control frame */
+       unsigned int version;                                 /*!< Supported T.38 version */
+       unsigned int max_datagram;                            /*!< Maximum datagram size supported */
+       enum ast_control_t38_rate rate;                       /*!< Maximum fax rate supported */
+       enum ast_control_t38_rate_management rate_management; /*!< Rate management setting */
+       unsigned int fill_bit_removal:1;                      /*!< Set if fill bit removal should be used */
+       unsigned int transcoding_mmr:1;                       /*!< Set if MMR transcoding should be used */
+       unsigned int transcoding_jbig:1;                      /*!< Set if JBIG transcoding should be used */
+};
+
 enum ast_control_transfer {
        AST_TRANSFER_SUCCESS = 0, /*!< Transfer request on the channel worked */
        AST_TRANSFER_FAILED,      /*!< Transfer request on the channel failed */
index 62377f8..f33de43 100644 (file)
@@ -3483,6 +3483,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
        case AST_CONTROL_CONNECTED_LINE:
        case AST_CONTROL_REDIRECTING:
        case AST_CONTROL_TRANSFER:
+       case AST_CONTROL_T38_PARAMETERS:
                break;
 
        case AST_CONTROL_CONGESTION:
@@ -3613,6 +3614,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        case AST_CONTROL_TRANSFER:
        case AST_CONTROL_CONNECTED_LINE:
        case AST_CONTROL_REDIRECTING:
+       case AST_CONTROL_T38_PARAMETERS:
                /* Nothing left to do for these. */
                res = 0;
                break;
index e305580..09c11db 100644 (file)
@@ -857,6 +857,25 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
                        }
                        snprintf(subclass, sizeof(subclass), "T38/%s", message);
                        break;
+               case AST_CONTROL_T38_PARAMETERS:
+                       if (f->datalen != sizeof(struct ast_control_t38_parameters *)) {
+                               message = "Invalid";
+                       } else {
+                               struct ast_control_t38_parameters *parameters = f->data.ptr;
+                               enum ast_control_t38 state = parameters->request_response;
+                               if (state == AST_T38_REQUEST_NEGOTIATE)
+                                       message = "Negotiation Requested";
+                               else if (state == AST_T38_REQUEST_TERMINATE)
+                                       message = "Negotiation Request Terminated";
+                               else if (state == AST_T38_NEGOTIATED)
+                                       message = "Negotiated";
+                               else if (state == AST_T38_TERMINATED)
+                                       message = "Terminated";
+                               else if (state == AST_T38_REFUSED)
+                                       message = "Refused";
+                       }
+                       snprintf(subclass, sizeof(subclass), "T38_Parameters/%s", message);
+                       break;
                case -1:
                        strcpy(subclass, "Stop generators");
                        break;
index ad8544c..138b7c0 100644 (file)
@@ -854,7 +854,8 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
                            (fr->subclass == AST_CONTROL_UNHOLD) ||
                            (fr->subclass == AST_CONTROL_VIDUPDATE) ||
                            (fr->subclass == AST_CONTROL_T38) ||
-                           (fr->subclass == AST_CONTROL_SRCUPDATE)) {
+                           (fr->subclass == AST_CONTROL_SRCUPDATE) ||
+                           (fr->subclass == AST_CONTROL_T38_PARAMETERS)) {
                                /* If we are going on hold, then break callback mode and P2P bridging */
                                if (fr->subclass == AST_CONTROL_HOLD) {
                                        if (instance0->engine->local_bridge) {
@@ -1073,7 +1074,8 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct
                            (fr->subclass == AST_CONTROL_UNHOLD) ||
                            (fr->subclass == AST_CONTROL_VIDUPDATE) ||
                            (fr->subclass == AST_CONTROL_T38) ||
-                           (fr->subclass == AST_CONTROL_SRCUPDATE)) {
+                           (fr->subclass == AST_CONTROL_SRCUPDATE) ||
+                           (fr->subclass == AST_CONTROL_T38_PARAMETERS)) {
                                if (fr->subclass == AST_CONTROL_HOLD) {
                                        /* If we someone went on hold we want the other side to reinvite back to us */
                                        if (who == c0) {