Thu Jul 9 13:40:44 2009

Asterisk developer's documentation


app_fax.c File Reference

#include "asterisk.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <pthread.h>
#include <errno.h>
#include <tiffio.h>
#include <spandsp.h>
#include <spandsp/expose.h>
#include <spandsp/version.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/dsp.h"
#include "asterisk/module.h"
#include "asterisk/manager.h"

Go to the source code of this file.

Data Structures

struct  fax_session

Defines

#define MAX_SAMPLES   240
#define WATCHDOG_STATE_TIMEOUT   5 * 60
#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void * fax_generator_alloc (struct ast_channel *chan, void *params)
static int fax_generator_generate (struct ast_channel *chan, void *data, int len, int samples)
static int load_module (void)
static void phase_e_handler (t30_state_t *f, void *user_data, int result)
static int rcvfax_exec (struct ast_channel *chan, void *data)
static void set_ecm (t30_state_t *state, int ecm)
static void set_file (t30_state_t *state, fax_session *s)
static void set_local_info (t30_state_t *state, fax_session *s)
static int set_logging (logging_state_t *state)
static int sndfax_exec (struct ast_channel *chan, void *data)
static void span_message (int level, const char *msg)
static int t38_tx_packet_handler (t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
static int transmit (fax_session *s)
static int transmit_audio (fax_session *s)
static int transmit_t38 (fax_session *s)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, }
static char * app_rcvfax_desc
static char * app_rcvfax_name = "ReceiveFAX"
static char * app_rcvfax_synopsis = "Receive a FAX"
static char * app_sndfax_desc
static char * app_sndfax_name = "SendFAX"
static char * app_sndfax_synopsis = "Send a FAX"
static const struct ast_module_infoast_module_info = &__mod_info
ast_generator generator


Define Documentation

#define MAX_SAMPLES   240

Definition at line 102 of file app_fax.c.

Referenced by fax_generator_generate().

#define WATCHDOG_STATE_TIMEOUT   5 * 60

Definition at line 111 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 110 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 778 of file app_fax.c.

static void __unreg_module ( void   )  [static]

Definition at line 778 of file app_fax.c.

static void* fax_generator_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 274 of file app_fax.c.

00275 {
00276    return params;
00277 }

static int fax_generator_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 279 of file app_fax.c.

References AST_FORMAT_SLINEAR, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_write(), buf, chan, errno, ast_frame::frametype, LOG_WARNING, MAX_SAMPLES, ast_channel::name, and ast_frame::samples.

00280 {
00281    fax_state_t *fax = (fax_state_t*) data;
00282    uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
00283    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00284     
00285    struct ast_frame outf = {
00286       .frametype = AST_FRAME_VOICE,
00287       .subclass = AST_FORMAT_SLINEAR,
00288       .src = __FUNCTION__,
00289    };
00290 
00291    if (samples > MAX_SAMPLES) {
00292       ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
00293       samples = MAX_SAMPLES;
00294    }
00295    
00296    if ((len = fax_tx(fax, buf, samples)) > 0) {
00297       outf.samples = len;
00298       AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
00299 
00300       if (ast_write(chan, &outf) < 0) {
00301          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00302          return -1;
00303       }
00304    }
00305 
00306    return 0;
00307 }

static int load_module ( void   )  [static]

Definition at line 761 of file app_fax.c.

References ast_register_application, rcvfax_exec(), and sndfax_exec().

00762 {
00763    int res ;
00764 
00765    res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc);
00766    res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc);
00767 
00768    /* The default SPAN message handler prints to stderr. It is something we do not want */
00769    span_set_message_handler(NULL);
00770 
00771    return res;
00772 }

static void phase_e_handler ( t30_state_t *  f,
void *  user_data,
int  result 
) [static]

Definition at line 157 of file app_fax.c.

References ast_debug, ast_log(), buf, EVENT_FLAG_CALL, LOG_WARNING, manager_event, pbx_builtin_setvar_helper(), s, and S_OR.

Referenced by transmit_audio(), and transmit_t38().

00158 {
00159    const char *local_ident;
00160    const char *far_ident;
00161    char buf[20];
00162    fax_session *s = (fax_session *) user_data;
00163    t30_stats_t stat;
00164    int pages_transferred;
00165 
00166    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00167 
00168    t30_get_transfer_statistics(f, &stat);
00169 
00170    s = (fax_session *) user_data;
00171 
00172    if (result != T30_ERR_OK) {
00173       s->finished = -1;
00174 
00175       /* FAXSTATUS is already set to FAILED */
00176       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00177 
00178       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00179 
00180       return;
00181    }
00182    
00183    s->finished = 1; 
00184    
00185    local_ident = t30_get_tx_ident(f);
00186    far_ident = t30_get_rx_ident(f);
00187    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
00188    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
00189    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00190 #if SPANDSP_RELEASE_DATE >= 20090220
00191    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00192 #else
00193    pages_transferred = stat.pages_transferred;
00194 #endif
00195    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00196    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00197    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00198    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00199    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00200    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
00201    
00202    ast_debug(1, "Fax transmitted successfully.\n");
00203    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00204    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00205    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00206    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00207    
00208    manager_event(EVENT_FLAG_CALL,
00209             s->direction ? "FaxSent" : "FaxReceived", 
00210             "Channel: %s\r\n"
00211             "Exten: %s\r\n"
00212             "CallerID: %s\r\n"
00213             "RemoteStationID: %s\r\n"
00214             "LocalStationID: %s\r\n"
00215             "PagesTransferred: %d\r\n"
00216             "Resolution: %d\r\n"
00217             "TransferRate: %d\r\n"
00218             "FileName: %s\r\n",
00219             s->chan->name,
00220             s->chan->exten,
00221             S_OR(s->chan->cid.cid_num, ""),
00222             far_ident,
00223             local_ident,
00224             pages_transferred,
00225             stat.y_resolution,
00226             stat.bit_rate,
00227             s->file_name);
00228 }

static int rcvfax_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 707 of file app_fax.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.

Referenced by load_module().

00708 {
00709    int res = 0;
00710    char *parse;
00711    fax_session session;
00712 
00713    AST_DECLARE_APP_ARGS(args,
00714       AST_APP_ARG(file_name);
00715       AST_APP_ARG(options);
00716    );
00717 
00718    if (chan == NULL) {
00719       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00720       return -1;
00721    }
00722 
00723    /* The next few lines of code parse out the filename and header from the input string */
00724    if (ast_strlen_zero(data)) {
00725       /* No data implies no filename or anything is present */
00726       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00727       return -1;
00728    }
00729 
00730    parse = ast_strdupa(data);
00731    AST_STANDARD_APP_ARGS(args, parse);
00732    
00733    session.caller_mode = FALSE;
00734 
00735    if (args.options) {
00736       if (strchr(args.options, 'c'))
00737          session.caller_mode = TRUE;
00738    }
00739 
00740    /* Done parsing */
00741    session.direction = 0;
00742    session.file_name = args.file_name;
00743    session.chan = chan;
00744    session.finished = 0;
00745 
00746    res = transmit(&session);
00747 
00748    return res;
00749 }

static void set_ecm ( t30_state_t *  state,
int  ecm 
) [static]

Definition at line 264 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

00265 {
00266    t30_set_ecm_capability(state, ecm);
00267    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00268 }

static void set_file ( t30_state_t *  state,
fax_session s 
) [static]

Definition at line 256 of file app_fax.c.

References s.

Referenced by transmit_audio(), and transmit_t38().

00257 {
00258    if (s->direction)
00259       t30_set_tx_file(state, s->file_name, -1, -1);
00260    else
00261       t30_set_rx_file(state, s->file_name, -1);
00262 }

static void set_local_info ( t30_state_t *  state,
fax_session s 
) [static]

Definition at line 243 of file app_fax.c.

References ast_strlen_zero(), pbx_builtin_getvar_helper(), and s.

Referenced by transmit_audio(), and transmit_t38().

00244 {
00245    const char *x;
00246 
00247    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00248    if (!ast_strlen_zero(x))
00249       t30_set_tx_ident(state, x);
00250 
00251    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00252    if (!ast_strlen_zero(x))
00253       t30_set_tx_page_header_info(state, x);
00254 }

static int set_logging ( logging_state_t *  state  )  [static]

Definition at line 233 of file app_fax.c.

References option_debug, and span_message().

Referenced by transmit_audio(), and transmit_t38().

00234 {
00235    int level = SPAN_LOG_WARNING + option_debug;
00236 
00237    span_log_set_message_handler(state, span_message);
00238    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00239 
00240    return 0;
00241 }

static int sndfax_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 663 of file app_fax.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.

Referenced by load_module().

00664 {
00665    int res = 0;
00666    char *parse;
00667    fax_session session;
00668 
00669    AST_DECLARE_APP_ARGS(args,
00670       AST_APP_ARG(file_name);
00671       AST_APP_ARG(options);
00672    );
00673 
00674    if (chan == NULL) {
00675       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00676       return -1;
00677    }
00678 
00679    /* The next few lines of code parse out the filename and header from the input string */
00680    if (ast_strlen_zero(data)) {
00681       /* No data implies no filename or anything is present */
00682       ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
00683       return -1;
00684    }
00685 
00686    parse = ast_strdupa(data);
00687    AST_STANDARD_APP_ARGS(args, parse);
00688    
00689    session.caller_mode = TRUE;
00690 
00691    if (args.options) {
00692       if (strchr(args.options, 'a'))
00693          session.caller_mode = FALSE;
00694    }
00695 
00696    /* Done parsing */
00697    session.direction = 1;
00698    session.file_name = args.file_name;
00699    session.chan = chan;
00700    session.finished = 0;
00701 
00702    res = transmit(&session);
00703 
00704    return res;
00705 }

static void span_message ( int  level,
const char *  msg 
) [static]

Definition at line 123 of file app_fax.c.

References ast_log(), LOG_DEBUG, LOG_ERROR, and LOG_WARNING.

Referenced by set_logging().

00124 {
00125    if (level == SPAN_LOG_ERROR) {
00126       ast_log(LOG_ERROR, "%s", msg);
00127    } else if (level == SPAN_LOG_WARNING) {
00128       ast_log(LOG_WARNING, "%s", msg);
00129    } else {
00130       ast_log(LOG_DEBUG, "%s", msg);
00131    }
00132 }

static int t38_tx_packet_handler ( t38_core_state_t *  s,
void *  user_data,
const uint8_t *  buf,
int  len,
int  count 
) [static]

Definition at line 134 of file app_fax.c.

References AST_FRAME_MODEM, AST_FRAME_SET_BUFFER, ast_log(), AST_MODEM_T38, ast_write(), chan, errno, ast_frame::frametype, and LOG_WARNING.

Referenced by transmit_t38().

00135 {
00136    struct ast_channel *chan = (struct ast_channel *) user_data;
00137 
00138    struct ast_frame outf = {
00139       .frametype = AST_FRAME_MODEM,
00140       .subclass = AST_MODEM_T38,
00141       .src = __FUNCTION__,
00142    };
00143 
00144    /* TODO: Asterisk does not provide means of resending the same packet multiple
00145      times so count is ignored at the moment */
00146 
00147    AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
00148 
00149    if (ast_write(chan, &outf) < 0) {
00150       ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
00151       return -1;
00152    }
00153 
00154    return 0;
00155 }

static int transmit ( fax_session s  )  [static]

Definition at line 605 of file app_fax.c.

References ast_answer(), ast_channel_get_t38_state(), ast_debug, ast_log(), AST_STATE_UP, LOG_ERROR, LOG_WARNING, pbx_builtin_setvar_helper(), s, T38_STATE_NEGOTIATED, transmit_audio(), and transmit_t38().

Referenced by rcvfax_exec(), and sndfax_exec().

00606 {
00607    int res = 0;
00608 
00609    /* Clear all channel variables which to be set by the application.
00610       Pre-set status to error so in case of any problems we can just leave */
00611    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
00612    pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
00613 
00614    pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
00615    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
00616    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL);
00617    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
00618    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
00619 
00620    if (s->chan->_state != AST_STATE_UP) {
00621       /* Shouldn't need this, but checking to see if channel is already answered
00622        * Theoretically asterisk should already have answered before running the app */
00623       res = ast_answer(s->chan);
00624       if (res) {
00625          ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
00626          return res;
00627       }
00628    }
00629 
00630    s->t38state = ast_channel_get_t38_state(s->chan);
00631    if (s->t38state != T38_STATE_NEGOTIATED) {
00632       /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
00633       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
00634       res = transmit_audio(s);
00635       if (res > 0) {
00636          /* transmit_audio reports switchover to T38. Update t38state */
00637          s->t38state = ast_channel_get_t38_state(s->chan);
00638          if (s->t38state != T38_STATE_NEGOTIATED) {
00639             ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
00640          }
00641       }
00642    }
00643 
00644    if (s->t38state == T38_STATE_NEGOTIATED) {
00645       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
00646       res = transmit_t38(s);
00647    }
00648 
00649    if (res) {
00650       ast_log(LOG_WARNING, "Transmission error\n");
00651       res = -1;
00652    } else if (s->finished < 0) {
00653       ast_log(LOG_WARNING, "Transmission failed\n");
00654    } else if (s->finished > 0) {
00655       ast_debug(1, "Transmission finished Ok\n");
00656    }
00657 
00658    return res;
00659 }

static int transmit_audio ( fax_session s  )  [static]

Definition at line 317 of file app_fax.c.

References ast_activate_generator(), AST_CONTROL_T38, ast_deactivate_generator(), ast_debug, ast_dsp_free(), ast_dsp_new(), ast_dsp_process(), ast_dsp_set_faxmode(), ast_dsp_set_features(), AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frdup(), ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_write_format(), AST_T38_NEGOTIATED, AST_T38_REQUEST_NEGOTIATE, ast_tvdiff_sec(), ast_tvnow(), ast_waitfor(), ast_frame::data, ast_frame::datalen, DSP_FAXMODE_DETECT_CNG, DSP_FEATURE_FAX_DETECT, ast_frame::frametype, generator, LOG_WARNING, phase_e_handler(), s, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_UNAVAILABLE, TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

00318 {
00319    int res = -1;
00320    int original_read_fmt = AST_FORMAT_SLINEAR;
00321    int original_write_fmt = AST_FORMAT_SLINEAR;
00322    fax_state_t fax;
00323    t30_state_t *t30state;
00324    struct ast_dsp *dsp = NULL;
00325    int detect_tone = 0;
00326    struct ast_frame *inf = NULL;
00327    struct ast_frame *fr;
00328    int last_state = 0;
00329    struct timeval now, start, state_change;
00330    enum ast_control_t38 t38control;
00331 
00332 #if SPANDSP_RELEASE_DATE >= 20080725
00333         /* for spandsp shaphots 0.0.6 and higher */
00334         t30state = &fax.t30;
00335 #else
00336         /* for spandsp release 0.0.5 */
00337         t30state = &fax.t30_state;
00338 #endif
00339 
00340    original_read_fmt = s->chan->readformat;
00341    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00342       res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
00343       if (res < 0) {
00344          ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
00345          goto done;
00346       }
00347    }
00348 
00349    original_write_fmt = s->chan->writeformat;
00350    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00351       res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
00352       if (res < 0) {
00353          ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
00354          goto done;
00355       }
00356    }
00357 
00358    /* Initialize T30 terminal */
00359    fax_init(&fax, s->caller_mode);
00360 
00361    /* Setup logging */
00362    set_logging(&fax.logging);
00363    set_logging(&t30state->logging);
00364 
00365    /* Configure terminal */
00366    set_local_info(t30state, s);
00367    set_file(t30state, s);
00368    set_ecm(t30state, TRUE);
00369 
00370    fax_set_transmit_on_idle(&fax, TRUE);
00371 
00372    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00373 
00374    if (s->t38state == T38_STATE_UNAVAILABLE) {
00375       ast_debug(1, "T38 is unavailable on %s\n", s->chan->name);
00376    } else if (!s->direction) {
00377       /* We are receiving side and this means we are the side which should
00378          request T38 when the fax is detected. Use DSP to detect fax tone */
00379       ast_debug(1, "Setting up CNG detection on %s\n", s->chan->name);
00380       dsp = ast_dsp_new();
00381       ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
00382       ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG);
00383       detect_tone = 1;
00384    }
00385 
00386    start = state_change = ast_tvnow();
00387 
00388    ast_activate_generator(s->chan, &generator, &fax);
00389 
00390    while (!s->finished) {
00391       res = ast_waitfor(s->chan, 20);
00392       if (res < 0)
00393          break;
00394       else if (res > 0)
00395          res = 0;
00396 
00397       inf = ast_read(s->chan);
00398       if (inf == NULL) {
00399          ast_debug(1, "Channel hangup\n");
00400          res = -1;
00401          break;
00402       }
00403 
00404       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00405 
00406       /* Detect fax tone */
00407       if (detect_tone && inf->frametype == AST_FRAME_VOICE) {
00408          /* Duplicate frame because ast_dsp_process may free the frame passed */
00409          fr = ast_frdup(inf);
00410 
00411          /* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */
00412          fr = ast_dsp_process(NULL, dsp, fr);
00413          if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') {
00414             ast_debug(1, "Fax tone detected. Requesting T38\n");
00415             t38control = AST_T38_REQUEST_NEGOTIATE;
00416             ast_indicate_data(s->chan, AST_CONTROL_T38, &t38control, sizeof(t38control));
00417             detect_tone = 0;
00418          }
00419 
00420          ast_frfree(fr);
00421       }
00422 
00423 
00424       /* Check the frame type. Format also must be checked because there is a chance
00425          that a frame in old format was already queued before we set chanel format
00426          to slinear so it will still be received by ast_read */
00427       if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) {
00428 
00429          if (fax_rx(&fax, inf->data, inf->samples) < 0) {
00430             /* I know fax_rx never returns errors. The check here is for good style only */
00431             ast_log(LOG_WARNING, "fax_rx returned error\n");
00432             res = -1;
00433             break;
00434          }
00435 
00436          /* Watchdog */
00437          if (last_state != t30state->state) {
00438             state_change = ast_tvnow();
00439             last_state = t30state->state;
00440          }
00441       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
00442             inf->datalen == sizeof(enum ast_control_t38)) {
00443          t38control =*((enum ast_control_t38 *) inf->data);
00444          if (t38control == AST_T38_NEGOTIATED) {
00445             /* T38 switchover completed */
00446             ast_debug(1, "T38 negotiated, finishing audio loop\n");
00447             res = 1;
00448             break;
00449          }
00450       }
00451 
00452       ast_frfree(inf);
00453       inf = NULL;
00454 
00455       /* Watchdog */
00456       now = ast_tvnow();
00457       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00458          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00459          res = -1;
00460          break;
00461       }
00462    }
00463 
00464    ast_debug(1, "Loop finished, res=%d\n", res);
00465 
00466    if (inf)
00467       ast_frfree(inf);
00468 
00469    if (dsp)
00470       ast_dsp_free(dsp);
00471 
00472    ast_deactivate_generator(s->chan);
00473 
00474    /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
00475       by t30_terminate, display diagnostics and set status variables although no transmittion
00476       has taken place yet. */
00477    if (res > 0) {
00478       t30_set_phase_e_handler(t30state, NULL, NULL);
00479    }
00480 
00481    t30_terminate(t30state);
00482    fax_release(&fax);
00483 
00484 done:
00485    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00486       if (ast_set_write_format(s->chan, original_write_fmt) < 0)
00487          ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name);
00488    }
00489 
00490    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00491       if (ast_set_read_format(s->chan, original_read_fmt) < 0)
00492          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name);
00493    }
00494 
00495    return res;
00496 
00497 }

static int transmit_t38 ( fax_session s  )  [static]

Definition at line 499 of file app_fax.c.

References AST_CONTROL_T38, ast_debug, AST_FRAME_CONTROL, AST_FRAME_MODEM, ast_frfree, ast_log(), AST_MODEM_T38, ast_read(), AST_T38_REFUSED, AST_T38_TERMINATED, ast_tvdiff_sec(), ast_tvdiff_us(), ast_tvnow(), ast_waitfor(), ast_frame::data, ast_frame::datalen, ast_frame::frametype, LOG_WARNING, phase_e_handler(), s, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, t38_tx_packet_handler(), TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

00500 {
00501    int res = 0;
00502    t38_terminal_state_t t38;
00503    struct ast_frame *inf = NULL;
00504    int last_state = 0;
00505    struct timeval now, start, state_change, last_frame;
00506    enum ast_control_t38 t38control;
00507 
00508    t30_state_t *t30state;
00509    t38_core_state_t *t38state;
00510 
00511 #if SPANDSP_RELEASE_DATE >= 20080725
00512    /* for spandsp shaphots 0.0.6 and higher */
00513    t30state = &t38.t30;
00514    t38state = &t38.t38_fe.t38;
00515 #else
00516    /* for spandsp releases 0.0.5 */
00517    t30state = &t38.t30_state;
00518    t38state = &t38.t38;
00519 #endif
00520 
00521    /* Initialize terminal */
00522    memset(&t38, 0, sizeof(t38));
00523    if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
00524       ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
00525       return -1;
00526    }
00527 
00528    /* Setup logging */
00529    set_logging(&t38.logging);
00530    set_logging(&t30state->logging);
00531    set_logging(&t38state->logging);
00532 
00533    /* Configure terminal */
00534    set_local_info(t30state, s);
00535    set_file(t30state, s);
00536    set_ecm(t30state, TRUE);
00537 
00538    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00539 
00540    now = start = state_change = ast_tvnow();
00541 
00542    while (!s->finished) {
00543 
00544       res = ast_waitfor(s->chan, 20);
00545       if (res < 0)
00546          break;
00547       else if (res > 0)
00548          res = 0;
00549 
00550       last_frame = now;
00551       now = ast_tvnow();
00552       t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00553 
00554       inf = ast_read(s->chan);
00555       if (inf == NULL) {
00556          ast_debug(1, "Channel hangup\n");
00557          res = -1;
00558          break;
00559       }
00560 
00561       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00562 
00563       if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) {
00564          t38_core_rx_ifp_packet(t38state, inf->data, inf->datalen, inf->seqno);
00565 
00566          /* Watchdog */
00567          if (last_state != t30state->state) {
00568             state_change = ast_tvnow();
00569             last_state = t30state->state;
00570          }
00571       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 &&
00572             inf->datalen == sizeof(enum ast_control_t38)) {
00573 
00574          t38control = *((enum ast_control_t38 *) inf->data);
00575 
00576          if (t38control == AST_T38_TERMINATED || t38control == AST_T38_REFUSED) {
00577             ast_debug(1, "T38 down, terminating\n");
00578             res = -1;
00579             break;
00580          }
00581       }
00582 
00583       ast_frfree(inf);
00584       inf = NULL;
00585 
00586       /* Watchdog */
00587       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00588          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00589          res = -1;
00590          break;
00591       }
00592    }
00593 
00594    ast_debug(1, "Loop finished, res=%d\n", res);
00595 
00596    if (inf)
00597       ast_frfree(inf);
00598 
00599    t30_terminate(t30state);
00600    t38_terminal_release(&t38);
00601 
00602    return res;
00603 }

static int unload_module ( void   )  [static]

Definition at line 751 of file app_fax.c.

References ast_unregister_application().

00752 {
00753    int res;
00754 
00755    res = ast_unregister_application(app_sndfax_name); 
00756    res |= ast_unregister_application(app_rcvfax_name);   
00757 
00758    return res;
00759 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, } [static]

Definition at line 778 of file app_fax.c.

char* app_rcvfax_desc [static]

Definition at line 76 of file app_fax.c.

char* app_rcvfax_name = "ReceiveFAX" [static]

Definition at line 74 of file app_fax.c.

char* app_rcvfax_synopsis = "Receive a FAX" [static]

Definition at line 75 of file app_fax.c.

char* app_sndfax_desc [static]

Definition at line 49 of file app_fax.c.

char* app_sndfax_name = "SendFAX" [static]

Definition at line 47 of file app_fax.c.

char* app_sndfax_synopsis = "Send a FAX" [static]

Definition at line 48 of file app_fax.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 778 of file app_fax.c.

struct ast_generator generator

Initial value:

Definition at line 309 of file app_fax.c.

Referenced by transmit_audio().


Generated on Thu Jul 9 13:40:44 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7