提交 39f9f22f authored 作者: Massimo Cetra's avatar Massimo Cetra

Completely new version of mod_fax



git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@9460 d0543943-73ff-0310-b7d9-9358b9ac24b2
上级 ce9d7576
<configuration name="fax.conf" description="FAX application configuration">
<settings>
<param name="use-ecm" value="true"/>
<param name="verbose" value="false"/>
<param name="disable-v17" value="false"/>
<param name="ident" value="SpanDSP Fax Ident"/>
<param name="header" value="SpanDSP Fax Header"/>
<param name="spool-dir" value="/tmp"/>
<param name="file-prefix" value="faxrx"/>
</settings>
</configuration>
...@@ -14,19 +14,21 @@ ...@@ -14,19 +14,21 @@
* for the specific language governing rights and limitations under the * for the specific language governing rights and limitations under the
* License. * License.
* *
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * The Original Code is FreeSWITCH mod_timezone.
* *
* The Initial Developer of the Original Code is * The Initial Developer of the Original Code is
* Anthony Minessale II <anthmct@yahoo.com> * Massimo Cetra <devel@navynet.it>
*
* Portions created by the Initial Developer are Copyright (C) * Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved. * the Initial Developer. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* *
* Brian West <brian@freeswitch.org> * Brian West <brian@freeswitch.org>
* Antonio Gallo <agx@linux.it> * Anthony Minessale II <anthmct@yahoo.com>
* Steve Underwood <steveu@coppice.org>
* *
* mod_fax.c -- Fax Module * mod_fax.c -- Fax applications provided by SpanDSP
* *
*/ */
...@@ -34,326 +36,534 @@ ...@@ -34,326 +36,534 @@
#include <spandsp.h> #include <spandsp.h>
#include <spandsp/version.h> #include <spandsp/version.h>
#define MAX_BLOCK_SIZE 240 /*****************************************************************************
OUR DEFINES AND STRUCTS
*****************************************************************************/
SWITCH_MODULE_LOAD_FUNCTION(mod_fax_load); typedef enum {
SWITCH_MODULE_DEFINITION(mod_fax, mod_fax_load, NULL, NULL); FUNCTION_TX,
FUNCTION_RX
} application_mode_t;
/* typedef enum {
* output spandsp lowlevel messages T38_MODE,
*/ AUDIO_MODE
} transport_mode_t;
static void span_message(int level, const char *msg)
{
int fs_log_level;
/* TODO: maybe is better to use switch_assert here? /* The global stuff */
if (msg==NULL) { static struct {
return; switch_memory_pool_t *pool;
} switch_mutex_t *mutex;
*/
switch_assert(msg);
switch (level) { uint32_t total_sessions;
/* TODO: i need to ask Coppice what SPAN_LOG_NONE and SPA_LOG_FLOW are exactly for
case SPAN_LOG_NONE:
return;
case SPAN_LOG_FLOW:
case SPAN_LOG_FLOW_2:
case SPAN_LOG_FLOW_3:
if (!debug)
return;
fs_log_level = SWITCH_LOG_DEBUG;
break;
*/
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;
default: /* SPAN_LOG_DEBUG, SPAN_LOG_DEBUG_2, SPAN_LOG_DEBUG_3 */
fs_log_level = SWITCH_LOG_DEBUG;
break;
}
switch_log_printf(SWITCH_CHANNEL_LOG, fs_log_level, "%s", msg );
}
/* short int use_ecm;
* This function is called when the negotiation is completed 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;
*/
static int phase_b_handler(t30_state_t *s, void *user_data, int result)
{
int session;
const char *u = NULL;
switch_assert(user_data != NULL);
session = (intptr_t) user_data;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Phase B: handler on session %d - (0x%X) %s\n", session, result, t30_frametype(result));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote ident: %s\n", t30_get_rx_ident(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote sub-address: %s\n", t30_get_rx_sub_address(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote polled sub-address: %s\n", t30_get_rx_polled_sub_address(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote sel. polling addr.: %s\n", t30_get_rx_selective_polling_address(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote sender ident: %s\n", t30_get_rx_sender_ident(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote password: %s\n", t30_get_rx_password(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
return T30_ERR_OK;
}; };
typedef struct pvt_s pvt_t;
/* /*****************************************************************************
* This function is called whenever a single new page has been received/transmitted LOGGING AND HELPER FUNCTIONS
*/ *****************************************************************************/
static int phase_d_handler(t30_state_t *s, void *user_data, int result) 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)
{ {
t30_stats_t t; int fs_log_level;
int session = (intptr_t) user_data;
switch_assert(user_data);
if (!result) return T30_ERR_OK; 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;
}
t30_get_transfer_statistics(s, &t); if ( !switch_strlen_zero(msg) )
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n"); switch_log_printf(SWITCH_CHANNEL_LOG, fs_log_level, "%s", msg );
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Phase D handler on channel %d - (0x%X) %s\n", session, session, result, t30_frametype(result));
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, "Pages in the file: %i\n", t.pages_in_file);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Image size: %i x %i\n", t.width, t.length);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Image resolution %i x %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, "Bad rows %i\n", t.bad_rows);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Longest bad row run %i\n", t.longest_bad_row_run);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Compression type %i %s\n", t.encoding, t4_encoding_to_str(t.encoding));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Image size (bytes) %i\n", t.image_size);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "ECM %s\n", (t.error_correcting_mode) ? "on" : "off");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "local ident: %s\n", t30_get_tx_ident(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote ident: %s\n", t30_get_rx_ident(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "bits per row - min %d, max %d\n", s->t4.min_row_bits, s->t4.max_row_bits);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
return T30_ERR_OK;
} }
/* /*
* This function is called when the fax has finished his job * Called at the end of the document
*/ */
static void phase_e_handler(t30_state_t *s, void *user_data, int result) static void phase_e_handler(t30_state_t *s, void *user_data, int result)
{ {
t30_stats_t t; t30_stats_t t;
const char *far_ident = NULL; const char *local_ident;
switch_channel_t *chan = (switch_channel_t *) user_data; const char *far_ident;
char buf[128]; switch_core_session_t *session;
switch_channel_t *chan;
char *tmp;
session = ( switch_core_session_t* ) user_data;
switch_assert(session);
chan = switch_core_session_get_channel(session);
switch_assert(chan); switch_assert(chan);
if (result == T30_ERR_OK) { t30_get_transfer_statistics(s, &t);
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) );
far_ident = switch_str_nil( t30_get_tx_ident(s) );
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
//TODO: add received/transmitted ? if (result == T30_ERR_OK)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Fax successfully processed.\n"); {
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, "Fax successfully sent.\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Local station id: %s\n", switch_str_nil( t30_get_rx_ident(s)) ); switch_channel_set_variable(chan, "fax_success", "1");
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, "Image resolution: %i x %i\n", t.x_resolution, t.y_resolution); else
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, "local ident: %s\n", t30_get_tx_ident(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "remote ident: %s\n", t30_get_rx_ident(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "station country: %s\n", t30_get_rx_country(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "station vendor: %s\n", t30_get_rx_vendor(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "station model: %s\n", t30_get_rx_model(s));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
//TODO: is the buffer too little? anyway <MikeJ> is going to write a new set_variable function that will allow a printf like syntax very soon
switch_snprintf(buf, sizeof(buf), "%d", t.pages_transferred);
switch_channel_set_variable(chan, "FAX_PAGES", buf);
switch_snprintf(buf, sizeof(buf), "%dx%d", t.x_resolution, t.y_resolution);
switch_channel_set_variable(chan, "FAX_SIZE", buf);
switch_snprintf(buf, sizeof(buf), "%d", t.bit_rate);
switch_channel_set_variable(chan, "FAX_SPEED", buf);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n");
//TODO: add received/transmitted ?
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Fax processing not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Fax processing not successful - result (%d) %s.\n", result, t30_completion_code_to_str(result));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "==============================================================================\n"); switch_channel_set_variable(chan, "fax_success", "0");
} }
//TODO: remove the assert once this has been tested switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Remote station id: %s\n", far_ident );
switch_channel_set_variable(chan, "FAX_REMOTESTATIONID", far_ident); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Local station id: %s\n", local_ident );
switch_snprintf(buf, sizeof(buf), "%d", result); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Pages transferred: %i\n", t.pages_transferred);
switch_channel_set_variable(chan, "FAX_RESULT", buf); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Total fax pages: %i\n", t.pages_in_file);
switch_channel_set_variable(chan, "FAX_ERROR", t30_completion_code_to_str(result)); 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");
/* /*
* TODO Set our channel variables
* Fire here an EVENT about the result of the fax call */
*/
}
/* tmp = switch_mprintf("%i", result);
* Document Handler - callback function for T.30 end of document handling. 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) );
static int document_handler(t30_state_t *s, void *user_data, int event) 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 )
{ {
int i = (intptr_t) user_data;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%d: Document handler on channel %d - event %d\n", i, i, event); switch_core_session_t *session;
return FALSE; 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;
} }
/* static switch_status_t spanfax_destroy( pvt_t *pvt ) {
* Main fax processing function: int terminate;
* - calling_party is boolean: TRUE = TxFax and FALSE = RxFax
*/ 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;
void process_fax(switch_core_session_t *session, char *data, int calling_party) 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)
{ {
switch_channel_t *channel; pvt_t *pvt;
switch_codec_t *orig_read_codec = NULL; const char *tmp;
switch_codec_t read_codec = {0};
switch_codec_t write_codec = {0}; switch_channel_t *channel;
switch_frame_t *read_frame = {0};
switch_frame_t write_frame = {0}; switch_codec_t *orig_read_codec = NULL;
switch_status_t status; switch_codec_t read_codec = {0};
fax_state_t fax; switch_codec_t write_codec = {0};
#define FAX_BUFFER_SIZE 4096 switch_frame_t *read_frame = {0};
int16_t buf[FAX_BUFFER_SIZE]; //TODO original value: 512 switch_frame_t write_frame = {0};
int tx = 0;
/*TODO: int calling_party = FALSE; DEPRECATED */ int16_t *buf = NULL;
/* Channels variable parsing */
char *file_name = NULL;
const char *fax_local_debug = NULL;
int debug = FALSE;
const char *fax_local_number = NULL;
const char *fax_local_name = NULL;
const char *fax_local_subname = NULL;
const char *fax_local_ecm = NULL;
const char *fax_local_v17 = NULL;
/* make sure we have a valid channel when starting the FAX application */ /* make sure we have a valid channel when starting the FAX application */
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL); switch_assert(channel != NULL);
/* set output varialbles to a reasonable result */ /* Allocate our structs */
switch_channel_set_variable(channel, "FAX_REMOTESTATIONID", "unknown"); pvt = switch_core_session_alloc(session, sizeof(pvt_t));
switch_channel_set_variable(channel, "FAX_PAGES", "0");
switch_channel_set_variable(channel, "FAX_SIZE", "0");
switch_channel_set_variable(channel, "FAX_SPEED", "0");
switch_channel_set_variable(channel, "FAX_RESULT", "1");
//TODO: add received/transmitted ?
switch_channel_set_variable(channel, "FAX_ERROR", "fax not processed yet");
/* counter_increment();
* SpanDSP initialization
* if fax_init by any chance fails to start we return quickly without touching anything else if ( !pvt )
*/ {
//TODO: ask Coppice to see if we need to clear the fax structure before fax_init is invoked switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot allocate application private data\n");
memset(&fax, 0, sizeof(fax));
if (NULL == fax_init(&fax, calling_party)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "fax_init failed\n");
return; return;
} }
else
{
memset( pvt, 0, sizeof(pvt_t) );
/* pvt->session = session;
* Enable options based on channel variables and input parameters pvt->app_mode = app_mode;
*/
/* file_name - Sets the TIFF filename where do you want to save the fax */ pvt->tx_page_start = -1;
file_name = switch_core_session_strdup(session, data); pvt->tx_page_end = -1;
/* it is important that file_name is not NULL or an empty string */
if (switch_strlen_zero(file_name)) { if ( pvt->app_mode == FUNCTION_TX )
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "fax filename is NULL or empty string\n"); 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");
return; return;
} }
if (calling_party)
t30_set_tx_file(&fax.t30, file_name, -1, -1);
else
t30_set_rx_file(&fax.t30, file_name, -1);
/* FAX_DEBUG - enable extra debugging if defined */ /* Retrieving our settings from the channel variables */
debug = ( NULL != switch_channel_get_variable(channel, "FAX_DEBUG") ); 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;
/* FAX_LOCAL_NUMBER - Set your station phone number */ if ( (tmp=switch_channel_get_variable(channel, "fax_disable_v17")) ) {
t30_set_tx_ident(&fax.t30, switch_str_nil(switch_channel_get_variable(channel, "FAX_LOCAL_NUMBER"))); if ( switch_true(tmp) )
pvt->disable_v17 = 1;
else
pvt->disable_v17 = 0;
}
else
pvt->disable_v17 = globals.disable_v17;
/* FAX_LOCAL_NAME - Set your station ID name (string) */ if ( (tmp=switch_channel_get_variable(channel, "fax_verbose")) ) {
t30_set_tx_page_header_info(&fax.t30, switch_str_nil(switch_channel_get_variable(channel, "FAX_LOCAL_NAME"))); if ( switch_true(tmp) )
pvt->verbose = 1;
else
pvt->verbose = 0;
}
else
pvt->verbose = globals.verbose;
/* FAX_LOCAL_SUBNAME - Set your station ID sub name */ if ( (tmp=switch_channel_get_variable(channel, "fax_force_caller")) ) {
t30_set_tx_sub_address(&fax.t30, switch_str_nil(switch_channel_get_variable(channel, "FAX_LOCAL_SUBNAME"))); if ( switch_true(tmp) )
pvt->caller = 1;
else
pvt->caller = 0;
}
/* FAX_DISABLE_ECM - Set if you want ECM on or OFF */ if ( (tmp=switch_channel_get_variable(channel, "fax_ident")) ) {
if (NULL != switch_channel_get_variable(channel, "FAX_DISABLE_ECM")) { pvt->ident = switch_core_session_strdup(session, tmp);
t30_set_ecm_capability(&fax.t30, TRUE);
t30_set_supported_compressions(&fax.t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
} else {
t30_set_ecm_capability(&fax.t30, FALSE);
} }
else
pvt->ident = switch_core_session_strdup(session, globals.ident);
/* FAX_DISABLE_V17 - set if you want 9600 or V17 (14.400) */ if ( (tmp=switch_channel_get_variable(channel, "fax_header")) ) {
if (NULL != switch_channel_get_variable(channel, "FAX_DISABLE_V17")) { pvt->header = switch_core_session_strdup(session, tmp);
t30_set_supported_modems(&fax.t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER | T30_SUPPORT_V17 );
} else {
t30_set_supported_modems(&fax.t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER);
} }
else
pvt->header = switch_core_session_strdup(session, globals.header);
/* TODO, ask Coppice if this is really working or not and then add FAX_PASSWORD channel variable (don't forget the wiki part too)
* t30_set_tx_password(&mc->fax.t30_state, "Password");
*/
/* Configure more spanDSP internal options */ if ( pvt->app_mode == FUNCTION_TX )
{
/* Select whether silent audio will be sent when FAX transmit is idle. */ if ( (tmp=switch_channel_get_variable(channel, "fax_start_page")) ) {
fax_set_transmit_on_idle(&fax, TRUE); pvt->tx_page_start = atoi(tmp);
}
/* Support for different image sizes && resolutions */ if ( (tmp=switch_channel_get_variable(channel, "fax_end_page")) ) {
t30_set_supported_image_sizes(&fax.t30, T30_SUPPORT_US_LETTER_LENGTH | T30_SUPPORT_US_LEGAL_LENGTH | T30_SUPPORT_UNLIMITED_LENGTH pvt->tx_page_end = atoi(tmp);
| 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);
/* set phase handlers callbaks */ if ( pvt->tx_page_end < -1 )
t30_set_phase_d_handler(&fax.t30, phase_d_handler, session); pvt->tx_page_end = -1;
t30_set_phase_e_handler(&fax.t30, phase_e_handler, channel); if ( pvt->tx_page_start < -1 )
if (debug) { pvt->tx_page_start = -1;
t30_set_phase_b_handler(&fax.t30, phase_b_handler, session); if ( (pvt->tx_page_end < pvt->tx_page_start) && ( pvt->tx_page_end != -1 ) )
t30_set_document_handler(&fax.t30, document_handler, session); pvt->tx_page_end = pvt->tx_page_start;
} }
/* set spanDSP logging functions - set spanlog debug messages to be outputted somewhere */ if ( !switch_strlen_zero(data) ) {
span_log_set_message_handler(&fax.logging, span_message); pvt->filename = switch_core_session_strdup(session, data);
span_log_set_message_handler(&fax.t30.logging, span_message); if ( pvt->app_mode == FUNCTION_TX ) {
if (debug) { if ( (switch_file_exists( pvt->filename, switch_core_session_get_pool(session) )!=SWITCH_STATUS_SUCCESS) ) {
/* TODO: original switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot send inexistant fax file [%s]\n", switch_str_nil(pvt->filename) );
span_log_set_level(&fax.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); goto done;
span_log_set_level(&fax.t30.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); }
*/ }
span_log_set_level(&fax.logging, SPAN_LOG_NONE|SPAN_LOG_FLOW|SPAN_LOG_FLOW_2|SPAN_LOG_FLOW_3|SPAN_LOG_ERROR|SPAN_LOG_PROTOCOL_ERROR|SPAN_LOG_WARNING|SPAN_LOG_PROTOCOL_WARNING|SPAN_LOG_DEBUG|SPAN_LOG_DEBUG_2|SPAN_LOG_DEBUG_3 );
span_log_set_level(&fax.t30.logging, SPAN_LOG_NONE|SPAN_LOG_FLOW|SPAN_LOG_FLOW_2|SPAN_LOG_FLOW_3|SPAN_LOG_ERROR|SPAN_LOG_PROTOCOL_ERROR|SPAN_LOG_WARNING|SPAN_LOG_PROTOCOL_WARNING|SPAN_LOG_DEBUG|SPAN_LOG_DEBUG_2|SPAN_LOG_DEBUG_3 );
} else {
span_log_set_level(&fax.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL );
span_log_set_level(&fax.t30.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL );
} }
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;
/* We're now ready to answer the channel and process the audio of the call */ time = switch_time_now();
/* Answer the call, otherwise we're not getting incoming audio */ if ( !(prefix=switch_channel_get_variable(channel, "fax_prefix")) ) {
switch_channel_answer(channel); prefix = globals.prepend_string;
}
/* TODO: fname = switch_mprintf("%s/%s-%ld-%ld.tif", globals.spool, prefix, globals.total_sessions, time);
* it could be a good idea to disable ECHOCAN on ZAP channels and to reset volumes too if ( fname )
* anyone know how to do this if one of the channell is OpenZap isntead of Sofia? {
* TIPS: perhaps "disable_ec" apps ? 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.
*/ */
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 */
/* We store the original channel codec before switching both /* We store the original channel codec before switching both
* legs of the calls to a linear 16 bit codec that is the one * legs of the calls to a linear 16 bit codec that is the one
* used internally by spandsp and FS will do the transcoding * used internally by spandsp and FS will do the transcoding
...@@ -361,140 +571,283 @@ void process_fax(switch_core_session_t *session, char *data, int calling_party) ...@@ -361,140 +571,283 @@ void process_fax(switch_core_session_t *session, char *data, int calling_party)
*/ */
orig_read_codec = switch_core_session_get_read_codec(session); orig_read_codec = switch_core_session_get_read_codec(session);
if (switch_core_codec_init(&read_codec, if (switch_core_codec_init(
"L16", &read_codec,
NULL, "L16",
orig_read_codec->implementation->samples_per_second, NULL,
orig_read_codec->implementation->microseconds_per_frame / 1000, orig_read_codec->implementation->samples_per_second,
1, orig_read_codec->implementation->microseconds_per_frame / 1000,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, 1,
NULL, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { NULL,
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Success L16 on Leg-A\n"); switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS
} else { )
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16 on Leg-A"); {
goto done; 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);
}
if (switch_core_codec_init(&write_codec, else
"L16", {
NULL, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Raw read codec activation Failed L16\n");
orig_read_codec->implementation->samples_per_second, goto done;
orig_read_codec->implementation->microseconds_per_frame / 1000, }
1,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, if (switch_core_codec_init(
NULL, &write_codec,
switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { "L16",
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Success L16 on Leg-B\n"); NULL,
} else { orig_read_codec->implementation->samples_per_second,
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16 on Leg-B"); orig_read_codec->implementation->microseconds_per_frame / 1000,
goto done; 1,
} SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL,
write_frame.codec = &write_codec; switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS
write_frame.data = buf; )
{
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;
}
/* /*
* now we enter a loop where we read audio frames to the channels and will pass it to spandsp * now we enter a loop where we read audio frames to the channels and will pass it to spandsp
* and if there is some outgoing frame we'll send it back to the calling fax machine
*/ */
while( switch_channel_ready(channel) )
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 */ /* read new audio frame from the channel */
status = switch_core_session_read_frame(session, &read_frame, -1, 0); status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
if (!SWITCH_READ_ACCEPTABLE(status)) {
if ( !SWITCH_READ_ACCEPTABLE(status) )
{
/* Our duty is over */
goto done; goto done;
} }
//DEBUG switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Frame Read: %d\n" , read_frame->samples); /* 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;
}
/* pass the new incoming audio frame to the fax_rx application */ /* pass the new incoming audio frame to the fax_rx function */
if( fax_rx(&fax, (int16_t *)read_frame->data, read_frame->samples) ) { if( fax_rx(pvt->fax_state, (int16_t *)read_frame->data, read_frame->samples) )
//TODO {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "FAX_RX error\n" ); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "fax_rx reported an error\n" );
goto done;
} }
//TODO
if (read_frame->samples > 256 )
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SAMPLES TOO BIG\n" );
if ((tx = fax_tx(&fax, (int16_t *) &buf, write_codec.implementation->samples_per_frame)) < 0) { 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 Error\n"); {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "fax_tx reported an error\n");
goto done; goto done;
} }
//DEBUG switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Fax Tx : %d %d\n" , tx, write_codec.implementation->samples_per_frame);
if (tx!=0) { if ( !tx )
{
/* switch_log_printf(SWITCH_CHANNEL_LOG, 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.datalen = tx * sizeof(int16_t);
write_frame.samples = tx; write_frame.samples = tx;
}
if (switch_core_session_write_frame(session, &write_frame, -1, 0) != SWITCH_STATUS_SUCCESS) { if ( switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS )
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bad Write, datalen: %d, samples: %d\n", {
write_frame.datalen, /* something weird has happened */
write_frame.samples switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
); "Cannot write frame [datalen: %d, samples: %d]\n",
goto done; write_frame.datalen, write_frame.samples );
} goto done;
//DEBUG switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Frame TX : %d %d\n" , write_frame.datalen, write_frame.samples);
} else {
// TODO
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "non trasmitting\n" );
} }
} }
done: done:
/* Destroy the SpanDSP structures */
spanfax_destroy( pvt );
// TODO: this is here to see if it gets called "pre" or "post" the document handler /* restore the original codecs over the channel */
if (debug)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FAX transmission has terminated.\n");
/* shutdown spandsp so it can create our tiff */ if ( read_codec.implementation )
t30_terminate(&fax.t30); {
fax_release(&fax); switch_core_codec_destroy(&read_codec);
/* restore the original codecs over the channels */
if (read_codec.implementation) {
switch_core_codec_destroy(&read_codec);
} }
if (write_codec.implementation) { if ( write_codec.implementation )
switch_core_codec_destroy(&write_codec); {
switch_core_codec_destroy(&write_codec);
} }
if (orig_read_codec) { if ( orig_read_codec )
switch_core_session_set_read_codec(session, 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);
} }
}
static void event_handler(switch_event_t *event)
{
load_configuration(1);
} }
SWITCH_STANDARD_APP(txfax_function) /* **************************************************************************
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)
{ {
process_fax(session, data, TRUE); process_fax(session, data, FUNCTION_TX);
} }
SWITCH_STANDARD_APP(rxfax_function) SWITCH_STANDARD_APP(spanfax_rx_function)
{ {
process_fax(session, data, FALSE); process_fax(session, data, FUNCTION_RX);
} }
SWITCH_MODULE_LOAD_FUNCTION(mod_fax_load) SWITCH_MODULE_LOAD_FUNCTION(mod_fax_init)
{ {
switch_application_interface_t *app_interface; switch_application_interface_t *app_interface;
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname); *module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_APP(app_interface, "rxfax", "FAX Receive Application", "FAX Receive Application", rxfax_function, "", SAF_NONE); 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", txfax_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "txfax", "FAX Transmit Application", "FAX Transmit Application", spanfax_tx_function, SPANFAX_TX_USAGE, SAF_NONE);
// TODO: ask if i can use LOG functions inside this macro memset(&globals, 0, sizeof(globals) );
/* its important to debug the exact spandsp used */ switch_core_new_memory_pool(&globals.pool);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "using spandsp %i %i\n", SPANDSP_RELEASE_DATE, SPANDSP_RELEASE_TIME ); switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
/* indicate that the module should continue to be loaded */ 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 );
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
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;
}
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论