- 可加载模块数据结构
可加载模块相关的文件如下
switch_loadable_module.h
switch_loadable_module.c
switch_types.h
switch_module_interfaces.h
在switch_loadable_module.c定义了可加载模块容器和可加载模块对象:
- struct switch_loadable_module {
- char *key;
- char *filename;
- int perm;
- switch_loadable_module_interface_t *module_interface;
- switch_dso_lib_t lib;
- switch_module_load_t switch_module_load;
- switch_module_runtime_t switch_module_runtime;
- switch_module_shutdown_t switch_module_shutdown;
- switch_memory_pool_t *pool;
- switch_status_t status;
- switch_thread_t *thread;
- switch_bool_t shutting_down;
- };
- struct switch_loadable_module_container {
- switch_hash_t *module_hash;
- switch_hash_t *endpoint_hash;
- switch_hash_t *codec_hash;
- switch_hash_t *dialplan_hash;
- switch_hash_t *timer_hash;
- switch_hash_t *application_hash;
- switch_hash_t *chat_application_hash;
- switch_hash_t *api_hash;
- switch_hash_t *json_api_hash;
- switch_hash_t *file_hash;
- switch_hash_t *speech_hash;
- switch_hash_t *asr_hash;
- switch_hash_t *directory_hash;
- switch_hash_t *chat_hash;
- switch_hash_t *say_hash;
- switch_hash_t *management_hash;
- switch_hash_t *limit_hash;
- switch_hash_t *secondary_recover_hash;
- switch_mutex_t *mutex;
- switch_memory_pool_t *pool;
- };
- static struct switch_loadable_module_container loadable_modules;
switch_loadable_module_container容器,除了模块哈希表module_hash外,还有各种接口哈希表,并且39行定义了一个全局结构体容器loadable_modules。容器管理多个模块对象。
模块对象比较重要的几个参数
key:用户哈希表搜索,本质上就是文件名filename
module_interface:模块接口,每个模块都会有个模块接口,通过模块接口可以获取该模块实现的各种接口,比如端点接口。
load、runtime、shutdown这组是模块可实现的三个默认接口,其中load是必须的,在加载模块后会首先调用。
- 模块初始化
- switch_loadable_module_init();//switch_loadable_module.c
- {
- //初始化容器loadable_modules
- char *cf = "modules.conf";
- char *pcf = "post_load_modules.conf";
- //载入核心默认模块
- switch_loadable_module_load_module("", "CORE_SOFTTIMER_MODULE", SWITCH_FALSE, &err);
- switch_loadable_module_load_module("", "CORE_PCM_MODULE", SWITCH_FALSE, &err);
- switch_loadable_module_load_module("", "CORE_SPEEX_MODULE", SWITCH_FALSE, &err);
- //循环解析modules.conf和post_load_modules.conf
- switch_loadable_module_load_module_ex();
- switch_loadable_module_runtime();
- chat_thread_start(1);
- }
前篇讲到在main函数,会调用模块初始化,首先加载三个核心内部的模块softtimer、pcm、speex,然后根据两个配置modules.conf和post_load_modules.conf,去加载模块。加载模块最终是调用switch_loadable_module_load_module_ex()进行。
-
- switch_loadable_module_load_module_ex()
- switch_loadable_module_load_module_ex()
- {
- //如果已加载
- if (switch_core_hash_find_locked(loadable_modules.module_hash, file, loadable_modules.mutex)) {
- }
- //如果未加载,则从模块名对应的文件载入
- else if(switch_loadable_module_load_file())
- {
- //填充模块对象结构体,加入模块容器
- switch_loadable_module_process();
- //如果有实现模块runtime函数,创建线程执行
- if (new_module->switch_module_runtime) {
- new_module->thread = switch_core_launch_thread(switch_loadable_module_exec, new_module, new_module->pool);
- }
- }
- }
载入模块先判断是否在容器中的模块哈希表,如果未存在,则做三件事
1、调用switch_loadable_module_load_file()从文件加载。
2、处理模块switch_loadable_module_process,主要是把模块加入各个哈希表。
3、如果模块有实现runtime,则新起线程执行之,这个函数不是所有模块都有,先不深入分析。
-
- switch_loadable_module_load_file
- static switch_status_t switch_loadable_module_load_file(char *path, char *filename, switch_bool_t global, switch_loadable_module_t **new_module)
- {
- switch_loadable_module_function_table_t *interface_struct_handle = NULL;
- switch_loadable_module_function_table_t *mod_interface_functions = NULL;
- switch_module_load_t load_func_ptr = NULL;
- //每个模块要实现模块函数表,并且命名规则如下,比如mod_sofia模块有sofia_module_interface
- struct_name = switch_core_sprintf(pool, "%s_module_interface", filename);
- //打开动态链接库
- switch_dso_open();
- //获取模块函数表指针
- interface_struct_handle = switch_dso_data_sym(dso, struct_name, &derr)
- if (interface_struct_handle) {
- mod_interface_functions = interface_struct_handle;
- //找到模块的load函数指针
- load_func_ptr = mod_interface_functions->load;
- }
- //执行该模块的load函数,返回模块接口
- status = load_func_ptr(&module_interface, pool);
- //申请module数据结构,并填充字段
- module = switch_core_alloc(pool, sizeof(switch_loadable_module_t);
- module->pool = pool;
- module->filename = switch_core_strdup(module->pool, path);
- module->module_interface = module_interface;
- module->switch_module_load = load_func_ptr;
- }
1、首先打开动态链接库 switch_dso_open,其内部实质使用dlopen。
2、拿到xx_module_interface函数指针,也就是说,每个模块都要以模块名字开头,定义一个函数表。使用switch_dso_data_sym,内部实质使用dlsym。
3、使用函数表,获取load函数;
4、调用load函数,获取模块接口函数module_interface。
-
- switch_loadable_module_process
- switch_loadable_module_process(file, new_module)
- {
- //插入模块容器中的模块哈希表,以文件名作为key
- switch_core_hash_insert(loadable_modules.module_hash, key, new_module);
- //如果该模块实现了端点接口
- if (new_module->module_interface->endpoint_interface) {
- //插入到模块容器中的端点哈希表
- switch_core_hash_insert(loadable_modules.endpoint_hash, ptr->interface_name, (const void *) ptr);
- }
- ...
- 其它接口判断
- }
从switch_loadable_module_load_file可以构造一个模块对象,然后插入到模块容器,并且进行一系列接口判断,如果该模块实现某类接口,则添加到该类接口的哈希,这里只列出端点接口的判断,后面各种类型的接口判断省略。
- 实现一个模块
在switch_type.h中,有如下定义。
- #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_MODULE_DEFINITION宏,可以定义一个函数表,函数表中有load、shutdown、runtime三个函数,这个函数表的名字,是xxx_module_interface,也就是switch_status_t switch_loadable_module_load_file中的interface_struct_handle。函数表定义如下:
- //每个模块实现一个函数表,取名如sofia_module_interface
- typedef struct switch_loadable_module_function_table {
- int switch_api_version;
- switch_module_load_t load;
- switch_module_shutdown_t shutdown;
- switch_module_runtime_t runtime;
- switch_module_flag_t flags;
- } switch_loadable_module_function_table_t;
-
- 模块接口
使用load函数,可以获取模块接口,也就是switch_status_t switch_loadable_module_load_file中的module_interface,模块接口里面有各种接口的指针,一个模块可以实现一个或多个接口。
- struct switch_loadable_module_interface {
- /*! the name of the module */
- const char *module_name;
- /*! the table of endpoints the module has implemented */
- switch_endpoint_interface_t *endpoint_interface;
- /*! the table of timers the module has implemented */
- switch_timer_interface_t *timer_interface;
- /*! the table of dialplans the module has implemented */
- switch_dialplan_interface_t *dialplan_interface;
- /*! the table of codecs the module has implemented */
- switch_codec_interface_t *codec_interface;
- /*! the table of applications the module has implemented */
- switch_application_interface_t *application_interface;
- ...
- switch_thread_rwlock_t *rwlock;
- int refs;
- switch_memory_pool_t *pool;
- };
- 各种接口
- 端点接口
- struct switch_endpoint_interface {
- /*! the interface's name */
- const char *interface_name;
- /*! channel abstraction methods */
- switch_io_routines_t *io_routines;
- /*! state machine methods */
- switch_state_handler_table_t *state_handler;
- /*! private information */
- void *private_info;
- switch_thread_rwlock_t *rwlock;
- int refs;
- switch_mutex_t *reflock;
- /* parent */
- switch_loadable_module_interface_t *parent;
- /* to facilitate linking */
- struct switch_endpoint_interface *next;
- switch_core_recover_callback_t recover_callback;
- };
端点是最重要的一类接口,比如sip就是属于端点,下面解释几个重点成员:
interface_name:接口名字
io_routines:数据流IO结构体
state_handler:状态处理回调,后面讲channel状态机会说明。
parent:父指针,指向模块接口
next:短接接口链表
-
-
- IO例程
-
- //io例程
- typedef enum {
- SWITCH_IO_OUTGOING_CHANNEL,
- SWITCH_IO_READ_FRAME,
- SWITCH_IO_WRITE_FRAME,
- SWITCH_IO_KILL_CHANNEL,
- SWITCH_IO_SEND_DTMF,
- SWITCH_IO_RECEIVE_MESSAGE,
- SWITCH_IO_RECEIVE_EVENT,
- SWITCH_IO_STATE_CHANGE,
- SWITCH_IO_READ_VIDEO_FRAME,
- SWITCH_IO_WRITE_VIDEO_FRAME,
- SWITCH_IO_GET_JB,
- } switch_io_routine_name_t;
- /*! \brief A table of i/o routines that an endpoint interface can implement */
- struct switch_io_routines {
- /*! creates an outgoing session from given session, caller profile */
- switch_io_outgoing_channel_t outgoing_channel;
- /*! read a frame from a session */
- switch_io_read_frame_t read_frame;
- /*! write a frame to a session */
- switch_io_write_frame_t write_frame;
- /*! send a kill signal to the session's channel */
- switch_io_kill_channel_t kill_channel;
- /*! send a string of DTMF digits to a session's channel */
- switch_io_send_dtmf_t send_dtmf;
- /*! receive a message from another session */
- switch_io_receive_message_t receive_message;
- /*! queue a message for another session */
- switch_io_receive_event_t receive_event;
- /*! change a sessions channel state */
- switch_io_state_change_t state_change;
- /*! read a video frame from a session */
- switch_io_read_video_frame_t read_video_frame;
- /*! write a video frame to a session */
- switch_io_write_video_frame_t write_video_frame;
- /*! change a sessions channel run state */
- switch_io_state_run_t state_run;
- /*! get sessions jitterbuffer */
- switch_io_get_jb_t get_jb;
- void *padding[10];
- };
io例程主要处理媒体的输入输出,其中最重要的就是读写一帧read_frame、write_frame,其它的后面讲到媒体流再细讲。
-
-
- 状态机
-
- typedef enum {
- SWITCH_SHN_ON_INIT,
- SWITCH_SHN_ON_ROUTING,
- SWITCH_SHN_ON_EXECUTE,
- SWITCH_SHN_ON_HANGUP,
- SWITCH_SHN_ON_EXCHANGE_MEDIA,
- SWITCH_SHN_ON_SOFT_EXECUTE,
- SWITCH_SHN_ON_CONSUME_MEDIA,
- SWITCH_SHN_ON_HIBERNATE,
- SWITCH_SHN_ON_RESET,
- SWITCH_SHN_ON_PARK,
- SWITCH_SHN_ON_REPORTING,
- SWITCH_SHN_ON_DESTROY
- } switch_state_handler_name_t;
- typedef switch_status_t (*switch_state_handler_t) (switch_core_session_t *);
- struct switch_state_handler_table {
- /*! executed when the state changes to init */
- switch_state_handler_t on_init;
- /*! executed when the state changes to routing */
- switch_state_handler_t on_routing;
- /*! executed when the state changes to execute */
- switch_state_handler_t on_execute;
- /*! executed when the state changes to hangup */
- switch_state_handler_t on_hangup;
- /*! executed when the state changes to exchange_media */
- switch_state_handler_t on_exchange_media;
- /*! executed when the state changes to soft_execute */
- switch_state_handler_t on_soft_execute;
- /*! executed when the state changes to consume_media */
- switch_state_handler_t on_consume_media;
- /*! executed when the state changes to hibernate */
- switch_state_handler_t on_hibernate;
- /*! executed when the state changes to reset */
- switch_state_handler_t on_reset;
- /*! executed when the state changes to park */
- switch_state_handler_t on_park;
- /*! executed when the state changes to reporting */
- switch_state_handler_t on_reporting;
- /*! executed when the state changes to destroy */
- switch_state_handler_t on_destroy;
- int flags;
- void *padding[10];
- };
每建立一个通话channel,都会有若干状态切换,刚开始是init,然后解析拨号规则,进入路由状态,路由规则解析完成后,会进入路由action执行状态。状态函数表switch_state_handler_table中的每个函数,跟状态一一对应。
-
- 应用接口
- struct switch_application_interface {
- /*! the name of the interface */
- const char *interface_name;
- /*! function the application implements */
- switch_application_function_t application_function;
- /*! the long winded description of the application */
- const char *long_desc;
- /*! the short and sweet description of the application */
- const char *short_desc;
- /*! an example of the application syntax */
- const char *syntax;
- /*! flags to control behaviour */
- uint32_t flags;
- switch_thread_rwlock_t *rwlock;
- int refs;
- switch_mutex_t *reflock;
- switch_loadable_module_interface_t *parent;
- struct switch_application_interface *next;
- };
- typedef void (*switch_application_function_t) (switch_core_session_t *, const char *);
freeswitch里有很多的app,这里不是手机的app,而是freeswitch中的可以实现一个功能的接口,或者可以理解为一个功能函数,应用接口会有个application_function。