1 /*
2  * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2013-2018, Grasshopper
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is Grasshopper
20  * Portions created by the Initial Developer are Copyright (C)
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  * Chris Rienzo <chris.rienzo@grasshopper.com>
25  *
26  * output_component.c -- Rayo output component implementation
27  *
28  */
29 #include "rayo_components.h"
30 #include "rayo_elements.h"
31 
32 /**
33  * An output component
34  */
35 struct output_component {
36 	/** component base class */
37 	struct rayo_component base;
38 	/** document to play */
39 	iks *document;
40 	/** where to start playing in document */
41 	int start_offset_ms;
42 	/** maximum time to play */
43 	int max_time_ms;
44 	/** silence between repeats */
45 	int repeat_interval_ms;
46 	/** number of times to repeat */
47 	int repeat_times;
48 	/** true if started paused */
49 	switch_bool_t start_paused;
50 	/** true if stopped */
51 	int stop;
52 	/** output renderer to use */
53 	const char *renderer;
54 	/** optional headers to pass to renderer */
55 	const char *headers;
56 	/** audio direction */
57 	const char *direction;
58 };
59 
60 #define OUTPUT_FINISH "finish", RAYO_OUTPUT_COMPLETE_NS
61 #define OUTPUT_MAX_TIME "max-time", RAYO_OUTPUT_COMPLETE_NS
62 
63 #define OUTPUT_COMPONENT(x) ((struct output_component *)x)
64 
65 /**
66  * Create new output component
67  */
create_output_component(struct rayo_actor * actor,const char * type,iks * output,const char * client_jid)68 static struct rayo_component *create_output_component(struct rayo_actor *actor, const char *type, iks *output, const char *client_jid)
69 {
70 	switch_memory_pool_t *pool;
71 	struct output_component *output_component = NULL;
72 
73 	switch_core_new_memory_pool(&pool);
74 	output_component = switch_core_alloc(pool, sizeof(*output_component));
75 	output_component = OUTPUT_COMPONENT(rayo_component_init((struct rayo_component *)output_component, pool, type, "output", NULL, actor, client_jid));
76 	if (output_component) {
77 		output_component->document = iks_copy(output);
78 		output_component->start_offset_ms = iks_find_int_attrib(output, "start-offset");
79 		output_component->repeat_interval_ms = iks_find_int_attrib(output, "repeat-interval");
80 		output_component->repeat_times = iks_find_int_attrib(output, "repeat-times");
81 		output_component->max_time_ms = iks_find_int_attrib(output, "max-time");
82 		output_component->start_paused = iks_find_bool_attrib(output, "start-paused");
83 		output_component->renderer = switch_core_strdup(RAYO_POOL(output_component), iks_find_attrib_soft(output, "renderer"));
84 		output_component->direction = strcmp(iks_find_attrib_soft(output, "direction"), "in") ? "m" : "mr";
85 		output_component->headers = NULL;
86 		/* get custom headers */
87 		{
88 			switch_stream_handle_t headers = { 0 };
89 			iks *header = NULL;
90 			int first = 1;
91 			SWITCH_STANDARD_STREAM(headers);
92 			for (header = iks_find(output, "header"); header; header = iks_next_tag(header)) {
93 				if (!strcmp("header", iks_name(header))) {
94 					const char *name = iks_find_attrib_soft(header, "name");
95 					const char *value = iks_find_attrib_soft(header, "value");
96 					if (!zstr(name) && !zstr(value)) {
97 						headers.write_function(&headers, "%s%s=%s", first ? "{" : ",", name, value);
98 						first = 0;
99 					}
100 				}
101 			}
102 			if (headers.data && !first) {
103 				headers.write_function(&headers, "}");
104 				output_component->headers = switch_core_strdup(RAYO_POOL(output_component), (char *)headers.data);
105 			}
106 			switch_safe_free(headers.data);
107 		}
108 	} else {
109 		switch_core_destroy_memory_pool(&pool);
110 	}
111 
112 	return RAYO_COMPONENT(output_component);
113 }
114 
115 /**
116  * Start execution of call output component
117  * @param component to start
118  * @param session the session to output to
119  * @param output the output request
120  * @param iq the original request
121  */
start_call_output(struct rayo_component * component,switch_core_session_t * session,iks * output,iks * iq)122 static iks *start_call_output(struct rayo_component *component, switch_core_session_t *session, iks *output, iks *iq)
123 {
124 	switch_stream_handle_t stream = { 0 };
125 
126 	/* acknowledge command */
127 	rayo_component_send_start(component, iq);
128 
129 	/* build playback command */
130 	SWITCH_STANDARD_STREAM(stream);
131 	stream.write_function(&stream, "{id=%s,session=%s,pause=%s",
132 		RAYO_JID(component), switch_core_session_get_uuid(session),
133 		OUTPUT_COMPONENT(component)->start_paused ? "true" : "false");
134 	if (OUTPUT_COMPONENT(component)->max_time_ms > 0) {
135 		stream.write_function(&stream, ",timeout=%i", OUTPUT_COMPONENT(component)->max_time_ms);
136 	}
137 	if (OUTPUT_COMPONENT(component)->start_offset_ms > 0) {
138 		stream.write_function(&stream, ",start_offset_ms=%i", OUTPUT_COMPONENT(component)->start_offset_ms);
139 	}
140 	stream.write_function(&stream, "}fileman://rayo://%s", RAYO_JID(component));
141 
142 	if (switch_ivr_displace_session(session, stream.data, 0, OUTPUT_COMPONENT(component)->direction) == SWITCH_STATUS_SUCCESS) {
143 		RAYO_RELEASE(component);
144 	} else {
145 		if (component->complete) {
146 			/* component is already destroyed */
147 			RAYO_RELEASE(component);
148 		} else {
149 			/* need to destroy component */
150 			if (OUTPUT_COMPONENT(component)->document) {
151 				iks_delete(OUTPUT_COMPONENT(component)->document);
152 			}
153 			if (switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
154 				rayo_component_send_complete(component, COMPONENT_COMPLETE_HANGUP);
155 			} else {
156 				rayo_component_send_complete(component, COMPONENT_COMPLETE_ERROR);
157 			}
158 		}
159 	}
160 	switch_safe_free(stream.data);
161 	return NULL;
162 }
163 
164 /**
165  * Start execution of call output component
166  */
start_call_output_component(struct rayo_actor * call,struct rayo_message * msg,void * session_data)167 static iks *start_call_output_component(struct rayo_actor *call, struct rayo_message *msg, void *session_data)
168 {
169 	iks *iq = msg->payload;
170 	switch_core_session_t *session = (switch_core_session_t *)session_data;
171 	struct rayo_component *output_component = NULL;
172 	iks *output = iks_find(iq, "output");
173 	iks *document = NULL;
174 
175 	/* validate output attributes */
176 	if (!VALIDATE_RAYO_OUTPUT(output)) {
177 		return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
178 	}
179 
180 	/* check if <document> exists */
181 	document = iks_find(output, "document");
182 	if (!document) {
183 		return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
184 	}
185 
186 	output_component = create_output_component(call, RAT_CALL_COMPONENT, output, iks_find_attrib(iq, "from"));
187 	if (!output_component) {
188 		return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to create output entity");
189 	}
190 	return start_call_output(output_component, session, output, iq);
191 }
192 
193 /**
194  * Start execution of mixer output component
195  */
start_mixer_output_component(struct rayo_actor * mixer,struct rayo_message * msg,void * data)196 static iks *start_mixer_output_component(struct rayo_actor *mixer, struct rayo_message *msg, void *data)
197 {
198 	iks *iq = msg->payload;
199 	struct rayo_component *component = NULL;
200 	iks *output = iks_find(iq, "output");
201 	iks *document = NULL;
202 	switch_stream_handle_t stream = { 0 };
203 
204 	/* validate output attributes */
205 	if (!VALIDATE_RAYO_OUTPUT(output)) {
206 		return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
207 	}
208 
209 	/* check if <document> exists */
210 	document = iks_find(output, "document");
211 	if (!document) {
212 		return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
213 	}
214 
215 	component = create_output_component(mixer, RAT_MIXER_COMPONENT, output, iks_find_attrib(iq, "from"));
216 	if (!component) {
217 		return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to create output entity");
218 	}
219 
220 	/* build conference command */
221 	SWITCH_STANDARD_STREAM(stream);
222 	stream.write_function(&stream, "%s play ", rayo_mixer_get_name(RAYO_MIXER(mixer)), RAYO_ID(component));
223 
224 	stream.write_function(&stream, "{id=%s,pause=%s",
225 		RAYO_JID(component),
226 		OUTPUT_COMPONENT(component)->start_paused ? "true" : "false");
227 	if (OUTPUT_COMPONENT(component)->max_time_ms > 0) {
228 		stream.write_function(&stream, ",timeout=%i", OUTPUT_COMPONENT(component)->max_time_ms);
229 	}
230 	if (OUTPUT_COMPONENT(component)->start_offset_ms > 0) {
231 		stream.write_function(&stream, ",start_offset_ms=%i", OUTPUT_COMPONENT(component)->start_offset_ms);
232 	}
233 	stream.write_function(&stream, "}fileman://rayo://%s", RAYO_JID(component));
234 
235 	/* acknowledge command */
236 	rayo_component_send_start(component, iq);
237 
238 	rayo_component_api_execute_async(component, "conference", stream.data);
239 
240 	switch_safe_free(stream.data);
241 	RAYO_RELEASE(component);
242 
243 	return NULL;
244 }
245 
246 /**
247  * Stop execution of output component
248  */
stop_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)249 static iks *stop_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
250 {
251 	iks *iq = msg->payload;
252 	iks *result = NULL;
253 	switch_core_session_t *session = NULL;
254 	switch_stream_handle_t stream = { 0 };
255 	char *command = switch_mprintf("%s stop", RAYO_JID(component));
256 	SWITCH_STANDARD_STREAM(stream);
257 	OUTPUT_COMPONENT(component)->stop = 1;
258 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
259 		session = (switch_core_session_t *)data;
260 	}
261 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s stopping\n", RAYO_JID(component));
262 	switch_api_execute("fileman", command, NULL, &stream);
263 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
264 		result = iks_new_iq_result(iq);
265 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
266 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
267 	} else if (!zstr((char *)stream.data)) {
268 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
269 	} else {
270 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
271 	}
272 	switch_safe_free(stream.data);
273 	switch_safe_free(command);
274 	return result;
275 }
276 
277 /**
278  * Pause execution of output component
279  */
pause_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)280 static iks *pause_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
281 {
282 	iks *iq = msg->payload;
283 	iks *result = NULL;
284 	switch_core_session_t *session = NULL;
285 	switch_stream_handle_t stream = { 0 };
286 	char *command = switch_mprintf("%s pause", RAYO_JID(component));
287 	SWITCH_STANDARD_STREAM(stream);
288 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
289 		session = (switch_core_session_t *)data;
290 	}
291 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s pausing\n", RAYO_JID(component));
292 	switch_api_execute("fileman", command, NULL, &stream);
293 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
294 		result = iks_new_iq_result(iq);
295 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
296 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
297 	} else if (!zstr((char *)stream.data)) {
298 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
299 	} else {
300 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
301 	}
302 	switch_safe_free(stream.data);
303 	switch_safe_free(command);
304 	return result;
305 }
306 
307 /**
308  * Resume execution of output component
309  */
resume_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)310 static iks *resume_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
311 {
312 	iks *iq = msg->payload;
313 	iks *result = NULL;
314 	switch_core_session_t *session = NULL;
315 	switch_stream_handle_t stream = { 0 };
316 	char *command = switch_mprintf("%s resume", RAYO_JID(component));
317 	SWITCH_STANDARD_STREAM(stream);
318 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
319 		session = (switch_core_session_t *)data;
320 	}
321 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s resuming\n", RAYO_JID(component));
322 	switch_api_execute("fileman", command, NULL, &stream);
323 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
324 		result = iks_new_iq_result(iq);
325 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
326 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
327 	} else if (!zstr((char *)stream.data)) {
328 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
329 	} else {
330 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
331 	}
332 	switch_safe_free(stream.data);
333 	switch_safe_free(command);
334 	return result;
335 }
336 
337 /**
338  * Speed up execution of output component
339  */
speed_up_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)340 static iks *speed_up_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
341 {
342 	iks *iq = msg->payload;
343 	iks *result = NULL;
344 	switch_core_session_t *session = NULL;
345 	switch_stream_handle_t stream = { 0 };
346 	char *command = switch_mprintf("%s speed:+", RAYO_JID(component));
347 	SWITCH_STANDARD_STREAM(stream);
348 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
349 		session = (switch_core_session_t *)data;
350 	}
351 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s speeding up\n", RAYO_JID(component));
352 	switch_api_execute("fileman", command, NULL, &stream);
353 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
354 		result = iks_new_iq_result(iq);
355 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
356 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
357 	} else if (!zstr((char *)stream.data)) {
358 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
359 	} else {
360 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
361 	}
362 	switch_safe_free(stream.data);
363 	switch_safe_free(command);
364 	return result;
365 }
366 
367 /**
368  * Slow down execution of output component
369  */
speed_down_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)370 static iks *speed_down_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
371 {
372 	iks *iq = msg->payload;
373 	iks *result = NULL;
374 	switch_core_session_t *session = NULL;
375 	switch_stream_handle_t stream = { 0 };
376 	char *command = switch_mprintf("%s speed:-", RAYO_JID(component));
377 	SWITCH_STANDARD_STREAM(stream);
378 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
379 		session = (switch_core_session_t *)data;
380 	}
381 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s slowing down\n", RAYO_JID(component));
382 	switch_api_execute("fileman", command, NULL, &stream);
383 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
384 		result = iks_new_iq_result(iq);
385 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
386 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
387 	} else if (!zstr((char *)stream.data)) {
388 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
389 	} else {
390 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
391 	}
392 	switch_safe_free(stream.data);
393 	switch_safe_free(command);
394 	return result;
395 }
396 
397 /**
398  * Increase volume of output component
399  */
volume_up_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)400 static iks *volume_up_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
401 {
402 	iks *iq = msg->payload;
403 	iks *result = NULL;
404 	switch_core_session_t *session = NULL;
405 	switch_stream_handle_t stream = { 0 };
406 	char *command = switch_mprintf("%s volume:+", RAYO_JID(component));
407 	SWITCH_STANDARD_STREAM(stream);
408 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
409 		session = (switch_core_session_t *)data;
410 	}
411 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s increasing volume\n", RAYO_JID(component));
412 	switch_api_execute("fileman", command, NULL, &stream);
413 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
414 		result = iks_new_iq_result(iq);
415 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
416 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
417 	} else if (!zstr((char *)stream.data)) {
418 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
419 	} else {
420 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
421 	}
422 	switch_safe_free(stream.data);
423 	switch_safe_free(command);
424 	return result;
425 }
426 
427 /**
428  * Lower volume of output component
429  */
volume_down_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)430 static iks *volume_down_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
431 {
432 	iks *iq = msg->payload;
433 	iks *result = NULL;
434 	switch_core_session_t *session = NULL;
435 	switch_stream_handle_t stream = { 0 };
436 	char *command = switch_mprintf("%s volume:-", RAYO_JID(component));
437 	SWITCH_STANDARD_STREAM(stream);
438 	if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
439 		session = (switch_core_session_t *)data;
440 	}
441 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s lowering volume\n", RAYO_JID(component));
442 	switch_api_execute("fileman", command, NULL, &stream);
443 	if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
444 		result = iks_new_iq_result(iq);
445 	} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
446 		result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
447 	} else if (!zstr((char *)stream.data)) {
448 		result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
449 	} else {
450 		result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
451 	}
452 	switch_safe_free(stream.data);
453 	switch_safe_free(command);
454 	return result;
455 }
456 
457 /**
458  * Seek output component
459  */
seek_output_component(struct rayo_actor * component,struct rayo_message * msg,void * data)460 static iks *seek_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
461 {
462 	iks *iq = msg->payload;
463 	iks *seek = iks_find(iq, "seek");
464 
465 	if (VALIDATE_RAYO_OUTPUT_SEEK(seek)) {
466 		iks *result = NULL;
467 		switch_core_session_t *session = NULL;
468 		int is_forward = !strcmp("forward", iks_find_attrib(seek, "direction"));
469 		int amount_ms = iks_find_int_attrib(seek, "amount");
470 		char *command = switch_mprintf("%s seek:%s%i", RAYO_JID(component),
471 			is_forward ? "+" : "-", amount_ms);
472 		switch_stream_handle_t stream = { 0 };
473 		SWITCH_STANDARD_STREAM(stream);
474 		if (!strcmp(RAYO_ACTOR(component)->type, RAT_CALL_COMPONENT)) {
475 			session = (switch_core_session_t *)data;
476 		}
477 
478 		switch_api_execute("fileman", command, NULL, &stream);
479 		if (!zstr((char *)stream.data) && !strncmp((char *)stream.data, "+OK", 3)) {
480 			result = iks_new_iq_result(iq);
481 		} else if (session && switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
482 			result = iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "call has ended");
483 		} else if (!zstr((char *)stream.data)) {
484 			result = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", (char *)stream.data);
485 		} else {
486 			result = iks_new_error(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR);
487 		}
488 		switch_safe_free(stream.data);
489 		switch_safe_free(command);
490 
491 		return result;
492 	}
493 	return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
494 }
495 
496 /**
497  * Rayo document playback state
498  */
499 struct rayo_file_context {
500 	/** handle to current file */
501 	switch_file_handle_t fh;
502 	/** current document being played */
503 	iks *cur_doc;
504 	/** current file string being played */
505 	char *ssml;
506 	/** The component */
507 	struct rayo_component *component;
508 	/** number of times played */
509 	int play_count;
510 	/** have any files successfully opened? */
511 	int could_open;
512 };
513 
514 /**
515  * open next file for reading
516  * @param handle the file handle
517  */
next_file(switch_file_handle_t * handle)518 static switch_status_t next_file(switch_file_handle_t *handle)
519 {
520 	int loops = 0;
521 	struct rayo_file_context *context = handle->private_info;
522 	struct output_component *output = context->component ? OUTPUT_COMPONENT(context->component) : NULL;
523 
524 	if (!output) {
525 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing output component!\n");
526 		return SWITCH_STATUS_FALSE;
527 	}
528 
529   top:
530 
531 	if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
532 		switch_core_file_close(&context->fh);
533 	}
534 
535 	if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
536 		/* unsupported */
537 		return SWITCH_STATUS_FALSE;
538 	}
539 
540 	if (!context->cur_doc) {
541 		context->cur_doc = iks_find(output->document, "document");
542 		if (!context->cur_doc) {
543 			iks_delete(output->document);
544 			output->document = NULL;
545 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing <document>\n");
546 			return SWITCH_STATUS_FALSE;
547 		}
548 	} else {
549 		context->cur_doc = iks_next_tag(context->cur_doc);
550 	}
551 
552 	/* done? */
553 	if (!context->cur_doc) {
554 		if (context->could_open && ++loops < 2 && (output->repeat_times == 0 || ++context->play_count < output->repeat_times)) {
555 			/* repeat all document(s) */
556 			if (!output->repeat_interval_ms) {
557 				goto top;
558 			}
559 		} else {
560 			/* no more files to play */
561 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Done playing\n");
562 			return SWITCH_STATUS_FALSE;
563 		}
564 	}
565 
566 	if (!context->cur_doc) {
567 		/* play silence between repeats */
568 		switch_safe_free(context->ssml);
569 		context->ssml = switch_mprintf("silence_stream://%i", output->repeat_interval_ms);
570 	} else {
571 		/* play next document */
572 		iks *speak = NULL;
573 
574 		switch_safe_free(context->ssml);
575 		context->ssml = NULL;
576  		speak = iks_find(context->cur_doc, "speak");
577 		if (speak) {
578 			/* <speak> is child node */
579 			char *ssml_str = iks_string(NULL, speak);
580 			if (zstr(output->renderer)) {
581 				/* FS must parse the SSML */
582 				context->ssml = switch_mprintf("ssml://%s", ssml_str);
583 			} else {
584 				/* renderer will parse the SSML */
585 				if (!zstr(output->headers) && !strncmp("unimrcp", output->renderer, 7)) {
586 					/* pass MRCP headers */
587 					context->ssml = switch_mprintf("tts://%s||%s%s", output->renderer, output->headers, ssml_str);
588 				} else {
589 					context->ssml = switch_mprintf("tts://%s||%s", output->renderer, ssml_str);
590 				}
591 			}
592 			iks_free(ssml_str);
593 		} else if (iks_has_children(context->cur_doc)) {
594 			/* check if <speak> is in CDATA */
595 			const char *ssml_str = NULL;
596 			iks *ssml = iks_child(context->cur_doc);
597 			if (ssml && iks_type(ssml) == IKS_CDATA) {
598 				ssml_str = iks_cdata(ssml);
599 			}
600 			if (zstr(ssml_str)) {
601 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing <document> CDATA\n");
602 				return SWITCH_STATUS_FALSE;
603 			}
604 			if (zstr(output->renderer)) {
605 				/* FS must parse the SSML */
606 				context->ssml = switch_mprintf("ssml://%s", ssml_str);
607 			} else {
608 				/* renderer will parse the SSML */
609 				if (!zstr(output->headers) && !strncmp("unimrcp", output->renderer, 7)) {
610 					/* pass MRCP headers */
611 					context->ssml = switch_mprintf("tts://%s||%s%s", output->renderer, output->headers, ssml_str);
612 				} else {
613 					context->ssml = switch_mprintf("tts://%s||%s", output->renderer, ssml_str);
614 				}
615 			}
616 		} else {
617 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing <speak>\n");
618 			return SWITCH_STATUS_FALSE;
619 		}
620 	}
621 	if (switch_core_file_open(&context->fh, context->ssml, handle->channels, handle->samplerate, handle->flags, NULL) != SWITCH_STATUS_SUCCESS) {
622 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to open %s\n", context->ssml);
623 		goto top;
624 	} else {
625 		context->could_open = 1;
626 	}
627 
628 	handle->samples = context->fh.samples;
629 	handle->format = context->fh.format;
630 	handle->sections = context->fh.sections;
631 	handle->seekable = context->fh.seekable;
632 	handle->speed = context->fh.speed;
633 	handle->vol = context->fh.vol;
634 	handle->offset_pos = context->fh.offset_pos;
635 	handle->interval = context->fh.interval;
636 
637 	if (switch_test_flag((&context->fh), SWITCH_FILE_NATIVE)) {
638 		switch_set_flag_locked(handle, SWITCH_FILE_NATIVE);
639 	} else {
640 		switch_clear_flag_locked(handle, SWITCH_FILE_NATIVE);
641 	}
642 
643 	return SWITCH_STATUS_SUCCESS;
644 }
645 
646 /**
647  * Transforms Rayo document into sub-format and opens file_string.
648  * @param handle
649  * @param path the inline Rayo document
650  * @return SWITCH_STATUS_SUCCESS if opened
651  */
rayo_file_open(switch_file_handle_t * handle,const char * path)652 static switch_status_t rayo_file_open(switch_file_handle_t *handle, const char *path)
653 {
654 	switch_status_t status = SWITCH_STATUS_FALSE;
655 	struct rayo_file_context *context = switch_core_alloc(handle->memory_pool, sizeof(*context));
656 
657 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got path %s\n", path);
658 
659 	context->component = RAYO_COMPONENT_LOCATE(path);
660 
661 	if (context->component) {
662 		handle->private_info = context;
663 		context->cur_doc = NULL;
664 		context->play_count = 0;
665 		context->could_open = 0;
666 		status = next_file(handle);
667 	} else {
668 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "File error! %s\n", path);
669 		return SWITCH_STATUS_FALSE;
670 	}
671 
672 	if (status != SWITCH_STATUS_SUCCESS && context->component) {
673 		/* complete error event will be sent by calling thread */
674 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Status = %i\n", status);
675 		RAYO_RELEASE(context->component);
676 	}
677 
678 	return status;
679 }
680 
681 /**
682  * Close SSML document.
683  * @param handle
684  * @return SWITCH_STATUS_SUCCESS
685  */
rayo_file_close(switch_file_handle_t * handle)686 static switch_status_t rayo_file_close(switch_file_handle_t *handle)
687 {
688 	struct rayo_file_context *context = (struct rayo_file_context *)handle->private_info;
689 
690 	if (context && context->component) {
691 		struct output_component *output = OUTPUT_COMPONENT(context->component);
692 
693 		/* send completion and destroy */
694 		if (!strcmp(RAYO_ACTOR(context->component)->type, RAT_CALL_COMPONENT)) {
695 			/* call output... check for hangup */
696 			switch_core_session_t *session = switch_core_session_locate(RAYO_ACTOR(context->component)->parent->id);
697 			if (session) {
698 				if (switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) {
699 					rayo_component_send_complete(context->component, COMPONENT_COMPLETE_HANGUP);
700 				} else if (output->stop) {
701 					rayo_component_send_complete(context->component, COMPONENT_COMPLETE_STOP);
702 				} else {
703 					rayo_component_send_complete(context->component, OUTPUT_FINISH);
704 				}
705 				switch_core_session_rwunlock(session);
706 			} else {
707 				/* session is gone */
708 				rayo_component_send_complete(context->component, COMPONENT_COMPLETE_HANGUP);
709 			}
710 		} else if (output->stop) {
711 			rayo_component_send_complete(context->component, COMPONENT_COMPLETE_STOP);
712 		} else {
713 			/* mixer output... finished */
714 			rayo_component_send_complete(context->component, OUTPUT_FINISH);
715 		}
716 		/* TODO timed out */
717 
718 		/* cleanup internals */
719 		switch_safe_free(context->ssml);
720 		context->ssml = NULL;
721 		if (output->document) {
722 			iks_delete(output->document);
723 			output->document = NULL;
724 		}
725 
726 		/* close SSML file */
727 		if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
728 			return switch_core_file_close(&context->fh);
729 		}
730 	}
731 
732 	return SWITCH_STATUS_SUCCESS;
733 }
734 
735 /**
736  * Read from SSML document
737  * @param handle
738  * @param data
739  * @param len
740  * @return
741  */
rayo_file_read(switch_file_handle_t * handle,void * data,size_t * len)742 static switch_status_t rayo_file_read(switch_file_handle_t *handle, void *data, size_t *len)
743 {
744 	switch_status_t status;
745 	struct rayo_file_context *context = (struct rayo_file_context *)handle->private_info;
746 	size_t llen = *len;
747 
748 	if (OUTPUT_COMPONENT(context->component)->stop) {
749 		return SWITCH_STATUS_FALSE;
750 	} else {
751 		status = switch_core_file_read(&context->fh, data, len);
752 		if (status != SWITCH_STATUS_SUCCESS) {
753 			if ((status = next_file(handle)) != SWITCH_STATUS_SUCCESS) {
754 				return status;
755 			}
756 			*len = llen;
757 			status = switch_core_file_read(&context->fh, data, len);
758 		}
759 	}
760 
761 	return status;
762 }
763 
764 /**
765  * Seek file
766  */
rayo_file_seek(switch_file_handle_t * handle,unsigned int * cur_sample,int64_t samples,int whence)767 static switch_status_t rayo_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
768 {
769 	struct rayo_file_context *context = handle->private_info;
770 
771 	if (samples == 0 && whence == SWITCH_SEEK_SET) {
772 		/* restart from beginning */
773 		context->cur_doc = NULL;
774 		context->play_count = 0;
775 		return next_file(handle);
776 	}
777 
778 	if (!handle->seekable) {
779 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "File is not seekable\n");
780 		return SWITCH_STATUS_NOTIMPL;
781 	}
782 
783 	return switch_core_file_seek(&context->fh, cur_sample, samples, whence);
784 }
785 
786 /**
787  * Manages access to fileman controls
788  */
789 struct {
790 	/** synchronizes access to fileman hash */
791 	switch_mutex_t *mutex;
792 	/** fileman mapped by id */
793 	switch_hash_t *hash;
794 } fileman_globals;
795 
796 #define FILE_STARTBYTES 1024 * 32
797 #define FILE_BLOCKSIZE 1024 * 8
798 #define FILE_BUFSIZE 1024 * 64
799 
800 /**
801  * Fileman playback state
802  */
803 struct fileman_file_context {
804 	/** handle to current file */
805 	switch_file_handle_t fh;
806 	/** file buffer */
807 	int16_t *abuf;
808 	/** end of file */
809 	int eof;
810 	/** maximum size of a packet in 2-byte samples */
811 	switch_size_t max_frame_len;
812 	/** optional session UUID */
813 	const char *uuid;
814 	/** fileman control ID */
815 	const char *id;
816 	/** done flag */
817 	int done;
818 };
819 
820 /**
821  * Wraps file with interface that can be controlled by fileman flags
822  * @param handle
823  * @param path the file to play
824  * @return SWITCH_STATUS_SUCCESS if opened
825  */
fileman_file_open(switch_file_handle_t * handle,const char * path)826 static switch_status_t fileman_file_open(switch_file_handle_t *handle, const char *path)
827 {
828 	int start_offset_ms = 0;
829 	switch_status_t status = SWITCH_STATUS_FALSE;
830 	struct fileman_file_context *context = switch_core_alloc(handle->memory_pool, sizeof(*context));
831 	handle->private_info = context;
832 
833 	if (handle->params) {
834 		const char *id = switch_event_get_header(handle->params, "id");
835 		const char *uuid = switch_event_get_header(handle->params, "session");
836 		const char *start_offset_ms_str = switch_event_get_header(handle->params, "start_offset_ms");
837 		if (!zstr(id)) {
838 			context->id = switch_core_strdup(handle->memory_pool, id);
839 		}
840 		if (!zstr(uuid)) {
841 			context->uuid = switch_core_strdup(handle->memory_pool, uuid);
842 		}
843 		if (!zstr(start_offset_ms_str) && switch_is_number(start_offset_ms_str)) {
844 			start_offset_ms = atoi(start_offset_ms_str);
845 			if (start_offset_ms < 0) {
846 				start_offset_ms = 0;
847 			}
848 		}
849 	}
850 
851 	switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Got path %s\n", path);
852 
853 	if ((status = switch_core_file_open(&context->fh, path, handle->channels, handle->samplerate, handle->flags, NULL)) != SWITCH_STATUS_SUCCESS) {
854 		return status;
855 	}
856 
857 	/* set up handle for external control */
858 	if (!context->id) {
859 		/* use filename as ID */
860 		context->id = switch_core_strdup(handle->memory_pool, path);
861 	}
862 	switch_mutex_lock(fileman_globals.mutex);
863 	if (!switch_core_hash_find(fileman_globals.hash, context->id)) {
864 		switch_core_hash_insert(fileman_globals.hash, context->id, handle);
865 	} else {
866 		switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_WARNING, "Duplicate fileman ID: %s\n", context->id);
867 		return SWITCH_STATUS_FALSE;
868 	}
869 	switch_mutex_unlock(fileman_globals.mutex);
870 
871 	context->max_frame_len = (handle->samplerate / 1000 * SWITCH_MAX_INTERVAL);
872 	switch_zmalloc(context->abuf, FILE_STARTBYTES * sizeof(*context->abuf));
873 
874 	if (!context->fh.audio_buffer) {
875 		switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Create audio buffer\n");
876 		switch_buffer_create_dynamic(&context->fh.audio_buffer, FILE_BLOCKSIZE, FILE_BUFSIZE, 0);
877 		switch_assert(context->fh.audio_buffer);
878 	}
879 
880 	handle->samples = context->fh.samples;
881 	handle->format = context->fh.format;
882 	handle->sections = context->fh.sections;
883 	handle->seekable = context->fh.seekable;
884 	handle->speed = context->fh.speed;
885 	handle->vol = context->fh.vol;
886 	handle->offset_pos = context->fh.offset_pos;
887 	handle->interval = context->fh.interval;
888 
889 	if (switch_test_flag((&context->fh), SWITCH_FILE_NATIVE)) {
890 		switch_set_flag_locked(handle, SWITCH_FILE_NATIVE);
891 	} else {
892 		switch_clear_flag_locked(handle, SWITCH_FILE_NATIVE);
893 	}
894 
895 	if (handle->params && switch_true(switch_event_get_header(handle->params, "pause"))) {
896 		switch_set_flag_locked(handle, SWITCH_FILE_PAUSE);
897 	}
898 
899 	if (handle->seekable && start_offset_ms) {
900 		unsigned int pos = 0;
901 		int32_t target = start_offset_ms * (handle->samplerate / 1000);
902 		switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "seek to position %d\n", target);
903 		switch_core_file_seek(&context->fh, &pos, target, SEEK_SET);
904 	}
905 
906 	return status;
907 }
908 
909 /**
910  * Close file.
911  * @param handle
912  * @return SWITCH_STATUS_SUCCESS
913  */
fileman_file_close(switch_file_handle_t * handle)914 static switch_status_t fileman_file_close(switch_file_handle_t *handle)
915 {
916 	struct fileman_file_context *context = (struct fileman_file_context *)handle->private_info;
917 	switch_file_handle_t *fh = &context->fh;
918 
919 	if (context->id) {
920 		switch_mutex_lock(fileman_globals.mutex);
921 		switch_core_hash_delete(fileman_globals.hash, context->id);
922 		switch_mutex_unlock(fileman_globals.mutex);
923 	}
924 
925 	if (switch_test_flag(fh, SWITCH_FILE_OPEN)) {
926 		free(context->abuf);
927 
928 		if (fh->audio_buffer) {
929 			switch_buffer_destroy(&fh->audio_buffer);
930 		}
931 
932 		if (fh->sp_audio_buffer) {
933 			switch_buffer_destroy(&fh->sp_audio_buffer);
934 		}
935 		return switch_core_file_close(fh);
936 	}
937 	return SWITCH_STATUS_SUCCESS;
938 }
939 
940 /**
941  * Write to file
942  * @param handle
943  * @param data
944  * @param len
945  * @return
946  */
fileman_file_write(switch_file_handle_t * handle,void * data,size_t * len)947 static switch_status_t fileman_file_write(switch_file_handle_t *handle, void *data, size_t *len)
948 {
949 	struct fileman_file_context *context = (struct fileman_file_context *)handle->private_info;
950 	switch_file_handle_t *fh = &context->fh;
951 	if (!switch_test_flag(handle, SWITCH_FILE_PAUSE)) {
952 		return switch_core_file_write(fh, data, len);
953 	}
954 	return SWITCH_STATUS_SUCCESS;
955 }
956 
957 /**
958  * Read from file
959  * @param handle
960  * @param data
961  * @param len
962  * @return
963  */
fileman_file_read(switch_file_handle_t * handle,void * data,size_t * len)964 static switch_status_t fileman_file_read(switch_file_handle_t *handle, void *data, size_t *len)
965 {
966 	struct fileman_file_context *context = (struct fileman_file_context *)handle->private_info;
967 	switch_file_handle_t *fh = &context->fh;
968 	switch_status_t status = SWITCH_STATUS_SUCCESS;
969 	switch_size_t o_len = 0;
970 
971 	/* anything called "_len" is measured in 2-byte samples */
972 
973 	if (switch_test_flag(fh, SWITCH_FILE_NATIVE)) {
974 		return switch_core_file_read(fh, data, len);
975 	}
976 
977 	//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "len = %"SWITCH_SIZE_T_FMT"\n", *len);
978 	if (*len > context->max_frame_len) {
979 		*len = context->max_frame_len;
980 	}
981 
982 	for (;;) {
983 		int do_speed = 1;
984 		size_t read_bytes = 0;
985 
986 		if (context->done) {
987 			/* done with this file */
988 			status = SWITCH_STATUS_FALSE;
989 			goto done;
990 		} else if (switch_test_flag(handle, SWITCH_FILE_PAUSE)) {
991 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read pause frame\n");
992 			memset(context->abuf, 255, *len * 2);
993 			do_speed = 0;
994 			o_len = *len;
995 		} else if (fh->sp_audio_buffer && (context->eof || (switch_buffer_inuse(fh->sp_audio_buffer) > (switch_size_t) (*len * 2)))) {
996 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read speed frame\n");
997 			/* get next speed frame */
998 			if (!(read_bytes = switch_buffer_read(fh->sp_audio_buffer, context->abuf, *len * 2))) {
999 				/* This is the reverse of what happens in switch_ivr_play_file... i think that implementation is wrong */
1000 				if (context->eof) {
1001 					/* done with file */
1002 					status = SWITCH_STATUS_FALSE;
1003 					goto done;
1004 				} else {
1005 					/* try again to fetch frame */
1006 					continue;
1007 				}
1008 			}
1009 
1010 			/* pad short frame with silence */
1011 			if (read_bytes < *len * 2) {
1012 				//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Padding speed frame %"SWITCH_SIZE_T_FMT" bytes\n", (context->frame_len * 2) - read_bytes);
1013 				memset(context->abuf + read_bytes, 255, (*len * 2) - read_bytes);
1014 			}
1015 			o_len = *len;
1016 			do_speed = 0;
1017 		} else if (fh->audio_buffer && (context->eof || (switch_buffer_inuse(fh->audio_buffer) > (switch_size_t) (*len * 2)))) {
1018 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "(2) Read audio frame\n");
1019 			/* get next file frame */
1020 			if (!(read_bytes = switch_buffer_read(fh->audio_buffer, context->abuf, *len * 2))) {
1021 				if (context->eof) {
1022 					/* done with file */
1023 					status = SWITCH_STATUS_FALSE;
1024 					goto done;
1025 				} else {
1026 					/* try again to fetch frame */
1027 					continue;
1028 				}
1029 			}
1030 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "(2) Read audio frame %"SWITCH_SIZE_T_FMT" bytes\n", read_bytes);
1031 			fh->offset_pos += read_bytes / 2;
1032 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "(2) file pos = %i\n", fh->offset_pos);
1033 
1034 			/* pad short frame with silence */
1035 			if (read_bytes < (*len * 2)) {
1036 				//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Padding audio frame %"SWITCH_SIZE_T_FMT" bytes\n", (context->frame_len * 2) - read_bytes);
1037 				memset(context->abuf + read_bytes, 255, (*len * 2) - read_bytes);
1038 			}
1039 
1040 			o_len = *len;
1041 		} else {
1042 			if (context->eof) {
1043 				/* done with file */
1044 				status = SWITCH_STATUS_FALSE;
1045 				goto done;
1046 			}
1047 			o_len = FILE_STARTBYTES / 2;
1048 			if (switch_core_file_read(fh, context->abuf, &o_len) != SWITCH_STATUS_SUCCESS) {
1049 				context->eof++;
1050 				/* at end of file... need to clear buffers before giving up */
1051 				continue;
1052 			}
1053 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read file %"SWITCH_SIZE_T_FMT" bytes\n", o_len * 2);
1054 
1055 			/* add file data to audio bufer */
1056 			switch_buffer_write(fh->audio_buffer, context->abuf, o_len * 2);
1057 
1058 			read_bytes = switch_buffer_read(fh->audio_buffer, context->abuf, *len * 2);
1059 			o_len = read_bytes / 2;
1060 			fh->offset_pos += o_len;
1061 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Read audio frame %"SWITCH_SIZE_T_FMT" bytes\n", read_bytes);
1062 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "file pos = %i\n", fh->offset_pos);
1063 		}
1064 
1065 		if (o_len <= 0) {
1066 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "o_len <= 0 (%"SWITCH_SIZE_T_FMT")\n", o_len);
1067 			status = SWITCH_STATUS_FALSE;
1068 			goto done;
1069 		}
1070 
1071 		/* limit speed... there is a .25 factor change in packet size relative to original packet size for each increment.
1072 		   Too many increments and we cause badness when (factor * speed * o_len) > o_len */
1073 		if (handle->speed > 2) {
1074 			handle->speed = 2;
1075 		} else if (handle->speed < -2) {
1076 			handle->speed = -2;
1077 		}
1078 
1079 		if (switch_test_flag(fh, SWITCH_FILE_SEEK)) {
1080 			/* file position has changed flush the buffer */
1081 			switch_buffer_zero(fh->audio_buffer);
1082 			switch_clear_flag_locked(fh, SWITCH_FILE_SEEK);
1083 		}
1084 
1085 		/* generate speed frames */
1086 		if (handle->speed && do_speed) {
1087 			float factor = 0.25f * abs(handle->speed);
1088 			switch_size_t new_len, supplement_len, step_len;
1089 			short *bp = context->abuf;
1090 			switch_size_t wrote_len = 0;
1091 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Generate speed frame (%i)\n", handle->speed);
1092 
1093 			supplement_len = (int) (factor * o_len);
1094 			if (!supplement_len) {
1095 				supplement_len = 1;
1096 			}
1097 			new_len = (handle->speed > 0) ? o_len - supplement_len : o_len + supplement_len;
1098 
1099 			step_len = (handle->speed > 0) ? (new_len / supplement_len) : (o_len / supplement_len);
1100 
1101 			if (!fh->sp_audio_buffer) {
1102 				switch_buffer_create_dynamic(&fh->sp_audio_buffer, 1024, 1024, 0);
1103 			}
1104 
1105 			while ((wrote_len + step_len) < new_len) {
1106 				switch_buffer_write(fh->sp_audio_buffer, bp, step_len * 2);
1107 				wrote_len += step_len;
1108 				bp += step_len;
1109 				if (handle->speed > 0) {
1110 					bp++;
1111 				} else {
1112 					float f;
1113 					short s;
1114 					f = (float) (*bp + *(bp + 1) + *(bp - 1));
1115 					f /= 3;
1116 					s = (short) f;
1117 					switch_buffer_write(fh->sp_audio_buffer, &s, 2);
1118 					wrote_len++;
1119 				}
1120 			}
1121 			if (wrote_len < new_len) {
1122 				switch_size_t r_len = new_len - wrote_len;
1123 				switch_buffer_write(fh->sp_audio_buffer, bp, r_len * 2);
1124 			}
1125 			continue;
1126 		}
1127 
1128 		/* adjust volume on frame */
1129 		if (handle->vol) {
1130 			//switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Adjust volume to = %i\n", handle->vol);
1131 			switch_change_sln_volume(context->abuf, *len, handle->vol);
1132 		}
1133 		break;
1134 	}
1135 
1136 done:
1137 
1138 	/* copy frame over to return to caller */
1139 	memcpy(data, context->abuf, *len * 2);
1140 	handle->offset_pos = context->fh.offset_pos;
1141 
1142 	return status;
1143 }
1144 
1145 /**
1146  * Seek file
1147  */
fileman_file_seek(switch_file_handle_t * handle,unsigned int * cur_sample,int64_t samples,int whence)1148 static switch_status_t fileman_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
1149 {
1150 	struct fileman_file_context *context = handle->private_info;
1151 
1152 	if (!handle->seekable) {
1153 		switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_WARNING, "File is not seekable\n");
1154 		return SWITCH_STATUS_NOTIMPL;
1155 	}
1156 	return switch_core_file_seek(&context->fh, cur_sample, samples, whence);
1157 }
1158 
1159 /**
1160  * Process fileman command
1161  */
fileman_process_cmd(const char * cmd,switch_file_handle_t * fhp)1162 static switch_status_t fileman_process_cmd(const char *cmd, switch_file_handle_t *fhp)
1163 {
1164 	if (zstr(cmd)) {
1165 		return SWITCH_STATUS_SUCCESS;
1166 	}
1167 
1168 	if (fhp) {
1169 		struct fileman_file_context *context = (struct fileman_file_context *)fhp->private_info;
1170 		if (!switch_test_flag(fhp, SWITCH_FILE_OPEN)) {
1171 			return SWITCH_STATUS_FALSE;
1172 		}
1173 
1174 		if (!strncasecmp(cmd, "speed", 5)) {
1175 			char *p;
1176 
1177 			if ((p = strchr(cmd, ':'))) {
1178 				p++;
1179 				if (*p == '+' || *p == '-') {
1180 					int step;
1181 					if (!(step = atoi(p))) {
1182 						if (*p == '+') {
1183 							step = 1;
1184 						} else {
1185 							step = -1;
1186 						}
1187 					}
1188 					fhp->speed += step;
1189 				} else {
1190 					int speed = atoi(p);
1191 					fhp->speed = speed;
1192 				}
1193 				return SWITCH_STATUS_SUCCESS;
1194 			}
1195 
1196 			return SWITCH_STATUS_FALSE;
1197 
1198 		} else if (!strncasecmp(cmd, "volume", 6)) {
1199 			char *p;
1200 
1201 			if ((p = strchr(cmd, ':'))) {
1202 				p++;
1203 				if (*p == '+' || *p == '-') {
1204 					int step;
1205 					if (!(step = atoi(p))) {
1206 						if (*p == '+') {
1207 							step = 1;
1208 						} else {
1209 							step = -1;
1210 						}
1211 					}
1212 					fhp->vol += step;
1213 				} else {
1214 					int vol = atoi(p);
1215 					fhp->vol = vol;
1216 				}
1217 				return SWITCH_STATUS_SUCCESS;
1218 			}
1219 
1220 			if (fhp->vol) {
1221 				switch_normalize_volume(fhp->vol);
1222 			}
1223 
1224 			return SWITCH_STATUS_FALSE;
1225 		} else if (!strcasecmp(cmd, "pause")) {
1226 			switch_set_flag_locked(fhp, SWITCH_FILE_PAUSE);
1227 			return SWITCH_STATUS_SUCCESS;
1228 		} else if (!strcasecmp(cmd, "resume")) {
1229 			switch_clear_flag_locked(fhp, SWITCH_FILE_PAUSE);
1230 			return SWITCH_STATUS_SUCCESS;
1231 		} else if (!strcasecmp(cmd, "stop")) {
1232 			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "Stopping file\n");
1233 			context->done = 1;
1234 			switch_set_flag_locked(fhp, SWITCH_FILE_DONE);
1235 			return SWITCH_STATUS_SUCCESS;
1236 		} else if (!strcasecmp(cmd, "truncate")) {
1237 			switch_core_file_truncate(fhp, 0);
1238 		} else if (!strcasecmp(cmd, "restart")) {
1239 			unsigned int pos = 0;
1240 			fhp->speed = 0;
1241 			switch_core_file_seek(fhp, &pos, 0, SEEK_SET);
1242 			return SWITCH_STATUS_SUCCESS;
1243 		} else if (!strncasecmp(cmd, "seek", 4)) {
1244 			unsigned int samps = 0;
1245 			unsigned int pos = 0;
1246 			char *p;
1247 
1248 			if ((p = strchr(cmd, ':'))) {
1249 				p++;
1250 				if (*p == '+' || *p == '-') {
1251 					int step;
1252 					int32_t target;
1253 					if (!(step = atoi(p))) {
1254 						if (*p == '+') {
1255 							step = 1000;
1256 						} else {
1257 							step = -1000;
1258 						}
1259 					}
1260 
1261 					samps = step * (fhp->samplerate / 1000);
1262 					target = (int32_t)fhp->pos + samps;
1263 
1264 					if (target < 0) {
1265 						target = 0;
1266 					}
1267 
1268 					switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "seek to position %d\n", target);
1269 					switch_core_file_seek(fhp, &pos, target, SEEK_SET);
1270 
1271 				} else {
1272 					samps = switch_atoui(p) * (fhp->samplerate / 1000);
1273 					switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->uuid), SWITCH_LOG_DEBUG, "seek to position %d\n", samps);
1274 					switch_core_file_seek(fhp, &pos, samps, SEEK_SET);
1275 				}
1276 			}
1277 
1278 			return SWITCH_STATUS_SUCCESS;
1279 		}
1280 	}
1281 
1282 	if (!strcmp(cmd, "true") || !strcmp(cmd, "undefined")) {
1283 		return SWITCH_STATUS_SUCCESS;
1284 	}
1285 
1286 	return SWITCH_STATUS_FALSE;
1287 }
1288 
1289 #define FILEMAN_SYNTAX "<id> <cmd>:<val>"
SWITCH_STANDARD_API(fileman_api)1290 SWITCH_STANDARD_API(fileman_api)
1291 {
1292 	char *mycmd = NULL, *argv[4] = { 0 };
1293 	int argc = 0;
1294 
1295 	if (!zstr(cmd) && (mycmd = strdup(cmd))) {
1296 		argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
1297 		if (argc >= 2 && !zstr(argv[0])) {
1298 			char *id = argv[0];
1299 			char *cmd = argv[1];
1300 			switch_file_handle_t *fh = NULL;
1301 			switch_mutex_lock(fileman_globals.mutex);
1302 			fh = (switch_file_handle_t *)switch_core_hash_find(fileman_globals.hash, id);
1303 			if (fh) {
1304 				if (fileman_process_cmd(cmd, fh) == SWITCH_STATUS_SUCCESS) {
1305 					stream->write_function(stream, "+OK\n");
1306 				} else {
1307 					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "fileman API failed for file %s\n", zstr(fh->file_path) ? "<null>" : fh->file_path);
1308 					stream->write_function(stream, "-ERR API call failed");
1309 				}
1310 				switch_mutex_unlock(fileman_globals.mutex);
1311 			} else {
1312 				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "fileman API failed for ID %s\n", zstr(id) ? "<null>" : id);
1313 				switch_mutex_unlock(fileman_globals.mutex);
1314 				stream->write_function(stream, "-ERR file handle not found\n");
1315 			}
1316 			goto done;
1317 		}
1318 	}
1319 
1320 	stream->write_function(stream, "-USAGE: %s\n", FILEMAN_SYNTAX);
1321 
1322   done:
1323 	switch_safe_free(mycmd);
1324 	return SWITCH_STATUS_SUCCESS;
1325 }
1326 
1327 static char *rayo_supported_formats[] = { "rayo", NULL };
1328 static char *fileman_supported_formats[] = { "fileman", NULL };
1329 
1330 /**
1331  * Initialize output component
1332  * @param module_interface
1333  * @param pool memory pool to allocate from
1334  * @param config_file to use
1335  * @return SWITCH_STATUS_SUCCESS if successful
1336  */
rayo_output_component_load(switch_loadable_module_interface_t ** module_interface,switch_memory_pool_t * pool,const char * config_file)1337 switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
1338 {
1339 	switch_api_interface_t *api_interface;
1340 	switch_file_interface_t *file_interface;
1341 
1342 	rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_OUTPUT_NS":output", start_call_output_component);
1343 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_EXT_NS":stop", stop_output_component);
1344 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":pause", pause_output_component);
1345 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":resume", resume_output_component);
1346 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-up", speed_up_output_component);
1347 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-down", speed_down_output_component);
1348 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-up", volume_up_output_component);
1349 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-down", volume_down_output_component);
1350 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":seek", seek_output_component);
1351 
1352 	rayo_actor_command_handler_add(RAT_MIXER, "", "set:"RAYO_OUTPUT_NS":output", start_mixer_output_component);
1353 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_EXT_NS":stop", stop_output_component);
1354 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":pause", pause_output_component);
1355 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":resume", resume_output_component);
1356 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-up", speed_up_output_component);
1357 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":speed-down", speed_down_output_component);
1358 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-up", volume_up_output_component);
1359 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":volume-down", volume_down_output_component);
1360 	rayo_actor_command_handler_add(RAT_MIXER_COMPONENT, "output", "set:"RAYO_OUTPUT_NS":seek", seek_output_component);
1361 
1362 	file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
1363 	file_interface->interface_name = "mod_rayo";
1364 	file_interface->extens = rayo_supported_formats;
1365 	file_interface->file_open = rayo_file_open;
1366 	file_interface->file_close = rayo_file_close;
1367 	file_interface->file_read = rayo_file_read;
1368 	file_interface->file_seek = rayo_file_seek;
1369 
1370 	switch_mutex_init(&fileman_globals.mutex, SWITCH_MUTEX_NESTED, pool);
1371 	switch_core_hash_init(&fileman_globals.hash);
1372 
1373 	file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
1374 	file_interface->interface_name = "mod_rayo";
1375 	file_interface->extens = fileman_supported_formats;
1376 	file_interface->file_open = fileman_file_open;
1377 	file_interface->file_close = fileman_file_close;
1378 	file_interface->file_write = fileman_file_write;
1379 	file_interface->file_read = fileman_file_read;
1380 	file_interface->file_seek = fileman_file_seek;
1381 
1382 	SWITCH_ADD_API(api_interface, "fileman", "Manage file audio", fileman_api, FILEMAN_SYNTAX);
1383 
1384 	return SWITCH_STATUS_SUCCESS;
1385 }
1386 
1387 /**
1388  * Shutdown output component
1389  * @return SWITCH_STATUS_SUCCESS if successful
1390  */
rayo_output_component_shutdown(void)1391 switch_status_t rayo_output_component_shutdown(void)
1392 {
1393 	if (fileman_globals.hash) {
1394 		switch_core_hash_destroy(&fileman_globals.hash);
1395 	}
1396 
1397 	return SWITCH_STATUS_SUCCESS;
1398 }
1399 
1400 /* For Emacs:
1401  * Local Variables:
1402  * mode:c
1403  * indent-tabs-mode:t
1404  * tab-width:4
1405  * c-basic-offset:4
1406  * End:
1407  * For VIM:
1408  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
1409  */
1410 
1411