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