Fax gateway functionality (i.e. translating between a T.30 terminal and a T.38

terminal). Can be enabled on a channel by setting FAXOPT(gateway)=yes in the
dialplan.

Big thanks to irroot for porting this code to use the framehooks api.


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@325816 65c4cc65-6c06-0410-ace0-fbb531ad65f3
10-digiumphones
Matthew Nicholson 14 years ago
parent 82d28452ca
commit 0f0956e67a

@ -150,6 +150,16 @@ pbx_lua
stopped and restarted using the autoservice_stop() and autoservice_start()
functions.
res_fax
--------------------------
* The ReceiveFAXStatus and SendFAXStatus manager events have been consolidated
into a FAXStatus event with an 'Operation' header that will be either
'send', 'receive', and 'gateway'.
* T.38 gateway functionality has been added to res_fax (and res_fax_spandsp).
Set FAXOPT(gateway)=yes to enable this functionality on a channel. This
feature will handle converting a fax call between an audio T.30 fax terminal
and an IFP T.38 fax terminal.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ----------------
------------------------------------------------------------------------------

@ -42,6 +42,8 @@ enum ast_fax_capabilities {
AST_FAX_TECH_T38 = (1 << 3),
/*! sending mulitple documents supported */
AST_FAX_TECH_MULTI_DOC = (1 << 4),
/*! T.38 - T.30 Gateway */
AST_FAX_TECH_GATEWAY = (1 << 5),
};
/*! \brief fax modem capabilities */
@ -168,6 +170,8 @@ struct ast_fax_session_details {
struct ast_fax_t38_parameters our_t38_parameters;
/*! the other endpoint's T.38 session parameters, if any */
struct ast_fax_t38_parameters their_t38_parameters;
/*! the id of the t.38 gateway framehook for this channel */
int gateway_id;
};
struct ast_fax_tech;
@ -204,6 +208,9 @@ struct ast_fax_session {
struct ast_smoother *smoother;
};
/* if this overlaps with any AST_FRFLAG_* values, problems will occur */
#define AST_FAX_FRFLAG_GATEWAY (1 << 13)
/*! \brief used to register a FAX technology module with res_fax */
struct ast_fax_tech {
/*! the type of fax session supported with this ast_fax_tech structure */

File diff suppressed because it is too large Load Diff

@ -5,6 +5,22 @@
*
* Matthew Nicholson <mnicholson@digium.com>
*
* Initial T.38-gateway code
* 2008, Daniel Ferenci <daniel.ferenci@nethemba.com>
* Created by Nethemba s.r.o. http://www.nethemba.com
* Sponsored by IPEX a.s. http://www.ipex.cz
*
* T.38-gateway integration into asterisk app_fax and rework
* 2008, Gregory Hinton Nietsky <gregory@dnstelecom.co.za>
* dns Telecom http://www.dnstelecom.co.za
*
* Modified to make T.38-gateway compatible with Asterisk 1.6.2
* 2010, Anton Verevkin <mymail@verevkin.it>
* ViaNetTV http://www.vianettv.com
*
* Modified to make T.38-gateway work
* 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
@ -46,9 +62,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/timing.h"
#include "asterisk/astobj2.h"
#include "asterisk/res_fax.h"
#include "asterisk/channel.h"
#define SPANDSP_FAX_SAMPLES 160
#define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */
#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
static void spandsp_fax_destroy(struct ast_fax_session *s);
@ -57,6 +75,9 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
static int spandsp_fax_start(struct ast_fax_session *s);
static int spandsp_fax_cancel(struct ast_fax_session *s);
static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
static int spandsp_fax_gateway_start(struct ast_fax_session *s);
static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s);
static char *spandsp_fax_cli_show_capabilities(int fd);
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
@ -75,7 +96,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
*/
.version = "pre-20090220",
#endif
.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,
.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY,
.new_session = spandsp_fax_new,
.destroy_session = spandsp_fax_destroy,
.read = spandsp_fax_read,
@ -114,6 +135,7 @@ static struct {
struct spandsp_pvt {
unsigned int ist38:1;
unsigned int isdone:1;
enum ast_t38_state ast_t38_state;
fax_state_t fax_state;
t38_terminal_state_t t38_state;
t30_state_t *t30_state;
@ -121,6 +143,9 @@ struct spandsp_pvt {
struct spandsp_fax_stats *stats;
struct spandsp_fax_gw_stats *t38stats;
t38_gateway_state_t t38_gw_state;
struct ast_timer *timer;
AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
};
@ -158,7 +183,9 @@ static void session_destroy(struct spandsp_pvt *p)
*/
static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
{
struct spandsp_pvt *p = data;
int res = -1;
struct ast_fax_session *s = data;
struct spandsp_pvt *p = s->tech_pvt;
struct ast_frame fax_frame = {
.frametype = AST_FRAME_MODEM,
.subclass.integer = AST_MODEM_T38,
@ -174,13 +201,23 @@ static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, c
AST_FRAME_SET_BUFFER(f, buf, 0, len);
if (!(f = ast_frisolate(f))) {
return -1;
return res;
}
/* no need to lock, this all runs in the same thread */
AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
ast_set_flag(f, AST_FAX_FRFLAG_GATEWAY);
if (p->ast_t38_state == T38_STATE_NEGOTIATED) {
res = ast_write(s->chan, f);
} else {
res = ast_queue_frame(s->chan, f);
}
ast_frfree(f);
} else {
/* no need to lock, this all runs in the same thread */
AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
}
return 0;
return res;
}
static int update_stats(struct spandsp_pvt *p, int completion_code)
@ -422,6 +459,11 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
goto e_return;
}
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
s->state = AST_FAX_STATE_INITIALIZED;
return p;
}
AST_LIST_HEAD_INIT(&p->read_frames);
if (s->details->caps & AST_FAX_TECH_RECEIVE) {
@ -450,7 +492,7 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
}
/* init t38 stuff */
t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p);
t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, s);
set_logging(&p->t38_state.logging, s->details);
}
@ -475,7 +517,12 @@ static void spandsp_fax_destroy(struct ast_fax_session *s)
{
struct spandsp_pvt *p = s->tech_pvt;
session_destroy(p);
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
spandsp_fax_gateway_cleanup(s);
} else {
session_destroy(p);
}
ast_free(p);
s->tech_pvt = NULL;
s->fd = -1;
@ -536,6 +583,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
{
struct spandsp_pvt *p = s->tech_pvt;
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
return spandsp_fax_gateway_process(s, f);
}
/* XXX do we need to lock here? */
if (s->state == AST_FAX_STATE_COMPLETE) {
ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
@ -549,6 +600,182 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
}
}
/*! \brief generate T.30 packets sent to the T.30 leg of gateway
* \param chan T.30 channel
* \param data fax session structure
* \param len not used
* \param samples no of samples generated
* \return -1 on failure or 0 on sucess*/
static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len, int samples)
{
int res = -1;
struct ast_fax_session *s = data;
struct spandsp_pvt *p = s->tech_pvt;
uint8_t buffer[AST_FRIENDLY_OFFSET + samples * sizeof(uint16_t)];
struct ast_frame *f;
struct ast_frame t30_frame = {
.frametype = AST_FRAME_VOICE,
.src = "res_fax_spandsp_g711",
.samples = samples,
.flags = AST_FAX_FRFLAG_GATEWAY,
};
AST_FRAME_SET_BUFFER(&t30_frame, buffer, AST_FRIENDLY_OFFSET, t30_frame.samples * sizeof(int16_t));
ast_format_set(&t30_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
if (!(f = ast_frisolate(&t30_frame))) {
return p->isdone ? -1 : res;
}
/* generate a T.30 packet */
if ((f->samples = t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples))) {
f->datalen = f->samples * sizeof(int16_t);
res = ast_write(chan, f);
}
ast_frfree(f);
return p->isdone ? -1 : res;
}
/*! \brief simple routine to allocate data to generator
* \param chan channel
* \param params generator data
* \return data to use in generator call*/
static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params) {
ao2_ref(params, +1);
return params;
}
static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data) {
ao2_ref(data, -1);
}
/*! \brief activate a spandsp gateway based on the information in the given fax session
* \param s fax session
* \return -1 on error 0 on sucess*/
static int spandsp_fax_gateway_start(struct ast_fax_session *s) {
struct spandsp_pvt *p = s->tech_pvt;
struct ast_fax_t38_parameters *t38_param;
int i, modems = 0;
struct ast_channel *peer;
static struct ast_generator t30_gen = {
alloc: spandsp_fax_gw_gen_alloc,
release: spandsp_fax_gw_gen_release,
generate: spandsp_fax_gw_t30_gen,
};
#if SPANDSP_RELEASE_DATE >= 20081012
/* for spandsp shaphots 0.0.6 and higher */
p->t38_core_state=&p->t38_gw_state.t38x.t38;
#else
/* for spandsp release 0.0.5 */
p->t38_core_state=&p->t38_gw_state.t38;
#endif
if (!t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, s)) {
return -1;
}
p->ist38 = 1;
p->ast_t38_state = ast_channel_get_t38_state(s->chan);
if (!(peer = ast_bridged_channel(s->chan))) {
ast_channel_unlock(s->chan);
return -1;
}
ast_activate_generator(p->ast_t38_state == T38_STATE_NEGOTIATED ? peer : s->chan, &t30_gen , s);
set_logging(&p->t38_gw_state.logging, s->details);
set_logging(&p->t38_core_state->logging, s->details);
t38_param = (p->ast_t38_state == T38_STATE_NEGOTIATED) ? &s->details->our_t38_parameters : &s->details->their_t38_parameters;
t38_set_t38_version(p->t38_core_state, t38_param->version);
t38_gateway_set_ecm_capability(&p->t38_gw_state, s->details->option.ecm);
t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp);
t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal);
t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr);
t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig);
t38_set_data_rate_management_method(p->t38_core_state,
(t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2);
t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE);
t38_set_sequence_number_handling(p->t38_core_state, TRUE);
if (AST_FAX_MODEM_V17 & s->details->modems) {
modems |= T30_SUPPORT_V17;
}
if (AST_FAX_MODEM_V27 & s->details->modems) {
modems |= T30_SUPPORT_V27TER;
}
if (AST_FAX_MODEM_V29 & s->details->modems) {
modems |= T30_SUPPORT_V29;
}
if (AST_FAX_MODEM_V34 & s->details->modems) {
#if defined(T30_SUPPORT_V34)
modems |= T30_SUPPORT_V34;
#elif defined(T30_SUPPORT_V34HDX)
modems |= T30_SUPPORT_V34HDX;
#else
ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n");
#endif
}
t38_gateway_set_supported_modems(&p->t38_gw_state, modems);
/* engage udptl nat on other side of T38 line
* (Asterisk changes media ports thus we send a few packets to reinitialize
* pinholes in NATs and FWs
*/
for (i=0; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) {
#if SPANDSP_RELEASE_DATE >= 20091228
t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL);
#elif SPANDSP_RELEASE_DATE >= 20081012
t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38x.t38.indicator_tx_count);
#else
t38_core_send_indicator(&p->t38_gw_state.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38.indicator_tx_count);
#endif
}
s->state = AST_FAX_STATE_ACTIVE;
return 0;
}
/*! \brief process a frame from the bridge
* \param s fax session
* \param f frame to process
* \return 1 on sucess 0 on incorect packet*/
static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f)
{
struct spandsp_pvt *p = s->tech_pvt;
/*invalid frame*/
if (!f->data.ptr || !f->datalen) {
return -1;
}
/* Process a IFP packet */
if ((f->frametype == AST_FRAME_MODEM) && (f->subclass.integer == AST_MODEM_T38)) {
return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
} else if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
return t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples);
}
return -1;
}
/*! \brief gather data and clean up after gateway ends
* \param s fax session*/
static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s)
{
struct spandsp_pvt *p = s->tech_pvt;
t38_stats_t t38_stats;
t38_gateway_get_transfer_statistics(&p->t38_gw_state, &t38_stats);
s->details->option.ecm = t38_stats.error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE;
s->details->pages_transferred = t38_stats.pages_transferred;
ast_string_field_build(s->details, transfer_rate, "%d", t38_stats.bit_rate);
}
/*! \brief */
static int spandsp_fax_start(struct ast_fax_session *s)
{
@ -556,6 +783,10 @@ static int spandsp_fax_start(struct ast_fax_session *s)
s->state = AST_FAX_STATE_OPEN;
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
return spandsp_fax_gateway_start(s);
}
if (p->ist38) {
#if SPANDSP_RELEASE_DATE >= 20080725
/* for spandsp shaphots 0.0.6 and higher */
@ -625,6 +856,12 @@ static int spandsp_fax_start(struct ast_fax_session *s)
static int spandsp_fax_cancel(struct ast_fax_session *s)
{
struct spandsp_pvt *p = s->tech_pvt;
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
p->isdone = 1;
return 0;
}
t30_terminate(p->t30_state);
p->isdone = 1;
return 0;
@ -653,7 +890,7 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
/*! \brief */
static char *spandsp_fax_cli_show_capabilities(int fd)
{
ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n");
ast_cli(fd, "SEND RECEIVE T.38 G.711 GATEWAY\n\n");
return CLI_SUCCESS;
}
@ -661,35 +898,48 @@ static char *spandsp_fax_cli_show_capabilities(int fd)
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
{
struct spandsp_pvt *p = s->tech_pvt;
t30_stats_t stats;
ao2_lock(s);
ast_cli(fd, "%-22s : %d\n", "session", s->id);
ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
if (s->state != AST_FAX_STATE_UNINITIALIZED) {
t30_get_transfer_statistics(p->t30_state, &stats);
ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
ast_cli(fd, "%-22s : %d\n", "session", s->id);
ast_cli(fd, "%-22s : %s\n", "operation", "Gateway");
ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
if (s->state != AST_FAX_STATE_UNINITIALIZED) {
t38_stats_t stats;
t38_gateway_get_transfer_statistics(&p->t38_gw_state, &stats);
ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
}
} else {
ast_cli(fd, "%-22s : %d\n", "session", s->id);
ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
if (s->state != AST_FAX_STATE_UNINITIALIZED) {
t30_stats_t stats;
t30_get_transfer_statistics(p->t30_state, &stats);
ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
#if SPANDSP_RELEASE_DATE >= 20090220
ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
#else
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
#endif
ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
ast_cli(fd, "\nData Statistics:\n");
ast_cli(fd, "\nData Statistics:\n");
#if SPANDSP_RELEASE_DATE >= 20090220
ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
#else
ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
#endif
ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
}
}
ao2_unlock(s);
ast_cli(fd, "\n\n");

Loading…
Cancel
Save