上一节,我们重点讲解了开发此服务架构和功能,已经freeswitch自定义application对应的方法。这一节,我们将重点介绍freeswitch自定义旁路输出media bug后,如何定义UDP服务,如何进行项目服务绑定。
我们先来看看C语言实现UDPserver
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void){
int socket_desc;
struct sockaddr_in server_addr, client_addr;
char server_message[2000], client_message[2000];
int client_struct_length = sizeof(client_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create UDP socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_desc < 0){
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Bind to the set port and IP:
if(bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
printf("Couldn't bind to the port\n");
return -1;
}
printf("Done with binding\n");
printf("Listening for incoming messages...\n\n");
// Receive client's message:
if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
(struct sockaddr*)&client_addr, &client_struct_length) < 0){
printf("Couldn't receive\n");
return -1;
}
printf("Received message from IP: %s and port: %i\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
printf("Msg from client: %s\n", client_message);
// Respond to client:
strcpy(server_message, client_message);
if (sendto(socket_desc, server_message, strlen(server_message), 0,
(struct sockaddr*)&client_addr, client_struct_length) < 0){
printf("Can't send\n");
return -1;
}
// Close the socket:
close(socket_desc);
return 0;
}
用c实现一个UDPclient
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void){
int socket_desc;
struct sockaddr_in server_addr;
char server_message[2000], client_message[2000];
int server_struct_length = sizeof(server_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_desc < 0){
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Get input from the user:
printf("Enter message: ");
gets(client_message);
// Send the message to server:
if(sendto(socket_desc, client_message, strlen(client_message), 0,
(struct sockaddr*)&server_addr, server_struct_length) < 0){
printf("Unable to send message\n");
return -1;
}
// Receive the server's response:
if(recvfrom(socket_desc, server_message, sizeof(server_message), 0,
(struct sockaddr*)&server_addr, &server_struct_length) < 0){
printf("Error while receiving server's msg\n");
return -1;
}
printf("Server's response: %s\n", server_message);
// Close the socket:
close(socket_desc);
return 0;
}
使用gcc 编译后,即可执行查看
稍微讲解一下:
int socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
创建socket,返回一个文件描述符
然后初始化server
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
然后将socket 描述符和 server addr进行绑定
bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr);
C语言实现UDPserver 是如此简单。
那我们需要当前Server启动时候的配置信息,比如server 的端口,绑定地址等。 因此需要我们在freeswitch 此app load时候,将这些信息进行初始化
CUSTOM event type
#define YOURAPP_EVENT_UDP "robot::udp"
定义你配置文件地址
#define app_config "/usr/local/freeswitch/conf/your_app.conf"
SWITCH_MODULE_LOAD_FUNCTION(mod_app_load)
{
switch_application_interface_t *app_interface;
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_APP(app_interface, "your_app", "your_app", "ai your_app", your_start_function, "[name]", SAF_NONE);
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
定义配置文件读取方法
int get_config__yxp_string(const char*set,const char*cmd,const char*def,char*res,int para_len,const char*filename)
{
}
接下来就是最关键的 SWITCH_STANDARD_APP
SWITCH_STANDARD_APP(robot_start_function)
{
switch_media_bug_t *bug;
switch_status_t status;
switch_channel_t *channel;
your_session_info_t *robot_info; // 自定义一个结构体
// 定义
status = switch_core_media_bug_add(session, "yourmod", NULL, your_app_callback, robot_info, 0, SMBF_READ_REPLACE, &bug);
}
接下来就是 your_app_callback 收到media bug回调后,将数据使用UDP server 传输出去
static switch_bool_t yourcode_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
{
your_session_info_t *robot_info;
switch_frame_t *frame;
robot_info = (your_session_info_t *) user_data;
if (robot_info == NULL) {
return SWITCH_FALSE;
}
switch (type) {
case SWITCH_ABC_TYPE_INIT:
break;
case SWITCH_ABC_TYPE_READ_REPLACE:
if(robot_info->uuid[0]==0) break;
frame = switch_core_media_bug_get_read_replace_frame(bug);
process_your_code();
break;
case SWITCH_ABC_TYPE_CLOSE:
process_yourcode_close();
break;
default:
break;
}
return SWITCH_TRUE;
}
处理frame数据
static switch_bool_t process_data(your_session_info_t *rh, switch_frame_t *frame,switch_media_bug_t*bug)
{
UDPSend();
return SWITCH_TRUE;
}
这样基本的流程完成了,我们需要完成当服务close,等释放内存、关闭socket等操作。
由此从freeswitch的媒体流,我们进行了转移到我们所熟悉的开发语言上,进行二次高级开发。
接下来就是我们之前讲过的使用freeswitch进行ESL连接,曾经我们处理event record的操作类似,不过我们这里需要使用ESL进行command。
当时候netty处理的流被ASR实时语音转写,再将结果实时送至 智能问答引擎(或者简单规则),我们将问答引擎返回的结果,使用ESL进行command处理。
既然是智能视频IVR,我们可以预先定义好多个视频+路由规则。当用户的话语触发了问答引擎的结果,一一对应至视频的路由。 由此视频IVR从传统按键,转化为通过识别声音而进行智能转发。
下面的我将继续,实现netty接收UDP数据后,如何进行实时语音识别,如何和问答引擎(或智能引擎进行对接),如何提升反应速度等。 (周末结束,繁忙的工作又开始是,加油!!)