總體結(jié)構(gòu)
FreeSWITCH 由一個(gè)穩(wěn)定的核心及外圍模塊組成。
FreeSWITCH 使用線程模型來(lái)處理并發(fā)請(qǐng)求,每個(gè)連接都在單獨(dú)的線程中進(jìn)行處理。這不僅能提供最大強(qiáng)度的并發(fā),更重要的是,即使某路電話發(fā)生問(wèn)題,也只影響到它所在的線程,而不會(huì)影響到其它電話。FreeSWITCH 的核心非常短小精悍,這也是保持穩(wěn)定的關(guān)鍵。所有其它功能都在外圍的模塊中。模塊是可以動(dòng)態(tài)加載(以及卸載)的,在實(shí)際應(yīng)用中可以只加載用到的模塊。外圍模塊通過(guò)核心提供的 Public API 與核心進(jìn)行通信,而核心則通過(guò)回調(diào)機(jī)制執(zhí)行外圍模塊中的代碼。
核心
FS Core 是 FreeSWITCH 的核心,它包含了關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)和復(fù)雜的代碼,但這些代碼只出現(xiàn)在核心中,并保持了最大限度的重用。外圍模塊只能通過(guò) API 調(diào)用核心的功能,因而核心運(yùn)行在一個(gè)受保護(hù)的環(huán)境中,核心代碼都經(jīng)過(guò)精心的編碼和嚴(yán)格的測(cè)試,最大限度地保持了系統(tǒng)整體的穩(wěn)定。
核心代碼保持了最高度的抽象,因而它可以調(diào)用不同功能,不同協(xié)議的模塊。同時(shí),良好的 API 也使得編寫不同的外圍模塊非常容易。
數(shù)據(jù)庫(kù)
FreeSWITCH 的核心除了使用內(nèi)部的隊(duì)列、哈希表存儲(chǔ)數(shù)據(jù)外,也使用外部的 SQL 數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)。當(dāng)前,系統(tǒng)的核心數(shù)據(jù)庫(kù)使用 SQLite,默認(rèn)的存儲(chǔ)位置是 db/core.db 。 使用外部數(shù)據(jù)庫(kù)的好處是--查詢數(shù)據(jù)不用鎖定內(nèi)存數(shù)據(jù)結(jié)構(gòu),這不僅能提供性能,而且降低了死鎖的風(fēng)險(xiǎn),保證了系統(tǒng)穩(wěn)定。命令 show calls、show channels 等都是直接從數(shù)據(jù)庫(kù)中讀取內(nèi)容并顯示的。由于 SQLite 會(huì)進(jìn)行讀鎖定,因此不建議直接讀取核心數(shù)據(jù)庫(kù)。
系統(tǒng)對(duì)數(shù)據(jù)庫(kù)操作做了優(yōu)化,在高并發(fā)狀態(tài)時(shí),核心會(huì)盡量將幾百條 SQL 一齊執(zhí)行,這大大提高了性能。但在低并發(fā)的狀態(tài)下執(zhí)行顯得稍微有點(diǎn)慢,如一個(gè) channel 已經(jīng)建立了,但還不能在 show channels 中顯示;或者,一個(gè) channel 已經(jīng) destroy 了,還顯示在 show channels 中。但由于這些數(shù)據(jù)只用于查詢,而不用于決策,所以一般沒(méi)什么問(wèn)題。
除核心數(shù)據(jù)庫(kù)外,系統(tǒng)也支持使用 ODBC 方式連接其它數(shù)據(jù)庫(kù),如 PostgreSQL、MySQL等。某些模塊,如 mod_sofia、mod_fifo等都有自己的數(shù)據(jù)庫(kù)(表)。如果在 *nix 類系統(tǒng)上使用 ODBC,需要安裝 UnixODBC,并進(jìn)行正確的配置,如果編譯安裝的話還需要開發(fā)包 unixodbc-devel(CentOS) 或 unixodbc-dev(Debian/Ubuntu)。由于 PostgreSQL、MySQL 等都是 Client-Server 的結(jié)構(gòu),因此,外部程序可以直接查詢數(shù)據(jù)(但需要清楚數(shù)據(jù)的準(zhǔn)確性,可能會(huì)比 FreeSWITCH 核心中的數(shù)據(jù)有所滯后)。
模塊
FreeSWITCH 主要分為以下幾個(gè)部分:
終點(diǎn)
End Points 是終結(jié) FreeSWITCH 的地方,也就是說(shuō)再往外走就超出 FreeSWITCH 的控制了。它主要包含了不同呼叫控制協(xié)議的接口,如 SIP, TDM 硬件,H323 以及 Google Talk 等。這使得 FreeSWITCH 可以與眾多不同的電話系統(tǒng)進(jìn)行通信。如,可以使用 mod_skypopen 與 Skype 網(wǎng)絡(luò)進(jìn)行通信。另外,前面也講過(guò),它還可以通過(guò) portaudio 驅(qū)動(dòng)本地聲卡,用作一個(gè)軟電話。
撥號(hào)計(jì)劃
Dialplan 主要是為了查找電話路由,主要的是 XML 描述的,但它也支持 Asterisk 格式的配置文件。另外它也持 ENUM 查詢。
XML 接口
XML Interface 支持多種獲取 XML 配置的方式,它可以是本地的配置文件,或從數(shù)據(jù)庫(kù)中讀取,甚至是一個(gè)能動(dòng)態(tài)返回 XML 的遠(yuǎn)程 HTTP 服務(wù)器。
編解碼器
FreeSWITCH 支持最廣泛的 Codec,除了大多數(shù) VoIP 系統(tǒng)支持的 G711、G722、G729、GSM 外,它還支持 iLBC,BV16/32、SILK、CELT等。它可以同時(shí)橋接不同采樣頻率的電話,以及電話會(huì)議等。
語(yǔ)音識(shí)別
支持語(yǔ)音自動(dòng)識(shí)別(ASR)及文本-語(yǔ)音轉(zhuǎn)換(TTS)。
文件格式
支持不同的聲音文件格式,如 wav,mp3等。
日志
日志可以寫到控制臺(tái)、日志文件、系統(tǒng)日志(syslog)以及遠(yuǎn)程的日志服務(wù)器。
通過(guò) swig 包裝支持多種腳本語(yǔ)本語(yǔ)言控制呼叫流程,如 Lua、Javascript、Perl等。
事件套接字
使用 Event Socket 可以使用任何其它語(yǔ)言通過(guò) Socket 方式控制呼叫流程、擴(kuò)展 FreeSWITCH 功能。
在 *nix 類系統(tǒng)上,F(xiàn)reeSWITCH 默認(rèn)的安裝位置是 /usr/local/freeswitch,在 Windows 上可能是 C:\freeswitch,目錄結(jié)構(gòu)大致相同。
bin 可執(zhí)行程序
db 系統(tǒng)數(shù)據(jù)庫(kù)(sqlite),F(xiàn)reeSWITCH 把呼叫信息存放到數(shù)據(jù)庫(kù)里以便在查詢時(shí)無(wú)需對(duì)核心數(shù)據(jù)結(jié)構(gòu)加鎖
htdocs HTTP Web srver 根目錄
lib 庫(kù)文件
mod 可加載模塊
run 運(yùn)行目錄,存放 PID
sounds 聲音文件,使用 playback() 時(shí)默認(rèn)的尋找路徑
grammar 語(yǔ)法
include 頭文件
log 日志,CDR 等
recordings 錄音,使用 record() 時(shí)默認(rèn)的存放路徑
scripts 嵌入式語(yǔ)言寫的腳本,如使用 lua()、luarun()、jsrun 等默認(rèn)尋找的路徑
storage 語(yǔ)言留言(Voicemail)的錄音
conf 配置文件,詳見下節(jié)
配置文件由許多 XML 文件組成。在系統(tǒng)裝載時(shí),XML解析器會(huì)將所有XML文件組織在一起,并讀入內(nèi)存,稱為XML注冊(cè)表。這種設(shè)計(jì)的好處在于其非常高的可擴(kuò)展性。由于XML文檔本身非常適合描述復(fù)雜的數(shù)據(jù)結(jié)構(gòu),在 FreeSWITCH 中 就可以非常靈活的使用這些數(shù)據(jù)。并且,外部應(yīng)用程序也可以很簡(jiǎn)單地生成XML,F(xiàn)reeSWITCH 在需要時(shí)可以動(dòng)態(tài)的裝載這些 XML。另外,系統(tǒng)還允許在某些 XML 節(jié)點(diǎn)上安裝回調(diào)程序(函數(shù)),當(dāng)這些節(jié)點(diǎn)的數(shù)據(jù)變化時(shí),系統(tǒng)便自動(dòng)調(diào)用這些回調(diào)程序。
使用 XML 唯一的不足就是手工編輯這些 XML 比較困難,但正如其作者所言,他絕對(duì)不是 XML 的粉絲,但這一缺點(diǎn)與它所帶來(lái)的好處相比是微不足道的。而且,將來(lái)也許會(huì)有圖形化的配置工具,到時(shí)候只所高級(jí)用戶會(huì)去看這些XML了。
配置文件的的目錄結(jié)構(gòu)如下(其中結(jié)尾有 “/” 的為目錄):
autoload_configs/
dialplan/
directory/
extensions.conf
freeswitch.xml
fur_elise.ttml
jingle_profiles/
lang/
mime.types
notify-voicemail.tpl
sip_profiles/
tetris.ttml
vars.xml
voicemail.tpl
web-vm.tpl
其中最重要的是 freeswitch.xml,就是它將所有配置文件“粘”到一起。只要有一點(diǎn) XML 知識(shí),這些配置是很容易看懂的。其中,X-PRE-PROCESS標(biāo)簽稱為預(yù)處理命令,它用來(lái)設(shè)置一些變量和裝入其它配置文件。在 XML 加載階段,F(xiàn)reeSWITCH 的 XML 解析器會(huì)先將預(yù)處理命令進(jìn)行展開,生成一個(gè)大的 XML 文件 log/freeswitch.xml.fsxml。該文件是一個(gè)內(nèi)存鏡像,用戶不應(yīng)該手工編輯它。但它對(duì)調(diào)試非常有用,假設(shè)你不慎弄錯(cuò)了某個(gè)標(biāo)簽,又不知道它在哪個(gè)地方,F(xiàn)reeSWITCH 在加載時(shí)就報(bào) XML 的某一行出錯(cuò),在該文件中就行容易找到。
整個(gè)XML文件分為幾個(gè)重要的部分:configuration (配置)、dialplan (撥號(hào)計(jì)劃)、directory(用戶目錄)及phrase(分詞)。每一部分又分別裝入不同的 XML。
XML由標(biāo)簽(Tag)和屬性構(gòu)成。<tag> 和 </tag>組成一對(duì)標(biāo)簽,如果該標(biāo)簽有相關(guān)屬性,剛以
<tag attr="value"></tag> 形式指定。有些標(biāo)簽無(wú)須配對(duì),則必須以 “/>”關(guān)閉該標(biāo)簽定義,
如<other\_tag attr="value"/>。
freeswitch.xml
<?xml version="1.0"?>
<document type="freeswitch/xml">
<!-- #comment 這是一個(gè)配置文件,本行是注釋 -->
<X-PRE-PROCESS cmd="include" data="vars.xml"/>
<section name="configuration" description="Various Configuration">
<X-PRE-PROCESS cmd="include" data="autoload_configs/*.xml"/>
</section>
</document>
上面是一個(gè)精減了的 freeswitch.xml。它的根是 document,在 document 中,有許多 section,每個(gè) section 都對(duì)應(yīng)一部分功能。其中有兩個(gè) X-PRE-PROCESS 預(yù)處理指令,它們的作用是將 data 參數(shù)指定的文件包含(include)到本文件中來(lái)。由于它是一個(gè)預(yù)處理指令,F(xiàn)reeSWITCH 在加載階段只對(duì)其進(jìn)行簡(jiǎn)單替換,并不進(jìn)行語(yǔ)法分析,因此,對(duì)它進(jìn)行注釋是沒(méi)有效果的,這是一個(gè)新手常犯的錯(cuò)誤。假設(shè) vars.xml 的內(nèi)容如下,它是一個(gè)合法的 XML:
<!-- this is vars.xml -->
<var>xxxxx</var>
若你在調(diào)試階段想把一條 X-PRE-PROCESS 指令注釋掉:
<!-- <X-PRE-PROCESS cmd="include" data="vars.xml"/> -->
當(dāng) FreeSWITCH 預(yù)處理時(shí),還沒(méi)有到達(dá) XML 解析階段,也就是說(shuō)它還不認(rèn)識(shí) XML 注釋語(yǔ)法,而僅會(huì)機(jī)械地將預(yù)處理指令替換為 vars.xml 里的內(nèi)容:
<!-- <!-- this is vars.xml -->
<var>xxxxx</var> -->
由于 XML 的注釋不能嵌套,因此便產(chǎn)生錯(cuò)誤的XML。解決辦法是破壞掉 X-PRE-PROCESS 的定義,如我常用下面兩種方法:
<xX-PRE-PROCESS cmd="include" data="vars.xml"/>
<XPRE-PROCESS cmd="include" data="vars.xml"/>
由于 FreeSWITCH 不認(rèn)識(shí) xX-PRE-PROCESS 及 XPRE-PROCESS,因此它會(huì)忽略掉該行,相當(dāng)于注釋掉了。
vars.xml 主要通過(guò) X-PRE-PROCESS 指令定義了一些全局變量。全局變量以 $${var} 表示,臨時(shí)變量以 ${var} 表示。有些變量是系統(tǒng)在運(yùn)行時(shí)自動(dòng)獲取的,如默認(rèn)情況下 $${base_dir}=/usr/local/freeswitch, $${local_ip_v4}=你機(jī)器的IP地址等。
autoload_configs目錄下面的各種配置文件會(huì)在系統(tǒng)啟動(dòng)時(shí)裝入。一般來(lái)說(shuō)都是模塊級(jí)的配置文件,每個(gè)模塊對(duì)應(yīng)一個(gè)。文件名一般以 模塊名.conf.xxml 方式命名。其中 modules.conf.xml 決定了 FreeSWITCH 啟動(dòng)時(shí)自動(dòng)加載哪些模塊。
定義 XML 撥號(hào)計(jì)劃,我們會(huì)有專門的章節(jié)講解撥號(hào)計(jì)劃。
它里面的配置文本決定了 FreeSWITCH 作為注冊(cè)服務(wù)器時(shí)哪些用戶可以注冊(cè)上來(lái)。FreeSWITCH 支持多個(gè)域(Domain),每個(gè)域可以寫到一個(gè) XML 文件里。默認(rèn)的配置包括一個(gè) default.xml,里面定義了 1000 ~ 1019 一共 20 個(gè)用戶。
它定義了 SIP 配置文件,實(shí)際上它是由 mod_sofia 模塊在 autoload_configs/sofia.conf.xml 中加載的。但由于它本身比較復(fù)雜又是核心的功能,因此單列了一個(gè)目錄。我們將會(huì)在后面加以詳細(xì)解釋。
XML 用戶目錄決定了哪些用戶可以注冊(cè)到 FreeSWITCH 上。當(dāng)然,SIP 并不要求一定要注冊(cè)才可以打電話,但是用戶認(rèn)證仍需要在用戶目錄中配置。
用戶目錄的默認(rèn)配置文件在 conf/directory/,系統(tǒng)自帶的配置文件為 default.xml(其中 dial-string 一行由于排版要求人工換行,實(shí)際上不應(yīng)該有換行):
<domain name="$${domain}">
<params>
<param name="dial-string" value="{presence_id=${dialed_user}@${dialed_domain}}
${sofia_contact(${dialed_user}@${dialed_domain})}"/>
</params>
<variables>
<variable name="record_stereo" value="true"/>
<variable name="default_gateway" value="$${default_provider}"/>
<variable name="default_areacode" value="$${default_areacode}"/>
<variable name="transfer_fallback_extension" value="operator"/>
</variables>
</domain>
該配置文件決定了哪些用戶能注冊(cè)到 FreeSWITCH 中。一般來(lái)說(shuō),所有用戶都應(yīng)該屬于同一個(gè) domain(除非你想使用多 domain,后面我們會(huì)有例子)。這里的 $${domain} 全局變量是在 vars.xml 中設(shè)置的,它默認(rèn)是主機(jī)的 IP 地址,但也可以修改,使用一個(gè)域名。params 中定義了該 domain 中所有用戶的公共參數(shù)。在這里只定義了一個(gè) dial-string,這是一個(gè)至關(guān)重要的參數(shù)。當(dāng)你在使用 user/user_name 或 sofia/internal/user_name 這樣的呼叫字符串時(shí),它會(huì)擴(kuò)展成實(shí)際的 SIP 地址。其中 sofia_contact 是一個(gè) API,它會(huì)根據(jù)用戶的注冊(cè)地址擴(kuò)展成相應(yīng)的呼叫字符串。
variables 則定義了一些公共變量,在用戶做主叫或被叫時(shí),這些變量會(huì)綁定到相應(yīng)的 Channel 上形成 Channel Variable。
在 domain 中還定義了許多組(group),組里面包含很多用戶(user)。
<groups>
<group name="default">
<users>
<X-PRE-PROCESS cmd="include" data="default/*.xml"/>
</users>
</group>
</groups>
在這里,組名 default 并沒(méi)有什么特殊的意義,它只是隨便起的,你可以修改成任何值。在用戶標(biāo)簽里,又使用預(yù)處理指令裝入了 default/ 目錄中的所有 XML 文件。你可以看到,在 default/ 目錄中,每個(gè)用戶都對(duì)應(yīng)一個(gè)文件。
你也可以定義其它的用戶組,組中的用戶并不需要是完整的 XML 節(jié)點(diǎn),也可以是一個(gè)指向一個(gè)已存在用戶的“指針”,如下圖,使用 type="pointer" 可以定義指針。
<group name="sales">
<users>
<user id="1000" type="pointer"/>
<user id="1001" type="pointer"/>
<user id="1002" type="pointer"/>
</users>
</group>
雖然我們這里設(shè)置了組,但使用組并不是必需的。如果你不打算使用組,可以將用戶節(jié)點(diǎn)(users)直接放到 domain 的下一級(jí)。但使用組可以支持像群呼、代接等業(yè)務(wù)。使用 group_call 可以同時(shí)或順序的呼叫某個(gè)組的用戶。
實(shí)際用戶相關(guān)的設(shè)置也很直觀,下面顯示了 alice 這個(gè)用戶的設(shè)置:
<user id="alice">
<params>
<param name="password" value="$${default_password}"/>
<param name="vm-password" value="alice"/>
</params>
<variables>
<variable name="toll_allow" value="domestic,international,local"/>
<variable name="accountcode" value="alice"/>
<variable name="user_context" value="default"/>
<variable name="effective_caller_id_name" value="Extension 1000"/>
<variable name="effective_caller_id_number" value="1000"/>
<variable name="outbound_caller_id_name" value="$${outbound_caller_name}"/>
<variable name="outbound_caller_id_number" value="$${outbound_caller_id}"/>
<variable name="callgroup" value="techsupport"/>
</variables>
</user>
由上面可以看到,實(shí)際上 params 和 variables 可以出現(xiàn)在 user 節(jié)點(diǎn)中,也可以出現(xiàn)在 group 或 domain 中。 當(dāng)它們有重復(fù)時(shí),優(yōu)先級(jí)順序?yàn)?user,group,domain。
當(dāng)然,用戶目錄還有一些更復(fù)雜的設(shè)置,我們留待以后再做研究。
再?gòu)?fù)習(xí)一下,F(xiàn)reeSWITCH是一個(gè)B2BUA,我們還是以第四章中的圖為例:
主要呼叫流程有以下兩種:
FreeSWITCH 同時(shí)呼叫 bob 和 alice,兩者接電話后 FreeSWITCH 將 a-leg 和 b-leg 橋接(bridge)到一起,兩者通話。
其中第二種又有一種變種。如市場(chǎng)上有人利用上、下行通話的不對(duì)稱性賣電話回?fù)芸ǐ@取不正當(dāng)利潤(rùn):bob 呼叫 FreeSWITCH,F(xiàn)reeSWITCH 不應(yīng)答,而是在獲取 bob 的主叫號(hào)碼后直接掛機(jī);然后 FreeSWITCH 回?fù)?bob;bob 接聽后 FreeSWITCH 啟動(dòng)一個(gè) IVR 程序指示 bob 輸入 alice 的號(hào)碼;然后 FreeSWITCH 呼叫 Alice……
在實(shí)際應(yīng)用中,由于涉及回鈴音、呼叫失敗等,實(shí)際情況要復(fù)雜的多。
對(duì)每一次呼叫,F(xiàn)reeSWITCH 都會(huì)啟動(dòng)一個(gè) Session(會(huì)話,它包含SIP會(huì)話,SIP會(huì)在每對(duì)UAC-UAS之間生成一個(gè) SIP Session),用于控制整個(gè)呼叫,它會(huì)一直持續(xù)到通話結(jié)束。其中,每個(gè) Session 都控制著一個(gè) Channel(信道),Channel 是一對(duì) UA 間通信的實(shí)體,相當(dāng)于 FreeSWITCH 的一條腿(leg),每個(gè) Channel 都有一個(gè)唯一的 UUID。另外,Channel 上可以綁定一些呼叫參數(shù),稱為 Channel Variable(信道變量)。Channel 中可能包含媒體(音頻或視頻流),也可能不包含。通話時(shí),F(xiàn)reeSWITCH 的作用是將兩個(gè) Channel(a-leg 和 b-leg,通常先創(chuàng)建的或占主動(dòng)的叫 a-leg)橋接(bridge)到一起,使雙方可以通話。
通話中,媒體(音頻或視頻)數(shù)據(jù)流在 RTP 包中傳送(不同于 SIP, RTP是另外的協(xié)議)。一般來(lái)說(shuō),Channel是雙向的,因此,媒體流會(huì)有發(fā)送(Send/Write)和接收(Receive/Read)兩個(gè)方向。
A ------ |a 交換機(jī) | ---X--- | 交換機(jī) b| -------- B
為了便于說(shuō)明,我們假定A與B不在同一臺(tái)服務(wù)器上(如在PSTN通話中可能不在同一座城市),中間需要經(jīng)過(guò)多級(jí)服務(wù)器的中轉(zhuǎn)。
假設(shè)上圖是在 PSTN 網(wǎng)絡(luò)中,A 呼叫 B,B 話機(jī)開始振鈴,A 端聽回鈴音(Ring Back Tone)。在早期,B 端所在的交換機(jī)只給 A 端交換機(jī)送地址全(ACM)信號(hào),證明呼叫是可以到達(dá) B 的,A 端聽到的回鈴音鈴流是由 A 端所在的交換機(jī)生成并發(fā)送的。但后來(lái),為了在 A 端能聽到 B 端特殊的回鈴音(如“您撥打的電話正在通話中…” 或 “對(duì)方暫時(shí)不方便接聽您的電話” 尤其是現(xiàn)代交換機(jī)支持各種個(gè)性化的彩鈴 - Ring Back Color Tone 等),回鈴音就只能由 B 端交換機(jī)發(fā)送。在 B 接聽電話前,回鈴音和彩鈴是不收費(fèi)的(不收取本次通話費(fèi)。彩鈴費(fèi)用一般是在 B 端以月租或套餐形式收取的)。這些回鈴音就稱為 Early Media(早期媒體)。它是由 SIP 的183(帶有SDP)消息描述的。
理論上講,B 接聽電話后交換機(jī) b 可以一直不向 a 交換機(jī)發(fā)送應(yīng)答消息,而將真正的話音數(shù)據(jù)偽裝成 Early Media,以實(shí)現(xiàn)“免費(fèi)通話”。
在每一個(gè) Channel 上都可以設(shè)置好多 Variable,稱為信道變量。FreeSWITCH 呼叫過(guò)程中,會(huì)根據(jù)這些變量控制 Channel 的行為。
${var} 是在 dialplan、application 或 directory 中設(shè)置的變量,它會(huì)影響呼叫流程并且可以動(dòng)態(tài)的改變。而 $${var} 則是全局的變量,它僅在預(yù)處理階段(系統(tǒng)啟動(dòng)時(shí),或重新裝載 - reloadxml時(shí))被求值。后者一般用于設(shè)置一些系統(tǒng)一旦啟動(dòng)就不會(huì)輕易改變的量,如 $${domain} 或 $${local_ip_v4}等。所以,兩者最大的區(qū)別是,$${var} 只求值一次,而 ${var} 則在每次執(zhí)行時(shí)求值(如一個(gè)新電話進(jìn)來(lái)時(shí))。
你會(huì)發(fā)現(xiàn),有些變量在顯示時(shí)(可以使用dp_tools 中的 info() 顯示,后面會(huì)講到)是以“variable_”開頭的,但在實(shí)際引用時(shí)要去掉這開頭的“variable_”。如“variable_user_name”,引用時(shí)要使用“${user_name}”。http://wiki.freeswitch.org/wiki/Channel_Variables#variable_xxxx 列舉了一些常見的變量顯示與引用時(shí)的對(duì)應(yīng)關(guān)系。
在 dialplan 中,有兩個(gè)程序可以給 Variable 賦值:
<action application="set" data="my_var=my_value"/>
<action application="export" data="my_var=my_value"/>
以上兩條命令都可以設(shè)置 my_var 變量的值為 my_value。不同的是 -- set 程序僅會(huì)作用于“當(dāng)前”的 Channel (a-leg),而 export 程序則會(huì)將變量設(shè)置到兩個(gè) Channel (a-leg 和 b-leg)上,如果當(dāng)時(shí) b-leg 還沒(méi)有創(chuàng)建,則會(huì)在創(chuàng)建時(shí)設(shè)置。另外,export 還可以只將變量設(shè)置到 b-leg 上:
<action appliction="export" data="nolocal:my_var=my_value"/>
在實(shí)際應(yīng)用中,如果 a-leg 上已經(jīng)有一些變量的值(如 var1、var2、var3),而想同時(shí)把這些變量都復(fù)制到 b-leg 上,可以使用以下幾種辦法:
<action application="export" data="var1=$var1"/>
<action application="export" data="var2=$var2"/>
<action application="export" data="var3=$var3"/>
或者使用如下等價(jià)的方式:
<action application="set" data="export_vars=var1,var2,var3">
所以,其實(shí) set 也具有能往 b-leg 上賦值的能力,其實(shí),它和 export 一樣,都是操作 export_vars 這個(gè)特殊的變量。
取消 Variable 定義
取消 Variable 定義只需對(duì)它賦一個(gè)特殊的值_undef_”:
<action application="set" data="var1=_undef_">
截取 Variable 的一部分
可以使用特殊的語(yǔ)法取一個(gè) Variable 的子串,格式是“${var:位置:長(zhǎng)度}”。其中 “位置” 從 0 開始計(jì)烽,若為負(fù)數(shù)則從字符串尾部開始計(jì)數(shù);如果“長(zhǎng)度”為 0 或小于 0,則會(huì)從當(dāng)前“位置”一直取到字符串結(jié)尾(或開頭,若“位置”為負(fù)的話)。例如 var 的值為 1234567890,那么:
${var} = 1234567890
${var:0:1} = 1
${var:1} = 234567890
${var:-4} = 7890
${var:-4:2} = 78
${var:4:2} = 56
本章描述了 FreeSWITCH 的架構(gòu)。到這里,讀者應(yīng)該對(duì) FreeSWITCH 有了一個(gè)總體的了解。我們也提到了一些基本元素和概念,簡(jiǎn)單介紹了配置文件的基本結(jié)構(gòu),由于脫離了實(shí)際單講配置會(huì)比較抽象,因此,我們把具體的配置也寫到后面的章節(jié)里,即,用到時(shí)再說(shuō)。