先简单说一下DTMF,DTMF是Double Tone Multiple Frequency的缩写,即双音多频。在电话通话中,通过两个不同的频率的组合来传递按键信息,如题图中所显示的,1209和697两种频率的组合就代表1,其它依此类推。
在模拟电话以及传统的PSTN中,DTMF与声音数据是混在一起的,因为它们根本没法分开。在VoIP中常常使用DTMF2833或SIP INFO来传输DTMF,但那不是我们今天要讲的内容。
由于DTMF与声音都混在话路中,在录音时就也一块将DTMF信息录在了录音文件中,如果想从录音文件中提取这些DTMF信息,就需要对声音文件进行分析,也就是今天我们要解决的问题。
我们有了FreeSWITCH,当然不需要去找别的工具,下面我们就来看一看怎么做。
为了做一次完整的实验,我们先得有个录音文件。首先把SIP电话设成使用inband方式发送DTMF,以便能够录到DTMF信息,具体的设置方式因不同的话机(或软电话)而已,我们就不多说了。然后,使用如下方法我们可以得到一个录音文件:
freeswitch> originate user/1008 &record(/tmp/dtmf.wav)
上面使用originate命令呼叫1008,被叫接听后,开始录音。记得接听后要按几个键啊。在本次实验中,我按了1234,并挂机。
挂机后找个工具播放一下dtmf.wav,便能听到嘀嘀的按键音,虽然每个按键的声音不一样,但我们的耳朵认不出来,还得借助软件。
我们昨天刚讲了Lua,今天正好进一步再来一个例子,因而我们写了一个Lua脚本来检测DTMF,命名为dtmf.lua,内容如下:
function onInputCBF(s, type, obj, arg)
if (type == "dtmf") then
freeswitch.consoleLog("INFO", "Got DTMF: " obj.digit " Duration: " obj.duration "\n")
end
return ''
end
session:answer()
session:execute("start_dtmf", "")
session:setInputCallback('onInputCBF', '')
session:streamFile("local_stream://moh」)
其中,我们设了一个回调函数 onInputCBF,当检测到DTMF时便进行回调,在日志中打印相关的DTMF信息。
session:answer() 对Channel进行应答 session:execute()执行一个App,这里我们执行了start_dtmf以启动对inband类型的DTMF的检测 session:setInputCallbck()安装一个回调函数,在检测到DTMF时便执行该回调函数,就是我们上面写的那个onInputCBF session:streamFile() 一行只是播放一个无限长的声音文件,防止挂机
通过该Lua脚本,当有电话呼入时,我们将来电路由到该脚本,便可以实时检测来电中的DTMF了。但是在这里我们有一个问题,那就是我们要检测的是录音文件里面的,它不是一路电话,即不是一个Channel。
当然,这也难不住我们,既然我们有FreeSWITCH,那我们可以弄两个FreeSWITCH实例,从一个中呼叫另一个,在其中一个执行playback以播放声音文件,另一个执行上面的Lua脚本检测,问题不就解决了?
是的,但我们还有更简单的解决办法。
在FreeSWITCH中,不管是播放声音文件还是检测DTMF都需要一个Channel,在没有实际Channel的情况下,我们就可以生成一个假的Channel。对于这一点,FreeSWITCH早就帮我们想到了,那就是loopback Interface。它其实也是一个Endpoint,通过下面的命令生成一个Channel,并执行我们的Lua脚本:
freeswitch> originate loopback/dtmf &lua(dtmf.lua)
其中,loopback/ 后面的dtmf是被叫号码,当一个Channel产生后,该Channel的一端(一头)会进入Dialplan查找路由,另一头则执行 lua App,即执行我们的Lua脚本。关于loopback我们就不多解释了,我们只需要知道它在查找Dialplan时需要在Dialplan中让它能找到,因而,我们在默认的Dialplan(default.xml)中加入以下内容:
上述Dialplan会匹配被叫号码dtmf,然后应答,然后播放一个声音文件,就是我们刚才录的那一个。
在Channel的另一头执行我们的Lua脚本,就可以检测DTMF了,笔者测试时,日志输出如下:
- [INFO] switch_cpp.cpp:1291 Got DTMF: 1 Duration: 1120
- [INFO] switch_cpp.cpp:1291 Got DTMF: 2 Duration: 1120
- [INFO] switch_cpp.cpp:1291 Got DTMF: 3 Duration: 1120
- [INFO] switch_cpp.cpp:1291 Got DTMF: 4 Duration: 1120
帅不帅?
当然,以上我们的Lua脚本比较简单,通过增加一些语句,你也可以比较精确的打印DTMF在录音文件中的时间等信息,这些,自己练习一下吧。
广告时间:
本文收录于《FreeSWITCH 实例解析》中,感兴趣的小伙伴可以点击链接购买商品。
现在加入FreeSWITCH VIP知识星球即可获取全部『FreeSWITCH系列』电子书。
VIP星球:

2019年最新一期FreeSWITCH培训(北京站)以及第八届FreeSWITCH开发者沙龙正在火热报名中,现在报名还可享受八折优惠,欢迎点击『阅读原文』了解详情。
同时欢迎赞助商及讲师加入我们本次的FreeSWITCH开发者沙龙。