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