I’ve spent many hours struggling over this until I found a suitable solution. And because I tend to forget I wrote this small HOWTO! Let’s begin…
T38Modem
This is actually one of the most compicated components to compile and setup, mainly because of all the scattered information out there on the matter. I have read documentation mentioned in http://www.voipinfo.ru/forum/viewtopic.php?t=11767 (in Russian) and http://www.voip-info.org/wiki/view/T38modem+configuration+with+Asterisk and I believe I have figured out a way to join all missing parts to get this to work.
Initially we should download all the required packages for t38modem to compile.
apt-get install cvs apt-get install g++ apt-get install flex apt-get install bison apt-get install libexpat1-dev apt-get install giflib-tools (You need this to be able to fax GIF files)
I have been struggling to make t38modem 1.1.0 to work and although I was successful in sending faxes, I haven’t yet managed to make it work for incoming faxes, probably because of the codecs used. So let’s proceed with the working version 1.0.
In order to compile T38modem 1.0 we need to download a CVS version of OPAL and PWLib that still has SIP support. So we issue the following commands:
cd /root cvs -z9 -d :pserver:anonymous@openh323.cvs.sourceforge.net:/cvsroot/openh323 co ptlib_unix cvs -z9 -d :pserver:anonymous@openh323.cvs.sourceforge.net:/cvsroot/openh323 co -D "5/21/2007 23:59:59" opal cvs -z9 -d :pserver:anonymous@openh323.cvs.sourceforge.net:/cvsroot/openh323 co t38modem
Note: It is wise to compile all parts of the t38modem inside the /root directory since compilation is using static declared paths to /root/opal and or /root/pwlib
We start off by compiling everything. If you are like me you probably hate breaking up the packaging of Debian so we will install everything under /opt. (From there you can easily create a Debian package as well if you so feel like it).
cd /root/pwlib ./configure --prefix=/opt/t38modem make make install cd /root/opal ./configure --prefix=/opt/t38modem make
If you come up against this error message:
gcc -I../../../include -fPIC -g -O2 -c h263ffmpeg.cxx -o obj/h263ffmpeg.o h263ffmpeg.cxx: In member function ‘bool DynaLink::InternalOpen(const char*, const char*)’: h263ffmpeg.cxx:288: error: ‘stderr’ was not declared in this scope h263ffmpeg.cxx:288: error: ‘fprintf’ was not declared in this scope h263ffmpeg.cxx: In member function ‘BOOL H263EncoderContext::OpenCodec()’: h263ffmpeg.cxx:962: warning: deprecated conversion from string constant to ‘char*’
Edit file opal/plugins/video/H.263-ffmpeg/h263ffmpeg.cxx and add around line 98 the following:
#include <stdio.h>Finally install the library by
make install
Now let’s move to the actual t38modem:
cd /root/t38modem make USE_OPAL=1 USE_UNIX98_PTY=1 opt
Since there is no configure script in t38modem and make install would throw the binary in /usr/local/bin we will just copy it ourselves:
cp obj_linux_x86_64_opal_r/t38modem /opt/t38modem/bin/
At this point we have compiled and installed t38modem successfully. If you want to add t38modem starting on startup (/etc/init.d/t38modem) you could use a script like this:
#!/bin/bash export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/opt/t38modem/lib/ # This is needed for t38modem to find the libraries T38_PROCESSES=5 START_PORT=6060 ASTERISK_IP=xx.xx.xx.xx BIND_IP=xx.xx.xx.xx case "$1" in start) COUNTER=0 CURRENT_PORT=${START_PORT} while [ $COUNTER -lt $T38_PROCESSES ]; do COMMAND="/opt/t38modem/bin/t38modem --no-h323 -u T38modem${COUNTER} --sip-listen udp$${BIND_IP}:${CURRENT_PORT} --sip-redundancy 3 --sip-old-asn --ptty +/dev/ttyT38-${COUNTER} --route modem:.*=sip:<dn>@${ASTERISK_IP} --route sip:.*=modem:<dn>" exec $COMMAND > /dev/null 2>&1 & PID=$! echo "Starting t38modem with pid $PID (pidfile /var/run/t38modem/$COUNTER)" echo $PID > /var/run/t38modem/$COUNTER COUNTER=$[ COUNTER + 1 ] CURRENT_PORT=$[ CURRENT_PORT + 1 ] done ;; stop) echo -n "Stopping t38modem pids: " for pidfile in `ls /var/run/t38modem` do _PID=`cat /var/run/t38modem/$pidfile` kill -9 $_PID rm /var/run/t38modem/$pidfile echo -n $_PID" " done echo " Done" ;; *) echo "Usage: $0 {start|stop}" >&2 exit 1 ;; esac exit 0
If you want to change the location of PID files change the /var/run/t38modem directory to whatever it suits you. Also set ASTERISK_IP to the IP of the asterisk server, and BIND_IP to the IP the t38modem will listen for incoming SIP connection. (This is used for incoming faxes only). Finally you set the T38_PROCESSES to the number of processes you want to start. Each of the process will create a t38modem with username T38modem-xx and will start listening to port 6060 and incrementing. The devices that will be created are /dev/ttyT38-xx
Asterisk 1.6
All the above tests have been made to asterisk 1.6.0.5. T38modem 1.0 when doing the initial INVITE to asterisk to perform an outgoing call in its SDP header adds only T.38 information and no audio codecs. This by default makes asterisk to reject the call because it cannot process it correctly. In order to fix this we need to apply this patch:
--- asterisk-1.6.0.5/channels/chan_sip.c 2008-12-03 16:13:27.000000000 +0200 +++ ../asterisk-1.6.0.5/channels/chan_sip.c 2009-01-28 12:05:28.000000000 +0200 @@ -1029,7 +1029,7 @@ #define T38FAX_RATE_14400 (1 << 13) /*!< 14400 bps t38FaxRate */ /*!< This is default: NO MMR and JBIG transcoding, NO fill bit removal, transferredTCF TCF, UDP FEC, Version 0 and 9600 max fax rate */ -static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600; +static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600| T38FAX_RATE_12000 | T38FAX_RATE_14400;; /*@}*/ /*! brief debugging state @@ -6701,10 +6701,12 @@ } } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1 && len > 0) || (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1 && len > 0) )) { - if (debug) - ast_verbose("Got T.38 offer in SDP in dialog %sn", p->callid); - udptlportno = x; - numberofmediastreams++; + if (t38action != SDP_T38_INITIATE || (p->owner && p->lastinvite)) { + if (debug) + ast_verbose("Got T.38 offer in SDP in dialog %sn", p->callid); + udptlportno = x; + numberofmediastreams++; + } } else ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %sn", m); if (numberofports > 1)
I haven’t tried any newer version of asterisk 1.6.x.x branch, I think this has been applied to trunk. So as you probably understand we need to compile asterisk ourselves:
wget http://downloads.asterisk.org/pub/telephony/asterisk/old-releases/asterisk-1.6.0.5.tar.gz tar xfvz asterisk-1.6.0.5.tar.gz patch -p0 < patch.diff # The above patch cd asterisk-1.6.0.5 ./configure --prefix=/opt/asterisk-1.6.0.5 make make install
(I think asterisk 1.6.0.5 has a tendency to ignore ./configuration prefix when doing install so you might need to move /var/spool/asterisk and /var/lib/asterisk to the path installed)
sip.conf configuration:
We need to add a peer for each one of the t38modems to sip.conf as well as globally enable t38pt_udptl and on each of the peers. We will create a generic options context to be used from all t38modems:
[t38modem-options](!) type = friend host = BIND_IP context = fax-out canreinvite = no disallow = all allow = g729 allow = ulaw allow = alaw t38pt_udptl = yes dtmfmode = rfc2833 nat = no qualify = yes [T38modem0](t38modem-options) port = 6060 [T38modem1](t38modem-options) port = 6061 [T38modem2](t38modem-options) port = 6062
Remember the BIND_IP configured on the t38modem startup script mentioned above? You need to set the same here so that asterisk can contact T38modems.
Also please note that you need to set canreinvite = no for t38modem to work. Additionally if the incoming calls arrive in G.729 you need to have a G.729 codec installed on your asterisk in order for T38modem to do a T.38 Re-Invite back.
extensions.conf configuration:
We need to setup a basic dialplan in order for incoming calls to be routed to t38modems and outgoing calls to be routed to the outside world:
[fax-in]
exten => _X.,1,NoOp("Incoming fax to ${EXTEN}")
exten => _X.,n(again),Set(AGAINCOUNTER=$[${AGAINCOUNTER} + 1]) ; in the loop: increase the call attempts counter
exten => _X.,n,GotoIf($[${AGAINCOUNTER} <= 20]?call:hangup) ; stop looping after 20 attempts
exten => _X.,n(call),Dial(SIP/${EXTEN}@T38modem${RAND(0,59)},20,gH) ; make the call to a random t38modem
exten => _X.,n,GotoIf($["${DIALSTATUS}" != "ANSWER"]?again:checktime) ; if it was not answered, call again
exten => _X.,n(checktime),GotoIf($[${ANSWEREDTIME} < 10]?again:hangup) ; if it was answered, but too short, call again
exten => _X.,n(hangup),Hangup() ; hangup
[fax-out]
exten => _X.,1,Dial(SIP/${EXTEN}@OUTSIDE-WORLD,60)
exten => _X.,n,Hangup()The fax-in context checks if a t38modem does not answer and retries a few times before giving up.
Hylafax 6.0
The easiest way to go is install the pre-compiled package provided by Aidan Van Dyk (aidan at ifax dot com). Edit your sources.list (/etc/apt/sources.list) to include the following:
deb http://code.highrise.ca/apt/
apt-get update apt-get install hylafax-server hylafax-client
Apt will complain about hylafax-server and hylafax-client not being verified but you can proceed without any worries. The debian package provided by Aidan Van Dyk does not contain AFM fonts used to render text files to PS. You can download them from ftp://ftp.hylafax.org/source/afm-tar.Z. If you need to see where to put it you can run gs -h and see the search paths at the end. Use one of those and add it in /etc/hylafax/hyla.conf in the FontMap option (delimeted with ‘:’)
We need to create configuration files for all t38modem instancies to our hylafax server. We create one file per t38modem instance under /etc/hylafax named config.ttyT38-xx containing:
# # HylaFAX configuration for a T38FAX Pseudo Modem # # these have been defined in config-common # SessionTracing: 0x2FFF # RingsBeforeAnswer: 1 # DynamicConfig: etc/DynamicConfig ModemType: Class1 # use class 1 interface ModemFlowControl: rtscts # default ModemRevQueryCmd: AT+FREV? # # AT#CID=10 enables ANI/DNIS reporting between the # first and second RINGs in the form: # # RING # NMBR = <calling number> # NDID = <called number> # RING # ModemResetCmds: "AT#CID=10" # enable ANI/DNIS reporting RingsBeforeAnswer: 2 # collect info between two RINGs #QualifyCID: etc/cid # CID access control list file #CIDNumber: "NMBR = " # pattern string for calling number #CIDName: "NDID = " # pattern string for called number CallIDPattern: "NMBR = " # pattern string for calling number CallIDPattern: "NDID = " # pattern string for called number # # T.38 dial modifiers # # F - enable T.38 mode request after dialing # V - disable T.38 mode request after dialing (remote host should do it) # # calling/called number dial modifiers # # L - reset and begin of calling number # D - continue of called number # #ModemDialCmd: ATDF%s # user can override F by dial V ModemDialCmd: ATDV%s # user can override V by dial F #ModemDialCmd: ATD%sF # user can't override F #ModemDialCmd: ATD%sV # user can't override V #ModemDialCmd: ATD%sVL # user can't override V or calling number
You can add additional options to each configuration file if you feel like it, you could refer to the man page of hylafax-config.
After that we need to restart hylafax so that the configuration files get populated to the location where hylafax expects them (/var/spool/hylafax):
/etc/init.d/hylafax restart
By default hylafax starts the faxgetty’s on startup, however if you want to start them during init you could add to /etc/inittab:
F1:23:respawn:/usr/sbin/faxgetty ttyT38-0 F2:23:respawn:/usr/sbin/faxgetty ttyT38-1 F3:23:respawn:/usr/sbin/faxgetty ttyT38-2 F4:23:respawn:/usr/sbin/faxgetty ttyT38-3
Or if you are using upstart (ubuntu users) create a file for each t38modem under /etc/event.d (named like the device e.g. ttyT38-0) like the following:
start on runlevel 2 start on runlevel 3 start on runlevel 4 start on runlevel 5 stop on runlevel 0 stop on runlevel 1 stop on runlevel 6 respawn exec /usr/sbin/faxgetty ttyT38-0
To reload init without rebooting type
init q
Or if you are using upstart
initctl start ttyT38-0 (and repeat for each of the t38modems you have)
Now if we do a faxstat -s we should see something along this lines:
faxstat -s HylaFAX scheduler on doom: Running Modem ttyT38-0 (): Running and idle Modem ttyT38-1 (): Running and idle Modem ttyT38-2 (): Running and idle
This means that everything is setup and ready to go. You can try to send a fax by doing:
sendfax -s a4 -m -n -D -S "Test Fax" -d file.pdf
If you feel there is a mistake anywhere please send me a note so that I can fix this.
Hey villalvilla,
Well your case is rather simple. You have an error in your asterisk dialplan. You need to create a context (like fax-out mentioned earlier) so that t38modems can dialout.
Hi mobius!
Thanks a lot for your quick answer.
I could fix it, but now problems are growing!
I spent 2 days with this. I use a T.38 termination service (t38faxing.com) that works fine, but my t38modem just sends at 1440 bpps. hylafax tries to sync at 4800 and then fallsdown to 2400 bpps.
t38modem’s log gives me this:
T38Modem Version 1.0.0
by OpenH323 Project on Unix Linux (2.6.26-2-amd64-x86_64)
2010/04/30 13:12:22.972 T38Modem Version 1.0.0 by OpenH323 Project on Unix Linux (2.6.26-2-amd64-x86_64) at 2010/4/30 13:12:22.972
Disabled H.323 protocol
Waiting for incoming SIP calls from udp$127.0.0.1:6062
Route table:
modem:.*=sip:@127.0.0.1
sip:.*=modem:
Call[1] from modem: to 34949217841, route to sip:34949217841@127.0.0.1
Open AudioModemMediaStream-Source-PCM-16 for Call[1]
Open OpalRTPMediaStream-Sink-G.711-uLaw-64k for Call[1]
2010/04/30 13:16:18.025 SIP Handle…r:dc002460 Patch Added sink
from PCM-16
Clock Rate = 8000
Frame Time = 8
Max Bit Rate = 64000
Max Frame Size = 16
Needs Jitter = 1
Rx Frames Per Packet = 240
Tx Frames Per Packet = 30
to G.711-uLaw-64k
Clock Rate = 8000
Frame Time = 8
Max Bit Rate = 64000
Max Frame Size = 8
Needs Jitter = 1
Rx Frames Per Packet = 240
Tx Frames Per Packet = 30
Open OpalRTPMediaStream-Source-G.711-uLaw-64k for Call[1]
Open AudioModemMediaStream-Sink-PCM-16 for Call[1]
2010/04/30 13:16:18.026 SIP Handle…r:dc002460 Patch Added sink
from G.711-uLaw-64k
Clock Rate = 8000
Frame Time = 8
Max Bit Rate = 64000
Max Frame Size = 8
Needs Jitter = 1
Rx Frames Per Packet = 240
Tx Frames Per Packet = 30
to PCM-16
Clock Rate = 8000
Frame Time = 8
Max Bit Rate = 128000
Max Frame Size = 16
Needs Jitter = 1
Rx Frames Per Packet = 240
Tx Frames Per Packet = 30
Close AudioModemMediaStream-Source-PCM-16
Close AudioModemMediaStream-Sink-PCM-16
Close OpalRTPMediaStream-Sink-G.711-uLaw-64k
Close OpalRTPMediaStream-Source-G.711-uLaw-64k
Open T38ModemMediaStream-Source-T.38-IFP-PRE for Call[1]
Open OpalRTPMediaStream-Sink-T.38-IFP-PRE for Call[1]
2010/04/30 13:16:22.840 SIP Handle…r:dc002460 Patch Added sink
from T.38-IFP-PRE
Max Bit Rate = 1440
to T.38-IFP-PRE
Max Bit Rate = 1440
Open OpalRTPMediaStream-Source-T.38-IFP-PRE for Call[1]
Open T38ModemMediaStream-Sink-T.38-IFP-PRE for Call[1]
2010/04/30 13:16:22.840 SIP Handle…r:dc002460 Patch Added sink
from T.38-IFP-PRE
Max Bit Rate = 1440
to T.38-IFP-PRE
Max Bit Rate = 1440
Close OpalRTPMediaStream-Sink-T.38-IFP-PRE
Close OpalRTPMediaStream-Source-T.38-IFP-PRE
Close T38ModemMediaStream-Source-T.38-IFP-PRE
Close T38ModemMediaStream-Sink-T.38-IFP-PRE
Call[1] cleared
Any Idea?
Thanks a lot again mobius, you are really helping me.
I’m composing a new article that fixes some issues in where you have mistakes.
When I fix this, we can share it if you want!
regards,
villalvilla
villalvilla,
You could try using t38modem 1.2.0. I’ve seen far greater results in outgoing faxes with 1.2.0. Also you should patch asterisk in order to add extra t38 capabilities, since by default (at least in the version mentioned in this howto) it does not go up to more that 7200
See:
+static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600| T38FAX_RATE_12000 | T38FAX_RATE_14400
Please if you have something extra to share, I will be delighted to add to this post
I would be interested in trying out the register feature of t38modem 1.2.0 with an OpenSIPS server instead of Asterisk when I get some time.
I think T.38 pass-through should work better with OpenSIPS, and you could have t38modems registered directly to it.
Mobius,
Thanks very much for putting this site together. I wish I had found it a week ago as it took me that long to get t38modem compiled on CentOS !!!
I finally have 1.2.1 compiled. My recommendation to all is to grab the latest trunk for ptlib, opal, and t38modem. That is what it took for me to be able to compile everything.
Now I move on to seeing how I can get t38modem to work with Hylafax.
Again, thanks for putting this site together. I am looking forward to hanging out here.
This was extremely helpful! For anyone who is having trouble with getting T38modem to go from “waiting to come ready”, I had to add a backslash in the command ” –sip-listen udp$${BIND_IP}:${CURRENT_PORT}” so that it looks like this: ” –sip-listen udp\$${BIND_IP}:${CURRENT_PORT}” in the /etc/init.d/t38modem file. Not sure if this is only for my specific case, but I did it on Ubuntu 10.04 server. Hope that helps anyone having trouble!
Hi, I’m having some trouble with
Asterisk 1.6.2.11 + t38modem 1.0.0 and Hylafax 4.4.3
I have 5 x t38 lines connecting to Gafachi SIP and they all send faxes successfully for about 20 minutes, then eventually everything comes to a halt with the modems outputing: “Waiting for modem to come ready”. I have to restart the system for Hylafax to continue processing the queue. But then eventually I must reboot again. All faxes are going through good otherwise.
Thanks, any help you can provide would be helpful. I don’t see any errors in any of the logs.
Bitronix,
I am guessing your issues is that your T38 modems are freezing. It’s common with t38modem 1.0. If you want t38modems only for outgoing faxes you should install t38modem 1.1.0
Otherwise, you could create a cron job and send AT commnads on the modem, if the modem does not respond restart it automatically
when i start the t-38 modem i get that :
/etc/init.d/t38modem: 46: COUNTER: not found
/etc/init.d/t38modem: 46: CURRENT_PORT: not found
Starting t38modem with pid 13639 (pidfile /var/run/t38modem/0)
/etc/init.d/t38modem: 46: COUNTER: not found
/etc/init.d/t38modem: 46: CURRENT_PORT: not found
Starting t38modem with pid 13640 (pidfile /var/run/t38modem/0)
/etc/init.d/t38modem: 46: COUNTER: not found
/etc/init.d/t38modem: 46: CURRENT_PORT: not found
Starting t38modem with pid 13641 (pidfile /var/run/t38modem/0)
/etc/init.d/t38modem: 46: COUNTER: not found
/etc/init.d/t38modem: 46: CURRENT_PORT: not found
Starting t38modem with pid 13642 (pidfile /var/run/t38modem/0)
/etc/init.d/t38modem: 46: COUNTER: not found
/etc/init.d/t38modem: 46: CURRENT_PORT: not found
Starting t38modem with pid 13643 (pidfile /var/run/t38modem/0)
/etc/init.d/t38modem: 46: COUNTER: not found
/etc/init.d/t38modem: 46: CURRENT_PORT: not found
/etc/init.d/t38modem: 46: Cannot fork
Hi Lawrence,
Check if /bin/sh is actually /bin/bash in your distribution and not /bin/ash or anything else. I’ve updated the post to mention /bin/bash instead of /bin/sh cause I think someone else had the same error before!
Hi,
I’ve followed your howto, but running make install for t38modem gives the following error:
make: *** No rule to make target `/root/openh323/openh323u.mak’. Stop.
The solution is simply to checkout, configure, make and make install openh323. You can use the following commands:
cd /root
cvs -z9 -d :pserver:anonymous@openh323.cvs.sourceforge.net:/cvsroot/openh323 co openh323
cd openh323
./configure
make
make install
finally running make install for t38modem has worked for me.
thanks for this great post!
Huge work!!
I have really liked it, very clear and concise.
I’m going to build a script from this, I’ll mention you in the code.
Thank you!!
Ramoncio,
I am glad you found it useful! By all means if you create a script send me a link to add it to the post
Hi, thanks for the informative post! I have finally gotten ptlib, opal, and t38modem compiled and am now getting an error when I run export LIBDIR=/opt/t38modem/lib/ then /opt/t38modem/bin/t38modem:
First:
error loading avcodec – avcodec: cannot open shared object file: No such file or directory
error loading libavcodec.so – /usr/local/lib/libavcodec.so: undefined symbol: lzo1x_decode
error loading avcodec – avcodec: cannot open shared object file: No such file or directory
error loading libavcodec.so – /usr/local/lib/libavcodec.so: undefined symbol: lzo1x_decode
Then:
t38modem: malloc.c:4630: _int_malloc: Assertion `(unsigned long)(size) >= (unsigned long)(nb)’ failed.
Any ideas? I have compiled all the versions exactly as shown in the post.
Thanks!