Freeswitch使用media_bug能力实现回铃音检测

发布于:2025-02-10 ⋅ 阅读:(44) ⋅ 点赞:(0)

利用freeswitch的media bug能力来在智能外呼时通过websocket对接智能中心的声音检测接口,来实现回铃音检测,来判断用户当前是否已响应,拒接,关机等。

1.回铃音处理流程

2.模块源码目录结构

首先新建一个freeswitch的源码的src/application目录下新建一个子目录mod_ringback_check,目录结构如下:

     mod_ringback_check:

            conf/autoload_configs/ringback_check.conf.xml

           mod_ringback_check.h

           mod_ringback_check.cpp --主要的media_bug代码

          light_websocket_client.cpp --用于websocket链接

          light_websocket_client.hpp

         Makefile --c++编译文件

        test_hly_press.sh -->压测脚本

3.源码解析

3.1配置文件(hly_check.conf.xml)

 首先将配置文件放置在模块的conf/autoload_configs目录下,这样安装时,会将该文件复制到freesswitch的conf/autoload_configs目录下。

<configuration name="hly_check.conf" description="mod_hly_check configuration">

<settings>

  <param name="ai_center_url" value="wss://xxx.xxx.xxx:8080/xxxx"/>

</settings>

</configuration>

通过xml配置文件的方式,配置智能中心对应的websocket地址,加载hly_check模块时将ai_center_url读取到对应的全局静态变量中。

3.2 头文件(hly_check.h)

typedef struct {

        CWebsocket*      cli; //websocket对象

        switch_core_session_t *session; //freeswitch session对象

        vector<uint8_t> audio_data; //发送给智能中心的语音流数据

        int data_len;  //数据流长度

 } ringback_check_info_t;

static struct {

    char *ai_center_url; //智能中心websocket地址

} global;

定义全局struct对象

3.3回铃音media_bug处理程序(mod_hly_check.cpp)

3.3.1 引入的头文件

#include <switch.h>

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <string>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <time.h>

#include "hly_check.h"

#include "light_websocket_client.hpp"

using namespace hlycheckws;

#define hly_PRIVATE "mod_hly_check_bug"

3.3.2读取xml文件的结构

static switch_xml_config_item_t instructions[] = {

    /* parameter name        type                 reloadable   pointer                         default value     options structure */

    SWITCH_CONFIG_ITEM_STRING_STRDUP("ai_center_url", CONFIG_RELOAD, &global.ai_center_url, NULL, "", "ai_center_url address"),

    SWITCH_CONFIG_ITEM_END()

};


3.3.3定义media_bug相关处理函数

//回铃音模块的加载函数

SWITCH_MODULE_LOAD_FUNCTION(mod_hly_check_load);

//回铃音模块的模块卸载函数

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hly_check_shutdown);

//回铃音模块的定义函数

SWITCH_MODULE_DEFINITION(mod_hly_check, mod_hly_check_load, mod_ringback_check_shutdown, NULL);

//回铃音模块的启动处理APP函数

SWITCH_STANDARD_APP(hly_check_start_app);

//回铃音模块的停止处理APP函数

SWITCH_STANDARD_APP(hly_check_stop_app);

//接收和处理模块启动时传递的参数的函数

switch_status_t hly_check_callback_start(switch_core_session_t *session, const char *parameter);

//语音流处理函数

static switch_bool_t callprogress_hly_check_process_buffer(switch_media_bug_t *bug, void *user_data,switch_abc_type_t type);

3.3.4 回铃音模块Load函数

SWITCH_MODULE_LOAD_FUNCTION(mod_hly_check_load)

{

    switch_application_interface_t *interface;

    if (switch_xml_config_parse_module_settings("hly_check.conf", SWITCH_FALSE, instructions) != SWITCH_STATUS_SUCCESS) {

        return SWITCH_STATUS_FALSE;

    }

   //注册开始回铃音APP程序名称(hly_check_start_detect)及处理函数(ringback_check_start_app)

    SWITCH_ADD_APP(app_interface, "hly_check_start_detect", "start", "ringback_check",ringback_check_start_app, "<name>", SAF_NONE);

   //注册停止回铃音APP程序名称(hly_check_stop_detect)及处理函数(hly_check_stop_app)

    SWITCH_ADD_APP(app_interface, "hly_check_stop_detect" , "stop", "ringback_check", ringback_check_stop_app, "", SAF_NONE);

    return SWITCH_STATUS_SUCCESS;

}

3.3.5回铃音模块shutdown函数

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hly_check_shutdown)

{

    switch_xml_config_cleanup(instructions); //释放xml文件对象

    return SWITCH_STATUS_SUCCESS;

}

3.3.6 回铃音模块start函数

SWITCH_STANDARD_APP(hly_start_app)

{

    switch_channel_t *channel;

    if (!session) {

        return;

    }

    channel = switch_core_session_get_channel(session);

    if (zstr(data)) {

        switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "-ERR missing hly_check parameter!");

    } else if (hly_check_callback_start(session, data) != SWITCH_STATUS_SUCCESS) { //开始回铃音检测处理

        switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "-ERR failed to start hly_check detector");

    } else {

        switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "+OK started");

    }

}

3.3.7回铃音检测处理函数(hly_check_callback_start)

switch_status_t hly_callback_start(switch_core_session_t *session, const char *name)

{

    switch_channel_t *channel = switch_core_session_get_channel(session);

    switch_media_bug_t *bug = NULL;

        hly_check_info_t *info = (hly_check_info_t *)switch_core_session_alloc(session, sizeof(ringback_check_info_t));

    bug = (switch_media_bug_t *)switch_channel_get_private(channel, hly_PRIVATE);

    if (bug) { return SWITCH_STATUS_FALSE; }

    hly_info->session =  session;

    //注册media bug的处理函数

    switch_core_media_bug_add(session, "hly_check_detect", NULL, check_process_buffer, info, 0, SMBF_READ_STREAM, &bug);

    if (!bug) { return SWITCH_STATUS_FALSE; }

    switch_channel_set_private(channel, hly_PRIVATE, bug);

    return SWITCH_STATUS_SUCCESS;

}

3.3.8 media_bug处理函数

static switch_bool_t check_process_buffer(switch_media_bug_t *bug, void *user_data,switch_abc_type_t type)

{

    hly_check_info_t *info = (hly_check_info_t *)user_data;

     switch_core_session_t *session = hly_info->session;

     switch (type) {

    case SWITCH_ABC_TYPE_INIT:

        {  

            char msg[100];

            int ret = 0;

            info->cli = new CWebsocket(global.ai_center_url, true);

            if (info->cli == NULL){return SWITCH_FALSE;}

             ret = info->ws_cli->connect_hostname();

            if (ret != 0) {

               return SWITCH_FALSE;

            }

            //给light_websocket_client传递接收数据的回调处理函数

            info->cli->callback_fun(callback_recv_data,hly_info);

            ret =hly_info->ws_cli->send(msg);

            hly_info->ws_cli->poll();

            hly_info->ws_cli->dispatch();

            break;

    }

        case SWITCH_ABC_TYPE_READ: {

            int res = 0;

            switch_frame_t tmpframe = {0};

        switch_channel_t *channel = switch_core_session_get_channel(session);

        uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE];

        tmpframe.data = data;

        tmpframe.buflen = sizeof(data);  

            if(hly_info && hly_info->ws_cli && (hly_info->ws_cli->get_websocket_state() != EWebsocketState::CLOSED) ) {

                res = switch_core_media_bug_read(bug, &tmpframe, SWITCH_FALSE);

                 //流内容小于24480时,先合并,不发给智能中心,流内容够24480后,统一发给智能中心

                if ((hly_info->len <= 24000) && (res != SWITCH_STATUS_FALSE)) {

                  const uint8_t* ptr = static_cast<const uint8_t*>(tmpframe.data);

                  info->audio_data.insert(info->audio_data.end(),ptr,ptr + tmpframe.datalen);                                       hly_info->len = hly_info->len + tmpframe.datalen;

              return SWITCH_TRUE;

                }

               res = info->cli->send_binary(info->audio_data);

                if(res != 0) {

                   return SWITCH_FALSE;

                }

                 info->ws_cli->poll();  //发送流数据                     

                info->ws_cli->dispatch();  //处理接收数据                    

               info->audio_data.clear();

                info->audio_data.shrink_to_fit();

                info->data_len = 0;

            } else {

               return SWITCH_FALSE;

            }

             //用户接听或拒接后,停掉media_bug处理程序

            if (switch_channel_get_callstate(channel) >= CCS_ACTIVE) {

        return SWITCH_FALSE;

        }  

            break;

        }

        case SWITCH_ABC_TYPE_CLOSE: {

                        if(info && info->ws_cli && (info->cli->get_websocket_state() != EWebsocketState::CLOSED) ) {

                if(info->len > 0) {

                   info->cli->send_binary(info->audio_data);

                }

                 info->cli->send("end");

                 while (info->cli->get_websocket_state() != EWebsocketState::CLOSED) {

                     info->cli->poll();                      

                     info->cli->dispatch();                      

                }

            }

            if(info) {

                info->audio_data.clear();

                info->audio_data.shrink_to_fit();

                info->data_len = 0;

            }

            clear_detector(info);                

            break;

        }

        default:

           break;

      }

     return SWITCH_TRUE;

}

3.3.9清理函数

/**

 * 释放info处理对象

 */

void clear_detector(hly_check_info_t *info){

     delete info->cli;

    info->ws_cli = NULL;

     memset(info->sid, 0, sizeof(char));

     memset(info->province, 0, sizeof(char));

     memset(info,0,sizeof(hly_check_info_t));

     info = NULL;

}

3.3.10 分发结果函数

static void callback_recv_data(std::string result,void *obj) {

   hly_check_info_t *rb_info = (hly_check_info_t *)obj;

   switch_channel_t *channel = switch_core_session_get_channel(rb_info->session);

   switch_event_t *event = NULL;

   if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, "result::data") == SWITCH_STATUS_SUCCESS) {

    switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "result", result.c_str());

    switch_channel_event_set_data(channel, event);

    switch_event_fire(&event);

   }

}

3.4 第三方websocket处理框架(light_websocket_client)

light_websocket_client处理框架很小巧,但他对于接收到的数据,只做了一个printf打印处理,而我们的业务不仅仅只对接收的数据进行打印,还有其他处理逻辑,因此对该框架的接收数据部分进行了改造,通过传递给其一个“回调函数”的方式,将接收到的数据返回给业务处理的回调函数。

具体改造点如下:

    (1)定义一个回调的函数原型

    typedef void (*Func)(std::string,void *);

    (2)给client类增加一个set方法,该方法用于传递回调函数

       void callback_fun(Func func,void *obj);

void CWebsocket::set_callback_fun(Func function,void *vobj) {

    func = function;

    obj = vobj;

}

       (3)在接收数据处,执行回调函数:

        if(func) {

                   func(stringMessage,obj);

           }

通过以上的程序处理,即可完成回铃音的检测功能。


网站公告

今日签到

点亮在社区的每一天
去签到