Skip to end of metadata
Go to start of metadata

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

  • No labels

16 Comments

  1. Whoever wrote this is clearly awesome.

    1. Based on the page history, it looks like I wrote it. I am pretty awesome.

      (Kevin totally wrote this. I imported it.)

      1. You should have a duel to see who is more awesome.

  2. Here are some of my thoughts on the tools necessary to create a T.38 Gateway.

    • Overview of the problem as I understand it

    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.

    • API required to facilitate this

    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.

    • Future uses of new API

    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

    1. 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 the res_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 knows res_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)

      • Allow modification of an audio stream in both directions (for muting)
      • Allow consumption of an audio stream
      • Allow consumption of a T.38 frame stream
      • Allow consumption of CONTROL frames
      • ...

      Requirements for T.38 Gateway satisfied by existing APIs:

      • Produce a stream of audio or T.38 frames
        • provided by the generator API
      • Respond to CONTROL frames with CONTROL frames
        • provided by ast_queue_frame()
      • ...
  3. 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:

      • ast_queue_frame(): A Tech Pvt can invoke the ast_queue_frame() function to queue a frame on the channel to later be read by ast_read(). Because there is an alert pipe associated with this queue, any interception or manipulation of the frames entering the read queue must occur before the frames actually enter the queue. This is important for applications such as app_dial which listen to this alert pipe to become aware of when a channel is ready to be read. If an application wishes to block a frame from entering the queue for any reason, this would only be safe to do here. Once a frame has entered the queue, it must be delivered untouched in ast_read().
      • ast_read(): This function is the trickiest of the 4 to hook into because ast_read may pull a frame from multiple places. The frame can be either pulled from the read queue, or from the Tech Pvt's read function. Since a hook has already been established before frames can enter the read queue, any frames being pulled from the there can NOT be hooked into during this function. In ast_read the hook must act only on frames received from the Tech Pvt's read function.
    • Write Direction Hook Locations:

      • ast_write(): This one is simple. The hook is placed before any of the Tech Pvt's write functions are invoked.
      • ast_indicate(): Similar to ast_write(), the hook must be placed immediately before the Tech Pvt's indicate function is called.

    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.

    • Ability to listen to FAX preamble generated by called endpoint: This can be achieved by using the AwesomeHook API to view the audio stream coming from a specific direction on a channel.
    • Ability to generate silence in both directions on a channel: This can be achieved in two ways. One way is to use the Awesomehook API to manipulate the media streams in both directions to only contain silence for a period of time. The other is by using the Generator API's ast_channel_start_silence_generator() and ast_channel_stop_silence_generator() functions. Using the Generator API would require the T.38 gateway to maintain generators on both channels involved in the bridge while using the AwesomeHook API would allow the both streams to be silenced using the already existing hooks on a single channel.
    • Ability to send T.38 negotiation requests and intercept the response: This can be achieved by using Channel API's ast_write() and ast_queue_frame() functions to send the T.38 negotiation request in the appropriate direction on a channel, and the AwesomeHook API to intercept and process the response.
    • Ability to intercept T.38 negotiation frames and respond: This can be achieved using the AwesomeHook API to intercept the T.38 negotiation request and the ast_write and ast_queue_frame Channel API functions to send the response in the appropriate direction.
    • Ability to consume T.38 frames and generate audio frames from the T.38 data via the FAX stack: This requirement is slightly more complex as it will require the use of both the AwesomeHook API and the Channel API. T.38 frames must be able to be consumed and blocked in a given direction on a channel while audio frames are later generated from the T.38 frames' data via the FAX stack. Since there is not necessarily a one-to-one relationship between the T.38 frames and the audio frames, some form of audio generation must exist independent of the consumption of the T.38 frames. Adding to this complication is the fact that this conversion can take place in either direction, meaning that it may either be the Tech PVT generating the T.38 frames which need to be converted to audio frames for the bridged channel to read, or the bridged channel generating the T.38 frames which need to be converted to audio frames for the Tech PVT to read. The cleanest way I can come up with to deal with this conversion is a combination of use of the AwesomeHook API to consume the T.38 frames and feed them into the Fax Stack, and a custom T.38 Gateway application specific frame generator using either the ast_write() or ast_queue_frame() Channel API functions (depending on the direction of the conversion) for sending the audio frames from the FAX stack as they become available. I do not know the exact relation ship of the conversion between T.38 frames and audio frames. It is very likely that the audio generator will have to perform some sort of buffering and timing to guarantee a FAX capable media stream.
    • Ability to consume audio fames and generate T.38 frames from the audio data via the FAX stack: Take what I said regarding the generation of audio frames from T.38 frames and reverse it. The relationship and tools required to achieve these two types of conversions is the same.
    • Ability to revert to audio mode after T.38 session is complete: This can be achieved by using the Channel API to send the appropriate T.38 negotiation request in the necessary direction. The AwesomeHook API can be used to intercept the response to know if the reversion failed or not.
    1. Comments/Questions:

      1. Regarding ast_queue_frame() as a hook location, I'm not sure that this is necessary. I think hooking into ast_read() only should be good enough. You are right that the alert pipe will trigger ast_read(), but if you don't want to actually return the frame that was put in the queue, you can return &ast_null_frame, instead.
      2. Good call on needing to hook into ast_indicate(). I didn't think about that.
      3. You mention that ast_write() and ast_queue_frame() could be used for handling T.38 control frame negotiation. Does the fact that you have also proposed these as interception points make this more complicated?
      4. I am in agreement on the method of doing the actual gateway operation. I think the combination of the ability to consume a stream and the existing generator API should suffice.
      1. 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.

        1. 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.

          1. 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.

            1. 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.

  4. 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?

    1. Howdy,

      You should take this to the asterisk-users mailing lists: http://lists.digium.com

      1. 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.

        1. 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.

  5. 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.