#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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, } |
static const char | app_rcvfax_name [] = "ReceiveFAX" |
static const char | app_sndfax_name [] = "SendFAX" |
static struct ast_module_info * | ast_module_info = &__mod_info |
static struct 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 331 of file app_fax.c.
References AST_FORMAT_SLINEAR, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_write(), errno, ast_frame::frametype, LOG_WARNING, MAX_SAMPLES, ast_channel::name, and ast_frame::samples.
00332 { 00333 fax_state_t *fax = (fax_state_t*) data; 00334 uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)]; 00335 int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET); 00336 00337 struct ast_frame outf = { 00338 .frametype = AST_FRAME_VOICE, 00339 .subclass.codec = AST_FORMAT_SLINEAR, 00340 .src = __FUNCTION__, 00341 }; 00342 00343 if (samples > MAX_SAMPLES) { 00344 ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples); 00345 samples = MAX_SAMPLES; 00346 } 00347 00348 if ((len = fax_tx(fax, buf, samples)) > 0) { 00349 outf.samples = len; 00350 AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t)); 00351 00352 if (ast_write(chan, &outf) < 0) { 00353 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); 00354 return -1; 00355 } 00356 } 00357 00358 return 0; 00359 }
static int load_module | ( | void | ) | [static] |
Definition at line 988 of file app_fax.c.
References ast_register_application_xml, rcvfax_exec(), and sndfax_exec().
00989 { 00990 int res ; 00991 00992 res = ast_register_application_xml(app_sndfax_name, sndfax_exec); 00993 res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec); 00994 00995 /* The default SPAN message handler prints to stderr. It is something we do not want */ 00996 span_set_message_handler(NULL); 00997 00998 return res; 00999 }
static void phase_e_handler | ( | t30_state_t * | f, | |
void * | user_data, | |||
int | result | |||
) | [static] |
Definition at line 203 of file app_fax.c.
References ast_debug, ast_log(), ast_manager_event, ast_channel::caller, fax_session::chan, ast_channel::connected, fax_session::direction, EVENT_FLAG_CALL, ast_channel::exten, fax_session::file_name, fax_session::finished, ast_party_connected_line::id, ast_party_caller::id, LOG_WARNING, ast_party_id::name, ast_channel::name, ast_party_id::number, pbx_builtin_setvar_helper(), S_COR, S_OR, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.
Referenced by transmit_audio(), and transmit_t38().
00204 { 00205 const char *local_ident; 00206 const char *far_ident; 00207 char buf[20]; 00208 fax_session *s = (fax_session *) user_data; 00209 t30_stats_t stat; 00210 int pages_transferred; 00211 00212 ast_debug(1, "Fax phase E handler. result=%d\n", result); 00213 00214 t30_get_transfer_statistics(f, &stat); 00215 00216 s = (fax_session *) user_data; 00217 00218 if (result != T30_ERR_OK) { 00219 s->finished = -1; 00220 00221 /* FAXSTATUS is already set to FAILED */ 00222 pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result)); 00223 00224 ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result)); 00225 00226 return; 00227 } 00228 00229 s->finished = 1; 00230 00231 local_ident = S_OR(t30_get_tx_ident(f), ""); 00232 far_ident = S_OR(t30_get_rx_ident(f), ""); 00233 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 00234 pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 00235 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident); 00236 #if SPANDSP_RELEASE_DATE >= 20090220 00237 pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx; 00238 #else 00239 pages_transferred = stat.pages_transferred; 00240 #endif 00241 snprintf(buf, sizeof(buf), "%d", pages_transferred); 00242 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf); 00243 snprintf(buf, sizeof(buf), "%d", stat.y_resolution); 00244 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf); 00245 snprintf(buf, sizeof(buf), "%d", stat.bit_rate); 00246 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 00247 00248 ast_debug(1, "Fax transmitted successfully.\n"); 00249 ast_debug(1, " Remote station ID: %s\n", far_ident); 00250 ast_debug(1, " Pages transferred: %d\n", pages_transferred); 00251 ast_debug(1, " Image resolution: %d x %d\n", stat.x_resolution, stat.y_resolution); 00252 ast_debug(1, " Transfer Rate: %d\n", stat.bit_rate); 00253 00254 ast_manager_event(s->chan, EVENT_FLAG_CALL, 00255 s->direction ? "FaxSent" : "FaxReceived", 00256 "Channel: %s\r\n" 00257 "Exten: %s\r\n" 00258 "CallerID: %s\r\n" 00259 "CallerIDName: %s\r\n" 00260 "ConnectedLineNum: %s\r\n" 00261 "ConnectedLineName: %s\r\n" 00262 "RemoteStationID: %s\r\n" 00263 "LocalStationID: %s\r\n" 00264 "PagesTransferred: %d\r\n" 00265 "Resolution: %d\r\n" 00266 "TransferRate: %d\r\n" 00267 "FileName: %s\r\n", 00268 s->chan->name, 00269 s->chan->exten, 00270 S_COR(s->chan->caller.id.number.valid, s->chan->caller.id.number.str, ""), 00271 S_COR(s->chan->caller.id.name.valid, s->chan->caller.id.name.str, ""), 00272 S_COR(s->chan->connected.id.number.valid, s->chan->connected.id.number.str, ""), 00273 S_COR(s->chan->connected.id.name.valid, s->chan->connected.id.name.str, ""), 00274 far_ident, 00275 local_ident, 00276 pages_transferred, 00277 stat.y_resolution, 00278 stat.bit_rate, 00279 s->file_name); 00280 }
static int rcvfax_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 909 of file app_fax.c.
References args, AST_APP_ARG, ast_channel_queryoption(), ast_channel_setoption(), AST_DECLARE_APP_ARGS, ast_log(), AST_OPTION_DIGIT_DETECT, AST_OPTION_FAX_DETECT, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, fax_session::direction, dummy(), FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.
Referenced by load_module().
00910 { 00911 int res = 0; 00912 char *parse; 00913 fax_session session; 00914 char restore_digit_detect = 0; 00915 00916 AST_DECLARE_APP_ARGS(args, 00917 AST_APP_ARG(file_name); 00918 AST_APP_ARG(options); 00919 ); 00920 00921 if (chan == NULL) { 00922 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00923 return -1; 00924 } 00925 00926 /* The next few lines of code parse out the filename and header from the input string */ 00927 if (ast_strlen_zero(data)) { 00928 /* No data implies no filename or anything is present */ 00929 ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n"); 00930 return -1; 00931 } 00932 00933 parse = ast_strdupa(data); 00934 AST_STANDARD_APP_ARGS(args, parse); 00935 00936 session.caller_mode = FALSE; 00937 00938 if (args.options) { 00939 if (strchr(args.options, 'c')) 00940 session.caller_mode = TRUE; 00941 } 00942 00943 /* Done parsing */ 00944 session.direction = 0; 00945 session.file_name = args.file_name; 00946 session.chan = chan; 00947 session.finished = 0; 00948 00949 /* get current digit detection mode, then disable digit detection if enabled */ 00950 { 00951 int dummy = sizeof(restore_digit_detect); 00952 00953 ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0); 00954 } 00955 00956 if (restore_digit_detect) { 00957 char new_digit_detect = 0; 00958 00959 ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0); 00960 } 00961 00962 /* disable FAX tone detection if enabled */ 00963 { 00964 char new_fax_detect = 0; 00965 00966 ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0); 00967 } 00968 00969 res = transmit(&session); 00970 00971 if (restore_digit_detect) { 00972 ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0); 00973 } 00974 00975 return res; 00976 }
static void set_ecm | ( | t30_state_t * | state, | |
int | ecm | |||
) | [static] |
Definition at line 316 of file app_fax.c.
Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().
00317 { 00318 t30_set_ecm_capability(state, ecm); 00319 t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION); 00320 }
static void set_file | ( | t30_state_t * | state, | |
fax_session * | s | |||
) | [static] |
Definition at line 308 of file app_fax.c.
References fax_session::direction, and fax_session::file_name.
Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().
00309 { 00310 if (s->direction) 00311 t30_set_tx_file(state, s->file_name, -1, -1); 00312 else 00313 t30_set_rx_file(state, s->file_name, -1); 00314 }
static void set_local_info | ( | t30_state_t * | state, | |
fax_session * | s | |||
) | [static] |
Definition at line 295 of file app_fax.c.
References ast_strlen_zero(), fax_session::chan, and pbx_builtin_getvar_helper().
Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().
00296 { 00297 const char *x; 00298 00299 x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID"); 00300 if (!ast_strlen_zero(x)) 00301 t30_set_tx_ident(state, x); 00302 00303 x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO"); 00304 if (!ast_strlen_zero(x)) 00305 t30_set_tx_page_header_info(state, x); 00306 }
static int set_logging | ( | logging_state_t * | state | ) | [static] |
Definition at line 285 of file app_fax.c.
References option_debug, and span_message().
Referenced by spandsp_fax_new(), spandsp_fax_start(), transmit_audio(), and transmit_t38().
00286 { 00287 int level = SPAN_LOG_WARNING + option_debug; 00288 00289 span_log_set_message_handler(state, span_message); 00290 span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 00291 00292 return 0; 00293 }
static int sndfax_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 840 of file app_fax.c.
References args, AST_APP_ARG, ast_channel_queryoption(), ast_channel_setoption(), AST_DECLARE_APP_ARGS, ast_log(), AST_OPTION_DIGIT_DETECT, AST_OPTION_FAX_DETECT, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, fax_session::direction, dummy(), FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.
Referenced by load_module().
00841 { 00842 int res = 0; 00843 char *parse; 00844 fax_session session = { 0, }; 00845 char restore_digit_detect = 0; 00846 00847 AST_DECLARE_APP_ARGS(args, 00848 AST_APP_ARG(file_name); 00849 AST_APP_ARG(options); 00850 ); 00851 00852 if (chan == NULL) { 00853 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00854 return -1; 00855 } 00856 00857 /* The next few lines of code parse out the filename and header from the input string */ 00858 if (ast_strlen_zero(data)) { 00859 /* No data implies no filename or anything is present */ 00860 ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n"); 00861 return -1; 00862 } 00863 00864 parse = ast_strdupa(data); 00865 AST_STANDARD_APP_ARGS(args, parse); 00866 00867 session.caller_mode = TRUE; 00868 00869 if (args.options) { 00870 if (strchr(args.options, 'a')) 00871 session.caller_mode = FALSE; 00872 } 00873 00874 /* Done parsing */ 00875 session.direction = 1; 00876 session.file_name = args.file_name; 00877 session.chan = chan; 00878 session.finished = 0; 00879 00880 /* get current digit detection mode, then disable digit detection if enabled */ 00881 { 00882 int dummy = sizeof(restore_digit_detect); 00883 00884 ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0); 00885 } 00886 00887 if (restore_digit_detect) { 00888 char new_digit_detect = 0; 00889 00890 ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0); 00891 } 00892 00893 /* disable FAX tone detection if enabled */ 00894 { 00895 char new_fax_detect = 0; 00896 00897 ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0); 00898 } 00899 00900 res = transmit(&session); 00901 00902 if (restore_digit_detect) { 00903 ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0); 00904 } 00905 00906 return res; 00907 }
static void span_message | ( | int | level, | |
const char * | msg | |||
) | [static] |
Definition at line 169 of file app_fax.c.
References ast_log(), LOG_DEBUG, LOG_ERROR, and LOG_WARNING.
Referenced by set_logging().
00170 { 00171 if (level == SPAN_LOG_ERROR) { 00172 ast_log(LOG_ERROR, "%s", msg); 00173 } else if (level == SPAN_LOG_WARNING) { 00174 ast_log(LOG_WARNING, "%s", msg); 00175 } else { 00176 ast_log(LOG_DEBUG, "%s", msg); 00177 } 00178 }
static int t38_tx_packet_handler | ( | t38_core_state_t * | s, | |
void * | user_data, | |||
const uint8_t * | buf, | |||
int | len, | |||
int | count | |||
) | [static] |
Definition at line 180 of file app_fax.c.
References AST_FRAME_MODEM, AST_FRAME_SET_BUFFER, ast_log(), AST_MODEM_T38, ast_write(), errno, ast_frame::frametype, and LOG_WARNING.
Referenced by spandsp_fax_new(), and transmit_t38().
00181 { 00182 struct ast_channel *chan = (struct ast_channel *) user_data; 00183 00184 struct ast_frame outf = { 00185 .frametype = AST_FRAME_MODEM, 00186 .subclass.integer = AST_MODEM_T38, 00187 .src = __FUNCTION__, 00188 }; 00189 00190 /* TODO: Asterisk does not provide means of resending the same packet multiple 00191 times so count is ignored at the moment */ 00192 00193 AST_FRAME_SET_BUFFER(&outf, buf, 0, len); 00194 00195 if (ast_write(chan, &outf) < 0) { 00196 ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); 00197 return -1; 00198 } 00199 00200 return 0; 00201 }
static int transmit | ( | fax_session * | s | ) | [static] |
Definition at line 782 of file app_fax.c.
References ast_channel::_state, ast_answer(), ast_channel_get_t38_state(), ast_debug, ast_log(), AST_STATE_UP, fax_session::chan, fax_session::finished, LOG_ERROR, LOG_WARNING, ast_channel::name, pbx_builtin_setvar_helper(), T38_STATE_NEGOTIATED, fax_session::t38state, transmit_audio(), and transmit_t38().
Referenced by rcvfax_exec(), and sndfax_exec().
00783 { 00784 int res = 0; 00785 00786 /* Clear all channel variables which to be set by the application. 00787 Pre-set status to error so in case of any problems we can just leave */ 00788 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 00789 pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 00790 00791 pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL); 00792 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL); 00793 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", "0"); 00794 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL); 00795 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 00796 00797 if (s->chan->_state != AST_STATE_UP) { 00798 /* Shouldn't need this, but checking to see if channel is already answered 00799 * Theoretically asterisk should already have answered before running the app */ 00800 res = ast_answer(s->chan); 00801 if (res) { 00802 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name); 00803 return res; 00804 } 00805 } 00806 00807 s->t38state = ast_channel_get_t38_state(s->chan); 00808 if (s->t38state != T38_STATE_NEGOTIATED) { 00809 /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */ 00810 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 00811 res = transmit_audio(s); 00812 if (res > 0) { 00813 /* transmit_audio reports switchover to T38. Update t38state */ 00814 s->t38state = ast_channel_get_t38_state(s->chan); 00815 if (s->t38state != T38_STATE_NEGOTIATED) { 00816 ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n"); 00817 } 00818 } 00819 } 00820 00821 if (s->t38state == T38_STATE_NEGOTIATED) { 00822 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 00823 res = transmit_t38(s); 00824 } 00825 00826 if (res) { 00827 ast_log(LOG_WARNING, "Transmission error\n"); 00828 res = -1; 00829 } else if (s->finished < 0) { 00830 ast_log(LOG_WARNING, "Transmission failed\n"); 00831 } else if (s->finished > 0) { 00832 ast_debug(1, "Transmission finished Ok\n"); 00833 } 00834 00835 return res; 00836 }
static int transmit_audio | ( | fax_session * | s | ) | [static] |
Definition at line 369 of file app_fax.c.
References ast_activate_generator(), ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_deactivate_generator(), ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_write_format(), AST_T38_NEGOTIATED, AST_T38_RATE_14400, AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_tvdiff_sec(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame_subclass::codec, ast_frame::data, ast_frame::datalen, FALSE, fax_session::finished, ast_frame::frametype, generator, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_channel::name, phase_e_handler(), ast_frame::ptr, ast_channel::readformat, ast_control_t38_parameters::request_response, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, T38_STATE_UNAVAILABLE, fax_session::t38parameters, TRUE, ast_control_t38_parameters::version, WATCHDOG_STATE_TIMEOUT, WATCHDOG_TOTAL_TIMEOUT, and ast_channel::writeformat.
Referenced by transmit().
00370 { 00371 int res = -1; 00372 int original_read_fmt = AST_FORMAT_SLINEAR; 00373 int original_write_fmt = AST_FORMAT_SLINEAR; 00374 fax_state_t fax; 00375 t30_state_t *t30state; 00376 struct ast_frame *inf = NULL; 00377 int last_state = 0; 00378 struct timeval now, start, state_change; 00379 enum ast_t38_state t38_state; 00380 struct ast_control_t38_parameters t38_parameters = { .version = 0, 00381 .max_ifp = 800, 00382 .rate = AST_T38_RATE_14400, 00383 .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, 00384 .fill_bit_removal = 1, 00385 /* 00386 * spandsp has API calls to support MMR and JBIG transcoding, but they aren't 00387 * implemented quite yet... so don't offer them to the remote endpoint 00388 * .transcoding_mmr = 1, 00389 * .transcoding_jbig = 1, 00390 */ 00391 }; 00392 00393 /* if in called party mode, try to use T.38 */ 00394 if (s->caller_mode == FALSE) { 00395 /* check if we are already in T.38 mode (unlikely), or if we can request 00396 * a switch... if so, request it now and wait for the result, rather 00397 * than starting an audio FAX session that will have to be cancelled 00398 */ 00399 if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) { 00400 return 1; 00401 } else if ((t38_state != T38_STATE_UNAVAILABLE) && 00402 (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE, 00403 (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) { 00404 /* wait up to five seconds for negotiation to complete */ 00405 unsigned int timeout = 5000; 00406 int ms; 00407 00408 ast_debug(1, "Negotiating T.38 for receive on %s\n", s->chan->name); 00409 while (timeout > 0) { 00410 ms = ast_waitfor(s->chan, 1000); 00411 if (ms < 0) { 00412 ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name); 00413 return -1; 00414 } 00415 if (!ms) { 00416 /* nothing happened */ 00417 if (timeout > 0) { 00418 timeout -= 1000; 00419 continue; 00420 } else { 00421 ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name); 00422 break; 00423 } 00424 } 00425 if (!(inf = ast_read(s->chan))) { 00426 return -1; 00427 } 00428 if ((inf->frametype == AST_FRAME_CONTROL) && 00429 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) && 00430 (inf->datalen == sizeof(t38_parameters))) { 00431 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00432 00433 switch (parameters->request_response) { 00434 case AST_T38_NEGOTIATED: 00435 ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name); 00436 res = 1; 00437 break; 00438 case AST_T38_REFUSED: 00439 ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name); 00440 break; 00441 default: 00442 ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name); 00443 break; 00444 } 00445 ast_frfree(inf); 00446 if (res == 1) { 00447 return 1; 00448 } else { 00449 break; 00450 } 00451 } 00452 ast_frfree(inf); 00453 } 00454 } 00455 } 00456 00457 #if SPANDSP_RELEASE_DATE >= 20080725 00458 /* for spandsp shaphots 0.0.6 and higher */ 00459 t30state = &fax.t30; 00460 #else 00461 /* for spandsp release 0.0.5 */ 00462 t30state = &fax.t30_state; 00463 #endif 00464 00465 original_read_fmt = s->chan->readformat; 00466 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00467 res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR); 00468 if (res < 0) { 00469 ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); 00470 goto done; 00471 } 00472 } 00473 00474 original_write_fmt = s->chan->writeformat; 00475 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00476 res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR); 00477 if (res < 0) { 00478 ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); 00479 goto done; 00480 } 00481 } 00482 00483 /* Initialize T30 terminal */ 00484 fax_init(&fax, s->caller_mode); 00485 00486 /* Setup logging */ 00487 set_logging(&fax.logging); 00488 set_logging(&t30state->logging); 00489 00490 /* Configure terminal */ 00491 set_local_info(t30state, s); 00492 set_file(t30state, s); 00493 set_ecm(t30state, TRUE); 00494 00495 fax_set_transmit_on_idle(&fax, TRUE); 00496 00497 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00498 00499 start = state_change = ast_tvnow(); 00500 00501 ast_activate_generator(s->chan, &generator, &fax); 00502 00503 while (!s->finished) { 00504 inf = NULL; 00505 00506 if ((res = ast_waitfor(s->chan, 25)) < 0) { 00507 ast_debug(1, "Error waiting for a frame\n"); 00508 break; 00509 } 00510 00511 /* Watchdog */ 00512 now = ast_tvnow(); 00513 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00514 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00515 res = -1; 00516 break; 00517 } 00518 00519 if (!res) { 00520 /* There was timeout waiting for a frame. Loop around and wait again */ 00521 continue; 00522 } 00523 00524 /* There is a frame available. Get it */ 00525 res = 0; 00526 00527 if (!(inf = ast_read(s->chan))) { 00528 ast_debug(1, "Channel hangup\n"); 00529 res = -1; 00530 break; 00531 } 00532 00533 ast_debug(10, "frame %d/%llu, len=%d\n", inf->frametype, (unsigned long long) inf->subclass.codec, inf->datalen); 00534 00535 /* Check the frame type. Format also must be checked because there is a chance 00536 that a frame in old format was already queued before we set channel format 00537 to slinear so it will still be received by ast_read */ 00538 if (inf->frametype == AST_FRAME_VOICE && inf->subclass.codec == AST_FORMAT_SLINEAR) { 00539 if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) { 00540 /* I know fax_rx never returns errors. The check here is for good style only */ 00541 ast_log(LOG_WARNING, "fax_rx returned error\n"); 00542 res = -1; 00543 break; 00544 } 00545 if (last_state != t30state->state) { 00546 state_change = ast_tvnow(); 00547 last_state = t30state->state; 00548 } 00549 } else if ((inf->frametype == AST_FRAME_CONTROL) && 00550 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS)) { 00551 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00552 00553 if (parameters->request_response == AST_T38_NEGOTIATED) { 00554 /* T38 switchover completed */ 00555 s->t38parameters = *parameters; 00556 ast_debug(1, "T38 negotiated, finishing audio loop\n"); 00557 res = 1; 00558 break; 00559 } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) { 00560 t38_parameters.request_response = AST_T38_NEGOTIATED; 00561 ast_debug(1, "T38 request received, accepting\n"); 00562 /* Complete T38 switchover */ 00563 ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)); 00564 /* Do not break audio loop, wait until channel driver finally acks switchover 00565 * with AST_T38_NEGOTIATED 00566 */ 00567 } 00568 } 00569 00570 ast_frfree(inf); 00571 inf = NULL; 00572 } 00573 00574 ast_debug(1, "Loop finished, res=%d\n", res); 00575 00576 if (inf) 00577 ast_frfree(inf); 00578 00579 ast_deactivate_generator(s->chan); 00580 00581 /* If we are switching to T38, remove phase E handler. Otherwise it will be executed 00582 by t30_terminate, display diagnostics and set status variables although no transmittion 00583 has taken place yet. */ 00584 if (res > 0) { 00585 t30_set_phase_e_handler(t30state, NULL, NULL); 00586 } 00587 00588 t30_terminate(t30state); 00589 fax_release(&fax); 00590 00591 done: 00592 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00593 if (ast_set_write_format(s->chan, original_write_fmt) < 0) 00594 ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name); 00595 } 00596 00597 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00598 if (ast_set_read_format(s->chan, original_read_fmt) < 0) 00599 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name); 00600 } 00601 00602 return res; 00603 00604 }
static int transmit_t38 | ( | fax_session * | s | ) | [static] |
Definition at line 606 of file app_fax.c.
References ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FRAME_CONTROL, AST_FRAME_MODEM, ast_frfree, ast_indicate_data(), ast_log(), AST_MODEM_T38, ast_read(), AST_T38_REFUSED, AST_T38_REQUEST_TERMINATE, AST_T38_TERMINATED, ast_tvdiff_sec(), ast_tvdiff_us(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame::data, ast_frame::datalen, disable_t38(), FALSE, ast_control_t38_parameters::fill_bit_removal, fax_session::finished, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_control_t38_parameters::max_ifp, ast_channel::name, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, t38, T38_STATE_NEGOTIATED, t38_tx_packet_handler(), fax_session::t38parameters, ast_control_t38_parameters::transcoding_jbig, ast_control_t38_parameters::transcoding_mmr, TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.
Referenced by transmit().
00607 { 00608 int res = 0; 00609 t38_terminal_state_t t38; 00610 struct ast_frame *inf = NULL; 00611 int last_state = 0; 00612 struct timeval now, start, state_change, last_frame; 00613 t30_state_t *t30state; 00614 t38_core_state_t *t38state; 00615 00616 #if SPANDSP_RELEASE_DATE >= 20080725 00617 /* for spandsp shaphots 0.0.6 and higher */ 00618 t30state = &t38.t30; 00619 t38state = &t38.t38_fe.t38; 00620 #else 00621 /* for spandsp releases 0.0.5 */ 00622 t30state = &t38.t30_state; 00623 t38state = &t38.t38; 00624 #endif 00625 00626 /* Initialize terminal */ 00627 memset(&t38, 0, sizeof(t38)); 00628 if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) { 00629 ast_log(LOG_WARNING, "Unable to start T.38 termination.\n"); 00630 res = -1; 00631 goto disable_t38; 00632 } 00633 00634 t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp); 00635 00636 if (s->t38parameters.fill_bit_removal) { 00637 t38_set_fill_bit_removal(t38state, TRUE); 00638 } 00639 if (s->t38parameters.transcoding_mmr) { 00640 t38_set_mmr_transcoding(t38state, TRUE); 00641 } 00642 if (s->t38parameters.transcoding_jbig) { 00643 t38_set_jbig_transcoding(t38state, TRUE); 00644 } 00645 00646 /* Setup logging */ 00647 set_logging(&t38.logging); 00648 set_logging(&t30state->logging); 00649 set_logging(&t38state->logging); 00650 00651 /* Configure terminal */ 00652 set_local_info(t30state, s); 00653 set_file(t30state, s); 00654 set_ecm(t30state, TRUE); 00655 00656 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00657 00658 now = start = state_change = ast_tvnow(); 00659 00660 while (!s->finished) { 00661 inf = NULL; 00662 00663 if ((res = ast_waitfor(s->chan, 25)) < 0) { 00664 ast_debug(1, "Error waiting for a frame\n"); 00665 break; 00666 } 00667 00668 last_frame = now; 00669 00670 /* Watchdog */ 00671 now = ast_tvnow(); 00672 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00673 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00674 res = -1; 00675 break; 00676 } 00677 00678 t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000)); 00679 00680 if (!res) { 00681 /* There was timeout waiting for a frame. Loop around and wait again */ 00682 continue; 00683 } 00684 00685 /* There is a frame available. Get it */ 00686 res = 0; 00687 00688 if (!(inf = ast_read(s->chan))) { 00689 ast_debug(1, "Channel hangup\n"); 00690 res = -1; 00691 break; 00692 } 00693 00694 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass.integer, inf->datalen); 00695 00696 if (inf->frametype == AST_FRAME_MODEM && inf->subclass.integer == AST_MODEM_T38) { 00697 t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno); 00698 if (last_state != t30state->state) { 00699 state_change = ast_tvnow(); 00700 last_state = t30state->state; 00701 } 00702 } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) { 00703 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00704 if (parameters->request_response == AST_T38_TERMINATED) { 00705 ast_debug(1, "T38 down, finishing\n"); 00706 break; 00707 } 00708 } 00709 00710 ast_frfree(inf); 00711 inf = NULL; 00712 } 00713 00714 ast_debug(1, "Loop finished, res=%d\n", res); 00715 00716 if (inf) 00717 ast_frfree(inf); 00718 00719 t30_terminate(t30state); 00720 t38_terminal_release(&t38); 00721 00722 disable_t38: 00723 /* if we are not the caller, it's our job to shut down the T.38 00724 * session when the FAX transmisson is complete. 00725 */ 00726 if ((s->caller_mode == FALSE) && 00727 (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) { 00728 struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, }; 00729 00730 if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) { 00731 /* wait up to five seconds for negotiation to complete */ 00732 unsigned int timeout = 5000; 00733 int ms; 00734 00735 ast_debug(1, "Shutting down T.38 on %s\n", s->chan->name); 00736 while (timeout > 0) { 00737 ms = ast_waitfor(s->chan, 1000); 00738 if (ms < 0) { 00739 ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name); 00740 return -1; 00741 } 00742 if (!ms) { 00743 /* nothing happened */ 00744 if (timeout > 0) { 00745 timeout -= 1000; 00746 continue; 00747 } else { 00748 ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name); 00749 break; 00750 } 00751 } 00752 if (!(inf = ast_read(s->chan))) { 00753 return -1; 00754 } 00755 if ((inf->frametype == AST_FRAME_CONTROL) && 00756 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) && 00757 (inf->datalen == sizeof(t38_parameters))) { 00758 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00759 00760 switch (parameters->request_response) { 00761 case AST_T38_TERMINATED: 00762 ast_debug(1, "Shut down T.38 on %s\n", s->chan->name); 00763 break; 00764 case AST_T38_REFUSED: 00765 ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name); 00766 break; 00767 default: 00768 ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name); 00769 break; 00770 } 00771 ast_frfree(inf); 00772 break; 00773 } 00774 ast_frfree(inf); 00775 } 00776 } 00777 } 00778 00779 return res; 00780 }
static int unload_module | ( | void | ) | [static] |
Definition at line 978 of file app_fax.c.
References ast_unregister_application().
00979 { 00980 int res; 00981 00982 res = ast_unregister_application(app_sndfax_name); 00983 res |= ast_unregister_application(app_rcvfax_name); 00984 00985 return res; 00986 }
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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, } [static] |
const char app_rcvfax_name[] = "ReceiveFAX" [static] |
const char app_sndfax_name[] = "SendFAX" [static] |
struct ast_module_info* ast_module_info = &__mod_info [static] |
struct ast_generator generator [static] |
Initial value:
{ alloc: fax_generator_alloc, generate: fax_generator_generate, }
Definition at line 361 of file app_fax.c.
Referenced by cli_alias_passthrough(), and transmit_audio().