认识拨号计划-Dialplan
拨号计划是FreeSWITCH 中⾄关重要的⼀部分。它的主要作⽤就是对电话进⾏路由(从这⼀点上来说,相当于⼀个路由表)。说的简明⼀点,就是当⼀个⽤户拨号时,对⽤户所拨的号码进⾏分析,进⽽决定下⼀步该做什么。当然,实际上,它所能做的⽐你想象的要强⼤的多。
我们在第⼆章中已经提到过修改过拨号计划,单从配置⽂件看,还算⽐较简单直观。实际上,它的概念也不是很复杂。如果你理解正则表达式,那你应该能看懂系统系统⾃带的⼤部分的配置。但是,在实际应⽤中,有许多问题还是常常令初学者感到疑惑。主要的问题是,要理解 Dialplan,还需要了解 FS 是怎样⼯作的(第五章),API 与 APP 的区别等。
通过本章,我们除了要了解Dialplan 的基本概念和运作⽅式,还要以理论与实践相结合的⽅式来进⾏学习,使⽤初学者能快速上⼿,有经验的⼈也能学到新的维护和调试技巧。
XML Dialplan
Dialplan 是FreeSWITCH 中⼀个抽象的部分,它可以⽀持多种不同的格式,如类似Asterisk 的格式(由mod_dialplan_asterisk提供)。但在实际使⽤中,⽤的最多的还是 XML 格式。下⾯,我们就先讨论这种格式。
配置⽂件的结构
拨号计划的配置⽂件在 conf/dialplan 中,在前⾯的章节中我们讲过,它们是在 l 中,由装⼊的。
拨号计划由多个 Context (上下⽂/环境)组成。每个 Context 中有多个 Extension (分⽀,在简单的 PBX 中也可以认为是分机号,但很显然,Extension 涵盖的内容远⽐分机号多)。所以,Context 就是多个 Extension 的逻辑集合,它相当于⼀个分组,⼀个 Context 中的 Extension 与其它 Context 中的 Extension 在逻辑上是隔离的。
下⾯是 Dialplan 的完整结构:
<?xml version="1.0"?>
<document type="freeswitch/xml">
<ction name="dialplan" description="Regex/XML Dialplan">
<context name="default">
<extension name="Test Extension">
吼猴
</extension>
</context>
</ction>
</document>
Extension 相当于路由表中的表项,其中,每⼀个 Extension 都有⼀个 name 属性。它可以是任何合法的字符串,本⾝对呼叫流程没有任何影响,但取⼀个好听的名字,有助于你在查看 Log 时发现它。
在 Extension 中可以对⼀些 condition (条件)进⾏判断,如果满⾜测试条件所指定的表达式,则执⾏相对应的 action (动作)。
例如,我们将下列 Extension 配置加⼊到 conf/l 中。并作为第⼀个 Extension。
<extension name="My Echo Test">
<condition field="destination_number" expression="^echo|1234$">
<action application="echo" data=""/>
</condition>
</extension>
FreeSWITCH 安装时,提供了很多例⼦,为了避免与提供的例⼦冲突,强列建议在学习时把⾃⼰写的Extension 写在最前⾯。当然我说的最前⾯并不是l 的第⼀⾏,⽽是放到第⼀个 Extension 的位置,就是以下语句的后⾯(你通常能在第13-14⾏找到它们):
<include>
<context name="default">
⽤你喜欢的编译器编辑好并存盘后,在 FreeSWITCH 命令⾏上(Console 或 fs_cli)执⾏ reloadxml 或按 F6键,使 FreeSWITCH 重新读⼊你修改过的配置⽂件。并按 F8 键将 log 级别设置为 DEBUG,以看到详细⽇志.然后,将软电话注册上,并拨叫 1234 或 echo (⼤部分软电话都能呼叫字母,如Zoiper,Xlite可以使⽤空格键切换数字和字母)。
你将会看到很多 Log, 注意如下的⾏:
Processing Seven <1000>->1234 in context default
parsing [default->My Echo Test] continue=fal
Regex (PASS) [Echo Test] destination_number(1234) =~ /^echo|1234$/ break=on-fal
Action echo()
包容性在我的终端上,上⾯的第⼀⾏是以绿⾊显⽰的。当然,为了排版⽅便,我省去了 Log 中的⽇期以及其它不关键的⼀些信息。
第⼀⾏,Processing 说明是在处理 Dialplan,Seven 是我的的 SIP 名字,1000 是我的分机号, 1234 是我所拨叫的号码,这⾥,我直接拨叫了 1234。它完整意思是说,呼叫已经达到路由阶段,要从 XML Dialplan 中查找路由,该呼叫来⾃ Seven,分机号是1000,它所呼叫的被叫号码是 1234 (或 echo,如果你拨叫 echo 的话)。
第⼆⾏,呼叫进⼊ parsing (解析XML) 阶段,它⾸先找到 XML 中的⼀个 Context,这⾥是 default(它是在 ur directory 中定义的,看第五章。ur directory 中有⼀项,说明,如果 1000 这个⽤户发起呼叫,则它的 context 就是 default,所以要从 XML Dialplan 中的 default 这个 Context 查起)。它⾸先找到的第⼀个 Extension 的 name 是 My Echo Test(还记得吧?我们我们把它放到了 Dialplan 的最前⾯)。continue=fal 的意思我们后⾯再讲。
第三⾏,由于该Extension 中有⼀个Condition,它的测试条件是destination_number,也就是被叫号码,所以,FreeSWITCH 测试被叫号码(这⾥是1234)是否与配置⽂件中的正则表达式相匹配。 ^echo|1234$ 是正则表达式,它匹配 echo 或 1234。所以这⾥匹配成功,Log 中显⽰ Regex (PASS)。当然既然匹配成功了,它就开始执⾏动作 echo(它是⼀个 APP),所以你就听到了⾃⼰的声⾳。
这是最简单的路由查找。前⾯我已经说了,系统⾃带了⼀些Dialplan 的例⼦,也许在第⼆章你已经测试过了。下⾯,我们试⼀下系统⾃带的echo 的例⼦。这次,我呼叫的是 9196。在 Log 中,还是从绿⾊的⾏开始看:
Processing Seven <1000>->9196 in context default
parsing [default->My Echo Test] continue=fal
Regex (FAIL) [Echo Test] destination_number(9196) =~ /^echo|1234$/ break=on-fal
parsing [default->unloop] continue=fal
Regex (PASS) [unloop] ${unroll_loops}(true) =~ /^true$/ break=on-fal
Regex (FAIL) [unloop] ${sip_looped_call}() =~ /^true$/ break=on-fal
parsing [default->tod_example] continue=true
Date/Time Match (FAIL) [tod_example] break=on-fal
parsing [default->holiday_example] continue=true
Date/Time Match (FAIL) [holiday_example] break=on-fal
parsing [default->global-intercept] continue=fal
Regex (FAIL) [global-intercept] destination_number(9196) =~ /^886$/ break=on-fal
parsing [default->group-intercept] continue=fal
Regex (FAIL) [group-intercept] destination_number(9196) =~ /^\*8$/ break=on-fal
parsing [default->intercept-ext] continue=fal
Regex (FAIL) [intercept-ext] destination_number(9196) =~ /^\*\*(\d+)$/ break=on-fal
parsing [default->redial] continue=fal
Regex (FAIL) [redial] destination_number(9196) =~ /^(redial|870)$/ break=on-fal
parsing [default->global] continue=true
Regex (FAIL) [global] ${call_debug}(fal) =~ /^true$/ break=never
[][][][][][] 此处省略五百字...
parsing [default->fax_receive] continue=fal
Regex (FAIL) [fax_receive] destination_number(9196) =~ /^9178$/ break=on-fal
parsing [default->fax_transmit] continue=fal
Regex (FAIL) [fax_transmit] destination_number(9196) =~ /^9179$/ break=on-fal
parsing [default->ringback_180] continue=fal
Regex (FAIL) [ringback_180] destination_number(9196) =~ /^9180$/ break=on-fal
parsing [default->ringback_183_uk_ring] continue=fal
Regex (FAIL) [ringback_183_uk_ring] destination_number(9196) =~ /^9181$/ break=on-fal
parsing [default->ringback_183_music_ring] continue=fal
Regex (FAIL) [ringback_183_music_ring] destination_number(9196) =~ /^9182$/ break=on-fal
parsing [default->ringback_post_answer_uk_ring] continue=fal
Regex (FAIL) [ringback_post_answer_uk_ring] destination_number(9196) =~ /^9183$/ break=on-fal
parsing [default->ringback_post_answer_music] continue=fal
Regex (FAIL) [ringback_post_answer_music] destination_number(9196) =~ /^9184$/ break=on-fal
parsing [default->ClueCon] continue=fal湖北一本大学
Regex (FAIL) [ClueCon] destination_number(9196) =~ /^9191$/ break=on-fal
parsing [default->show_info] continue=fal
Regex (FAIL) [show_info] destination_number(9196) =~ /^9192$/ break=on-fal
parsing [default->video_record] continue=fal
Regex (FAIL) [video_record] destination_number(9196) =~ /^9193$/ break=on-fal
parsing [default->video_playback] continue=fal
Regex (FAIL) [video_playback] destination_number(9196) =~ /^9194$/ break=on-fal
parsing [default->delay_echo] continue=fal
Regex (FAIL) [delay_echo] destination_number(9196) =~ /^9195$/ break=on-fal
parsing [default->echo] continue=fal
Regex (PASS) [echo] destination_number(9196) =~ /^9196$/ break=on-fal
Action answer()
Action echo()
你可以看到,前⾯的正则表达式匹配都没有成功(Regex (FAIL)),只是到最后匹配到^9196$才成功(你看到Regex (PASS)了),成功后先应答(answer),然后执⾏ echo。
在这⼀节⾥,我们花了很多篇幅来讲解如此简单的问题。但实际上,我是想让你知道,这⼀节最重要的不是讲Dialplan,⽽是告诉你如何看Log。在邮件列表上,⼤多数新⼿遇到的问题都可以很轻松的从Log 中看出来,但他们不知道怎么看,或者是看了也不理解。所以,在这⾥,我想请你再看⼀下我们的第⼀个例⼦。永远记住:遇到Dialplan 的问题,按F8打开DEBUG级别的⽇志,从绿⾊的⾏开始看起(当然,如果你的终端不能显⽰颜⾊,那么,从Processing ⼀⾏看起)。我们的第⼀个例⼦虽然只有短短的四⾏ Log,但是它包含了所有你需要的信息。
默认的配置⽂件结构
系统默认提供的配置⽂件包含三个Context:default、features和public,它们分别在三个XML ⽂件中。default 是默认的dialplan,⼀般来说注册⽤户都可以使⽤它来打电话,如拨打其它分机或外部电话等。⽽public 则是接收外部呼叫,因为从外部进来的呼叫是不可信的,所以要进⾏更严格的控制。如,你肯定不想从外部进来的电话再通过你的⽹关进⾏国内或国际长途呼叫。
当然,这么说不是绝对的,等你熟悉了 Dialplan 的概念之后,可以发挥你的想象⼒进⾏任何有创意的配置。
其中,在 default 和 public 中,⼜通过 INCLUDE 预处理指令分别加⼊了 default/ 和 include/ ⽬录中的所有 XML ⽂件。这些⽬录中的⽂件仅包含⼀些额外的Extension。由于Dialplan 在处理是时候是顺序
处理的,所以,⼀定要注意这些⽂件的装⼊顺序。通常,这些⽂件都按⽂件名排序,如00_,01_等等。如果你新加⼊Extension,可以在这些⽬录⾥创建⽂件。但要注意,这些⽂件的优先级⽐直接写在如l 中低。我前⾯已经说过,由于你不熟悉系统提供的默认的Dialplan,很可能出现与系统冲突的情况。当然,你已经学会如何查看Log,所以能很容易的找到问题所在。但在本书中,我还是坚持将新加的Extension 加在 Dialplan 中的最前⾯,以便于说明问题。
实际上,由于在处理Dialplan 时要对每⼀项进⾏正则表达式匹配,是⾮常影响效率的。所以,在⽣产环境中,往往要删除这些默认的Dialplan,⽽只配置有⽤的部分。但我们还不能删,因为⾥⾯有好多例⼦我们可以学习。
正则表达式
Dialplan 使⽤ Perl 兼容的正则表达式(PCRE, Perl-compatible regular expressions)匹配。熟悉编程的同学肯定已经很熟悉它了,为了⽅便不熟悉的同学,在这⾥仅作简单介绍:
^1234$ ^ 匹配字符串开头,$ 匹配结尾,所以本表达式严格匹配 1234
^1234|5678$ | 是或的意思,表⽰匹配 1234 或 5678
^123[0-9]$ [ ] 表式匹配其中的任意⼀个字符,其中的 - 是省略的⽅式,表⽰ 0 到 9,它等于 [01234
56789]
也就是说它会匹配 1230,1231,1232 (1239)
^123\d$ 同上,\d 等于 [0-9]
^123\d+$ + 号表⽰1个或多个它前⾯的字符,因为 + 前⾯是 \d,所以它就等于1个或多个数字,实际上,
它匹配任何以123开头的⾄少4位数的数字串,如1230,12300,12311,123456789等
^123\d*$ *号与+号的不同在于,它匹配0个或多个前⾯的字符。
所以,它匹配以123开头的⾄少3位数的数字串,如 123,123789
^123 跟上⾯⼀样,由于没有结尾的$,它匹配任何以123开头的数字串,但除此之外,它还匹配后⾯是字母的情况,如 123abc
123$ 匹配任何以123结尾的字符串
^123\d{5}$ {5}表⽰精确匹配5位,包含它前⾯的⼀个字符。在这⾥,它匹配以123开头的所有8位的
电话号码
^123(\d+)$ ( )在匹配中不起作⽤,跟^123\d+是相同的,但它对匹配结果有作⽤,
匹配结果中除123之外的数字都将存储在$1这个变量中,在下⼀步使⽤
^123(\d)(\d+)$ 如果⽤它跟12345678匹配,则匹配成功,结果是 $1 = 4, $2 = 5678
简单的正则表达式⽐较容易理解,更深⼊的学习请查阅相关资料。正则表达式功能很强⼤,但配置不当也容易出现错误,轻者造成电话不通,重者可能会造成误拨或套拨,带来经济损失。
信道变量 - Channel Variables
在FreeSWITCH 中,每⼀次呼叫都由⼀条或多条“腿”(Call Leg)组成,其中的⼀条腿⼜称为⼀个Channel(信道),每⼀个Channel 都有好多属性,⽤于标识Channel 的状态,性能等,这些属性称为 Channel Variable(信道变量),简写为 Channel Var 或 Chan Var 或 Var。
通过使⽤ info 这个 APP,可以查看所有的 Channel Var。我们先修改⼀下 Dialplan。
<extension name="Show Channel Variable">
<condition field="destination_number" expression="^1235$">
<action application="info" data=""/>
大学迟到检讨书</condition>
</extension>
加⼊ l 中,为了复习上⼀节的内容,我们这⼀次加⼊ My Echo Test 这⼀ Extension 的后⾯,存盘,然后在 FreeSWITCH 命令⾏上执⾏ reloadxml。从软电话上呼叫 1235,可以看到有很多 Log输出,还是从绿⾊的⾏开始看:
Processing Seven <1000>->1235 in context default
parsing [default->Echo Test] continue=fal
Regex (FAIL) [Echo Test] destination_number(1235) =~ /^echo|1234$/ break=on-fal
parsing [default->Show Channel Variable] continue=fal
Regex (PASS) [Show Channel Variable] destination_number(1235) =~ /^1235$/ break=on-fal
Action info()
...
EXECUTE sofia/internal/1000@192.168.7.10 info()
2010-10-23 09:46:31.662281 [INFO] mod_dptools.c:1171 CHANNEL_DATA:
Channel-State: [CS_EXECUTE]
Channel-Call-State: [RINGING]
Channel-State-Number: [4]
Channel-Name: [sofia/internal/1000@192.168.7.10]
Unique-ID: [cfea988b-2dc4-42ec-b731-2cd7ea864fc6]
Caller-Direction: [inbound]
怎样制作寿司Caller-Urname: [1000]
Caller-Dialplan: [XML]
Caller-Caller-ID-Name: [Seven]
Caller-Caller-ID-Number: [1000]
Caller-Network-Addr: [192.168.7.10]
Caller-ANI: [1000]
Caller-Destination-Number: [1235]
variable_direction: [inbound]
variable_uuid: [cfea988b-2dc4-42ec-b731-2cd7ea864fc6]
variable_sip_local_network_addr: [123.130.140.154]
variable_remote_media_ip: [123.130.140.154]
variable_remote_media_port: [8000]
variable_sip_u_codec_name: [PCMA]
variable_sip_u_codec_rate: [8000]
variable_sip_u_codec_ptime: [20]
variable_read_codec: [PCMA]
variable_read_rate: [8000]
variable_write_codec: [PCMA]
variable_write_rate: [8000]
variable_endpoint_disposition: [RECEIVED]
variable_current_application: [info]
为节省篇幅,我们删去了⼀部分。
可以看到,由于我们呼叫的是1235,它在第三⾏测试My Echo Test 的1234 的时候失败了,接在接下
来测试1235的时候成功了,便执⾏相对应的Action -info 这个APP。它的作⽤就是把所有 Channel Variables 都打印到 Log 中。
所有的 Channel Variable 都是可以在 Dialplan 中访问的,使⽤格式是 ${变量名},如 ${destination_number}。将下列配置加⼊ Dialplan 中(存盘,reloadxml 不⽤再说了吧?):
<extension name="Accessing Channel Variable">
<condition field="destination_number" expression="^1236(\d+)$">
<action application="log" data="INFO Hahaha, I know you called ${destination_number}"/>
<action application="log" data="INFO The Last few digists is $1"/>
<action application="log" data="ERR This is not actually an error, just jocking"/>
<action application="hangup"/>
</condition>
</extension>
这次我们呼叫 1236789,看看结果:
Processing Seven <1000>->1236789 in context default
parsing [default->Echo Test] continue=fal
Regex (FAIL) [Echo Test] destination_number(1236789) =~ /^echo|1234$/ break=on-fal
parsing [default->Show Channel Variable] continue=fal
Regex (FAIL) [Show Channel Variable] destination_number(1236789) =~ /^1235$/ break=on-fal
parsing [default->Accessing Channel Variable] continue=fal
Regex (PASS) [Accessing Channel Variable] destination_number(1236789) =~ /^1236(\d+)$/ break=on-fal
Action log(INFO Hahaha, I know you called ${destination_number})
Action log(NOTICE The Last few digists is 789)
Action log(ERR This is not actually an error, just jocking)
八字对联
Action hangup()
[DEBUG] switch_core_state_machine.c:157 sofia/internal/1000@192.168.7.10 Standard EXECUTE
EXECUTE sofia/internal/1000@192.168.7.10 log(INFO Hahaha, I know you called 1236789)
[INFO] mod_dptools.c:1152 Hahaha, I know you called 1236789
EXECUTE sofia/internal/1000@192.168.7.10 log(NOTICE The Last few digists is 789)
[NOTICE] mod_dptools.c:1152 The Last few digists is 789
EXECUTE sofia/internal/1000@192.168.7.10 log(ERR This is not actually an error, just jocking)
[ERR] mod_dptools.c:1152 This is not actually an error, just jocking
EXECUTE sofia/internal/1000@192.168.7.10 hangup()
你肯定看到彩⾊的 Log 了,同时也看到了⽤ $ 表⽰的 Channel Variable 被替换成了相应的值。
同时你也看到,这次实验我们特意增加了⼏个 Action。⼀个 Action 通常有两个参数,⼀个是 applicati
on,代表要执⾏的 APP,另⼀个是 data,就是 APP 的参数,当 APP 没有参数时,data也可能省略。
⼀个 Action 必须是⼀个合法的 XML 标签,在前⾯,你看到的 context,extension 等都是成对出现的,如。但由于 Action ⽐较简单,⼀般彩⽤简写的形式来关闭标签,即。注意⼤于号前⾯的“/”,如果不⼩漏掉,在 reloadxml 时将会出现类似“+OK [[error near line 3371]: unexpected closing tag ]” 的错误,⽽实际的错误位置⼜通常不是出错的那⼀⾏。这是在编辑XML ⽂件时经常遇到的问题,⼜⽐较难于查找。因此在修改时要多加⼩⼼,并推荐使⽤具有语法⾼亮的功能的编辑器来编辑。
测试条件 - Conditions
动作与反动作 - Action & Anti-Action
⼯作机制进阶
实例解析
...
以上的论述应该涵盖了 Dialplan 的所有概念,当然,要活学活⽤,还需要⼀些经验。下⾯,我们讲⼏个真实的例⼦。这些例⼦⼤部分来⾃默认的配置⽂件。Local_Extension
我们要看的第⼀个例⼦是 Local_Extension。 FreeSWITCH 默认的配置提供了 1000 - 1019 共 20 个 SIP 账号,密码都是 1234 。
<extension name="Local_Extension">
<condition field="destination_number" expression="^(10[01][0-9])$">
//actions
</condition>
</extension>
这个框架说明,⽤正则表达式 (10[01][0-9])$ 来匹配被叫号码,它匹配所有 1000 - 1019 这 20 个号码。
这⾥我们假设在 SIP 客户端上,⽤ 1000 和 1001 分别注册到了 FreeSWITCH 上,则 1000 呼叫 1001 时,FreeSWITCH 会建⽴⼀个 Channel,该 Channel 构成⼀次呼叫的 a-leg(⼀条腿)。初始化完毕后,Channel 进⼊ ROUTING 状态,即进⼊ Dialplan。由于被叫号码 1001 与这⾥的正则表达式匹配,所以,会执⾏下⾯这些 Action。另外,由于我们在正则表达式中使⽤了 “( )”,因此,匹配结果会放⼊变量 $1 中,因此,在这⾥,$1 = 1001。
<action application="t" data="dialed_extension=$1"/>
<action application="export" data="dialed_extension=$1"/>
t 和 export 都是设置⼀个变量,该变量的名字是 dialed_extension,值是 1001。
关于t 和export 的区别我们在前⾯已经讲过了。这⾥再重复⼀次:t 是将变量设置到当前的Channel 上,即a-leg。⽽export 则也将变量设置到b-leg 上。当然,这⾥ b-leg 还不存在。所以在这⾥它对该 Channel 的影响与 t 其实是⼀样的。因此,使⽤ t 完全是多余的。但是除此之外,export 还设置了⼀个特殊的变量,叫 export_vars,它的值是dialed_extension。所以,实际上。上⾯的第⼆⾏就等价于下⾯的两⾏:
<action application="t" data="dialed_extension=$1"/>
<action application="t" data="export_vars=dialed_extension"/>
.
<!-- bind_meta_app can have the 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_ssion::$${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="t" data="ringback=${us-ring}"/>
设置回铃⾳是美⾳(不同国家的回铃⾳是有区别的),${us-ring} 的值是在 l 中设置的。
<action application="t" data="transfer_ringback=$${hold_music}"/>
设置呼叫转移时,⽤户听到的回铃⾳。
温存<action application="t" data="call_timeout=30"/>
设置呼叫超时。
<action application="t" data="hangup_after_bridge=true"/>
<!--<action application="t" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> -->
<action application="t" data="continue_on_fail=true"/>
这些变量影响呼叫流程,详细说明见下⾯的 bridge。
<action application="hash" data="inrt/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
<action application="hash" data="inrt/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
<action application="hash" data="inrt/${domain_name}-last_dial_ext/${called_party_callgroup}/${uuid}"/>
<action application="hash" data="inrt/${domain_name}-last_dial_ext/global/${uuid}"/>
hash 是内存中的哈希表数据结构。它可以设置⼀个键-值对(Key-Value pair)。如,上⾯最后⼀⾏上向${domain_name}-last_dial_ext 这个哈希表中插⼊global 这么⼀个键,它的值是 ${uuid},就是本 Channel 的唯⼀标志。
不管是上⾯的 t,还是 hash,都是保存⼀些数据为后⾯做准备的。笼怎么读
<action application="t" data="called_party_callgroup=${ur_data(${dialed_extension}@${domain_name} var callgroup)}"/>
<!--<action application="export" data="nolocal:sip_cure_media=${ur_data(${dialed_extension}@${domain_name} var sip_cure_media)}"/>-->
这⼀⾏默认是注释掉的,因此不起作⽤。 nolocal 的作⽤我们已前也讲到过,它告诉 export 只将该变量设置到 b-leg 上,⽽不要设置到 a-leg 上。
<action application="hash" data="inrt/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
还是 hash.
<action application="bridge" data="{sip_invite_domain=$${domain}}ur/${dialed_extension}@${domain_name}"/>
bridge 是最关键的部分。其实上⾯除 bridge 以外的 action 都可以省略,只是会少⼀些功能。
回忆⼀下第四章中的内容。⽤户1000 其实是⼀个SIP UA(UAC),它向FreeSWITCH(作为UAS)发送⼀个INVITE 请求。然后FreeSWITCH 建⽴⼀个Channel,从 INVITE 请求中找到被叫号码(destination_number=1001),然后在 Dialplan 中查找 1001 就⼀直⾛到这⾥。
bridge 的作⽤就是把 FreeSWITCH 作为⼀个 SIP UAC,再向 1001 这个 SIP UA(UAS)发起⼀个 INVITE 请求,并建⽴⼀个 Channel。这就是我们的 b-leg。1001 开始振铃,bridge 把回铃⾳传回到1000,因此,1000 就能听到回铃⾳(如果1001 有⾃⼰的回铃⾳,则1000 能听到,否则,将会听到默认的回铃⾳${us-ring})。