分享

freeswitch中拨号计划

 草莓加冰 2017-05-16
1、正则表达式
例子说明
^1234$ ^匹配字符串的开头,$ 匹配字符串的结尾,
表示严格匹配1234

^1234 |5678$  |是或的意思,表示匹配1234或5678
^123[0-9]$ 表示匹配1230、1231...1239

^123\d$ \d等于[0-9]

^123\d+$ 表示匹配123开头的至少4位数的数字串
^123\d*$ 表示匹配123开头的至少3位数的数字串 

^123由于没有结尾的$,表示匹配任何以123开头
的数字串,或后面是字母的情况 

123$ 表示匹配任何以123结尾的字符串 

^123\d{5}$  {5}表示精确匹配5位,包含它前面的一个
字符。匹配以123开头的所有8位数字串

^123(\d+)$ 跟^123\d+$是相同的;匹配结果中除123
之外的数字都将存储在$1这个变量中,
在后面可以引用  

^123(\d)(\d+)$ 如果用它跟12345678匹配,则匹配成功,
结果是$1=4,$2=5678 

..表示匹配任意一个字符,“.*”表示匹配
任意字符串 
FreeSwitch提供了简单的API可以测试你写的正则表达式是否正确,只需要在命令行输入“regex 要匹配的字符串 | 正则表达式”即可。如:
freeswitch> regex 1234 | \d
true
freeswitch> regex 1234 | \d{4}
true
freeswitch> regex 1234 | \d{5}
false
freeswitch> regex 1234 | ^123
true
2、通道变量
在FreeSwitch中,每一次呼叫都由一条或多条“腿(Call Leg)组成,其中的一条腿又成为一个Channel(通道),每一个Channel都由很有属性,用于标识Channel的状态、性能等,这些属性称为Channel Variable(通道变量),可简写为Channel Var、Chan Var或Var。
log的作用就是将信息写到日志中,他的第一个参数是loglevel,就是log的级别,不同的级别在彩色的终端上能以不同的颜色显示,日志的级别有以下几种(数越大显示越详细):
0 - CONSOLE
1 - ALERT
2 - CRIT
3 - ERR
4 - WARNING
5 - NOTICE
6 - INFO
7 - DEBUG
3、测试条件
大部分的测试条件都是针对被叫号码的,但你也可以对其他变量进行测试,如在
Dialplan的default.xml中IP地址就可以这样测试:
<condition field="network_addr" expression="^192\.168\.200\.158$">
Dialplan中的测试条件
变量说明
contextDialplan当前的Context 
rdnis被转移的号码
destination_number被叫号码
dialplanDialplan模块的名字                   
caller_id_name主叫名称
caller_id_number主叫号码   
ani主叫的自动号码识别 
aniii主叫类型
uuid本Channel的唯一标志
source呼叫源,来自哪个Freeswitch模块
chan_name               Channel名字
network_addr主叫IP地址
year当前的年
yday 一年中的第几天
mon
mday 
week 一年中第几周
mweek    本月中的第几周
wday一周中的第几天
hour小时,0-23 
minute   分,0-59
minute-of-day    一天中的第几分钟(1-1440) 

除此之外,测试还接受用户在用户目录中设置的变量,但必须使用${}对变量进行引用。如下面toll_allow就是在用户目录中设置的:
<condition field="${toll_allow}" expressio="internation">
如果在你的用户目录中设置以下变量,可以在Dialplan中通过condition一行进行相关测试。

用户目录中的设置:
<user id="1000">
<variables>
<variable name="my_test_var" value="my_test_value"/>
</variables>
</user>

Dialplan中的测试条件:
<condition field="${my_test_var}" expressio="my_test_value$">

测试条件不可以嵌套,但可以迭加。如下:

<extension name="Testing">
<condition field="network_addr" expression="^192\.168\.200\.158$"/>
<condition field="destination_number" expression="^1234$">
<action application="log" data="INFO , I know you called ${destination_number}"/>
</condition>
</extension>

两个condition是迭加关系,即只有在IP地址和被叫号码两个条件都匹配的情况下才执行
action,否则就跳过本extension ,继续解析下一项。
迭加的condition还可以构成其他关系,break参数就是用于实现这个功能,它有以下几个值:
on-false:第一次匹配失败时停止,但继续处理其他extension ,这个是默认的配置。

on-true:第一次匹配成功时停止,会先完成对应的action,再继续处理其他extension。

always:不管是否匹配,都停止。

never:不管是否匹配,都继续。

假设呼叫1234,如果ip地址是192.168.200.158,就播放早上好,如果ip是192.168.200.159,就播放晚上好,设置如下:

<extension name="Testing">
<condition field="destination_number" expression="^1234$"/>
<condition field="network_addr" expression="^192\.168\.200\.158$" break="on-true">
<action application="playback" data="good-morning.wav"/>
</condition>
<condition field="network_addr" expression="^192\.168\.200\.159$">
<action application="playback" data="good-night.wav"/>
</condition>
</extension>

4、动作与反动作

<extension name="Testing">
<condition field="destination_number" expression="^1234$"/>
<condition field="network_addr" expression="^192\.168\.200\.158$" break="on-true">
<action application="playback" data="good-morning.wav"/>
<anti-action application="playback" data="good-night.wav"/>
</condition>
</extension>
如果呼叫ip地址来自192.168.200.158则播放good-morning.wav,否则播放good-night.wav。

5、实例解析
1、Local_Extension
Freeswitch默认的配置提供了1000-1019共20个SIP账号,密码都是1234。Freeswtch通过Dialplan可以将来话路由到这些本地的号码。Config/dialplan/default.xml中的Local_Extension部分如下:
<extension name="Local_Extension">
<condition field="destination_number" expression="^(10[01][0-9])$">
<! - - 此处省略many actions - ->
</condition>
</extension>
在SIP客户端上用1000呼叫1001时,FS会建立一个Channel,该Channel构成一次呼叫的a-leg。初始化完毕后,Channel进入ROUTING状态,即到达Dialplan。由于被叫号码1001与这里的正则表达式匹配,所以会执行下面的action。由于在正则表达式中使用个“()”,因此匹配结果会放入$1中,因此在这里$1=1001。
下面分析省略的actions里面的内容:
<action application="set" data="dialed_extension=$1"/>
<action application="export" data="dialed_extension=$1"/>
set和export都是设置一个变量,名字为dialed_extension,值是1001。而set是讲变量设置到当前的Channel上,即a-leg。export除具备set的功能外,将变量设置到b-leg上。当然,这时b-leg还不存在。
接着往下看:
<!-- bind_meta_app can have these args <key> [a|b|ab] [a|b|o|s] <app> --> <action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/> <action application="bind_meta_app" data="2 b s record_session::$${recordings_dir}/${
caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/> <action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/> <action application="bind_meta_app" data="4 b s execute_extension::att_xfer XML 
features"/>
bind_meta_app的作用是在该Channel上绑定DTMF。上面四行分别绑定了1、2、3、4四个按键,它们都绑在b-leg上。注意,这时b-leg还不存在。
往下看:
<action application="set" data="ringback=${us-ring}"/> 
此处,设置回铃音是美音,${us-ring}的值是在vars.xml中设置的。接下来:
<action application="set" data="transfer_ringback=$${hold_music}"/>
如果发生呼叫转移,用户听到的回铃音。
<action application="set" data="call_timeout=30"/>
call_timeout是设置呼叫超时的变量。
继续看:
<action application="set" data="hangup_after_bridge=true"/> 
<!--<action application="set" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> --> 
<action application="set" data="continue_on_fail=true"/> 
<action application="hash" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/> 
<action application="hash" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/> 
<action application="hash" data="insert/${domain_name}-last_dial_ext/${called_party_callgroup}/${uuid}"/> 
<action application="hash" data="insert/${domain_name}-last_dial_ext/global/${uuid}"/>
hash是内存中的哈希表数据结构。他可以设置一个键-值对。例如最后一行${domain_name}-last_dial_ext这个哈希表中插入一个global键,它的值是${uuid},即本Channel的唯一标志。
不管是set,还是hash,都是保存数据为后面做准备。不同的是set将变量绑定到Channel上,以通道变量的形式存在,而hash保存到内存的哈希表数据结构中。
往下看,是设置通道变量:
<action application="set" data="called_party_callgroup=${user_data(${dialed_extension}@${domain_name} var callgroup)}"/>
<!--<action application="export" data="nolocal:rtp_secure_media=${user_data(${dialed_extension}@${domain_name} var rtp_secure_media)}"/>-->
最后一行默认是注释掉的,nolocal的作用是告诉export只将该变量设置到b-leg上,而不要设置到a-leg上。
下面还是往哈希表中插入数据:
<action application="hash" data="insert/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
再往下看:
<action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
bridge是最关键的部分。bridge相当于一座桥,它的作用是把两条腿1000和1001给桥接起来。为了能连接到1001,Freeswitch作为SIP UAC,向1001这个SIP UA发起INVITE请求,并建立一个新的Channel,就是我们的b-leg。1001开始振铃,bridge把回铃音传回1000,因此1000就能听见回铃音。
当然,在呼叫之前首先要查找1001这个用户是否已经注册,否则会直接返回USER_NOT_REGISTERED,就不会建立b-leg。
domain和domain_name都是预设的变量,默认的是服务器的IP地址。user是一个特殊的
Endpoint,它指的是本地用户。这里呼叫字符串翻译出来就是
{sip_invite_domain=192.168.200.158}user/1001@192.168.200.158
其中,{}里是设置通道变量,这些变量只会建立在b-leg上。
以上电话路由基本上就完成了,我们已经建立了1000到1001之间的呼叫,1001已经开始振铃,接下来会有以下几种情况:
1.被叫应答
2.被叫忙
3.被叫无应答
4.被叫拒绝
5.其他情况
第一种情况:1000与1001畅聊,这个时候bridge一直是阻塞的,也就是说,bridge会一直等待b-leg(1001)挂机后才返回,这时才有可能继续执行下面的action。无论哪一方挂机,bridge就算结束了。
如果1000(主叫)先挂机,Freeswitch会将挂机原因(一般是NORMAL_RELEASE)发送给1001,同时释放b-leg,由于a-leg已经没有了,Dialplan就没有往下执行的必要了,因此会产生计费信息,并销毁a-leg。
如果1001先挂机,b-leg就没有了,但b-leg会将挂机原因传到a-leg。在a-leg决定是否继续往下执行之前,会检查一些变量,以决定该怎么做。hangup_after_bridge=true的意思是,如果bridge正常完成后,就挂机,到这里a-leg就释放了。
如果1001没接电话,如1001拒接(返回CALL_REJECTED,有些会返回USER_BUSY,隐藏真实原因)、忙(USER_BUSY)、无应答(NO_ANSWER或NO_USER_RESPONSE)等。出现这些情况Freeswitch会认为是不成功的bridge,就不管hangup_after_bridge的变量了。这时候会检查另一个变量continue_on_fail。设置的continue_on_fail=true,在bridge失败后会继续执行下面的action。
通过给continue_on_fail不同的值,可以决定在什么情况下继续,比如下面的设置将只在用户忙和无应答的情况下才会继续呼叫流程:
<action application="set" data="continue_on_fail=USER_BUSY,NO_ANSWER”/>
其他可能的值有:NORMAL_TEMPORARY_FAILURE(临时故障),TIMEOUT(超时),NO_ROUTE_DESTINATION(呼叫不可达)。
接着往下看:
<action application="answer"/>
Dialplan执行到这里,Freeswitch会给主叫1000回送应答消息,以建立真正的媒体流,准备对其播放声音。接下来:
<action application="sleep" data="1000"/> 
<action application="bridge" data="loopback/app=voicemail:default ${domain_name} ${dialed_extension}"/>
sleep表示暂停,1000表示暂停的毫秒数(1s),然后转到1001的语音信箱。注意,Freeswitch默认配置文件中是通过transfer加loopback Endpoint的方式转到voicemail的。

2、回声和延迟回声
关于回声,如果拨打9196,就能听见自己的回声。Dialplan如下:
<extension name="echo"> <condition field="destination_number" expression="^9196$"> <action application="answer"/> <action application="echo"/></condition> </extension>
延迟回声如下:
<extension name="delay_echo"> <condition field="destination_number" expression="^9195$"> <action application="answer"/> <action application="delay_echo" data="5000"/> </condition> </extension>
注意:版本不同expression的值有可能不同。

3、会议
下面是将来话转入会议电话,其中nb_conferences就是默认的8000Hz的窄带电话会议。
<extension name="nb_conferences"> <condition field="destination_number" expression="^(30\d{2})$"> <action application="answer"/> <action application="conference" data="$1-${domain_name}@default"/> </condition> </extension>
正则表达式^(30)$ 匹配以30开头的4位数字的被叫号码。所有呼叫30xx的电话均可进入一个会议,大家可以畅所欲言。
4、将通话的双方转入会议
回头看一下上节的一条代码:
<action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
bind_meta_app是一个APP,它有一系列参数,其中,3表示绑定DTMF按键3;b表示要绑到b-leg上;s表示same。如果在b-leg上按下*3(必须先按*),则执行execute_extension这个APP。execute_extension是临时去别的地方执行一些Dialplan指定的APP,在这里它的参数是cf XML features,表示要去XML Dialplan的features这个Context中找一个匹配cf的extension。
在conf/dialplan/features.xml中很容易找到匹配cf的这个extension:
<!-- Used to transfer both legs into a conference --> 
<extension name="cf"> 
<condition field="destination_number" expression="^cf$"> 
<action application="answer"/> 
<action application="transfer" data="-both 30${dialed_extension:2} XML default"/> 
</condition> 
</extension>
answer在这个APP里没有什么用,因为呼叫已经应答了。transfer是一个APP,它会将当前通话重新转移到ROUTING阶段,重新去Dialplan中进行路由。它的第一个参数是-both,表示将它自己和a-leg都转移到Dialplan中重新路由。路由的Dialplan类型仍然是XML,Context是Default,被叫号码30${dialed_extension:2}中的dialed_extension来自:
<action application="export" data="dialed_extension=$1"/>
注意,上面是用export把变量设置到b-leg上的,实际上就是最初的被叫号码1001,${dialed_extension:2}是从1001的第二个位置开始截取,一直截取到字符串结尾的字符串,由于位置是从0开始计数的,因而1001:2最终结果就是01因而最终transfer的一行就等价于:
<action application="transfer" data="-both 3001 XML default"/>
其中,-both表示将两条腿都分别转到3001这个extension上。所以,重新路由后就到了会议的情况。最终1000和1001都进入名为3001的会议上。

6、常用的Dialplan App
(1)set
用于设置一个通道变量,如:
<action application="set" data="my_var=123456"/>
(2)echo
回声,在调试时比较有用,如:
<action application="echo"/>
(3)info
info在调试时也比较有用,它会在日志中打印全部的通道变量,如:
<action application="info"/>
(4)answer
用于应答一路呼叫
<action application="answer"/>
(5)bridge
负责桥接另一条腿,它的参数是一个呼叫字符串:
<action application="bridge" data="user/1000">
bridge操作是阻塞,它会一直等到b-leg释放后才继续往下走。
(6)playback
用于给Channel放音,如果需要对主叫放音,如:
<action application="playback" data="/tmp/test.wav"/>
它的参数是声音文件的路径,如果需要播放多个文件,可以串联操作,如:
<action application="playback" data="/tmp/test1.wav"/>
<action application="playback" data="/tmp/test2.wav"/>
<action application="playback" data="/tmp/test3.wav"/>
另外,通过mod_shout模块,也可以支持本地货远程HTTP或Shout Cast服务器上的.mp3格式的文件,如:
<action application="playback" data="/tmp/test.mp3"/>
<action application="playback" data="http://localhost/test.mp3"/>
<action application="playback" data="shout://localhost/test.mp3"/>
在播放过程中,用户可以按*键停止播放,或者在播放前通过Chan Var选择其他按键完成此功能:
<action application="set" data="playback_terminators=1"/>
也可以使用如下方法取消按键中断的方式:
<action application="set" data="playback_terminators=none"/>
(7)sleep
sleep用于设置可以等待/暂停的一段时间,单位默认为毫秒:
<action application="sleep" data="1000"/>
(8)ring_ready
ring_ready用于在SIP中给对方回180消息,即通知对方可以振铃了:
<action application="ring_ready" data="1000"/>
(9)pre_answer
pre_answer用于在SIP中给对方回183消息,后续的playback之类的动作将作为早期媒体给对方发过去,如彩铃音:
<action application="ring_ready" data="1000"/>
<action application="playback" data="/tmp/music.wav"/>
注意:虽然Freeswitch可以把媒体发给对方,但如果在一定时间内没有应答,对端通常也会挂断该通话。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多