Adding Call Quality stats to CDR

I’m running Asterisk 11 and I’m trying to list packet loss etc in my CDR records for all calls so I can better track issues with the system and call quality issues. I did some digging and found some custom context code to pull the stats from a channel and put them into the CDR of the call. I then found how to register a hangup handler and put them together so that I could write the call stats to the CDR on hangup. The problem is I can only seem to register the hangup handler for outbound calls with the [macro-dialout-trunk-predial-hook]. I want to be able to have it trigger on hangup for internal calls (i.e. ext to ext) and ConfBridge calls (ext to confbridge) but I can’t seem to find where I need to add my handler register line (exten => s,1,Set(CHANNEL(hangup_handler_push)=hangup-handler,s,1)?

I see that internal calls start with the from-internal context however I can’t seem to latch on i.e.[from-internal-custom] does not work. any ideas? here is the code below for my custom context.

[macro-dialout-trunk-predial-hook] ; check to ensure this context doesn’t already exist before adding
exten => s,1,Set(CHANNEL(hangup_handler_push)=hangup-handler,s,1)

[from-internal-custom]
exten => s,1,Set(CHANNEL(hangup_handler_push)=hangup-handler,s,1)

[hangup-handler]
exten => s,1,NoOp(This line will appear in the log AFTER every outgoing call hangs up)

exten => s,1,GotoIf($[$["${CHANNEL(channeltype)}" != “SIP”] | $["${DIALSTATUS}" = “”]]?continue)
; - These RTCP stats provided by CHANNEL(rtpqos) apparently only show data for the last RTCP message we got!
exten => s,n,NoOp(-- QoS stats ${CHANNEL(rtpqos,audio,all)}) ; causes a warning for non-SIP channels
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOS: ${RTPAUDIOQOS}) ; same as CHANNEL(rtpqos,audio,all)
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSBRIDGED: ${RTPAUDIOQOSBRIDGED})
; - See bug/patch #10590 for these TOTAL values that cover the ENTIRE call
; - Obtain the stats for the first (=original =local) channel i.e. the caller
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSJITTER: ${RTPAUDIOQOSJITTER})
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSLOSS: ${RTPAUDIOQOSLOSS})
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSRTT: ${RTPAUDIOQOSRTT})
; - The BRIDGED channel is the outbound (=new =2nd =callee =remote) channel established by Dial()
; - It normally has a lower packet count because it connected later (if both legs use 20 ms packets)
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSJITTERBRIDGED: ${RTPAUDIOQOSJITTERBRIDGED})
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSLOSSBRIDGED: ${RTPAUDIOQOSLOSSBRIDGED})
exten => s,n,NoOp(-- QoS stats RTPAUDIOQOSRTTBRIDGED: ${RTPAUDIOQOSRTTBRIDGED})
; - Preparing data for storage -
exten => s,n,NoOp(-- BRIDGED packet loss: ${CUT(RTPAUDIOQOSLOSSBRIDGED,;,1):5} lost of ${CUT(RTPAUDIOQOSLOSSBRIDGED,;,2):9} in total --)
; - The jitter we measured ourselves - we might also be interested in the maximum jitter
exten => s,n,Set(JITTER_RX_LOCAL_AVG=${CUT(RTPAUDIOQOSJITTER,;,3):12})
exten => s,n,Set(JITTER_RX_REMOTE_AVG=${CUT(RTPAUDIOQOSJITTERBRIDGED,;,3):12})
exten => s,n,Set(RTT_LOCAL_AVG=${CUT(RTPAUDIOQOSRTT,;,3):7})
exten => s,n,Set(RTT_REMOTE_AVG=${CUT(RTPAUDIOQOSRTTBRIDGED,;,3):7})
exten => s,n,Set(FORMAT_NATIVE=${CHANNEL(audionativeformat)})
;exten => s,n,GotoIf($["${HANGUPCAUSE}" != “16”]?continue) ; only store the data if this was a normal call

exten => s,n,Set(LOST_LOCAL_TOT=${MATH(${CUT(RTPAUDIOQOSLOSS,;,1):10} / ${CUT(RTPAUDIOQOSLOSS,;,2):10},float)})
exten => s,n,Set(LOST_REMOTE_TOT=${MATH(${CUT(RTPAUDIOQOSLOSSBRIDGED,;,1):10} / ${CUT(RTPAUDIOQOSLOSSBRIDGED,;,2):10},float)})
exten => s,n,Set(JITTER_REP_LOCAL_AVG=${MATH(${CUT(RTPAUDIOQOSJITTER,;,7):19} / 1000)})
exten => s,n,Set(JITTER_REP_REMOTE_AVG=${MATH(${CUT(RTPAUDIOQOSJITTERBRIDGED,;,7):19} / 1000)})
exten => s,n,Set(CDR(userfield)=${CDR(userfield)}&lost_remote:${LOST_REMOTE_TOT}&lost_local:${LOST_LOCAL_TOT}&format_native=${FORMAT_NATIVE})
exten => s,n,Set(CDR(userfield)=${CDR(userfield)}&jitter_remote:${JITTER_RX_REMOTE_AVG}&jitter_local:${JITTER_RX_LOCAL_AVG})
exten => s,n,Set(CDR(userfield)=${CDR(userfield)}&jitter_rep_remote:${JITTER_REP_REMOTE_AVG}&jitter_rep_local:${JITTER_REP_LOCAL_AVG})
exten => s,n,Set(CDR(userfield)=${CDR(userfield)}&rtt_remote:${RTT_REMOTE_AVG}&rtt_local:${RTT_LOCAL_AVG})
exten => s,n,Return() ;this line is required for all hangup handlers

1 Like

I think to do what you want, you would have to copy the entire context [macro-hangupcall] from extensions_additional.conf to extensions_override_freepbx.conf and make whatever changes you want there. The problem with this approach is that the contents of the override file literally override the contexts created automatically by FreePBX, so there is no guarantee that some future update will be compatible with what is placed in the override file, maybe long after you’ve forgotten about the edits. If you do this, add a noop line that will log a clue where the context is defined in case of future issues.

I got a few steps further I found this hook: macro-dialout-one-predial-hook which triggers when I call internal ext’s so I now have my hangup handler registered for external and extension to extension calls using the custom contexts however I can’t latch into the conference. I would like hangup to work for conferences as well.

I tried searching for a context I could execute in using this line in various *-custom contexts
exten => s,1,NoOp(This line will appear in the log AFTER every outgoing call hangs up)

however no matter what context I use I never see the NoOp line print in the console as the dialplan executes. I tried:
[macro-user-callerid-custom]
[marco-hangupcall-custom]
[from-internal-custom]
[sub-record-check-custom]
[ext-meetme-custom]
[sub-conference-options-custom]

No matter which I pick I never see my NoOp line printed when entering the Conference?

I also tried to override the macro-hangupcall and it didn’t execute for some reason, Do I have to do something special other than a core reload in asterisk?

Thanks,

This would be a great feature to add to FreePBX. Have you created a ticket for this feature?

Has this been implemented yet? Is there a way to get it to work?