先簡(jiǎn)單說(shuō)一下DTMF,DTMF是Double Tone Multiple Frequency的縮寫(xiě),即雙音多頻。在電話(huà)通話(huà)中,通過(guò)兩個(gè)不同的頻率的組合來(lái)傳遞按鍵信息,如題圖中所顯示的,1209和697兩種頻率的組合就代表1,其它依此類(lèi)推。
在模擬電話(huà)以及傳統的PSTN中,DTMF與聲音數據是混在一起的,因為它們根本沒(méi)法分開(kāi)。在VoIP中常常使用DTMF2833或SIP INFO來(lái)傳輸DTMF,但那不是我們今天要講的內容。
由于DTMF與聲音都混在話(huà)路中,在錄音時(shí)就也一塊將DTMF信息錄在了錄音文件中,如果想從錄音文件中提取這些DTMF信息,就需要對聲音文件進(jìn)行分析,也就是今天我們要解決的問(wèn)題。
我們有了FreeSWITCH,當然不需要去找別的工具,下面我們就來(lái)看一看怎么做。
為了做一次完整的實(shí)驗,我們先得有個(gè)錄音文件。首先把SIP電話(huà)設成使用inband方式發(fā)送DTMF,以便能夠錄到DTMF信息,具體的設置方式因不同的話(huà)機(或軟電話(huà))而已,我們就不多說(shuō)了。然后,使用如下方法我們可以得到一個(gè)錄音文件:
freeswitch> originate user/1008 &record(/tmp/dtmf.wav)
上面使用originate命令呼叫1008,被叫接聽(tīng)后,開(kāi)始錄音。記得接聽(tīng)后要按幾個(gè)鍵啊。在本次實(shí)驗中,我按了1234,并掛機。
掛機后找個(gè)工具播放一下dtmf.wav,便能聽(tīng)到嘀嘀的按鍵音,雖然每個(gè)按鍵的聲音不一樣,但我們的耳朵認不出來(lái),還得借助軟件。
我們昨天剛講了Lua,今天正好進(jìn)一步再來(lái)一個(gè)例子,因而我們寫(xiě)了一個(gè)Lua腳本來(lái)檢測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」)
其中,我們設了一個(gè)回調函數 onInputCBF,當檢測到DTMF時(shí)便進(jìn)行回調,在日志中打印相關(guān)的DTMF信息。
session:answer() 對Channel進(jìn)行應答 session:execute()執行一個(gè)App,這里我們執行了start_dtmf以啟動(dòng)對inband類(lèi)型的DTMF的檢測 session:setInputCallbck()安裝一個(gè)回調函數,在檢測到DTMF時(shí)便執行該回調函數,就是我們上面寫(xiě)的那個(gè)onInputCBF session:streamFile() 一行只是播放一個(gè)無(wú)限長(cháng)的聲音文件,防止掛機
通過(guò)該Lua腳本,當有電話(huà)呼入時(shí),我們將來(lái)電路由到該腳本,便可以實(shí)時(shí)檢測來(lái)電中的DTMF了。但是在這里我們有一個(gè)問(wèn)題,那就是我們要檢測的是錄音文件里面的,它不是一路電話(huà),即不是一個(gè)Channel。
當然,這也難不住我們,既然我們有FreeSWITCH,那我們可以弄兩個(gè)FreeSWITCH實(shí)例,從一個(gè)中呼叫另一個(gè),在其中一個(gè)執行playback以播放聲音文件,另一個(gè)執行上面的Lua腳本檢測,問(wèn)題不就解決了?
是的,但我們還有更簡(jiǎn)單的解決辦法。
在FreeSWITCH中,不管是播放聲音文件還是檢測DTMF都需要一個(gè)Channel,在沒(méi)有實(shí)際Channel的情況下,我們就可以生成一個(gè)假的Channel。對于這一點(diǎn),FreeSWITCH早就幫我們想到了,那就是loopback Interface。它其實(shí)也是一個(gè)Endpoint,通過(guò)下面的命令生成一個(gè)Channel,并執行我們的Lua腳本:
freeswitch> originate loopback/dtmf &lua(dtmf.lua)
其中,loopback/ 后面的dtmf是被叫號碼,當一個(gè)Channel產(chǎn)生后,該Channel的一端(一頭)會(huì )進(jìn)入Dialplan查找路由,另一頭則執行 lua App,即執行我們的Lua腳本。關(guān)于loopback我們就不多解釋了,我們只需要知道它在查找Dialplan時(shí)需要在Dialplan中讓它能找到,因而,我們在默認的Dialplan(default.xml)中加入以下內容:
上述Dialplan會(huì )匹配被叫號碼dtmf,然后應答,然后播放一個(gè)聲音文件,就是我們剛才錄的那一個(gè)。
在Channel的另一頭執行我們的Lua腳本,就可以檢測DTMF了,筆者測試時(shí),日志輸出如下:
- [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腳本比較簡(jiǎn)單,通過(guò)增加一些語(yǔ)句,你也可以比較精確的打印DTMF在錄音文件中的時(shí)間等信息,這些,自己練習一下吧。
廣告時(shí)間:
本文收錄于《FreeSWITCH 實(shí)例解析》中,感興趣的小伙伴可以點(diǎn)擊鏈接購買(mǎi)商品。
現在加入FreeSWITCH VIP知識星球即可獲取全部『FreeSWITCH系列』電子書(shū)。
VIP星球:

2019年最新一期FreeSWITCH培訓(北京站)以及第八屆FreeSWITCH開(kāi)發(fā)者沙龍正在火熱報名中,現在報名還可享受八折優(yōu)惠,歡迎點(diǎn)擊『閱讀原文』了解詳情。
同時(shí)歡迎贊助商及講師加入我們本次的FreeSWITCH開(kāi)發(fā)者沙龍。