Skip to main content

Chapter 24: Fax and T.38

FreeSWITCH delivers fax functionality through mod_spandsp, a module built on the SpanDSP library. It exposes two dialplan applications for sending and receiving fax documents over audio (G.711) and T.38 (UDPTL), a T.38 gateway mode for bridging between fax endpoints that negotiate different transport types, and a set of channel variables that control behavior at the session level.

Overview

mod_spandsp must be loaded before fax applications are available. It registers the rxfax, txfax, stopfax, and t38_gateway applications and reads its configuration from autoload_configs/spandsp.conf.xml at load time and on module reload.

Fax sessions begin in audio mode using G.711. If T.38 is enabled and the remote endpoint supports it, the module issues or accepts a re-INVITE to switch to UDPTL transport mid-call. The jitter buffer is disabled automatically when a fax application is invoked.

Echo cancellation should be disabled before invoking a fax application. Use the disable_ec application in the dialplan before calling rxfax or txfax. The supporting dialplan applications used in the examples below (set, answer, playback, hangup) are documented in the dptools reference.


Module Configuration

spandsp.conf.xml Structure

The configuration file contains two relevant sections: <modem-settings> for software modem configuration and <fax-settings> for fax behavior. The <descriptors> section configures call-progress tone detection and is not part of fax document transfer.

<configuration name="spandsp.conf" description="SpanDSP config">
<fax-settings>
<param name="use-ecm" value="true"/>
<param name="verbose" value="false"/>
<param name="disable-v17" value="false"/>
<param name="ident" value="SpanDSP Fax Ident"/>
<param name="header" value="SpanDSP Fax Header"/>
<param name="spool-dir" value="$${temp_dir}"/>
<param name="file-prefix" value="faxrx"/>
<!-- <param name="t38-tx-reinvite-packet-count" value="100"/> -->
<!-- <param name="t38-rx-reinvite-packet-count" value="50"/> -->
</fax-settings>
</configuration>

Fax Settings Parameter Reference

ParameterPurposeAccepted ValuesDefault
use-ecmEnable ITU-T T.30 Error Correction Modetrue, falsetrue
verboseEnable verbose SpanDSP library loggingtrue, falsefalse
verbose-log-levelFreeSWITCH log level used for SpanDSP verbose outputFreeSWITCH log level string (e.g., INFO, DEBUG)DEBUG
disable-v17Disable V.17 modem support, limiting transfer to 9600 bps maximumtrue, falsefalse
enable-tepEnable Telephone Extension Processor preambletrue, falsefalse
enable-colourEnable colour fax supporttrue, falsefalse
enable-image-resizingEnable image rescaling compressiontrue, falsefalse
enable-colour-to-bilevelConvert colour images to bi-level on receivetrue, falsefalse
enable-grayscale-to-bilevelConvert grayscale images to bi-level on receivetrue, falsefalse
enable-t38Allow T.38 negotiation when the remote endpoint offers ittrue, falsetrue
enable-t38-requestActively request a T.38 re-INVITE after audio fax tone is detectedtrue, falsefalse
identLocal station identifier (TSI) transmitted to the remoteStringSpanDSP Fax Ident
headerPage header text printed on transmitted pagesStringSpanDSP Fax Header
spool-dirDirectory where received fax files are written when no filename is passed to rxfaxPath$${temp_dir}
file-prefixFilename prefix used for auto-generated received fax filenamesStringfaxrx
t38-tx-reinvite-packet-countNumber of audio packets to process before sending a T.38 re-INVITE on transmitInteger 0-999100
t38-rx-reinvite-packet-countNumber of audio packets to process before sending a T.38 re-INVITE on receiveInteger 0-99950

Receiving a Fax with rxfax

The rxfax application answers the call (if not already answered), switches the codec to linear PCM, and receives an incoming fax document, writing it to a TIFF file.

Syntax:

rxfax [/path/to/output.tif]

The filename argument is optional. When omitted, rxfax generates a filename automatically using the spool-dir and file-prefix configuration values in the format:

{spool-dir}/{file-prefix}-{session_count}-{timestamp}.tif

The channel variable fax_filename is set to the path of the written file after the application completes.

Minimal dialplan action:

<action application="rxfax" data="/var/spool/fax/inbound.tif"/>

With T.38 enabled and echo cancellation disabled:

<action application="disable_ec"/>
<action application="set" data="fax_enable_t38=true"/>
<action application="set" data="fax_ident=+15551234567"/>
<action application="set" data="fax_header=My Company Fax"/>
<action application="answer"/>
<action application="playback" data="silence_stream://2000"/>
<action application="rxfax" data="/var/spool/fax/inbound.tif"/>
<action application="hangup"/>

Sending a Fax with txfax

The txfax application transmits a fax document from a local TIFF file to the remote endpoint. The channel must be answered or in a bridged state before txfax is invoked; txfax does not answer the channel.

Syntax:

txfax /path/to/document.tif

The filename argument is required. The file must exist at the specified path. Page range selection is controlled by the channel variables fax_start_page and fax_end_page; there are no positional page-range arguments to the application.

Minimal dialplan action:

<action application="txfax" data="/var/spool/fax/outbound.tif"/>

With per-session overrides:

<action application="set" data="fax_ident=+15551234567"/>
<action application="set" data="fax_header=My Company - Page %p of %t"/>
<action application="set" data="fax_enable_t38=true"/>
<action application="set" data="fax_start_page=0"/>
<action application="txfax" data="/var/spool/fax/outbound.tif"/>
<action application="hangup"/>

T.38 Protocol Support

T.38 is the ITU-T standard for carrying fax signals over IP networks as UDPTL packets rather than audio. mod_spandsp supports two T.38 operating modes: negotiated T.38 within rxfax and txfax, and a dedicated T.38 gateway mode.

T.38 Negotiation via rxfax and txfax

When fax_enable_t38 is true (or enable-t38 is set in spandsp.conf.xml), the fax applications will accept a T.38 re-INVITE from the remote endpoint and switch from audio to UDPTL transport mid-call. The transition occurs transparently during the fax session.

When fax_enable_t38_request is true (or enable-t38-request is set globally), the module will itself issue a re-INVITE requesting T.38 after a configured number of audio packets. The packet count threshold is set by fax_t38_tx_reinvite_packet_count (for txfax) or fax_t38_rx_reinvite_packet_count (for rxfax).

When fax_enable_t38_insist is true, the re-INVITE or response is sent with a flag that causes FreeSWITCH to require T.38 and hang up the session if the remote endpoint declines.

The fax_t38_status result variable reports the outcome: negotiated, requested, refused, or off.

T.38 Gateway Mode

The t38_gateway application places a B2BUA leg into T.38 gateway mode, converting between audio fax signals on one leg and T.38 UDPTL on the other. This is used when one endpoint supports T.38 and the other does not.

Syntax:

t38_gateway [self|peer] [nocng]
ArgumentPurpose
selfApply gateway processing to the current (A) leg
peerApply gateway processing to the peer (B) leg (default)
nocngStart gateway mode immediately without waiting for fax tone detection

When nocng is not specified, t38_gateway listens for CED (2100 Hz) or V.21 preamble before activating gateway mode. The timeout for tone detection is controlled by the channel variable t38_gateway_detect_timeout (integer, seconds; default 20).

The channel variable t38_gateway_redundancy sets the UDPTL redundancy level used by the gateway (integer; default 3). The single value is applied to both the FEC span and the FEC entries count.

Example: inline gateway on a bridged call:

<action application="set" data="execute_on_answer=t38_gateway peer"/>
<action application="bridge" data="sofia/gateway/mycarrier/${destination_number}"/>

Channel Variable Reference

Channel variables override the global defaults from spandsp.conf.xml on a per-session basis. Input variables are read before the fax application begins. Result variables are set by the application when it completes.

Input Variables

VariablePurpose
fax_identOverrides the global ident setting for this session (station identifier transmitted to remote)
fax_headerOverrides the global header setting for this session (page header text)
fax_timezoneTimezone name used to format the page header timestamp
fax_use_ecmOverrides use-ecm for this session (true or false)
fax_disable_v17Disables V.17 for this session (true or false)
fax_enable_tepEnables TEP preamble for this session (true or false)
fax_enable_colourEnables colour fax for this session (true or false)
fax_enable_image_resizingEnables image rescaling for this session (true or false)
fax_enable_colour_to_bilevelEnables colour-to-bi-level conversion for this session (true or false)
fax_enable_grayscale_to_bilevelEnables grayscale-to-bi-level conversion for this session (true or false)
fax_enable_t38Enables T.38 negotiation for this session (true or false)
fax_enable_t38_requestEnables active T.38 re-INVITE for this session (true or false)
fax_enable_t38_insistRequires T.38; hangs up if remote declines (true or false)
fax_t38_tx_reinvite_packet_countAudio packet count before T.38 re-INVITE is sent during transmit (integer)
fax_t38_rx_reinvite_packet_countAudio packet count before T.38 re-INVITE is sent during receive (integer)
fax_spoolOverrides spool-dir for auto-generated filenames in rxfax
fax_prefixOverrides file-prefix for auto-generated filenames in rxfax
fax_start_pageFirst page to transmit (0-based index; txfax only)
fax_end_pageLast page to transmit (0-based index; txfax only; -1 means last page)
fax_force_callerForce the fax session to act as caller (true) or answerer (false) regardless of call direction
fax_verboseEnables per-session verbose SpanDSP logging (true or false)
fax_verbose_log_levelFreeSWITCH log level for per-session verbose output
fax_trace_dirDirectory path for writing a per-session SpanDSP trace log
t38_gateway_detect_timeoutSeconds to wait for fax tone before activating t38_gateway (integer; default 20)
t38_legSet by t38_gateway to record which leg gateway processing is applied to (self or peer, defaulting to peer).
t38_gateway_redundancyUDPTL redundancy level for t38_gateway mode (integer; default 3). The value is applied to both the FEC span and the FEC entries count.
t38_peerUUID of the peer leg used to pair the two sides of a t38_gateway session. Set automatically on both legs by t38_gateway.
t38_traceWhen true, traces the raw audio of a t38_gateway session to <temp_dir>/<uuid>_read.raw and <uuid>_write.raw.

Result Variables

The following variables are set on the channel when rxfax or txfax completes. They are available for subsequent dialplan actions or ESL event handlers.

VariablePurpose
fax_success1 if the fax completed without error; 0 otherwise
fax_result_codeNumeric T.30 completion code
fax_result_textHuman-readable description of the T.30 completion code
fax_filenamePath to the TIFF file written (receive) or read (transmit)
fax_document_transferred_pagesNumber of pages actually transferred in this session
fax_document_total_pagesTotal pages in the file (pages sent for txfax; pages written to TIFF for rxfax)
fax_image_resolutionLine resolution of the last transferred page in {x}x{y} meters-per-pixel format
fax_file_image_resolutionFile resolution of the last transferred page
fax_image_pixel_sizePixel dimensions of the last transferred page as {width}x{height}
fax_file_image_pixel_sizePixel dimensions as stored in the TIFF file
fax_image_sizeCompressed image size in bytes
fax_bad_rowsNumber of bad rows detected in the last page
fax_longest_bad_row_runLength of the longest run of consecutive bad rows
fax_encodingNumeric compression code for the last page
fax_encoding_nameString name of the compression type (e.g., T.6)
fax_transfer_rateNegotiated modem bit rate in bits per second
fax_ecm_usedon if ECM was used during the session; off otherwise
fax_ecm_requested1 if ECM was requested; 0 otherwise (in gateway mode the values are true and false respectively)
fax_t38_statusT.38 outcome: negotiated, requested, refused, or off
fax_v17_disabled1 if V.17 was disabled for this session; 0 otherwise
fax_local_station_idLocal station identifier used during this session
fax_remote_station_idRemote station identifier received from the far end
fax_remote_countryCountry reported by the remote fax device (set at T.30 phase B negotiation; empty string if not provided)
fax_remote_vendorVendor string reported by the remote fax device (set at T.30 phase B negotiation; empty string if not provided)
fax_remote_modelModel string reported by the remote fax device (set at T.30 phase B negotiation; empty string if not provided)
fax_trace_fileFull path to the per-session SpanDSP trace log file; set only when fax_trace_dir input variable is provided

Completion Hooks

mod_spandsp honors the standard execute_on_* and system_on_* channel-variable hooks at several points in the fax life cycle. The execute_on_* variables run a dialplan application (for example lua fax_result.lua); the system_on_* variables run a host command through system() after expanding channel variables. Set these as channel variables before invoking rxfax or txfax.

VariableWhen it firesAction type
execute_on_fax_phase_bAt T.30 phase B (after the initial transport negotiation, when station identifiers are exchanged)Dialplan app
execute_on_fax_phase_dAt T.30 phase D (after each page completes)Dialplan app
system_on_fax_resultWhen the fax session ends, regardless of outcomesystem() command
execute_on_fax_resultWhen the fax session ends, regardless of outcome (after system_on_fax_result)Dialplan app
system_on_fax_successWhen the fax session ends with a successful resultsystem() command
execute_on_fax_successWhen the fax session ends with a successful result (after system_on_fax_success)Dialplan app
system_on_fax_failureWhen the fax session ends with an error resultsystem() command
execute_on_fax_failureWhen the fax session ends with an error result (after system_on_fax_failure)Dialplan app

The result variables in section 6.2 are populated before these hooks run, so an execute_on_fax_result handler can read fax_success, fax_result_code, fax_document_transferred_pages, and fax_filename directly.


Default Dialplan Examples

The vanilla conf/vanilla/dialplan/default.xml includes two fax extensions that demonstrate minimal usage.

fax_receive (extension 9178)

Answers the call, plays two seconds of silence to allow the fax CED tone to stabilize, receives the fax to a temporary file, then hangs up.

<extension name="fax_receive">
<condition field="destination_number" expression="^9178$">
<action application="answer"/>
<action application="playback" data="silence_stream://2000"/>
<action application="rxfax" data="$${temp_dir}/rxfax.tif"/>
<action application="hangup"/>
</condition>
</extension>

fax_transmit (extension 9179)

Transmits a fax from a temporary file. The channel is in a bridged state when txfax is called; no explicit answer is required because the outbound bridge has already answered the session.

<extension name="fax_transmit">
<condition field="destination_number" expression="^9179$">
<action application="txfax" data="$${temp_dir}/txfax.tif"/>
<action application="hangup"/>
</condition>
</extension>

Practical Inbound Fax with T.38 and Result Logging

The following is a complete inbound fax extension using per-session overrides, T.38 support, and result handling via execute_on_fax_result.

<extension name="inbound_fax">
<condition field="destination_number" expression="^1234$">
<action application="set" data="fax_ident=+15551234567"/>
<action application="set" data="fax_header=ACME Corp"/>
<action application="set" data="fax_enable_t38=true"/>
<action application="set" data="fax_enable_t38_request=true"/>
<action application="set" data="execute_on_fax_result=lua fax_result.lua"/>
<action application="disable_ec"/>
<action application="answer"/>
<action application="playback" data="silence_stream://2000"/>
<action application="rxfax" data="/var/spool/fax/${uuid}.tif"/>
<action application="hangup"/>
</condition>
</extension>

After rxfax completes, FreeSWITCH executes the application defined in execute_on_fax_result. The result variables fax_success, fax_result_code, fax_document_transferred_pages, and fax_filename are available at that point.