首页 > 其他分享 >C语言中使用ESL连接FreeSwitch

C语言中使用ESL连接FreeSwitch

时间:2023-01-04 18:11:06浏览次数:44  
标签:esl handle thread C语言 DEBUG FreeSwitch ESL event

前言
之前在【FreeSwitch开发实践】在nodejs中用ESL连接FreeSwitch一文介绍了在NodeJS下使用ESL连接FreeSwitch, 本文则对在C语言下使用ESL连接FreeSwitch作了一个系统介绍。和NodeJS下使用ESL需要安装modesl模块一样,C语言下使用ESL也需要libesl库.
(本文代码示例下载地址)

1、libesl库编译安装
libesl库在FreeSwitch中是自带的,所以编译FreeSwitch的时候,实际已经安装了libesl,这里单独对FreeSwitch下编译libesl简要说明下:

#进入FreeSwitch下libesl源码目录
/data/freeswitch/libs/esl
make
make install

运行完编译命令后,libesl实际已经安装到了FreeSwitch的安装目录下:
/usr/local/freeswitch/lib/

 

2、在Makefile中引入libesl
上一节说明了,libesl的编译安装过程以及编译后库的生成目录,这里对在Makefile中引入libesl,介绍下。
Makefile如下:

TOP_PATH := $(shell pwd)
INCLUDE := -I/data/freeswitch/libs/esl/src/include
LIBS_PATH := -L/usr/local/freeswitch/lib
LIBS := -lesl -lpthread
CC := gcc
TARGET := esl_test

SRCDIRS := ./
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
CFILENDIR := $(CFILES)
COBJS := $(patsubst %, %, $(CFILENDIR:.c=.o))

.PHONY: clean

$(TARGET):$(COBJS)
$(CC) $^ $(INCLUDE) $(LIBS_PATH) $(LIBS) -o $(TOP_PATH)/$@

$(COBJS) : %.o : %.c
$(CC) -c $< $(INCLUDE) $(LIBS_PATH) $(LIBS) -o $@

clean:
@rm *.o
@rm $(TOP_PATH)/$(TARGET)

其中, INCLUDE 是FreeSwitch中esl头文件的目录, LIBS_PATH 是libesl的最终生成目录,LIBS 为引入的库,-lesl则为libesl.a库的引入。

3、ESL连接FreeSwitch
先看一下用C语言版本的ESL连接FreeSwitch

esl_handle_t handle = {{0}};
esl_global_set_default_logger(ESL_LOG_LEVEL_DEBUG);

memset(&handle, 0, sizeof(handle));
if (esl_connect_timeout(&handle, "10.0.8.10", 8021, "", "ClueCon", 3000)) {
esl_global_set_default_logger(7);
esl_log(ESL_LOG_ERROR, "Error Connecting [%s]\n", handle.err);
return -1;
}


连接FreeSwitch用的接口是esl_connect_timeout, 需要的参数分别是·ip、端口、密码和超时时间,基本和NodeJS中类似。

4、ESL事件订阅
esl_events(&handle, ESL_EVENT_TYPE_PLAIN, "CHANNEL_ANSWER");
esl_events(&handle, ESL_EVENT_TYPE_PLAIN, "CHANNEL_HANGUP_COMPLETE");
1
2
事件订阅用到的esl_events接口, 第3个参数是事件名称, 第2个参数是事件体的类型,包括plain/xml/json,定义如下:

typedef enum {
ESL_EVENT_TYPE_PLAIN,
ESL_EVENT_TYPE_XML,
ESL_EVENT_TYPE_JSON
} esl_event_type_t;

5、事件监听
esl_recv_event_timed(handle, 10, 1, NULL);
1
完整的定义如下:

/*!
\brief Poll the handle's socket until an event is received, a connection error occurs or ms expires
\param handle Handle to poll
\param ms Maximum time to poll
\param check_q If set to 1, will check the handle queue (handle->race_event) and return the last event from it
\param[out] save_event If this is not NULL, will return the event received
*/
ESL_DECLARE(esl_status_t) esl_recv_event_timed(esl_handle_t *handle, uint32_t ms, int check_q, esl_event_t **save_event);

6、完整的例子
下面是完整的例子,包括ESL连接,发送命令和事件订阅、事件监听。

#include <stdio.h>
#include <stdlib.h>
#include <esl.h>
#include <signal.h>
#include <getopt.h>

static void _sleep_ns(int secs, long nsecs) {
#ifndef WIN32
if (nsecs > 999999999) {
secs += nsecs/1000000000;
nsecs = nsecs % 1000000000;
}
{
struct timespec ts = { secs, nsecs };
nanosleep(&ts, NULL);
}
#else
Sleep(secs*1000 + nsecs/1000000);
#endif
}

static void sleep_ns(long nsecs) { _sleep_ns(0, nsecs); }
static void sleep_ms(int msecs) { sleep_ns(msecs*1000000); }
static void sleep_s(int secs) { _sleep_ns(secs, 0); }
static void *msg_thread_run(esl_thread_t *me, void *obj);
static esl_mutex_t *MUTEX = NULL;
static int thread_running = 0, thread_up = 0, check_up = 0;


int main()
{
char cmd_str[2048] = "";
esl_handle_t handle = {{0}};
esl_global_set_default_logger(ESL_LOG_LEVEL_DEBUG);

memset(&handle, 0, sizeof(handle));
//ESL连接FreeSwitch
if (esl_connect_timeout(&handle, "10.0.8.10", 8021, "", "ClueCon", 3000)) {
esl_global_set_default_logger(7);
esl_log(ESL_LOG_ERROR, "Error Connecting [%s]\n", handle.err);
return -1;
}

esl_mutex_create(&MUTEX);

//启动ESL事件临听线程
if (esl_thread_create_detached(msg_thread_run, &handle) != ESL_SUCCESS) {
esl_log(ESL_LOG_ERROR, "Error starting thread!\n");
esl_disconnect(&handle);
return 0;
}

//ESL事件订阅
esl_events(&handle, ESL_EVENT_TYPE_PLAIN, "CHANNEL_ANSWER");
esl_events(&handle, ESL_EVENT_TYPE_PLAIN, "CHANNEL_HANGUP_COMPLETE");

//发送api version命令
snprintf(cmd_str, sizeof(cmd_str), "api version\n\n");
esl_send_recv(&handle, cmd_str);
//接收api version返回
if (handle.last_sr_event && handle.last_sr_event->body) {
esl_log(ESL_LOG_INFO, "%s\n", handle.last_sr_event->body);
}

//拨打软电话命令api originate user/1000 &echo
snprintf(cmd_str, sizeof(cmd_str), "api originate user/1000 &echo\n\n");
esl_send_recv(&handle, cmd_str);
if (handle.last_sr_event && handle.last_sr_event->body) {
esl_log(ESL_LOG_INFO, "%s\n", handle.last_sr_event->body);
}

//等待,防止主线程退出
while(handle.connected)
{
sleep_ms(10);
}
//断开ESL连接
esl_disconnect(&handle);

//等待ESL事件监听线程退出
do {
esl_mutex_lock(MUTEX);
check_up = thread_up;
esl_mutex_unlock(MUTEX);
sleep_ms(10);
} while (check_up > 0);

esl_mutex_destroy(&MUTEX);
}
//事件监听线程
static void *msg_thread_run(esl_thread_t *me, void *obj)
{
esl_handle_t *handle = (esl_handle_t *) obj;
thread_running = 1;
esl_mutex_lock(MUTEX);
thread_up = 1;
esl_mutex_unlock(MUTEX);

while(thread_running && handle->connected) {
int aok = 1;
esl_status_t status;

esl_mutex_lock(MUTEX);
//等待事件到来,只有esl_events注册的事件才能监听到
status = esl_recv_event_timed(handle, 10, 1, NULL);
esl_mutex_unlock(MUTEX);

if (status == ESL_BREAK) {
sleep_ms(1);
} else if (status == ESL_FAIL) {
esl_log(ESL_LOG_WARNING, "Disconnected.\n");
thread_running = 0;
} else if (status == ESL_SUCCESS) {
//事件到来,打印事件体
esl_log(ESL_LOG_INFO, "coming event_body:%xs\n", handle->last_event);
if (handle->last_event->body) {
esl_log(ESL_LOG_INFO, "event_body:%s\n", handle->last_event->body);
}
}
}
esl_mutex_lock(MUTEX);
thread_up = 0;
esl_mutex_unlock(MUTEX);
thread_running = 0;
esl_log(ESL_LOG_DEBUG, "Thread Done\n");
}

本例子是博主亲自实验过的,基于fs_cli实现,如果读者有兴趣可以详细看一下fs_cli源码,应该会有更多收获。

输出:

[root@VM-8-10-centos esl_test]# ./esl_test
[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Content-Type] = [auth/request]
[DEBUG] esl.c:1467 esl_recv_event() RECV MESSAGE
Event-Name: SOCKET_DATA
Content-Type: auth/request


[DEBUG] esl.c:1495 esl_send() SEND
auth ClueCon


[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Content-Type] = [command/reply]
[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Reply-Text] = [+OK accepted]
[DEBUG] esl.c:1467 esl_recv_event() RECV MESSAGE
Event-Name: SOCKET_DATA
Content-Type: command/reply
Reply-Text: +OK accepted


[DEBUG] esl.c:1495 esl_send() SEND
event plain CHANNEL_ANSWER


[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Content-Type] = [command/reply]
[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Reply-Text] = [+OK event listener enabled plain]
[DEBUG] esl.c:1495 esl_send() SEND
event plain CHANNEL_HANGUP_COMPLETE


[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Content-Type] = [command/reply]
[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Reply-Text] = [+OK event listener enabled plain]
[DEBUG] esl.c:1495 esl_send() SEND
api version


[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Content-Type] = [api/response]
[DEBUG] esl.c:1303 esl_recv_event() RECV HEADER [Content-Length] = [113]
[INFO] test.c:59 main() FreeSWITCH Version 1.10.7-release+git~20211024T163933Z~883d2cb662~64bit (git 883d2cb 2021-10-24 16:39:33Z 64bit)

[DEBUG] esl.c:1495 esl_send() SEND
api originate user/1000 &echo

answer事件

[INFO] test.c:108 msg_thread_run() coming event_body:a40008c0s
[INFO] test.c:110 msg_thread_run() event_body:Event-Name: CHANNEL_ANSWER
Core-UUID: 8f363510-c0cf-4aa4-bbfc-577cc6ff543b
FreeSWITCH-Hostname: VM-8-10-centos
FreeSWITCH-Switchname: VM-8-10-centos
FreeSWITCH-IPv4: 10.0.8.10
FreeSWITCH-IPv6: fe80%3A%3A5054%3Aff%3Afe58%3A28ea
Event-Date-Local: 2022-07-17%2022%3A34%3A37
Event-Date-GMT: Sun,%2017%20Jul%202022%2014%3A34%3A37%20GMT
Event-Date-Timestamp: 1658068477404961
Event-Calling-File: switch_channel.c
Event-Calling-Function: switch_channel_perform_mark_answered
Event-Calling-Line-Number: 3884
Event-Sequence: 646
Channel-State: CS_CONSUME_MEDIA
Channel-Call-State: RINGING
Channel-State-Number: 7
Channel-Name: sofia/internal/1000%4061.149.73.246%3A3387
Unique-ID: ba515d00-58f1-4433-af24-f0cafeac7e4e
Call-Direction: outbound
Presence-Call-Direction: outbound
Channel-HIT-Dialplan: false
Channel-Presence-ID: 1000%4010.0.8.10
Channel-Call-UUID: ba515d00-58f1-4433-af24-f0cafeac7e4e
Answer-State: answered
Channel-Read-Codec-Name: PCMU
Channel-Read-Codec-Rate: 8000
Channel-Read-Codec-Bit-Rate: 64000
Channel-Write-Codec-Name: PCMU
Channel-Write-Codec-Rate: 8000
Channel-Write-Codec-Bit-Rate: 64000
Caller-Direction: outbound
Caller-Logical-Direction: outbound
Caller-Caller-ID-Number: 0000000000
Caller-Orig-Caller-ID-Number: 0000000000
Caller-Callee-ID-Name: Outbound%20Call
Caller-Callee-ID-Number: 1000
Caller-Network-Addr: 61.149.73.246
Caller-ANI: 0000000000
Caller-Destination-Number: 1000
Caller-Unique-ID: ba515d00-58f1-4433-af24-f0cafeac7e4e
Caller-Source: src/switch_ivr_originate.c
Caller-Context: default
Caller-Channel-Name: sofia/internal/1000%4061.149.73.246%3A3387
Caller-Profile-Index: 1
Caller-Profile-Created-Time: 1658068471904968
Caller-Channel-Created-Time: 1658068471904968
Caller-Channel-Answered-Time: 1658068477404961
Caller-Channel-Progress-Time: 1658068471984961
Caller-Channel-Progress-Media-Time: 0
Caller-Channel-Hangup-Time: 0
Caller-Channel-Transfer-Time: 0
Caller-Channel-Resurrect-Time: 0
Caller-Channel-Bridged-Time: 0
Caller-Channel-Last-Hold: 0
Caller-Channel-Hold-Accum: 0
Caller-Screen-Bit: true
Caller-Privacy-Hide-Name: false
Caller-Privacy-Hide-Number: false
variable_direction: outbound
variable_is_outbound: true

hangup事件

[INFO] test.c:108 msg_thread_run() coming event_body:8400df60s
[INFO] test.c:110 msg_thread_run() event_body:Event-Name: CHANNEL_HANGUP_COMPLETE
Core-UUID: f7005cb2-45e7-4643-ae7e-d10d45a7b4d1
FreeSWITCH-Hostname: VM-8-10-centos
FreeSWITCH-Switchname: VM-8-10-centos
FreeSWITCH-IPv4: 10.0.8.10
FreeSWITCH-IPv6: fe80%3A%3A5054%3Aff%3Afe58%3A28ea
Event-Date-Local: 2022-07-17%2022%3A32%3A42
Event-Date-GMT: Sun,%2017%20Jul%202022%2014%3A32%3A42%20GMT
Event-Date-Timestamp: 1658068362698622
Event-Calling-File: switch_core_state_machine.c
Event-Calling-Function: switch_core_session_reporting_state
Event-Calling-Line-Number: 943
Event-Sequence: 846950
Hangup-Cause: NORMAL_CLEARING
Channel-State: CS_REPORTING
Channel-Call-State: HANGUP
Channel-State-Number: 11
Channel-Name: sofia/external/1001%4010.0.8.10
Unique-ID: 44c3b4f0-7091-49ea-8f61-8a368648609a
Call-Direction: inbound
Presence-Call-Direction: inbound
Channel-HIT-Dialplan: true
Channel-Call-UUID: 44c3b4f0-7091-49ea-8f61-8a368648609a
Answer-State: hangup
Hangup-Cause: NORMAL_CLEARING
Caller-Direction: inbound
Caller-Logical-Direction: inbound
Caller-Username: 1001
Caller-Dialplan: XML
Caller-Caller-ID-Name: 1001
Caller-Caller-ID-Number: 1001
Caller-Orig-Caller-ID-Name: 1001
Caller-Orig-Caller-ID-Number: 1001
Caller-Network-Addr: 20.52.185.66
Caller-ANI: 1001
Caller-Destination-Number: 2985720103


总结
以上就是今天的内容,详细的介绍了C语言使用ESL连接FreeSwitch。

标签:esl,handle,thread,C语言,DEBUG,FreeSwitch,ESL,event
From: https://www.cnblogs.com/kn-zheng/p/17025656.html

相关文章

  • 针对FreeSWITCH的最佳开源GUI解决方案
    为了获得FreeSWITCH的最大利益,您需要能够正确选择GUI解决方案。看看FreeSWITCH的一些开源GUI解决方案,见证了它们的广泛普及和采用率。FreeSWITCHGUI被广泛地描述为FsG......
  • 手把手教你部署验证freeswitch(避免踩坑)
    介绍:freeswitch可集成ASR(语音识别)和TTS(文本转语音)创建智能电话机器人和用户通话,可用于问卷调查,自动催缴等业务,电话咨询服务,支持百度,科大,阿里云,腾讯等ASR系统,可以真人预......
  • FreeSWITCH学习笔记3(3.5)- 初识FreeSWITCH
    目录配置SIP网关拨打外部电话:从某一分机上呼出呼入电话处理 配置SIP网关拨打外部电话 originatesofia/gateway/zlz/1003&echo(前提是设置了1003,并且选定了才......
  • FreeSWITCH学习笔记3(3.3、3.4)- 初识FreeSWITCH
    目录:1、配置FreeSWITCH2、FreeSWITCH用作软电话 1、配置FreeSWITCH具体参考:《FreeSWITCH权威指南》3.3节以下,将通过添加一个新的FreeSWITCH用户来简单熟悉一下Free......
  • FreeSWITCH学习笔记1 - PSTN与VoIP基础
    目录: FreeSWITCHFreeSWITCH是一个电话的软交换解决方案,包括一个软电话和软交换机用以提供语音和聊天的产品驱动。FreeSWITCH可以用作交换机引擎、PBX、多媒体网关以......
  • FreeSWITCH学习笔记2 - PSTN、PBX及呼叫中心业务
    目录:POTSPBXPrivateBranchExchange,用户级交换机,即公司内部使用的电话业务网络,系统内部分机用户分享一定数量的外线。PBX俗称:程控交换机,程控用户交换机,电话交换机,......
  • FreeSWITCH学习笔记3(3.1、3.2)- 初识FreeSWITCH
    目录:FreeSWITCH的功能连接SIP电话连接SIP电话——实操 1、FreeSWITCH的功能  安装完成FreeSWITCH后,以管理员身份运行。通过在控制台中输入:shutdown......
  • FreeSWITCH学习笔记7(7-7.3.1) - SIP协议
    目录:   7.1、SIP协议基础 7.1.1、HTTP与SIP协议基础   7.1.2、SIP的基础概念和相关元素       7.1.3、SIP协议的基本方法和头域简介 ......
  • FreeSWITCH学习笔记7(7.3.2-结束) - SIP协议
    目录: 7.3.2、通过B2BUA呼叫 具体流程描述见7.3.2。  7.4、深入理解SIP7.4.1、SIPURI 7.4.2、SDP和SOASDP——(SessionDescriptionProtocol,会话描述......
  • FreeSWITCH学习笔记6(6.1.7-) - 拨号计划
    目录:   6.1.7工作机制深入剖析 举例见6.1.7。6.1.8内联执行  6.1.9实例解析实例见6.1.9。 6.2inlineDialplan(内联拨号计划)    6.3......