freeswitch笔记(9)-esl outbound中如何放音采集按键?
关于这个功能,esl-client 上给出的源码示例极具误导性,根本跑不起来,见: https://github.com/esl-client/esl-client/blob/master/src/test/java/OutboundTest.java
正确姿势:必须在事件订阅的回调里,才能拿到用户按键值
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
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. 事件回调中,要考虑用户按错键的情况,比如提示用户按数字键,然后用户输入了字母或星号之类的,遇到这种要把之前的输入结果清掉。