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