From 65400642f786d8c483a12fc092bd33be75a7778e Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Mon, 31 May 2010 06:04:12 -0400 Subject: [PATCH] rework of new mod_spandsp to have functions broken up into different c files --- src/mod/applications/mod_spandsp/Makefile.am | 2 +- .../mod_spandsp/mod_spandsp.2008.vcproj | 16 + .../applications/mod_spandsp/mod_spandsp.c | 2657 +---------------- .../applications/mod_spandsp/mod_spandsp.h | 64 + .../mod_spandsp/mod_spandsp_codecs.c | 962 ++++++ .../mod_spandsp/mod_spandsp_dsp.c | 138 + .../mod_spandsp/mod_spandsp_fax.c | 1743 +++++++++++ 7 files changed, 2949 insertions(+), 2633 deletions(-) create mode 100644 src/mod/applications/mod_spandsp/mod_spandsp.h create mode 100644 src/mod/applications/mod_spandsp/mod_spandsp_codecs.c create mode 100644 src/mod/applications/mod_spandsp/mod_spandsp_dsp.c create mode 100644 src/mod/applications/mod_spandsp/mod_spandsp_fax.c diff --git a/src/mod/applications/mod_spandsp/Makefile.am b/src/mod/applications/mod_spandsp/Makefile.am index b99d485313..7d2b30f69d 100644 --- a/src/mod/applications/mod_spandsp/Makefile.am +++ b/src/mod/applications/mod_spandsp/Makefile.am @@ -10,7 +10,7 @@ SPANDSP_BUILDDIR=$(switch_builddir)/libs/spandsp SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la mod_LTLIBRARIES = mod_spandsp.la -mod_spandsp_la_SOURCES = mod_spandsp.c udptl.c +mod_spandsp_la_SOURCES = mod_spandsp.c udptl.c mod_spandsp_fax.c mod_spandsp_dsp.c mod_spandsp_codecs.c mod_spandsp_la_CFLAGS = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff -I. mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA) mod_spandsp_la_LDFLAGS = -avoid-version -module -no-undefined -shared -ljpeg diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.2008.vcproj b/src/mod/applications/mod_spandsp/mod_spandsp.2008.vcproj index 3f229cba8c..580c99049e 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.2008.vcproj +++ b/src/mod/applications/mod_spandsp/mod_spandsp.2008.vcproj @@ -285,6 +285,22 @@ RelativePath=".\mod_spandsp.c" > + + + + + + + + diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c index c3ef8d2830..78bf184f73 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp.c @@ -17,1968 +17,25 @@ * The Original Code is FreeSWITCH mod_spandsp. * * The Initial Developer of the Original Code is - * Massimo Cetra + * Michael Jerris + * Massimo Cetra * Anthony Minessale II + * Brian West * Steve Underwood - * Michael Jerris * Antonio Gallo - * - * mod_spandsp.c -- Fax applications and VoIP Codecs (G.711, G.722, G.726, GSM-FR, IMA_ADPCM, LPC10) provided by SpanDSP + * mod_spandsp.c -- Module implementing spandsp fax, dsp, and codec functionality * */ -#include - -#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES -#include +#include "mod_spandsp.h" #include -#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_GW -} application_mode_t; - -typedef enum { - T38_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 { - switch_memory_pool_t *pool; - switch_mutex_t *mutex; - - uint32_t total_sessions; - - 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; - char *spool; -} globals; - -struct pvt_s { - switch_core_session_t *session; - - application_mode_t app_mode; - - 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; - char *header; - - int use_ecm; - int disable_v17; - int verbose; - int caller; - - int tx_page_start; - int tx_page_end; - - int done; - - t38_mode_t t38_mode; - - 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 -*****************************************************************************/ - -static void counter_increment(void) -{ - switch_mutex_lock(globals.mutex); - globals.total_sessions++; - switch_mutex_unlock(globals.mutex); -} - -static void spanfax_log_message(int level, const char *msg) -{ - int fs_log_level; - - switch (level) { - case SPAN_LOG_NONE: - return; - case SPAN_LOG_ERROR: - case SPAN_LOG_PROTOCOL_ERROR: - fs_log_level = SWITCH_LOG_ERROR; - break; - case SPAN_LOG_WARNING: - case SPAN_LOG_PROTOCOL_WARNING: - fs_log_level = SWITCH_LOG_WARNING; - break; - case SPAN_LOG_FLOW: - case SPAN_LOG_FLOW_2: - case SPAN_LOG_FLOW_3: - default: /* SPAN_LOG_DEBUG, SPAN_LOG_DEBUG_2, SPAN_LOG_DEBUG_3 */ - fs_log_level = SWITCH_LOG_DEBUG; - break; - } - - if (!zstr(msg)) { - switch_log_printf(SWITCH_CHANNEL_LOG, fs_log_level, "%s", msg); - } -} - -/* - * Called at the end of the document - */ -static void phase_e_handler(t30_state_t *s, void *user_data, int result) -{ - t30_stats_t t; - const char *local_ident; - const char *far_ident; - switch_core_session_t *session; - switch_channel_t *channel; - pvt_t *pvt; - char *tmp; - - pvt = (pvt_t *) user_data; - switch_assert(pvt); - - session = pvt->session; - switch_assert(session); - - channel = switch_core_session_get_channel(session); - switch_assert(channel); - - t30_get_transfer_statistics(s, &t); - local_ident = switch_str_nil(t30_get_tx_ident(s)); - far_ident = switch_str_nil(t30_get_rx_ident(s)); - - 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) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax successfully received.\n"); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax successfully managed. How ?\n"); - } - switch_channel_set_variable(channel, "fax_success", "1"); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax processing not successful - result (%d) %s.\n", result, - t30_completion_code_to_str(result)); - switch_channel_set_variable(channel, "fax_success", "0"); - } - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote station id: %s\n", far_ident); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local station id: %s\n", local_ident); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Pages transferred: %i\n", - pvt->app_mode == FUNCTION_TX ? t.pages_tx : t.pages_rx); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Total fax pages: %i\n", t.pages_in_file); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Image resolution: %ix%i\n", t.x_resolution, t.y_resolution); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "ECM status %s\n", (t.error_correcting_mode) ? "on" : "off"); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "remote country: %s\n", switch_str_nil(t30_get_rx_country(s))); - 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"); - - /* - Set our channel variables - */ - - tmp = switch_mprintf("%i", result); - if (tmp) { - switch_channel_set_variable(channel, "fax_result_code", tmp); - switch_safe_free(tmp); - } - - switch_channel_set_variable(channel, "fax_result_text", t30_completion_code_to_str(result)); - - switch_channel_set_variable(channel, "fax_ecm_used", (t.error_correcting_mode) ? "on" : "off"); - switch_channel_set_variable(channel, "fax_local_station_id", local_ident); - switch_channel_set_variable(channel, "fax_remote_station_id", far_ident); - - tmp = switch_mprintf("%i", pvt->app_mode == FUNCTION_TX ? t.pages_tx : t.pages_rx); - if (tmp) { - switch_channel_set_variable(channel, "fax_document_transferred_pages", tmp); - switch_safe_free(tmp); - } - - tmp = switch_mprintf("%i", t.pages_in_file); - if (tmp) { - switch_channel_set_variable(channel, "fax_document_total_pages", tmp); - switch_safe_free(tmp); - } - - tmp = switch_mprintf("%ix%i", t.x_resolution, t.y_resolution); - if (tmp) { - switch_channel_set_variable(channel, "fax_image_resolution", tmp); - switch_safe_free(tmp); - } - - tmp = switch_mprintf("%d", t.image_size); - if (tmp) { - switch_channel_set_variable(channel, "fax_image_size", tmp); - switch_safe_free(tmp); - } - - tmp = switch_mprintf("%d", t.bad_rows); - if (tmp) { - switch_channel_set_variable(channel, "fax_bad_rows", tmp); - switch_safe_free(tmp); - } - - tmp = switch_mprintf("%i", t.bit_rate); - if (tmp) { - switch_channel_set_variable(channel, "fax_transfer_rate", tmp); - switch_safe_free(tmp); - } - - /* switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); */ - - pvt->done = 1; - - /* - TODO Fire events - */ -} - -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); - - channel = switch_core_session_get_channel(session); - switch_assert(channel); - - - switch (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)); - } - if (pvt->fax_state == NULL) { - return SWITCH_STATUS_FALSE; - } - - fax = pvt->fax_state; - t30 = fax_get_t30_state(fax); - - memset(fax, 0, sizeof(fax_state_t)); - if (fax_init(fax, pvt->caller) == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my fax structs\n"); - return SWITCH_STATUS_FALSE; - } - - fax_set_transmit_on_idle(fax, TRUE); - - span_log_set_message_handler(&fax->logging, spanfax_log_message); - span_log_set_message_handler(&t30->logging, spanfax_log_message); - - if (pvt->verbose) { - 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; - } - - /* add to timer thread processing */ - add_pvt(pvt); - - t38 = pvt->t38_state; - t30 = t38_terminal_get_t30_state(t38); - - memset(t38, 0, sizeof(t38_terminal_state_t)); - - 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 (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; - } - - span_log_set_message_handler(&t38->logging, spanfax_log_message); - span_log_set_message_handler(&t30->logging, spanfax_log_message); - - 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); - } - break; - 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); /* 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; -} - -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; - } else { - terminate = 1; - } - - t30 = fax_get_t30_state(pvt->fax_state); - if (terminate && t30) { - t30_terminate(t30); - } - - fax_release(pvt->fax_state); - } - - if (pvt->t38_state) { - - /* remove from timer thread processing */ - del_pvt(pvt); - - if (pvt->t38_state) { - terminate = 1; - } else { - terminate = 0; - } - - t30 = t38_terminal_get_t30_state(pvt->t38_state); - - if (terminate && t30) { - t30_terminate(t30); - } - - 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 -*****************************************************************************/ - -static pvt_t *pvt_init(switch_core_session_t *session, application_mode_t app_mode) -{ - switch_channel_t *channel; - pvt_t *pvt = NULL; - const char *tmp; - - /* Make sure we have a valid channel when starting the FAX application */ - channel = switch_core_session_get_channel(session); - switch_assert(channel != NULL); - - if (!switch_channel_media_ready(channel)) { - switch_channel_answer(channel); - } - - /* Allocate our structs */ - pvt = switch_core_session_alloc(session, sizeof(pvt_t)); - pvt->session = session; - - pvt->app_mode = app_mode; - - pvt->tx_page_start = -1; - pvt->tx_page_end = -1; - - - switch(pvt->app_mode) { - - 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 */ - - if ((tmp = switch_channel_get_variable(channel, "fax_use_ecm"))) { - pvt->use_ecm = switch_true(tmp); - } else { - pvt->use_ecm = globals.use_ecm; - } - - if ((tmp = switch_channel_get_variable(channel, "fax_disable_v17"))) { - pvt->disable_v17 = switch_true(tmp); - } else { - pvt->disable_v17 = globals.disable_v17; - } - - if ((tmp = switch_channel_get_variable(channel, "fax_verbose"))) { - pvt->verbose = switch_true(tmp); - } else { - pvt->verbose = globals.verbose; - } - - if ((tmp = switch_channel_get_variable(channel, "fax_force_caller"))) { - if (switch_true(tmp)) { - pvt->caller = 1; - } else { - pvt->caller = 0; - } - } - - if ((tmp = switch_channel_get_variable(channel, "fax_ident"))) { - pvt->ident = switch_core_session_strdup(session, tmp); - } else { - pvt->ident = switch_core_session_strdup(session, globals.ident); - } - - if ((tmp = switch_channel_get_variable(channel, "fax_header"))) { - pvt->header = switch_core_session_strdup(session, tmp); - } else { - pvt->header = switch_core_session_strdup(session, globals.header); - } - - if (pvt->app_mode == FUNCTION_TX) { - if ((tmp = switch_channel_get_variable(channel, "fax_start_page"))) { - pvt->tx_page_start = atoi(tmp); - } - - if ((tmp = switch_channel_get_variable(channel, "fax_end_page"))) { - pvt->tx_page_end = atoi(tmp); - } - - if (pvt->tx_page_end < -1) { - pvt->tx_page_end = -1; - } - - if (pvt->tx_page_start < -1) { - pvt->tx_page_start = -1; - } - - 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); - if (pvt->app_mode == FUNCTION_TX) { - if ((switch_file_exists(pvt->filename, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot send inexistant fax file [%s]\n", - switch_str_nil(pvt->filename)); - goto done; - } - } - } else { - if (pvt->app_mode == FUNCTION_TX) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Fax TX filename not set.\n"); - goto done; - } else if (pvt->app_mode == FUNCTION_RX) { - char *fname; - const char *prefix; - switch_time_t time; - - time = switch_time_now(); - - if (!(prefix = switch_channel_get_variable(channel, "fax_prefix"))) { - prefix = globals.prepend_string; - } - - fname = switch_mprintf("%s/%s-%ld-%ld.tif", globals.spool, prefix, globals.total_sessions, time); - if (fname) { - pvt->filename = switch_core_session_strdup(session, fname); - switch_safe_free(fname); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot automatically set fax RX destination file\n"); - goto done; - } - } else { - assert(0); /* UH ?? */ - } - } - - /* - *** Initialize the SpanDSP elements *** - - Note: we could analyze if a fax was already detected in previous stages - and if so, when T.38 will be supported, send a reinvite in T38_MODE, - bypassing AUDIO_MODE. - */ - - if ((spanfax_init(pvt, AUDIO_MODE) != SWITCH_STATUS_SUCCESS)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize Fax engine\n"); - return; - } - - /* - Note: Disable echocan on the channel, remember to call app "disable_ec" in the dialplan - before invoking fax applications - */ - - /* - Note: we are disabling the Jitterbuffer, here, before we answer. - If you have set it to something else and the channel is pre-answered, - it will have no effect. Make sure that if you want more reliable - faxes, it is disabled. - */ - switch_channel_set_variable(channel, "jitterbuffer_msec", "0"); - - - /* We store the original channel codec before switching both - * legs of the calls to a linear 16 bit codec that is the one - * 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, - 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 done; - } - - 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 done; - } - - 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; - - /* - if we are in T.38 mode, we should: 1- initialize the ptv->t38_state stuff, if not done - and then set some callbacks when reading frames. - The only thing we need, then, in this loop, is: - - read a frame without blocking - - eventually feed that frame in spandsp, - - 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); - - if (!SWITCH_READ_ACCEPTABLE(status) || pvt->done) { - /* Our duty is over */ - 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)) { - /* 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; - } - } - - if ((tx = fax_tx(pvt->fax_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 done; - } - - 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 done; - } - - } - - done: - /* Destroy the SpanDSP structures */ - spanfax_destroy(pvt); - - /* restore the original codecs over the channel */ - - 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); - } -} - -/* ************************************************************************** - CONFIGURATION - ************************************************************************* */ - -void load_configuration(switch_bool_t reload) -{ - switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, cfg = NULL; - - if ((xml = switch_xml_open_cfg("fax.conf", &cfg, NULL))) { - if ((x_lists = switch_xml_child(cfg, "settings"))) { - for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) { - const char *name = switch_xml_attr(x_list, "name"); - const char *value = switch_xml_attr(x_list, "value"); - - if (zstr(name)) { - continue; - } - - if (zstr(value)) { - continue; - } - - if (!strcmp(name, "use-ecm")) { - if (switch_true(value)) - globals.use_ecm = 1; - else - globals.use_ecm = 0; - } else if (!strcmp(name, "verbose")) { - if (switch_true(value)) - globals.verbose = 1; - else - globals.verbose = 0; - } else if (!strcmp(name, "disable-v17")) { - if (switch_true(value)) - 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")) { - strncpy(globals.header, value, sizeof(globals.header) - 1); - } else if (!strcmp(name, "spool-dir")) { - globals.spool = switch_core_strdup(globals.pool, value); - } else if (!strcmp(name, "file-prefix")) { - globals.prepend_string = switch_core_strdup(globals.pool, value); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown parameter %s\n", name); - } - - } - } - - switch_xml_free(xml); - } -} - -static void event_handler(switch_event_t *event) -{ - load_configuration(1); -} - - - - - - -typedef struct { - switch_core_session_t *session; - dtmf_rx_state_t *dtmf_detect; -} switch_inband_dtmf_t; - -static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) -{ - switch_inband_dtmf_t *pvt = (switch_inband_dtmf_t *) user_data; - switch_frame_t *frame = NULL; - char digit_str[80]; - switch_channel_t *channel = switch_core_session_get_channel(pvt->session); - - switch (type) { - case SWITCH_ABC_TYPE_INIT: - pvt->dtmf_detect = dtmf_rx_init(NULL, NULL, NULL); - break; - case SWITCH_ABC_TYPE_CLOSE: - if (pvt->dtmf_detect) { - dtmf_rx_free(pvt->dtmf_detect); - } - break; - case SWITCH_ABC_TYPE_READ_REPLACE: - if ((frame = switch_core_media_bug_get_read_replace_frame(bug))) { - dtmf_rx(pvt->dtmf_detect, frame->data, frame->samples); - dtmf_rx_get(pvt->dtmf_detect, digit_str, sizeof(digit_str)); - if (digit_str[0]) { - char *p = digit_str; - while (p && *p) { - switch_dtmf_t dtmf; - dtmf.digit = *p; - dtmf.duration = switch_core_default_dtmf_duration(0); - switch_channel_queue_dtmf(channel, &dtmf); - p++; - } - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, "DTMF DETECTED: [%s]\n", - digit_str); - } - switch_core_media_bug_set_read_replace_frame(bug, frame); - } - break; - case SWITCH_ABC_TYPE_WRITE: - default: - break; - } - - return SWITCH_TRUE; -} - -switch_status_t spandsp_stop_inband_dtmf_session(switch_core_session_t *session) -{ - switch_media_bug_t *bug; - switch_channel_t *channel = switch_core_session_get_channel(session); - - if ((bug = switch_channel_get_private(channel, "dtmf"))) { - switch_channel_set_private(channel, "dtmf", NULL); - switch_core_media_bug_remove(session, &bug); - return SWITCH_STATUS_SUCCESS; - } - return SWITCH_STATUS_FALSE; -} - -switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session) -{ - switch_channel_t *channel = switch_core_session_get_channel(session); - switch_media_bug_t *bug; - switch_status_t status; - switch_inband_dtmf_t *pvt; - switch_codec_implementation_t read_impl = { 0 }; - - switch_core_session_get_read_impl(session, &read_impl); - - if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) { - return SWITCH_STATUS_MEMERR; - } - - - pvt->session = session; - - - if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { - return SWITCH_STATUS_FALSE; - } - - if ((status = switch_core_media_bug_add(session, "spandsp_dtmf_detect", NULL, - inband_dtmf_callback, pvt, 0, SMBF_READ_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) { - return status; - } - - switch_channel_set_private(channel, "dtmf", bug); - - return SWITCH_STATUS_SUCCESS; -} - -/* ************************************************************************** - CODECS SECTION - ************************************************************************* */ - -/* LPC10 - START */ - -struct lpc10_context { - lpc10_encode_state_t *encoder_object; - lpc10_decode_state_t *decoder_object; -}; - -static switch_status_t switch_lpc10_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - struct lpc10_context *context = NULL; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(struct lpc10_context))))) { - return SWITCH_STATUS_FALSE; - } else { - - if (encoding) { - context->encoder_object = lpc10_encode_init(context->encoder_object, TRUE); - } - - if (decoding) { - context->decoder_object = lpc10_decode_init(context->decoder_object, TRUE); - } - - codec->private_info = context; - - return SWITCH_STATUS_SUCCESS; - } -} - -static switch_status_t switch_lpc10_destroy(switch_codec_t *codec) -{ - struct lpc10_context *context = codec->private_info; - codec->private_info = NULL; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - if (context->encoder_object) - lpc10_encode_free(context->encoder_object); - context->encoder_object = NULL; - if (context->decoder_object) - lpc10_decode_free(context->decoder_object); - context->decoder_object = NULL; - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_lpc10_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - struct lpc10_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *encoded_data_len = lpc10_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_lpc10_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - struct lpc10_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *decoded_data_len = (2 * lpc10_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); - - return SWITCH_STATUS_SUCCESS; -} - -/* LPC10 - END */ - - -/* GSM - START */ -struct gsm_context { - gsm0610_state_t *decoder_object; - gsm0610_state_t *encoder_object; -}; - -static switch_status_t switch_gsm_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - struct gsm_context *context = NULL; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(*context))))) { - return SWITCH_STATUS_FALSE; - } else { - if (encoding) { - context->encoder_object = gsm0610_init(context->encoder_object, GSM0610_PACKING_VOIP); - } - if (decoding) { - context->decoder_object = gsm0610_init(context->decoder_object, GSM0610_PACKING_VOIP); - } - - codec->private_info = context; - return SWITCH_STATUS_SUCCESS; - } -} - -static switch_status_t switch_gsm_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - struct gsm_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *encoded_data_len = gsm0610_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_gsm_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - struct gsm_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *decoded_data_len = (2 * gsm0610_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_gsm_destroy(switch_codec_t *codec) -{ - struct gsm_context *context = codec->private_info; - - codec->private_info = NULL; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - if (context->decoder_object) - gsm0610_free(context->decoder_object); - context->decoder_object = NULL; - if (context->encoder_object) - gsm0610_free(context->encoder_object); - context->encoder_object = NULL; - - return SWITCH_STATUS_SUCCESS; -} - -/* GSM - END */ - -#ifdef ENABLE_G711 -/* G711 - START */ -static switch_status_t switch_g711u_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding)) { - return SWITCH_STATUS_FALSE; - } else { - return SWITCH_STATUS_SUCCESS; - } -} - -static switch_status_t switch_g711u_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - short *dbuf; - unsigned char *ebuf; - uint32_t i; - - dbuf = decoded_data; - ebuf = encoded_data; - - for (i = 0; i < decoded_data_len / sizeof(short); i++) { - ebuf[i] = linear_to_ulaw(dbuf[i]); - } - - *encoded_data_len = i; - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g711u_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - short *dbuf; - unsigned char *ebuf; - uint32_t i; - - dbuf = decoded_data; - ebuf = encoded_data; - - if (*flag & SWITCH_CODEC_FLAG_SILENCE) { - memset(dbuf, 0, codec->implementation->decoded_bytes_per_packet); - *decoded_data_len = codec->implementation->decoded_bytes_per_packet; - } else { - for (i = 0; i < encoded_data_len; i++) { - dbuf[i] = ulaw_to_linear(ebuf[i]); - } - - *decoded_data_len = i * 2; - } - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g711u_destroy(switch_codec_t *codec) -{ - return SWITCH_STATUS_SUCCESS; -} - - -static switch_status_t switch_g711a_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding)) { - return SWITCH_STATUS_FALSE; - } else { - return SWITCH_STATUS_SUCCESS; - } -} - -static switch_status_t switch_g711a_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - short *dbuf; - unsigned char *ebuf; - uint32_t i; - - dbuf = decoded_data; - ebuf = encoded_data; - - for (i = 0; i < decoded_data_len / sizeof(short); i++) { - ebuf[i] = linear_to_alaw(dbuf[i]); - } - - *encoded_data_len = i; - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g711a_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - short *dbuf; - unsigned char *ebuf; - uint32_t i; - - dbuf = decoded_data; - ebuf = encoded_data; - - if (*flag & SWITCH_CODEC_FLAG_SILENCE) { - memset(dbuf, 0, codec->implementation->decoded_bytes_per_packet); - *decoded_data_len = codec->implementation->decoded_bytes_per_packet; - } else { - for (i = 0; i < encoded_data_len; i++) { - dbuf[i] = alaw_to_linear(ebuf[i]); - } - - *decoded_data_len = i * 2; - } - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g711a_destroy(switch_codec_t *codec) -{ - return SWITCH_STATUS_SUCCESS; -} - -/* G711 - END */ -#endif - - -/* G722 - START */ - -struct g722_context { - g722_decode_state_t *decoder_object; - g722_encode_state_t *encoder_object; -}; - -static switch_status_t switch_g722_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - struct g722_context *context = NULL; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(struct g722_context))))) { - return SWITCH_STATUS_FALSE; - } else { - if (encoding) { - context->encoder_object = g722_encode_init(context->encoder_object, 64000, G722_PACKED); - } - if (decoding) { - context->decoder_object = g722_decode_init(context->decoder_object, 64000, G722_PACKED); - } - } - - codec->private_info = context; - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g722_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - struct g722_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *encoded_data_len = g722_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g722_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - struct g722_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *decoded_data_len = (2 * g722_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g722_destroy(switch_codec_t *codec) -{ - struct g722_context *context = codec->private_info; - - codec->private_info = NULL; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - if (context->decoder_object) - g722_decode_free(context->decoder_object); - context->decoder_object = NULL; - if (context->encoder_object) - g722_encode_free(context->encoder_object); - context->encoder_object = NULL; - - return SWITCH_STATUS_SUCCESS; -} - -/* G722 - END */ - -/* G726 - START */ - -static switch_status_t switch_g726_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - int packing = G726_PACKING_RIGHT; - g726_state_t *context = NULL; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding)) { - return SWITCH_STATUS_FALSE; - } - - if ((flags & SWITCH_CODEC_FLAG_AAL2 || strstr(codec->implementation->iananame, "AAL2"))) { - packing = G726_PACKING_LEFT; - } - - context = g726_init(context, codec->implementation->bits_per_second, G726_ENCODING_LINEAR, packing); - - codec->private_info = context; - return SWITCH_STATUS_SUCCESS; - -} - -static switch_status_t switch_g726_destroy(switch_codec_t *codec) -{ - g726_state_t *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - g726_free(context); - - codec->private_info = NULL; - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g726_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - g726_state_t *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *encoded_data_len = g726_encode(context, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_g726_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - g726_state_t *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *decoded_data_len = (2 * g726_decode(context, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); - - return SWITCH_STATUS_SUCCESS; -} - -/* G726 - START */ - -/* IMA_ADPCM - START */ - -struct ima_adpcm_context { - ima_adpcm_state_t *decoder_object; - ima_adpcm_state_t *encoder_object; -}; - -static switch_status_t switch_adpcm_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) -{ - uint32_t encoding, decoding; - struct ima_adpcm_context *context = NULL; - - encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); - decoding = (flags & SWITCH_CODEC_FLAG_DECODE); - - if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(*context))))) { - return SWITCH_STATUS_FALSE; - } else { - if (encoding) { - context->encoder_object = ima_adpcm_init(context->encoder_object, IMA_ADPCM_DVI4, 0); - } - if (decoding) { - context->decoder_object = ima_adpcm_init(context->decoder_object, IMA_ADPCM_DVI4, 0); - } - - codec->private_info = context; - return SWITCH_STATUS_SUCCESS; - } -} - -static switch_status_t switch_adpcm_encode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *decoded_data, - uint32_t decoded_data_len, - uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, - unsigned int *flag) -{ - struct ima_adpcm_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *encoded_data_len = ima_adpcm_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_adpcm_decode(switch_codec_t *codec, - switch_codec_t *other_codec, - void *encoded_data, - uint32_t encoded_data_len, - uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, - unsigned int *flag) -{ - struct ima_adpcm_context *context = codec->private_info; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - *decoded_data_len = (2 * ima_adpcm_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t switch_adpcm_destroy(switch_codec_t *codec) -{ - struct ima_adpcm_context *context = codec->private_info; - - codec->private_info = NULL; - - if (!context) { - return SWITCH_STATUS_FALSE; - } - - if (context->decoder_object) - ima_adpcm_free(context->decoder_object); - context->decoder_object = NULL; - if (context->encoder_object) - ima_adpcm_free(context->encoder_object); - context->encoder_object = NULL; - - return SWITCH_STATUS_SUCCESS; -} - -/* IMA_ADPCM - END */ - /* ************************************************************************** FREESWITCH MODULE DEFINITIONS ************************************************************************* */ @@ -1994,15 +51,14 @@ static switch_event_node_t *NODE = NULL; SWITCH_STANDARD_APP(spanfax_tx_function) { - process_fax(session, data, FUNCTION_TX); + mod_spandsp_fax_process_fax(session, data, FUNCTION_TX); } SWITCH_STANDARD_APP(spanfax_rx_function) { - process_fax(session, data, FUNCTION_RX); + mod_spandsp_fax_process_fax(session, data, FUNCTION_RX); } - SWITCH_STANDARD_APP(dtmf_session_function) { spandsp_inband_dtmf_session(session); @@ -2013,354 +69,38 @@ 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) +static void event_handler(switch_event_t *event) { - 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; + mod_spandsp_fax_event_handler(event); } -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); + time_t timeout = switch_epoch_time_now(NULL) + 20; + const char *var; 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); -} + if ((var = switch_channel_get_variable(channel, "t38_gateway_detect_timeout"))) { + long to = atol(var); + if (to > -1) { + timeout = (time_t) (switch_epoch_time_now(NULL) + to); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s invalid timeout value.\n", switch_channel_get_name(channel)); + } + } + + switch_ivr_tone_detect_session(session, "t38", "1100.0", "rw", timeout, 1, data, NULL, t38_gateway_start); +} SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) { switch_application_interface_t *app_interface; - switch_codec_interface_t *codec_interface; - int mpf, spf, bpf, ebpf, count; *module_interface = switch_loadable_module_create_module_interface(pool, modname); @@ -2375,358 +115,14 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) SWITCH_ADD_APP(app_interface, "spandsp_stop_dtmf", "stop inband dtmf", "Stop detecting inband dtmf.", stop_dtmf_session_function, "", SAF_NONE); 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; - globals.disable_v17 = 0; - globals.prepend_string = switch_core_strdup(globals.pool, "fax"); - globals.spool = switch_core_strdup(globals.pool, "/tmp"); - strncpy(globals.ident, "SpanDSP Fax Ident", sizeof(globals.ident) - 1); - strncpy(globals.header, "SpanDSP Fax Header", sizeof(globals.header) - 1); - - load_configuration(0); + mod_spandsp_fax_load(pool); + mod_spandsp_codecs_load(module_interface, pool); if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &NODE) != SWITCH_STATUS_SUCCESS)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our reloadxml handler!\n"); /* Not such severe to prevent loading */ } - - /* IMA_ADPCM */ - mpf = 10000, spf = 80, bpf = 160, ebpf = 80; - SWITCH_ADD_CODEC(codec_interface, "ADPCM (IMA)"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 5, /* the IANA code number */ - "DVI4", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 32000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - (ebpf * count) + 4, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - spf * count, /* number of frames per network packet */ - switch_adpcm_init, /* function to initialize a codec handle using this implementation */ - switch_adpcm_encode, /* function to encode raw data into encoded data */ - switch_adpcm_decode, /* function to decode encoded data into raw data */ - switch_adpcm_destroy); /* deinitalize a codec handle using this implementation */ - } - mpf = 10000, spf = 160, bpf = 320, ebpf = 160; - for (count = 6; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 6, /* the IANA code number */ - "DVI4", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 16000, /* samples transferred per second */ - 16000, /* actual samples transferred per second */ - 64000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - (ebpf * count) + 4, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - spf * count, /* number of frames per network packet */ - switch_adpcm_init, /* function to initialize a codec handle using this implementation */ - switch_adpcm_encode, /* function to encode raw data into encoded data */ - switch_adpcm_decode, /* function to decode encoded data into raw data */ - switch_adpcm_destroy); /* deinitalize a codec handle using this implementation */ - } - - /* G726 */ - mpf = 10000, spf = 80, bpf = 160, ebpf = 20; - SWITCH_ADD_CODEC(codec_interface, "G.726 16k (AAL2)"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 124, /* the IANA code number */ - "AAL2-G726-16", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 16000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - SWITCH_ADD_CODEC(codec_interface, "G.726 16k"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 124, /* the IANA code number */ - "G726-16", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 16000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - /* Increase encoded bytes per frame by 10 */ - ebpf = ebpf + 10; - - SWITCH_ADD_CODEC(codec_interface, "G.726 24k (AAL2)"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 123, /* the IANA code number */ - "AAL2-G726-24", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 24000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - - SWITCH_ADD_CODEC(codec_interface, "G.726 24k"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 123, /* the IANA code number */ - "G726-24", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 24000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - /* Increase encoded bytes per frame by 10 */ - ebpf = ebpf + 10; - - SWITCH_ADD_CODEC(codec_interface, "G.726 32k (AAL2)"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 122, /* the IANA code number */ - "AAL2-G726-32", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 32000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - SWITCH_ADD_CODEC(codec_interface, "G.726 32k"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 122, /* the IANA code number */ - "G726-32", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 32000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - /* Increase encoded bytes per frame by 10 */ - ebpf = ebpf + 10; - - SWITCH_ADD_CODEC(codec_interface, "G.726 40k (AAL2)"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 121, /* the IANA code number */ - "AAL2-G726-40", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 40000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - SWITCH_ADD_CODEC(codec_interface, "G.726 40k"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 121, /* the IANA code number */ - "G726-40", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 40000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count * 10, /* number of frames per network packet */ - switch_g726_init, /* function to initialize a codec handle using this implementation */ - switch_g726_encode, /* function to encode raw data into encoded data */ - switch_g726_decode, /* function to decode encoded data into raw data */ - switch_g726_destroy); /* deinitalize a codec handle using this implementation */ - } - /* G722 */ - mpf = 10000, spf = 80, bpf = 320, ebpf = 80; - SWITCH_ADD_CODEC(codec_interface, "G.722"); - for (count = 6; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 9, /* the IANA code number */ - "G722", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 16000, /* actual samples transferred per second */ - 64000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - spf * count, /* number of frames per network packet */ - switch_g722_init, /* function to initialize a codec handle using this implementation */ - switch_g722_encode, /* function to encode raw data into encoded data */ - switch_g722_decode, /* function to decode encoded data into raw data */ - switch_g722_destroy); /* deinitalize a codec handle using this implementation */ - } - -#ifdef ENABLE_G711 - /* G711 */ - mpf = 10000, spf = 80, bpf = 160, ebpf = 80; - SWITCH_ADD_CODEC(codec_interface, "G.711 ulaw"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 0, /* the IANA code number */ - "PCMU", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 64000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - spf * count, /* number of frames per network packet */ - switch_g711u_init, /* function to initialize a codec handle using this implementation */ - switch_g711u_encode, /* function to encode raw data into encoded data */ - switch_g711u_decode, /* function to decode encoded data into raw data */ - switch_g711u_destroy); /* deinitalize a codec handle using this implementation */ - } - - SWITCH_ADD_CODEC(codec_interface, "G.711 alaw"); - for (count = 12; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 8, /* the IANA code number */ - "PCMA", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 64000, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - spf * count, /* number of frames per network packet */ - switch_g711a_init, /* function to initialize a codec handle using this implementation */ - switch_g711a_encode, /* function to encode raw data into encoded data */ - switch_g711a_decode, /* function to decode encoded data into raw data */ - switch_g711a_destroy); /* deinitalize a codec handle using this implementation */ - } -#endif - - /* GSM */ - mpf = 20000, spf = 160, bpf = 320, ebpf = 33; - SWITCH_ADD_CODEC(codec_interface, "GSM"); - for (count = 6; count > 0; count--) { - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 3, /* the IANA code number */ - "GSM", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 13200, /* bits transferred per second */ - mpf * count, /* number of microseconds per frame */ - spf * count, /* number of samples per frame */ - bpf * count, /* number of bytes per frame decompressed */ - ebpf * count, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - count, /* number of frames per network packet */ - switch_gsm_init, /* function to initialize a codec handle using this implementation */ - switch_gsm_encode, /* function to encode raw data into encoded data */ - switch_gsm_decode, /* function to decode encoded data into raw data */ - switch_gsm_destroy); /* deinitalize a codec handle using this implementation */ - } - /* LPC10 */ -#if SWITCH_MAX_INTERVAL >= 90 - SWITCH_ADD_CODEC(codec_interface, "LPC-10"); - switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ - 7, /* the IANA code number */ - "LPC", /* the IANA code name */ - NULL, /* default fmtp to send (can be overridden by the init function) */ - 8000, /* samples transferred per second */ - 8000, /* actual samples transferred per second */ - 2400, /* bits transferred per second */ - 90000, /* number of microseconds per frame */ - 720, /* number of samples per frame */ - 1440, /* number of bytes per frame decompressed */ - 28, /* number of bytes per frame compressed */ - 1, /* number of channels represented */ - 4, /* number of frames per network packet */ - switch_lpc10_init, /* function to initialize a codec handle using this implementation */ - switch_lpc10_encode, /* function to encode raw data into encoded data */ - switch_lpc10_decode, /* function to decode encoded data into raw data */ - switch_lpc10_destroy); /* deinitalize a codec handle using this implementation */ -#endif - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "mod_spandsp loaded, using spandsp library version [%s]\n", SPANDSP_RELEASE_DATETIME_STRING); return SWITCH_STATUS_SUCCESS; @@ -2734,12 +130,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_spandsp_shutdown) { - switch_memory_pool_t *pool = globals.pool; - switch_event_unbind(&NODE); - switch_core_destroy_memory_pool(&pool); - memset(&globals, 0, sizeof(globals)); + mod_spandsp_fax_shutdown(); return SWITCH_STATUS_UNLOAD; } diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h new file mode 100644 index 0000000000..f0f18c3a93 --- /dev/null +++ b/src/mod/applications/mod_spandsp/mod_spandsp.h @@ -0,0 +1,64 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2010, Anthony Minessale II + * + * 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. + * + * The Original Code is FreeSWITCH mod_spandsp. + * + * The Initial Developer of the Original Code is + * Massimo Cetra + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Brian West + * Anthony Minessale II + * Steve Underwood + * Antonio Gallo + * mod_spandsp.h -- applications provided by SpanDSP + * + */ + +#include + +#ifdef WIN32 +#define FAX_INVALID_SOCKET INVALID_HANDLE_VALUE +typedef HANDLE zap_socket_t; +#else +#define FAX_INVALID_SOCKET -1 +typedef int zap_socket_t; +#endif + +#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES +#include + + +typedef enum { + FUNCTION_TX, + FUNCTION_RX, + FUNCTION_GW +} mod_spandsp_fax_application_mode_t; + +void mod_spandsp_fax_shutdown(void); +void mod_spandsp_fax_load(switch_memory_pool_t *pool); +void mod_spandsp_fax_event_handler(switch_event_t *event); +void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *data, mod_spandsp_fax_application_mode_t app_mode); +switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, const char *data); + +switch_status_t spandsp_stop_inband_dtmf_session(switch_core_session_t *session); +switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session); + +switch_status_t mod_spandsp_codecs_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_codecs.c b/src/mod/applications/mod_spandsp/mod_spandsp_codecs.c new file mode 100644 index 0000000000..9852ac81f2 --- /dev/null +++ b/src/mod/applications/mod_spandsp/mod_spandsp_codecs.c @@ -0,0 +1,962 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2010, Anthony Minessale II + * + * 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. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / G722 codec module + * + * The Initial Developer of the Original Code is + * Brian K. West + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian K. West + * Anthony Minessale II + * Michael Jerris + * + * mod_spandsp_codecs.c -- VoIP Codecs (G.711, G.722, G.726, GSM-FR, IMA_ADPCM, LPC10) + * + * This module wouldn't be possible without generous contributions from Steve Underwood. Thanks! + * + */ + +#include "mod_spandsp.h" + +/* LPC10 - START */ + +struct lpc10_context { + lpc10_encode_state_t *encoder_object; + lpc10_decode_state_t *decoder_object; +}; + +static switch_status_t switch_lpc10_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + struct lpc10_context *context = NULL; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(struct lpc10_context))))) { + return SWITCH_STATUS_FALSE; + } else { + + if (encoding) { + context->encoder_object = lpc10_encode_init(context->encoder_object, TRUE); + } + + if (decoding) { + context->decoder_object = lpc10_decode_init(context->decoder_object, TRUE); + } + + codec->private_info = context; + + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status_t switch_lpc10_destroy(switch_codec_t *codec) +{ + struct lpc10_context *context = codec->private_info; + codec->private_info = NULL; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + if (context->encoder_object) + lpc10_encode_free(context->encoder_object); + context->encoder_object = NULL; + if (context->decoder_object) + lpc10_decode_free(context->decoder_object); + context->decoder_object = NULL; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_lpc10_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + struct lpc10_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *encoded_data_len = lpc10_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_lpc10_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + struct lpc10_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *decoded_data_len = (2 * lpc10_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); + + return SWITCH_STATUS_SUCCESS; +} + +/* LPC10 - END */ + + +/* GSM - START */ +struct gsm_context { + gsm0610_state_t *decoder_object; + gsm0610_state_t *encoder_object; +}; + +static switch_status_t switch_gsm_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + struct gsm_context *context = NULL; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(*context))))) { + return SWITCH_STATUS_FALSE; + } else { + if (encoding) { + context->encoder_object = gsm0610_init(context->encoder_object, GSM0610_PACKING_VOIP); + } + if (decoding) { + context->decoder_object = gsm0610_init(context->decoder_object, GSM0610_PACKING_VOIP); + } + + codec->private_info = context; + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status_t switch_gsm_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + struct gsm_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *encoded_data_len = gsm0610_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_gsm_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + struct gsm_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *decoded_data_len = (2 * gsm0610_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_gsm_destroy(switch_codec_t *codec) +{ + struct gsm_context *context = codec->private_info; + + codec->private_info = NULL; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + if (context->decoder_object) + gsm0610_free(context->decoder_object); + context->decoder_object = NULL; + if (context->encoder_object) + gsm0610_free(context->encoder_object); + context->encoder_object = NULL; + + return SWITCH_STATUS_SUCCESS; +} + +/* GSM - END */ + +#ifdef ENABLE_G711 +/* G711 - START */ +static switch_status_t switch_g711u_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding)) { + return SWITCH_STATUS_FALSE; + } else { + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status_t switch_g711u_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + short *dbuf; + unsigned char *ebuf; + uint32_t i; + + dbuf = decoded_data; + ebuf = encoded_data; + + for (i = 0; i < decoded_data_len / sizeof(short); i++) { + ebuf[i] = linear_to_ulaw(dbuf[i]); + } + + *encoded_data_len = i; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g711u_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + short *dbuf; + unsigned char *ebuf; + uint32_t i; + + dbuf = decoded_data; + ebuf = encoded_data; + + if (*flag & SWITCH_CODEC_FLAG_SILENCE) { + memset(dbuf, 0, codec->implementation->decoded_bytes_per_packet); + *decoded_data_len = codec->implementation->decoded_bytes_per_packet; + } else { + for (i = 0; i < encoded_data_len; i++) { + dbuf[i] = ulaw_to_linear(ebuf[i]); + } + + *decoded_data_len = i * 2; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g711u_destroy(switch_codec_t *codec) +{ + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t switch_g711a_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding)) { + return SWITCH_STATUS_FALSE; + } else { + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status_t switch_g711a_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + short *dbuf; + unsigned char *ebuf; + uint32_t i; + + dbuf = decoded_data; + ebuf = encoded_data; + + for (i = 0; i < decoded_data_len / sizeof(short); i++) { + ebuf[i] = linear_to_alaw(dbuf[i]); + } + + *encoded_data_len = i; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g711a_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + short *dbuf; + unsigned char *ebuf; + uint32_t i; + + dbuf = decoded_data; + ebuf = encoded_data; + + if (*flag & SWITCH_CODEC_FLAG_SILENCE) { + memset(dbuf, 0, codec->implementation->decoded_bytes_per_packet); + *decoded_data_len = codec->implementation->decoded_bytes_per_packet; + } else { + for (i = 0; i < encoded_data_len; i++) { + dbuf[i] = alaw_to_linear(ebuf[i]); + } + + *decoded_data_len = i * 2; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g711a_destroy(switch_codec_t *codec) +{ + return SWITCH_STATUS_SUCCESS; +} + +/* G711 - END */ +#endif + + +/* G722 - START */ + +struct g722_context { + g722_decode_state_t *decoder_object; + g722_encode_state_t *encoder_object; +}; + +static switch_status_t switch_g722_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + struct g722_context *context = NULL; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(struct g722_context))))) { + return SWITCH_STATUS_FALSE; + } else { + if (encoding) { + context->encoder_object = g722_encode_init(context->encoder_object, 64000, G722_PACKED); + } + if (decoding) { + context->decoder_object = g722_decode_init(context->decoder_object, 64000, G722_PACKED); + } + } + + codec->private_info = context; + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g722_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + struct g722_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *encoded_data_len = g722_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g722_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + struct g722_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *decoded_data_len = (2 * g722_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g722_destroy(switch_codec_t *codec) +{ + struct g722_context *context = codec->private_info; + + codec->private_info = NULL; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + if (context->decoder_object) + g722_decode_free(context->decoder_object); + context->decoder_object = NULL; + if (context->encoder_object) + g722_encode_free(context->encoder_object); + context->encoder_object = NULL; + + return SWITCH_STATUS_SUCCESS; +} + +/* G722 - END */ + +/* G726 - START */ + +static switch_status_t switch_g726_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + int packing = G726_PACKING_RIGHT; + g726_state_t *context = NULL; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding)) { + return SWITCH_STATUS_FALSE; + } + + if ((flags & SWITCH_CODEC_FLAG_AAL2 || strstr(codec->implementation->iananame, "AAL2"))) { + packing = G726_PACKING_LEFT; + } + + context = g726_init(context, codec->implementation->bits_per_second, G726_ENCODING_LINEAR, packing); + + codec->private_info = context; + return SWITCH_STATUS_SUCCESS; + +} + +static switch_status_t switch_g726_destroy(switch_codec_t *codec) +{ + g726_state_t *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + g726_free(context); + + codec->private_info = NULL; + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g726_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + g726_state_t *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *encoded_data_len = g726_encode(context, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_g726_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + g726_state_t *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *decoded_data_len = (2 * g726_decode(context, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); + + return SWITCH_STATUS_SUCCESS; +} + +/* G726 - START */ + +/* IMA_ADPCM - START */ + +struct ima_adpcm_context { + ima_adpcm_state_t *decoder_object; + ima_adpcm_state_t *encoder_object; +}; + +static switch_status_t switch_adpcm_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + uint32_t encoding, decoding; + struct ima_adpcm_context *context = NULL; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(*context))))) { + return SWITCH_STATUS_FALSE; + } else { + if (encoding) { + context->encoder_object = ima_adpcm_init(context->encoder_object, IMA_ADPCM_DVI4, 0); + } + if (decoding) { + context->decoder_object = ima_adpcm_init(context->decoder_object, IMA_ADPCM_DVI4, 0); + } + + codec->private_info = context; + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status_t switch_adpcm_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + struct ima_adpcm_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *encoded_data_len = ima_adpcm_encode(context->encoder_object, (uint8_t *) encoded_data, (int16_t *) decoded_data, decoded_data_len / 2); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_adpcm_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + struct ima_adpcm_context *context = codec->private_info; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + *decoded_data_len = (2 * ima_adpcm_decode(context->decoder_object, (int16_t *) decoded_data, (uint8_t *) encoded_data, encoded_data_len)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_adpcm_destroy(switch_codec_t *codec) +{ + struct ima_adpcm_context *context = codec->private_info; + + codec->private_info = NULL; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + if (context->decoder_object) + ima_adpcm_free(context->decoder_object); + context->decoder_object = NULL; + if (context->encoder_object) + ima_adpcm_free(context->encoder_object); + context->encoder_object = NULL; + + return SWITCH_STATUS_SUCCESS; +} + +/* IMA_ADPCM - END */ + + +switch_status_t mod_spandsp_codecs_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) +{ + switch_codec_interface_t *codec_interface; + int mpf, spf, bpf, ebpf, count; + + /* IMA_ADPCM */ + mpf = 10000, spf = 80, bpf = 160, ebpf = 80; + SWITCH_ADD_CODEC(codec_interface, "ADPCM (IMA)"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 5, /* the IANA code number */ + "DVI4", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 32000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + (ebpf * count) + 4, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + spf * count, /* number of frames per network packet */ + switch_adpcm_init, /* function to initialize a codec handle using this implementation */ + switch_adpcm_encode, /* function to encode raw data into encoded data */ + switch_adpcm_decode, /* function to decode encoded data into raw data */ + switch_adpcm_destroy); /* deinitalize a codec handle using this implementation */ + } + mpf = 10000, spf = 160, bpf = 320, ebpf = 160; + for (count = 6; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 6, /* the IANA code number */ + "DVI4", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 16000, /* samples transferred per second */ + 16000, /* actual samples transferred per second */ + 64000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + (ebpf * count) + 4, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + spf * count, /* number of frames per network packet */ + switch_adpcm_init, /* function to initialize a codec handle using this implementation */ + switch_adpcm_encode, /* function to encode raw data into encoded data */ + switch_adpcm_decode, /* function to decode encoded data into raw data */ + switch_adpcm_destroy); /* deinitalize a codec handle using this implementation */ + } + + /* G726 */ + mpf = 10000, spf = 80, bpf = 160, ebpf = 20; + SWITCH_ADD_CODEC(codec_interface, "G.726 16k (AAL2)"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 124, /* the IANA code number */ + "AAL2-G726-16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 16000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + SWITCH_ADD_CODEC(codec_interface, "G.726 16k"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 124, /* the IANA code number */ + "G726-16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 16000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + /* Increase encoded bytes per frame by 10 */ + ebpf = ebpf + 10; + + SWITCH_ADD_CODEC(codec_interface, "G.726 24k (AAL2)"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 123, /* the IANA code number */ + "AAL2-G726-24", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 24000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + + SWITCH_ADD_CODEC(codec_interface, "G.726 24k"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 123, /* the IANA code number */ + "G726-24", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 24000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + /* Increase encoded bytes per frame by 10 */ + ebpf = ebpf + 10; + + SWITCH_ADD_CODEC(codec_interface, "G.726 32k (AAL2)"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 122, /* the IANA code number */ + "AAL2-G726-32", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 32000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + SWITCH_ADD_CODEC(codec_interface, "G.726 32k"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 122, /* the IANA code number */ + "G726-32", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 32000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + /* Increase encoded bytes per frame by 10 */ + ebpf = ebpf + 10; + + SWITCH_ADD_CODEC(codec_interface, "G.726 40k (AAL2)"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 121, /* the IANA code number */ + "AAL2-G726-40", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 40000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + SWITCH_ADD_CODEC(codec_interface, "G.726 40k"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 121, /* the IANA code number */ + "G726-40", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 40000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count * 10, /* number of frames per network packet */ + switch_g726_init, /* function to initialize a codec handle using this implementation */ + switch_g726_encode, /* function to encode raw data into encoded data */ + switch_g726_decode, /* function to decode encoded data into raw data */ + switch_g726_destroy); /* deinitalize a codec handle using this implementation */ + } + /* G722 */ + mpf = 10000, spf = 80, bpf = 320, ebpf = 80; + SWITCH_ADD_CODEC(codec_interface, "G.722"); + for (count = 6; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 9, /* the IANA code number */ + "G722", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 16000, /* actual samples transferred per second */ + 64000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + spf * count, /* number of frames per network packet */ + switch_g722_init, /* function to initialize a codec handle using this implementation */ + switch_g722_encode, /* function to encode raw data into encoded data */ + switch_g722_decode, /* function to decode encoded data into raw data */ + switch_g722_destroy); /* deinitalize a codec handle using this implementation */ + } + +#ifdef ENABLE_G711 + /* G711 */ + mpf = 10000, spf = 80, bpf = 160, ebpf = 80; + SWITCH_ADD_CODEC(codec_interface, "G.711 ulaw"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 0, /* the IANA code number */ + "PCMU", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 64000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + spf * count, /* number of frames per network packet */ + switch_g711u_init, /* function to initialize a codec handle using this implementation */ + switch_g711u_encode, /* function to encode raw data into encoded data */ + switch_g711u_decode, /* function to decode encoded data into raw data */ + switch_g711u_destroy); /* deinitalize a codec handle using this implementation */ + } + + SWITCH_ADD_CODEC(codec_interface, "G.711 alaw"); + for (count = 12; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 8, /* the IANA code number */ + "PCMA", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 64000, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + spf * count, /* number of frames per network packet */ + switch_g711a_init, /* function to initialize a codec handle using this implementation */ + switch_g711a_encode, /* function to encode raw data into encoded data */ + switch_g711a_decode, /* function to decode encoded data into raw data */ + switch_g711a_destroy); /* deinitalize a codec handle using this implementation */ + } +#endif + + /* GSM */ + mpf = 20000, spf = 160, bpf = 320, ebpf = 33; + SWITCH_ADD_CODEC(codec_interface, "GSM"); + for (count = 6; count > 0; count--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 3, /* the IANA code number */ + "GSM", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 13200, /* bits transferred per second */ + mpf * count, /* number of microseconds per frame */ + spf * count, /* number of samples per frame */ + bpf * count, /* number of bytes per frame decompressed */ + ebpf * count, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + count, /* number of frames per network packet */ + switch_gsm_init, /* function to initialize a codec handle using this implementation */ + switch_gsm_encode, /* function to encode raw data into encoded data */ + switch_gsm_decode, /* function to decode encoded data into raw data */ + switch_gsm_destroy); /* deinitalize a codec handle using this implementation */ + } + /* LPC10 */ +#if SWITCH_MAX_INTERVAL >= 90 + SWITCH_ADD_CODEC(codec_interface, "LPC-10"); + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 7, /* the IANA code number */ + "LPC", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 2400, /* bits transferred per second */ + 90000, /* number of microseconds per frame */ + 720, /* number of samples per frame */ + 1440, /* number of bytes per frame decompressed */ + 28, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + 4, /* number of frames per network packet */ + switch_lpc10_init, /* function to initialize a codec handle using this implementation */ + switch_lpc10_encode, /* function to encode raw data into encoded data */ + switch_lpc10_decode, /* function to decode encoded data into raw data */ + switch_lpc10_destroy); /* deinitalize a codec handle using this implementation */ +#endif + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c new file mode 100644 index 0000000000..bb9fd6a2c5 --- /dev/null +++ b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c @@ -0,0 +1,138 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2010, Anthony Minessale II + * + * 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. + * + * The Original Code is FreeSWITCH mod_spandsp. + * + * The Initial Developer of the Original Code is + * Massimo Cetra + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Brian West + * Anthony Minessale II + * Steve Underwood + * Antonio Gallo + * mod_spandsp_dsp.c -- dsp applications provided by SpanDSP + * + */ + +#include "mod_spandsp.h" + +typedef struct { + switch_core_session_t *session; + dtmf_rx_state_t *dtmf_detect; +} switch_inband_dtmf_t; + +static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + switch_inband_dtmf_t *pvt = (switch_inband_dtmf_t *) user_data; + switch_frame_t *frame = NULL; + char digit_str[80]; + switch_channel_t *channel = switch_core_session_get_channel(pvt->session); + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + pvt->dtmf_detect = dtmf_rx_init(NULL, NULL, NULL); + break; + case SWITCH_ABC_TYPE_CLOSE: + if (pvt->dtmf_detect) { + dtmf_rx_free(pvt->dtmf_detect); + } + break; + case SWITCH_ABC_TYPE_READ_REPLACE: + if ((frame = switch_core_media_bug_get_read_replace_frame(bug))) { + dtmf_rx(pvt->dtmf_detect, frame->data, frame->samples); + dtmf_rx_get(pvt->dtmf_detect, digit_str, sizeof(digit_str)); + if (digit_str[0]) { + char *p = digit_str; + while (p && *p) { + switch_dtmf_t dtmf; + dtmf.digit = *p; + dtmf.duration = switch_core_default_dtmf_duration(0); + switch_channel_queue_dtmf(channel, &dtmf); + p++; + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, "DTMF DETECTED: [%s]\n", + digit_str); + } + switch_core_media_bug_set_read_replace_frame(bug, frame); + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + return SWITCH_TRUE; +} + +switch_status_t spandsp_stop_inband_dtmf_session(switch_core_session_t *session) +{ + switch_media_bug_t *bug; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if ((bug = switch_channel_get_private(channel, "dtmf"))) { + switch_channel_set_private(channel, "dtmf", NULL); + switch_core_media_bug_remove(session, &bug); + return SWITCH_STATUS_SUCCESS; + } + return SWITCH_STATUS_FALSE; +} + +switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_media_bug_t *bug; + switch_status_t status; + switch_inband_dtmf_t *pvt; + switch_codec_implementation_t read_impl = { 0 }; + + switch_core_session_get_read_impl(session, &read_impl); + + if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) { + return SWITCH_STATUS_MEMERR; + } + + pvt->session = session; + + + if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + if ((status = switch_core_media_bug_add(session, "spandsp_dtmf_detect", NULL, + inband_dtmf_callback, pvt, 0, SMBF_READ_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + switch_channel_set_private(channel, "dtmf", bug); + + return SWITCH_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c new file mode 100644 index 0000000000..016d78eca3 --- /dev/null +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -0,0 +1,1743 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2010, Anthony Minessale II + * + * 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. + * + * The Original Code is FreeSWITCH mod_fax. + * + * The Initial Developer of the Original Code is + * Massimo Cetra + * + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Brian West + * Anthony Minessale II + * Steve Underwood + * Antonio Gallo + * mod_spandsp_fax.c -- Fax applications provided by SpanDSP + * + */ + +#include "mod_spandsp.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 { + T38_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 { + switch_memory_pool_t *pool; + switch_mutex_t *mutex; + + uint32_t total_sessions; + + 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; + char *spool; +} globals; + +struct pvt_s { + switch_core_session_t *session; + + mod_spandsp_fax_application_mode_t app_mode; + + 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; + char *header; + + int use_ecm; + int disable_v17; + int verbose; + int caller; + + int tx_page_start; + int tx_page_end; + + int done; + + t38_mode_t t38_mode; + + 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 +*****************************************************************************/ + +static void counter_increment(void) +{ + switch_mutex_lock(globals.mutex); + globals.total_sessions++; + switch_mutex_unlock(globals.mutex); +} + +static void spanfax_log_message(int level, const char *msg) +{ + int fs_log_level; + + switch (level) { + case SPAN_LOG_NONE: + return; + case SPAN_LOG_ERROR: + case SPAN_LOG_PROTOCOL_ERROR: + fs_log_level = SWITCH_LOG_ERROR; + break; + case SPAN_LOG_WARNING: + case SPAN_LOG_PROTOCOL_WARNING: + fs_log_level = SWITCH_LOG_WARNING; + break; + case SPAN_LOG_FLOW: + case SPAN_LOG_FLOW_2: + case SPAN_LOG_FLOW_3: + default: /* SPAN_LOG_DEBUG, SPAN_LOG_DEBUG_2, SPAN_LOG_DEBUG_3 */ + fs_log_level = SWITCH_LOG_DEBUG; + break; + } + + if (!zstr(msg)) { + switch_log_printf(SWITCH_CHANNEL_LOG, fs_log_level, "%s", msg); + } +} + +/* + * Called at the end of the document + */ +static void phase_e_handler(t30_state_t *s, void *user_data, int result) +{ + t30_stats_t t; + const char *local_ident; + const char *far_ident; + switch_core_session_t *session; + switch_channel_t *channel; + pvt_t *pvt; + char *tmp; + + pvt = (pvt_t *) user_data; + switch_assert(pvt); + + session = pvt->session; + switch_assert(session); + + channel = switch_core_session_get_channel(session); + switch_assert(channel); + + t30_get_transfer_statistics(s, &t); + local_ident = switch_str_nil(t30_get_tx_ident(s)); + far_ident = switch_str_nil(t30_get_rx_ident(s)); + + 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) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax successfully received.\n"); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax successfully managed. How ?\n"); + } + switch_channel_set_variable(channel, "fax_success", "1"); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Fax processing not successful - result (%d) %s.\n", result, + t30_completion_code_to_str(result)); + switch_channel_set_variable(channel, "fax_success", "0"); + } + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote station id: %s\n", far_ident); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local station id: %s\n", local_ident); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Pages transferred: %i\n", + pvt->app_mode == FUNCTION_TX ? t.pages_tx : t.pages_rx); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Total fax pages: %i\n", t.pages_in_file); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Image resolution: %ix%i\n", t.x_resolution, t.y_resolution); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Transfer Rate: %i\n", t.bit_rate); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "ECM status %s\n", (t.error_correcting_mode) ? "on" : "off"); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "remote country: %s\n", switch_str_nil(t30_get_rx_country(s))); + 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"); + + /* + Set our channel variables + */ + + tmp = switch_mprintf("%i", result); + if (tmp) { + switch_channel_set_variable(channel, "fax_result_code", tmp); + switch_safe_free(tmp); + } + + switch_channel_set_variable(channel, "fax_result_text", t30_completion_code_to_str(result)); + + switch_channel_set_variable(channel, "fax_ecm_used", (t.error_correcting_mode) ? "on" : "off"); + switch_channel_set_variable(channel, "fax_local_station_id", local_ident); + switch_channel_set_variable(channel, "fax_remote_station_id", far_ident); + + tmp = switch_mprintf("%i", pvt->app_mode == FUNCTION_TX ? t.pages_tx : t.pages_rx); + if (tmp) { + switch_channel_set_variable(channel, "fax_document_transferred_pages", tmp); + switch_safe_free(tmp); + } + + tmp = switch_mprintf("%i", t.pages_in_file); + if (tmp) { + switch_channel_set_variable(channel, "fax_document_total_pages", tmp); + switch_safe_free(tmp); + } + + tmp = switch_mprintf("%ix%i", t.x_resolution, t.y_resolution); + if (tmp) { + switch_channel_set_variable(channel, "fax_image_resolution", tmp); + switch_safe_free(tmp); + } + + tmp = switch_mprintf("%d", t.image_size); + if (tmp) { + switch_channel_set_variable(channel, "fax_image_size", tmp); + switch_safe_free(tmp); + } + + tmp = switch_mprintf("%d", t.bad_rows); + if (tmp) { + switch_channel_set_variable(channel, "fax_bad_rows", tmp); + switch_safe_free(tmp); + } + + tmp = switch_mprintf("%i", t.bit_rate); + if (tmp) { + switch_channel_set_variable(channel, "fax_transfer_rate", tmp); + switch_safe_free(tmp); + } + + /* switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); */ + + pvt->done = 1; + + /* + TODO Fire events + */ +} + +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); + + channel = switch_core_session_get_channel(session); + switch_assert(channel); + + + switch (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)); + } + if (pvt->fax_state == NULL) { + return SWITCH_STATUS_FALSE; + } + + fax = pvt->fax_state; + t30 = fax_get_t30_state(fax); + + memset(fax, 0, sizeof(fax_state_t)); + if (fax_init(fax, pvt->caller) == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my fax structs\n"); + return SWITCH_STATUS_FALSE; + } + + fax_set_transmit_on_idle(fax, TRUE); + + span_log_set_message_handler(&fax->logging, spanfax_log_message); + span_log_set_message_handler(&t30->logging, spanfax_log_message); + + if (pvt->verbose) { + 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; + } + + /* add to timer thread processing */ + add_pvt(pvt); + + t38 = pvt->t38_state; + t30 = t38_terminal_get_t30_state(t38); + + memset(t38, 0, sizeof(t38_terminal_state_t)); + + 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 (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; + } + + span_log_set_message_handler(&t38->logging, spanfax_log_message); + span_log_set_message_handler(&t30->logging, spanfax_log_message); + + 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); + } + break; + 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); /* 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; +} + +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; + } else { + terminate = 1; + } + + t30 = fax_get_t30_state(pvt->fax_state); + if (terminate && t30) { + t30_terminate(t30); + } + + fax_release(pvt->fax_state); + } + + if (pvt->t38_state) { + + /* remove from timer thread processing */ + del_pvt(pvt); + + if (pvt->t38_state) { + terminate = 1; + } else { + terminate = 0; + } + + t30 = t38_terminal_get_t30_state(pvt->t38_state); + + if (terminate && t30) { + t30_terminate(t30); + } + + 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 || !pvt->t38_core) { + 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 +*****************************************************************************/ + +static pvt_t *pvt_init(switch_core_session_t *session, mod_spandsp_fax_application_mode_t app_mode) +{ + switch_channel_t *channel; + pvt_t *pvt = NULL; + const char *tmp; + + /* Make sure we have a valid channel when starting the FAX application */ + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + if (!switch_channel_media_ready(channel)) { + switch_channel_answer(channel); + } + + /* Allocate our structs */ + pvt = switch_core_session_alloc(session, sizeof(pvt_t)); + pvt->session = session; + + pvt->app_mode = app_mode; + + pvt->tx_page_start = -1; + pvt->tx_page_end = -1; + + + switch(pvt->app_mode) { + + 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 */ + + if ((tmp = switch_channel_get_variable(channel, "fax_use_ecm"))) { + pvt->use_ecm = switch_true(tmp); + } else { + pvt->use_ecm = globals.use_ecm; + } + + if ((tmp = switch_channel_get_variable(channel, "fax_disable_v17"))) { + pvt->disable_v17 = switch_true(tmp); + } else { + pvt->disable_v17 = globals.disable_v17; + } + + if ((tmp = switch_channel_get_variable(channel, "fax_verbose"))) { + pvt->verbose = switch_true(tmp); + } else { + pvt->verbose = globals.verbose; + } + + if ((tmp = switch_channel_get_variable(channel, "fax_force_caller"))) { + if (switch_true(tmp)) { + pvt->caller = 1; + } else { + pvt->caller = 0; + } + } + + if ((tmp = switch_channel_get_variable(channel, "fax_ident"))) { + pvt->ident = switch_core_session_strdup(session, tmp); + } else { + pvt->ident = switch_core_session_strdup(session, globals.ident); + } + + if ((tmp = switch_channel_get_variable(channel, "fax_header"))) { + pvt->header = switch_core_session_strdup(session, tmp); + } else { + pvt->header = switch_core_session_strdup(session, globals.header); + } + + if (pvt->app_mode == FUNCTION_TX) { + if ((tmp = switch_channel_get_variable(channel, "fax_start_page"))) { + pvt->tx_page_start = atoi(tmp); + } + + if ((tmp = switch_channel_get_variable(channel, "fax_end_page"))) { + pvt->tx_page_end = atoi(tmp); + } + + if (pvt->tx_page_end < -1) { + pvt->tx_page_end = -1; + } + + if (pvt->tx_page_start < -1) { + pvt->tx_page_start = -1; + } + + 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 mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *data, mod_spandsp_fax_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); + if (pvt->app_mode == FUNCTION_TX) { + if ((switch_file_exists(pvt->filename, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot send inexistant fax file [%s]\n", + switch_str_nil(pvt->filename)); + goto done; + } + } + } else { + if (pvt->app_mode == FUNCTION_TX) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Fax TX filename not set.\n"); + goto done; + } else if (pvt->app_mode == FUNCTION_RX) { + char *fname; + const char *prefix; + switch_time_t time; + + time = switch_time_now(); + + if (!(prefix = switch_channel_get_variable(channel, "fax_prefix"))) { + prefix = globals.prepend_string; + } + + fname = switch_mprintf("%s/%s-%ld-%ld.tif", globals.spool, prefix, globals.total_sessions, time); + if (fname) { + pvt->filename = switch_core_session_strdup(session, fname); + switch_safe_free(fname); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot automatically set fax RX destination file\n"); + goto done; + } + } else { + assert(0); /* UH ?? */ + } + } + + /* + *** Initialize the SpanDSP elements *** + + Note: we could analyze if a fax was already detected in previous stages + and if so, when T.38 will be supported, send a reinvite in T38_MODE, + bypassing AUDIO_MODE. + */ + + if ((spanfax_init(pvt, AUDIO_MODE) != SWITCH_STATUS_SUCCESS)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize Fax engine\n"); + return; + } + + /* + Note: Disable echocan on the channel, remember to call app "disable_ec" in the dialplan + before invoking fax applications + */ + + /* + Note: we are disabling the Jitterbuffer, here, before we answer. + If you have set it to something else and the channel is pre-answered, + it will have no effect. Make sure that if you want more reliable + faxes, it is disabled. + */ + switch_channel_set_variable(channel, "jitterbuffer_msec", "0"); + + + /* We store the original channel codec before switching both + * legs of the calls to a linear 16 bit codec that is the one + * 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, + 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 done; + } + + 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 done; + } + + 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; + + /* + if we are in T.38 mode, we should: 1- initialize the ptv->t38_state stuff, if not done + and then set some callbacks when reading frames. + The only thing we need, then, in this loop, is: + - read a frame without blocking + - eventually feed that frame in spandsp, + - 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); + + if (!SWITCH_READ_ACCEPTABLE(status) || pvt->done) { + /* Our duty is over */ + 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)) { + /* 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; + } + } + + if ((tx = fax_tx(pvt->fax_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 done; + } + + 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 done; + } + + } + + done: + /* Destroy the SpanDSP structures */ + spanfax_destroy(pvt); + + /* restore the original codecs over the channel */ + + 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); + } +} + +/* ************************************************************************** + CONFIGURATION + ************************************************************************* */ + +void load_configuration(switch_bool_t reload) +{ + switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, cfg = NULL; + + if ((xml = switch_xml_open_cfg("fax.conf", &cfg, NULL))) { + if ((x_lists = switch_xml_child(cfg, "settings"))) { + for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) { + const char *name = switch_xml_attr(x_list, "name"); + const char *value = switch_xml_attr(x_list, "value"); + + if (zstr(name)) { + continue; + } + + if (zstr(value)) { + continue; + } + + if (!strcmp(name, "use-ecm")) { + if (switch_true(value)) + globals.use_ecm = 1; + else + globals.use_ecm = 0; + } else if (!strcmp(name, "verbose")) { + if (switch_true(value)) + globals.verbose = 1; + else + globals.verbose = 0; + } else if (!strcmp(name, "disable-v17")) { + if (switch_true(value)) + 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")) { + strncpy(globals.header, value, sizeof(globals.header) - 1); + } else if (!strcmp(name, "spool-dir")) { + globals.spool = switch_core_strdup(globals.pool, value); + } else if (!strcmp(name, "file-prefix")) { + globals.prepend_string = switch_core_strdup(globals.pool, value); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown parameter %s\n", name); + } + + } + } + + switch_xml_free(xml); + } +} + +void mod_spandsp_fax_event_handler(switch_event_t *event) +{ + load_configuration(1); +} + + +void mod_spandsp_fax_load(switch_memory_pool_t *pool) +{ + memset(&globals, 0, sizeof(globals)); + memset(&t38_state_list, 0, sizeof(t38_state_list)); + + globals.pool = 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; + globals.disable_v17 = 0; + globals.prepend_string = switch_core_strdup(globals.pool, "fax"); + globals.spool = switch_core_strdup(globals.pool, "/tmp"); + strncpy(globals.ident, "SpanDSP Fax Ident", sizeof(globals.ident) - 1); + strncpy(globals.header, "SpanDSP Fax Header", sizeof(globals.header) - 1); + + load_configuration(0); +} + +void mod_spandsp_fax_shutdown(void) +{ + memset(&globals, 0, sizeof(globals)); +} + +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); + + msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE; + msg.from = __FILE__; + msg.string_arg = peer_uuid; + switch_core_session_receive_message(session, &msg); + + 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) { + spanfax_init(pvt, T38_GATEWAY_MODE); + 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: + + + msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE; + msg.from = __FILE__; + msg.string_arg = peer_uuid; + switch_core_session_receive_message(session, &msg); + + 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; + const char *t38_trace = switch_channel_get_variable(channel, "t38_trace"); + char *trace_read, *trace_write; + zap_socket_t read_fd = FAX_INVALID_SOCKET, write_fd = FAX_INVALID_SOCKET; + switch_core_session_message_t msg = { 0 }; + switch_event_t *event; + + 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); + + msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE; + msg.from = __FILE__; + msg.string_arg = peer_uuid; + switch_core_session_receive_message(session, &msg); + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + + 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, 0, SWITCH_TRUE, NULL); + + if (switch_true(t38_trace)) { + trace_read = switch_core_session_sprintf(session, "%s%s%s_read.raw", SWITCH_GLOBAL_dirs.temp_dir, + SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(session)); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Tracing inbound audio to %s\n", trace_read); + switch_channel_set_variable(channel, "t38_trace_read", trace_read); + + trace_write = switch_core_session_sprintf(session, "%s%s%s_write.raw", SWITCH_GLOBAL_dirs.temp_dir, + SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(session)); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Tracing outbound audio to %s\n", trace_write); + switch_channel_set_variable(channel, "t38_trace_read", trace_write); + + + if ((write_fd = open(trace_read, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) != FAX_INVALID_SOCKET) { + if ((read_fd = open(trace_write, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == FAX_INVALID_SOCKET) { + close(write_fd); + write_fd = FAX_INVALID_SOCKET; + } + } + } + + 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 (read_fd != FAX_INVALID_SOCKET) { + write(read_fd, read_frame->data, read_frame->datalen); + } + 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 (write_fd != FAX_INVALID_SOCKET) { + write(write_fd, write_frame.data, write_frame.datalen); + } + + if (switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { + goto end_unlock; + } + } + + end_unlock: + + msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE; + msg.from = __FILE__; + msg.string_arg = peer_uuid; + switch_core_session_receive_message(session, &msg); + + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + + if (read_fd != FAX_INVALID_SOCKET) { + close(read_fd); + read_fd = FAX_INVALID_SOCKET; + } + + if (write_fd != FAX_INVALID_SOCKET) { + close(write_fd); + write_fd = FAX_INVALID_SOCKET; + } + + + 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); + + switch_channel_set_variable(channel, "rtp_autoflush_during_bridge", "false"); + + switch_channel_clear_flag(channel, CF_REDIRECT); + + 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 +}; + +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_flag(channel, CF_REDIRECT); + switch_channel_set_state(channel, CS_RESET); + + switch_channel_set_flag(other_channel, CF_REDIRECT); + switch_channel_set_state(other_channel, CS_RESET); + + switch_core_session_rwunlock(other_session); + + } + + return SWITCH_FALSE; +} + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */