主頁 > 後端開發 > FreeSWITCH添加自定義endpoint

FreeSWITCH添加自定義endpoint

2023-05-29 07:44:41 後端開發

作業系統 :CentOS 7.6_x64      FreeSWITCH版本 :1.10.9   日常開發程序中會遇到需要擴展FreeSWITCH對接其它系統的情況,這里記錄下撰寫FreeSWITCH自定義endpoint的程序,

一、模塊定義函式

使用FreeSWITCH自帶的框架來定義模塊函式,函式指標及引數串列定義如下(src/include/switch_types.h)
#define SWITCH_MODULE_LOAD_ARGS (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
#define SWITCH_MODULE_RUNTIME_ARGS (void)
#define SWITCH_MODULE_SHUTDOWN_ARGS (void)
typedef switch_status_t (*switch_module_load_t) SWITCH_MODULE_LOAD_ARGS;
typedef switch_status_t (*switch_module_runtime_t) SWITCH_MODULE_RUNTIME_ARGS;
typedef switch_status_t (*switch_module_shutdown_t) SWITCH_MODULE_SHUTDOWN_ARGS;
#define SWITCH_MODULE_LOAD_FUNCTION(name) switch_status_t name SWITCH_MODULE_LOAD_ARGS
#define SWITCH_MODULE_RUNTIME_FUNCTION(name) switch_status_t name SWITCH_MODULE_RUNTIME_ARGS
#define SWITCH_MODULE_SHUTDOWN_FUNCTION(name) switch_status_t name SWITCH_MODULE_SHUTDOWN_ARGS
 

1、模塊加載

SWITCH_MODULE_LOAD_FUNCTION 模塊加載函式,負責系統啟動時或運行時加載模塊,可以進行配置讀取及資源初始化,

2、模塊卸載

SWITCH_MODULE_SHUTDOWN_FUNCTION 模塊卸載函式,負載模塊卸載及相關資源回收,

3、模塊運行時

SWITCH_MODULE_RUNTIME_FUNCTION 模塊運行時函式,可以啟動執行緒處理請求,監聽socket等,

4、模塊定義

SWITCH_MODULE_DEFINITION 相關代碼:
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;

#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) 

二、模塊加載流程

FreeSWITCH使用 switch_loadable_module_load_module 或 switch_loadable_module_load_module_ex 進行模塊加載,具體實作邏輯可以在 switch_loadable_module.c 中查看,這里做下簡單介紹, 

1、模塊加載函式

通過 switch_loadable_module_load_module 函式加載模塊,函式呼叫鏈如下:
switch_loadable_module_load_module 
        => switch_loadable_module_load_module_ex  
        => switch_loadable_module_load_file
            => switch_loadable_module_process
            => switch_core_launch_thread  =>  switch_loadable_module_exec
通過 switch_dso_data_sym 根據定義的 XXX_module_interface 從動態庫里面獲取回呼函式指標,使用 switch_loadable_module_function_table_t 資料結構進行回呼函式系結, switch_dso_data_sym 函式實作如下(src/switch_dso.c):
void *switch_dso_data_sym(switch_dso_lib_t lib, const char *sym, char **err)
{
    void *addr = dlsym(lib, sym);
    if (!addr) {
        char *err_str = NULL;
        dlerror();

        if (!(addr = dlsym(lib, sym))) {
            err_str = (char *)dlerror();
        }

        if (err_str) {
            *err = strdup(err_str);
        }
    }
    return addr;
}
switch_loadable_module_exec函式:
static void *SWITCH_THREAD_FUNC switch_loadable_module_exec(switch_thread_t *thread, void *obj)
{


    switch_status_t status = SWITCH_STATUS_SUCCESS;
    switch_core_thread_session_t *ts = obj;
    switch_loadable_module_t *module = ts->objs[0];
    int restarts;

    switch_assert(thread != NULL);
    switch_assert(module != NULL);

    for (restarts = 0; status != SWITCH_STATUS_TERM && !module->shutting_down; restarts++) {
        status = module->switch_module_runtime();
    }
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Thread ended for %s\n", module->module_interface->module_name);

    if (ts->pool) {
        switch_memory_pool_t *pool = ts->pool;
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying Pool for %s\n", module->module_interface->module_name);
        switch_core_destroy_memory_pool(&pool);
    }
    switch_thread_exit(thread, 0);
    return NULL;
}
switch_loadable_module_exec 函式為獨立執行緒中運行,模塊運行時通過 module->switch_module_runtime() 觸發,

2、FreeSWITCH啟動時加載模塊

1)整體結構

 函式呼叫鏈如下:

main 
    => switch_core_init_and_modload 
        => switch_core_init
        => switch_loadable_module_init => switch_loadable_module_load_module
main函式在switch.c中實作,    2)加載順序 先加載系統核心模塊:
switch_loadable_module_load_module_ex("", "CORE_SOFTTIMER_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
switch_loadable_module_load_module_ex("", "CORE_PCM_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
switch_loadable_module_load_module_ex("", "CORE_SPEEX_MODULE", SWITCH_FALSE, SWITCH_FALSE, &err, SWITCH_LOADABLE_MODULE_TYPE_COMMON, event_hash);
使用 switch_xml_open_cfg 函式(src/switch_xml.c中定義)先后加載以下檔案中定義的模塊: pre_load_modules.conf modules.conf post_load_modules.conf 具體格式參考 conf/autoload_configs/modules.conf.xml   3)xml加載程序  函式呼叫鏈如下:
main => switch_core_init_and_modload 
        => switch_core_init 
            => switch_xml_init 
                => switch_xml_open_root => XML_OPEN_ROOT_FUNCTION
其中 SWITCH_GLOBAL_filenames 變數定義如下(main => switch_core_set_globals):
if (!SWITCH_GLOBAL_filenames.conf_name && (SWITCH_GLOBAL_filenames.conf_name = (char *) malloc(BUFSIZE))) {
        switch_snprintf(SWITCH_GLOBAL_filenames.conf_name, BUFSIZE, "%s", "freeswitch.xml");
}
XML_OPEN_ROOT_FUNCTION實作如下(src/switch_xml.c):
static switch_xml_open_root_function_t XML_OPEN_ROOT_FUNCTION = (switch_xml_open_root_function_t)__switch_xml_open_root;

SWITCH_DECLARE_NONSTD(switch_xml_t) __switch_xml_open_root(uint8_t reload, const char **err, void *user_data)
{
    char path_buf[1024];
    uint8_t errcnt = 0;
    switch_xml_t new_main, r = NULL;

    if (MAIN_XML_ROOT) {
        if (!reload) {
            r = switch_xml_root();
            goto done;
        }
    }

    switch_snprintf(path_buf, sizeof(path_buf), "%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, SWITCH_GLOBAL_filenames.conf_name);
    if ((new_main = switch_xml_parse_file(path_buf))) {
        *err = switch_xml_error(new_main);
        switch_copy_string(not_so_threadsafe_error_buffer, *err, sizeof(not_so_threadsafe_error_buffer));
        *err = not_so_threadsafe_error_buffer;
        if (!zstr(*err)) {
            switch_xml_free(new_main);
            new_main = NULL;
            errcnt++;
        } else {
            *err = "Success";
            switch_xml_set_root(new_main);

        }
    } else {
        *err = "Cannot Open log directory or XML Root!";
        errcnt++;
    }

    if (errcnt == 0) {
        r = switch_xml_root();
    }

 done:

    return r;
}
freeswitch.xml 為xml檔案的總入口,配置的有加載各個模塊的資料:
<section name="configuration" description="Various Configuration">
    <X-PRE-PROCESS cmd="include" data="autoload_configs/*.xml"/>
</section>

3、控制臺動態加載

在fs_cli中可以使用load及reload加載模塊,具體流程如下:
fs_cli => load ... => SWITCH_STANDARD_API(load_function) => switch_loadable_module_load_module 

fs_cli => reload ... => SWITCH_STANDARD_API(reload_function) => switch_loadable_module_unload_module 
                                                             => switch_loadable_module_load_module

三、關鍵資料結構

1、switch_loadable_module_t

作用:用于定義模塊資訊, 結構體定義:
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;
    switch_loadable_module_type_t type;
};

typedef struct switch_loadable_module switch_loadable_module_t;
欄位解釋: key =》 模塊檔案名稱 filename => 模塊檔案路徑(動態庫路徑) perm =》 定義模塊是否允許被卸載 module_interface =》 模塊介面(由switch_module_load函式賦值) lib =》 動態庫句柄(dlopen函式回傳) switch_module_load =》 模塊加載函式 switch_module_runtime =》 模塊運行時函式 switch_module_shutdown =》 模塊關閉(卸載)函式 pool =》 模塊記憶體池 status =》 switch_module_shutdown 函式的回傳值 shutting_down => 模塊是否關閉

2、switch_loadable_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;
    /*! the table of chat applications the module has implemented */
    switch_chat_application_interface_t *chat_application_interface;
    /*! the table of api functions the module has implemented */
    switch_api_interface_t *api_interface;
    /*! the table of json api functions the module has implemented */
    switch_json_api_interface_t *json_api_interface;
    /*! the table of file formats the module has implemented */
    switch_file_interface_t *file_interface;
    /*! the table of speech interfaces the module has implemented */
    switch_speech_interface_t *speech_interface;
    /*! the table of directory interfaces the module has implemented */
    switch_directory_interface_t *directory_interface;
    /*! the table of chat interfaces the module has implemented */
    switch_chat_interface_t *chat_interface;
    /*! the table of say interfaces the module has implemented */
    switch_say_interface_t *say_interface;
    /*! the table of asr interfaces the module has implemented */
    switch_asr_interface_t *asr_interface;
    /*! the table of management interfaces the module has implemented */
    switch_management_interface_t *management_interface;
    /*! the table of limit interfaces the module has implemented */
    switch_limit_interface_t *limit_interface;
    /*! the table of database interfaces the module has implemented */
    switch_database_interface_t *database_interface;
    switch_thread_rwlock_t *rwlock;
    int refs;
    switch_memory_pool_t *pool;
};

typedef struct switch_loadable_module_interface switch_loadable_module_interface_t;
欄位解釋: module_name => 模塊的名稱 endpoint_interface => 模塊endpoint的具體實作 timer_interface => 模塊timer的具體實作 dialplan_interface => 模塊dialplan的具體實作 codec_interface => 模塊編解碼的具體實作 application_interface => 模塊提供的app工具的具體實作 chat_application_interface => 模塊提供的文本聊天app工具的具體實作 api_interface => 模塊提供的api具體實作 json_api_interface => 模塊提供的json格式api的具體實作 file_interface => 模塊支持的檔案格式的具體實作(比如mp4、mkv等檔案格式) speech_interface => 模塊使用的speech介面實作 directory_interface => 模塊使用的directory介面實作 chat_interface => 模塊使用的chat介面實作 say_interface => 模塊使用的say介面實作 asr_interface => 模塊使用的asr介面實作 management_interface => 模塊使用的管理介面實作 limit_interface => 模塊使用的limit介面實作 database_interface => 模塊使用的limit介面實作 rwlock => 模塊使用的鎖 refs => 模塊鎖的計數器 pool =》 模塊記憶體池 使用 switch_loadable_module_create_module_interface 來創建 switch_loadable_module_interface_t 實體,
SWITCH_DECLARE(switch_loadable_module_interface_t *) switch_loadable_module_create_module_interface(switch_memory_pool_t *pool, const char *name)
{
    switch_loadable_module_interface_t *mod;

    mod = switch_core_alloc(pool, sizeof(switch_loadable_module_interface_t));
    switch_assert(mod != NULL);

    mod->pool = pool;

    mod->module_name = switch_core_strdup(mod->pool, name);
    switch_thread_rwlock_create(&mod->rwlock, mod->pool);
    return mod;
}
使用 switch_loadable_module_create_interface 來創建模塊里面的子介面,示例如下:
*module_interface = switch_loadable_module_create_module_interface(pool, modname);

rtc_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
rtc_endpoint_interface->interface_name = "rtc";
rtc_endpoint_interface->io_routines = &rtc_io_routines;
rtc_endpoint_interface->state_handler = &rtc_event_handlers;
rtc_endpoint_interface->recover_callback = rtc_recover_callback;
具體實作如下:
SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_module_interface_t *mod, switch_module_interface_name_t iname)
{

    switch (iname) {
    case SWITCH_ENDPOINT_INTERFACE:
        ALLOC_INTERFACE(endpoint)

    case SWITCH_TIMER_INTERFACE:
        ALLOC_INTERFACE(timer)

    case SWITCH_DIALPLAN_INTERFACE:
        ALLOC_INTERFACE(dialplan)

    case SWITCH_CODEC_INTERFACE:
        ALLOC_INTERFACE(codec)

    case SWITCH_APPLICATION_INTERFACE:
        ALLOC_INTERFACE(application)

    case SWITCH_CHAT_APPLICATION_INTERFACE:
        ALLOC_INTERFACE(chat_application)

    case SWITCH_API_INTERFACE:
        ALLOC_INTERFACE(api)

    case SWITCH_JSON_API_INTERFACE:
        ALLOC_INTERFACE(json_api)

    case SWITCH_FILE_INTERFACE:
        ALLOC_INTERFACE(file)

    case SWITCH_SPEECH_INTERFACE:
        ALLOC_INTERFACE(speech)

    case SWITCH_DIRECTORY_INTERFACE:
        ALLOC_INTERFACE(directory)

    case SWITCH_CHAT_INTERFACE:
        ALLOC_INTERFACE(chat)

    case SWITCH_SAY_INTERFACE:
        ALLOC_INTERFACE(say)

    case SWITCH_ASR_INTERFACE:
        ALLOC_INTERFACE(asr)

    case SWITCH_MANAGEMENT_INTERFACE:
        ALLOC_INTERFACE(management)

    case SWITCH_LIMIT_INTERFACE:
        ALLOC_INTERFACE(limit)

    case SWITCH_DATABASE_INTERFACE:
        ALLOC_INTERFACE(database)

    default:
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n");
        return NULL;
    }
}

3、switch_endpoint_interface_t

作用:endpoint的入口 結構體定義:
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;

};

typedef struct switch_endpoint_interface switch_endpoint_interface_t;
欄位解釋: interface_name => endpoint名稱,比如:"rtc" io_routines => endpoint對應的io操作回呼函式 state_handler => endpoint對應的事件處理回呼函式 private_info => endpoint私有引數配置(比如編碼格式、采樣率等) rwlock => endpoint鎖 refs => endpoint鎖的參考次數 reflock => endpoint參考鎖 parent => endpoint所屬模塊 next => next指標 recover_callback => endpoint對應的recover回呼函式

4、switch_io_routines

作用:存盤io操作的回呼函式 結構體定義:
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;
    /*! read a video frame from a session */
    switch_io_read_text_frame_t read_text_frame;
    /*! write a video frame to a session */
    switch_io_write_text_frame_t write_text_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];
};

typedef struct switch_io_routines switch_io_routines_t;
欄位解釋: outgoing_channel => 創建外呼channel的回呼函式 read_frame => 讀session音頻資料的回呼函式 write_frame => 寫session音頻資料的回呼函式 kill_channel => kill信號處理函式,用于處理channel接收的kill信號 send_dtmf => send dtmf操作的回呼函式,用于處理channel接收的DTMF字串 receive_message => 處理channel訊息的回呼函式,用于處理其它channel發來的訊息 receive_event => 發送channel訊息的回呼函式,用于向目標session發送自定義事件(比如rtc session、rtmp session等) state_change => channel狀態修改的回呼函式 read_video_frame => 讀session視頻資料的回呼函式 write_video_frame => 寫session視頻資料的回呼函式 read_text_frame => 讀session文本資料的回呼函式 write_text_frame => 寫session文本資料的回呼函式 state_run => 改變session的運行狀態,目前沒見到有endpoint使用過 get_jb => 獲取session的jitter_buffer

5、switch_state_handler_table_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];
};


typedef struct switch_state_handler_table switch_state_handler_table_t;
引數解釋: on_init => channel進入 CS_INIT 狀態的回呼函式 on_routing => channel進入 CS_ROUTING 狀態的回呼函式 on_execute => channel進入 CS_EXECUTE 狀態的回呼函式,用于執行操作 on_hangup => channel進入 CS_HANGUP 狀態的回呼函式 on_exchange_media => channel進入 CS_EXCHANGE_MEDIA 狀態的回呼函式 on_soft_execute => channel進入 CS_SOFT_EXECUTE 狀態的回呼函式,用于從其它channel接識訓發送資料 on_consume_media => channel進入 CS_CONSUME_MEDIA 狀態的回呼函式, on_hibernate => channel進入 CS_HIBERNATE 狀態的回呼函式,sleep操作 on_reset => channel進入 CS_RESET 狀態的回呼函式 on_park => channel進入 CS_PARK 狀態的回呼函式 on_reporting => channel進入 CS_REPORTING 狀態的回呼函式 on_destroy => channel進入 CS_DESTROY 狀態的回呼函式 switch_core_state_machine.c中使用 STATE_MACRO 觸發,部分觸發代碼如下:
case CS_ROUTING:    /* Look for a dialplan and find something to do */
    STATE_MACRO(routing, "ROUTING");
    break;
case CS_RESET:        /* Reset */
    STATE_MACRO(reset, "RESET");
    break;
    /* These other states are intended for prolonged durations so we do not signal lock for them */
case CS_EXECUTE:    /* Execute an Operation */
    STATE_MACRO(execute, "EXECUTE");
    break;
case CS_EXCHANGE_MEDIA:    /* loop all data back to source */
    STATE_MACRO(exchange_media, "EXCHANGE_MEDIA");
    break;
case CS_SOFT_EXECUTE:    /* send/recieve data to/from another channel */
    STATE_MACRO(soft_execute, "SOFT_EXECUTE");
    break;
case CS_PARK:        /* wait in limbo */
    STATE_MACRO(park, "PARK");
    break;
case CS_CONSUME_MEDIA:    /* wait in limbo */
    STATE_MACRO(consume_media, "CONSUME_MEDIA");
    break;
case CS_HIBERNATE:    /* sleep */
    STATE_MACRO(hibernate, "HIBERNATE");
    break;

四、模塊撰寫示例 

1、撰寫c風格的endpoint模塊

仿照mod_rtc模塊撰寫,核心檔案只有兩個: mod_rtc.c Makefile.am   1)復制mod_artc目錄 cp mod_rtc mod_ctest -r

 2)修改檔案名

mv mod_rtc.c mod_ctest.c   3)修改檔案內容,將rtc關鍵字替換成ctest

 4)修改編譯選項

檔案: freeswitch-1.10.9.-release/configure.ac 仿照rtc模塊,添加ctest模塊內容: src/mod/endpoints/mod_ctest/Makefile

 

5)開啟模塊編譯 檔案:freeswitch-1.10.9.-release/modules.conf 仿照rtc模塊,添加ctest模塊編譯: endpoints/mod_ctest

 

6)生成Makefile ./rebootstrap.sh && ./configure

 7)安裝模塊

在 freeswitch-1.10.9.-release 根目錄(或mod_ctest目錄)執行如下指令: make && make install

 8)加載模塊

檔案:conf/autoload_configs/modules.conf.xml 添加如下內容:   9)模塊測驗 控制臺加載測驗: reload mod_ctest

c風格endpoint模塊編譯及運行效果視頻:

關注微信公眾號(聊聊博文,文末可掃碼)后回復 2023052801 獲取, 

2、撰寫c++風格的endpoint模塊

仿照mod_h323模塊撰寫,目錄結構、編譯等參考c風格endpoint模塊撰寫部分,關鍵點描述可以從如下渠道獲取: 關注微信公眾號(聊聊博文,文末可掃碼)后回復 20230528 獲取, 加載效果如下:

c++風格endpoint模塊編譯及運行效果視頻:

關注微信公眾號(聊聊博文,文末可掃碼)后回復 2023052802 獲取, 

五、資源下載

本文涉及原始碼和檔案,可以從如下途徑獲取:

關注微信公眾號(聊聊博文,文末可掃碼)后回復 20230528 獲取,

 

微信公眾號:

  • E-Mail : [email protected]
  • 轉載請注明出處,謝謝!

    轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553635.html

    標籤:其他

    上一篇:【python基礎】基本資料型別-字串型別

    下一篇:返回列表

    標籤雲
    其他(159863) Python(38178) JavaScript(25460) Java(18141) C(15232) 區塊鏈(8268) C#(7972) AI(7469) 爪哇(7425) MySQL(7214) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5343) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4577) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1977) 功能(1967) Web開發(1951) HtmlCss(1948) C++(1924) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1878) .NETCore(1862) 谷歌表格(1846) Unity3D(1843) for循环(1842)

    熱門瀏覽
    • 【C++】Microsoft C++、C 和匯編程式檔案

      ......

      uj5u.com 2020-09-10 00:57:23 more
    • 例外宣告

      相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

      uj5u.com 2020-09-10 00:57:27 more
    • Codeforces 1400E Clear the Multiset(貪心 + 分治)

      鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

      uj5u.com 2020-09-10 00:57:30 more
    • UVA11610 【Reverse Prime】

      本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

      uj5u.com 2020-09-10 00:57:36 more
    • 統計區間素數數量

      1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

      uj5u.com 2020-09-10 00:57:47 more
    • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

      1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

      uj5u.com 2020-09-10 00:58:04 more
    • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

      1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

      uj5u.com 2020-09-10 00:59:11 more
    • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

      1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

      uj5u.com 2020-09-10 01:00:19 more
    • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

      今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

      uj5u.com 2020-09-10 01:00:22 more
    • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

      眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

      uj5u.com 2020-09-10 01:00:41 more
    最新发布
    • FreeSWITCH添加自定義endpoint

      作業系統 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 日常開發程序中會遇到需要擴展FreeSWITCH對接其它系統的情況,這里記錄下撰寫FreeSWITCH自定義endpoint的程序。 一、模塊定義函式 使用FreeSWITCH自帶的框架來定義模塊函式,函式指標及引數 ......

      uj5u.com 2023-05-29 07:44:41 more
    • 【python基礎】基本資料型別-字串型別

      # 1.初識字串 字串就是一系列字符。在python中,用引號括起來文本內容的都是字串。 其語法格式為:‘文本內容’或者“文本內容” 我們發現其中的引號可以是單引號,也可以是雙引號。這樣的靈活性可以使我們進行引號之間的嵌套。 撰寫程式如下所示: ![image](https://img2023 ......

      uj5u.com 2023-05-29 07:44:15 more
    • Rust Web 全堆疊開發之自建TCP、HTTP Server

      # Rust Web 全堆疊開發之自建TCP、HTTP Server ## 課程簡介 ### 預備知識 - Rust 編程語言入門 - https://www.bilibili.com/video/BV1hp4y1k7SV ### 課程主要內容 - WebService - 服務器端Web App - ......

      uj5u.com 2023-05-29 07:43:58 more
    • Python 標準類別庫-因特網資料處理之Base64資料編碼

      該模塊提供將二進制資料編碼為可列印ASCII字符并將這種編碼解碼回二進制資料的功能。它為[RFC 3548](https://tools.ietf.org/html/rfc3548.html)中指定的編碼提供編碼和解碼功能。定義了Base16、Base32和Base64演算法,以及事實上的標準Asci ......

      uj5u.com 2023-05-29 07:43:47 more
    • 關于STL容器的簡單總結

      # 關于STL容器的簡單總結 ## 1、結構體中多載運算子的示例 ``` //結構體小于符號的多載 struct buf { int a,b; bool operator queuea; //定義 a.push(x); //壓入 a.pop(); //彈出 a.size(); //取大小 a.fro ......

      uj5u.com 2023-05-29 07:43:38 more
    • 樹莓派使用HC-SR04超聲波測距

      ### 超聲波模塊介紹 超聲波測距原理很簡單: 1、通過記錄發送超聲波的時間、記錄超聲波回傳的時間,回傳時間與發送時間相減得到超聲波的持續時間。 2、通過公式:(**超聲波持續時間** * **聲波速度**) / **2**就可以得出距離; ![image.png](https://img2023. ......

      uj5u.com 2023-05-29 07:43:26 more
    • Python 使用ConfigParser操作ini組態檔

      ini 組態檔格式如下 要求:ini 檔案必須是GBK編碼,如果是UTF-8編碼,python讀取組態檔會報錯。 # 這里是注釋內容 # [FY12361] #婦幼保健介面服務埠 serverIP=192.168.1.11 serverPort=8400 [SM] #國產SM加密服務埠 se ......

      uj5u.com 2023-05-29 07:42:39 more
    • Python asyncio之協程學習總結

      ## 實踐環境 Python 3.6.2 ## 什么是協程 **協程**(Coroutine)一種電腦程式組件,該程式組件通過允許暫停和恢復任務,為非搶占式多任務生成子程式。**協程**也可以簡單理解為協作的程式,通過協同多任務處理實作并發的函式的變種(一種可以支持中斷的函式)。 下面,我們通過日常 ......

      uj5u.com 2023-05-29 07:42:28 more
    • 【python基礎】基本資料型別-字串型別

      # 1.初識字串 字串就是一系列字符。在python中,用引號括起來文本內容的都是字串。 其語法格式為:‘文本內容’或者“文本內容” 我們發現其中的引號可以是單引號,也可以是雙引號。這樣的靈活性可以使我們進行引號之間的嵌套。 撰寫程式如下所示: ![image](https://img2023 ......

      uj5u.com 2023-05-29 07:42:08 more
    • Python 標準類別庫-因特網資料處理之Base64資料編碼

      該模塊提供將二進制資料編碼為可列印ASCII字符并將這種編碼解碼回二進制資料的功能。它為[RFC 3548](https://tools.ietf.org/html/rfc3548.html)中指定的編碼提供編碼和解碼功能。定義了Base16、Base32和Base64演算法,以及事實上的標準Asci ......

      uj5u.com 2023-05-29 07:41:52 more