freeswitch笔记(9)-esl outbound中如何放音采集按键?
时间:2022-07-22
本文章向大家介绍freeswitch笔记(9)-esl outbound中如何放音采集按键?,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
关于这个功能,esl-client 上给出的源码示例极具误导性,根本跑不起来,见: https://github.com/esl-client/esl-client/blob/master/src/test/java/OutboundTest.java
正确姿势:必须在事件订阅的回调里,才能拿到用户按键值
示例代码:
package org.freeswitch.esl.client;
import org.freeswitch.esl.client.dptools.Execute;
import org.freeswitch.esl.client.dptools.ExecuteException;
import org.freeswitch.esl.client.internal.Context;
import org.freeswitch.esl.client.outbound.IClientHandler;
import org.freeswitch.esl.client.outbound.IClientHandlerFactory;
import org.freeswitch.esl.client.outbound.SocketClient;
import org.freeswitch.esl.client.transport.event.EslEvent;
import org.freeswitch.esl.client.transport.message.EslHeaders.Name;
import org.freeswitch.esl.client.transport.message.EslMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.regex.Pattern;
import static com.google.common.base.Throwables.throwIfUnchecked;
public class OutboundDTMFTest {
private static Logger logger = LoggerFactory.getLogger(OutboundDTMFTest.class);
private static String sb = "/usr/local/freeswitch/sounds/en/us/callie/ivr/8000/";
String prompt = sb + "ivr-please_enter_extension_followed_by_pound.wav";
String failed = sb + "ivr-that_was_an_invalid_entry.wav";
public static void main(String[] args) {
new OutboundDTMFTest();
}
public OutboundDTMFTest() {
try {
//outbound test
final SocketClient outboundServer = new SocketClient(
new InetSocketAddress("localhost", 8086),
new OutboundHandlerFactory());
outboundServer.startAsync();
} catch (Throwable t) {
throwIfUnchecked(t);
}
}
public class OutboundHandlerFactory implements IClientHandlerFactory {
@Override
public IClientHandler createClientHandler() {
//just for sample , recommend use singleton pattern, to avoid new too many instance
return new OutboundHandler();
}
}
public class OutboundHandler implements IClientHandler {
StringBuffer buffer = new StringBuffer(10);
String pattern1 = "^\d+";
String pattern2 = "^\d+#$";
@Override
public void onConnect(Context context, EslEvent eslEvent) {
try {
Execute exe = new Execute(context, "");
//订阅DTMF事件
EslMessage eslMessage = context.sendCommand("event plain DTMF");
if (eslMessage.getHeaderValue(Name.REPLY_TEXT).startsWith("+OK")) {
logger.info("subscribe event success!");
}
exe.answer();
int timeOutSeconds = 30;
//放音采集
exe.playAndGetDigits(1,
1, 10, timeOutSeconds * 1000, "#", prompt,
failed, pattern1, timeOutSeconds * 1000);
//等待用户输入按键
long start = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - start > timeOutSeconds * 1000) {
break;
}
if (buffer.length() > 0 && Pattern.matches(pattern2, buffer.toString())) {
break;
}
Thread.sleep(50);
}
System.out.println("you pressed:" + buffer.toString());
} catch (ExecuteException | InterruptedException e) {
logger.error("Could not prompt for digits", e);
} finally {
context.closeChannel();
}
}
@Override
public void onEslEvent(Context ctx, EslEvent event) {
// System.out.println(event.getEventName());
if (event.getEventName().equalsIgnoreCase("DTMF")) {
String key = event.getEventHeaders().get("DTMF-Digit");
if ("#".equalsIgnoreCase(key)) {
//检查是否输入正确(如果错误,请将之前输入的清空掉)
if (!Pattern.matches(pattern1, buffer.toString())) {
buffer.setLength(0);
return;
}
}
buffer.append(key);
}
}
}
}
解释一下:
1. 首先要订阅DTMF事件,只有在事件回调里,才能拿到用户按键信息
2. playAndGetDigits 在outbound async full异步模式下,这个方法的返回值,其实没啥用,永远都是__undef__,所以要在后面循环检测结果,还要考虑用户一直不按键的情况,要有超时保底
3. 事件回调onEslEvent与用户进线onConnect是在2个不同的方法中,但是都是在同一个线程里的,所以为方便起见,用了一个线程安全的StringBuffer用来保存按键信息
4. 事件回调中,要考虑用户按错键的情况,比如提示用户按数字键,然后用户输入了字母或星号之类的,遇到这种要把之前的输入结果清掉。
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(47)-工作流设计-补充
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(46)-工作流设计-设计分支
- 瑞典研发未来概念车,车厢、底盘能分离,颠覆汽车使用方式
- WCF技术剖析之十一:异步操作在WCF中的应用(下篇)
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(45)-工作流设计-设计步骤
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(44)-工作流设计-设计表单
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(43)-工作流设计-字段分类设计
- 因为这个原因,你的工作即将被取代!
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(73)-微信公众平台开发-消息管理
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理
- 充斥着AI生成内容的世界,你能辨别虚拟与现实吗?
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(71)-微信公众平台开发-公众号管理
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(69)-微信公众平台开发-功能概述
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(68)-微信公众平台开发- 资源环境准备
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- C++核心准则CP.111:如果真的需要好双重检查锁,使用惯用模式
- C++核心准则CP.200:使用volatile只能表明该变量是非C++内存
- C++核心准则E.2:通过抛出异常来表明函数无法执行指定的任务
- C++核心准则E.3:异常应该只用于错误处理
- [Go] GO语言实战-gin框架项目实现中英文切换
- [Go] Go语言实战-基于websocket浏览器通知的实现
- 快10年的老前端了,我还分不清 slice 和 splice,这到底是谁的锅
- 第4章 Jenkins系统用户认证配置管理
- 回答公众号留言的2个关于相关性分析的问题
- css 透明度和百分比对应值
- Qt项目DeskGirl开发
- 三日php之路 -- 第二,三天(php知识要点)
- GitLabRunner和流水线的数据采集与监控
- FPGA开源工具链
- 电脑软件(例如 Adobe、Autodesk)为什么那么容易被破解?