]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Tea for two (or maybe thirty eight?)
authorAnthony Minessale <anthm@freeswitch.org>
Thu, 13 May 2010 02:25:54 +0000 (21:25 -0500)
committerBrian West <brian@freeswitch.org>
Fri, 21 May 2010 21:47:21 +0000 (16:47 -0500)
17 files changed:
configure.in
src/include/switch_rtp.h
src/include/switch_types.h
src/mod/.gitignore
src/mod/applications/mod_fax/Makefile [deleted file]
src/mod/applications/mod_fax/Makefile.am [new file with mode: 0644]
src/mod/applications/mod_fax/mod_fax.c
src/mod/applications/mod_fax/udptl.c [new file with mode: 0644]
src/mod/applications/mod_fax/udptl.h [new file with mode: 0644]
src/mod/applications/mod_t38gateway/Makefile.am
src/mod/applications/mod_t38gateway/mod_t38gateway.c
src/mod/endpoints/mod_sofia/mod_sofia.c
src/mod/endpoints/mod_sofia/mod_sofia.h
src/mod/endpoints/mod_sofia/sofia.c
src/mod/endpoints/mod_sofia/sofia_glue.c
src/switch_core_session.c
src/switch_rtp.c

index 838c1087a300dbd2d797d6d0052f090e192eb789..b19303d6df63edde639a91dbce68368e56ec993f 100644 (file)
@@ -869,32 +869,33 @@ fi
 CHECK_ERLANG
 
 AC_CONFIG_FILES([Makefile
-                build/Makefile
-                src/Makefile
-                src/mod/Makefile
-                src/mod/applications/mod_enum/Makefile
+               build/Makefile
+               src/Makefile
+               src/mod/Makefile
+               src/mod/applications/mod_enum/Makefile
                src/mod/applications/mod_expr/Makefile
+               src/mod/applications/mod_fax/Makefile
                src/mod/applications/mod_stress/Makefile
                src/mod/applications/mod_t38gateway/Makefile
                src/mod/endpoints/mod_portaudio/Makefile
                src/mod/endpoints/mod_skinny/Makefile
                src/mod/endpoints/mod_skypopen/Makefile
-                src/mod/endpoints/mod_sofia/Makefile
+               src/mod/endpoints/mod_sofia/Makefile
                src/mod/formats/mod_portaudio_stream/Makefile
-                src/mod/asr_tts/mod_unimrcp/Makefile
-                src/mod/languages/mod_java/Makefile
+               src/mod/asr_tts/mod_unimrcp/Makefile
+               src/mod/languages/mod_java/Makefile
                src/mod/languages/mod_lua/Makefile
-                src/mod/languages/mod_python/Makefile
+               src/mod/languages/mod_python/Makefile
                src/mod/languages/mod_spidermonkey/Makefile
-                src/mod/event_handlers/mod_erlang_event/Makefile
-                src/include/switch_am_config.h
-                build/getsounds.sh
-                build/getlib.sh
+               src/mod/event_handlers/mod_erlang_event/Makefile
+               src/include/switch_am_config.h
+               build/getsounds.sh
+               build/getlib.sh
                build/freeswitch.pc
-                build/modmake.rules
-                libs/xmlrpc-c/include/xmlrpc-c/config.h
-                libs/xmlrpc-c/xmlrpc_config.h
-                scripts/gentls_cert])
+               build/modmake.rules
+               libs/xmlrpc-c/include/xmlrpc-c/config.h
+               libs/xmlrpc-c/xmlrpc_config.h
+               scripts/gentls_cert])
 
 AM_CONDITIONAL(ISLINUX, [test `uname -s` = Linux])
 AM_CONDITIONAL(ISMAC, [test `uname -s` = Darwin])
index 8e0693cdc17ae65ce94659abc7119ad475a87edf..7e6028a68bb6c591647d3e03a3eee5b054acebaf 100644 (file)
@@ -176,6 +176,8 @@ SWITCH_DECLARE(switch_port_t) switch_rtp_get_remote_port(switch_rtp_t *rtp_sessi
 SWITCH_DECLARE(void) switch_rtp_reset_media_timer(switch_rtp_t *rtp_session);
 SWITCH_DECLARE(void) switch_rtp_set_max_missed_packets(switch_rtp_t *rtp_session, uint32_t max);
 
+SWITCH_DECLARE(switch_status_t) switch_rtp_udptl_mode(switch_rtp_t *rtp_session);
+
 /*! 
   \brief Assign a local address to the RTP session
   \param rtp_session an RTP session to assign the local address to
index a3a9599b62228faf500fc4ef322321e0b9489b45..0467895e8cc57ce5977ab4a3ce5d9b67a51b71a6 100644 (file)
@@ -499,7 +499,6 @@ typedef enum {
        SWITCH_RTP_FLAG_GOOGLEHACK    - Convert payload from 102 to 97
        SWITCH_RTP_FLAG_VAD           - Enable VAD
        SWITCH_RTP_FLAG_BREAK             - Stop what you are doing and return SWITCH_STATUS_BREAK
-       SWITCH_RTP_FLAG_MINI              - Use mini RTP when possible
        SWITCH_RTP_FLAG_DATAWAIT          - Do not return from reads unless there is data even when non blocking
        SWITCH_RTP_FLAG_BUGGY_2833    - Emulate the bug in cisco equipment to allow interop
        SWITCH_RTP_FLAG_PASS_RFC2833  - Pass 2833 (ignore it)
@@ -518,7 +517,7 @@ typedef enum {
        SWITCH_RTP_FLAG_GOOGLEHACK = (1 << 8),
        SWITCH_RTP_FLAG_VAD = (1 << 9),
        SWITCH_RTP_FLAG_BREAK = (1 << 10),
-       SWITCH_RTP_FLAG_MINI = (1 << 11),
+       SWITCH_RTP_FLAG_UDPTL = (1 << 11),
        SWITCH_RTP_FLAG_DATAWAIT = (1 << 12),
        SWITCH_RTP_FLAG_BUGGY_2833 = (1 << 13),
        SWITCH_RTP_FLAG_PASS_RFC2833 = (1 << 14),
@@ -725,10 +724,13 @@ typedef enum {
        SWITCH_MESSAGE_INDICATE_APPLICATION_EXEC,
        SWITCH_MESSAGE_INDICATE_APPLICATION_EXEC_COMPLETE,
        SWITCH_MESSAGE_INDICATE_PHONE_EVENT,
+       SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION,
+       SWITCH_MESSAGE_INDICATE_UDPTL_MODE,
        SWITCH_MESSAGE_INVALID
 } switch_core_session_message_types_t;
 
 typedef struct {
+       uint16_t T38FaxVersion;
        uint32_t T38MaxBitRate;
        switch_bool_t T38FaxFillBitRemoval;
        switch_bool_t T38FaxTranscodingMMR;
@@ -1008,7 +1010,8 @@ typedef enum {
 
 
 typedef enum {
-       CF_APP_TAGGED = (1 << 0)
+       CF_APP_TAGGED = (1 << 0),
+       CF_APP_T38 = (1 << 1)
 } switch_channel_app_flag_t;
 
 
@@ -1034,7 +1037,8 @@ typedef enum {
        SFF_RFC2833 = (1 << 4),
        SFF_PROXY_PACKET = (1 << 5),
        SFF_DYNAMIC = (1 << 6),
-       SFF_ZRTP = (1 << 7)
+       SFF_ZRTP = (1 << 7),
+       SFF_UDPTL_PACKET = (1 << 8)
 } switch_frame_flag_enum_t;
 typedef uint32_t switch_frame_flag_t;
 
index 9961a5410498c0a43ca1e4bccc8b75500806ee5b..e26a2619d6878a3e8dd4092baa0277a166a06ea1 100644 (file)
@@ -1,5 +1,7 @@
 /Makefile
 /Makefile.in
+/applications/mod_fax/Makefile
+/applications/mod_fax/Makefile.in
 /applications/mod_commands/Makefile
 /applications/mod_conference/Makefile
 /applications/mod_dptools/Makefile
diff --git a/src/mod/applications/mod_fax/Makefile b/src/mod/applications/mod_fax/Makefile
deleted file mode 100644 (file)
index d4609e8..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-BASE=../../../..
-
-TIFF_DIR=$(switch_srcdir)/libs/tiff-3.8.2
-TIFF_BUILDDIR=$(switch_builddir)/libs/tiff-3.8.2
-TIFF_LA=$(TIFF_BUILDDIR)/libtiff/libtiff.la
-
-SPANDSP_DIR=$(switch_srcdir)/libs/spandsp
-SPANDSP_BUILDDIR=$(switch_builddir)/libs/spandsp
-SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la
-
-LOCAL_CFLAGS=-I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff
-LOCAL_LIBADD=$(SPANDSP_LA) $(TIFF_LA)
-LOCAL_LDFLAGS=-ljpeg
-
-include $(BASE)/build/modmake.rules
-$(MODNAME).lo: $(SPANDSP_LA) $(TIFF_LA)
-
-$(SPANDSP_LA): $(TIFF_LA) $(SPANDSP_DIR) $(SPANDSP_DIR)/.update
-       cd $(SPANDSP_BUILDDIR) && $(MAKE) -j1
-       $(TOUCH_TARGET)
-
-$(TIFF_LA): $(TIFF_DIR) $(TIFF_DIR)/.update
-       cd $(TIFF_BUILDDIR) && $(MAKE) -j1
-       $(TOUCH_TARGET)
-
-
diff --git a/src/mod/applications/mod_fax/Makefile.am b/src/mod/applications/mod_fax/Makefile.am
new file mode 100644 (file)
index 0000000..10babdd
--- /dev/null
@@ -0,0 +1,17 @@
+include $(top_srcdir)/build/modmake.rulesam
+MODNAME=mod_fax
+
+TIFF_DIR=$(switch_srcdir)/libs/tiff-3.8.2
+TIFF_BUILDDIR=$(switch_builddir)/libs/tiff-3.8.2
+TIFF_LA=$(TIFF_BUILDDIR)/libtiff/libtiff.la
+
+SPANDSP_DIR=$(switch_srcdir)/libs/spandsp
+SPANDSP_BUILDDIR=$(switch_builddir)/libs/spandsp
+SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la
+
+mod_LTLIBRARIES = mod_fax.la
+mod_fax_la_SOURCES  = mod_fax.c ../mod_t38gateway/udptl.c
+mod_fax_la_CFLAGS   = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff -I../mod_t38gateway
+mod_fax_la_LIBADD   = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA)
+mod_fax_la_LDFLAGS  = -avoid-version -module -no-undefined -shared -ljpeg
+
index 185a341d3a08f46c6d3bf5c39367f77edaa161a5..dbe018ab1fb85270d65d77f8d00e91d55386ae3d 100644 (file)
@@ -23,7 +23,7 @@
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- * 
+ *
  * Brian West <brian@freeswitch.org>
  * Anthony Minessale II <anthm@freeswitch.org>
  * Steve Underwood <steveu@coppice.org>
 #include <spandsp.h>
 #include <spandsp/version.h>
 
+#include "udptl.h"
+
+#define LOCAL_FAX_MAX_DATAGRAM      400
+#define MAX_FEC_ENTRIES             4
+#define MAX_FEC_SPAN                4
+
 /*****************************************************************************
        OUR DEFINES AND STRUCTS
 *****************************************************************************/
 
 typedef enum {
        FUNCTION_TX,
-       FUNCTION_RX
+       FUNCTION_RX,
+    FUNCTION_GW
 } application_mode_t;
 
 typedef enum {
        T38_MODE,
-       AUDIO_MODE
+       AUDIO_MODE,
+    T38_GATEWAY_MODE
 } transport_mode_t;
 
+typedef enum {
+    T38_MODE_UNKNOWN = 0,
+    T38_MODE_NEGOTIATED = 1,
+    T38_MODE_REQUESTED = 2,
+    T38_MODE_REFUSED = -1,
+} t38_mode_t;
 
 /* The global stuff */
 static struct {
@@ -63,6 +77,9 @@ static struct {
        short int use_ecm;
        short int verbose;
        short int disable_v17;
+    short int enable_t38;
+    short int enable_t38_request;
+    short int enable_t38_insist;
        char ident[20];
        char header[50];
        char *prepend_string;
@@ -76,6 +93,10 @@ struct pvt_s {
 
        fax_state_t *fax_state;
        t38_terminal_state_t *t38_state;
+       t38_gateway_state_t *t38_gateway_state;
+    t38_core_state_t *t38_core;
+
+    udptl_state_t *udptl_state;
 
        char *filename;
        char *ident;
@@ -90,15 +111,138 @@ struct pvt_s {
        int tx_page_end;
 
        int done;
+    
+    t38_mode_t t38_mode;
 
-       /* UNUSED AT THE MOMENT
-          int          enable_t38_reinvite;
-        */
-
+    struct pvt_s *next;
 };
 
 typedef struct pvt_s pvt_t;
 
+static void launch_timer_thread(void);
+
+static struct {
+    pvt_t *head;
+    switch_mutex_t *mutex;
+    switch_thread_t *thread;
+    int thread_running;
+} t38_state_list;
+
+static int add_pvt(pvt_t *pvt)
+{
+    int r = 0;
+    uint32_t sanity = 50;
+
+    switch_mutex_lock(t38_state_list.mutex);
+    if (!t38_state_list.thread_running) {
+
+        launch_timer_thread();
+
+        while(--sanity && !t38_state_list.thread_running) {
+            switch_yield(10000);
+        }
+    }
+    switch_mutex_unlock(t38_state_list.mutex);
+    
+    if (t38_state_list.thread_running) {
+        switch_mutex_lock(t38_state_list.mutex);
+        pvt->next = t38_state_list.head;
+        t38_state_list.head = pvt;
+        switch_mutex_unlock(t38_state_list.mutex);
+    }
+
+    return r;
+
+}
+
+
+static int del_pvt(pvt_t *del_pvt)
+{
+    pvt_t *p, *l = NULL;
+    int r = 0;
+
+    if (!t38_state_list.thread_running) goto end;
+    
+    switch_mutex_lock(t38_state_list.mutex);
+    for (p = t38_state_list.head; p; p = p->next) {
+        if (p == del_pvt) {
+            if (l) {
+                l->next = p->next;
+            } else {
+                t38_state_list.head = p->next;
+            }
+            p->next = NULL;
+            r = 1;
+            goto end;
+        }
+
+        l = p;
+    }
+
+ end:
+
+    switch_mutex_unlock(t38_state_list.mutex);
+
+    return r;
+
+}
+
+static void *SWITCH_THREAD_FUNC timer_thread_run(switch_thread_t *thread, void *obj)
+{
+    switch_timer_t timer = { 0 };
+    pvt_t *pvt;
+    int samples = 240;
+    int ms = 30;
+
+    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "timer thread started.\n");
+
+       if (switch_core_timer_init(&timer, "soft", ms, samples, NULL) != SWITCH_STATUS_SUCCESS) {
+        return NULL;
+    }
+
+    t38_state_list.thread_running = 1;
+
+    while(t38_state_list.thread_running) {
+
+        switch_mutex_lock(t38_state_list.mutex);
+
+        if (!t38_state_list.head) {
+            switch_mutex_unlock(t38_state_list.mutex);
+            goto end;
+        }
+
+        for (pvt = t38_state_list.head; pvt; pvt = pvt->next) {
+            if (pvt->udptl_state) {
+                t38_terminal_send_timeout(pvt->t38_state, samples);
+            }
+        }
+
+        switch_mutex_unlock(t38_state_list.mutex);
+
+        switch_core_timer_next(&timer);
+    }
+    
+ end:
+
+    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "timer thread ended.\n");
+
+    t38_state_list.thread_running = 0;
+    switch_core_timer_destroy(&timer);
+    
+    return NULL;
+}
+
+static void launch_timer_thread(void)
+{
+
+       switch_threadattr_t *thd_attr = NULL;
+
+       switch_threadattr_create(&thd_attr, globals.pool);
+       switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+       switch_thread_create(&t38_state_list.thread, thd_attr, timer_thread_run, NULL, globals.pool);
+}
+
+
 /*****************************************************************************
        LOGGING AND HELPER FUNCTIONS
 *****************************************************************************/
@@ -167,7 +311,6 @@ static void phase_e_handler(t30_state_t *s, void *user_data, int result)
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
 
        if (result == T30_ERR_OK) {
-
                if (pvt->app_mode == FUNCTION_TX) {
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax successfully sent.\n");
                } else if (pvt->app_mode == FUNCTION_RX) {
@@ -195,8 +338,7 @@ static void phase_e_handler(t30_state_t *s, void *user_data, int result)
        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "remote vendor:    %s\n", switch_str_nil(t30_get_rx_vendor(s)));
        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "remote model:     %s\n", switch_str_nil(t30_get_rx_model(s)));
 
-       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
-                                         "==============================================================================\n");
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "==============================================================================\n");
 
        /*
           Set our channel variables
@@ -259,14 +401,47 @@ static void phase_e_handler(t30_state_t *s, void *user_data, int result)
         */
 }
 
+static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
+{
+    switch_frame_t out_frame = { 0 };
+    switch_core_session_t *session;
+    switch_channel_t *channel;
+    pvt_t *pvt;
+    uint8_t pkt[LOCAL_FAX_MAX_DATAGRAM];
+    int x;
+    int r = 0;
+
+    pvt = (pvt_t *) user_data;
+    session = pvt->session;
+    channel = switch_core_session_get_channel(session);
+
+    /* we need to build a real packet here and make write_frame.packet and write_frame.packetlen point to it */
+    out_frame.flags = SFF_UDPTL_PACKET | SFF_PROXY_PACKET;
+    out_frame.packet = pkt;
+    out_frame.packetlen = udptl_build_packet(pvt->udptl_state, pkt, buf, len);
+    
+    //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "WRITE %d udptl bytes\n", out_frame.packetlen);
+
+    for (x = 0; x < count; x++) {
+        if (switch_core_session_write_frame(session, &out_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
+            r = -1;
+            break;
+        }
+    }
+
+    return r;
+}
+
 static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode)
 {
 
        switch_core_session_t *session;
        switch_channel_t *channel;
        fax_state_t *fax;
+       t38_terminal_state_t *t38;
        t30_state_t *t30;
 
+
        session = (switch_core_session_t *) pvt->session;
        switch_assert(session);
 
@@ -278,7 +453,8 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode)
        case AUDIO_MODE:
                if (pvt->fax_state == NULL) {
                        pvt->fax_state = (fax_state_t *) switch_core_session_alloc(pvt->session, sizeof(fax_state_t));
-               } else {
+               }
+               if (pvt->fax_state == NULL) {
                        return SWITCH_STATUS_FALSE;
                }
 
@@ -300,54 +476,152 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode)
                        span_log_set_level(&fax->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
                        span_log_set_level(&t30->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
                }
+               break;
+       case T38_MODE:
+               if (pvt->t38_state == NULL) {
+                       pvt->t38_state = (t38_terminal_state_t *) switch_core_session_alloc(pvt->session, sizeof(t38_terminal_state_t));
+               }
+               if (pvt->t38_state == NULL) {
+                       return SWITCH_STATUS_FALSE;
+               }
+               if (pvt->udptl_state == NULL) {
+            pvt->udptl_state = (udptl_state_t *) switch_core_session_alloc(pvt->session, sizeof(udptl_state_t));
+        }
+               if (pvt->udptl_state == NULL) {
+               t38_terminal_free(pvt->t38_state);
+            pvt->t38_state = NULL;
+                       return SWITCH_STATUS_FALSE;
+               }
 
-               t30_set_tx_ident(t30, pvt->ident);
-               t30_set_tx_page_header_info(t30, pvt->header);
+        /* add to timer thread processing */
+        add_pvt(pvt);
+        
+               t38 = pvt->t38_state;
+               t30 = t38_terminal_get_t30_state(t38);
 
-               t30_set_phase_e_handler(t30, phase_e_handler, pvt);
+               memset(t38, 0, sizeof(t38_terminal_state_t));
 
-               t30_set_supported_image_sizes(t30,
-                                                                         T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH
-                                                                         | T30_SUPPORT_215MM_WIDTH | T30_SUPPORT_255MM_WIDTH | T30_SUPPORT_303MM_WIDTH);
-               t30_set_supported_resolutions(t30,
-                                                                         T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION
-                                                                         | T30_SUPPORT_R8_RESOLUTION | T30_SUPPORT_R16_RESOLUTION);
+               if (t38_terminal_init(t38, pvt->caller, t38_tx_packet_handler, pvt) == NULL) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my T.38 structs\n");
+                       return SWITCH_STATUS_FALSE;
+               }
 
+        pvt->t38_core = t38_terminal_get_t38_core_state(pvt->t38_state);
 
-               if (pvt->disable_v17) {
-                       t30_set_supported_modems(t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER);
-                       switch_channel_set_variable(channel, "fax_v17_disabled", "1");
-               } else {
-                       t30_set_supported_modems(t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER | T30_SUPPORT_V17);
-                       switch_channel_set_variable(channel, "fax_v17_disabled", "0");
+        if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, 
+                       (udptl_rx_packet_handler_t *) t38_core_rx_ifp_packet, (void *) pvt->t38_core) == NULL) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my UDPTL structs\n");
+                       return SWITCH_STATUS_FALSE;
                }
 
-               if (pvt->use_ecm) {
-                       t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
-                       t30_set_ecm_capability(t30, TRUE);
-                       switch_channel_set_variable(channel, "fax_ecm_requested", "1");
-               } else {
-                       t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION);
-                       switch_channel_set_variable(channel, "fax_ecm_requested", "0");
-               }
+               span_log_set_message_handler(&t38->logging, spanfax_log_message);
+               span_log_set_message_handler(&t30->logging, spanfax_log_message);
 
-               if (pvt->app_mode == FUNCTION_TX) {
-                       t30_set_tx_file(t30, pvt->filename, pvt->tx_page_start, pvt->tx_page_end);
-               } else {
-                       t30_set_rx_file(t30, pvt->filename, -1);
+               if (pvt->verbose) {
+                       span_log_set_level(&t38->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+                       span_log_set_level(&t30->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
                }
-               switch_channel_set_variable(channel, "fax_filename", pvt->filename);
                break;
-       case T38_MODE:
-               /* 
-                  Here goes the T.38 SpanDSP initializing functions 
-                  T.38 will require a big effort as it needs a different approach
-                  but the pieces are already in place
-                */
+ case T38_GATEWAY_MODE:
+        if (pvt->t38_gateway_state == NULL) {
+                pvt->t38_gateway_state = (t38_gateway_state_t *) switch_core_session_alloc(pvt->session, sizeof(t38_gateway_state_t));
+        }
+
+        if (pvt->udptl_state == NULL) {
+                pvt->udptl_state = (udptl_state_t *) switch_core_session_alloc(pvt->session, sizeof(udptl_state_t));
+        }
+
+        if (t38_gateway_init(pvt->t38_gateway_state, t38_tx_packet_handler, pvt) == NULL) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my T.38 structs\n");
+                t38_gateway_free(pvt->t38_gateway_state);
+         pvt->t38_gateway_state = NULL;
+                
+                return SWITCH_STATUS_FALSE;
+        }
+
+        pvt->t38_core = t38_gateway_get_t38_core_state(pvt->t38_gateway_state);
+
+        if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, 
+                                       (udptl_rx_packet_handler_t *) t38_core_rx_ifp_packet, (void *) pvt->t38_core) == NULL) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my UDPTL structs\n");
+                t38_gateway_free(pvt->t38_gateway_state);
+                udptl_release(pvt->udptl_state);
+                pvt->udptl_state = NULL;
+                return SWITCH_STATUS_FALSE;
+        }
+
+        t38_gateway_set_transmit_on_idle(pvt->t38_gateway_state, TRUE);
+
+        if (switch_true(switch_channel_get_variable(channel, "fax_v17_disabled"))) {
+                t38_gateway_set_supported_modems(pvt->t38_gateway_state, T30_SUPPORT_V29 | T30_SUPPORT_V27TER);
+        } else {
+                t38_gateway_set_supported_modems(pvt->t38_gateway_state, T30_SUPPORT_V17 | T30_SUPPORT_V29 | T30_SUPPORT_V27TER);
+        }
+
+     t38_gateway_set_ecm_capability(pvt->t38_gateway_state, pvt->use_ecm);
+     switch_channel_set_variable(channel, "fax_ecm_requested", pvt->use_ecm ? "true" : "false");
+     
+        if (switch_true(switch_channel_get_variable(channel, "FAX_DISABLE_ECM"))) {
+                t38_gateway_set_ecm_capability(pvt->t38_gateway_state, FALSE);
+        } else {
+                t38_gateway_set_ecm_capability(pvt->t38_gateway_state, TRUE);
+        }
+        
+
+     span_log_set_message_handler(&pvt->t38_gateway_state->logging, spanfax_log_message);
+     span_log_set_message_handler(&pvt->t38_core->logging, spanfax_log_message);
+
+        if (pvt->verbose) {
+                span_log_set_level(&pvt->t38_gateway_state->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+                span_log_set_level(&pvt->t38_core->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+        }
+
+     t38_set_t38_version(pvt->t38_core, 0);
+     t38_gateway_set_ecm_capability(pvt->t38_gateway_state, 1);
+
+     return SWITCH_STATUS_SUCCESS;
+
        default:
-               assert(0);                              /* Whaaat ? */
-               break;
+               assert(0);                              /* What? */
+               return SWITCH_STATUS_SUCCESS;
        }                                                       /* Switch trans mode */
+    
+       /* All the things which are common to audio and T.38 FAX setup */
+       t30_set_tx_ident(t30, pvt->ident);
+       t30_set_tx_page_header_info(t30, pvt->header);
+
+       t30_set_phase_e_handler(t30, phase_e_handler, pvt);
+
+       t30_set_supported_image_sizes(t30,
+                                                                 T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH
+                                                               | T30_SUPPORT_215MM_WIDTH | T30_SUPPORT_255MM_WIDTH | T30_SUPPORT_303MM_WIDTH);
+       t30_set_supported_resolutions(t30,
+                                                                 T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION
+                                                               | T30_SUPPORT_R8_RESOLUTION | T30_SUPPORT_R16_RESOLUTION);
+
+       if (pvt->disable_v17) {
+               t30_set_supported_modems(t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER);
+               switch_channel_set_variable(channel, "fax_v17_disabled", "1");
+       } else {
+               t30_set_supported_modems(t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER | T30_SUPPORT_V17);
+               switch_channel_set_variable(channel, "fax_v17_disabled", "0");
+       }
+
+       if (pvt->use_ecm) {
+               t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
+               t30_set_ecm_capability(t30, TRUE);
+               switch_channel_set_variable(channel, "fax_ecm_requested", "1");
+       } else {
+               t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION);
+               switch_channel_set_variable(channel, "fax_ecm_requested", "0");
+       }
+
+       if (pvt->app_mode == FUNCTION_TX) {
+               t30_set_tx_file(t30, pvt->filename, pvt->tx_page_start, pvt->tx_page_end);
+       } else {
+               t30_set_rx_file(t30, pvt->filename, -1);
+       }
+       switch_channel_set_variable(channel, "fax_filename", pvt->filename);
 
        return SWITCH_STATUS_SUCCESS;
 }
@@ -357,6 +631,8 @@ static switch_status_t spanfax_destroy(pvt_t *pvt)
        int terminate;
        t30_state_t *t30;
 
+    if (!pvt) return SWITCH_STATUS_FALSE;
+
        if (pvt->fax_state) {
                if (pvt->t38_state) {
                        terminate = 0;
@@ -373,6 +649,10 @@ static switch_status_t spanfax_destroy(pvt_t *pvt)
        }
 
        if (pvt->t38_state) {
+
+        /* remove from timer thread processing */
+        del_pvt(pvt);
+
                if (pvt->t38_state) {
                        terminate = 1;
                } else {
@@ -380,6 +660,7 @@ static switch_status_t spanfax_destroy(pvt_t *pvt)
                }
 
                t30 = t38_terminal_get_t30_state(pvt->t38_state);
+
                if (terminate && t30) {
                        t30_terminate(t30);
                }
@@ -387,28 +668,210 @@ static switch_status_t spanfax_destroy(pvt_t *pvt)
                t38_terminal_release(pvt->t38_state);
        }
 
+    if (pvt->t38_gateway_state) {
+        t38_gateway_release(pvt->t38_gateway_state);
+    }
+
+       if (pvt->udptl_state) {
+               udptl_release(pvt->udptl_state);
+    }
        return SWITCH_STATUS_SUCCESS;
 }
 
+static t38_mode_t configure_t38(pvt_t *pvt)
+{
+    switch_core_session_t *session = pvt->session;
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+    switch_t38_options_t *t38_options = switch_channel_get_private(channel, "t38_options");
+    int method = 2;
+
+    if (!t38_options) {
+        pvt->t38_mode = T38_MODE_REFUSED;
+        return pvt->t38_mode;
+    }
+
+    t38_set_t38_version(pvt->t38_core, t38_options->T38FaxVersion);
+    t38_set_max_buffer_size(pvt->t38_core, t38_options->T38FaxMaxBuffer);
+    t38_set_fastest_image_data_rate(pvt->t38_core, t38_options->T38MaxBitRate);
+    t38_set_fill_bit_removal(pvt->t38_core, t38_options->T38FaxFillBitRemoval);
+    t38_set_mmr_transcoding(pvt->t38_core, t38_options->T38FaxTranscodingMMR);
+    t38_set_jbig_transcoding(pvt->t38_core, t38_options->T38FaxTranscodingJBIG);
+    t38_set_max_datagram_size(pvt->t38_core, t38_options->T38FaxMaxDatagram);
+
+    if (t38_options->T38FaxRateManagement) { 
+        if (!strcasecmp(t38_options->T38FaxRateManagement, "transferredTCF")) {
+            method = 2;
+        } else {
+            method = 1;
+        }
+    }
+
+    t38_set_data_rate_management_method(pvt->t38_core, method);
+
+
+    //t38_set_data_transport_protocol(pvt->t38_core, int data_transport_protocol);
+    //t38_set_redundancy_control(pvt->t38_core, int category, int setting);
+
+    return pvt->t38_mode;
+}
+
+static t38_mode_t negotiate_t38(pvt_t *pvt)
+{
+    switch_core_session_t *session = pvt->session;
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+    switch_core_session_message_t msg = { 0 };
+    switch_t38_options_t *t38_options = switch_channel_get_private(channel, "t38_options");
+    int enabled = 0, insist = 0;
+    const char *v;
+
+    pvt->t38_mode = T38_MODE_REFUSED;
+
+    if (pvt->app_mode == FUNCTION_GW) {
+        enabled = 1;
+    } else if ((v = switch_channel_get_variable(channel, "fax_enable_t38"))) {
+        enabled = switch_true(v);
+    } else {
+        enabled = globals.enable_t38;
+    }
+
+    if (!(enabled && t38_options)) {
+        /* if there is no t38_options the endpoint will refuse the transition */
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s NO T38 options detected.\n", switch_channel_get_name(channel));
+        switch_channel_set_private(channel, "t38_options", NULL);
+    } else {
+        pvt->t38_mode = T38_MODE_NEGOTIATED;
+        
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxVersion = %d\n", t38_options->T38FaxVersion);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38MaxBitRate = %d\n", t38_options->T38MaxBitRate);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxFillBitRemoval = %d\n", t38_options->T38FaxFillBitRemoval);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxTranscodingMMR = %d\n", t38_options->T38FaxTranscodingMMR);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxTranscodingJBIG = %d\n", t38_options->T38FaxTranscodingJBIG);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxRateManagement = '%s'\n", t38_options->T38FaxRateManagement);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxMaxBuffer = %d\n", t38_options->T38FaxMaxBuffer);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxMaxDatagram = %d\n", t38_options->T38FaxMaxDatagram);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxUdpEC = '%s'\n", t38_options->T38FaxUdpEC);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38VendorInfo = '%s'\n", switch_str_nil(t38_options->T38VendorInfo));
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "ip = '%s'\n", t38_options->ip ? t38_options->ip : "Not specified");
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "port = %d\n", t38_options->port);
+
+        /* Time to practice our negotiating skills, by editing the t38_options */
+
+        /* use default IP/PORT */
+        t38_options->ip = NULL;
+        t38_options->port = 0;
+
+        if (t38_options->T38FaxVersion > 3) {
+            t38_options->T38FaxVersion = 3;
+        }
+        t38_options->T38MaxBitRate = (pvt->disable_v17)  ?  9600  :  14400;
+        t38_options->T38FaxFillBitRemoval = 1;
+        t38_options->T38FaxTranscodingMMR = 0;
+        t38_options->T38FaxTranscodingJBIG = 0;
+        t38_options->T38FaxRateManagement = "transferredTCF";
+        t38_options->T38FaxMaxBuffer = 2000;
+        t38_options->T38FaxMaxDatagram = LOCAL_FAX_MAX_DATAGRAM;
+        if (strcasecmp(t38_options->T38FaxUdpEC, "t38UDPRedundancy") == 0
+            ||
+            strcasecmp(t38_options->T38FaxUdpEC, "t38UDPFEC") == 0) {
+            t38_options->T38FaxUdpEC = "t38UDPRedundancy";
+        } else {
+            t38_options->T38FaxUdpEC = NULL;
+        }
+        t38_options->T38VendorInfo = "0 0 0";
+    }
+
+    if ((v = switch_channel_get_variable(channel, "fax_enable_t38_insist"))) {
+        insist = switch_true(v);
+    } else {
+        insist = globals.enable_t38_insist;
+    }
+
+    /* This will send the options back in a response */
+    msg.from = __FILE__;
+    msg.message_id = SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION;
+    msg.numeric_arg = insist;
+    switch_core_session_receive_message(session, &msg);            
+
+       return pvt->t38_mode;
+}
+
+
+
+static t38_mode_t request_t38(pvt_t *pvt)
+{
+    switch_core_session_t *session = pvt->session;
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+    switch_core_session_message_t msg = { 0 };
+    switch_t38_options_t *t38_options = NULL;
+    int enabled = 0, insist = 0;
+    const char *v;
+
+    pvt->t38_mode = T38_MODE_UNKNOWN;
+
+    if (pvt->app_mode == FUNCTION_GW) {
+        enabled = 1;
+    } else if ((v = switch_channel_get_variable(channel, "fax_enable_t38"))) {
+        enabled = switch_true(v);
+    } else {
+        enabled = globals.enable_t38;
+    }
+
+    if (enabled) {
+        if ((v = switch_channel_get_variable(channel, "fax_enable_t38_request"))) {
+            enabled = switch_true(v);
+        } else {
+            enabled = globals.enable_t38_request;
+        }
+    }
+
+
+    if ((v = switch_channel_get_variable(channel, "fax_enable_t38_insist"))) {
+        insist = switch_true(v);
+    } else {
+        insist = globals.enable_t38_insist;
+    }
+
+    if (enabled) {
+        t38_options = switch_core_session_alloc(session, sizeof(*t38_options));
+        
+        t38_options->T38MaxBitRate = (pvt->disable_v17) ? 9600 : 14400;
+        t38_options->T38FaxVersion = 0;
+        t38_options->T38FaxFillBitRemoval = 1;
+        t38_options->T38FaxTranscodingMMR = 0;
+        t38_options->T38FaxTranscodingJBIG = 0;
+        t38_options->T38FaxRateManagement = "transferredTCF";
+        t38_options->T38FaxMaxBuffer = 2000;
+        t38_options->T38FaxMaxDatagram = LOCAL_FAX_MAX_DATAGRAM;
+        t38_options->T38FaxUdpEC = "t38UDPRedundancy";
+        t38_options->T38VendorInfo = "0 0 0";
+        
+        /* use default IP/PORT */
+        t38_options->ip = NULL;
+        t38_options->port = 0;
+        switch_channel_set_private(channel, "t38_options", t38_options);
+        pvt->t38_mode = T38_MODE_REQUESTED;
+
+        /* This will send a request for t.38 mode */
+        msg.from = __FILE__;
+        msg.message_id = SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA;
+        msg.numeric_arg = insist;
+        switch_core_session_receive_message(session, &msg);  
+    }
+
+       return pvt->t38_mode;
+}
+
 /*****************************************************************************
        MAIN FAX PROCESSING
 *****************************************************************************/
 
-void process_fax(switch_core_session_t *session, const char *data, application_mode_t app_mode)
+static pvt_t *pvt_init(switch_core_session_t *session, application_mode_t app_mode)
 {
-       pvt_t *pvt;
-       const char *tmp;
+    switch_channel_t *channel;
+    pvt_t *pvt = NULL;
+    const char *tmp;
 
-       switch_channel_t *channel;
-       switch_codec_t read_codec = { 0 };
-       switch_codec_t write_codec = { 0 };
-       switch_frame_t *read_frame = { 0 };
-       switch_frame_t write_frame = { 0 };
-       switch_codec_implementation_t read_impl = { 0 };
-       int16_t *buf = NULL;
-       switch_core_session_get_read_impl(session, &read_impl);
-
-       /* make sure we have a valid channel when starting the FAX application */
+       /* Make sure we have a valid channel when starting the FAX application */
        channel = switch_core_session_get_channel(session);
        switch_assert(channel != NULL);
 
@@ -418,36 +881,25 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
 
        /* Allocate our structs */
        pvt = switch_core_session_alloc(session, sizeof(pvt_t));
+    pvt->session = session;
 
-       counter_increment();
-
-       if (!pvt) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot allocate application private data\n");
-               return;
-       } else {
-               memset(pvt, 0, sizeof(pvt_t));
-
-               pvt->session = session;
-               pvt->app_mode = app_mode;
+    pvt->app_mode = app_mode;
 
-               pvt->tx_page_start = -1;
-               pvt->tx_page_end = -1;
+    pvt->tx_page_start = -1;
+    pvt->tx_page_end = -1;
 
-               if (pvt->app_mode == FUNCTION_TX) {
-                       pvt->caller = 1;
-               } else if (pvt->app_mode == FUNCTION_RX) {
-                       pvt->caller = 0;
-               } else {
-                       assert(0);                      /* UH ? */
-               }
-       }
 
+    switch(pvt->app_mode) {
 
-       buf = switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE);
-       if (!buf) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot allocate application buffer data\n");
-               return;
-       }
+    case FUNCTION_TX:
+        pvt->caller = 1;
+        break;
+    case FUNCTION_RX:
+        pvt->caller = 0;
+        break;
+    case FUNCTION_GW:
+        break;
+    }
 
        /* Retrieving our settings from the channel variables */
 
@@ -490,7 +942,6 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
        }
 
        if (pvt->app_mode == FUNCTION_TX) {
-
                if ((tmp = switch_channel_get_variable(channel, "fax_start_page"))) {
                        pvt->tx_page_start = atoi(tmp);
                }
@@ -510,7 +961,31 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
                if ((pvt->tx_page_end < pvt->tx_page_start) && (pvt->tx_page_end != -1)) {
                        pvt->tx_page_end = pvt->tx_page_start;
                }
-       }
+       }    
+
+    return pvt;
+}
+
+void process_fax(switch_core_session_t *session, const char *data, application_mode_t app_mode)
+{
+       pvt_t *pvt;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_codec_t read_codec = { 0 };
+       switch_codec_t write_codec = { 0 };
+       switch_frame_t *read_frame = { 0 };
+       switch_frame_t write_frame = { 0 };
+       switch_codec_implementation_t read_impl = { 0 };
+       int16_t *buf = NULL;
+
+       switch_core_session_get_read_impl(session, &read_impl);
+
+       counter_increment();
+
+    
+    pvt = pvt_init(session, app_mode);
+    
+
+       buf = switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE);
 
        if (!zstr(data)) {
                pvt->filename = switch_core_session_strdup(session, data);
@@ -581,7 +1056,6 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
         * used internally by spandsp and FS will do the transcoding
         * from G.711 or any other original codec
         */
-
        if (switch_core_codec_init(&read_codec,
                                                           "L16",
                                                           NULL,
@@ -617,6 +1091,11 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
 
        switch_ivr_sleep(session, 250, SWITCH_TRUE, NULL);
 
+
+    /* If you have the means, I highly recommend picking one up. ...*/
+    request_t38(pvt);
+
+
        while (switch_channel_ready(channel)) {
                int tx = 0;
                switch_status_t status;
@@ -630,7 +1109,7 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
                   - call t38_terminal_send_timeout(), sleep for a while
 
                   The T.38 stuff can be placed here (and the audio stuff can be skipped)
-                */
+        */
 
                /* read new audio frame from the channel */
                status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
@@ -640,9 +1119,67 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
                        goto done;
                }
 
+        switch (pvt->t38_mode) {
+        case T38_MODE_REQUESTED:
+            {
+                if (switch_channel_test_app_flag(channel, CF_APP_T38)) {
+                    switch_core_session_message_t msg = { 0 };
+                    pvt->t38_mode = T38_MODE_NEGOTIATED;
+                    spanfax_init(pvt, T38_MODE);
+                    configure_t38(pvt);
+
+                    /* This will change the rtp stack to udptl mode */
+                    msg.from = __FILE__;
+                    msg.message_id = SWITCH_MESSAGE_INDICATE_UDPTL_MODE;
+                    switch_core_session_receive_message(session, &msg);
+                }
+                continue;
+            }
+            break;
+        case T38_MODE_UNKNOWN:
+            {
+                if (switch_channel_test_app_flag(channel, CF_APP_T38)) {
+                    if (negotiate_t38(pvt) == T38_MODE_NEGOTIATED) {
+                        /* is is safe to call this again, it was already called above in AUDIO_MODE */
+                        /* but this is the only way to set up the t38 stuff */
+                        spanfax_init(pvt, T38_MODE);
+                        continue;
+                    }
+                }
+            }
+            break;
+        case T38_MODE_NEGOTIATED:
+            {
+                /* do what we need to do when we are in t38 mode */
+                if (switch_test_flag(read_frame, SFF_CNG)) {
+                    /* dunno what to do, most likely you will not get too many of these since we turn off the timer in udptl mode */
+                    continue;
+                }
+
+                if (switch_test_flag(read_frame, SFF_UDPTL_PACKET)) {
+                    /* now we know we can cast frame->packet to a udptl structure */
+                    //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "READ %d udptl bytes\n", read_frame->packetlen);
+                    
+                    udptl_rx_packet(pvt->udptl_state, read_frame->packet, read_frame->packetlen);
+
+
+                }
+            }
+            continue;
+        default:
+            break;
+        }
+
                /* Skip CNG frames (auto-generated by FreeSWITCH, usually) */
-               if (!switch_test_flag(read_frame, SFF_CNG)) {
-                       /* pass the new incoming audio frame to the fax_rx function */
+               if (switch_test_flag(read_frame, SFF_CNG)) {
+                       /* We have no real signal data for the FAX software, but we have a space in time if we have a CNG indication.
+                          Do a fill-in operation in the FAX machine, to keep things rolling along. */
+                       if (fax_rx_fillin(pvt->fax_state, read_frame->samples)) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fax_rx_fillin reported an error\n");
+                               goto done;
+                       }
+               } else {
+                       /* Pass the new incoming audio frame to the fax_rx function */
                        if (fax_rx(pvt->fax_state, (int16_t *) read_frame->data, read_frame->samples)) {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fax_rx reported an error\n");
                                goto done;
@@ -664,9 +1201,6 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
                }
 
                if (switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
-                       /* something weird has happened */
-                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR,
-                                                         "Cannot write frame [datalen: %d, samples: %d]\n", write_frame.datalen, write_frame.samples);
                        goto done;
                }
 
@@ -687,11 +1221,6 @@ void process_fax(switch_core_session_t *session, const char *data, application_m
        if (switch_core_codec_ready(&write_codec)) {
                switch_core_codec_destroy(&write_codec);
        }
-
-
-
-
-
 }
 
 /* **************************************************************************
@@ -731,6 +1260,18 @@ void load_configuration(switch_bool_t reload)
                                                globals.disable_v17 = 1;
                                        else
                                                globals.disable_v17 = 0;
+                               } else if (!strcmp(name, "enable-t38")) {
+                                       if (switch_true(value)) {
+                                               globals.enable_t38= 1;
+                    } else {
+                                               globals.enable_t38 = 0;
+                    }
+                               } else if (!strcmp(name, "enable-t38-request")) {
+                                       if (switch_true(value)) {
+                                               globals.enable_t38_request = 1;
+                    } else {
+                                               globals.enable_t38_request = 0;
+                    }
                                } else if (!strcmp(name, "ident")) {
                                        strncpy(globals.ident, value, sizeof(globals.ident) - 1);
                                } else if (!strcmp(name, "header")) {
@@ -853,19 +1394,6 @@ switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session)
        return SWITCH_STATUS_SUCCESS;
 }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
 /* **************************************************************************
    FREESWITCH MODULE DEFINITIONS
    ************************************************************************* */
@@ -900,12 +1428,358 @@ SWITCH_STANDARD_APP(stop_dtmf_session_function)
        spandsp_stop_inband_dtmf_session(session);
 }
 
+static const switch_state_handler_table_t t38_gateway_state_handlers;
+
+static switch_status_t t38_gateway_on_soft_execute(switch_core_session_t *session)
+{
+    switch_core_session_t *other_session;
+
+    switch_channel_t *other_channel, *channel = switch_core_session_get_channel(session);
+    pvt_t *pvt;
+    const char *peer_uuid = switch_channel_get_variable(channel, "t38_peer");
+    switch_core_session_message_t msg = { 0 };
+    switch_status_t status;
+    switch_frame_t *read_frame = { 0 };
+
+    if (!(other_session = switch_core_session_locate(peer_uuid))) {
+        switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Cannot locate channel with uuid %s", 
+                          switch_channel_get_name(channel), peer_uuid);
+        goto end;
+    }
+
+    other_channel = switch_core_session_get_channel(other_session);
+
+    pvt = pvt_init(session, FUNCTION_GW);
+    request_t38(pvt);
+
+    while (switch_channel_ready(channel) && switch_channel_up(other_channel) && !switch_channel_test_app_flag(channel, CF_APP_T38)) {
+               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+               if (!SWITCH_READ_ACCEPTABLE(status) || pvt->done) {
+                       /* Our duty is over */
+            goto end_unlock;
+               }
+
+        if (switch_test_flag(read_frame, SFF_CNG)) {
+            continue;
+        }
+
+        if (switch_core_session_write_frame(other_session, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
+            goto end_unlock;
+        }
+    }
+
+       if (!(switch_channel_ready(channel) && switch_channel_up(other_channel))) {
+        goto end_unlock;
+    }
+
+    if (!switch_channel_test_app_flag(channel, CF_APP_T38)) {
+        switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Could not negotiate T38\n", switch_channel_get_name(channel));
+        goto end_unlock;
+    }
+
+    if (pvt->t38_mode == T38_MODE_REQUESTED) {
+        configure_t38(pvt);
+        pvt->t38_mode = T38_MODE_NEGOTIATED;
+    } else {
+        if (negotiate_t38(pvt) != T38_MODE_NEGOTIATED) {
+            switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Could not negotiate T38\n", switch_channel_get_name(channel));
+            switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+            goto end_unlock;
+        }
+    }
+
+    spanfax_init(pvt, T38_GATEWAY_MODE);
+
+    /* This will change the rtp stack to udptl mode */
+    msg.from = __FILE__;
+    msg.message_id = SWITCH_MESSAGE_INDICATE_UDPTL_MODE;
+    switch_core_session_receive_message(session, &msg);
+
+
+    /* wake up the audio side */
+    switch_channel_set_private(channel, "_t38_pvt", pvt);
+    switch_channel_set_app_flag(other_channel, CF_APP_T38);
+
+
+       while (switch_channel_ready(channel) && switch_channel_up(other_channel)) {
+
+               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+               if (!SWITCH_READ_ACCEPTABLE(status) || pvt->done) {
+                       /* Our duty is over */
+                       goto end_unlock;
+               }
+        
+        if (switch_test_flag(read_frame, SFF_CNG)) {
+            continue;
+        }
+        
+        if (switch_test_flag(read_frame, SFF_UDPTL_PACKET)) {
+            udptl_rx_packet(pvt->udptl_state, read_frame->packet, read_frame->packetlen);
+        }
+    }
+
+ end_unlock:
+
+    switch_channel_hangup(other_channel, SWITCH_CAUSE_NORMAL_CLEARING);
+    switch_core_session_rwunlock(other_session);
+
+ end:
+
+    switch_channel_clear_state_handler(channel, &t38_gateway_state_handlers);
+    switch_channel_set_variable(channel, "t38_peer", NULL);
+
+    switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t t38_gateway_on_consume_media(switch_core_session_t *session)
+{
+    switch_core_session_t *other_session;
+    switch_channel_t *other_channel, *channel = switch_core_session_get_channel(session);
+    const char *peer_uuid = switch_channel_get_variable(channel, "t38_peer");
+    pvt_t *pvt = NULL;
+    switch_codec_t read_codec = { 0 };
+       switch_codec_t write_codec = { 0 };
+       switch_frame_t *read_frame = { 0 };
+       switch_frame_t write_frame = { 0 };
+       switch_codec_implementation_t read_impl = { 0 };
+       int16_t *buf = NULL;
+    switch_status_t status;
+    switch_size_t tx;
+
+       switch_core_session_get_read_impl(session, &read_impl);
+
+    buf = switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE);
+
+    if (!(other_session = switch_core_session_locate(peer_uuid))) {
+        switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        goto end;
+    }
+
+    other_channel = switch_core_session_get_channel(other_session);
+    
+    while (switch_channel_ready(channel) && switch_channel_up(other_channel) && !switch_channel_test_app_flag(channel, CF_APP_T38)) {
+               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+        
+               if (!SWITCH_READ_ACCEPTABLE(status)) {
+                       /* Our duty is over */
+                       goto end_unlock;
+               }
+
+        if (switch_test_flag(read_frame, SFF_CNG)) {
+            continue;
+        }
+
+        if (switch_core_session_write_frame(other_session, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
+            goto end_unlock;
+        }
+    }
+
+       if (!(switch_channel_ready(channel) && switch_channel_up(other_channel))) {
+        goto end_unlock;
+    }
+
+    if (!switch_channel_test_app_flag(channel, CF_APP_T38)) {
+        switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        goto end_unlock;
+    }
+    
+    if (!(pvt = switch_channel_get_private(other_channel, "_t38_pvt"))) {
+        switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        goto end_unlock;
+    }
+    
+    if (switch_core_codec_init(&read_codec,
+                                                          "L16",
+                                                          NULL,
+                                                          read_impl.samples_per_second,
+                                                          read_impl.microseconds_per_packet / 1000,
+                                                          1,
+                                                          SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+                                                          NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Raw read codec activation Success L16 %u\n",
+                                                 read_codec.implementation->microseconds_per_packet);
+               switch_core_session_set_read_codec(session, &read_codec);
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Raw read codec activation Failed L16\n");
+               goto end_unlock;
+       }
+
+       if (switch_core_codec_init(&write_codec,
+                                                          "L16",
+                                                          NULL,
+                                                          read_impl.samples_per_second,
+                                                          read_impl.microseconds_per_packet / 1000,
+                                                          1,
+                                                          SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+                                                          NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Raw write codec activation Success L16\n");
+               write_frame.codec = &write_codec;
+               write_frame.data = buf;
+               write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Raw write codec activation Failed L16\n");
+               goto end_unlock;
+       }
+
+       switch_ivr_sleep(session, 250, SWITCH_TRUE, NULL);
+
+       while (switch_channel_ready(channel) && switch_channel_up(other_channel)) {
+               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+               if (!SWITCH_READ_ACCEPTABLE(status) || pvt->done) {
+                       /* Our duty is over */
+                       goto end_unlock;
+               }
+
+
+               /* Skip CNG frames (auto-generated by FreeSWITCH, usually) */
+               if (!switch_test_flag(read_frame, SFF_CNG)) {
+                       if (t38_gateway_rx(pvt->t38_gateway_state, (int16_t *) read_frame->data, read_frame->samples)) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fax_rx reported an error\n");
+                               goto end_unlock;
+                       }
+               }
+
+               if ((tx = t38_gateway_tx(pvt->t38_gateway_state, buf, write_codec.implementation->samples_per_packet)) < 0) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fax_tx reported an error\n");
+                       goto end_unlock;
+               }
+
+               if (!tx) {
+                       /* switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "No audio samples to send\n"); */
+                       continue;
+               } else {
+                       /* Set our write_frame data */
+                       write_frame.datalen = tx * sizeof(int16_t);
+                       write_frame.samples = tx;
+               }
+
+               if (switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
+                       goto end_unlock;
+               }
+    }
+
+ end_unlock:
+
+    switch_channel_hangup(other_channel, SWITCH_CAUSE_NORMAL_CLEARING);
+    switch_core_session_rwunlock(other_session);
+
+       switch_core_session_set_read_codec(session, NULL);
+
+       if (switch_core_codec_ready(&read_codec)) {
+               switch_core_codec_destroy(&read_codec);
+       }
+
+       if (switch_core_codec_ready(&write_codec)) {
+               switch_core_codec_destroy(&write_codec);
+       }
+
+
+ end:
+
+    switch_channel_clear_state_handler(channel, &t38_gateway_state_handlers);
+    switch_channel_set_variable(channel, "t38_peer", NULL);
+    switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+    
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t t38_gateway_on_reset(switch_core_session_t *session)
+{
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+
+    if (switch_channel_test_app_flag(channel, CF_APP_TAGGED)) {
+        switch_channel_clear_app_flag(channel, CF_APP_TAGGED);
+        switch_channel_set_state(channel, CS_CONSUME_MEDIA);
+    } else {
+        switch_channel_set_state(channel, CS_SOFT_EXECUTE);
+    }
+
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static const switch_state_handler_table_t t38_gateway_state_handlers = {
+       /*.on_init */ NULL,
+       /*.on_routing */ NULL,
+       /*.on_execute */ NULL,
+       /*.on_hangup */ NULL,
+       /*.on_exchange_media */ NULL,
+       /*.on_soft_execute */ t38_gateway_on_soft_execute,
+       /*.on_consume_media */ t38_gateway_on_consume_media,
+       /*.on_hibernate */ NULL,
+       /*.on_reset */ t38_gateway_on_reset,
+    /*.on_park */ NULL,
+    /*.on_reporting */ NULL,
+    /*.on_destroy */ NULL,
+    SSH_FLAG_STICKY
+};
+
+static switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, const char *data)
+{
+    switch_channel_t *other_channel = NULL, *channel = switch_core_session_get_channel(session);
+    switch_core_session_t *other_session = NULL;
+    int peer = app && !strcasecmp(app, "peer");
+    
+    if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
+        other_channel = switch_core_session_get_channel(other_session);
+
+        switch_channel_set_variable(channel, "t38_peer", switch_core_session_get_uuid(other_session));
+        switch_channel_set_variable(other_channel, "t38_peer", switch_core_session_get_uuid(session));
+
+
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s starting gateway mode to %s\n", 
+                          switch_channel_get_name(peer ? channel : other_channel),
+                          switch_channel_get_name(peer ? other_channel : channel));
+        
+        
+        switch_channel_clear_state_handler(channel, NULL);
+        switch_channel_clear_state_handler(other_channel, NULL);
+
+        switch_channel_add_state_handler(channel, &t38_gateway_state_handlers);
+        switch_channel_add_state_handler(other_channel, &t38_gateway_state_handlers);
+
+        switch_channel_set_app_flag(peer ? channel : other_channel, CF_APP_TAGGED);
+        switch_channel_clear_app_flag(peer ? other_channel : channel, CF_APP_TAGGED);   
+        
+        switch_channel_set_state(channel, CS_RESET);
+        switch_channel_set_state(other_channel, CS_RESET);
+        
+        switch_core_session_rwunlock(other_session);
+
+    }
+    
+    return SWITCH_FALSE;
+}
+
+
+SWITCH_STANDARD_APP(t38_gateway_function)
+{
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+
+    if (zstr(data) || strcasecmp(data, "self")) {
+        data = "peer";
+    }
+
+    switch_channel_set_variable(channel, "t38_leg", data);
+    
+       switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", 0, 1, data, NULL, t38_gateway_start);
+}
+
+
 SWITCH_MODULE_LOAD_FUNCTION(mod_fax_init)
 {
        switch_application_interface_t *app_interface;
 
        *module_interface = switch_loadable_module_create_module_interface(pool, modname);
 
+       SWITCH_ADD_APP(app_interface, "t38_gateway", "Convert to T38 Gateway if tones are heard", "Convert to T38 Gateway if tones are heard", 
+                   t38_gateway_function, "", SAF_MEDIA_TAP);
+
        SWITCH_ADD_APP(app_interface, "rxfax", "FAX Receive Application", "FAX Receive Application", spanfax_rx_function, SPANFAX_RX_USAGE,
                                   SAF_SUPPORT_NOMEDIA);
        SWITCH_ADD_APP(app_interface, "txfax", "FAX Transmit Application", "FAX Transmit Application", spanfax_tx_function, SPANFAX_TX_USAGE,
@@ -915,9 +1789,12 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_fax_init)
        SWITCH_ADD_APP(app_interface, "spandsp_start_dtmf", "Detect dtmf", "Detect inband dtmf on the session", dtmf_session_function, "", SAF_MEDIA_TAP);
 
        memset(&globals, 0, sizeof(globals));
+    memset(&t38_state_list, 0, sizeof(t38_state_list));
        switch_core_new_memory_pool(&globals.pool);
        switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
-
+       switch_mutex_init(&t38_state_list.mutex, SWITCH_MUTEX_NESTED, globals.pool);
+    
+    globals.enable_t38 = 1;
        globals.total_sessions = 0;
        globals.verbose = 1;
        globals.use_ecm = 1;
diff --git a/src/mod/applications/mod_fax/udptl.c b/src/mod/applications/mod_fax/udptl.c
new file mode 100644 (file)
index 0000000..a265151
--- /dev/null
@@ -0,0 +1,563 @@
+//#define UDPTL_DEBUG
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2009, Steve Underwood <steveu@coppice.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Contributor(s):
+ * 
+ * Steve Underwood <steveu@coppice.org>
+ *
+ * udptl.c -- UDPTL handling for T.38
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <memory.h>
+
+#include "udptl.h"
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static int decode_length(const uint8_t *buf, int limit, int *len, int *pvalue)
+{
+       if (*len >= limit)
+               return -1;
+       if ((buf[*len] & 0x80) == 0) {
+               *pvalue = buf[(*len)++];
+               return 0;
+       }
+       if ((buf[*len] & 0x40) == 0) {
+               if (*len >= limit - 1)
+                       return -1;
+               *pvalue = (buf[(*len)++] & 0x3F) << 8;
+               *pvalue |= buf[(*len)++];
+               return 0;
+       }
+       *pvalue = (buf[(*len)++] & 0x3F) << 14;
+       /* Indicate we have a fragment */
+       return 1;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static int decode_open_type(const uint8_t *buf, int limit, int *len, const uint8_t ** p_object, int *p_num_octets)
+{
+       int octet_cnt;
+       int octet_idx;
+       int stat;
+       int i;
+       const uint8_t **pbuf;
+
+       for (octet_idx = 0, *p_num_octets = 0;; octet_idx += octet_cnt) {
+               if ((stat = decode_length(buf, limit, len, &octet_cnt)) < 0)
+                       return -1;
+               if (octet_cnt > 0) {
+                       *p_num_octets += octet_cnt;
+
+                       pbuf = &p_object[octet_idx];
+                       i = 0;
+                       /* Make sure the buffer contains at least the number of bits requested */
+                       if ((*len + octet_cnt) > limit)
+                               return -1;
+
+                       *pbuf = &buf[*len];
+                       *len += octet_cnt;
+               }
+               if (stat == 0)
+                       break;
+       }
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static int encode_length(uint8_t *buf, int *len, int value)
+{
+       int multiplier;
+
+       if (value < 0x80) {
+               /* 1 octet */
+               buf[(*len)++] = value;
+               return value;
+       }
+       if (value < 0x4000) {
+               /* 2 octets */
+               /* Set the first bit of the first octet */
+               buf[(*len)++] = ((0x8000 | value) >> 8) & 0xFF;
+               buf[(*len)++] = value & 0xFF;
+               return value;
+       }
+       /* Fragmentation */
+       multiplier = (value < 0x10000) ? (value >> 14) : 4;
+       /* Set the first 2 bits of the octet */
+       buf[(*len)++] = 0xC0 | multiplier;
+       return multiplier << 14;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static int encode_open_type(uint8_t *buf, int *len, const uint8_t *data, int num_octets)
+{
+       int enclen;
+       int octet_idx;
+       uint8_t zero_byte;
+
+       /* If open type is of zero length, add a single zero byte (10.1) */
+       if (num_octets == 0) {
+               zero_byte = 0;
+               data = &zero_byte;
+               num_octets = 1;
+       }
+       /* Encode the open type */
+       for (octet_idx = 0;; num_octets -= enclen, octet_idx += enclen) {
+               if ((enclen = encode_length(buf, len, num_octets)) < 0)
+                       return -1;
+               if (enclen > 0) {
+                       memcpy(&buf[*len], &data[octet_idx], enclen);
+                       *len += enclen;
+               }
+               if (enclen >= num_octets)
+                       break;
+       }
+
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_rx_packet(udptl_state_t *s, const uint8_t buf[], int len)
+{
+       int stat;
+       int stat2;
+       int i;
+       int j;
+       int k;
+       int l;
+       int m;
+       int x;
+       int limit;
+       int which;
+       int ptr;
+       int count;
+       int total_count;
+       int seq_no;
+       const uint8_t *msg;
+       const uint8_t *data;
+       int msg_len;
+       int repaired[16];
+       const uint8_t *bufs[16];
+       int lengths[16];
+       int span;
+       int entries;
+
+       ptr = 0;
+       /* Decode seq_number */
+       if (ptr + 2 > len)
+               return -1;
+       seq_no = (buf[0] << 8) | buf[1];
+       ptr += 2;
+       /* Break out the primary packet */
+       if ((stat = decode_open_type(buf, len, &ptr, &msg, &msg_len)) != 0)
+               return -1;
+       /* Decode error_recovery */
+       if (ptr + 1 > len)
+               return -1;
+       /* Our buffers cannot tolerate overlength packets */
+       if (msg_len > LOCAL_FAX_MAX_DATAGRAM)
+               return -1;
+       /* Update any missed slots in the buffer */
+       for (i = s->rx_seq_no; seq_no > i; i++) {
+               x = i & UDPTL_BUF_MASK;
+               s->rx[x].buf_len = -1;
+               s->rx[x].fec_len[0] = 0;
+               s->rx[x].fec_span = 0;
+               s->rx[x].fec_entries = 0;
+       }
+       /* Save the new packet. Pure redundancy mode won't use this, but some systems will switch
+          into FEC mode after sending some redundant packets. */
+       x = seq_no & UDPTL_BUF_MASK;
+       memcpy(s->rx[x].buf, msg, msg_len);
+       s->rx[x].buf_len = msg_len;
+       s->rx[x].fec_len[0] = 0;
+       s->rx[x].fec_span = 0;
+       s->rx[x].fec_entries = 0;
+       if ((buf[ptr++] & 0x80) == 0) {
+               /* Secondary packet mode for error recovery */
+               /* We might have the packet we want, but we need to check through
+                  the redundant stuff, and verify the integrity of the UDPTL.
+                  This greatly reduces our chances of accepting garbage. */
+               total_count = 0;
+               do {
+                       if ((stat2 = decode_length(buf, len, &ptr, &count)) < 0)
+                               return -1;
+                       for (i = 0; i < count; i++) {
+                               if ((stat = decode_open_type(buf, len, &ptr, &bufs[total_count + i], &lengths[total_count + i])) != 0)
+                                       return -1;
+                       }
+                       total_count += count;
+               }
+               while (stat2 > 0);
+               /* We should now be exactly at the end of the packet. If not, this is a fault. */
+               if (ptr != len)
+                       return -1;
+               if (seq_no > s->rx_seq_no) {
+                       /* We received a later packet than we expected, so we need to check if we can fill in the gap from the
+                          secondary packets. */
+                       /* Step through in reverse order, so we go oldest to newest */
+                       for (i = total_count; i > 0; i--) {
+                               if (seq_no - i >= s->rx_seq_no) {
+                                       /* This one wasn't seen before */
+                                       /* Decode the secondary packet */
+#if defined(UDPTL_DEBUG)
+                                       fprintf(stderr, "Secondary %d, len %d\n", seq_no - i, lengths[i - 1]);
+#endif
+                                       /* Save the new packet. Redundancy mode won't use this, but some systems will switch into
+                                          FEC mode after sending some redundant packets, and this may then be important. */
+                                       x = (seq_no - i) & UDPTL_BUF_MASK;
+                                       memcpy(s->rx[x].buf, bufs[i - 1], lengths[i - 1]);
+                                       s->rx[x].buf_len = lengths[i - 1];
+                                       s->rx[x].fec_len[0] = 0;
+                                       s->rx[x].fec_span = 0;
+                                       s->rx[x].fec_entries = 0;
+                                       if (s->rx_packet_handler(s->user_data, bufs[i - 1], lengths[i - 1], seq_no - i) < 0)
+                                               fprintf(stderr, "Bad IFP\n");
+                               }
+                       }
+               }
+       } else {
+               /* FEC mode for error recovery */
+
+               /* Decode the FEC packets */
+               /* The span is defined as an unconstrained integer, but will never be more
+                  than a small value. */
+               if (ptr + 2 > len)
+                       return -1;
+               if (buf[ptr++] != 1)
+                       return -1;
+               span = buf[ptr++];
+
+               x = seq_no & UDPTL_BUF_MASK;
+
+               s->rx[x].fec_span = span;
+
+               memset(repaired, 0, sizeof(repaired));
+               repaired[x] = TRUE;
+
+               /* The number of entries is defined as a length, but will only ever be a small
+                  value. Treat it as such. */
+               if (ptr + 1 > len)
+                       return -1;
+               entries = buf[ptr++];
+               s->rx[x].fec_entries = entries;
+
+               /* Decode the elements */
+               for (i = 0; i < entries; i++) {
+                       if ((stat = decode_open_type(buf, len, &ptr, &data, &s->rx[x].fec_len[i])) != 0)
+                               return -1;
+                       if (s->rx[x].fec_len[i] > LOCAL_FAX_MAX_DATAGRAM)
+                               return -1;
+
+                       /* Save the new FEC data */
+                       memcpy(s->rx[x].fec[i], data, s->rx[x].fec_len[i]);
+#if 0
+                       fprintf(stderr, "FEC: ");
+                       for (j = 0; j < s->rx[x].fec_len[i]; j++)
+                               fprintf(stderr, "%02X ", data[j]);
+                       fprintf(stderr, "\n");
+#endif
+               }
+               /* We should now be exactly at the end of the packet. If not, this is a fault. */
+               if (ptr != len)
+                       return -1;
+               /* See if we can reconstruct anything which is missing */
+               /* TODO: this does not comprehensively hunt back and repair everything that is possible */
+               for (l = x; l != ((x - (16 - span * entries)) & UDPTL_BUF_MASK); l = (l - 1) & UDPTL_BUF_MASK) {
+                       if (s->rx[l].fec_len[0] <= 0)
+                               continue;
+                       for (m = 0; m < s->rx[l].fec_entries; m++) {
+                               limit = (l + m) & UDPTL_BUF_MASK;
+                               for (which = -1, k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit;
+                                        k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK) {
+                                       if (s->rx[k].buf_len <= 0)
+                                               which = (which == -1) ? k : -2;
+                               }
+                               if (which >= 0) {
+                                       /* Repairable */
+                                       for (j = 0; j < s->rx[l].fec_len[m]; j++) {
+                                               s->rx[which].buf[j] = s->rx[l].fec[m][j];
+                                               for (k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit;
+                                                        k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK)
+                                                       s->rx[which].buf[j] ^= (s->rx[k].buf_len > j) ? s->rx[k].buf[j] : 0;
+                                       }
+                                       s->rx[which].buf_len = s->rx[l].fec_len[m];
+                                       repaired[which] = TRUE;
+                               }
+                       }
+               }
+               /* Now play any new packets forwards in time */
+               for (l = (x + 1) & UDPTL_BUF_MASK, j = seq_no - UDPTL_BUF_MASK; l != x; l = (l + 1) & UDPTL_BUF_MASK, j++) {
+                       if (repaired[l]) {
+#if defined(UDPTL_DEBUG)
+                               fprintf(stderr, "Fixed packet %d, len %d\n", j, l);
+#endif
+                               if (s->rx_packet_handler(s->user_data, s->rx[l].buf, s->rx[l].buf_len, j) < 0)
+                                       fprintf(stderr, "Bad IFP\n");
+                       }
+               }
+       }
+       /* If packets are received out of sequence, we may have already processed this packet from the error
+          recovery information in a packet already received. */
+       if (seq_no >= s->rx_seq_no) {
+               /* Decode the primary packet */
+#if defined(UDPTL_DEBUG)
+               fprintf(stderr, "Primary packet %d, len %d\n", seq_no, msg_len);
+#endif
+               if (s->rx_packet_handler(s->user_data, msg, msg_len, seq_no) < 0)
+                       fprintf(stderr, "Bad IFP\n");
+       }
+
+       s->rx_seq_no = (seq_no + 1) & 0xFFFF;
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_build_packet(udptl_state_t *s, uint8_t buf[], const uint8_t msg[], int msg_len)
+{
+       uint8_t fec[LOCAL_FAX_MAX_DATAGRAM];
+       int i;
+       int j;
+       int seq;
+       int entry;
+       int entries;
+       int span;
+       int m;
+       int len;
+       int limit;
+       int high_tide;
+
+       /* UDPTL cannot cope with zero length messages, and our buffering for redundancy limits their
+          maximum length. */
+       if (msg_len < 1 || msg_len > LOCAL_FAX_MAX_DATAGRAM)
+               return -1;
+       seq = s->tx_seq_no & 0xFFFF;
+
+       /* Map the sequence number to an entry in the circular buffer */
+       entry = seq & UDPTL_BUF_MASK;
+
+       /* We save the message in a circular buffer, for generating FEC or
+          redundancy sets later on. */
+       s->tx[entry].buf_len = msg_len;
+       memcpy(s->tx[entry].buf, msg, msg_len);
+
+       /* Build the UDPTL packet */
+
+       len = 0;
+       /* Encode the sequence number */
+       buf[len++] = (seq >> 8) & 0xFF;
+       buf[len++] = seq & 0xFF;
+
+       /* Encode the primary packet */
+       if (encode_open_type(buf, &len, msg, msg_len) < 0)
+               return -1;
+
+       /* Encode the appropriate type of error recovery information */
+       switch (s->error_correction_scheme) {
+       case UDPTL_ERROR_CORRECTION_NONE:
+               /* Encode the error recovery type */
+               buf[len++] = 0x00;
+               /* The number of entries will always be zero, so it is pointless allowing
+                  for the fragmented case here. */
+               if (encode_length(buf, &len, 0) < 0)
+                       return -1;
+               break;
+       case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+               /* Encode the error recovery type */
+               buf[len++] = 0x00;
+               if (s->tx_seq_no > s->error_correction_entries)
+                       entries = s->error_correction_entries;
+               else
+                       entries = s->tx_seq_no;
+               /* The number of entries will always be small, so it is pointless allowing
+                  for the fragmented case here. */
+               if (encode_length(buf, &len, entries) < 0)
+                       return -1;
+               /* Encode the elements */
+               for (i = 0; i < entries; i++) {
+                       j = (entry - i - 1) & UDPTL_BUF_MASK;
+                       if (encode_open_type(buf, &len, s->tx[j].buf, s->tx[j].buf_len) < 0)
+                               return -1;
+               }
+               break;
+       case UDPTL_ERROR_CORRECTION_FEC:
+               span = s->error_correction_span;
+               entries = s->error_correction_entries;
+               if (seq < s->error_correction_span * s->error_correction_entries) {
+                       /* In the initial stages, wind up the FEC smoothly */
+                       entries = seq / s->error_correction_span;
+                       if (seq < s->error_correction_span)
+                               span = 0;
+               }
+               /* Encode the error recovery type */
+               buf[len++] = 0x80;
+               /* Span is defined as an inconstrained integer, which it dumb. It will only
+                  ever be a small value. Treat it as such. */
+               buf[len++] = 1;
+               buf[len++] = span;
+               /* The number of entries is defined as a length, but will only ever be a small
+                  value. Treat it as such. */
+               buf[len++] = entries;
+               for (m = 0; m < entries; m++) {
+                       /* Make an XOR'ed entry the maximum length */
+                       limit = (entry + m) & UDPTL_BUF_MASK;
+                       high_tide = 0;
+                       for (i = (limit - span * entries) & UDPTL_BUF_MASK; i != limit; i = (i + entries) & UDPTL_BUF_MASK) {
+                               if (high_tide < s->tx[i].buf_len) {
+                                       for (j = 0; j < high_tide; j++)
+                                               fec[j] ^= s->tx[i].buf[j];
+                                       for (; j < s->tx[i].buf_len; j++)
+                                               fec[j] = s->tx[i].buf[j];
+                                       high_tide = s->tx[i].buf_len;
+                               } else {
+                                       for (j = 0; j < s->tx[i].buf_len; j++)
+                                               fec[j] ^= s->tx[i].buf[j];
+                               }
+                       }
+                       if (encode_open_type(buf, &len, fec, high_tide) < 0)
+                               return -1;
+               }
+               break;
+       }
+
+       if (s->verbose)
+               fprintf(stderr, "\n");
+       s->tx_seq_no++;
+       return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_set_error_correction(udptl_state_t *s, int ec_scheme, int span, int entries)
+{
+       switch (ec_scheme) {
+       case UDPTL_ERROR_CORRECTION_FEC:
+       case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+       case UDPTL_ERROR_CORRECTION_NONE:
+               s->error_correction_scheme = ec_scheme;
+               break;
+       case -1:
+               /* Just don't change the scheme */
+               break;
+       default:
+               return -1;
+       }
+       if (span >= 0)
+               s->error_correction_span = span;
+       if (entries >= 0)
+               s->error_correction_entries = entries;
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_get_error_correction(udptl_state_t *s, int *ec_scheme, int *span, int *entries)
+{
+       if (ec_scheme)
+               *ec_scheme = s->error_correction_scheme;
+       if (span)
+               *span = s->error_correction_span;
+       if (entries)
+               *entries = s->error_correction_entries;
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_set_local_max_datagram(udptl_state_t *s, int max_datagram)
+{
+       s->local_max_datagram_size = max_datagram;
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_get_local_max_datagram(udptl_state_t *s)
+{
+       return s->local_max_datagram_size;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_set_far_max_datagram(udptl_state_t *s, int max_datagram)
+{
+       s->far_max_datagram_size = max_datagram;
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_get_far_max_datagram(udptl_state_t *s)
+{
+       return s->far_max_datagram_size;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+udptl_state_t *udptl_init(udptl_state_t *s, int ec_scheme, int span, int entries, udptl_rx_packet_handler_t rx_packet_handler, void *user_data)
+{
+       int i;
+
+       if (rx_packet_handler == NULL)
+               return NULL;
+
+       if (s == NULL) {
+               if ((s = (udptl_state_t *) malloc(sizeof(*s))) == NULL)
+                       return NULL;
+       }
+       memset(s, 0, sizeof(*s));
+
+       s->error_correction_scheme = ec_scheme;
+       s->error_correction_span = span;
+       s->error_correction_entries = entries;
+
+       s->far_max_datagram_size = LOCAL_FAX_MAX_DATAGRAM;
+       s->local_max_datagram_size = LOCAL_FAX_MAX_DATAGRAM;
+
+       memset(&s->rx, 0, sizeof(s->rx));
+       memset(&s->tx, 0, sizeof(s->tx));
+       for (i = 0; i <= UDPTL_BUF_MASK; i++) {
+               s->rx[i].buf_len = -1;
+               s->tx[i].buf_len = -1;
+       }
+
+       s->rx_packet_handler = rx_packet_handler;
+       s->user_data = user_data;
+
+       return s;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int udptl_release(udptl_state_t *s)
+{
+       return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/src/mod/applications/mod_fax/udptl.h b/src/mod/applications/mod_fax/udptl.h
new file mode 100644 (file)
index 0000000..b0eb1b2
--- /dev/null
@@ -0,0 +1,153 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2009, Steve Underwood <steveu@coppice.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Contributor(s):
+ * 
+ * Steve Underwood <steveu@coppice.org>
+ *
+ * udptl.h -- UDPTL handling for T.38
+ *
+ */
+
+#if !defined(_UDPTL_H_)
+#define _UDPTL_H_
+
+#define LOCAL_FAX_MAX_DATAGRAM      400
+#define LOCAL_FAX_MAX_FEC_PACKETS   5
+
+#define UDPTL_BUF_MASK              15
+
+typedef int (udptl_rx_packet_handler_t) (void *user_data, const uint8_t msg[], int len, int seq_no);
+
+typedef struct {
+       int buf_len;
+       uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+} udptl_fec_tx_buffer_t;
+
+typedef struct {
+       int buf_len;
+       uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+       int fec_len[LOCAL_FAX_MAX_FEC_PACKETS];
+       uint8_t fec[LOCAL_FAX_MAX_FEC_PACKETS][LOCAL_FAX_MAX_DATAGRAM];
+       int fec_span;
+       int fec_entries;
+} udptl_fec_rx_buffer_t;
+
+struct udptl_state_s {
+       udptl_rx_packet_handler_t *rx_packet_handler;
+       void *user_data;
+
+       /*! This option indicates the error correction scheme used in transmitted UDPTL
+          packets. */
+       int error_correction_scheme;
+
+       /*! This option indicates the number of error correction entries transmitted in
+          UDPTL packets. */
+       int error_correction_entries;
+
+       /*! This option indicates the span of the error correction entries in transmitted
+          UDPTL packets (FEC only). */
+       int error_correction_span;
+
+       /*! This option indicates the maximum size of a datagram that can be accepted by
+          the remote device. */
+       int far_max_datagram_size;
+
+       /*! This option indicates the maximum size of a datagram that we are prepared to
+          accept. */
+       int local_max_datagram_size;
+
+       int verbose;
+
+       int tx_seq_no;
+       int rx_seq_no;
+       int rx_expected_seq_no;
+
+       udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1];
+       udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1];
+};
+
+enum {
+       UDPTL_ERROR_CORRECTION_NONE,
+       UDPTL_ERROR_CORRECTION_FEC,
+       UDPTL_ERROR_CORRECTION_REDUNDANCY
+};
+
+typedef struct udptl_state_s udptl_state_t;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*! \brief Process an arriving UDPTL packet.
+    \param s The UDPTL context.
+    \param buf The UDPTL packet buffer.
+    \param len The length of the packet.
+    \return 0 for OK. */
+       int udptl_rx_packet(udptl_state_t *s, const uint8_t buf[], int len);
+
+/*! \brief Construct a UDPTL packet, ready for transmission.
+    \param s The UDPTL context.
+    \param buf The UDPTL packet buffer.
+    \param msg The primary packet.
+    \param len The length of the primary packet.
+    \return The length of the constructed UDPTL packet. */
+       int udptl_build_packet(udptl_state_t *s, uint8_t buf[], const uint8_t msg[], int msg_len);
+
+/*! \brief Change the error correction settings of a UDPTL context.
+    \param s The UDPTL context.
+    \param ec_scheme One of the optional error correction schemes.
+    \param span The packet span over which error correction should be applied.
+    \param entries The number of error correction entries to include in packets.
+    \return 0 for OK. */
+       int udptl_set_error_correction(udptl_state_t *s, int ec_scheme, int span, int entries);
+
+/*! \brief Check the error correction settings of a UDPTL context.
+    \param s The UDPTL context.
+    \param ec_scheme One of the optional error correction schemes.
+    \param span The packet span over which error correction is being applied.
+    \param entries The number of error correction being included in packets.
+    \return 0 for OK. */
+       int udptl_get_error_correction(udptl_state_t *s, int *ec_scheme, int *span, int *entries);
+
+       int udptl_set_local_max_datagram(udptl_state_t *s, int max_datagram);
+
+       int udptl_get_local_max_datagram(udptl_state_t *s);
+
+       int udptl_set_far_max_datagram(udptl_state_t *s, int max_datagram);
+
+       int udptl_get_far_max_datagram(udptl_state_t *s);
+
+/*! \brief Initialise a UDPTL context.
+    \param s The UDPTL context.
+    \param ec_scheme One of the optional error correction schemes.
+    \param span The packet span over which error correction should be applied.
+    \param entries The number of error correction entries to include in packets.
+    \param rx_packet_handler The callback function, used to report arriving IFP packets.
+    \param user_data An opaque pointer supplied to rx_packet_handler.
+    \return A pointer to the UDPTL context, or NULL if there was a problem. */
+       udptl_state_t *udptl_init(udptl_state_t *s, int ec_scheme, int span, int entries, udptl_rx_packet_handler_t rx_packet_handler, void *user_data);
+
+/*! \brief Release a UDPTL context.
+    \param s The UDPTL context.
+    \return 0 for OK. */
+       int udptl_release(udptl_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
+/*- End of file ------------------------------------------------------------*/
index 31367e30e54e6495c642d5192077e28c25e0ddc5..b4980bf8a17b7fed407370d3c74852fcccff0634 100644 (file)
@@ -1,8 +1,17 @@
 include $(top_srcdir)/build/modmake.rulesam
 MODNAME=mod_t38gateway
 
+TIFF_DIR=$(switch_srcdir)/libs/tiff-3.8.2
+TIFF_BUILDDIR=$(switch_builddir)/libs/tiff-3.8.2
+TIFF_LA=$(TIFF_BUILDDIR)/libtiff/libtiff.la
+
+SPANDSP_DIR=$(switch_srcdir)/libs/spandsp
+SPANDSP_BUILDDIR=$(switch_builddir)/libs/spandsp
+SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la
+
 mod_LTLIBRARIES = mod_t38gateway.la
 mod_t38gateway_la_SOURCES  = mod_t38gateway.c udptl.c
-mod_t38gateway_la_CFLAGS   = $(AM_CFLAGS)
-mod_t38gateway_la_LIBADD   = $(switch_builddir)/libfreeswitch.la
-mod_t38gateway_la_LDFLAGS  = -avoid-version -module -no-undefined -shared
+mod_t38gateway_la_CFLAGS   = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff
+mod_t38gateway_la_LIBADD   = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA)
+mod_t38gateway_la_LDFLAGS  = -avoid-version -module -no-undefined -shared -ljpeg
+
index ed866934f8361317cea3f74eb6993dc63d1eda0c..13a7cc837e108efd70a144a7000e0e346ff2f06f 100644 (file)
@@ -199,7 +199,7 @@ SWITCH_STANDARD_APP(t38gateway_start_function)
        t38gateway_info->t38_core = t38_gateway_get_t38_core_state(t38gateway_info->t38);
        t38gateway_info->udptl = udptl_init(NULL, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, rx_packet_handler, (void *) t38gateway_info);
 
-       status = switch_core_media_bug_add(session, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
+       status = switch_core_media_bug_add(session, "T.38 gateway", NULL, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
 
        if (status != SWITCH_STATUS_SUCCESS) {
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure hooking to stream\n");
@@ -316,7 +316,7 @@ SWITCH_STANDARD_API(t38gateway_api_main)
 
        /* Add a media bug that allows me to intercept the 
         * reading leg of the audio stream */
-       status = switch_core_media_bug_add(t38gateway_session, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
+       status = switch_core_media_bug_add(t38gateway_session, "T.38 gateway", NULL, t38gateway_callback, t38gateway_info, 0, SMBF_READ_STREAM, &bug);
 
        /* If adding a media bug fails exit */
        if (status != SWITCH_STATUS_SUCCESS) {
index d28f37e415420cd1cf082e021538bb789b556cd6..a1d367e5b9f7f91c602234f3a0080aa77e3cf0dc 100644 (file)
@@ -977,9 +977,9 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
                                                        if (tech_pvt->last_codec_ms && tech_pvt->last_codec_ms == codec_ms) {
                                                                tech_pvt->mismatch_count++;
                                                        }
-
+                                                       
                                                        tech_pvt->last_codec_ms = codec_ms;
-
+                                                       
                                                        if (tech_pvt->mismatch_count > MAX_MISMATCH_FRAMES) {
                                                                if (switch_rtp_ready(tech_pvt->rtp_session) && codec_ms != tech_pvt->codec_ms) {
                                                                        const char *val;
@@ -996,19 +996,23 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
                                                                        }
 
                                                                        tech_pvt->read_frame.datalen = 0;
-                                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
-                                                                                                         "We were told to use ptime %d but what they meant to say was %d\n"
-                                                                                                         "This issue has so far been identified to happen on the following broken platforms/devices:\n"
-                                                                                                         "Linksys/Sipura aka Cisco\n"
-                                                                                                         "ShoreTel\n"
-                                                                                                         "Sonus/L3\n"
-                                                                                                         "We will try to fix it but some of the devices on this list are so broken who knows what will happen..\n",
-                                                                                                         (int) tech_pvt->codec_ms, (int) codec_ms);
 
-                                                                       switch_channel_set_variable_printf(channel, "sip_h_X-Broken-PTIME", "Adv=%d;Sent=%d",
-                                                                                                                                          (int) tech_pvt->codec_ms, (int) codec_ms);
-
-                                                                       tech_pvt->codec_ms = codec_ms;
+                                                                       if (codec_ms != tech_pvt->codec_ms) {
+                                                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
+                                                                                                                 "We were told to use ptime %d but what they meant to say was %d\n"
+                                                                                                                 "This issue has so far been identified to happen on the following broken platforms/devices:\n"
+                                                                                                                 "Linksys/Sipura aka Cisco\n"
+                                                                                                                 "ShoreTel\n"
+                                                                                                                 "Sonus/L3\n"
+                                                                                                                 "We will try to fix it but some of the devices on this list are so broken,\n"
+                                                                                                                 "who knows what will happen..\n",
+                                                                                                                 (int) tech_pvt->codec_ms, (int) codec_ms);
+
+                                                                               switch_channel_set_variable_printf(channel, "sip_h_X-Broken-PTIME", "Adv=%d;Sent=%d",
+                                                                                                                                                  (int) tech_pvt->codec_ms, (int) codec_ms);
+
+                                                                               tech_pvt->codec_ms = codec_ms;
+                                                                       }
 
                                                                        if (sofia_glue_tech_set_codec(tech_pvt, 2) != SWITCH_STATUS_SUCCESS) {
                                                                                *frame = NULL;
@@ -1049,6 +1053,7 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
                                                                        tech_pvt->last_ts = 0;
 
                                                                        /* inform them of the codec they are actually sending */
+
                                                                        if (++tech_pvt->codec_reinvites > 2) {
                                                                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
                                                                                                                  "Ok, some devices *cough* X-lite *cough*\n"
@@ -1065,7 +1070,10 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
                                                } else {
                                                        tech_pvt->mismatch_count = 0;
                                                }
+
                                                tech_pvt->last_ts = tech_pvt->read_frame.timestamp;
+
+
                                        } else {
                                                tech_pvt->mismatch_count = 0;
                                                tech_pvt->last_ts = 0;
@@ -1512,16 +1520,70 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
                }
                break;
 
+       case SWITCH_MESSAGE_INDICATE_UDPTL_MODE:
+               {
+                       switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
+                       
+                       if (!t38_options) {
+                               nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
+                               goto end_lock;
+                       }
 
-       case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA:
+                       if (switch_rtp_ready(tech_pvt->rtp_session)) {
+                               switch_rtp_udptl_mode(tech_pvt->rtp_session);
+                       }
+               }
+               break;
+       case SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION:
                {
-                       switch_t38_options_t *t38_options = (switch_t38_options_t *) msg->pointer_arg;
+                       switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
+                       
+                       if (!t38_options) {
+                               nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
+                               goto end_lock;
+                       }
+
+                       if (switch_rtp_ready(tech_pvt->rtp_session)) {
+                               switch_rtp_udptl_mode(tech_pvt->rtp_session);
+                       }
+
+                       
+                       sofia_glue_set_image_sdp(tech_pvt, t38_options, msg->numeric_arg);
 
-                       sofia_glue_set_image_sdp(tech_pvt, t38_options);
+                       if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) {
+                               char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_RESPONSE_HEADER_PREFIX);
+                               if (sofia_use_soa(tech_pvt)) {
+                                       nua_respond(tech_pvt->nh, SIP_200_OK,
+                                                               NUTAG_AUTOANSWER(0),
+                                                               SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
+                                                               SIPTAG_CALL_INFO_STR(switch_channel_get_variable(tech_pvt->channel, SOFIA_SIP_HEADER_PREFIX "call_info")),
+                                                               SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str),
+                                                               SOATAG_REUSE_REJECTED(1), SOATAG_ORDERED_USER(1), SOATAG_AUDIO_AUX("cn telephone-event"), NUTAG_INCLUDE_EXTRA_SDP(1),
+                                                               TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
+                                                               TAG_END());
+                               } else {
+                                       nua_respond(tech_pvt->nh, SIP_200_OK,
+                                                               NUTAG_AUTOANSWER(0),
+                                                               NUTAG_MEDIA_ENABLE(0),
+                                                               SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
+                                                               SIPTAG_CALL_INFO_STR(switch_channel_get_variable(tech_pvt->channel, SOFIA_SIP_HEADER_PREFIX "call_info")),
+                                                               SIPTAG_CONTENT_TYPE_STR("application/sdp"),
+                                                               SIPTAG_PAYLOAD_STR(tech_pvt->local_sdp_str),
+                                                               TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
+                                                               TAG_END());
+                               }
+                               switch_safe_free(extra_headers);
+                               sofia_set_flag_locked(tech_pvt, TFLAG_ANS);
+                       }                       
+               }
+               break;
 
-                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Sending request for image media. %s\n",
-                                                         switch_channel_get_name(channel), tech_pvt->local_sdp_str);
+       case SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA:
+               {
+                       switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
 
+                       sofia_glue_set_image_sdp(tech_pvt, t38_options, msg->numeric_arg);
+                       
                        switch_channel_set_flag(channel, CF_REQ_MEDIA);
                        sofia_set_flag_locked(tech_pvt, TFLAG_SENT_UPDATE);
                        sofia_glue_do_invite(session);
index b3f71b26c71e16a55ce58f7cacd87d22d265a8ef..8d165f04ed75154c361471cb833f7bd7d6068676 100644 (file)
@@ -930,7 +930,7 @@ void sofia_glue_toggle_hold(private_object_t *tech_pvt, int sendonly);
 const char *sofia_state_string(int state);
 switch_status_t sofia_glue_tech_set_codec(private_object_t *tech_pvt, int force);
 void sofia_wait_for_reply(struct private_object *tech_pvt, nua_event_t event, uint32_t timeout);
-void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options);
+void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options, int insist);
 
 /*
  * SLA (shared line appearance) entrypoints
index 19cccc6aed87e5854047cd26905111449b2f28ea..6405a8704cca849f86dbe654fbb36b7d77b5cb64 100644 (file)
@@ -4578,6 +4578,11 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
                                                        sdp_parser_free(parser);
                                                }
                                        }
+
+                                       if (match && switch_channel_test_app_flag(tech_pvt->channel, CF_APP_T38)) {
+                                               goto done;
+                                       }
+
                                        if (match) {
                                                if (sofia_glue_tech_choose_port(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) {
                                                        goto done;
index 7777ca0cb870f5a137bce961bead376bb174facd..ce2483061a5cef7b0d9d8de017bbea705131cb48 100644 (file)
@@ -37,7 +37,7 @@
 #include <switch_stun.h>
 
 
-void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options)
+void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *t38_options, int insist)
 {
        char buf[2048];
        const char *ip = t38_options->ip;
@@ -45,6 +45,9 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
        const char *family = "IP4";
        const char *username = tech_pvt->profile->username;
 
+
+       //sofia_clear_flag(tech_pvt, TFLAG_ENABLE_SOA);
+       
        if (!ip) {
                if (!(ip = tech_pvt->adv_sdp_audio_ip)) {
                        ip = tech_pvt->proxy_sdp_audio_ip;
@@ -76,15 +79,29 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
        }
 
        tech_pvt->session_id++;
-
+       
        family = strchr(ip, ':') ? "IP6" : "IP4";
+       
+
        switch_snprintf(buf, sizeof(buf),
                                        "v=0\n"
                                        "o=%s %010u %010u IN %s %s\n"
                                        "s=%s\n"
                                        "c=IN %s %s\n"
-                                       "t=0 0\n"
+                                       "t=0 0\n",
+                                       username,
+                                       tech_pvt->owner_id,
+                                       tech_pvt->session_id,
+                                       family,
+                                       ip,
+                                       username,
+                                       family,
+                                       ip);
+       
+
+       switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
                                        "m=image %d udptl t38\n"
+                                       "a=T38FaxVersion:%d\n"
                                        "a=T38MaxBitRate:%d\n"
                                        "%s"
                                        "%s"
@@ -94,23 +111,32 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t *
                                        "a=T38FaxMaxDatagram:%d\n"
                                        "a=T38FaxUdpEC:%s\n"
                                        "a=T38VendorInfo:%s\n",
-                                       username,
-                                       tech_pvt->owner_id,
-                                       tech_pvt->session_id,
-                                       family,
-                                       ip,
-                                       username,
-                                       family,
-                                       ip,
                                        port,
+                                       t38_options->T38FaxVersion,
                                        t38_options->T38MaxBitRate,
                                        t38_options->T38FaxFillBitRemoval ? "a=T38FaxFillBitRemoval\n" : "",
                                        t38_options->T38FaxTranscodingMMR ? "a=T38FaxTranscodingMMR\n" : "",
                                        t38_options->T38FaxTranscodingJBIG ? "a=T38FaxTranscodingJBIG\n" : "",
                                        t38_options->T38FaxRateManagement,
-                                       t38_options->T38FaxMaxBuffer, t38_options->T38FaxMaxDatagram, t38_options->T38FaxUdpEC, t38_options->T38VendorInfo);
+                                       t38_options->T38FaxMaxBuffer, 
+                                       t38_options->T38FaxMaxDatagram, 
+                                       t38_options->T38FaxUdpEC, 
+                                       t38_options->T38VendorInfo);
+       
+
+
+       if (insist) {
+               switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 
+                                               "m=audio 0 RTP/AVP 19\n");
+       }
 
        sofia_glue_tech_set_local_sdp(tech_pvt, buf, SWITCH_TRUE);
+
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "%s image media sdp:\n%s\n",
+                                         switch_channel_get_name(tech_pvt->channel), tech_pvt->local_sdp_str);
+       
+
 }
 
 void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32_t port, const char *sr, int force)
@@ -1582,7 +1608,9 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
                return status;
        }
 
-       sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0);
+       if (!switch_channel_get_private(tech_pvt->channel, "t38_options") || zstr(tech_pvt->local_sdp_str)) {
+               sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0);
+       }
 
        sofia_set_flag_locked(tech_pvt, TFLAG_READY);
 
@@ -3417,10 +3445,24 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t *
                }
 
                if (got_udptl && m->m_type == sdp_media_image && m->m_port) {
-                       switch_t38_options_t *t38_options = switch_core_session_alloc(tech_pvt->session, sizeof(switch_t38_options_t));
+                       switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options");
+
+                       if (!t38_options) {
+                               t38_options = switch_core_session_alloc(tech_pvt->session, sizeof(switch_t38_options_t));
+                       }
+                       
+                       t38_options->port = m->m_port;
+
+                       if (m->m_connections) {
+                               t38_options->ip = switch_core_session_strdup(tech_pvt->session, m->m_connections->c_address);
+                       } else if (sdp && sdp->sdp_connection) {
+                               t38_options->ip = switch_core_session_strdup(tech_pvt->session, sdp->sdp_connection->c_address);
+                       }
 
                        for (attr = m->m_attributes; attr; attr = attr->a_next) {
-                               if (!strcasecmp(attr->a_name, "T38MaxBitRate") && attr->a_value) {
+                                if (!strcasecmp(attr->a_name, "T38FaxVersion") && attr->a_value) {
+                                        t38_options->T38FaxVersion = (uint16_t) atoi(attr->a_value);
+                               } else if (!strcasecmp(attr->a_name, "T38MaxBitRate") && attr->a_value) {
                                        t38_options->T38MaxBitRate = (uint32_t) atoi(attr->a_value);
                                } else if (!strcasecmp(attr->a_name, "T38FaxFillBitRemoval")) {
                                        t38_options->T38FaxFillBitRemoval = SWITCH_TRUE;
@@ -3440,13 +3482,13 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t *
                                        t38_options->T38VendorInfo = switch_core_session_strdup(tech_pvt->session, attr->a_value);
                                }
                        }
-
+                       
                        switch_channel_set_variable(tech_pvt->channel, "has_t38", "true");
                        switch_channel_set_private(tech_pvt->channel, "t38_options", t38_options);
-
-                       //switch_channel_set_flag(tech_pvt->channel, CF_PROXY_MEDIA);
-                       //switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA);
-
+                       switch_channel_set_app_flag(tech_pvt->channel, CF_APP_T38);
+                       /* do nothing here, mod_fax will trigger a response (if it's listening =/)*/
+                       match = 1;
+                       goto done;
                } else if (m->m_type == sdp_media_audio && m->m_port && !got_audio) {
                        sdp_rtpmap_t *map;
                        for (attr = m->m_attributes; attr; attr = attr->a_next) {
index 980280208d9e1e18da08f34be0a6a5537cfdf83a..842c3c97243904d14571a105f6ce70ccb6a85ef0 100644 (file)
@@ -556,6 +556,7 @@ static const char *message_names[] = {
        "APPLICATION_EXEC",
        "APPLICATION_EXEC_COMPLETE",
        "PHONE_EVENT",
+       "T38_DESCRIPTION"
        "INVALID"
 };
 
index d3a06fa7931782d5afcff2821ed43dfaa04c05e0..623b7e56b2012618606104662b1bb4a0bc505a67 100644 (file)
@@ -1025,6 +1025,33 @@ SWITCH_DECLARE(switch_port_t) switch_rtp_get_remote_port(switch_rtp_t *rtp_sessi
 }
 
 
+SWITCH_DECLARE(switch_status_t) switch_rtp_udptl_mode(switch_rtp_t *rtp_session) 
+{
+       READ_INC(rtp_session);
+       WRITE_INC(rtp_session);
+
+       if (rtp_session->timer.timer_interface) {
+               switch_core_timer_destroy(&rtp_session->timer);
+               memset(&rtp_session->timer, 0, sizeof(rtp_session->timer));
+       }
+
+       switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_UDPTL);
+       switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA);
+       switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, FALSE);
+
+       switch_clear_flag(rtp_session, SWITCH_RTP_FLAG_USE_TIMER);
+       switch_clear_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK);
+               
+       WRITE_DEC(rtp_session);
+       READ_DEC(rtp_session);
+
+       switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_FLUSH);
+
+       return SWITCH_STATUS_SUCCESS;
+
+}
+
+
 SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_session, const char *host, switch_port_t port, switch_port_t remote_rtcp_port,
                                                                                                                          switch_bool_t change_adv_addr, const char **err)
 {
@@ -1227,7 +1254,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_change_interval(switch_rtp_t *rtp_ses
                                                                                         rtp_session->timer_name, ms_per_packet / 1000,
                                                                                         samples_per_interval, rtp_session->pool)) == SWITCH_STATUS_SUCCESS) {
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
-                                                         "RE-Starting timer [%s] %d bytes per %dms\n", rtp_session->timer_name, samples_per_interval, ms_per_packet);
+                                                         "RE-Starting timer [%s] %d bytes per %dms\n", rtp_session->timer_name, samples_per_interval, ms_per_packet / 1000);
                } else {
                        memset(&rtp_session->timer, 0, sizeof(rtp_session->timer));
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
@@ -2451,9 +2478,14 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
                        }
                }
 
-               if (bytes && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA)) {
+               if (bytes && (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_UDPTL))) {
                        /* Fast PASS! */
                        *flags |= SFF_PROXY_PACKET;
+
+                       if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_UDPTL)) {
+                               *flags |= SFF_UDPTL_PACKET;
+                       }
+
                        ret = (int) bytes;
                        goto end;
                }
@@ -3475,12 +3507,12 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra
                return -1;
        }
        
-       if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA)) {
+       if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_UDPTL)) {
                switch_size_t bytes;
                char bufa[30];
                const char *tx_host;
                /* Fast PASS! */
-               if (!switch_test_flag(frame, SFF_PROXY_PACKET)) {
+               if (!switch_test_flag(frame, SFF_PROXY_PACKET) && !switch_test_flag(frame, SFF_UDPTL_PACKET)) {
                        return 0;
                }
                bytes = frame->packetlen;