概述

之前的文章中,我们讲解了freeswitch的源码基本结构,如何新增一个插件式模块,以及如何在模块中新增一个命令式API接口和APP接口。

freeswitch本身是事件驱动的,它可以并发响应多个事件,也可以广播事件。

freeswitch的事件可以由核心产生,也可以由外部模块或外部源产生。

freeswitch系统中的几乎所有事件都会产生事件消息,这些事件可以被外部实体监听(通过event socket),也可以被内部模块监听。

freeswitch的事件系统是双向的,除了允许外部程序监听事件外,外部程序还可以向freeswitch发送事件。

你可以从自己的程序中实时发送/接收事件。这种组合允许你以几乎任何你能想到的方式使用freeswitch。

通道事件

在freeswitch的事件系统中,有一类以“CHANNEL_“开头的事件,这些事件表示了一个呼叫通道(channel)的状态变化的全部过程,是我们在业务开发中最常用的一类事件。

常见的通道事件:

  1. CHANNEL_ANSWER
  2.  
  3. CHANNEL_APPLICATION
  4.  
  5. CHANNEL_BRIDGE
  6.  
  7. CHANNEL_CALLSTATE
  8.  
  9. CHANNEL_CREATE
  10.  
  11. CHANNEL_DATA
  12.  
  13. CHANNEL_DESTROY
  14.  
  15. CHANNEL_EXECUTE
  16.  
  17. CHANNEL_EXECUTE_COMPLETE
  18.  
  19. CHANNEL_GLOBAL
  20.  
  21. CHANNEL_HANGUP
  22.  
  23. CHANNEL_HANGUP_COMPLETE
  24.  
  25. CHANNEL_HOLD
  26.  
  27. CHANNEL_ORIGINATE
  28.  
  29. CHANNEL_OUTGOING
  30.  
  31. CHANNEL_PARK
  32.  
  33. CHANNEL_PROGRESS
  34.  
  35. CHANNEL_PROGRESS_MEDIA
  36.  
  37. CHANNEL_STATE
  38.  
  39. CHANNEL_UNBRIDGE
  40.  
  41. CHANNEL_UNHOLD
  42.  
  43. CHANNEL_UNPARK
  44.  
  45. CHANNEL_UUID

通道事件可以携带一通呼叫的全部呼叫信息,也可以携带呼叫流程中的自定义信息,这个属性让我们可以很方便的在一通呼叫的不同阶段之间传递自定义参数。

本节我们来介绍如何在模块中增加一个channel event事件处理,并传递一个自定义参数。

开发环境

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

freeswitch:v1.8.7

GCC:4.8.5

代码处理

新增模块的方法请参考之前的内容,本节内容在模块mod_task的基础上修改。

mod_task.c内容如下:

  1. #include <switch.h>
  2.  
  3. SWITCH_MODULE_LOAD_FUNCTION(mod_task_load);
  4.  
  5. SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_task_shutdown);
  6.  
  7. SWITCH_MODULE_DEFINITION(mod_task, mod_task_load, mod_task_shutdown, NULL);
  8.  
  9. SWITCH_STANDARD_API(task_api_function)
  10.  
  11. {
  12.  
  13. //SWITCH_STANDARD_API have three args: (cmd, session, stream)
  14.  
  15. char *mycmd = NULL;
  16.  
  17. int argc = 0;
  18.  
  19. char *argv[16];
  20.  
  21. bzero(argv, sizeof(argv));
  22.  
  23. //split cmd and parse
  24.  
  25. if (cmd)
  26.  
  27. {
  28.  
  29. mycmd = strdup(cmd);
  30.  
  31. if (!mycmd)
  32.  
  33. {
  34.  
  35. stream->write_function(stream, "Out of memory\n");
  36.  
  37. return SWITCH_STATUS_FALSE;
  38.  
  39. }
  40.  
  41. if (!(argc = switch_split(mycmd, ' ', argv)) || !argv[0])
  42.  
  43. {
  44.  
  45. argc = 0;
  46.  
  47. switch_safe_free(mycmd);
  48.  
  49. return SWITCH_STATUS_FALSE;
  50.  
  51. }
  52.  
  53. }
  54.  
  55. //parse cmd, brach process
  56.  
  57. if(0 == strcmp("test1", argv[0]))
  58.  
  59. {
  60.  
  61. stream->write_function(stream, "task api test1, cmd:%s, session:%p", cmd, session);
  62.  
  63. }
  64.  
  65. else if(0 == strcmp("test2", argv[0]))
  66.  
  67. {
  68.  
  69. stream->write_function(stream, "task api test2, cmd:%s, session:%p", cmd, session);
  70.  
  71. }
  72.  
  73. else
  74.  
  75. {
  76.  
  77. stream->write_function(stream, "unknown cmd, cmd:%s, session:%p", cmd, session);
  78.  
  79. }
  80.  
  81. switch_safe_free(mycmd);
  82.  
  83. return SWITCH_STATUS_SUCCESS;
  84.  
  85. }
  86.  
  87. SWITCH_STANDARD_APP(task_app_function)
  88.  
  89. {
  90.  
  91. switch_channel_t *pchannel = NULL;
  92.  
  93. //task_app(session, data);
  94.  
  95. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
  96.  
  97. "task_app_function start, session=%p, data=%s\n", (void*)session, data);
  98.  
  99. //export variable task_str for hangup event
  100.  
  101. pchannel = switch_core_session_get_channel(session);
  102.  
  103. if(NULL != pchannel)
  104.  
  105. {
  106.  
  107. switch_channel_export_variable(pchannel, "task_str", "task_app export variable", SWITCH_EXPORT_VARS_VARIABLE);
  108.  
  109. }
  110.  
  111. }
  112.  
  113. void task_event_channel_hangup_complete(switch_event_t *event)
  114.  
  115. {
  116.  
  117. const char *uuid = switch_event_get_header(event, "Unique-ID");
  118.  
  119. const char *call_dir = switch_event_get_header(event, "Call-Direction");
  120.  
  121. const char* task_str = switch_event_get_header(event, "variable_task_str");
  122.  
  123. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
  124.  
  125. "task_event_channel_hangup_complete, uuid=%s, call_dir=%s, task_str=%s\n",
  126.  
  127. uuid, call_dir, task_str);
  128.  
  129. }
  130.  
  131. void task_event_handler(switch_event_t *event)
  132.  
  133. {
  134.  
  135. switch (event->event_id)
  136.  
  137. {
  138.  
  139. case SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE:
  140.  
  141. task_event_channel_hangup_complete(event);
  142.  
  143. break;
  144.  
  145. case SWITCH_EVENT_CHANNEL_ANSWER:
  146.  
  147. default:
  148.  
  149. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
  150.  
  151. "unsupported event. event:%d\n", event->event_id);
  152.  
  153. break;
  154.  
  155. }
  156.  
  157. return;
  158.  
  159. }
  160.  
  161. SWITCH_MODULE_LOAD_FUNCTION(mod_task_load)
  162.  
  163. {
  164.  
  165. switch_api_interface_t* api_interface = NULL;
  166.  
  167. switch_application_interface_t* app_interface = NULL;
  168.  
  169. *module_interface = switch_loadable_module_create_module_interface(pool, modname);
  170.  
  171. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
  172.  
  173. "mod_task_load start\n");
  174.  
  175. // register APP
  176.  
  177. SWITCH_ADD_APP(app_interface,
  178.  
  179. "task_app",
  180.  
  181. "task_app",
  182.  
  183. "task_app",
  184.  
  185. task_app_function,
  186.  
  187. "NULL",
  188.  
  189. SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
  190.  
  191. // register API
  192.  
  193. SWITCH_ADD_API( api_interface,
  194.  
  195. "task",
  196.  
  197. "task api",
  198.  
  199. task_api_function,
  200.  
  201. "<cmd> <args>");
  202.  
  203. // 注册终端命令自动补全
  204.  
  205. switch_console_set_complete("add task test1 [args]");
  206.  
  207. switch_console_set_complete("add task test2 [args]");
  208.  
  209. ///////////////EVENT INIT////////////////////
  210.  
  211. if (SWITCH_STATUS_SUCCESS != switch_event_bind(modname, SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE, SWITCH_EVENT_SUBCLASS_ANY, task_event_handler, NULL)){
  212.  
  213. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't bind event\n");
  214.  
  215. return SWITCH_STATUS_GENERR;
  216.  
  217. }
  218.  
  219. return SWITCH_STATUS_SUCCESS;
  220.  
  221. }
  222.  
  223. SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_task_shutdown)
  224.  
  225. {
  226.  
  227. switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
  228.  
  229. "mod_task_shutdown stop\n");
  230.  
  231. return SWITCH_STATUS_SUCCESS;
  232.  
  233. }

呼叫处理过程中,我们在“task_app_function“函数中通过”switch_channel_export_variable“接口设置了一个自定义通道变量”task_str“的值为”task_app export variable“。

然后在呼叫的挂机事件中,我们又取出了消息头域中“variable_task_str“的值并打印到日志中。

通过这种方式,我们可以在呼叫流程的不同阶段传递任意自定义参数

编译安装

进入task模块目录,编译安装,在Makefile.am文件未变化的情况下,不需要重新config。

  1. cd $(top_srcdir)/src/mod/applications/mod_task
  2.  
  3. make install

配置启动

修改dialplan拨号计划

  1. cd /usr/local/freeswitch/conf/dialplan
  2.  
  3. vi public.xml
  4.  
  5.  
  6. <include>
  7.  
  8. <context name="public">
  9.  
  10. <extension name="test">
  11.  
  12. <condition>
  13.  
  14. <action application="task_app" data="${destination_number}"/>
  15.  
  16. </condition>
  17.  
  18. </extension>
  19.  

启动fs

  1. cd /usr/local/freeswitch/bin/
  2.  
  3. ./freeswitch nonat

加载测试

freeswitch启动成功后,在freeswitch命令行中输入API命令加载mod_task模块:

  1. freeswitch@localhost.localdomain> load mod_task
  2.  
  3. 2021-09-03 11:34:50.954223 [INFO] mod_enum.c:882 ENUM Reloaded
  4.  
  5. 2021-09-03 11:34:50.954223 [INFO] mod_task.c:134 mod_task_load start
  6.  
  7. 2021-09-03 11:34:50.954223 [CONSOLE] switch_loadable_module.c:1540 Successfully Loaded [mod_task]
  8.  
  9. 2021-09-03 11:34:50.954223 [NOTICE] switch_loadable_module.c:292 Adding Application 'task_app'
  10.  
  11. +OK Reloading XML
  12.  
  13. +OK
  14.  
  15. 2021-09-03 11:34:50.954223 [NOTICE] switch_loadable_module.c:338 Adding API Function 'task'

通过其他sip服务器发起invite呼叫到本机的5080端口,在日志中可以查看到:

  1. 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]
  2.  
  3. 2021-09-03 11:34:56.614251 [INFO] mod_dialplan_xml.c:637 Processing 10011 <10011>->10012 in context public
  4.  
  5. 2021-09-03 11:34:56.614251 [INFO] mod_task.c:88 task_app_function start, session=0x7fbb8402fab8, data=10012
  6.  
  7. 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.
  8.  
  9. 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]
  10.  
  11. 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
  12.  
  13. 2021-09-03 11:34:56.614251 [NOTICE] switch_core_session.c:1744 Session 1 (sofia/external/10011@192.168.0.110) Ended
  14.  
  15. 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 “的函数打印中”task_str=task_app export variable “的打印信息,验证了在呼叫中设置的自定义参数传递到挂机事件的后处理的过程

总结

freeswitch的event事件是整个架构体系中非常重要的一环,基础核心层通过event事件将所有呼叫相关的信息异步的通知到应用层,极大的方便了呼叫流程的业务开发。

其中的异步设计思想值得我们多多参考学习。


空空如常

求真得真

freeswitch的event事件处理的更多相关文章

  1. firefox 的event事件处理

    前几天,在用angularJs实现一个功能,点击后获取event的x,y坐标时,IE9, chrome下功能正常.但是firefox报event 未定义.初始代码如下: html: <div c ...

  2. js中event事件处理

    1. HTML事件  直接添加到HTML结构中 function show() { alert('hello'); } <body> <button id="btn&quo ...

  3. 15.深入k8s:Event事件处理及其源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 概述 k8s的Event事件是一种资源对象,用于展示集群内发生的情况 ...

  4. jQuery和ExtJS的timeOut超时问题和event事件处理问题

    对jQuery来说,超时可以直接设置timeout参数,并在error事件中捕获第二个参数,如果是“timeout”则表明捕获了超时事件,非常清楚. 例子: $.ajax({         type ...

  5. Yii2的深入学习--事件Event

    我们先来看下事件在 Yii2 中的使用,如下内容摘自 Yii2中文文档 事件可以将自定义代码“注入”到现有代码中的特定执行点.附加自定义代码到某个事件,当这个事件被触发时,这些代码就会自动执行.例如, ...

  6. React使用笔记(3)-React Event Listener

    Date: 2015-11-28 12:18 Category: Web Tags: JavaScript Author: 刘理想 [toc] 1. 构造基本结构 首先,我们先创建一个按钮,一个输入框 ...

  7. Android学习笔记-事件处理

    第三章 Android的事件处理 Android提供两种事件处理方式,基于回调和基于监听器.前者常用于传统图形界面编程中,而后者在AWT/Swing开发中常用. 3.1 事件处理概述 对于基于回调的事 ...

  8. Freeswitch 入门

    让我们从最初的运行开始,一步一步进入 FreeSWITCH 的神秘世界. 命令行参数 一般来说,FreeSWITCH 不需要任何命令行参数就可以启动,但在某些情况下,你需要以一些特殊的参数启动.在此, ...

  9. go语言使用go-sciter创建桌面应用(六) Element元素操作和Event事件响应

    详细的文档请看下面两个链接: https://sciter.com/docs/content/sciter/Element.htm https://sciter.com/docs/content/sc ...

随机推荐

  1. 【阅读笔记】Java核心技术卷一 #2.Chapter4

    4 对象和类 4.1 面向对象程序设计概述(略) 4.2 使用预定义类 java.time.LocalDate static LocalDate now(); static LocalDate of( ...

  2. 云平台制作(1)-OPC Client取数模块的制作

    近来由于工程需要,基于OPC DA 2.0搭建通用的取数模块,与远程webscoket服务端连接,并传输数据.在网上找了些资料,修改相应网友公开的源代码,基本达到要求,特供大家参考. 1.实体类 us ...

  3. Go语言基础知识总结(持续中)

    Go基础知识总结 变量声明 Go语言中的变量需要声明以后才可以使用(需要提前定义变量)并且声明后必须使用(不适用会报错) 标准声明 var 变量名 变量类型 example: var name str ...

  4. 分布式事务最终一致性-CAP框架轻松搞定

    前言 对于分布式事务,常用的解决方案根据一致性的程度可以进行如下划分: 强一致性(2PC.3PC):数据库层面的实现,通过锁定资源,牺牲可用性,保证数据的强一致性,效率相对比较低. 弱一致性(TCC) ...

  5. LVM磁盘配额

    目录 一.LVM概述 1.1.逻辑卷管理 1.2.LVM机制的基本概念 二.LVM管理命令 三.磁盘配额概述 3.1.实现磁盘配额的条件 3.2.Linux磁盘限额的特点 3.3.常用命令及选项 3. ...

  6. WPF上传图片到服务器文件夹

    1.前端用ListBox加载显示多张图片 1 <ListBox Name="lbHeadImages" Grid.Row="1" ScrollViewer ...

  7. Access, Modify, Change Time of Linux File

    All these 3 time can be viewed by "stat " command. Access time is influenced by read opera ...

  8. Golang语言系列-16-context上下文

    context上下文 控制子goroutine退出 全局变量方式 package main import ( "fmt" "sync" "time&q ...

  9. 【笔记】衡量线性回归法的指标 MSE,RMS,MAE以及评价回归算法 R Square

    衡量线性回归法的指标 MSE,RMS,MAE以及评价回归算法 R Square 衡量线性回归法的指标 对于分类问题来说,我们将原始数据分成了训练数据集和测试数据集两部分,我们使用训练数据集得到模型以后 ...

  10. Spring 学习笔记(3)Spring MVC

    一.什么是 MVC MVC 实际上就是一种设计模式 Model-View-Controller Model 模型其实就是数据,Dao,Bean 等等 View 视图就是所看到的东西,网页,JSP,展示 ...