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