关于这个功能,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. 事件回调中,要考虑用户按错键的情况,比如提示用户按数字键,然后用户输入了字母或星号之类的,遇到这种要把之前的输入结果清掉。

freeswitch笔记(9)-esl outbound中如何放音采集按键?的更多相关文章

  1. shell 中函数放回字符串问题

    shell 中函数放回字符串问题 shell 中不可以直接 return 字符串 ,可以return 数字.如果要return 字符串 改为 echo "hello world" ...

  2. python笔记之提取网页中的超链接

    python笔记之提取网页中的超链接 对于提取网页中的超链接,先把网页内容读取出来,然后用beautifulsoup来解析是比较方便的.但是我发现一个问题,如果直接提取a标签的href,就会包含jav ...

  3. 阅读OReilly.Web.Scraping.with.Python.2015.6笔记---找出网页中所有的href

    阅读OReilly.Web.Scraping.with.Python.2015.6笔记---找出网页中所有的href 1.查找以<a>开头的所有文本,然后判断href是否在<a> ...

  4. [译]我们应该在HTML文档中何处放script标签

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  5. 从加载DLL的中获取放置于Resources文件夹中资源字典的几种方法

    原文:从加载DLL的中获取放置于Resources文件夹中资源字典的几种方法 主程序 为 Main_Test.exe 被加载的DLL 为 Load_Test.dll  此DLL 中 有一个 文件夹Re ...

  6. [RN] React Native 使用 图片预览和放大 插件 react-native-image-zoom-viewer 过程中,放大报错问题

    React Native 使用 图片预览和放大 插件 react-native-image-zoom-viewer 过程中,放大报错问题 报错如下: Cannot record touch end w ...

  7. Django笔记八之model中Meta参数的使用

    前面介绍了 model 的字段属性,字段类型,这篇笔记介绍一下 model 的 Meta 选项. 这个选项提供了一些参数,比如排序(ordering),表名(db_table)等. 但这都不是必需的, ...

  8. H5之audio标签放音兼容所有浏览器方法

    前端交流群,群文件提供大量文档.书籍和资料.期待你的加入!群号:127768464 由于项目需要,最近刚做了一个网页放音的功能,使用到了H5新标签<audio></audio> ...

  9. c#录音和放音,超简单!不用DirectX

    最近在做android与C#录音并互相通信的小东西.但是卡在C#录音这儿了.找了好久,说的都是DirectX,可是我总是安装不上,这才找到了这个简单的录音方法.当然,如果你想要录得好并且处理音频,那还 ...

  10. alsamixer + alsactl 控制放音通道

    1 使用alsamixer的gui界面配置放音(控制OUT1,OUT2的音量); 2 退出alsamixer,使用alsactl  store生成配置文件,文件位于/etc/asound.state; ...

随机推荐

  1. 第二章 Spring Boot 整合 Kafka消息队列 生产者

    ​ 系列文章目录 第一章 Kafka 配置部署及SASL_PLAINTEXT安全认证 第二章  Spring Boot 整合 Kafka消息队列 生产者 第三章  Spring Boot 整合 Kaf ...

  2. 【BUG】Hexo|GET _MG_0001.JPG 404 (Not Found),hexo博客搭建过程图片路径正确却找不到图片

    我的问题 我查了好多资料,结果原因是图片名称开头是_则该文件会被忽略...我注意到网上并没有提到这个问题,遂补了一下这篇博客并且汇总了我找到的所有解决办法. 具体检查方式: hexo生成一下静态资源: ...

  3. 【ROS】6.1 中间人攻击移动过程

    原视频 中间人攻击 实验目的 ARP欺骗可以作为中间人攻击的一个起手式.前一个实验我们完成了ARP欺骗,在本章实验中,我们将会在上一个实验的基础上,针对键盘控制过程的报文进行修改,使键盘控制异常. 本 ...

  4. Rust 在 Linux 下的安装

    Rust 在 Linux 下的安装 我感觉, Rust 这个语言, 集 C, C++, Java, Scala, JavaScript, TypeScript, Python ... 等语言的优势于一 ...

  5. 区块链共识算法--PoW

    PoW算法为一种概率算法,其共识结果是临时的,随着时间推移或某种强化,共识结果被推翻的概率越来越小,最终称为事实上结果 1 研究 工作量证明(Proof Of Work,简称POW),简单理解就是一份 ...

  6. ComfyUI+通义万相 Wan2.1系列生成视频教程

    一.Wan2.1系列 Wan2.1 系列是阿里巴巴通义实验室推出的开源视频生成模型套件,共有 4 个模型,包括文生视频的 Wan2.1-T2V-1.3B 和 Wan2.1-T2V-14B,以及图生视频 ...

  7. 【教程】Ubuntu 16.04 配置 CLion 开发 ROS Melodic

    [教程]Ubuntu 16.04 配置 CLion 开发 ROS Melodic 目录 [教程]Ubuntu 16.04 配置 CLion 开发 ROS Melodic 笔者环境 步骤 下载安装 CL ...

  8. Spring Boot2.5 集成数据库连接池 HikariCP

    目录 §工程环境 §数据库连接池介绍 数据库连接池工作原理剖析 §Java常见数据库连接池性能比较 §数据库连接池选型 Druid vs HikariCP性能对比 §HikariCP为什么这么快 §数 ...

  9. 「Log」2023.8.28 小记

    序幕 七点多到校,整理博客,开了一篇新做题记录. 整理一下学过知识点,准备阶段复习. 八点整开始打模拟赛,接下来算游记. T1 是个静态区间第 \(k\) 小板子,纯主席树被空间卡掉了,打了 \(60 ...

  10. java的StackOverflowError异常

    之前明明能查到,现在突然报错StackOverflowError,并一直在控制台返回空对象 多次遇到这种情况 发现是东西存入缓存中,缓存内存不够导致栈溢出,刷新kill缓存即可