Wednesday 24 July 2013

How to pause and resume Asterisk call recordings

If you record your calls you may wish to enable pausing and unpausing of the recordings. This is relativly simple to do once you understand the features.conf file and the ActivateOn and ActivatedBy fields.
Firstly lets take the example where you receive an incoming call and wish to pause and unpause it. features.conf would contain the following two lines in the [applicationmap] section :-
InPauseMonitor   => #1,peer/callee,Macro,recpause,recording-disabled
InUnpauseMonitor => #3,peer/callee,Macro,recunpause,recording-enabled
It is the callee which is allowed to pause and unpause and since the Monitor() application is being run on the inbound channel the macro needs to be run on the peer.
For doing the same with outbound calls its the caller who is allowed to pause and unpause and since this is the same channel which ran the Monitor() application the macro needs to be run on the same channel :-
OutPauseMonitor   => #1,self/caller,Macro,recpause,recording-disabled
OutUnpauseMonitor => #3,self/caller,Macro,recunpause,recording-enabled
Here are the macros which are called and should be placed in extensions.conf. Rather than pausing and unapusing the recording we are actually stopping and starting the recording. That is purely because we have all the individual recordings sent to a remote server which combines and converts them to a mp3 file therefore reducing the cpu load on the asterisk server.
[macro-recpause]
exten => s,1,Playback(/var/lib/asterisk/sounds/recording/beep/beep)
exten => s,n,NoOp(Call Paused - Channel=${CHANNEL} BrigdePeer=${BRIDGEPEER})
exten => s,n,StopMonitor
[macro-recunpause]
exten => s,1,Monitor(wav,${FNAME}_${EPOCH})
exten => s,n,NoOp(Call Unpaused - Channel=${CHANNEL} BrigdePeer=${BRIDGEPEER})
exten => s,n,Playback(/var/lib/asterisk/sounds/recording/beepbeep/beepbeep)
In the features.conf file in addition to specifying the macro to be run we also specified a music on hold class to be used for playing music on hold to the other party. Shown below are the contents of musiconhold.conf and exactly the same files are played as in the ones in the macros. This ensures both parties hear the exact same audio. You can use different audio files if you wish although make sure the music on hold files are the exact same duration otherwise they will be cut short if they are longer or partially repeated if they are shorter.
[recording-disabled]
mode=files
directory=/var/lib/asterisk/sounds/recording/beep
[recording-enabled]
mode=files
directory=/var/lib/asterisk/sounds/recording/beepbeep

How to get the SIP response code in Asterisk 10+

In early Asterisk 1.8 you used to be able to get the SIP response code by using a dialplan entry like :-
exten => _X.,n,Set(SIPcause=${MASTER_CHANNEL(HASH(SIP_CAUSE,${CDR(dstchannel)}))}
The Asterisk developers discovered the way the information was being populated caused a significant performance hit and so decided to turn the feature off by default. It can be enabled by editing sip.conf and adding the following line

storesipcause=yes

From asterisk version 10 there is now a new way to get the SIP cause however the way in which it is read is a bit convoluted. Contrary to the example given on the official Asterisk WIKI you cannot query it in the channel which performed the dial. It has to be queried by the channel which actually generated the SIP response.
You therefore have to do a few things :-
  1. Add a hangup handler to the destination channel so that a dialplan routine is run when that channel hangs up.
  2. Use the new 'b' option to the Dial command which causes a dialplan routine to be called just before the dial happens and this is used to setup the hangup handler on the destination channel.
  3. In the hangup handler get a list of hangupcause strings which in most cases will be a single string which we can just use. If you simultaneously dialled two destinations you will have two strings however and will need to decide which to use.
  4. If the hangupcause string is blank then it probably just means the call was answered and was hungup normally so we can just return.
  5. Once we have the hangupcause to check read the SIP cause string.
  6. Now the problem of how to pass the variable back. Normal variable inheritance does not work properly because of the channel being hung up at this point so we use yet another new feature by setting a shared variable in the master channel.
  7. After the original dial we read the shared variable out of its own channel shared area and set a normal variable and then use the cut function to read the numerical value.
Finally here is a complete example :-


 [dial_sip]
exten => _X.,1,Dial(SIP/${ddi}@${carrier},,b(dial_sip^set_handler^1))
exten => _X.,n,Set(SIPcause=${SHARED(SIPcause)}, Responsetime=$[${EPOCH}-${dialtime}])
exten => _X.,n,Set(SIPcode=${CUT(SIPcause," ",2)})

exten => set_handler,1,Set(CHANNEL(hangup_handler_push)=dial_sip,outbound_handler,1)
exten => set_handler,n,Return()

exten => outbound_handler,1,NoOp(Destination channel has hungup)
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()})
; If no hangup causes are available then its probably because it is a regular call and the call ended normally so we just return.
same => n,ExecIf($["${HANGUPCAUSE_STRING}" = ""]?Return())
same => n,NoOp(Got Channel ID ${HANGUPCAUSE_STRING} master ${MASTERCHANNEL} with Technology Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)}, Asterisk Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},ast)})
same => n,Set(SHARED(SIPcause,${MASTERCHANNEL})=${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)})
same => n,Return()