关于这个功能,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. 最新Typora1.9.5破解版下载与使用教程(Windows+Mac)

    一.Typora是什么? 一款 Markdown 编辑器和阅读器,能知道Typora的小伙伴,肯定也会用的 二.使用步骤 1.下载软件 夸克网盘:https://pan.quark.cn/s/2d6d ...

  2. uv全功能更新:统一管理Python项目、工具、脚本和环境的终极解决方案

    花下猫语:uv 项目自发布起就大受欢迎,目前 Github star 52.6 K,远超过它的同类竞品们.前不久,它的创始人在 X 上披露了一组惊人的数据:uv 曾占据了 PyPI 超过 20% 的流 ...

  3. 解决MySQL 8.0 设置简单密码报错ERROR 1819 (HY000): Your password does not satisfy the current policy require...

    MySQL8.0下设置简单密码出现错误提示:ERROR 1819 (HY000): Your password does not satisfy the current policy requirem ...

  4. 深度解析JS事件驱动模型:如何理解浏览器中的异步回调和事件循环

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  5. 通过COM,用Python调用C#库

    1.C#配置 (1)类库 (2)COM互操作打勾 (3)代码中类必须要有无参构造函数,否则不会注册成功!!! using System; using System.Runtime.InteropSer ...

  6. scikit-learn生成随机数据集

    %matplotlib inline from sklearn import datasets import matplotlib.pyplot as plt import numpy as np s ...

  7. java从小白到老白⑤——传智播客27版笔记

    今天主要说些内部类的相关基础知识,如果能做出下面这个小题目,再下面的内容不看也罢(面试题答案在最后) 内部类面试题:补全下列代码,实现目标输出,其中描述阶段的数字只能调用已有变量,不能用其他方式 pu ...

  8. deepseek-r1的1.5b、7b、8b、14b、32b、70b和671b有啥区别?

    DeepSeek-R1系列提供了多种参数规模的模型(1.5B.7B.8B.14B.32B.70B 和 671B),它们在模型架构.性能表现.资源需求和适用场景上有显著差异.以下是对这些版本的核心区别总 ...

  9. obs学习之4——枚举设备、选择设备

    obs学习之4--枚举设备.选择设备

  10. ChatterBot人工智能,聊天机器人,无坑指南(安装,使用)(2.使用篇)

    上一篇(安装):https://www.cnblogs.com/Ctrl-cCtrl-v/p/13220584.html 基础代码: 1 from chatterbot import ChatBot ...