Answered Time On Channel
Hi,
I have a dial plan where I need to notify an external system when a call was answered and when the call hung up. In both requests the start time needs to be the same. My Dialplan looks something like this:
[outbound]
Exten => _X.,1,Dial(SIP/${EXTEN}@1.1.1.1,,U(call-answer-from-carrier))
Exten => h,1,NoOp(ANSWERED_TIME: ${ANSWEREDTIME} >>> DIAL_TIME:
${DIALEDTIME} >>> HANGUP_TIME: ${EPOCH} >>> ANSWERED TIME
${MATH(${EPOCH}-${ANSWEREDTIME},int)})
[call-answer-from-carrier]
Exten => s,1,Noop(CALL WAS ANSWERED AT ${EPOCH}
Exten => s,n,Agi(some_script.py)
Now in theory the hangup time of the call (${EPOCH} in the h extension)
minus the answered time should be the same as the noop from my subroutine. I am finding that some times they match and some times they are off by a second. My issue is that the external system expects the answered time to the same for when we call it from the SubRoutine as well as from the h extension. I assume the difference is based on the microsecond of when we look at EPOCH how DIALEDTIME is rounded.
Any tips on how I can get the same answered time across the board?
TIA.
Dovid
7 thoughts on - Answered Time On Channel
Seems like what I needed was the SHARED function which is working perfectly.
Don’t use an ‘h’ extension, use a hangup handler. Use the MASTER_CHANNEL() function to set variables to ensure they are always set in the “top most” channel. Below is an untested example, but is inspired by dialplan code I use in production. Maybe it will help.
[outbound] ; this is called on the incoming (caller) channel exten => _X.,1,Noop
,,U(answer_handler)b(pre_dial_handler^s^1)g
same => n,Set(MASTER_CHANNEL(start_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Set(CHANNEL(hangup_handler_push)=hangup_handler,s,1)
same => n,Set(MASTER_CHANNEL(callid_ingress)=${SIPCALLID})
same => n, *** unrelated dialplan, AGIs, etc. ***
same => n,Dial(SIP/${EXTEN}@1.1.1.1
same => n, *** dialplan for the caller when the callee hangs up first, not run when caller hangs up first. use it to try dialing another destination, play intercept to caller, etc. ***
[pre_dial_handler] ; this is called on the outgoing (callee) channel exten => s,1,Noop
same => n,Set(MASTER_CHANNEL(callid_egress)=${SIPCALLID})
same => n,Set(MASTER_CHANNEL(dial_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Return
[answer_handler] ; run on outgoing (callee) channel, but sets answer_timestamp in the caller channel exten => s,1,Noop
same => n,Set(MASTER_CHANNEL(answer_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Return
[hangup_handler] ; run on incoming (caller) channel, use to do final post call cleanup exten => s,1,Noop same => n,Set(MASTER_CHANNEL(hangup_timestamp)=${STRFTIME(,,%s.%3q)})
same => n, ***post call cleanup AGIs, dialplan, etc.***
same => n,Return
It seems that what ever I set in my answer handler does not show up in the hangup handler. In order to do billing I can’t rely on the g option where the caller hangs up the call. Looks like I can either use h or a hangup handler along with the shared function.
My issue seems to be that I am using local channels. So for instance if I
have:
[incoming]
Exten => _X.,1,NooP()
Exten => _X.,n,Dial(Local/${EXTEN}@out1/n)
Exten => _X.,n,Dial(Local/${EXTEN}@out2/n)
Exten => h,1,DumpChan()
[out1]
exten => _X.,1,Noop same => n,Set(MASTER_CHANNEL(start_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Set(CHANNEL(hangup_handler_push)=hangup_handler,s,1)
same => n,Set(MASTER_CHANNEL(callid_ingress)=${SIPCALLID})
same => n,Dial(SIP/1111${EXTEN}@1.1.1.1:5063
,,U(answer_handler)b(pre_dial_handler^s^1))
Exten => h,1,DumpChan()
[out2]
exten => _X.,1,Noop same => n,Set(MASTER_CHANNEL(start_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Set(CHANNEL(hangup_handler_push)=hangup_handler,s,1)
same => n,Set(MASTER_CHANNEL(callid_ingress)=${SIPCALLID})
same => n,Dial(SIP/2222${EXTEN}@1.1.1.1:5063
,,U(answer_handler)b(pre_dial_handler^s^1))
Exten => h,1,DumpChan()
[pre_dial_handler] ; this is called on the outgoing (callee) channel exten => s,1,Noop same => n,Set(MASTER_CHANNEL(callid_egress)=${SIPCALLID})
same => n,Set(MASTER_CHANNEL(dial_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Return
[answer_handler] ; run on outgoing (callee) channel, but sets answer_timestamp in the caller channel exten => s,1,Noop same => n,Set(MASTER_CHANNEL(answer_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Return
[hangup_handler] ; run on incoming (caller) channel, use to do final post call cleanup exten => s,1,Noop
same => n,Set(MASTER_CHANNEL(hangup_timestamp)=${STRFTIME(,,%s.%3q)})
same => n,Return
When using MASTER_CHANNEL it sets it on the channel that created the local channel (so it ends up in context incoming and NOT in out1). Not sure if this is a bug since the context incoming is generating the channel that calls out1 that then calls the answer_handler.
Seems I hit this: https://issues.asterisk.org/jira/browse/ASTERISK-23133
My suggestion is to stop trying to do this in the dialplan. Let asterisk dialplan do it’s job and process the call. Manage this either through the AMI events and call your scripts from there or if you are using an asterisk version that has ARI then allow it to do your call set up and tear down. Either way don’t over complicate your dialplan.
Nathan Kirk
From the hangup handler specification:
Hangup handlers are an alternative to the h extension. They can be used in addition to the h extension. The idea is to attach a Gosub routine to a channel that will execute when the call hangs up. Whereas which h extension gets executed depends on the location of dialplan execution when the call hangs up, hangup handlers are attached to the call channel. You can attach multiple handlers that will execute in the order of most recently added first.