#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 = "a9c98e5d177805051735cb5b0b16b0a0" , .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 struct ast_module_info * | ast_module_info = &__mod_info |
ast_generator | generator |
#define MAX_SAMPLES 240 |
#define WATCHDOG_STATE_TIMEOUT 5 * 60 |
#define WATCHDOG_TOTAL_TIMEOUT 30 * 60 |
static void* fax_generator_alloc | ( | struct ast_channel * | chan, | |
void * | params | |||
) | [static] |
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 880 of file app_fax.c.
References ast_register_application, rcvfax_exec(), and sndfax_exec().
00881 { 00882 int res ; 00883 00884 res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc); 00885 res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc); 00886 00887 /* The default SPAN message handler prints to stderr. It is something we do not want */ 00888 span_set_message_handler(NULL); 00889 00890 return res; 00891 }
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 826 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().
00827 { 00828 int res = 0; 00829 char *parse; 00830 fax_session session; 00831 00832 AST_DECLARE_APP_ARGS(args, 00833 AST_APP_ARG(file_name); 00834 AST_APP_ARG(options); 00835 ); 00836 00837 if (chan == NULL) { 00838 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00839 return -1; 00840 } 00841 00842 /* The next few lines of code parse out the filename and header from the input string */ 00843 if (ast_strlen_zero(data)) { 00844 /* No data implies no filename or anything is present */ 00845 ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n"); 00846 return -1; 00847 } 00848 00849 parse = ast_strdupa(data); 00850 AST_STANDARD_APP_ARGS(args, parse); 00851 00852 session.caller_mode = FALSE; 00853 00854 if (args.options) { 00855 if (strchr(args.options, 'c')) 00856 session.caller_mode = TRUE; 00857 } 00858 00859 /* Done parsing */ 00860 session.direction = 0; 00861 session.file_name = args.file_name; 00862 session.chan = chan; 00863 session.finished = 0; 00864 00865 res = transmit(&session); 00866 00867 return res; 00868 }
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 782 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().
00783 { 00784 int res = 0; 00785 char *parse; 00786 fax_session session = { 0, }; 00787 00788 AST_DECLARE_APP_ARGS(args, 00789 AST_APP_ARG(file_name); 00790 AST_APP_ARG(options); 00791 ); 00792 00793 if (chan == NULL) { 00794 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00795 return -1; 00796 } 00797 00798 /* The next few lines of code parse out the filename and header from the input string */ 00799 if (ast_strlen_zero(data)) { 00800 /* No data implies no filename or anything is present */ 00801 ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n"); 00802 return -1; 00803 } 00804 00805 parse = ast_strdupa(data); 00806 AST_STANDARD_APP_ARGS(args, parse); 00807 00808 session.caller_mode = TRUE; 00809 00810 if (args.options) { 00811 if (strchr(args.options, 'a')) 00812 session.caller_mode = FALSE; 00813 } 00814 00815 /* Done parsing */ 00816 session.direction = 1; 00817 session.file_name = args.file_name; 00818 session.chan = chan; 00819 session.finished = 0; 00820 00821 res = transmit(&session); 00822 00823 return res; 00824 }
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 724 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().
00725 { 00726 int res = 0; 00727 00728 /* Clear all channel variables which to be set by the application. 00729 Pre-set status to error so in case of any problems we can just leave */ 00730 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 00731 pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 00732 00733 pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL); 00734 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL); 00735 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL); 00736 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL); 00737 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 00738 00739 if (s->chan->_state != AST_STATE_UP) { 00740 /* Shouldn't need this, but checking to see if channel is already answered 00741 * Theoretically asterisk should already have answered before running the app */ 00742 res = ast_answer(s->chan); 00743 if (res) { 00744 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name); 00745 return res; 00746 } 00747 } 00748 00749 s->t38state = ast_channel_get_t38_state(s->chan); 00750 if (s->t38state != T38_STATE_NEGOTIATED) { 00751 /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */ 00752 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 00753 res = transmit_audio(s); 00754 if (res > 0) { 00755 /* transmit_audio reports switchover to T38. Update t38state */ 00756 s->t38state = ast_channel_get_t38_state(s->chan); 00757 if (s->t38state != T38_STATE_NEGOTIATED) { 00758 ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n"); 00759 } 00760 } 00761 } 00762 00763 if (s->t38state == T38_STATE_NEGOTIATED) { 00764 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 00765 res = transmit_t38(s); 00766 } 00767 00768 if (res) { 00769 ast_log(LOG_WARNING, "Transmission error\n"); 00770 res = -1; 00771 } else if (s->finished < 0) { 00772 ast_log(LOG_WARNING, "Transmission failed\n"); 00773 } else if (s->finished > 0) { 00774 ast_debug(1, "Transmission finished Ok\n"); 00775 } 00776 00777 return res; 00778 }
static int transmit_audio | ( | fax_session * | s | ) | [static] |
Definition at line 317 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(), ast_frame::data, ast_frame::datalen, FALSE, ast_frame::frametype, generator, LOG_ERROR, LOG_WARNING, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, s, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, T38_STATE_UNAVAILABLE, TRUE, ast_control_t38_parameters::version, 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_frame *inf = NULL; 00325 int last_state = 0; 00326 struct timeval now, start, state_change; 00327 enum ast_t38_state t38_state; 00328 struct ast_control_t38_parameters t38_parameters = { .version = 0, 00329 .max_ifp = 800, 00330 .rate = AST_T38_RATE_14400, 00331 .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, 00332 .fill_bit_removal = 1, 00333 /* 00334 * spandsp has API calls to support MMR and JBIG transcoding, but they aren't 00335 * implemented quite yet... so don't offer them to the remote endpoint 00336 * .transcoding_mmr = 1, 00337 * .transcoding_jbig = 1, 00338 */ 00339 }; 00340 00341 /* if in called party mode, try to use T.38 */ 00342 if (s->caller_mode == FALSE) { 00343 /* check if we are already in T.38 mode (unlikely), or if we can request 00344 * a switch... if so, request it now and wait for the result, rather 00345 * than starting an audio FAX session that will have to be cancelled 00346 */ 00347 if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) { 00348 return 1; 00349 } else if ((t38_state != T38_STATE_UNAVAILABLE) && 00350 (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE, 00351 (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) { 00352 /* wait up to five seconds for negotiation to complete */ 00353 unsigned int timeout = 5000; 00354 int ms; 00355 00356 ast_debug(1, "Negotiating T.38 for receive on %s\n", s->chan->name); 00357 while (timeout > 0) { 00358 ms = ast_waitfor(s->chan, 1000); 00359 if (ms < 0) { 00360 ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name); 00361 return -1; 00362 } 00363 if (!ms) { 00364 /* nothing happened */ 00365 if (timeout > 0) { 00366 timeout -= 1000; 00367 continue; 00368 } else { 00369 ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name); 00370 break; 00371 } 00372 } 00373 if (!(inf = ast_read(s->chan))) { 00374 return -1; 00375 } 00376 if ((inf->frametype == AST_FRAME_CONTROL) && 00377 (inf->subclass == AST_CONTROL_T38_PARAMETERS) && 00378 (inf->datalen == sizeof(t38_parameters))) { 00379 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00380 00381 switch (parameters->request_response) { 00382 case AST_T38_NEGOTIATED: 00383 ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name); 00384 res = 1; 00385 break; 00386 case AST_T38_REFUSED: 00387 ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name); 00388 break; 00389 default: 00390 ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name); 00391 break; 00392 } 00393 ast_frfree(inf); 00394 if (res == 1) { 00395 return 1; 00396 } else { 00397 break; 00398 } 00399 } 00400 ast_frfree(inf); 00401 } 00402 } 00403 } 00404 00405 #if SPANDSP_RELEASE_DATE >= 20080725 00406 /* for spandsp shaphots 0.0.6 and higher */ 00407 t30state = &fax.t30; 00408 #else 00409 /* for spandsp release 0.0.5 */ 00410 t30state = &fax.t30_state; 00411 #endif 00412 00413 original_read_fmt = s->chan->readformat; 00414 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00415 res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR); 00416 if (res < 0) { 00417 ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); 00418 goto done; 00419 } 00420 } 00421 00422 original_write_fmt = s->chan->writeformat; 00423 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00424 res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR); 00425 if (res < 0) { 00426 ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); 00427 goto done; 00428 } 00429 } 00430 00431 /* Initialize T30 terminal */ 00432 fax_init(&fax, s->caller_mode); 00433 00434 /* Setup logging */ 00435 set_logging(&fax.logging); 00436 set_logging(&t30state->logging); 00437 00438 /* Configure terminal */ 00439 set_local_info(t30state, s); 00440 set_file(t30state, s); 00441 set_ecm(t30state, TRUE); 00442 00443 fax_set_transmit_on_idle(&fax, TRUE); 00444 00445 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00446 00447 start = state_change = ast_tvnow(); 00448 00449 ast_activate_generator(s->chan, &generator, &fax); 00450 00451 while (!s->finished) { 00452 inf = NULL; 00453 00454 if ((res = ast_waitfor(s->chan, 25)) < 0) { 00455 ast_debug(1, "Error waiting for a frame\n"); 00456 break; 00457 } 00458 00459 /* Watchdog */ 00460 now = ast_tvnow(); 00461 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00462 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00463 res = -1; 00464 break; 00465 } 00466 00467 if (!res) { 00468 /* There was timeout waiting for a frame. Loop around and wait again */ 00469 continue; 00470 } 00471 00472 /* There is a frame available. Get it */ 00473 res = 0; 00474 00475 if (!(inf = ast_read(s->chan))) { 00476 ast_debug(1, "Channel hangup\n"); 00477 res = -1; 00478 break; 00479 } 00480 00481 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen); 00482 00483 /* Check the frame type. Format also must be checked because there is a chance 00484 that a frame in old format was already queued before we set channel format 00485 to slinear so it will still be received by ast_read */ 00486 if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) { 00487 if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) { 00488 /* I know fax_rx never returns errors. The check here is for good style only */ 00489 ast_log(LOG_WARNING, "fax_rx returned error\n"); 00490 res = -1; 00491 break; 00492 } 00493 if (last_state != t30state->state) { 00494 state_change = ast_tvnow(); 00495 last_state = t30state->state; 00496 } 00497 } else if ((inf->frametype == AST_FRAME_CONTROL) && 00498 (inf->subclass == AST_CONTROL_T38_PARAMETERS)) { 00499 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00500 00501 if (parameters->request_response == AST_T38_NEGOTIATED) { 00502 /* T38 switchover completed */ 00503 s->t38parameters = *parameters; 00504 ast_debug(1, "T38 negotiated, finishing audio loop\n"); 00505 res = 1; 00506 break; 00507 } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) { 00508 t38_parameters.request_response = AST_T38_NEGOTIATED; 00509 ast_debug(1, "T38 request received, accepting\n"); 00510 /* Complete T38 switchover */ 00511 ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)); 00512 /* Do not break audio loop, wait until channel driver finally acks switchover 00513 * with AST_T38_NEGOTIATED 00514 */ 00515 } 00516 } 00517 00518 ast_frfree(inf); 00519 inf = NULL; 00520 } 00521 00522 ast_debug(1, "Loop finished, res=%d\n", res); 00523 00524 if (inf) 00525 ast_frfree(inf); 00526 00527 ast_deactivate_generator(s->chan); 00528 00529 /* If we are switching to T38, remove phase E handler. Otherwise it will be executed 00530 by t30_terminate, display diagnostics and set status variables although no transmittion 00531 has taken place yet. */ 00532 if (res > 0) { 00533 t30_set_phase_e_handler(t30state, NULL, NULL); 00534 } 00535 00536 t30_terminate(t30state); 00537 fax_release(&fax); 00538 00539 done: 00540 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00541 if (ast_set_write_format(s->chan, original_write_fmt) < 0) 00542 ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name); 00543 } 00544 00545 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00546 if (ast_set_read_format(s->chan, original_read_fmt) < 0) 00547 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name); 00548 } 00549 00550 return res; 00551 00552 }
static int transmit_t38 | ( | fax_session * | s | ) | [static] |
Definition at line 554 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(), ast_frame::data, ast_frame::datalen, FALSE, ast_frame::frametype, LOG_ERROR, LOG_WARNING, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, s, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, t38_tx_packet_handler(), TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.
Referenced by transmit().
00555 { 00556 int res = 0; 00557 t38_terminal_state_t t38; 00558 struct ast_frame *inf = NULL; 00559 int last_state = 0; 00560 struct timeval now, start, state_change, last_frame; 00561 t30_state_t *t30state; 00562 t38_core_state_t *t38state; 00563 00564 #if SPANDSP_RELEASE_DATE >= 20080725 00565 /* for spandsp shaphots 0.0.6 and higher */ 00566 t30state = &t38.t30; 00567 t38state = &t38.t38_fe.t38; 00568 #else 00569 /* for spandsp releases 0.0.5 */ 00570 t30state = &t38.t30_state; 00571 t38state = &t38.t38; 00572 #endif 00573 00574 /* Initialize terminal */ 00575 memset(&t38, 0, sizeof(t38)); 00576 if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) { 00577 ast_log(LOG_WARNING, "Unable to start T.38 termination.\n"); 00578 res = -1; 00579 goto disable_t38; 00580 } 00581 00582 t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp); 00583 00584 if (s->t38parameters.fill_bit_removal) { 00585 t38_set_fill_bit_removal(t38state, TRUE); 00586 } 00587 if (s->t38parameters.transcoding_mmr) { 00588 t38_set_mmr_transcoding(t38state, TRUE); 00589 } 00590 if (s->t38parameters.transcoding_jbig) { 00591 t38_set_jbig_transcoding(t38state, TRUE); 00592 } 00593 00594 /* Setup logging */ 00595 set_logging(&t38.logging); 00596 set_logging(&t30state->logging); 00597 set_logging(&t38state->logging); 00598 00599 /* Configure terminal */ 00600 set_local_info(t30state, s); 00601 set_file(t30state, s); 00602 set_ecm(t30state, TRUE); 00603 00604 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00605 00606 now = start = state_change = ast_tvnow(); 00607 00608 while (!s->finished) { 00609 00610 res = ast_waitfor(s->chan, 20); 00611 if (res < 0) 00612 break; 00613 else if (res > 0) 00614 res = 0; 00615 00616 last_frame = now; 00617 now = ast_tvnow(); 00618 t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000)); 00619 00620 inf = ast_read(s->chan); 00621 if (inf == NULL) { 00622 ast_debug(1, "Channel hangup\n"); 00623 res = -1; 00624 break; 00625 } 00626 00627 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen); 00628 00629 if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) { 00630 t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno); 00631 00632 /* Watchdog */ 00633 if (last_state != t30state->state) { 00634 state_change = ast_tvnow(); 00635 last_state = t30state->state; 00636 } 00637 } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) { 00638 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00639 if (parameters->request_response == AST_T38_TERMINATED) { 00640 ast_debug(1, "T38 down, finishing\n"); 00641 break; 00642 } 00643 } 00644 00645 ast_frfree(inf); 00646 inf = NULL; 00647 00648 /* Watchdog */ 00649 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00650 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00651 res = -1; 00652 break; 00653 } 00654 } 00655 00656 ast_debug(1, "Loop finished, res=%d\n", res); 00657 00658 if (inf) 00659 ast_frfree(inf); 00660 00661 t30_terminate(t30state); 00662 t38_terminal_release(&t38); 00663 00664 disable_t38: 00665 /* if we are not the caller, it's our job to shut down the T.38 00666 * session when the FAX transmisson is complete. 00667 */ 00668 if ((s->caller_mode == FALSE) && 00669 (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) { 00670 struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, }; 00671 00672 if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) { 00673 /* wait up to five seconds for negotiation to complete */ 00674 unsigned int timeout = 5000; 00675 int ms; 00676 00677 ast_debug(1, "Shutting down T.38 on %s\n", s->chan->name); 00678 while (timeout > 0) { 00679 ms = ast_waitfor(s->chan, 1000); 00680 if (ms < 0) { 00681 ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name); 00682 return -1; 00683 } 00684 if (!ms) { 00685 /* nothing happened */ 00686 if (timeout > 0) { 00687 timeout -= 1000; 00688 continue; 00689 } else { 00690 ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name); 00691 break; 00692 } 00693 } 00694 if (!(inf = ast_read(s->chan))) { 00695 return -1; 00696 } 00697 if ((inf->frametype == AST_FRAME_CONTROL) && 00698 (inf->subclass == AST_CONTROL_T38_PARAMETERS) && 00699 (inf->datalen == sizeof(t38_parameters))) { 00700 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00701 00702 switch (parameters->request_response) { 00703 case AST_T38_TERMINATED: 00704 ast_debug(1, "Shut down T.38 on %s\n", s->chan->name); 00705 break; 00706 case AST_T38_REFUSED: 00707 ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name); 00708 break; 00709 default: 00710 ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name); 00711 break; 00712 } 00713 ast_frfree(inf); 00714 break; 00715 } 00716 ast_frfree(inf); 00717 } 00718 } 00719 } 00720 00721 return res; 00722 }
static int unload_module | ( | void | ) | [static] |
Definition at line 870 of file app_fax.c.
References ast_unregister_application().
00871 { 00872 int res; 00873 00874 res = ast_unregister_application(app_sndfax_name); 00875 res |= ast_unregister_application(app_rcvfax_name); 00876 00877 return res; 00878 }
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 = "a9c98e5d177805051735cb5b0b16b0a0" , .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] |
struct ast_module_info* ast_module_info = &__mod_info [static] |
struct ast_generator generator |
Initial value:
{ alloc: fax_generator_alloc, generate: fax_generator_generate, }
Definition at line 309 of file app_fax.c.
Referenced by transmit_audio().