Functional Requirements
Use case 1:
(A) Calling T.38 endpoint <--> (B) Channel Driver 1<--> (C) Asterisk Core <--> (D) T.38 Gateway Object <--> (E) Channel Driver 2<--> (F) Called T.30 endpoint
In this use case, the calling endpoint is T.38 equipped and the called endpoint is not (supports T.30 only); in addition, the calling endpoint is properly configured to not initiate T.38 re-INVITEs itself, but relies on the receiving gateway (Asterisk+T.38 Gateway) to do so.
during call setup, called endpoint is ringing, gateway does nothing
when called endpoint has answered, gateway listens for FAX preamble (using V.21 detector) generated by called endpoint
if preamble is not detected after 15 seconds from call answer, gateway removes itself from the audio path (this may need to be optional)
if preamble is detected, gateway mutes audio path in both directions (generating silence towards both endpoints). gateway waits one second, then constructs T.38 negotiation request as a control frame, using parameters previously set via FAXOPT() dialplan function, and sends control frame to core
if T.38 negotiation fails (times out, or is rejected), gateway removes itself from the audio path (thus clearing the 'mute' state in both
directions)
if T.38 negotiation succeeds (request is accepted), gateway constructs T.38 session in FAX stack, feeds it negotiated parameters, and starts
session; if this fails, gateway causes channel(s) to be hung up and reports appropriate errors
while T.38 session is active, gateway receives T.38 media frames from calling endpoint and passes them to FAX stack; gateway receives T.38
media frames from FAX stack and sends them to calling endpoint. gateway also receives audio frames from called endpoint and passes them to FAX
stack, and receives audio frames from FAX stack and sends them to called endpoint
when T.38 session is complete, gateway sends T.38 negotiation request to revert to audio mode
if audio mode reversion fails, gateway causes channel(s) to be hung up
if audio mode reversion succeeds, gateway removes itself from audio path
Use case 2:
(A) Calling T.38 endpoint <--> (B) Channel Driver 1 <--> (C) Asterisk Core <--> (D) T.38 Gateway Object <--> (E) Channel Driver 2 <--> (F) Called T.30 endpoint
In this use case, the calling endpoint is T.38 equipped and the called endpoint is not (supports T.30 only); in addition, the calling endpoint
is improperly configured to initiate T.38 re-INVITEs itself, not waiting for the receiving gateway to do so.
during call setup, called endpoint is ringing, gateway does nothing
when called endpoint has answered, gateway listens for FAX preamble (using V.21 detector) generated by called endpoint
if preamble is not detected after 15 seconds from call answer, gateway removes itself from the audio path (this may need to be optional)
if preamble is detected, gateway mutes audio path in both directions (generating silence towards both endpoints). gateway waits one second,
and during this time a T.38 negotiation request is received from the calling endpoint; gateway processes this request using parameters
previously set using FAXOPT() dialplan function
if the T.38 negotiation request cannot be accepted, gateway sends a T.38 negotiation failure response, and removes itself from the audio path
(thus clearing the 'mute' state in both directions)
if T.38 negotiation succeeds (request can be accepted), gateway sends a T.38 negotiation success response, constructs T.38 session in FAX stack,
feeds it negotiated parameters, and starts session; if this fails, gateway causes channel(s) to be hung up and reports appropriate errors
while T.38 session is active, gateway receives T.38 media frames from calling endpoint and passes them to FAX stack; gateway receives T.38 media frames from FAX stack and sends them to calling endpoint. gateway also receives audio frames from called endpoint and passes them to FAX
stack, and receives audio frames from FAX stack and sends them to called endpoint
when T.38 session is complete, gateway sends T.38 negotiation request to revert to audio mode (this may be received from calling endpoint first,
though)
if audio mode reversion fails, gateway causes channel(s) to be hung up
if audio mode reversion succeeds, gateway removes itself from audio path
Use case 3:
(A) Calling T.30 endpoint <--> (B) Channel Driver 1 <--> (C) Asterisk Core <--> (D) T.38 Gateway Object <--> (E) Channel Driver 2 <--> (F) Called T.38 endpoint
In this use case, the calling endpoint is not T.38 equipped but the called endpoint is. In addition, the called endpoint is improperly configured to rely on the calling endpoint to initiate T.38 re-INVITEs.
during call setup, called endpoint is ringing, gateway does nothing
when called endpoint has answered, gateway listens for FAX preamble (using V.21 detector) generated by called endpoint
if preamble is not detected after 15 seconds from call answer, gateway removes itself from the audio path (this may need to be optional)
if preamble is detected, gateway mutes audio path in both directions (generating silence towards both endpoints). gateway waits one second, then constructs T.38 negotiation request as a control frame, using parameters previously set via FAXOPT() dialplan function, and sends control frame to called endpoint
if T.38 negotiation fails (times out, or is rejected), gateway removes itself from the audio path (thus clearing the 'mute' state in both
directions)
if T.38 negotiation succeeds (request is accepted), gateway constructs T.38 session in FAX stack, feeds it negotiated parameters, and starts
session; if this fails, gateway causes channel(s) to be hung up and reports appropriate errors
while T.38 session is active, gateway receives T.38 media frames from called endpoint and passes them to FAX stack; gateway receives T.38
media frames from FAX stack and sends them to called endpoint. gateway also receives audio frames from calling endpoint and passes them to FAX
stack, and receives audio frames from FAX stack and sends them to calling endpoint
when T.38 session is complete, gateway sends T.38 negotiation request to revert to audio mode
if audio mode reversion fails, gateway causes channel(s) to be hung up
if audio mode reversion succeeds, gateway removes itself from audio path
Use case 4:
(A) Calling T.30 endpoint <--> (B) Channel Driver 1 <--> (C) Asterisk Core <--> (D) T.38 Gateway Object <--> (E) Channel Driver 2 <--> (F) Called T.38 endpoint
In this use case, the calling endpoint is not T.38 equipped but the called endpoint is. In addition, the called endpoint is properly configured to initiate T.38 re-INVITEs itself.
during call setup, called endpoint is ringing, gateway does nothing
when called endpoint has answered, gateway listens for FAX preamble (using V.21 detector) generated by called endpoint
if preamble is not detected after 15 seconds from call answer, gateway removes itself from the audio path (this may need to be optional)
if preamble is detected, gateway mutes audio path in both directions (generating silence towards both endpoints). gateway waits one second, and during this time a T.38 negotiation request is received from the called endpoint; gateway processes this request using parameters previously set using FAXOPT() dialplan function
if the T.38 negotiation request cannot be accepted, gateway sends a T.38 negotiation failure response, and removes itself from the audio path
(thus clearing the 'mute' state in both directions)
if T.38 negotiation succeeds (request can be accepted), gateway sends a T.38 negotiation success response, constructs T.38 session in FAX stack,
feeds it negotiated parameters, and starts session; if this fails, gateway causes channel(s) to be hung up and reports appropriate errors
while T.38 session is active, gateway receives T.38 media frames from called endpoint and passes them to FAX stack; gateway receives T.38
media frames from FAX stack and sends them to called endpoint. gateway also receives audio frames from calling endpoint and passes them to FAX
stack, and receives audio frames from FAX stack and sends them to calling endpoint
when T.38 session is complete, gateway sends T.38 negotiation request to revert to audio mode
if audio mode reversion fails, gateway causes channel(s) to be hung up
if audio mode reversion succeeds, gateway removes itself from audio path
16 Comments
Kevin P. Fleming
Whoever wrote this is clearly awesome.
Russell Bryant
Based on the page history, it looks like I wrote it. I am pretty awesome.
(Kevin totally wrote this. I imported it.)
Joshua C. Colp
You should have a duel to see who is more awesome.
David Vossel
Here are some of my thoughts on the tools necessary to create a T.38 Gateway.
From a high level, what we want here is a gateway app that sits on a channel and does whatever the translation is from T.38 to T.30 and T.30 to T.38. To accomplish this, this app needs the ability to watch a channel and immediately take control once it detects a fax is coming through. When the fax is detected it must engage by faking that the channel matches the protocol used by the bridge channel on the Asterisk core side of things, and faking the bridged channel is using the same protocol as the tech_pvt on the channel driver side of things. For this translation to take place, the app must be capable of manipulating the signal and media streams in both directions on a channel. This would allow for T.38 frames to magically turn into audio for T.30 and vise versa.
As far as tools go that need to be implemented for this to be possible, it seems to me that we need a way for a module to hook into a channel's signal and media streams similar to the way audiohook works with voice frames. The main difference here being that this new hook API will allow the app to view everything read or written on a channel. It should also allow the app to manipulate those frames in any way it wants. This means a T.38 frame can be read from a channel, the hook can intercept that T.38 frame and turn it into a VOICE frame before it ever leaves ast_read(). The same sort of interception and manipulation should be possible in the ast_write() direction as well. This allows for a sleeper app like T.38 gateway to silently listen to a channel and take/release control as needed in a way that is completely transparent and independent to the rest of Asterisk.
I'm going to ahead and refer to these new way of hooking into a channel as the awesomehook API.
Using the awesomehook API, apps can view ALL media streams and translate/manipulate those steams in a way that makes the most sense for them, guaranteeing the highest quality possible. This immediately solves the issue of 8khz limited audiohooks for future development, and since this is not limited to just audio streams this has some really cool implications for other types of streams. This would allow for video streams to be manipulated, meaning things like adding text over a video stream would be possible. Video streams can be injected into a call allowing for things like a video conference bridge to switch video streams on the fly, possibly even independently of the audio. This would make recording ALL present and future media steams in the same way MixMonitor works for audio trivial. Text streams could be hooked into and translated to different languages in real-time. New gateways like the T.38 gateway could be created to translate between protocols I can't even think of yet.
I think awesomehooks are awesome, and it would be a another step in the right direction of expanding Asterisk beyond its primary use as only a telephone system for old school 8khz audio. Please let me implement the awesomehooks.
~Vossel
Russell Bryant
I think you're on the right track with this. One thing that you said that caught me was talking about converting to and from audio from within
ast_read()
. While this would be ideal, I'm afraid that theres_fax
API will not support this exactly. I think that the frames of the other type are not going to come back synchronously. It may even be some period of time before frames of the other type can be delivered. For this to work, I think it might have to be implemented as a combination of an awesomehook and a generator. We may need to consult with someone that knowsres_fax
in more detail to confirm this behavior.I think the next step needs to be to define some specific requirements for the API, and then its design (define the API, but not yet implement). For the requirements of the API, they should be derived from the T.38 gateway operation use cases. Go through each use case and identify exactly what operations need to be supported. You've done this a bit already in this writeup. With these requirements in place, we should be able to easily map each of them to a feature of the API you have defined. As long as we have the requirements right, then we will know the API is sufficient.
For example, what I have in mind for documenting the API requirements is something like:
Awesomehook API requirements (as derived from T.38 Gateway operating modes)
Requirements for T.38 Gateway satisfied by existing APIs:
ast_queue_frame()
David Vossel
Before I begin mapping out how I believe each of the T.38 gateway requirements map to various APIs, a more in depth look at exactly how the AwesomeHook API will behave is necessary.
AwesomeHook API high level overview of operation
The AwesomeHook API is ultimately a layer between the Channel API and the Tech Pvt. Because of this, the AwesomeHook API must live within the Channel API and act as a intermediate step between communications from the Tech Pvt to the Channel API, and communications from the Channel API to the Tech Pvt. This communication occurs in 4 places within the Channel API, ast_queue_frame(), ast_read(), ast_write(), and ast_indicate(). Below I have outlined from a high level how the AwesomeHook API fits into each of these places.
AwesomeHook API places of frame interception
Read Direction Hook Locations:
Write Direction Hook Locations:
T.38 Gateway requirements mapped to the AwesomeHook API and existing Asterisk APIs.
Now that the AwesomeHook API has been outlined in more concrete terms, I'd like to discuss how the T.38 Gateway application will interact with this API and other already existing APIs to achieve it's high level requirements. I have outlined the T.38 Gateway's requirements as I understand them below. Under each requirement I have made notes of how I believe the requirement can be achieved.
Russell Bryant
Comments/Questions:
David Vossel
In regards to item (3.), I thought about this. It shouldn't cause complications as the frames being pushed to those functions won't be the same type as the ones filtered in that direction. They will pass through the callbacks, but should be ignored.
David Vossel
The callback could be called recursively because of this though. For instance if a frame is intercepted in ast_write, then ast_write is called again within the callback, the callback will get called again within that, which is kind of weird.
Russell Bryant
Yeah, I think it will end up getting called recursively, but I suppose that's okay. The hook will have an internal state machine to know what frames it's supposed to be doing what with, so it will just ignore them as appropriate, like you said. The fact that it will end up getting called recursively will probably require some specific attention with regard to locking (trying to avoid abusing recursive locks), but should be okay.
Russell Bryant
I have included a picture of the sequence diagram we were doing on the whiteboard as we went through how the gatewaying operating would work using this approach.
Roman Nikiforov
Hello everyone.
I've just tryed the following:
fax <-- T.38 SIP alaw --> * < – T.30 SIP alaw --> fax
and it didnt passed.
Here is the sip.conf peers:
205test ;; this one is t.30 fax peer
username=205test
secret=205test
callerid=83912745000
type=friend
call-limit=1
allowtransfer=yes
insecure=port,invite
qualify=yes
t38pt_udptl=no
canreinvite=no
nat=yes
host=dynamic
dtmfmode=rfc2833
context=mobilon
disallow=all
allow=alaw
fortest2 ;; this one is t.38 fax peer
username=fortest2
secret=test111
callerid=83912745000
type=friend
call-limit=1
allowtransfer=yes
insecure=port,invite
qualify=yes
t38pt_udptl=yes
canreinvite=no
nat=yes
host=dynamic
dtmfmode=rfc2833
context=mobilon
disallow=all
allow=alaw
this is extensions.conf:
mobilon
exten => 101,1,Set(FAXOPT(gateway)=yes)
exten => 101,n,Dial(SIP/[email protected],,t)
exten => 101,n,Hangup()
and here is the console output (fax set debug on):
== Using SIP RTP CoS mark 5
– Executing [email protected]:1 Set("SIP/fortest2-00000033", "FAXOPT(gateway)=yes") in new stack
– Executing [email protected]:2 Dial("SIP/fortest2-00000033", "SIP/[email protected],,t") in new stack
== Using SIP RTP CoS mark 5
– Called SIP/[email protected]
– SIP/205test-00000034 is ringing
– SIP/205test-00000034 is ringing
– SIP/205test-00000034 answered SIP/fortest2-00000033
== Spawn extension (mobilon, 101, 3) exited non-zero on 'SIP/fortest2-00000033'
either I did something wrong or this scheme is not suppose to work?
Malcolm Davenport
Howdy,
You should take this to the asterisk-users mailing lists: http://lists.digium.com
Roman Nikiforov
Sorry, didnt quite understand. To take what? There is just a mailing list, not a problem solving forum.
By the way - is there any major differences between this T.38 Gateway and Attrafax gateway.so module?
P.S. Thx for the picture, Russell. Incredible job.
Malcolm Davenport
The wiki here isn't a problem solving forum, noting your comment below, also. Problem solving is better left to the asterisk-users mailing list, the IRC channel, or the forums.
Roman Nikiforov
Just have tested it like its in a picture:
SIP <-- g726.t38 > * <- DAHDI
and got following errors:
24:42 ERROR1442: res_fax.c:838 fax_session_reserve: Could not locate a FAX technology module with capabilities (0x20)
24:42 ERROR1442: res_fax.c:2424 fax_gateway_new: Can't reserve a FAX session, gateway attempt failed.
24:42 ERROR1442: res_fax.c:3510 acf_faxopt_write: Error attaching T.38 gateway to channel SIP/fortest2-00000004.