Wed Apr 6 11:29:49 2011

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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 147 of file app_fax.c.

Referenced by fax_generator_generate().

#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES

Definition at line 33 of file app_fax.c.

#define WATCHDOG_STATE_TIMEOUT   5 * 60

Definition at line 156 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 155 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 998 of file app_fax.c.

static void __unreg_module ( void   )  [static]

Definition at line 998 of file app_fax.c.

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

Definition at line 319 of file app_fax.c.

00320 {
00321    return params;
00322 }

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

Definition at line 324 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.

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

static int load_module ( void   )  [static]

Definition at line 981 of file app_fax.c.

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

00982 {
00983    int res ;
00984 
00985    res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
00986    res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
00987 
00988    /* The default SPAN message handler prints to stderr. It is something we do not want */
00989    span_set_message_handler(NULL);
00990 
00991    return res;
00992 }

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

Definition at line 202 of file app_fax.c.

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

Referenced by transmit_audio(), and transmit_t38().

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

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

Definition at line 902 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().

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

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

Definition at line 309 of file app_fax.c.

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

00310 {
00311    t30_set_ecm_capability(state, ecm);
00312    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00313 }

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

Definition at line 301 of file app_fax.c.

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

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

00302 {
00303    if (s->direction)
00304       t30_set_tx_file(state, s->file_name, -1, -1);
00305    else
00306       t30_set_rx_file(state, s->file_name, -1);
00307 }

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

Definition at line 288 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().

00289 {
00290    const char *x;
00291 
00292    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00293    if (!ast_strlen_zero(x))
00294       t30_set_tx_ident(state, x);
00295 
00296    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00297    if (!ast_strlen_zero(x))
00298       t30_set_tx_page_header_info(state, x);
00299 }

static int set_logging ( logging_state_t *  state  )  [static]

Definition at line 278 of file app_fax.c.

References option_debug, and span_message().

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

00279 {
00280    int level = SPAN_LOG_WARNING + option_debug;
00281 
00282    span_log_set_message_handler(state, span_message);
00283    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00284 
00285    return 0;
00286 }

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

Definition at line 833 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().

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

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

Definition at line 168 of file app_fax.c.

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

Referenced by set_logging().

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

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 179 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().

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

static int transmit ( fax_session s  )  [static]

Definition at line 775 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().

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

static int transmit_audio ( fax_session s  )  [static]

Definition at line 362 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().

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

static int transmit_t38 ( fax_session s  )  [static]

Definition at line 599 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().

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

static int unload_module ( void   )  [static]

Definition at line 971 of file app_fax.c.

References ast_unregister_application().

00972 {
00973    int res;
00974 
00975    res = ast_unregister_application(app_sndfax_name); 
00976    res |= ast_unregister_application(app_rcvfax_name);   
00977 
00978    return res;
00979 }


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

Definition at line 998 of file app_fax.c.

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

Definition at line 145 of file app_fax.c.

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

Definition at line 144 of file app_fax.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 998 of file app_fax.c.

struct ast_generator generator [static]

Initial value:

Definition at line 354 of file app_fax.c.

Referenced by cli_alias_passthrough(), and transmit_audio().


Generated on Wed Apr 6 11:29:49 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7