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: mrcp_recorder_state_machine.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $
17  */
18 
19 #include "apt_obj_list.h"
20 #include "apt_log.h"
21 #include "mrcp_recorder_state_machine.h"
22 #include "mrcp_generic_header.h"
23 #include "mrcp_recorder_header.h"
24 #include "mrcp_recorder_resource.h"
25 #include "mrcp_message.h"
26 
27 /** MRCP recorder states */
28 typedef enum {
29 	RECORDER_STATE_IDLE,
30 	RECORDER_STATE_RECORDING,
31 
32 	RECORDER_STATE_COUNT
33 } mrcp_recorder_state_e;
34 
35 static const char * state_names[RECORDER_STATE_COUNT] = {
36 	"IDLE",
37 	"RECORDING",
38 };
39 
40 typedef struct mrcp_recorder_state_machine_t mrcp_recorder_state_machine_t;
41 
42 struct mrcp_recorder_state_machine_t {
43 	/** state machine base */
44 	mrcp_state_machine_t   base;
45 	/** recorder state */
46 	mrcp_recorder_state_e  state;
47 	/** request sent to recorder engine and waiting for the response to be received */
48 	mrcp_message_t        *active_request;
49 	/** in-progress record request */
50 	mrcp_message_t        *record;
51 	/** properties used in set/get params */
52 	mrcp_message_header_t *properties;
53 };
54 
55 typedef apt_bool_t (*recorder_method_f)(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message);
56 
recorder_request_dispatch(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)57 static APR_INLINE apt_bool_t recorder_request_dispatch(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
58 {
59 	state_machine->active_request = message;
60 	return state_machine->base.on_dispatch(&state_machine->base,message);
61 }
62 
recorder_response_dispatch(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)63 static APR_INLINE apt_bool_t recorder_response_dispatch(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
64 {
65 	state_machine->active_request = NULL;
66 	if(state_machine->base.active == FALSE) {
67 		/* this is the response to deactivation (STOP) request */
68 		return state_machine->base.on_deactivate(&state_machine->base);
69 	}
70 	return state_machine->base.on_dispatch(&state_machine->base,message);
71 }
72 
recorder_event_dispatch(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)73 static APR_INLINE apt_bool_t recorder_event_dispatch(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
74 {
75 	if(state_machine->base.active == FALSE) {
76 		/* do nothing, state machine has already been deactivated */
77 		return FALSE;
78 	}
79 	return state_machine->base.on_dispatch(&state_machine->base,message);
80 }
81 
recorder_state_change(mrcp_recorder_state_machine_t * state_machine,mrcp_recorder_state_e state,mrcp_message_t * message)82 static APR_INLINE void recorder_state_change(mrcp_recorder_state_machine_t *state_machine, mrcp_recorder_state_e state, mrcp_message_t *message)
83 {
84 	apt_log(APT_LOG_MARK,APT_PRIO_INFO,"State Transition %s -> %s "APT_SIDRES_FMT,
85 		state_names[state_machine->state],
86 		state_names[state],
87 		MRCP_MESSAGE_SIDRES(message));
88 	state_machine->state = state;
89 	if(state == RECORDER_STATE_IDLE) {
90 		state_machine->record = NULL;
91 	}
92 }
93 
94 
recorder_request_set_params(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)95 static apt_bool_t recorder_request_set_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
96 {
97 	mrcp_header_fields_set(state_machine->properties,&message->header,message->pool);
98 	return recorder_request_dispatch(state_machine,message);
99 }
100 
recorder_response_set_params(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)101 static apt_bool_t recorder_response_set_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
102 {
103 	return recorder_response_dispatch(state_machine,message);
104 }
105 
recorder_request_get_params(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)106 static apt_bool_t recorder_request_get_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
107 {
108 	return recorder_request_dispatch(state_machine,message);
109 }
110 
recorder_response_get_params(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)111 static apt_bool_t recorder_response_get_params(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
112 {
113 	mrcp_header_fields_get(&message->header,state_machine->properties,&state_machine->active_request->header,message->pool);
114 	return recorder_response_dispatch(state_machine,message);
115 }
116 
recorder_request_record(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)117 static apt_bool_t recorder_request_record(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
118 {
119 	mrcp_header_fields_inherit(&message->header,state_machine->properties,message->pool);
120 	if(state_machine->state == RECORDER_STATE_RECORDING) {
121 		mrcp_message_t *response;
122 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Reject RECORD Request "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
123 			MRCP_MESSAGE_SIDRES(message),
124 			message->start_line.request_id);
125 
126 		/* there is in-progress request, reject this one */
127 		response = mrcp_response_create(message,message->pool);
128 		response->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
129 		return recorder_response_dispatch(state_machine,response);
130 	}
131 
132 	return recorder_request_dispatch(state_machine,message);
133 }
134 
recorder_response_record(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)135 static apt_bool_t recorder_response_record(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
136 {
137 	if(message->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS) {
138 		state_machine->record = state_machine->active_request;
139 		recorder_state_change(state_machine,RECORDER_STATE_RECORDING,message);
140 	}
141 	return recorder_response_dispatch(state_machine,message);
142 }
143 
recorder_request_stop(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)144 static apt_bool_t recorder_request_stop(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
145 {
146 	mrcp_message_t *response;
147 	if(state_machine->state == RECORDER_STATE_RECORDING) {
148 		/* found in-progress RECORDER request, stop it */
149 		return recorder_request_dispatch(state_machine,message);
150 	}
151 
152 	/* found no in-progress RECORDER request, sending immediate response */
153 	response = mrcp_response_create(message,message->pool);
154 	return recorder_response_dispatch(state_machine,response);
155 }
156 
recorder_response_stop(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)157 static apt_bool_t recorder_response_stop(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
158 {
159 	mrcp_generic_header_t *generic_header = mrcp_generic_header_prepare(message);
160 	/* append active id list */
161 	active_request_id_list_append(generic_header,state_machine->record->start_line.request_id);
162 	mrcp_generic_header_property_add(message,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);
163 	recorder_state_change(state_machine,RECORDER_STATE_IDLE,message);
164 	return recorder_response_dispatch(state_machine,message);
165 }
166 
recorder_request_start_timers(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)167 static apt_bool_t recorder_request_start_timers(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
168 {
169 	mrcp_message_t *response;
170 	if(state_machine->state == RECORDER_STATE_RECORDING) {
171 		/* found in-progress request */
172 		return recorder_request_dispatch(state_machine,message);
173 	}
174 
175 	/* found no in-progress request */
176 	response = mrcp_response_create(message,message->pool);
177 	response->start_line.status_code = MRCP_STATUS_CODE_METHOD_NOT_VALID;
178 	return recorder_response_dispatch(state_machine,response);
179 }
180 
recorder_response_start_timers(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)181 static apt_bool_t recorder_response_start_timers(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
182 {
183 	return recorder_response_dispatch(state_machine,message);
184 }
185 
recorder_event_start_of_input(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)186 static apt_bool_t recorder_event_start_of_input(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
187 {
188 	if(!state_machine->record) {
189 		/* unexpected event, no in-progress record request */
190 		return FALSE;
191 	}
192 
193 	if(state_machine->record->start_line.request_id != message->start_line.request_id) {
194 		/* unexpected event */
195 		return FALSE;
196 	}
197 
198 	message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
199 	return recorder_event_dispatch(state_machine,message);
200 }
201 
recorder_event_record_complete(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)202 static apt_bool_t recorder_event_record_complete(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
203 {
204 	if(!state_machine->record) {
205 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECORD-COMPLETE Event "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
206 			MRCP_MESSAGE_SIDRES(message),
207 			message->start_line.request_id);
208 		return FALSE;
209 	}
210 
211 	if(state_machine->record->start_line.request_id != message->start_line.request_id) {
212 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Unexpected RECORD-COMPLETE Event "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
213 			MRCP_MESSAGE_SIDRES(message),
214 			message->start_line.request_id);
215 		return FALSE;
216 	}
217 
218 	if(state_machine->active_request && state_machine->active_request->start_line.method_id == RECORDER_STOP) {
219 		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Ignore RECORD-COMPLETE Event "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]: waiting for STOP response",
220 			MRCP_MESSAGE_SIDRES(message),
221 			message->start_line.request_id);
222 		return FALSE;
223 	}
224 
225 	if(mrcp_resource_header_property_check(message,RECORDER_HEADER_COMPLETION_CAUSE) != TRUE) {
226 		mrcp_recorder_header_t *recorder_header = mrcp_resource_header_prepare(message);
227 		recorder_header->completion_cause = RECORDER_COMPLETION_CAUSE_SUCCESS_SILENCE;
228 		mrcp_resource_header_property_add(message,RECORDER_HEADER_COMPLETION_CAUSE);
229 	}
230 	recorder_state_change(state_machine,RECORDER_STATE_IDLE,message);
231 	return recorder_event_dispatch(state_machine,message);
232 }
233 
234 static recorder_method_f recorder_request_method_array[RECORDER_METHOD_COUNT] = {
235 	recorder_request_set_params,
236 	recorder_request_get_params,
237 	recorder_request_record,
238 	recorder_request_stop,
239 	recorder_request_start_timers
240 };
241 
242 static recorder_method_f recorder_response_method_array[RECORDER_METHOD_COUNT] = {
243 	recorder_response_set_params,
244 	recorder_response_get_params,
245 	recorder_response_record,
246 	recorder_response_stop,
247 	recorder_response_start_timers
248 };
249 
250 static recorder_method_f recorder_event_method_array[RECORDER_EVENT_COUNT] = {
251 	recorder_event_start_of_input,
252 	recorder_event_record_complete
253 };
254 
255 /** Update state according to received incoming request from MRCP client */
recorder_request_state_update(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)256 static apt_bool_t recorder_request_state_update(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
257 {
258 	recorder_method_f method;
259 	if(message->start_line.method_id >= RECORDER_METHOD_COUNT) {
260 		return FALSE;
261 	}
262 
263 	apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process %s Request "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
264 		message->start_line.method_name.buf,
265 		MRCP_MESSAGE_SIDRES(message),
266 		message->start_line.request_id);
267 	method = recorder_request_method_array[message->start_line.method_id];
268 	if(method) {
269 		return method(state_machine,message);
270 	}
271 	return recorder_request_dispatch(state_machine,message);
272 }
273 
274 /** Update state according to received outgoing response from recorder engine */
recorder_response_state_update(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)275 static apt_bool_t recorder_response_state_update(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
276 {
277 	recorder_method_f method;
278 	if(!state_machine->active_request) {
279 		/* unexpected response, no active request waiting for response */
280 		return FALSE;
281 	}
282 	if(state_machine->active_request->start_line.request_id != message->start_line.request_id) {
283 		/* unexpected response, request id doesn't match */
284 		return FALSE;
285 	}
286 
287 	if(message->start_line.method_id >= RECORDER_METHOD_COUNT) {
288 		return FALSE;
289 	}
290 
291 	apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process %s Response "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
292 		message->start_line.method_name.buf,
293 		MRCP_MESSAGE_SIDRES(message),
294 		message->start_line.request_id);
295 	method = recorder_response_method_array[message->start_line.method_id];
296 	if(method) {
297 		return method(state_machine,message);
298 	}
299 	return recorder_response_dispatch(state_machine,message);
300 }
301 
302 /** Update state according to received outgoing event from recorder engine */
recorder_event_state_update(mrcp_recorder_state_machine_t * state_machine,mrcp_message_t * message)303 static apt_bool_t recorder_event_state_update(mrcp_recorder_state_machine_t *state_machine, mrcp_message_t *message)
304 {
305 	recorder_method_f method;
306 	if(message->start_line.method_id >= RECORDER_EVENT_COUNT) {
307 		return FALSE;
308 	}
309 
310 	apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Process %s Event "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
311 		message->start_line.method_name.buf,
312 		MRCP_MESSAGE_SIDRES(message),
313 		message->start_line.request_id);
314 	method = recorder_event_method_array[message->start_line.method_id];
315 	if(method) {
316 		return method(state_machine,message);
317 	}
318 	return recorder_event_dispatch(state_machine,message);
319 }
320 
321 /** Update state according to request received from MRCP client or response/event received from recorder engine */
recorder_state_update(mrcp_state_machine_t * base,mrcp_message_t * message)322 static apt_bool_t recorder_state_update(mrcp_state_machine_t *base, mrcp_message_t *message)
323 {
324 	mrcp_recorder_state_machine_t *state_machine = (mrcp_recorder_state_machine_t*)base;
325 	apt_bool_t status = TRUE;
326 	switch(message->start_line.message_type) {
327 		case MRCP_MESSAGE_TYPE_REQUEST:
328 			status = recorder_request_state_update(state_machine,message);
329 			break;
330 		case MRCP_MESSAGE_TYPE_RESPONSE:
331 			status = recorder_response_state_update(state_machine,message);
332 			break;
333 		case MRCP_MESSAGE_TYPE_EVENT:
334 			status = recorder_event_state_update(state_machine,message);
335 			break;
336 		default:
337 			status = FALSE;
338 			break;
339 	}
340 	return status;
341 }
342 
343 /** Deactivate state machine */
recorder_state_deactivate(mrcp_state_machine_t * base)344 static apt_bool_t recorder_state_deactivate(mrcp_state_machine_t *base)
345 {
346 	mrcp_recorder_state_machine_t *state_machine = (mrcp_recorder_state_machine_t*)base;
347 	mrcp_message_t *message;
348 	mrcp_message_t *source;
349 	if(state_machine->state != RECORDER_STATE_RECORDING) {
350 		/* no in-progress RECORD request to deactivate */
351 		return FALSE;
352 	}
353 	source = state_machine->record;
354 	if(!source) {
355 		return FALSE;
356 	}
357 
358 	/* create internal STOP request */
359 	message = mrcp_request_create(
360 						source->resource,
361 						source->start_line.version,
362 						RECORDER_STOP,
363 						source->pool);
364 	message->channel_id = source->channel_id;
365 	message->start_line.request_id = source->start_line.request_id + 1;
366 	apt_string_set(&message->start_line.method_name,"DEACTIVATE"); /* informative only */
367 	message->header = source->header;
368 	apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Create and Process STOP Request "APT_SIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
369 		MRCP_MESSAGE_SIDRES(message),
370 		message->start_line.request_id);
371 	return recorder_request_dispatch(state_machine,message);
372 }
373 
374 /** Create MRCP recorder state machine */
mrcp_recorder_state_machine_create(void * obj,mrcp_version_e version,apr_pool_t * pool)375 mrcp_state_machine_t* mrcp_recorder_state_machine_create(void *obj, mrcp_version_e version, apr_pool_t *pool)
376 {
377 	mrcp_recorder_state_machine_t *state_machine = apr_palloc(pool,sizeof(mrcp_recorder_state_machine_t));
378 	mrcp_state_machine_init(&state_machine->base,obj);
379 	state_machine->base.update = recorder_state_update;
380 	state_machine->base.deactivate = recorder_state_deactivate;
381 	state_machine->state = RECORDER_STATE_IDLE;
382 	state_machine->active_request = NULL;
383 	state_machine->record = NULL;
384 	state_machine->properties = mrcp_message_header_create(
385 			mrcp_generic_header_vtable_get(version),
386 			mrcp_recorder_header_vtable_get(version),
387 			pool);
388 	return &state_machine->base;
389 }
390