freeswitch的event事件處理

概述

之前的文章中辜羊,我們講解了freeswitch的源碼基本結(jié)構(gòu),如何新增一個插件式模塊馒稍,以及如何在模塊中新增一個命令式API接口和APP接口构罗。

freeswitch本身是事件驅(qū)動的,它可以并發(fā)響應(yīng)多個事件溪北,也可以廣播事件桐绒。

freeswitch的事件可以由核心產(chǎn)生夺脾,也可以由外部模塊或外部源產(chǎn)生。

freeswitch系統(tǒng)中的幾乎所有事件都會產(chǎn)生事件消息掏膏,這些事件可以被外部實體監(jiān)聽(通過event socket)劳翰,也可以被內(nèi)部模塊監(jiān)聽。

freeswitch的事件系統(tǒng)是雙向的馒疹,除了允許外部程序監(jiān)聽事件外佳簸,外部程序還可以向freeswitch發(fā)送事件。

你可以從自己的程序中實時發(fā)送/接收事件颖变。這種組合允許你以幾乎任何你能想到的方式使用freeswitch生均。


通道事件

在freeswitch的事件系統(tǒng)中,有一類以“CHANNEL_“開頭的事件腥刹,這些事件表示了一個呼叫通道(channel)的狀態(tài)變化的全部過程马胧,是我們在業(yè)務(wù)開發(fā)中最常用的一類事件。

常見的通道事件:

CHANNEL_ANSWER

CHANNEL_APPLICATION

CHANNEL_BRIDGE

CHANNEL_CALLSTATE

CHANNEL_CREATE

CHANNEL_DATA

CHANNEL_DESTROY

CHANNEL_EXECUTE

CHANNEL_EXECUTE_COMPLETE

CHANNEL_GLOBAL

CHANNEL_HANGUP

CHANNEL_HANGUP_COMPLETE

CHANNEL_HOLD

CHANNEL_ORIGINATE

CHANNEL_OUTGOING

CHANNEL_PARK

CHANNEL_PROGRESS

CHANNEL_PROGRESS_MEDIA

CHANNEL_STATE

CHANNEL_UNBRIDGE

CHANNEL_UNHOLD

CHANNEL_UNPARK

CHANNEL_UUID

通道事件可以攜帶一通呼叫的全部呼叫信息衔峰,也可以攜帶呼叫流程中的自定義信息佩脊,這個屬性讓我們可以很方便的在一通呼叫的不同階段之間傳遞自定義參數(shù)。

本節(jié)我們來介紹如何在模塊中增加一個channel event事件處理垫卤,并傳遞一個自定義參數(shù)威彰。


開發(fā)環(huán)境

centos:CentOS release 7.0 (Final)或以上版本

freeswitch:v1.8.7

GCC:4.8.5


代碼處理

新增模塊的方法請參考之前的內(nèi)容征绸,本節(jié)內(nèi)容在模塊mod_task的基礎(chǔ)上修改结榄。

mod_task.c內(nèi)容如下:

#include <switch.h>

SWITCH_MODULE_LOAD_FUNCTION(mod_task_load);

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_task_shutdown);

SWITCH_MODULE_DEFINITION(mod_task, mod_task_load, mod_task_shutdown, NULL);

SWITCH_STANDARD_API(task_api_function)

{

? ? //SWITCH_STANDARD_API have three args: (cmd, session, stream)

? ? char *mycmd = NULL;

? ? int argc = 0;

? ? char *argv[16];

? ? bzero(argv, sizeof(argv));

? ? //split cmd and parse

? ? if (cmd)

? ? {

? ? ? ? mycmd = strdup(cmd);

? ? ? ? if (!mycmd)

? ? ? ? {

? ? ? ? ? ? stream->write_function(stream, "Out of memory\n");

? ? ? ? ? ? return SWITCH_STATUS_FALSE;? ?

? ? ? ? }


? ? ? ? if (!(argc = switch_split(mycmd, ' ', argv)) || !argv[0])

? ? ? ? {

? ? ? ? ? ? argc = 0;

? ? ? ? ? ? switch_safe_free(mycmd);

? ? ? ? ? ? return SWITCH_STATUS_FALSE;

? ? ? ? }

? ? }

? ? //parse cmd, brach process

? ? if(0 == strcmp("test1", argv[0]))

? ? {

? ? ? ? stream->write_function(stream, "task api test1, cmd:%s, session:%p", cmd, session);

? ? }

? ? else if(0 == strcmp("test2", argv[0]))

? ? {

? ? ? ? stream->write_function(stream, "task api test2, cmd:%s, session:%p", cmd, session);

? ? }

? ? else

? ? {

? ? ? ? stream->write_function(stream, "unknown cmd, cmd:%s, session:%p", cmd, session);

? ? }? ?

? ? switch_safe_free(mycmd);

return SWITCH_STATUS_SUCCESS;

}

SWITCH_STANDARD_APP(task_app_function)

{

? ? switch_channel_t *pchannel = NULL;

? ? //task_app(session, data);

? ? switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,

? ? ? ? ? ? "task_app_function start, session=%p, data=%s\n", (void*)session, data);

? ? //export variable task_str for hangup event

? ? pchannel = switch_core_session_get_channel(session);

? ? if(NULL != pchannel)

? ? {

? ? ? ? switch_channel_export_variable(pchannel, "task_str", "task_app export variable", SWITCH_EXPORT_VARS_VARIABLE);

? ? }

}

void task_event_channel_hangup_complete(switch_event_t *event)

{

? ? const char *uuid = switch_event_get_header(event, "Unique-ID");

? ? const char *call_dir = switch_event_get_header(event, "Call-Direction");

? ? const char* task_str = switch_event_get_header(event, "variable_task_str");

? ? switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,

? ? ? ? "task_event_channel_hangup_complete, uuid=%s, call_dir=%s, task_str=%s\n",

? ? ? ? uuid, call_dir, task_str);

}

void task_event_handler(switch_event_t *event)

{

? ? switch (event->event_id)

? ? {

? ? ? ? case SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE:

? ? ? ? ? ? task_event_channel_hangup_complete(event);

? ? ? ? ? ? break;

? ? ? ? case SWITCH_EVENT_CHANNEL_ANSWER:

? ? ? ? default:

? ? ? ? ? ? switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,

? ? ? ? ? ? ? ? "unsupported event. event:%d\n", event->event_id);

? ? ? ? ? ? break;

? ? }

? ? return;

}

SWITCH_MODULE_LOAD_FUNCTION(mod_task_load)

{

switch_api_interface_t* api_interface = NULL;

? ? switch_application_interface_t* app_interface = NULL;

? ? *module_interface = switch_loadable_module_create_module_interface(pool, modname);

switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,

? ? ? ? ? ? "mod_task_load start\n");

? ? // register APP

? ? SWITCH_ADD_APP(app_interface,

? ? "task_app",

? ? "task_app",

? ? "task_app",

? ? task_app_function,

? ? "NULL",

? ? SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);

? ? // register API

? ? SWITCH_ADD_API( api_interface,

? ? ? ? ? ? ? ? ? ? "task",

? ? ? ? ? ? ? ? ? ? "task api",

? ? ? ? ? ? ? ? ? ? task_api_function,

? ? ? ? ? ? ? ? ? ? "<cmd> <args>");


? ? // 注冊終端命令自動補全

? ? switch_console_set_complete("add task test1 [args]");

? ? switch_console_set_complete("add task test2 [args]");

? ? ///////////////EVENT INIT////////////////////

? ? if (SWITCH_STATUS_SUCCESS != switch_event_bind(modname, SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE, SWITCH_EVENT_SUBCLASS_ANY, task_event_handler, NULL)){

? ? ? ? switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't bind event\n");

? ? ? ? return SWITCH_STATUS_GENERR;

? ? }

? ? return SWITCH_STATUS_SUCCESS;

}

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_task_shutdown)

{

switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,

? ? ? ? ? ? "mod_task_shutdown stop\n");

return SWITCH_STATUS_SUCCESS;

}

呼叫處理過程中,我們在“task_app_function“函數(shù)中通過”switch_channel_export_variable“接口設(shè)置了一個自定義通道變量”task_str“的值為”task_app export variable“魏滚。

然后在呼叫的掛機事件中评抚,我們又取出了消息頭域中“variable_task_str“的值并打印到日志中豹缀。

通過這種方式,我們可以在呼叫流程的不同階段傳遞任意自定義參數(shù)慨代。


編譯安裝

進入task模塊目錄邢笙,編譯安裝,在Makefile.am文件未變化的情況下侍匙,不需要重新config氮惯。

cd? $(top_srcdir)/src/mod/applications/mod_task

make? install


配置啟動

修改dialplan撥號計劃

cd /usr/local/freeswitch/conf/dialplan

vi public.xml

<include>

? <context name="public">

? ? <extension name="test">

? ? ? <condition>

? ? ? ? <action application="task_app" data="${destination_number}"/>

? ? ? </condition>

? ? </extension>


啟動fs

cd /usr/local/freeswitch/bin/

./freeswitch –nonat


加載測試

freeswitch啟動成功后,在freeswitch命令行中輸入API命令加載mod_task模塊:

freeswitch@localhost.localdomain> load mod_task

2021-09-03 11:34:50.954223 [INFO] mod_enum.c:882 ENUM Reloaded

2021-09-03 11:34:50.954223 [INFO] mod_task.c:134 mod_task_load start

2021-09-03 11:34:50.954223 [CONSOLE] switch_loadable_module.c:1540 Successfully Loaded [mod_task]

2021-09-03 11:34:50.954223 [NOTICE] switch_loadable_module.c:292 Adding Application 'task_app'

+OK Reloading XML

+OK

2021-09-03 11:34:50.954223 [NOTICE] switch_loadable_module.c:338 Adding API Function 'task'

通過其他sip服務(wù)器發(fā)起invite呼叫到本機的5080端口丈积,在日志中可以查看到:

freeswitch@localhost.localdomain> 2021-09-03 11:34:56.614251 [NOTICE] switch_channel.c:1114 New Channel sofia/external/10011@192.168.0.110 [a34a67c3-2b8d-401f-a16f-1f5ec3e5169f]

2021-09-03 11:34:56.614251 [INFO] mod_dialplan_xml.c:637 Processing 10011 <10011>->10012 in context public

2021-09-03 11:34:56.614251 [INFO] mod_task.c:88 task_app_function start, session=0x7fbb8402fab8, data=10012

2021-09-03 11:34:56.614251 [NOTICE] switch_core_state_machine.c:385 sofia/external/10011@192.168.0.110 has executed the last dialplan instruction, hanging up.

2021-09-03 11:34:56.614251 [NOTICE] switch_core_state_machine.c:387 Hangup sofia/external/10011@192.168.0.110 [CS_EXECUTE] [NORMAL_CLEARING]

2021-09-03 11:34:56.614251 [INFO] mod_task.c:105 task_event_channel_hangup_complete, uuid=a34a67c3-2b8d-401f-a16f-1f5ec3e5169f, call_dir=inbound, task_str=task_app export variable

2021-09-03 11:34:56.614251 [NOTICE] switch_core_session.c:1744 Session 1 (sofia/external/10011@192.168.0.110) Ended

2021-09-03 11:34:56.614251 [NOTICE] switch_core_session.c:1748 Close Channel sofia/external/10011@192.168.0.110 [CS_DESTROY]

在日志中筐骇,可以看到“task_app_function start “的信息债鸡,同時也可以看到” task_event_channel_hangup_complete “的函數(shù)打印中”task_str=task_app export variable “的打印信息江滨,驗證了在呼叫中設(shè)置的自定義參數(shù)傳遞到掛機事件的后處理的過程


總結(jié)

freeswitch的event事件是整個架構(gòu)體系中非常重要的一環(huán)厌均,基礎(chǔ)核心層通過event事件將所有呼叫相關(guān)的信息異步的通知到應(yīng)用層唬滑,極大的方便了呼叫流程的業(yè)務(wù)開發(fā)。

其中的異步設(shè)計思想值得我們多多參考學(xué)習(xí)。



空空如常

求真得真

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晶密,一起剝皮案震驚了整個濱河市擒悬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稻艰,老刑警劉巖懂牧,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尊勿,居然都是意外死亡僧凤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門元扔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躯保,“玉大人,你說我怎么就攤上這事澎语⊥臼拢” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵擅羞,是天一觀的道長尸变。 經(jīng)常有香客問我,道長祟滴,這世上最難降的妖魔是什么振惰? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮垄懂,結(jié)果婚禮上骑晶,老公的妹妹穿的比我還像新娘。我一直安慰自己草慧,他們只是感情好桶蛔,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著漫谷,像睡著了一般仔雷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舔示,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天碟婆,我揣著相機與錄音,去河邊找鬼惕稻。 笑死竖共,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俺祠。 我是一名探鬼主播公给,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼借帘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淌铐?” 一聲冷哼從身側(cè)響起肺然,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腿准,沒想到半個月后际起,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡吐葱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年加叁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唇撬。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡它匕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窖认,到底是詐尸還是另有隱情豫柬,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布扑浸,位于F島的核電站烧给,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏喝噪。R本人自食惡果不足惜础嫡,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酝惧。 院中可真熱鬧榴鼎,春花似錦、人聲如沸晚唇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哩陕。三九已至平项,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悍及,已是汗流浹背闽瓢。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留心赶,地道東北人扣讼。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像园担,于是被迫代替她去往敵國和親届谈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 概述 之前的文章中弯汰,我們講解了freeswitch的源碼基本結(jié)構(gòu)艰山,如何新增一個插件式模塊,以及如何在模塊中新增一個...
    求真得真閱讀 688評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理咏闪,服務(wù)發(fā)現(xiàn)曙搬,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 目錄 1. 安裝freeswitch├── 1.1 相關(guān)地址├── 1.2 安裝基礎(chǔ)包├── 1.3 安裝依賴包├...
    Jetsly閱讀 67,207評論 8 25
  • 1.查看網(wǎng)關(guān)注冊狀態(tài) sofia status 2.橋接(未實踐) http://wiki.freeswitch....
    幽瀾先生閱讀 3,794評論 0 1
  • 概述 上一章我們講解了freeswitch的源碼基本結(jié)構(gòu)鸽嫂,以及如何新增一個插件式模塊纵装。 freeswitch的架構(gòu)...
    求真得真閱讀 935評論 0 0