2008-09-03 17:08:30 +00:00
/*
2008-09-03 17:35:09 +00:00
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
* Copyright ( C ) 2005 / 2006 , Anthony Minessale II < anthmct @ yahoo . com >
2008-09-03 17:08:30 +00:00
*
2008-09-03 17:35:09 +00:00
* 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 .
2008-09-03 17:08:30 +00:00
*
2008-09-05 11:34:48 +00:00
* The Original Code is FreeSWITCH mod_timezone .
2008-09-03 17:35:09 +00:00
*
* The Initial Developer of the Original Code is
2008-09-05 11:34:48 +00:00
* Massimo Cetra < devel @ navynet . it >
*
2008-09-03 17:35:09 +00:00
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
*
2008-09-03 18:55:30 +00:00
* Brian West < brian @ freeswitch . org >
2008-09-05 11:34:48 +00:00
* Anthony Minessale II < anthmct @ yahoo . com >
* Steve Underwood < steveu @ coppice . org >
2008-09-03 17:08:30 +00:00
*
2008-09-05 11:34:48 +00:00
* mod_fax . c - - Fax applications provided by SpanDSP
2008-09-03 17:08:30 +00:00
*
*/
# include <switch.h>
# include <spandsp.h>
2008-09-04 11:12:14 +00:00
# include <spandsp/version.h>
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
/*****************************************************************************
OUR DEFINES AND STRUCTS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
typedef enum {
FUNCTION_TX ,
FUNCTION_RX
} application_mode_t ;
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
typedef enum {
T38_MODE ,
AUDIO_MODE
} transport_mode_t ;
2008-09-04 09:33:04 +00:00
2008-09-04 07:08:34 +00:00
2008-09-05 11:34:48 +00:00
/* The global stuff */
static struct {
switch_memory_pool_t * pool ;
switch_mutex_t * mutex ;
2008-09-04 17:18:22 +00:00
2008-09-05 11:34:48 +00:00
uint32_t total_sessions ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
short int use_ecm ;
short int verbose ;
short int disable_v17 ;
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 ;
char * filename ;
char * ident ;
char * header ;
int use_ecm ;
int disable_v17 ;
int verbose ;
int caller ;
int tx_page_start ;
int tx_page_end ;
/* UNUSED AT THE MOMENT
int enable_t38_reinvite ;
*/
2008-09-03 23:16:23 +00:00
2008-09-04 09:33:04 +00:00
} ;
2008-09-05 11:34:48 +00:00
typedef struct pvt_s pvt_t ;
2008-09-04 09:33:04 +00:00
2008-09-05 11:34:48 +00:00
/*****************************************************************************
LOGGING AND HELPER FUNCTIONS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-09-04 09:33:04 +00:00
2008-09-05 11:34:48 +00:00
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 )
2008-09-04 09:33:04 +00:00
{
2008-09-05 11:34:48 +00:00
int fs_log_level ;
2008-09-04 17:18:22 +00:00
2008-09-05 11:34:48 +00:00
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 ;
}
2008-09-04 17:18:22 +00:00
2008-09-05 11:34:48 +00:00
if ( ! switch_strlen_zero ( msg ) )
switch_log_printf ( SWITCH_CHANNEL_LOG , fs_log_level , " %s " , msg ) ;
2008-09-04 09:33:04 +00:00
2008-09-03 23:16:23 +00:00
}
/*
2008-09-05 11:34:48 +00:00
* Called at the end of the document
2008-09-03 23:16:23 +00:00
*/
2008-09-03 17:08:30 +00:00
static void phase_e_handler ( t30_state_t * s , void * user_data , int result )
{
2008-09-05 11:34:48 +00:00
t30_stats_t t ;
const char * local_ident ;
const char * far_ident ;
switch_core_session_t * session ;
switch_channel_t * chan ;
char * tmp ;
session = ( switch_core_session_t * ) user_data ;
switch_assert ( session ) ;
2008-09-04 17:18:22 +00:00
2008-09-05 11:34:48 +00:00
chan = switch_core_session_get_channel ( session ) ;
2008-09-04 17:18:22 +00:00
switch_assert ( chan ) ;
2008-09-04 16:54:10 +00:00
2008-09-05 11:34:48 +00:00
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 )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Fax successfully sent. \n " ) ;
switch_channel_set_variable ( chan , " fax_success " , " 1 " ) ;
}
else
{
2008-09-04 17:18:22 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Fax processing not successful - result (%d) %s. \n " , result , t30_completion_code_to_str ( result ) ) ;
2008-09-05 11:34:48 +00:00
switch_channel_set_variable ( chan , " fax_success " , " 0 " ) ;
2008-09-04 16:54:10 +00:00
}
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Remote station id: %s \n " , far_ident ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Local station id: %s \n " , local_ident ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Pages transferred: %i \n " , t . pages_transferred ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Total fax pages: %i \n " , t . pages_in_file ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Image resolution: %ix%i \n " , t . x_resolution , t . y_resolution ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Transfer Rate: %i \n " , t . bit_rate ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " ECM status %s \n " , ( t . error_correcting_mode ) ? " on " : " off " ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " remote country: %s \n " , switch_str_nil ( t30_get_rx_country ( s ) ) ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " remote vendor: %s \n " , switch_str_nil ( t30_get_rx_vendor ( s ) ) ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " remote model: %s \n " , switch_str_nil ( t30_get_rx_model ( s ) ) ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " ============================================================================== \n " ) ;
2008-09-03 17:08:30 +00:00
2008-09-04 14:01:27 +00:00
/*
2008-09-05 11:34:48 +00:00
Set our channel variables
*/
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
tmp = switch_mprintf ( " %i " , result ) ;
if ( tmp ) {
switch_channel_set_variable ( chan , " fax_result_code " , tmp ) ;
switch_safe_free ( tmp ) ;
}
switch_channel_set_variable ( chan , " fax_result_text " , t30_completion_code_to_str ( result ) ) ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
switch_channel_set_variable ( chan , " fax_ecm_used " , ( t . error_correcting_mode ) ? " on " : " off " ) ;
switch_channel_set_variable ( chan , " fax_local_station_id " , local_ident ) ;
switch_channel_set_variable ( chan , " fax_remote_station_id " , far_ident ) ;
tmp = switch_mprintf ( " %i " , t . pages_transferred ) ;
if ( tmp ) {
switch_channel_set_variable ( chan , " fax_document_transferred_pages " , tmp ) ;
switch_safe_free ( tmp ) ;
}
tmp = switch_mprintf ( " %i " , t . pages_in_file ) ;
if ( tmp ) {
switch_channel_set_variable ( chan , " 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 ( chan , " fax_image_resolution " , tmp ) ;
switch_safe_free ( tmp ) ;
}
tmp = switch_mprintf ( " %d " , t . image_size ) ;
if ( tmp ) {
switch_channel_set_variable ( chan , " fax_image_size " , tmp ) ;
switch_safe_free ( tmp ) ;
}
tmp = switch_mprintf ( " %d " , t . bad_rows ) ;
if ( tmp ) {
switch_channel_set_variable ( chan , " fax_bad_rows " , tmp ) ;
switch_safe_free ( tmp ) ;
}
tmp = switch_mprintf ( " %i " , t . bit_rate ) ;
if ( tmp ) {
switch_channel_set_variable ( chan , " fax_transfer_rate " , tmp ) ;
switch_safe_free ( tmp ) ;
}
/*
TODO Fire events
*/
}
static switch_status_t spanfax_init ( pvt_t * pvt , transport_mode_t trans_mode )
2008-09-03 17:08:30 +00:00
{
2008-09-05 11:34:48 +00:00
switch_core_session_t * session ;
switch_channel_t * chan ;
fax_state_t * fax ;
session = ( switch_core_session_t * ) pvt - > session ;
switch_assert ( session ) ;
chan = switch_core_session_get_channel ( session ) ;
switch_assert ( chan ) ;
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 ) ) ;
}
else
return SWITCH_STATUS_FALSE ;
fax = pvt - > fax_state ;
memset ( fax , 0 , sizeof ( fax_state_t ) ) ;
if ( fax_init ( fax , pvt - > caller ) = = NULL )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , 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 ( & fax - > 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 ( & fax - > t30 . logging , SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW ) ;
}
t30_set_tx_ident ( & fax - > t30 , pvt - > ident ) ;
t30_set_tx_page_header_info ( & fax - > t30 , pvt - > header ) ;
t30_set_phase_e_handler ( & fax - > t30 , phase_e_handler , pvt - > session ) ;
t30_set_supported_image_sizes ( & fax - > 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 ( & fax - > t30 ,
T30_SUPPORT_STANDARD_RESOLUTION | T30_SUPPORT_FINE_RESOLUTION | T30_SUPPORT_SUPERFINE_RESOLUTION
| T30_SUPPORT_R8_RESOLUTION | T30_SUPPORT_R16_RESOLUTION ) ;
//TODO Disable V17
if ( pvt - > disable_v17 )
{
t30_set_supported_modems ( & fax - > t30 , T30_SUPPORT_V29 | T30_SUPPORT_V27TER ) ;
switch_channel_set_variable ( chan , " fax_v17_disabled " , " 1 " ) ;
} else {
t30_set_supported_modems ( & fax - > t30 , T30_SUPPORT_V29 | T30_SUPPORT_V27TER | T30_SUPPORT_V17 ) ;
switch_channel_set_variable ( chan , " fax_v17_disabled " , " 0 " ) ;
}
if ( pvt - > use_ecm )
{
t30_set_supported_compressions ( & fax - > t30 , T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION ) ;
t30_set_ecm_capability ( & fax - > t30 , TRUE ) ;
switch_channel_set_variable ( chan , " fax_ecm_requested " , " 1 " ) ;
}
else
{
t30_set_supported_compressions ( & fax - > t30 , T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION ) ;
switch_channel_set_variable ( chan , " fax_ecm_requested " , " 0 " ) ;
}
if ( pvt - > app_mode = = FUNCTION_TX )
{
t30_set_tx_file ( & fax - > t30 , pvt - > filename , pvt - > tx_page_start , pvt - > tx_page_end ) ;
}
else
{
t30_set_rx_file ( & fax - > t30 , pvt - > filename , - 1 ) ;
}
switch_channel_set_variable ( chan , " fax_filename " , pvt - > filename ) ;
break ;
case T38_MODE :
/*
Here goes the T .38 SpanDSP initializing functions
T .38 will require a big effort as it needs a different approac
but the pieces are already in place
*/
default :
assert ( 0 ) ; /* Whaaat ? */
break ;
} /* Switch trans mode */
return SWITCH_STATUS_SUCCESS ;
2008-09-03 17:08:30 +00:00
}
2008-09-05 11:34:48 +00:00
static switch_status_t spanfax_destroy ( pvt_t * pvt ) {
int terminate ;
if ( pvt - > fax_state )
{
if ( pvt - > t38_state )
terminate = 0 ;
else
terminate = 1 ;
if ( terminate & & & pvt - > fax_state - > t30 )
t30_terminate ( & pvt - > fax_state - > t30 ) ;
fax_release ( pvt - > fax_state ) ;
}
if ( pvt - > t38_state )
{
if ( pvt - > t38_state )
terminate = 1 ;
else
terminate = 0 ;
2008-09-04 11:17:50 +00:00
2008-09-05 11:34:48 +00:00
if ( terminate & & & pvt - > t38_state - > t30 )
t30_terminate ( & pvt - > t38_state - > t30 ) ;
t38_terminal_release ( pvt - > t38_state ) ;
}
return SWITCH_STATUS_SUCCESS ;
}
/*****************************************************************************
MAIN FAX PROCESSING
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_fax ( switch_core_session_t * session , const char * data , application_mode_t app_mode )
2008-09-03 17:08:30 +00:00
{
2008-09-05 11:34:48 +00:00
pvt_t * pvt ;
const char * tmp ;
switch_channel_t * channel ;
switch_codec_t * orig_read_codec = 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 } ;
int16_t * buf = NULL ;
2008-09-04 16:54:10 +00:00
/* make sure we have a valid channel when starting the FAX application */
channel = switch_core_session_get_channel ( session ) ;
2008-09-05 11:34:48 +00:00
switch_assert ( channel ! = NULL ) ;
2008-09-04 12:07:55 +00:00
2008-09-05 11:34:48 +00:00
/* Allocate our structs */
pvt = switch_core_session_alloc ( session , sizeof ( pvt_t ) ) ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
counter_increment ( ) ;
if ( ! pvt )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Cannot allocate application private data \n " ) ;
2008-09-04 10:37:13 +00:00
return ;
}
2008-09-05 11:34:48 +00:00
else
{
memset ( pvt , 0 , sizeof ( pvt_t ) ) ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
pvt - > session = session ;
pvt - > app_mode = app_mode ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
pvt - > tx_page_start = - 1 ;
pvt - > tx_page_end = - 1 ;
if ( pvt - > app_mode = = FUNCTION_TX )
pvt - > caller = 1 ;
else if ( pvt - > app_mode = = FUNCTION_RX )
pvt - > caller = 0 ;
else
assert ( 0 ) ; /* UH ? */
}
buf = switch_core_session_alloc ( session , SWITCH_RECOMMENDED_BUFFER_SIZE ) ;
if ( ! buf ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Cannot allocate application buffer data \n " ) ;
2008-09-04 10:37:13 +00:00
return ;
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
/* Retrieving our settings from the channel variables */
if ( ( tmp = switch_channel_get_variable ( channel , " fax_use_ecm " ) ) ) {
if ( switch_true ( tmp ) )
pvt - > use_ecm = 1 ;
else
pvt - > use_ecm = 0 ;
}
else
pvt - > use_ecm = globals . use_ecm ;
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_disable_v17 " ) ) ) {
if ( switch_true ( tmp ) )
pvt - > disable_v17 = 1 ;
else
pvt - > disable_v17 = 0 ;
}
else
pvt - > disable_v17 = globals . disable_v17 ;
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_verbose " ) ) ) {
if ( switch_true ( tmp ) )
pvt - > verbose = 1 ;
else
pvt - > verbose = 0 ;
}
else
pvt - > verbose = globals . verbose ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_force_caller " ) ) ) {
if ( switch_true ( tmp ) )
pvt - > caller = 1 ;
else
pvt - > caller = 0 ;
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_ident " ) ) ) {
pvt - > ident = switch_core_session_strdup ( session , tmp ) ;
2008-09-03 23:16:23 +00:00
}
2008-09-05 11:34:48 +00:00
else
pvt - > ident = switch_core_session_strdup ( session , globals . ident ) ;
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_header " ) ) ) {
pvt - > header = switch_core_session_strdup ( session , tmp ) ;
2008-09-04 16:54:10 +00:00
}
2008-09-05 11:34:48 +00:00
else
pvt - > header = switch_core_session_strdup ( session , globals . header ) ;
2008-09-03 23:16:23 +00:00
2008-09-04 09:33:04 +00:00
2008-09-05 11:34:48 +00:00
if ( pvt - > app_mode = = FUNCTION_TX )
{
2008-09-04 10:37:13 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_start_page " ) ) ) {
pvt - > tx_page_start = atoi ( tmp ) ;
}
2008-09-04 10:37:13 +00:00
2008-09-05 11:34:48 +00:00
if ( ( tmp = switch_channel_get_variable ( channel , " fax_end_page " ) ) ) {
pvt - > tx_page_end = atoi ( tmp ) ;
}
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
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 ;
2008-09-04 09:33:04 +00:00
}
2008-09-03 23:16:23 +00:00
2008-09-05 11:34:48 +00:00
if ( ! switch_strlen_zero ( 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_LOG , SWITCH_LOG_ERROR , " Cannot send inexistant fax file [%s] \n " , switch_str_nil ( pvt - > filename ) ) ;
goto done ;
}
}
2008-09-04 13:45:22 +00:00
}
2008-09-05 11:34:48 +00:00
else {
if ( pvt - > app_mode = = FUNCTION_TX ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Fax TX filename not set. \n " ) ;
goto done ;
}
else if ( pvt - > app_mode = = FUNCTION_RX )
{
char * fname ;
char * prefix ;
switch_time_t time ;
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
time = switch_time_now ( ) ;
2008-09-04 10:37:13 +00:00
2008-09-05 11:34:48 +00:00
if ( ! ( prefix = switch_channel_get_variable ( channel , " fax_prefix " ) ) ) {
prefix = globals . prepend_string ;
}
2008-09-04 10:37:13 +00:00
2008-09-05 11:34:48 +00:00
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_LOG , SWITCH_LOG_ERROR , " Cannot automagically 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 .
2008-09-04 10:37:13 +00:00
*/
2008-09-05 11:34:48 +00:00
if ( ( spanfax_init ( pvt , AUDIO_MODE ) ! = SWITCH_STATUS_SUCCESS ) )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Cannot initialize Fax engine \n " ) ;
return ;
}
/* Answer the call */
switch_channel_answer ( channel ) ;
/* Note: Disable echocan on the channel, it there is an API call to do that */
2008-09-04 16:54:10 +00:00
/* 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
*/
orig_read_codec = switch_core_session_get_read_codec ( session ) ;
2008-09-05 11:34:48 +00:00
if ( switch_core_codec_init (
& read_codec ,
" L16 " ,
NULL ,
orig_read_codec - > implementation - > samples_per_second ,
orig_read_codec - > implementation - > microseconds_per_frame / 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_LOG , SWITCH_LOG_DEBUG , " Raw read codec activation Success L16 \n " ) ;
switch_core_session_set_read_codec ( session , & read_codec ) ;
}
else
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Raw read codec activation Failed L16 \n " ) ;
goto done ;
}
if ( switch_core_codec_init (
& write_codec ,
" L16 " ,
NULL ,
orig_read_codec - > implementation - > samples_per_second ,
orig_read_codec - > implementation - > microseconds_per_frame / 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_LOG , 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_LOG , SWITCH_LOG_ERROR , " Raw write codec activation Failed L16 \n " ) ;
goto done ;
}
2008-09-04 16:54:10 +00:00
/*
* now we enter a loop where we read audio frames to the channels and will pass it to spandsp
*/
2008-09-05 11:34:48 +00:00
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 )
*/
2008-09-04 16:54:10 +00:00
2008-09-04 17:18:22 +00:00
/* read new audio frame from the channel */
2008-09-05 11:34:48 +00:00
status = switch_core_session_read_frame ( session , & read_frame , SWITCH_IO_FLAG_NONE , 0 ) ;
if ( ! SWITCH_READ_ACCEPTABLE ( status ) )
{
/* Our duty is over */
2008-09-04 17:18:22 +00:00
goto done ;
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
/* Skip CNG frames (autogenerated by FreeSWITCH, usually) */
if ( switch_test_flag ( read_frame , SFF_CNG ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Skipping CNG frame \n " ) ;
continue ;
}
2008-09-04 16:25:24 +00:00
2008-09-05 11:34:48 +00:00
/* 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_LOG , SWITCH_LOG_ERROR , " fax_rx reported an error \n " ) ;
goto done ;
2008-09-04 16:25:24 +00:00
}
2008-09-05 11:34:48 +00:00
if ( ( tx = fax_tx ( pvt - > fax_state , buf , write_codec . implementation - > samples_per_frame ) ) < 0 )
{
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " fax_tx reported an error \n " ) ;
2008-09-04 17:18:22 +00:00
goto done ;
}
2008-09-05 11:34:48 +00:00
if ( ! tx )
{
/* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No audio samples to send\n"); */
continue ;
}
else
{
/* Set our write_frame data */
2008-09-04 17:18:22 +00:00
write_frame . datalen = tx * sizeof ( int16_t ) ;
write_frame . samples = tx ;
2008-09-05 11:34:48 +00:00
}
2008-09-04 17:18:22 +00:00
2008-09-05 11:34:48 +00:00
if ( switch_core_session_write_frame ( session , & write_frame , SWITCH_IO_FLAG_NONE , 0 ) ! = SWITCH_STATUS_SUCCESS )
{
/* something weird has happened */
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR ,
" Cannot write frame [datalen: %d, samples: %d] \n " ,
write_frame . datalen , write_frame . samples ) ;
goto done ;
2008-09-04 16:25:24 +00:00
}
2008-09-05 11:34:48 +00:00
2008-09-04 16:54:10 +00:00
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
done :
/* Destroy the SpanDSP structures */
spanfax_destroy ( pvt ) ;
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
/* restore the original codecs over the channel */
2008-09-04 09:33:04 +00:00
2008-09-05 11:34:48 +00:00
if ( read_codec . implementation )
{
switch_core_codec_destroy ( & read_codec ) ;
2008-09-04 16:54:10 +00:00
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
if ( write_codec . implementation )
{
switch_core_codec_destroy ( & write_codec ) ;
2008-09-04 16:54:10 +00:00
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
if ( orig_read_codec )
{
switch_core_session_set_read_codec ( session , orig_read_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 ( switch_strlen_zero ( name ) ) {
continue ;
}
if ( switch_strlen_zero ( 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 , " 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 ) ;
2008-09-04 16:54:10 +00:00
}
2008-09-05 11:34:48 +00:00
}
2008-09-03 17:08:30 +00:00
2008-09-05 11:34:48 +00:00
static void event_handler ( switch_event_t * event )
{
load_configuration ( 1 ) ;
2008-09-03 17:08:30 +00:00
}
2008-09-05 11:34:48 +00:00
/* **************************************************************************
FREESWITCH MODULE DEFINITIONS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define SPANFAX_RX_USAGE "<filename>"
# define SPANFAX_TX_USAGE "<filename>"
SWITCH_MODULE_LOAD_FUNCTION ( mod_fax_init ) ;
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_fax_shutdown ) ;
SWITCH_MODULE_DEFINITION ( mod_fax , mod_fax_init , mod_fax_shutdown , NULL ) ;
static switch_event_node_t * NODE = NULL ;
SWITCH_STANDARD_APP ( spanfax_tx_function )
2008-09-04 11:12:14 +00:00
{
2008-09-05 11:34:48 +00:00
process_fax ( session , data , FUNCTION_TX ) ;
2008-09-04 11:12:14 +00:00
}
2008-09-05 11:34:48 +00:00
SWITCH_STANDARD_APP ( spanfax_rx_function )
2008-09-04 11:12:14 +00:00
{
2008-09-05 11:34:48 +00:00
process_fax ( session , data , FUNCTION_RX ) ;
2008-09-04 11:12:14 +00:00
}
2008-09-05 11:34:48 +00:00
SWITCH_MODULE_LOAD_FUNCTION ( mod_fax_init )
2008-09-03 17:08:30 +00:00
{
2008-09-04 16:54:10 +00:00
switch_application_interface_t * app_interface ;
2008-09-05 11:34:48 +00:00
2008-09-04 16:54:10 +00:00
* module_interface = switch_loadable_module_create_module_interface ( pool , modname ) ;
2008-09-05 11:34:48 +00:00
SWITCH_ADD_APP ( app_interface , " rxfax " , " FAX Receive Application " , " FAX Receive Application " , spanfax_rx_function , SPANFAX_RX_USAGE , SAF_NONE ) ;
SWITCH_ADD_APP ( app_interface , " txfax " , " FAX Transmit Application " , " FAX Transmit Application " , spanfax_tx_function , SPANFAX_TX_USAGE , SAF_NONE ) ;
memset ( & globals , 0 , sizeof ( globals ) ) ;
switch_core_new_memory_pool ( & globals . pool ) ;
switch_mutex_init ( & globals . mutex , SWITCH_MUTEX_NESTED , globals . pool ) ;
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 ) ;
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 */
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " mod_fax loaded, using spandsp library version %d [%d] \n " , SPANDSP_RELEASE_DATE , SPANDSP_RELEASE_TIME ) ;
2008-09-03 17:08:30 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2008-09-05 11:34:48 +00:00
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_fax_shutdown )
{
switch_memory_pool_t * pool = globals . pool ;
switch_core_destroy_memory_pool ( & pool ) ;
memset ( & globals , 0 , sizeof ( globals ) ) ;
return SWITCH_STATUS_UNLOAD ;
}
2008-09-03 17:08:30 +00:00
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : nil
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
2008-09-04 07:08:34 +00:00
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 :
2008-09-03 17:08:30 +00:00
*/