创建一个模块
我们创建一个mod_book的模块
创建一个mod_book.c
文件,代码如下
c
// mod_book.c
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load); // 模块加载函数的声明
SWITCH_MODULE_DEFINITION(mod_book, mod_book_load, NULL, NULL); // 模块的定义
// 模块加载函数的定义
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load) {
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
return SWITCH_STATUS_SUCCESS;
}
创建一个Makefile用于编译
makefile
# FreeSWITCH源码目录
BASE=/root/sources/freeswitch
include $(BASE)/build/modmake.rules
编译
执行make install
进行编译安装
sh
➜ mod_book make install
Compiling /root/code/mod_book/mod_book.c...
libtool: compile: gcc -I/usr/include/uuid -I/root/sources/freeswitch/src/include -I/root/sources/freeswitch/src/include -I/root/sources/freeswitch/libs/libteletone/src -fPIC -ffast-math -Werror -Wno-unused-result -Wno-misleading-indentation -fvisibility=hidden -DSWITCH_API_VISIBILITY=1 -DCJSON_API_VISIBILITY=1 -DHAVE_VISIBILITY=1 -g -ggdb -DHAVE_OPENSSL -g -O2 -Wall -std=c99 -pedantic -Wdeclaration-after-statement -D_GNU_SOURCE -DHAVE_CONFIG_H -c /root/code/mod_book/mod_book.c -fPIC -DPIC -o .libs/mod_book.o
Creating mod_book.la...
installing mod_book.la
libtool: warning: relinking 'mod_book.la'
在FreeSWITCH中加载模块
执行load mod_book
加载模块
sh
freeswitch@debianh61> load mod_book
2025-04-06 08:21:21.994470 99.73% [INFO] mod_enum.c:884 ENUM Reloaded
2025-04-06 08:21:21.994470 99.73% [CONSOLE] switch_loadable_module.c:1772 Successfully Loaded [mod_book]
+OK Reloading XML
+OK
2025-04-06 08:21:21.994470 99.73% [INFO] switch_time.c:1436 Timezone reloaded 597 definitions
添加一个Dialplan
c
#include <stdlib.h>
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load); // 模块加载函数的声明
SWITCH_MODULE_DEFINITION(mod_book, mod_book_load, NULL, NULL); // 模块的定义
// dialplan的回调函数
SWITCH_STANDARD_DIALPLAN(book_dialplan_hunt) {
switch_caller_extension_t *extension = NULL; // 创建一个分机,用于dialplan查找时候返回该分机
switch_channel_t *channel = switch_core_session_get_channel(session); // 获取通话的channel
if(!caller_profile) {
caller_profile = switch_channel_get_caller_profile(channel); // 获取主叫的一些信息
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in context %s\n", caller_profile->caller_id_name, caller_profile->caller_id_number,
caller_profile->destination_number, caller_profile->context);
// 创建一个分机,因为查找dialplan的结果是返回一个extension
extension = switch_caller_extension_new(session, "book_extension_name", "book_extension_number");
if (!extension) abort();
// 往分机里面添加一个application,用于后续EXECUTE执行阶段执行该app
switch_caller_extension_add_application(session, extension, "log", "INFO Hey, I'm in the book");
return extension;
}
// 模块加载函数的定义
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load) {
switch_dialplan_interface_t *dp_interface; // 定义一个dialplan
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
// 向核心注册dialplan,并设置一个回调函数
SWITCH_ADD_DIALPLAN(dp_interface, "book", book_dialplan_hunt);
return SWITCH_STATUS_SUCCESS;
}
重新编译安装模块
sh
make install
重新加载模块
使用reload mod_book
重新加载模块
sh
freeswitch@debianh61> reload mod_book
2025-04-06 08:55:17.714472 99.77% [NOTICE] switch_loadable_module.c:1197 Deleting Dialplan 'book'
2025-04-06 08:55:17.714472 99.77% [CONSOLE] switch_loadable_module.c:2357 mod_book has no shutdown routine
2025-04-06 08:55:17.714472 99.77% [CONSOLE] switch_loadable_module.c:2374 mod_book unloaded.
2025-04-06 08:55:17.714472 99.77% [INFO] mod_enum.c:884 ENUM Reloaded
2025-04-06 08:55:17.714472 99.77% [INFO] switch_time.c:1436 Timezone reloaded 597 definitions
2025-04-06 08:55:17.714472 99.77% [CONSOLE] switch_loadable_module.c:1772 Successfully Loaded [mod_book]
+OK Reloading XML
+OK module unloaded
+OK module loaded
2025-04-06 08:55:17.714472 99.77% [NOTICE] switch_loadable_module.c:273 Adding Dialplan 'book'
测试
我们发起一个呼叫originate user/1000 999 book
, 该呼叫走book这个dialplan
sh
freeswitch@debianh61> originate user/1000 999 book
2025-04-06 08:56:32.154504 99.97% [NOTICE] switch_channel.c:1142 New Channel sofia/internal/[email protected]:55000 [1906c61d-5afc-40d2-91d8-696cc52d2700]
2025-04-06 08:56:32.154504 99.97% [NOTICE] switch_ivr_originate.c:3059 Cannot create outgoing channel of type [error] cause: [USER_NOT_REGISTERED]
2025-04-06 08:56:32.154504 99.97% [INFO] sofia_glue.c:1659 sofia/internal/[email protected]:55000 sending invite call-id: (null)
2025-04-06 08:56:32.394500 99.97% [NOTICE] sofia.c:7604 Ring-Ready sofia/internal/[email protected]:55000!
2025-04-06 08:56:33.594500 99.93% [NOTICE] sofia.c:8681 Channel [sofia/internal/[email protected]:55000] has been answered
+OK 1906c61d-5afc-40d2-91d8-696cc52d2700
2025-04-06 08:56:33.614471 99.93% [NOTICE] switch_ivr.c:2303 Transfer sofia/internal/[email protected]:55000 to book[999@default]
2025-04-06 08:56:33.614471 99.93% [INFO] mod_book.c:16 Processing <0000000000>->999 in context default
2025-04-06 08:56:33.614471 99.93% [INFO] switch_channel.c:3289 sofia/internal/[email protected]:55000 Flipping CID from "" <0000000000> to "Outbound Call" <1000>
EXECUTE [depth=0] sofia/internal/[email protected]:55000 log(INFO Hey, I'm in the book)
2025-04-06 08:56:33.614471 99.93% [INFO] mod_dptools.c:1865 Hey, I'm in the book
2025-04-06 08:56:33.614471 99.93% [NOTICE] switch_core_state_machine.c:382 sofia/internal/[email protected]:55000 has executed the last dialplan instruction, hanging up.
2025-04-06 08:56:33.614471 99.93% [NOTICE] switch_core_state_machine.c:384 Hangup sofia/internal/[email protected]:55000 [CS_EXECUTE] [NORMAL_CLEARING]
freeswitch@debianh61> 2025-04-06 08:56:33.614471 99.93% [NOTICE] switch_core_session.c:1762 Session 4 (sofia/internal/[email protected]:55000) Ended
2025-04-06 08:56:33.614471 99.93% [NOTICE] switch_core_session.c:1766 Close Channel sofia/internal/[email protected]:55000 [CS_DESTROY]

增加一个APP
c
#include <stdlib.h>
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load); // 模块加载函数的声明
SWITCH_MODULE_DEFINITION(mod_book, mod_book_load, NULL, NULL); // 模块的定义
SWITCH_STANDARD_APP(book_function); // APP回调函数声明
switch_application_interface_t *app_interface; // 声明一个app_interface
// APP回调函数
SWITCH_STANDARD_APP(book_function) {
const char* name;
if(zstr(data)) { // data为传给该函数的参数
name = "No Name";
} else {
name = data;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "I'm a book, My name is: %s\n", name);
}
// dialplan的回调函数
SWITCH_STANDARD_DIALPLAN(book_dialplan_hunt) {
switch_caller_extension_t *extension = NULL; // 创建一个分机,用于dialplan查找时候返回该分机
switch_channel_t *channel = switch_core_session_get_channel(session); // 获取通话的channel
if(!caller_profile) {
caller_profile = switch_channel_get_caller_profile(channel); // 获取主叫的一些信息
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in context %s\n", caller_profile->caller_id_name, caller_profile->caller_id_number,
caller_profile->destination_number, caller_profile->context);
// 创建一个分机,因为查找dialplan的结果是返回一个extension
extension = switch_caller_extension_new(session, "book_extension_name", "book_extension_number");
if (!extension) abort();
// 往分机里面添加一个application,用于后续EXECUTE执行阶段执行该app
// 这里使用我们自定义的APP
switch_caller_extension_add_application(session, extension, "book", "Use my book application");
return extension;
}
// 模块加载函数的定义
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load) {
switch_dialplan_interface_t *dp_interface; // 定义一个dialplan
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
// 向核心注册dialplan,并设置一个回调函数
SWITCH_ADD_DIALPLAN(dp_interface, "book", book_dialplan_hunt);
// 向核心注册APP,回调函数为book_function
SWITCH_ADD_APP(app_interface, "book", "book example short descript", "book example long descript.......", book_function, "[name]", SAF_SUPPORT_NOMEDIA);
return SWITCH_STATUS_SUCCESS;
}
重新编译安装模块
sh
make install
重新加载模块
使用reload mod_book
重新加载模块
sh
freeswitch@debianh61> reload mod_book
2025-04-06 09:29:44.054472 99.83% [NOTICE] switch_loadable_module.c:1197 Deleting Dialplan 'book'
2025-04-06 09:29:44.054472 99.83% [CONSOLE] switch_loadable_module.c:2357 mod_book has no shutdown routine
2025-04-06 09:29:44.054472 99.83% [CONSOLE] switch_loadable_module.c:2374 mod_book unloaded.
2025-04-06 09:29:44.054472 99.83% [INFO] mod_enum.c:884 ENUM Reloaded
2025-04-06 09:29:44.054472 99.83% [INFO] switch_time.c:1436 Timezone reloaded 597 definitions
2025-04-06 09:29:44.054472 99.83% [CONSOLE] switch_loadable_module.c:1772 Successfully Loaded [mod_book]
+OK Reloading XML
+OK module unloaded
+OK module loaded
2025-04-06 09:29:44.054472 99.83% [NOTICE] switch_loadable_module.c:273 Adding Dialplan 'book'
2025-04-06 09:29:44.054472 99.83% [NOTICE] switch_loadable_module.c:329 Adding Application 'book'

测试
执行originate user/1000 999 book
发起一个呼叫
sh
freeswitch@debianh61> originate user/1000 999 book
2025-04-06 09:31:36.214501 99.77% [NOTICE] switch_channel.c:1142 New Channel sofia/internal/[email protected]:55000 [4a0a0226-4e53-41fc-b0bd-88ccbba2d707]
2025-04-06 09:31:36.214501 99.77% [NOTICE] switch_ivr_originate.c:3059 Cannot create outgoing channel of type [error] cause: [USER_NOT_REGISTERED]
2025-04-06 09:31:36.214501 99.77% [INFO] sofia_glue.c:1659 sofia/internal/[email protected]:55000 sending invite call-id: (null)
2025-04-06 09:31:36.494486 99.77% [NOTICE] sofia.c:7604 Ring-Ready sofia/internal/[email protected]:55000!
2025-04-06 09:31:40.174504 99.77% [NOTICE] sofia.c:8681 Channel [sofia/internal/[email protected]:55000] has been answered
+OK 4a0a0226-4e53-41fc-b0bd-88ccbba2d707
2025-04-06 09:31:40.194503 99.77% [NOTICE] switch_ivr.c:2303 Transfer sofia/internal/[email protected]:55000 to book[999@default]
2025-04-06 09:31:40.194503 99.77% [INFO] mod_book.c:32 Processing <0000000000>->999 in context default
2025-04-06 09:31:40.194503 99.77% [INFO] switch_channel.c:3289 sofia/internal/[email protected]:55000 Flipping CID from "" <0000000000> to "Outbound Call" <1000>
EXECUTE [depth=0] sofia/internal/[email protected]:55000 book(Use my book application)
2025-04-06 09:31:40.194503 99.77% [INFO] mod_book.c:21 I'm a book, My name is: Use my book application
2025-04-06 09:31:40.194503 99.77% [NOTICE] switch_core_state_machine.c:382 sofia/internal/[email protected]:55000 has executed the last dialplan instruction, hanging up.
2025-04-06 09:31:40.194503 99.77% [NOTICE] switch_core_state_machine.c:384 Hangup sofia/internal/[email protected]:55000 [CS_EXECUTE] [NORMAL_CLEARING]

增加一个API
c
#include <stdlib.h>
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load); // 模块加载函数的声明
SWITCH_MODULE_DEFINITION(mod_book, mod_book_load, NULL, NULL); // 模块的定义
SWITCH_STANDARD_APP(book_function); // APP回调函数声明
switch_application_interface_t *app_interface; // 声明一个app_interface
switch_api_interface_t *api_interface; // 声明一个api_interface
// APP回调函数
SWITCH_STANDARD_APP(book_function) {
const char* name;
if(zstr(data)) { // data为传给该函数的参数
name = "No Name";
} else {
name = data;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "I'm a book, My name is: %s\n", name);
}
// dialplan的回调函数
SWITCH_STANDARD_DIALPLAN(book_dialplan_hunt) {
switch_caller_extension_t *extension = NULL; // 创建一个分机,用于dialplan查找时候返回该分机
switch_channel_t *channel = switch_core_session_get_channel(session); // 获取通话的channel
if(!caller_profile) {
caller_profile = switch_channel_get_caller_profile(channel); // 获取主叫的一些信息
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in context %s\n", caller_profile->caller_id_name, caller_profile->caller_id_number,
caller_profile->destination_number, caller_profile->context);
// 创建一个分机,因为查找dialplan的结果是返回一个extension
extension = switch_caller_extension_new(session, "book_extension_name", "book_extension_number");
if (!extension) abort();
// 往分机里面添加一个application,用于后续EXECUTE执行阶段执行该app
switch_caller_extension_add_application(session, extension, "book", "Use my book application");
return extension;
}
// API回调函数
SWITCH_STANDARD_API(book_api_function) {
const char* name;
if (zstr(cmd)) { // 从命令行读取
name = "api no name";
} else {
name = cmd;
}
stream->write_function(stream, "I'm book api function, my name is: %s\n", name); // 输出
return SWITCH_STATUS_SUCCESS;
}
// 模块加载函数的定义
SWITCH_MODULE_LOAD_FUNCTION(mod_book_load) {
switch_dialplan_interface_t *dp_interface; // 定义一个dialplan
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
// 向核心注册dialplan,并设置一个回调函数
SWITCH_ADD_DIALPLAN(dp_interface, "book", book_dialplan_hunt);
// 向核心注册APP,回调函数为book_function
SWITCH_ADD_APP(app_interface, "book", "book example short descript", "book example long descript.......", book_function, "[name]", SAF_SUPPORT_NOMEDIA);
// 向核心注册API,回调函数为book_api_function
SWITCH_ADD_API(api_interface, "book", "book example description", book_api_function, "[name]");
return SWITCH_STATUS_SUCCESS;
}
重新编译安装模块
sh
make install
重新加载模块
使用reload mod_book
重新加载模块
sh
freeswitch@debianh61> reload mod_book
2025-04-06 09:41:15.674474 99.67% [NOTICE] switch_loadable_module.c:1197 Deleting Dialplan 'book'
2025-04-06 09:41:15.674474 99.67% [NOTICE] switch_loadable_module.c:1230 Deleting Application 'book'
2025-04-06 09:41:15.674474 99.67% [CONSOLE] switch_loadable_module.c:2357 mod_book has no shutdown routine
2025-04-06 09:41:15.674474 99.67% [CONSOLE] switch_loadable_module.c:2374 mod_book unloaded.
2025-04-06 09:41:15.674474 99.67% [INFO] mod_enum.c:884 ENUM Reloaded
2025-04-06 09:41:15.674474 99.67% [INFO] switch_time.c:1436 Timezone reloaded 597 definitions
2025-04-06 09:41:15.674474 99.67% [CONSOLE] switch_loadable_module.c:1772 Successfully Loaded [mod_book]
+OK Reloading XML
+OK module unloaded
+OK module loaded
2025-04-06 09:41:15.674474 99.67% [NOTICE] switch_loadable_module.c:273 Adding Dialplan 'book'
2025-04-06 09:41:15.674474 99.67% [NOTICE] switch_loadable_module.c:329 Adding Application 'book'
2025-04-06 09:41:15.674474 99.67% [NOTICE] switch_loadable_module.c:389 Adding API Function 'book'

测试
我们在命令行输入book
和book test666test777
进行测试
sh
freeswitch@debianh61> book
I'm book api function, my name is: api no name
freeswitch@debianh61> book test666test777
I'm book api function, my name is: test666test777
示例程序宏定义参考
SWITCH_MODULE_LOAD_FUNCTION
c
#define SWITCH_MODULE_LOAD_ARGS (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
#define SWITCH_MODULE_LOAD_FUNCTION(name) switch_status_t name SWITCH_MODULE_LOAD_ARGS
SWITCH_ADD_APP
c
#define SWITCH_ADD_APP(app_int, int_name, short_descript, long_descript, funcptr, syntax_string, app_flags) \
for (;;) { \
app_int = (switch_application_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_APPLICATION_INTERFACE); \
app_int->interface_name = int_name; \
app_int->application_function = funcptr; \
app_int->short_desc = short_descript; \
app_int->long_desc = long_descript; \
app_int->syntax = syntax_string; \
app_int->flags = app_flags; \
break; \
}
SWITCH_ADD_API
c
#define SWITCH_ADD_API(api_int, int_name, descript, funcptr, syntax_string) \
for (;;) { \
api_int = (switch_api_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_API_INTERFACE); \
api_int->interface_name = int_name; \
api_int->desc = descript; \
api_int->function = funcptr; \
api_int->syntax = syntax_string; \
break; \
}
SWITCH_ADD_DIALPLAN
c
#define SWITCH_ADD_DIALPLAN(dp_int, int_name, funcptr) \
for (;;) { \
dp_int = (switch_dialplan_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_DIALPLAN_INTERFACE); \
dp_int->hunt_function = funcptr; \
dp_int->interface_name = int_name; \
break; \
}
SWITCH_STANDARD_APP
c
#define SWITCH_STANDARD_APP(name) static void name (switch_core_session_t *session, const char *data)
SWITCH_STANDARD_API
c
#define SWITCH_STANDARD_API(name) static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream)
SWITCH_MODULE_DEFINITION
c
#define SWITCH_MODULE_DEFINITION_EX(name, load, shutdown, runtime, flags) \
static const char modname[] = #name ; \
SWITCH_MOD_DECLARE_DATA switch_loadable_module_function_table_t name##_module_interface = { \
SWITCH_API_VERSION, \
load, \
shutdown, \
runtime, \
flags \
}
#define SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) \
SWITCH_MODULE_DEFINITION_EX(name, load, shutdown, runtime, SMODF_NONE)
SWITCH_STANDARD_DIALPLAN
c
#define SWITCH_STANDARD_DIALPLAN(name) static switch_caller_extension_t *name (switch_core_session_t *session, void *arg, switch_caller_profile_t *caller_profile)
参考资料
FreeSWITCH权威指南