Sat Mar 10 01:54:32 2012

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/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 SPANDSP_EXPOSE_INTERNAL_STRUCTURES
#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, const char *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, const char *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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, }
static const char app_rcvfax_name [] = "ReceiveFAX"
static const char app_sndfax_name [] = "SendFAX"
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_generator generator


Define Documentation

#define MAX_SAMPLES   240

Definition at line 148 of file app_fax.c.

Referenced by fax_generator_generate().

#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES

Definition at line 34 of file app_fax.c.

#define WATCHDOG_STATE_TIMEOUT   5 * 60

Definition at line 157 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 156 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1005 of file app_fax.c.

static void __unreg_module ( void   )  [static]

Definition at line 1005 of file app_fax.c.

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

Definition at line 326 of file app_fax.c.

00327 {
00328    return params;
00329 }

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

Definition at line 331 of file app_fax.c.

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

00332 {
00333    fax_state_t *fax = (fax_state_t*) data;
00334    uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
00335    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00336     
00337    struct ast_frame outf = {
00338       .frametype = AST_FRAME_VOICE,
00339       .subclass.codec = AST_FORMAT_SLINEAR,
00340       .src = __FUNCTION__,
00341    };
00342 
00343    if (samples > MAX_SAMPLES) {
00344       ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
00345       samples = MAX_SAMPLES;
00346    }
00347    
00348    if ((len = fax_tx(fax, buf, samples)) > 0) {
00349       outf.samples = len;
00350       AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
00351 
00352       if (ast_write(chan, &outf) < 0) {
00353          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00354          return -1;
00355       }
00356    }
00357 
00358    return 0;
00359 }

static int load_module ( void   )  [static]

Definition at line 988 of file app_fax.c.

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

00989 {
00990    int res ;
00991 
00992    res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
00993    res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
00994 
00995    /* The default SPAN message handler prints to stderr. It is something we do not want */
00996    span_set_message_handler(NULL);
00997 
00998    return res;
00999 }

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

Definition at line 203 of file app_fax.c.

References ast_debug, ast_log(), ast_manager_event, ast_channel::caller, fax_session::chan, ast_channel::connected, fax_session::direction, EVENT_FLAG_CALL, ast_channel::exten, fax_session::file_name, fax_session::finished, ast_party_connected_line::id, ast_party_caller::id, LOG_WARNING, ast_party_id::name, ast_channel::name, ast_party_id::number, pbx_builtin_setvar_helper(), S_COR, S_OR, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.

Referenced by transmit_audio(), and transmit_t38().

00204 {
00205    const char *local_ident;
00206    const char *far_ident;
00207    char buf[20];
00208    fax_session *s = (fax_session *) user_data;
00209    t30_stats_t stat;
00210    int pages_transferred;
00211 
00212    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00213 
00214    t30_get_transfer_statistics(f, &stat);
00215 
00216    s = (fax_session *) user_data;
00217 
00218    if (result != T30_ERR_OK) {
00219       s->finished = -1;
00220 
00221       /* FAXSTATUS is already set to FAILED */
00222       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00223 
00224       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00225 
00226       return;
00227    }
00228    
00229    s->finished = 1; 
00230    
00231    local_ident = S_OR(t30_get_tx_ident(f), "");
00232    far_ident = S_OR(t30_get_rx_ident(f), "");
00233    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
00234    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
00235    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00236 #if SPANDSP_RELEASE_DATE >= 20090220
00237    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00238 #else
00239    pages_transferred = stat.pages_transferred;
00240 #endif
00241    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00242    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00243    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00244    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00245    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00246    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
00247    
00248    ast_debug(1, "Fax transmitted successfully.\n");
00249    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00250    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00251    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00252    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00253    
00254    ast_manager_event(s->chan, EVENT_FLAG_CALL,
00255       s->direction ? "FaxSent" : "FaxReceived",
00256       "Channel: %s\r\n"
00257       "Exten: %s\r\n"
00258       "CallerID: %s\r\n"
00259       "CallerIDName: %s\r\n"
00260       "ConnectedLineNum: %s\r\n"
00261       "ConnectedLineName: %s\r\n"
00262       "RemoteStationID: %s\r\n"
00263       "LocalStationID: %s\r\n"
00264       "PagesTransferred: %d\r\n"
00265       "Resolution: %d\r\n"
00266       "TransferRate: %d\r\n"
00267       "FileName: %s\r\n",
00268       s->chan->name,
00269       s->chan->exten,
00270       S_COR(s->chan->caller.id.number.valid, s->chan->caller.id.number.str, ""),
00271       S_COR(s->chan->caller.id.name.valid, s->chan->caller.id.name.str, ""),
00272       S_COR(s->chan->connected.id.number.valid, s->chan->connected.id.number.str, ""),
00273       S_COR(s->chan->connected.id.name.valid, s->chan->connected.id.name.str, ""),
00274       far_ident,
00275       local_ident,
00276       pages_transferred,
00277       stat.y_resolution,
00278       stat.bit_rate,
00279       s->file_name);
00280 }

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

Definition at line 909 of file app_fax.c.

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

Referenced by load_module().

00910 {
00911    int res = 0;
00912    char *parse;
00913    fax_session session;
00914    char restore_digit_detect = 0;
00915 
00916    AST_DECLARE_APP_ARGS(args,
00917       AST_APP_ARG(file_name);
00918       AST_APP_ARG(options);
00919    );
00920 
00921    if (chan == NULL) {
00922       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00923       return -1;
00924    }
00925 
00926    /* The next few lines of code parse out the filename and header from the input string */
00927    if (ast_strlen_zero(data)) {
00928       /* No data implies no filename or anything is present */
00929       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00930       return -1;
00931    }
00932 
00933    parse = ast_strdupa(data);
00934    AST_STANDARD_APP_ARGS(args, parse);
00935    
00936    session.caller_mode = FALSE;
00937 
00938    if (args.options) {
00939       if (strchr(args.options, 'c'))
00940          session.caller_mode = TRUE;
00941    }
00942 
00943    /* Done parsing */
00944    session.direction = 0;
00945    session.file_name = args.file_name;
00946    session.chan = chan;
00947    session.finished = 0;
00948 
00949    /* get current digit detection mode, then disable digit detection if enabled */
00950    {
00951       int dummy = sizeof(restore_digit_detect);
00952 
00953       ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
00954    }
00955 
00956    if (restore_digit_detect) {
00957       char new_digit_detect = 0;
00958 
00959       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
00960    }
00961 
00962    /* disable FAX tone detection if enabled */
00963    {
00964       char new_fax_detect = 0;
00965 
00966       ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
00967    }
00968 
00969    res = transmit(&session);
00970 
00971    if (restore_digit_detect) {
00972       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
00973    }
00974 
00975    return res;
00976 }

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

Definition at line 316 of file app_fax.c.

Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().

00317 {
00318    t30_set_ecm_capability(state, ecm);
00319    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00320 }

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

Definition at line 308 of file app_fax.c.

References fax_session::direction, and fax_session::file_name.

Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().

00309 {
00310    if (s->direction)
00311       t30_set_tx_file(state, s->file_name, -1, -1);
00312    else
00313       t30_set_rx_file(state, s->file_name, -1);
00314 }

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

Definition at line 295 of file app_fax.c.

References ast_strlen_zero(), fax_session::chan, and pbx_builtin_getvar_helper().

Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().

00296 {
00297    const char *x;
00298 
00299    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00300    if (!ast_strlen_zero(x))
00301       t30_set_tx_ident(state, x);
00302 
00303    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00304    if (!ast_strlen_zero(x))
00305       t30_set_tx_page_header_info(state, x);
00306 }

static int set_logging ( logging_state_t *  state  )  [static]

Definition at line 285 of file app_fax.c.

References option_debug, and span_message().

Referenced by spandsp_fax_new(), spandsp_fax_start(), transmit_audio(), and transmit_t38().

00286 {
00287    int level = SPAN_LOG_WARNING + option_debug;
00288 
00289    span_log_set_message_handler(state, span_message);
00290    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00291 
00292    return 0;
00293 }

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

Definition at line 840 of file app_fax.c.

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

Referenced by load_module().

00841 {
00842    int res = 0;
00843    char *parse;
00844    fax_session session = { 0, };
00845    char restore_digit_detect = 0;
00846 
00847    AST_DECLARE_APP_ARGS(args,
00848       AST_APP_ARG(file_name);
00849       AST_APP_ARG(options);
00850    );
00851 
00852    if (chan == NULL) {
00853       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00854       return -1;
00855    }
00856 
00857    /* The next few lines of code parse out the filename and header from the input string */
00858    if (ast_strlen_zero(data)) {
00859       /* No data implies no filename or anything is present */
00860       ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
00861       return -1;
00862    }
00863 
00864    parse = ast_strdupa(data);
00865    AST_STANDARD_APP_ARGS(args, parse);
00866    
00867    session.caller_mode = TRUE;
00868 
00869    if (args.options) {
00870       if (strchr(args.options, 'a'))
00871          session.caller_mode = FALSE;
00872    }
00873 
00874    /* Done parsing */
00875    session.direction = 1;
00876    session.file_name = args.file_name;
00877    session.chan = chan;
00878    session.finished = 0;
00879 
00880    /* get current digit detection mode, then disable digit detection if enabled */
00881    {
00882       int dummy = sizeof(restore_digit_detect);
00883 
00884       ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
00885    }
00886 
00887    if (restore_digit_detect) {
00888       char new_digit_detect = 0;
00889 
00890       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
00891    }
00892 
00893    /* disable FAX tone detection if enabled */
00894    {
00895       char new_fax_detect = 0;
00896 
00897       ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
00898    }
00899 
00900    res = transmit(&session);
00901 
00902    if (restore_digit_detect) {
00903       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
00904    }
00905 
00906    return res;
00907 }

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

Definition at line 169 of file app_fax.c.

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

Referenced by set_logging().

00170 {
00171    if (level == SPAN_LOG_ERROR) {
00172       ast_log(LOG_ERROR, "%s", msg);
00173    } else if (level == SPAN_LOG_WARNING) {
00174       ast_log(LOG_WARNING, "%s", msg);
00175    } else {
00176       ast_log(LOG_DEBUG, "%s", msg);
00177    }
00178 }

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 180 of file app_fax.c.

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

Referenced by spandsp_fax_new(), and transmit_t38().

00181 {
00182    struct ast_channel *chan = (struct ast_channel *) user_data;
00183 
00184    struct ast_frame outf = {
00185       .frametype = AST_FRAME_MODEM,
00186       .subclass.integer = AST_MODEM_T38,
00187       .src = __FUNCTION__,
00188    };
00189 
00190    /* TODO: Asterisk does not provide means of resending the same packet multiple
00191      times so count is ignored at the moment */
00192 
00193    AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
00194 
00195    if (ast_write(chan, &outf) < 0) {
00196       ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
00197       return -1;
00198    }
00199 
00200    return 0;
00201 }

static int transmit ( fax_session s  )  [static]

Definition at line 782 of file app_fax.c.

References ast_channel::_state, ast_answer(), ast_channel_get_t38_state(), ast_debug, ast_log(), AST_STATE_UP, fax_session::chan, fax_session::finished, LOG_ERROR, LOG_WARNING, ast_channel::name, pbx_builtin_setvar_helper(), T38_STATE_NEGOTIATED, fax_session::t38state, transmit_audio(), and transmit_t38().

Referenced by rcvfax_exec(), and sndfax_exec().

00783 {
00784    int res = 0;
00785 
00786    /* Clear all channel variables which to be set by the application.
00787       Pre-set status to error so in case of any problems we can just leave */
00788    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
00789    pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
00790 
00791    pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
00792    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
00793    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", "0");
00794    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
00795    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
00796 
00797    if (s->chan->_state != AST_STATE_UP) {
00798       /* Shouldn't need this, but checking to see if channel is already answered
00799        * Theoretically asterisk should already have answered before running the app */
00800       res = ast_answer(s->chan);
00801       if (res) {
00802          ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
00803          return res;
00804       }
00805    }
00806 
00807    s->t38state = ast_channel_get_t38_state(s->chan);
00808    if (s->t38state != T38_STATE_NEGOTIATED) {
00809       /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
00810       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
00811       res = transmit_audio(s);
00812       if (res > 0) {
00813          /* transmit_audio reports switchover to T38. Update t38state */
00814          s->t38state = ast_channel_get_t38_state(s->chan);
00815          if (s->t38state != T38_STATE_NEGOTIATED) {
00816             ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
00817          }
00818       }
00819    }
00820 
00821    if (s->t38state == T38_STATE_NEGOTIATED) {
00822       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
00823       res = transmit_t38(s);
00824    }
00825 
00826    if (res) {
00827       ast_log(LOG_WARNING, "Transmission error\n");
00828       res = -1;
00829    } else if (s->finished < 0) {
00830       ast_log(LOG_WARNING, "Transmission failed\n");
00831    } else if (s->finished > 0) {
00832       ast_debug(1, "Transmission finished Ok\n");
00833    }
00834 
00835    return res;
00836 }

static int transmit_audio ( fax_session s  )  [static]

Definition at line 369 of file app_fax.c.

References ast_activate_generator(), ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_deactivate_generator(), ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_write_format(), AST_T38_NEGOTIATED, AST_T38_RATE_14400, AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_tvdiff_sec(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame_subclass::codec, ast_frame::data, ast_frame::datalen, FALSE, fax_session::finished, ast_frame::frametype, generator, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_channel::name, phase_e_handler(), ast_frame::ptr, ast_channel::readformat, ast_control_t38_parameters::request_response, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, T38_STATE_UNAVAILABLE, fax_session::t38parameters, TRUE, ast_control_t38_parameters::version, WATCHDOG_STATE_TIMEOUT, WATCHDOG_TOTAL_TIMEOUT, and ast_channel::writeformat.

Referenced by transmit().

00370 {
00371    int res = -1;
00372    int original_read_fmt = AST_FORMAT_SLINEAR;
00373    int original_write_fmt = AST_FORMAT_SLINEAR;
00374    fax_state_t fax;
00375    t30_state_t *t30state;
00376    struct ast_frame *inf = NULL;
00377    int last_state = 0;
00378    struct timeval now, start, state_change;
00379    enum ast_t38_state t38_state;
00380    struct ast_control_t38_parameters t38_parameters = { .version = 0,
00381                           .max_ifp = 800,
00382                           .rate = AST_T38_RATE_14400,
00383                           .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
00384                           .fill_bit_removal = 1,
00385 /*
00386  * spandsp has API calls to support MMR and JBIG transcoding, but they aren't
00387  * implemented quite yet... so don't offer them to the remote endpoint
00388  *                        .transcoding_mmr = 1,
00389  *                        .transcoding_jbig = 1,
00390 */
00391    };
00392 
00393    /* if in called party mode, try to use T.38 */
00394    if (s->caller_mode == FALSE) {
00395       /* check if we are already in T.38 mode (unlikely), or if we can request
00396        * a switch... if so, request it now and wait for the result, rather
00397        * than starting an audio FAX session that will have to be cancelled
00398        */
00399       if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
00400          return 1;
00401       } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
00402             (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
00403              (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
00404          /* wait up to five seconds for negotiation to complete */
00405          unsigned int timeout = 5000;
00406          int ms;
00407          
00408          ast_debug(1, "Negotiating T.38 for receive on %s\n", s->chan->name);
00409          while (timeout > 0) {
00410             ms = ast_waitfor(s->chan, 1000);
00411             if (ms < 0) {
00412                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
00413                return -1;
00414             }
00415             if (!ms) {
00416                /* nothing happened */
00417                if (timeout > 0) {
00418                   timeout -= 1000;
00419                   continue;
00420                } else {
00421                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name);
00422                   break;
00423                }
00424             }
00425             if (!(inf = ast_read(s->chan))) {
00426                return -1;
00427             }
00428             if ((inf->frametype == AST_FRAME_CONTROL) &&
00429                 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
00430                 (inf->datalen == sizeof(t38_parameters))) {
00431                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00432                
00433                switch (parameters->request_response) {
00434                case AST_T38_NEGOTIATED:
00435                   ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name);
00436                   res = 1;
00437                   break;
00438                case AST_T38_REFUSED:
00439                   ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name);
00440                   break;
00441                default:
00442                   ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name);
00443                   break;
00444                }
00445                ast_frfree(inf);
00446                if (res == 1) {
00447                   return 1;
00448                } else {
00449                   break;
00450                }
00451             }
00452             ast_frfree(inf);
00453          }
00454       }
00455    }
00456 
00457 #if SPANDSP_RELEASE_DATE >= 20080725
00458         /* for spandsp shaphots 0.0.6 and higher */
00459         t30state = &fax.t30;
00460 #else
00461         /* for spandsp release 0.0.5 */
00462         t30state = &fax.t30_state;
00463 #endif
00464 
00465    original_read_fmt = s->chan->readformat;
00466    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00467       res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
00468       if (res < 0) {
00469          ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
00470          goto done;
00471       }
00472    }
00473 
00474    original_write_fmt = s->chan->writeformat;
00475    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00476       res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
00477       if (res < 0) {
00478          ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
00479          goto done;
00480       }
00481    }
00482 
00483    /* Initialize T30 terminal */
00484    fax_init(&fax, s->caller_mode);
00485 
00486    /* Setup logging */
00487    set_logging(&fax.logging);
00488    set_logging(&t30state->logging);
00489 
00490    /* Configure terminal */
00491    set_local_info(t30state, s);
00492    set_file(t30state, s);
00493    set_ecm(t30state, TRUE);
00494 
00495    fax_set_transmit_on_idle(&fax, TRUE);
00496 
00497    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00498 
00499    start = state_change = ast_tvnow();
00500 
00501    ast_activate_generator(s->chan, &generator, &fax);
00502 
00503    while (!s->finished) {
00504       inf = NULL;
00505 
00506       if ((res = ast_waitfor(s->chan, 25)) < 0) {
00507          ast_debug(1, "Error waiting for a frame\n");
00508          break;
00509       }
00510 
00511       /* Watchdog */
00512       now = ast_tvnow();
00513       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00514          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00515          res = -1;
00516          break;
00517       }
00518 
00519       if (!res) {
00520          /* There was timeout waiting for a frame. Loop around and wait again */
00521          continue;
00522       }
00523 
00524       /* There is a frame available. Get it */
00525       res = 0;
00526 
00527       if (!(inf = ast_read(s->chan))) {
00528          ast_debug(1, "Channel hangup\n");
00529          res = -1;
00530          break;
00531       }
00532 
00533       ast_debug(10, "frame %d/%llu, len=%d\n", inf->frametype, (unsigned long long) inf->subclass.codec, inf->datalen);
00534 
00535       /* Check the frame type. Format also must be checked because there is a chance
00536          that a frame in old format was already queued before we set channel format
00537          to slinear so it will still be received by ast_read */
00538       if (inf->frametype == AST_FRAME_VOICE && inf->subclass.codec == AST_FORMAT_SLINEAR) {
00539          if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
00540             /* I know fax_rx never returns errors. The check here is for good style only */
00541             ast_log(LOG_WARNING, "fax_rx returned error\n");
00542             res = -1;
00543             break;
00544          }
00545          if (last_state != t30state->state) {
00546             state_change = ast_tvnow();
00547             last_state = t30state->state;
00548          }
00549       } else if ((inf->frametype == AST_FRAME_CONTROL) &&
00550             (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS)) {
00551          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00552 
00553          if (parameters->request_response == AST_T38_NEGOTIATED) {
00554             /* T38 switchover completed */
00555             s->t38parameters = *parameters;
00556             ast_debug(1, "T38 negotiated, finishing audio loop\n");
00557             res = 1;
00558             break;
00559          } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
00560             t38_parameters.request_response = AST_T38_NEGOTIATED;
00561             ast_debug(1, "T38 request received, accepting\n");
00562             /* Complete T38 switchover */
00563             ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
00564             /* Do not break audio loop, wait until channel driver finally acks switchover
00565              * with AST_T38_NEGOTIATED
00566              */
00567          }
00568       }
00569 
00570       ast_frfree(inf);
00571       inf = NULL;
00572    }
00573 
00574    ast_debug(1, "Loop finished, res=%d\n", res);
00575 
00576    if (inf)
00577       ast_frfree(inf);
00578 
00579    ast_deactivate_generator(s->chan);
00580 
00581    /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
00582       by t30_terminate, display diagnostics and set status variables although no transmittion
00583       has taken place yet. */
00584    if (res > 0) {
00585       t30_set_phase_e_handler(t30state, NULL, NULL);
00586    }
00587 
00588    t30_terminate(t30state);
00589    fax_release(&fax);
00590 
00591 done:
00592    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00593       if (ast_set_write_format(s->chan, original_write_fmt) < 0)
00594          ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name);
00595    }
00596 
00597    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00598       if (ast_set_read_format(s->chan, original_read_fmt) < 0)
00599          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name);
00600    }
00601 
00602    return res;
00603 
00604 }

static int transmit_t38 ( fax_session s  )  [static]

Definition at line 606 of file app_fax.c.

References ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FRAME_CONTROL, AST_FRAME_MODEM, ast_frfree, ast_indicate_data(), ast_log(), AST_MODEM_T38, ast_read(), AST_T38_REFUSED, AST_T38_REQUEST_TERMINATE, AST_T38_TERMINATED, ast_tvdiff_sec(), ast_tvdiff_us(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame::data, ast_frame::datalen, disable_t38(), FALSE, ast_control_t38_parameters::fill_bit_removal, fax_session::finished, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_control_t38_parameters::max_ifp, ast_channel::name, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, t38, T38_STATE_NEGOTIATED, t38_tx_packet_handler(), fax_session::t38parameters, ast_control_t38_parameters::transcoding_jbig, ast_control_t38_parameters::transcoding_mmr, TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

00607 {
00608    int res = 0;
00609    t38_terminal_state_t t38;
00610    struct ast_frame *inf = NULL;
00611    int last_state = 0;
00612    struct timeval now, start, state_change, last_frame;
00613    t30_state_t *t30state;
00614    t38_core_state_t *t38state;
00615 
00616 #if SPANDSP_RELEASE_DATE >= 20080725
00617    /* for spandsp shaphots 0.0.6 and higher */
00618    t30state = &t38.t30;
00619    t38state = &t38.t38_fe.t38;
00620 #else
00621    /* for spandsp releases 0.0.5 */
00622    t30state = &t38.t30_state;
00623    t38state = &t38.t38;
00624 #endif
00625 
00626    /* Initialize terminal */
00627    memset(&t38, 0, sizeof(t38));
00628    if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
00629       ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
00630       res = -1;
00631       goto disable_t38;
00632    }
00633 
00634    t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
00635 
00636    if (s->t38parameters.fill_bit_removal) {
00637       t38_set_fill_bit_removal(t38state, TRUE);
00638    }
00639    if (s->t38parameters.transcoding_mmr) {
00640       t38_set_mmr_transcoding(t38state, TRUE);
00641    }
00642    if (s->t38parameters.transcoding_jbig) {
00643       t38_set_jbig_transcoding(t38state, TRUE);
00644    }
00645 
00646    /* Setup logging */
00647    set_logging(&t38.logging);
00648    set_logging(&t30state->logging);
00649    set_logging(&t38state->logging);
00650 
00651    /* Configure terminal */
00652    set_local_info(t30state, s);
00653    set_file(t30state, s);
00654    set_ecm(t30state, TRUE);
00655 
00656    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00657 
00658    now = start = state_change = ast_tvnow();
00659 
00660    while (!s->finished) {
00661       inf = NULL;
00662 
00663       if ((res = ast_waitfor(s->chan, 25)) < 0) {
00664          ast_debug(1, "Error waiting for a frame\n");
00665          break;
00666       }
00667 
00668       last_frame = now;
00669 
00670       /* Watchdog */
00671       now = ast_tvnow();
00672       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00673          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00674          res = -1;
00675          break;
00676       }
00677       
00678       t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00679 
00680       if (!res) {
00681          /* There was timeout waiting for a frame. Loop around and wait again */
00682          continue;
00683       }
00684 
00685       /* There is a frame available. Get it */
00686       res = 0;
00687 
00688       if (!(inf = ast_read(s->chan))) {
00689          ast_debug(1, "Channel hangup\n");
00690          res = -1;
00691          break;
00692       }
00693 
00694       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass.integer, inf->datalen);
00695 
00696       if (inf->frametype == AST_FRAME_MODEM && inf->subclass.integer == AST_MODEM_T38) {
00697          t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
00698          if (last_state != t30state->state) {
00699             state_change = ast_tvnow();
00700             last_state = t30state->state;
00701          }
00702       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
00703          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00704          if (parameters->request_response == AST_T38_TERMINATED) {
00705             ast_debug(1, "T38 down, finishing\n");
00706             break;
00707          }
00708       }
00709 
00710       ast_frfree(inf);
00711       inf = NULL;
00712    }
00713 
00714    ast_debug(1, "Loop finished, res=%d\n", res);
00715 
00716    if (inf)
00717       ast_frfree(inf);
00718 
00719    t30_terminate(t30state);
00720    t38_terminal_release(&t38);
00721 
00722 disable_t38:
00723    /* if we are not the caller, it's our job to shut down the T.38
00724     * session when the FAX transmisson is complete.
00725     */
00726    if ((s->caller_mode == FALSE) &&
00727        (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) {
00728       struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
00729 
00730       if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) {
00731          /* wait up to five seconds for negotiation to complete */
00732          unsigned int timeout = 5000;
00733          int ms;
00734          
00735          ast_debug(1, "Shutting down T.38 on %s\n", s->chan->name);
00736          while (timeout > 0) {
00737             ms = ast_waitfor(s->chan, 1000);
00738             if (ms < 0) {
00739                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
00740                return -1;
00741             }
00742             if (!ms) {
00743                /* nothing happened */
00744                if (timeout > 0) {
00745                   timeout -= 1000;
00746                   continue;
00747                } else {
00748                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name);
00749                   break;
00750                }
00751             }
00752             if (!(inf = ast_read(s->chan))) {
00753                return -1;
00754             }
00755             if ((inf->frametype == AST_FRAME_CONTROL) &&
00756                 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
00757                 (inf->datalen == sizeof(t38_parameters))) {
00758                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00759                
00760                switch (parameters->request_response) {
00761                case AST_T38_TERMINATED:
00762                   ast_debug(1, "Shut down T.38 on %s\n", s->chan->name);
00763                   break;
00764                case AST_T38_REFUSED:
00765                   ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name);
00766                   break;
00767                default:
00768                   ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name);
00769                   break;
00770                }
00771                ast_frfree(inf);
00772                break;
00773             }
00774             ast_frfree(inf);
00775          }
00776       }
00777    }
00778 
00779    return res;
00780 }

static int unload_module ( void   )  [static]

Definition at line 978 of file app_fax.c.

References ast_unregister_application().

00979 {
00980    int res;
00981 
00982    res = ast_unregister_application(app_sndfax_name); 
00983    res |= ast_unregister_application(app_rcvfax_name);   
00984 
00985    return res;
00986 }


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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, } [static]

Definition at line 1005 of file app_fax.c.

const char app_rcvfax_name[] = "ReceiveFAX" [static]

Definition at line 146 of file app_fax.c.

const char app_sndfax_name[] = "SendFAX" [static]

Definition at line 145 of file app_fax.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1005 of file app_fax.c.

struct ast_generator generator [static]

Initial value:

Definition at line 361 of file app_fax.c.

Referenced by cli_alias_passthrough(), and transmit_audio().


Generated on Sat Mar 10 01:54:32 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7