CAF: Working With Caf Call Legs
Contents |
Overview
CAF Call leg (CTBCAFCallLeg):
The CTBCAFCallLeg (based on CTBCMCLeg base class) is the representation of communication with one peer, either through one protocol (SS7, ISDN, SIP, CAS) or media-only.
CTBCAFCallLeg objects can be created either after the toolpack_engine has notified the CAF application that a new call has been received, or else when the CAF application asks toolpack_engine to create a new outgoing call. When creating a new outgoing call, attributes are provided to indicate toolpack_engine which NAP to make the call to (which also defines the signaling protocol), which calling/called numbers to use, etc.
From the C++ point of view, the CTBCMCLeg contains most of the APIs require to manipulate call legs, while the CTBCAFCallLeg class is providing the interface to attach call legs to a parent CTBCAFCallFlow object
CAF Mixer (CTBCAFMixer):
Although the CAF Call Legs (CTBCAFCallLeg) contain the necessary functionality to be joined two by two, it is sometimes necessary, rather than have audio flowing directly between two legs, to mix audio from multiple call legs or sources. That's when the CTBCAFMixer object is used.
The CTBCAFMixer (based on CTBCMCMixer base class) is the representation of a DSP resource used to mix audio from different sources (call legs, stream server, or other mixers).
From the C++ point of view, the CTBCMCMixer contains most of the APIs require to manipulate mixers, while the CTBCAFMixer class is providing the interface to attach mixers to a parent CTBCAFCallFlow object.
The CTBCAFMixer object can be used for:
- Conferences
- Background music playing on a call
- Recording both call legs of a call into one mixed audio file
- etc.
See more information on mixers here: [Audio Mixers]
CAF Call Flow (CTBCAFCallFlow):
The CTBCAFCallFlow base class contains one or multiple CTBCAFCallLeg objects, and optionally one or multiple CTBCAFMixer objects. The role of the CTBCAFCallFlow object is to implement a call flow that involves actions on legs and mixers that have a relation with each other.
CAF Call Bridge (CTBCAFBridge, based on CTBCAFCallFlow):
A typical implementation of a call flow is the CTBCAFBridge class (based on the CTBCAFCallFlow base class). This class is designed to - Receive an incoming call - Make a corresponding outgoing call - Join them together - Terminate both of them when one leg hangups.
CAF Call Behavior (CTBCAFCallBehavior):
Behaviors CTBCAFCallBehavior can be attached to a call flow (CTBCAFCallFlow). Their role is to modify the basic call flow, decorating it with optional functionality (CDR logging, FAX detection, Ring tone playback, etc.)
In order to implement their functionality, behavior objects, like their parent call flow object, receive events from all call legs and mixers of the call flow. They can also manipulate call legs and mixers.
Actions on call legs
The call flow (and it's behaviors) can perform various actions on each of it's call legs. Here is a list of the actions available on the CTBCAFCallLeg objects (please refer to the header file CTBCMCLeg.hpp for more information on each of these functions):
Actions on mixers
The call flow (and it's behaviors) can perform various actions on each of it's mixers. Here is a list of the actions available on the CTBCAFMixer objects (please refer to the header file CTBCMCMixer.hpp for more information on each of these functions):
Events received from call legs by the CTBCAFCallFlow object
The call flow object (as well as all it's behaviors) receive the following events from it's call legs (please refer to the header file ITBCAFCallFlow.hpp for more information on each of these functions):
[Call flow events from it's call legs]
Events behavior chain
All the events describe above are called consecutively on each behavior attached to the call flow, and ultimately, called on the call flow itself.
Each behavior in the chain can
- Pass the event to the next behavior in the chain
- Consume the event so other behaviors in the chain and the call flow are unaware that this event occurred
The CTBCAFCallBehavior default implementation is to forward the event to the next event in the chain.
When you implement your own behaviors, you can override only a couple of events (like OnCallLegAnswered() for example) to perform specific tasks, and leave all other events to the base class, so you don't have to write a lot of code to create a new behavior.
Creating the CTBCAFCallFlow object and it's legs
Steps to create a new call flow are:
- Create a new object (using a class derived from CTBCAFCallFlow class)
- Attach behaviors to the call flow
- Add attributes of incoming and outgoing legs to create
- Tell the call flow object to initialize itself, and create the call legs as requested
Here is an example:
ITBCAFCallFlow* pCallFlow; ITBCAFCallFlow* pParentCallOrBehavior; // Create the call flow object (here using the CTBCAFBridge class, based on CTBCAFCallFlow) pCallFlow = tbnew CTBCAFBridge( 0, this); // Optionally, attach behaviors pParentCallOrBehavior = pCallFlow; pParentCallOrBehavior = tbnew CTBCAFCallBehaviorBusyTone( pParentCallOrBehavior, ... ); pParentCallOrBehavior = tbnew CTBCAFCallBehaviorBridgeCdr( pParentCallOrBehavior, ... ); // Add an incoming call leg to that call flow (CTBCAFCallLeg object will get created based on provided attributes) pCallFlow->AddIncoming( in_LegId, ptrIncomingLegAttribute, ptrIncomingLegProtocolAttributes, ptrAcceptCallProtAttribute ); // Add an outgoing call leg pCallFlow->AddOutgoing( ptrOutgoingLegAttribute, ptrOutgoingLegProtocolAttributes ); // Initialize the call (this actually creates the call legs) pCallFlow->InitCall( &pCallFlow );
For a more complete code example, please refer to the simple_call application, in file CTBS2GWSimpleCall.cpp file, or here:
Constructing leg attributes
Incoming call leg attributes
To create the incoming call leg, the following information is required:
- Smart pointer to incoming call leg attributes, received from toolpack_engine through OnCallLegPresent().
ptrIncomingLegAttribute = tbnew CTBCMC_CALL_LEG_ATTRIBUTE( *(in_CallLegAttribute.GetCallLegAttribute()) );
- Smart pointer to incoming call leg attributes, received from toolpack_engine through OnCallLegPresent().
ptrIncomingLegProtocolAttributes = tbnew CTBCMC_PROTOCOL_ATTRIBUTE( in_ProtocolAttribute, TBX_TRUE );
- Smart pointer to empty protocol attributes that Behaviors can fill, and that will be used when accepting the call
Outgoing call leg attributes
To create the outgoing call leg, the following information is required:
- Smart pointer to outgoing call leg attributes to use to create the call, generally derived from the incoming call attributes
ptrOutgoingLegAttribute = tbnew CTBCMC_CALL_LEG_ATTRIBUTE(); ptrOutgoingLegAttribute->CopyLegAttributeFrom( *(ptrIncomingLegAttribute.Get()) );
- Smart pointer to empty protocol attributes that Behaviors can fill, and that will be used when creating the outgoing call (SIP invite, ISUP IAM, ISDN Setup, etc.)
Call leg initialization callbacks
All behaviors attached to the call flow will be notified of the creation of new call legs through callback OnInitIncomingCallLeg or OnInitOutgoingCallLeg.
OnInitIncomingCallLeg()
In this callback, each behavior may consult the incoming call attributes or protocol attributes, and can modify the protocol attributes that will be used to accept the call (actually providing protocol-specific information, like SS7 IEs, or SIP headers).
OnInitOutgoingCallLeg()
In this callback, each behavior can modify the protocol attributes that will be used to create the call (actually providing protocol-specific information, like SS7 IEs, or SIP headers that will be used in the initial signaling message, as SIP Invite, ISUP IAM, or ISDN Setup).
Destruction the CTBCAFCallFlow object and it's legs
Self-destructing call legs, behaviors, and call flow
The CTBCAFCallFlow object contains smart pointers to it's call legs. Destruction of these call legs will thus occur when removing the last smart pointer reference to the pointed call leg object.
The CAF framework is responsible to determine when a call leg needs to be destroyed, and will call the function OnLegFreed() of all behaviors and of the CTBCAFCallFlow object itself. When CTBCAFCallFlow::OnLegFreed is called, the smart pointer to that leg is removed (eventually causing leg destruction).
Also upon destruction of the last call leg, object based on CTBCAFCallFlow or CTBCAFCallBehavior will delete themselves automatically, by calling the attached "free listener" (that was provided in the constructor of the call flow object).
Call termination callbacks and actions
OnCallLegTerminatingIndication()
This callback means that toolpack_engine has received a termination request from a call leg (SIP Bye, ISUP REL, for example). A typical use of that event is to call TerminateCall() on both incoming and outgoing legs of a call flow, to hangup both sides.
TerminateCall()
It is MANDATORY that this function is called for each call leg before the leg can be destroyed. It's used to confirm to toolpack_engine that the CAF application has terminated with the call leg, and that the signaling can properly be terminated.
It's up to the application, however, to call the function immediately (within OnCallLegTerminatingIndication() for example) or later after doing some more job (like asynchronous database updates, for example)
OnCallLegTerminated()
This callback confirms that toolpack_engine has finished closing the data path and signaling for this call leg. It will also be called when a leg is destroyed because of a lost of communication with toolpack_engine (restart, network failure, crash).
It's the last event that should be received on a call leg (outside OnLegFreed(), of course).
FreeLeg()
It is MANDATORY that this function is called for each call leg after OnCallLegTerminated().
It's up to the application, however, to call the function immediately (within OnCallLegTerminated() for example) or later after doing some more job (like asynchronous database updates, for example)
OnLegFreed()
This callback confirms that the leg is being freed.
It's the last chance for a call flow or a behavior to release things related to a call leg, like canceling timers for example.
The base classes CTBCAFCallBehavior and CTBCAFCallFlow
- Important note: Behaviors that implement that function MUST, as the LAST line of their function, call the base class' CTBCAFCallBehavior::OnLegFreed. Not calling base class' function will cause the call to leak. Calling that before the last line of the function will cause crash, as the Behavior object will destroy itself within that function upon destruction of the last leg of the call.
Re-synchronizing with toolpack_engine
There are a couple of cases where a CAF application needs to re-synchronize with toolpack_engine:
- The CAF application was restarted
- The toolpack_engine was restarted
- Network connection between CAF application and toolpack_engine was momentarily down
When communication with toolpack_engine is lost, all call legs are notified through OnSyncLost(), but are kept in the CAF framework for 10 seconds, in case the engine is re-connected and also still has the legs available. After 10 seconds, legs are terminated in the CAF framwork. This does not mean, however, that they are terminated in the toolpack_engine. Upon reconnection with engine, legs still present in the engine will be sent back to the CAF application through OnCallLegSync().
Possible scenarios for a leg are thus:
- Engine is reconnected within 10 seconds -> Some legs no more exist in toolpack_engine, and will be notified by OnCallLegTerminated(). Legs that are still valid are notified with OnLinkSync().
- Engine has been disconnected for more than 10 seconds -> CAF framework will terminate call legs, and notify them through OnCallLegTerminated()
- Engine is reconnected after a >10 seconds disconnection period -> Call legs still available in toolpack_engine are pushed back to CAF application through OnCallLegSync() and OnLinkSync()
Re-synchronization sequence
In all the cases above, the re-synchronization mechanism involves the following steps:
- OnCmcLibNotReady() (CAF application callback): Indicates that communication with toolpack_engine has been interrupted. It will not be possible to perform actions (play file, ask for digit collection) on call legs until toolpack_engine is ready again (OnCmcLibReady).
- OnSyncLost() (CAF call flow and behavior callback): Same meaning as OnCmcLibNotReady(), passed to each call leg context individually.
- OnCallLegSync() (CAF application callback): Indicates that the toolpack_engine knows about a call leg that does not yet exist in the CAF application.
- OnLinkSync() (CAF application callback): Indicates that the toolpack_engine knows about a link between two call legs (they are part of same call flow)
- OnCmcLibReady() (CAF application callback): Indicates that communication with toolpack_engine has been restored, and that all legs and links have been re-synchronized
- OnSyncDone() (CAF call flow and behavior callback): Indicates to a call leg (that previously received OnSyncLost()) that the call is still present in the toolpack_engine, and ready to be controlled again.
- OnCallLegTerminated() (CAF call flow and behavior callback): When this function is called with Reason TBCMC_CALL_REASON_CODE_TOOLPACK_SYNC_DROP, it indicates that this call leg is no more present in toolpack_engine after re-synchronizing, and must thus be freed in the CAF application too.
Rebuilding new legs and call flow objects
When an application receives OnCallLegSync(), it must take the decision to either keep the call leg (create a CTBCAFCallLeg object) or refuse it (tell toolpack_engine that we don't want to re-synchronize it, and that it must be terminated).
Dropping re-synchronized legs
A static helper function has been provided by the class CTBCMCLeg to terminate re-synchronized call legs without having to worry about creating any object:
CTBCMCLeg::RefuseLeg()
An application that wants to refuse a re-synchronized leg should call it.
Keeping re-synchronized legs
A helper class is provided to help creating CTBCAFCallLeg objects for re-synchronized legs, and later retrieve them and attach them to a CTBCAFCallFlow parent, upon OnLinkSync():
mpCallLegResync = tbnew CTBCAFCallLegResync
This class has a function to allocate and store a call leg:
mpCallLegResync->AllocateCallLeg()
Then, upon OnLinkSync(), you can retrieve the corresponding call legs from CTBCAFCallLegResync object:
mpCallLegResync->FindCallLeg() mpCallLegResync->RemoveCallLeg()
Finally, upon OnCmcLibReady(), you can destroy the CTBCAFCallLegResync object, which will take care of refusing for you any call leg that it still references (that have not been removed by CTBCAFCallLegResync::RemoveCallLeg)
delete mpCallLegResync