#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 761 of file app_fax.c.
References ast_register_application, rcvfax_exec(), and sndfax_exec().
00762 { 00763 int res ; 00764 00765 res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc); 00766 res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc); 00767 00768 /* The default SPAN message handler prints to stderr. It is something we do not want */ 00769 span_set_message_handler(NULL); 00770 00771 return res; 00772 }
static void phase_e_handler | ( | t30_state_t * | f, | |
void * | user_data, | |||
int | result | |||
) | [static] |
Definition at line 157 of file app_fax.c.
References ast_debug, ast_log(), buf, EVENT_FLAG_CALL, LOG_WARNING, manager_event, pbx_builtin_setvar_helper(), s, and S_OR.
Referenced by transmit_audio(), and transmit_t38().
00158 { 00159 const char *local_ident; 00160 const char *far_ident; 00161 char buf[20]; 00162 fax_session *s = (fax_session *) user_data; 00163 t30_stats_t stat; 00164 int pages_transferred; 00165 00166 ast_debug(1, "Fax phase E handler. result=%d\n", result); 00167 00168 t30_get_transfer_statistics(f, &stat); 00169 00170 s = (fax_session *) user_data; 00171 00172 if (result != T30_ERR_OK) { 00173 s->finished = -1; 00174 00175 /* FAXSTATUS is already set to FAILED */ 00176 pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result)); 00177 00178 ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result)); 00179 00180 return; 00181 } 00182 00183 s->finished = 1; 00184 00185 local_ident = t30_get_tx_ident(f); 00186 far_ident = t30_get_rx_ident(f); 00187 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 00188 pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 00189 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident); 00190 #if SPANDSP_RELEASE_DATE >= 20090220 00191 pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx; 00192 #else 00193 pages_transferred = stat.pages_transferred; 00194 #endif 00195 snprintf(buf, sizeof(buf), "%d", pages_transferred); 00196 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf); 00197 snprintf(buf, sizeof(buf), "%d", stat.y_resolution); 00198 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf); 00199 snprintf(buf, sizeof(buf), "%d", stat.bit_rate); 00200 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 00201 00202 ast_debug(1, "Fax transmitted successfully.\n"); 00203 ast_debug(1, " Remote station ID: %s\n", far_ident); 00204 ast_debug(1, " Pages transferred: %d\n", pages_transferred); 00205 ast_debug(1, " Image resolution: %d x %d\n", stat.x_resolution, stat.y_resolution); 00206 ast_debug(1, " Transfer Rate: %d\n", stat.bit_rate); 00207 00208 manager_event(EVENT_FLAG_CALL, 00209 s->direction ? "FaxSent" : "FaxReceived", 00210 "Channel: %s\r\n" 00211 "Exten: %s\r\n" 00212 "CallerID: %s\r\n" 00213 "RemoteStationID: %s\r\n" 00214 "LocalStationID: %s\r\n" 00215 "PagesTransferred: %d\r\n" 00216 "Resolution: %d\r\n" 00217 "TransferRate: %d\r\n" 00218 "FileName: %s\r\n", 00219 s->chan->name, 00220 s->chan->exten, 00221 S_OR(s->chan->cid.cid_num, ""), 00222 far_ident, 00223 local_ident, 00224 pages_transferred, 00225 stat.y_resolution, 00226 stat.bit_rate, 00227 s->file_name); 00228 }
static int rcvfax_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 707 of file app_fax.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.
Referenced by load_module().
00708 { 00709 int res = 0; 00710 char *parse; 00711 fax_session session; 00712 00713 AST_DECLARE_APP_ARGS(args, 00714 AST_APP_ARG(file_name); 00715 AST_APP_ARG(options); 00716 ); 00717 00718 if (chan == NULL) { 00719 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00720 return -1; 00721 } 00722 00723 /* The next few lines of code parse out the filename and header from the input string */ 00724 if (ast_strlen_zero(data)) { 00725 /* No data implies no filename or anything is present */ 00726 ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n"); 00727 return -1; 00728 } 00729 00730 parse = ast_strdupa(data); 00731 AST_STANDARD_APP_ARGS(args, parse); 00732 00733 session.caller_mode = FALSE; 00734 00735 if (args.options) { 00736 if (strchr(args.options, 'c')) 00737 session.caller_mode = TRUE; 00738 } 00739 00740 /* Done parsing */ 00741 session.direction = 0; 00742 session.file_name = args.file_name; 00743 session.chan = chan; 00744 session.finished = 0; 00745 00746 res = transmit(&session); 00747 00748 return res; 00749 }
static void set_ecm | ( | t30_state_t * | state, | |
int | ecm | |||
) | [static] |
Definition at line 264 of file app_fax.c.
Referenced by transmit_audio(), and transmit_t38().
00265 { 00266 t30_set_ecm_capability(state, ecm); 00267 t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); 00268 }
static void set_file | ( | t30_state_t * | state, | |
fax_session * | s | |||
) | [static] |
Definition at line 256 of file app_fax.c.
References s.
Referenced by transmit_audio(), and transmit_t38().
00257 { 00258 if (s->direction) 00259 t30_set_tx_file(state, s->file_name, -1, -1); 00260 else 00261 t30_set_rx_file(state, s->file_name, -1); 00262 }
static void set_local_info | ( | t30_state_t * | state, | |
fax_session * | s | |||
) | [static] |
Definition at line 243 of file app_fax.c.
References ast_strlen_zero(), pbx_builtin_getvar_helper(), and s.
Referenced by transmit_audio(), and transmit_t38().
00244 { 00245 const char *x; 00246 00247 x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID"); 00248 if (!ast_strlen_zero(x)) 00249 t30_set_tx_ident(state, x); 00250 00251 x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO"); 00252 if (!ast_strlen_zero(x)) 00253 t30_set_tx_page_header_info(state, x); 00254 }
static int set_logging | ( | logging_state_t * | state | ) | [static] |
Definition at line 233 of file app_fax.c.
References option_debug, and span_message().
Referenced by transmit_audio(), and transmit_t38().
00234 { 00235 int level = SPAN_LOG_WARNING + option_debug; 00236 00237 span_log_set_message_handler(state, span_message); 00238 span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 00239 00240 return 0; 00241 }
static int sndfax_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 663 of file app_fax.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.
Referenced by load_module().
00664 { 00665 int res = 0; 00666 char *parse; 00667 fax_session session; 00668 00669 AST_DECLARE_APP_ARGS(args, 00670 AST_APP_ARG(file_name); 00671 AST_APP_ARG(options); 00672 ); 00673 00674 if (chan == NULL) { 00675 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00676 return -1; 00677 } 00678 00679 /* The next few lines of code parse out the filename and header from the input string */ 00680 if (ast_strlen_zero(data)) { 00681 /* No data implies no filename or anything is present */ 00682 ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n"); 00683 return -1; 00684 } 00685 00686 parse = ast_strdupa(data); 00687 AST_STANDARD_APP_ARGS(args, parse); 00688 00689 session.caller_mode = TRUE; 00690 00691 if (args.options) { 00692 if (strchr(args.options, 'a')) 00693 session.caller_mode = FALSE; 00694 } 00695 00696 /* Done parsing */ 00697 session.direction = 1; 00698 session.file_name = args.file_name; 00699 session.chan = chan; 00700 session.finished = 0; 00701 00702 res = transmit(&session); 00703 00704 return res; 00705 }
static void span_message | ( | int | level, | |
const char * | msg | |||
) | [static] |
Definition at line 123 of file app_fax.c.
References ast_log(), LOG_DEBUG, LOG_ERROR, and LOG_WARNING.
Referenced by set_logging().
00124 { 00125 if (level == SPAN_LOG_ERROR) { 00126 ast_log(LOG_ERROR, "%s", msg); 00127 } else if (level == SPAN_LOG_WARNING) { 00128 ast_log(LOG_WARNING, "%s", msg); 00129 } else { 00130 ast_log(LOG_DEBUG, "%s", msg); 00131 } 00132 }
static int t38_tx_packet_handler | ( | t38_core_state_t * | s, | |
void * | user_data, | |||
const uint8_t * | buf, | |||
int | len, | |||
int | count | |||
) | [static] |
Definition at line 134 of file app_fax.c.
References AST_FRAME_MODEM, AST_FRAME_SET_BUFFER, ast_log(), AST_MODEM_T38, ast_write(), chan, errno, ast_frame::frametype, and LOG_WARNING.
Referenced by transmit_t38().
00135 { 00136 struct ast_channel *chan = (struct ast_channel *) user_data; 00137 00138 struct ast_frame outf = { 00139 .frametype = AST_FRAME_MODEM, 00140 .subclass = AST_MODEM_T38, 00141 .src = __FUNCTION__, 00142 }; 00143 00144 /* TODO: Asterisk does not provide means of resending the same packet multiple 00145 times so count is ignored at the moment */ 00146 00147 AST_FRAME_SET_BUFFER(&outf, buf, 0, len); 00148 00149 if (ast_write(chan, &outf) < 0) { 00150 ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); 00151 return -1; 00152 } 00153 00154 return 0; 00155 }
static int transmit | ( | fax_session * | s | ) | [static] |
Definition at line 605 of file app_fax.c.
References ast_answer(), ast_channel_get_t38_state(), ast_debug, ast_log(), AST_STATE_UP, LOG_ERROR, LOG_WARNING, pbx_builtin_setvar_helper(), s, T38_STATE_NEGOTIATED, transmit_audio(), and transmit_t38().
Referenced by rcvfax_exec(), and sndfax_exec().
00606 { 00607 int res = 0; 00608 00609 /* Clear all channel variables which to be set by the application. 00610 Pre-set status to error so in case of any problems we can just leave */ 00611 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 00612 pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 00613 00614 pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL); 00615 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL); 00616 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL); 00617 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL); 00618 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 00619 00620 if (s->chan->_state != AST_STATE_UP) { 00621 /* Shouldn't need this, but checking to see if channel is already answered 00622 * Theoretically asterisk should already have answered before running the app */ 00623 res = ast_answer(s->chan); 00624 if (res) { 00625 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name); 00626 return res; 00627 } 00628 } 00629 00630 s->t38state = ast_channel_get_t38_state(s->chan); 00631 if (s->t38state != T38_STATE_NEGOTIATED) { 00632 /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */ 00633 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 00634 res = transmit_audio(s); 00635 if (res > 0) { 00636 /* transmit_audio reports switchover to T38. Update t38state */ 00637 s->t38state = ast_channel_get_t38_state(s->chan); 00638 if (s->t38state != T38_STATE_NEGOTIATED) { 00639 ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n"); 00640 } 00641 } 00642 } 00643 00644 if (s->t38state == T38_STATE_NEGOTIATED) { 00645 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 00646 res = transmit_t38(s); 00647 } 00648 00649 if (res) { 00650 ast_log(LOG_WARNING, "Transmission error\n"); 00651 res = -1; 00652 } else if (s->finished < 0) { 00653 ast_log(LOG_WARNING, "Transmission failed\n"); 00654 } else if (s->finished > 0) { 00655 ast_debug(1, "Transmission finished Ok\n"); 00656 } 00657 00658 return res; 00659 }
static int transmit_audio | ( | fax_session * | s | ) | [static] |
Definition at line 317 of file app_fax.c.
References ast_activate_generator(), AST_CONTROL_T38, ast_deactivate_generator(), ast_debug, ast_dsp_free(), ast_dsp_new(), ast_dsp_process(), ast_dsp_set_faxmode(), ast_dsp_set_features(), AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frdup(), ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_write_format(), AST_T38_NEGOTIATED, AST_T38_REQUEST_NEGOTIATE, ast_tvdiff_sec(), ast_tvnow(), ast_waitfor(), ast_frame::data, ast_frame::datalen, DSP_FAXMODE_DETECT_CNG, DSP_FEATURE_FAX_DETECT, ast_frame::frametype, generator, LOG_WARNING, phase_e_handler(), ast_frame::ptr, s, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_UNAVAILABLE, TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.
Referenced by transmit().
00318 { 00319 int res = -1; 00320 int original_read_fmt = AST_FORMAT_SLINEAR; 00321 int original_write_fmt = AST_FORMAT_SLINEAR; 00322 fax_state_t fax; 00323 t30_state_t *t30state; 00324 struct ast_dsp *dsp = NULL; 00325 int detect_tone = 0; 00326 struct ast_frame *inf = NULL; 00327 struct ast_frame *fr; 00328 int last_state = 0; 00329 struct timeval now, start, state_change; 00330 enum ast_control_t38 t38control; 00331 00332 #if SPANDSP_RELEASE_DATE >= 20081012 00333 /* for spandsp shaphots 0.0.6 and higher */ 00334 t30state = &fax.t30; 00335 #else 00336 /* for spandsp release 0.0.5 */ 00337 t30state = &fax.t30_state; 00338 #endif 00339 00340 original_read_fmt = s->chan->readformat; 00341 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00342 res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR); 00343 if (res < 0) { 00344 ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); 00345 goto done; 00346 } 00347 } 00348 00349 original_write_fmt = s->chan->writeformat; 00350 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00351 res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR); 00352 if (res < 0) { 00353 ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); 00354 goto done; 00355 } 00356 } 00357 00358 /* Initialize T30 terminal */ 00359 fax_init(&fax, s->caller_mode); 00360 00361 /* Setup logging */ 00362 set_logging(&fax.logging); 00363 set_logging(&t30state->logging); 00364 00365 /* Configure terminal */ 00366 set_local_info(t30state, s); 00367 set_file(t30state, s); 00368 set_ecm(t30state, TRUE); 00369 00370 fax_set_transmit_on_idle(&fax, TRUE); 00371 00372 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00373 00374 if (s->t38state == T38_STATE_UNAVAILABLE) { 00375 ast_debug(1, "T38 is unavailable on %s\n", s->chan->name); 00376 } else if (!s->direction) { 00377 /* We are receiving side and this means we are the side which should 00378 request T38 when the fax is detected. Use DSP to detect fax tone */ 00379 ast_debug(1, "Setting up CNG detection on %s\n", s->chan->name); 00380 dsp = ast_dsp_new(); 00381 ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT); 00382 ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG); 00383 detect_tone = 1; 00384 } 00385 00386 start = state_change = ast_tvnow(); 00387 00388 ast_activate_generator(s->chan, &generator, &fax); 00389 00390 while (!s->finished) { 00391 res = ast_waitfor(s->chan, 20); 00392 if (res < 0) 00393 break; 00394 else if (res > 0) 00395 res = 0; 00396 00397 inf = ast_read(s->chan); 00398 if (inf == NULL) { 00399 ast_debug(1, "Channel hangup\n"); 00400 res = -1; 00401 break; 00402 } 00403 00404 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen); 00405 00406 /* Detect fax tone */ 00407 if (detect_tone && inf->frametype == AST_FRAME_VOICE) { 00408 /* Duplicate frame because ast_dsp_process may free the frame passed */ 00409 fr = ast_frdup(inf); 00410 00411 /* Do not pass channel to ast_dsp_process otherwise it may queue modified audio frame back */ 00412 fr = ast_dsp_process(NULL, dsp, fr); 00413 if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') { 00414 ast_debug(1, "Fax tone detected. Requesting T38\n"); 00415 t38control = AST_T38_REQUEST_NEGOTIATE; 00416 ast_indicate_data(s->chan, AST_CONTROL_T38, &t38control, sizeof(t38control)); 00417 detect_tone = 0; 00418 } 00419 00420 ast_frfree(fr); 00421 } 00422 00423 00424 /* Check the frame type. Format also must be checked because there is a chance 00425 that a frame in old format was already queued before we set chanel format 00426 to slinear so it will still be received by ast_read */ 00427 if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) { 00428 00429 if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) { 00430 /* I know fax_rx never returns errors. The check here is for good style only */ 00431 ast_log(LOG_WARNING, "fax_rx returned error\n"); 00432 res = -1; 00433 break; 00434 } 00435 00436 /* Watchdog */ 00437 if (last_state != t30state->state) { 00438 state_change = ast_tvnow(); 00439 last_state = t30state->state; 00440 } 00441 } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 && 00442 inf->datalen == sizeof(enum ast_control_t38)) { 00443 t38control =*((enum ast_control_t38 *) inf->data.ptr); 00444 if (t38control == AST_T38_NEGOTIATED) { 00445 /* T38 switchover completed */ 00446 ast_debug(1, "T38 negotiated, finishing audio loop\n"); 00447 res = 1; 00448 break; 00449 } 00450 } 00451 00452 ast_frfree(inf); 00453 inf = NULL; 00454 00455 /* Watchdog */ 00456 now = ast_tvnow(); 00457 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00458 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00459 res = -1; 00460 break; 00461 } 00462 } 00463 00464 ast_debug(1, "Loop finished, res=%d\n", res); 00465 00466 if (inf) 00467 ast_frfree(inf); 00468 00469 if (dsp) 00470 ast_dsp_free(dsp); 00471 00472 ast_deactivate_generator(s->chan); 00473 00474 /* If we are switching to T38, remove phase E handler. Otherwise it will be executed 00475 by t30_terminate, display diagnostics and set status variables although no transmittion 00476 has taken place yet. */ 00477 if (res > 0) { 00478 t30_set_phase_e_handler(t30state, NULL, NULL); 00479 } 00480 00481 t30_terminate(t30state); 00482 fax_release(&fax); 00483 00484 done: 00485 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00486 if (ast_set_write_format(s->chan, original_write_fmt) < 0) 00487 ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name); 00488 } 00489 00490 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00491 if (ast_set_read_format(s->chan, original_read_fmt) < 0) 00492 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name); 00493 } 00494 00495 return res; 00496 00497 }
static int transmit_t38 | ( | fax_session * | s | ) | [static] |
Definition at line 499 of file app_fax.c.
References AST_CONTROL_T38, ast_debug, AST_FRAME_CONTROL, AST_FRAME_MODEM, ast_frfree, ast_log(), AST_MODEM_T38, ast_read(), AST_T38_REFUSED, AST_T38_TERMINATED, ast_tvdiff_sec(), ast_tvdiff_us(), ast_tvnow(), ast_waitfor(), ast_frame::data, ast_frame::datalen, ast_frame::frametype, LOG_WARNING, phase_e_handler(), ast_frame::ptr, s, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, t38_tx_packet_handler(), TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.
Referenced by transmit().
00500 { 00501 int res = 0; 00502 t38_terminal_state_t t38; 00503 struct ast_frame *inf = NULL; 00504 int last_state = 0; 00505 struct timeval now, start, state_change, last_frame; 00506 enum ast_control_t38 t38control; 00507 00508 t30_state_t *t30state; 00509 t38_core_state_t *t38state; 00510 00511 #if SPANDSP_RELEASE_DATE >= 20081012 00512 /* for spandsp shaphots 0.0.6 and higher */ 00513 t30state = &t38.t30; 00514 t38state = &t38.t38_fe.t38; 00515 #else 00516 /* for spandsp releases 0.0.5 */ 00517 t30state = &t38.t30_state; 00518 t38state = &t38.t38; 00519 #endif 00520 00521 /* Initialize terminal */ 00522 memset(&t38, 0, sizeof(t38)); 00523 if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) { 00524 ast_log(LOG_WARNING, "Unable to start T.38 termination.\n"); 00525 return -1; 00526 } 00527 00528 /* Setup logging */ 00529 set_logging(&t38.logging); 00530 set_logging(&t30state->logging); 00531 set_logging(&t38state->logging); 00532 00533 /* Configure terminal */ 00534 set_local_info(t30state, s); 00535 set_file(t30state, s); 00536 set_ecm(t30state, TRUE); 00537 00538 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00539 00540 now = start = state_change = ast_tvnow(); 00541 00542 while (!s->finished) { 00543 00544 res = ast_waitfor(s->chan, 20); 00545 if (res < 0) 00546 break; 00547 else if (res > 0) 00548 res = 0; 00549 00550 last_frame = now; 00551 now = ast_tvnow(); 00552 t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000)); 00553 00554 inf = ast_read(s->chan); 00555 if (inf == NULL) { 00556 ast_debug(1, "Channel hangup\n"); 00557 res = -1; 00558 break; 00559 } 00560 00561 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen); 00562 00563 if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) { 00564 t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno); 00565 00566 /* Watchdog */ 00567 if (last_state != t30state->state) { 00568 state_change = ast_tvnow(); 00569 last_state = t30state->state; 00570 } 00571 } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38 && 00572 inf->datalen == sizeof(enum ast_control_t38)) { 00573 00574 t38control = *((enum ast_control_t38 *) inf->data.ptr); 00575 00576 if (t38control == AST_T38_TERMINATED || t38control == AST_T38_REFUSED) { 00577 ast_debug(1, "T38 down, terminating\n"); 00578 res = -1; 00579 break; 00580 } 00581 } 00582 00583 ast_frfree(inf); 00584 inf = NULL; 00585 00586 /* Watchdog */ 00587 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00588 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00589 res = -1; 00590 break; 00591 } 00592 } 00593 00594 ast_debug(1, "Loop finished, res=%d\n", res); 00595 00596 if (inf) 00597 ast_frfree(inf); 00598 00599 t30_terminate(t30state); 00600 t38_terminal_release(&t38); 00601 00602 return res; 00603 }
static int unload_module | ( | void | ) | [static] |
Definition at line 751 of file app_fax.c.
References ast_unregister_application().
00752 { 00753 int res; 00754 00755 res = ast_unregister_application(app_sndfax_name); 00756 res |= ast_unregister_application(app_rcvfax_name); 00757 00758 return res; 00759 }
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().