1 /*
2 * Copyright 2008-2014 Arsen Chaloyan
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * $Id: dtmfsession.cpp 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $
17 */
18
19 #include "dtmfsession.h"
20 #include "dtmfscenario.h"
21 #include "mrcp_message.h"
22 #include "mrcp_generic_header.h"
23 #include "mrcp_recog_header.h"
24 #include "mrcp_recog_resource.h"
25 #include "mpf_dtmf_generator.h"
26 #include "apt_nlsml_doc.h"
27 #include "apt_log.h"
28
29 struct RecogChannel
30 {
31 /** MRCP control channel */
32 mrcp_channel_t* m_pMrcpChannel;
33 /** DTMF generator */
34 mpf_dtmf_generator_t* m_pDtmfGenerator;
35 /** Streaming is in-progress */
36 bool m_Streaming;
37 };
38
DtmfSession(const DtmfScenario * pScenario)39 DtmfSession::DtmfSession(const DtmfScenario* pScenario) :
40 UmcSession(pScenario),
41 m_pRecogChannel(NULL)
42 {
43 }
44
~DtmfSession()45 DtmfSession::~DtmfSession()
46 {
47 }
48
Start()49 bool DtmfSession::Start()
50 {
51 /* create channel and associate all the required data */
52 m_pRecogChannel = CreateRecogChannel();
53 if(!m_pRecogChannel)
54 return false;
55
56 /* add channel to session (send asynchronous request) */
57 if(!AddMrcpChannel(m_pRecogChannel->m_pMrcpChannel))
58 {
59 delete m_pRecogChannel;
60 m_pRecogChannel = NULL;
61 return false;
62 }
63 return true;
64 }
65
OnSessionTerminate(mrcp_sig_status_code_e status)66 bool DtmfSession::OnSessionTerminate(mrcp_sig_status_code_e status)
67 {
68 if(m_pRecogChannel)
69 {
70 if(m_pRecogChannel->m_pDtmfGenerator)
71 {
72 mpf_dtmf_generator_destroy(m_pRecogChannel->m_pDtmfGenerator);
73 m_pRecogChannel->m_pDtmfGenerator = NULL;
74 }
75
76 delete m_pRecogChannel;
77 m_pRecogChannel = NULL;
78 }
79 return UmcSession::OnSessionTerminate(status);
80 }
81
ReadStream(mpf_audio_stream_t * pStream,mpf_frame_t * pFrame)82 static apt_bool_t ReadStream(mpf_audio_stream_t* pStream, mpf_frame_t* pFrame)
83 {
84 RecogChannel* pRecogChannel = (RecogChannel*) pStream->obj;
85 if(pRecogChannel && pRecogChannel->m_Streaming)
86 {
87 if(pRecogChannel->m_pDtmfGenerator)
88 {
89 mpf_dtmf_generator_put_frame(pRecogChannel->m_pDtmfGenerator,pFrame);
90 }
91 }
92 return TRUE;
93 }
94
CreateRecogChannel()95 RecogChannel* DtmfSession::CreateRecogChannel()
96 {
97 mrcp_channel_t* pChannel;
98 mpf_termination_t* pTermination;
99 mpf_stream_capabilities_t* pCapabilities;
100 apr_pool_t* pool = GetSessionPool();
101
102 /* create channel */
103 RecogChannel *pRecogChannel = new RecogChannel;
104 pRecogChannel->m_pMrcpChannel = NULL;
105 pRecogChannel->m_pDtmfGenerator = NULL;
106 pRecogChannel->m_Streaming = false;
107
108 /* create source stream capabilities */
109 pCapabilities = mpf_source_stream_capabilities_create(pool);
110 GetScenario()->InitCapabilities(pCapabilities);
111
112 static const mpf_audio_stream_vtable_t audio_stream_vtable =
113 {
114 NULL,
115 NULL,
116 NULL,
117 ReadStream,
118 NULL,
119 NULL,
120 NULL,
121 NULL
122 };
123
124 pTermination = CreateAudioTermination(
125 &audio_stream_vtable, /* virtual methods table of audio stream */
126 pCapabilities, /* capabilities of audio stream */
127 pRecogChannel); /* object to associate */
128
129 pChannel = CreateMrcpChannel(
130 MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */
131 pTermination, /* media termination, used to terminate audio stream */
132 NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
133 pRecogChannel); /* object to associate */
134 if(!pChannel)
135 {
136 delete pRecogChannel;
137 return NULL;
138 }
139
140 pRecogChannel->m_pMrcpChannel = pChannel;
141 return pRecogChannel;
142 }
143
OnChannelAdd(mrcp_channel_t * pMrcpChannel,mrcp_sig_status_code_e status)144 bool DtmfSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
145 {
146 if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
147 return false;
148
149 if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
150 {
151 /* error case, just terminate the demo */
152 return Terminate();
153 }
154
155 RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
156 if(pRecogChannel)
157 {
158 const mpf_audio_stream_t* pStream = mrcp_application_audio_stream_get(pMrcpChannel);
159 if(pStream)
160 {
161 pRecogChannel->m_pDtmfGenerator = mpf_dtmf_generator_create(pStream,GetSessionPool());
162 }
163 }
164
165 return StartRecognition(pMrcpChannel);
166 }
167
OnMessageReceive(mrcp_channel_t * pMrcpChannel,mrcp_message_t * pMrcpMessage)168 bool DtmfSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
169 {
170 if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
171 return false;
172
173 const DtmfScenario* pScenario = GetScenario();
174 RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
175 if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
176 {
177 if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNIZE)
178 {
179 /* received the response to RECOGNIZE request */
180 if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
181 {
182 /* start to stream the DTMFs to recognize */
183 if(pRecogChannel && pRecogChannel->m_pDtmfGenerator)
184 {
185 const char* digits = pScenario->GetDigits();
186 if(digits)
187 {
188 mpf_dtmf_generator_enqueue(pRecogChannel->m_pDtmfGenerator,digits);
189 pRecogChannel->m_Streaming = true;
190 }
191 }
192 }
193 else
194 {
195 /* received unexpected response, terminate the session */
196 Terminate();
197 }
198 }
199 else
200 {
201 /* received unexpected response */
202 }
203 }
204 else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
205 {
206 if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNITION_COMPLETE)
207 {
208 ParseNLSMLResult(pMrcpMessage);
209 if(pRecogChannel)
210 {
211 pRecogChannel->m_Streaming = false;
212 }
213 Terminate();
214 }
215 else if(pMrcpMessage->start_line.method_id == RECOGNIZER_START_OF_INPUT)
216 {
217 /* received start-of-input, do whatever you need here */
218 }
219 }
220 return true;
221 }
222
StartRecognition(mrcp_channel_t * pMrcpChannel)223 bool DtmfSession::StartRecognition(mrcp_channel_t* pMrcpChannel)
224 {
225 RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
226 /* create and send RECOGNIZE request */
227 mrcp_message_t* pMrcpMessage = CreateRecognizeRequest(pMrcpChannel);
228 if(pMrcpMessage)
229 {
230 SendMrcpRequest(pRecogChannel->m_pMrcpChannel,pMrcpMessage);
231 }
232
233 return true;
234 }
235
CreateRecognizeRequest(mrcp_channel_t * pMrcpChannel)236 mrcp_message_t* DtmfSession::CreateRecognizeRequest(mrcp_channel_t* pMrcpChannel)
237 {
238 mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,RECOGNIZER_RECOGNIZE);
239 if(!pMrcpMessage)
240 return NULL;
241
242 const DtmfScenario* pScenario = GetScenario();
243
244 mrcp_generic_header_t* pGenericHeader;
245 mrcp_recog_header_t* pRecogHeader;
246
247 /* get/allocate generic header */
248 pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
249 if(pGenericHeader)
250 {
251 apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
252 mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
253 /* set message body */
254 if(pScenario->GetGrammar())
255 apt_string_assign(&pMrcpMessage->body,pScenario->GetGrammar(),pMrcpMessage->pool);
256 }
257 /* get/allocate recognizer header */
258 pRecogHeader = (mrcp_recog_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
259 if(pRecogHeader)
260 {
261 /* set recognizer header fields */
262 if(pMrcpMessage->start_line.version == MRCP_VERSION_2)
263 {
264 pRecogHeader->cancel_if_queue = FALSE;
265 mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_CANCEL_IF_QUEUE);
266 }
267 }
268 return pMrcpMessage;
269 }
270
ParseNLSMLResult(mrcp_message_t * pMrcpMessage)271 bool DtmfSession::ParseNLSMLResult(mrcp_message_t* pMrcpMessage)
272 {
273 nlsml_result_t *pResult = nlsml_result_parse(pMrcpMessage->body.buf, pMrcpMessage->body.length, pMrcpMessage->pool);
274 if(!pResult)
275 return false;
276
277 nlsml_result_trace(pResult, pMrcpMessage->pool);
278 return true;
279 }
280