参考:https://freeswitch.org/confluence/display/FREESWITCH/Creating+New+Modules
参考:https://freeswitch.org/confluence/display/FREESWITCH/mod_skel
1、新模块文件清单
Makefile.am # 参考autotools的Makefile.am语法
*.c # c源码
1.1、模块的Makefile.am
# 默认
include $(top_srcdir)/build/modmake.rulesam
# 模块名称,对应模块的文件夹名称
MODNAME=mod_event_redis
# 模块编译后lib文件名称
mod_LTLIBRARIES = mod_event_redis.la
# 模块入口资源文件
mod_event_redis_la_SOURCES = mod_event_redis.c
# 默认
mod_event_redis_la_CFLAGS = $(AM_CFLAGS)
mod_event_redis_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_event_redis_la_LDFLAGS = -avoid-version -module -no-undefined -shared
2、模块接口
// 使用这个宏定义了模块的加载函数,在这个函数中你应该初始化任何全局结构,连接任何事件或状态处理程序等。如果你返回除了 SWITCH_STATUS_SUCCESS 以外的任何东西,模块将不会继续被加载。
SWITCH_MODULE_LOAD_FUNCTION(load);
// 这是模块的运行时循环,从这里你可以监听套接字,产生新的线程来处理请求。等等
SWITCH_MODULE_RUNTIME_FUNCTION(runtime);
// 这是您为卸载或重新加载模块进行任何清理的地方,您应该释放状态处理程序、事件保留等。您还应该与关闭运行时线程同步(通常使用类似于关闭函数的共享“运行”变量设置为运行时函数注意到的某个值,设置为第三个值,然后退出)。
SWITCH_MODULE_SHUTDOWN_FUNCTION(shutdown);
// 模块定义(模块name,load函数,shutdown函数,runtime函数)
SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime);
// 实现加载模块时被调函数的逻辑
SWITCH_MODULE_LOAD_FUNCTION(load)
{
}
// 实现模块运行时被调函数的逻辑
SWITCH_MODULE_RUNTIME_FUNCTION(runtime)
{
}
// 实现模块停止时被调函数的逻辑
SWITCH_MODULE_SHUTDOWN_FUNCTION(shutdown)
{
}
3、基础函数
3.1、XML API
3.1.1、switch_xml_free(switch_xml_t xml)
释放switch_xml_t 类型内存
3.1.2、switch_xml_locate_domain (const char *domain_name, switch_event_t *params, switch_xml_t *root, switch_xml_t *domain)
获取domian信息,返回switch_status_t
3.1.3、switch_xml_locate_user(const char *key, const char *user_name, const char *domain_name, const char *ip, switch_xml_t *root, switch_xml_t *domain, switch_xml_t *user, switch_event_t *params)
获取用户信息,返回switch_status_t
3.1.4、switch_xml_open_cfg(const char *file_path, switch_xml_t *node, switch_event_t *params)
获取对应xml配置文件,返回switch_xml_t
file_path:配置部分的名称,例如 modules.conf
node:一个指针,如果找到,则指向该节点
params:可选的 URL 格式的参数传递给外部网关
3.2、绑定事件
3.2.1、订阅事件
switch_event_bind(const char *id, switch_event_types_t event, const char *subclass_name, switch_event_callback_t callback, void *user_data);
// id 是活页夹的标识符标记,使用您的模块名称
// event 是您想要接收的事件 SWITCH_EVENT_ALL 事件类型和 SWITCH_EVENT_SUBCLASS_ANY 子类
// subclass_name 显然是子类的名称
// user_data 是一个指针,它将在回调时传回给您叫做
// e.g.
static void event_handler(switch_event_t *event) {
}
// 绑定事件,并且回调指定的函数 event_handler()
if (switch_event_bind(modname, SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
}
3.2.2、根据通道状态订阅事件
// Globally 全局变量
switch_core_add_state_handler(const switch_state_handler_table_t *state_handler);
switch_core_remove_state_handler(const switch_state_handler_table_t *state_handler);
// On a specific channel 具体的通道
switch_channel_add_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler);
switch_channel_clear_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler);
// e.g.
switch_status_t on_my_callback(switch_core_session_t *session)
{
}
struct switch_state_handler_table_t state_handlers = {
/*switch_state_handler_t on_init;*/ NULL,
/*switch_state_handler_t on_routing;*/ NULL,
/*switch_state_handler_t on_execute;*/ NULL,
/*switch_state_handler_t on_hangup;*/ NULL,
/*switch_state_handler_t on_exchange_media;*/ NULL,
/*switch_state_handler_t on_soft_execute;*/ NULL,
/*switch_state_handler_t on_consume_media;*/ NULL,
/*switch_state_handler_t on_hibernate;*/ NULL,
/*switch_state_handler_t on_reset;*/ NULL,
/*switch_state_handler_t on_park;*/ NULL,
/*switch_state_handler_t on_reporting;*/ NULL,
/*switch_state_handler_t on_destroy;*/ on_my_callback
};
switch_core_add_state_handler(&state_handlers);
3.3、拨打电话相关
FreeSWITCH 调用分析
(谈论通话状态、会话、频道、私人数据等)
3.3.1、****从您的模块中拨打电话****
Below is my current understanding. I'm not core developer, so all is guessed --Sathieu 11:14, 18 February 2010 (UTC)
When originate your_module/dest vmain is called from the console:
your_io_routines.outgoing_channel is called
create a new session (with switch_core_session_request())
parse outbound_profile->destination_number to get called number
qualify the session, attached channel and attached private data
The channel is currently in CS_NEW
Change channel state to CS_INIT
Make the phone ring (and call switch_channel_mark_ring_ready())
your_state_handler.on_init is called
Change channel state to CS_ROUTING
your_state_handler.on_routing is called
What to do here?
The call is answered
switch_channel_mark_answered(channel)
3.3.2、通话****Session对象****
switch_core_session_t *session;
if ((session = switch_core_session_locate(uuid_here))) {
/* do stuff with session */
switch_core_session_rwunlock(session);
}
// NOTE: switch_core_session_locate() will automatically lock the session. When you are done with the session object it is MANDATORY to call switch_core_session_rwunlock() or bad things will happen.
// 注意:switch_core_session_locate() 将自动锁定会话。 完成会话对象后,必须调用 switch_core_session_rwunlock() 否则会发生坏事。
3.4、Macros (宏)
3.4.1、****SWITCH_DECLARE_GLOBAL_STRING_FUNC****
这个宏允许你定义一个动态的全局字符串(它实际上是你的模块的静态)设置函数。 宏将释放先前的值(如果有),然后将新值 strdup 到其中。 第一个值是要定义的函数的名称,第二个参数是 char * 指针,用于存储指向已分配字符串的指针。
3.4.2、****switch_malloc****
用于尝试常规 malloc() 的宏,它将 abort() 进程(例如,使用 SIGABRT 退出),将错误消息打印到 stderr,其中包含发生错误的文件名和行号。
#define switch_malloc(ptr, len) (void)( (!!(ptr = malloc(len))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%s", __FILE__, __LINE__),abort(), 0), ptr )
3.4.3、****switch_zmalloc****
根据 #switch_malloc,但使用 calloc() 以便缓冲区的内容在使用前用字节值零填充。
#define switch_zmalloc(ptr, len) (void)( (!!(ptr = calloc(1, (len)))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%s", __FILE__, __LINE__),abort(), 0), ptr)
3.4.4、****switch_strdup****
与#switch_malloc 类似,如果strdup() 返回一个空指针,宏将中止()(因为strdup() 在内部只是一个malloc() 和一个strcpy())。
#define switch_strdup(ptr, s) (void)( (!!(ptr = strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%s", __FILE__, __LINE__),abort(), 0), ptr)
3.4.5、****switch_safe_free****
避免释放空指针的宏。 指针在 free()'ing 之后设置为 null 以避免指向过时的缓冲区。
#define switch_safe_free(it) if (it) {free(it);it=NULL;}
4、新模块清单
1、新建Makefile.am文件
2、将模块添加到 FS configure.ac
3、将模块添加到 build/modules.conf.in(注释掉)
4、在模块顶部的注释中尽可能详细地描述模块
5、demo参考:https://github.com/Atoms-Cat/freeswitch
# 模块目录: src/mod/event_handlers/mod_event_redis
# 实现把 channel 信息缓存到 redis