diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 9a8900dbfa..45a8ab04bb 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -22,6 +22,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/Makefile b/src/mod/event_handlers/mod_rayo/Makefile index a3bfc45d47..3b7e5dba3f 100644 --- a/src/mod/event_handlers/mod_rayo/Makefile +++ b/src/mod/event_handlers/mod_rayo/Makefile @@ -10,6 +10,7 @@ LOCAL_OBJS= $(IKS_LA) \ iks_helpers.o \ nlsml.o \ rayo_components.o \ + rayo_cpa_detector.o \ rayo_elements.o \ rayo_fax_components.o \ rayo_input_component.o \ @@ -23,6 +24,7 @@ LOCAL_SOURCES= \ iks_helpers.c \ nlsml.c \ rayo_components.c \ + rayo_cpa_detector.c \ rayo_elements.c \ rayo_fax_components.c \ rayo_input_component.c \ diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 9a8900dbfa..45a8ab04bb 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -22,6 +22,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c new file mode 100644 index 0000000000..9267e05456 --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c @@ -0,0 +1,435 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2014, Grasshopper + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_cpa_detector.c -- Glue to normalize events from and to allow multiple instantiation of various detectors in FreeSWITCH + */ + +#include "rayo_cpa_detector.h" + +static struct { + /** detectors supported by this module mapped by signal-type */ + switch_hash_t *detectors; +} globals; + +struct rayo_cpa_detector; + +/** + * Detector definition + */ +struct rayo_cpa_detector { + /** unique internal name of this detector */ + const char *name; + /** detector ID */ + const char *uuid; + /** start detection APP */ + const char *start_app; + /** args to pass to start detection app */ + const char *start_app_args; + /** stop detection APP */ + const char *stop_app; + /** args to pass to stop detection app */ + const char *stop_app_args; + /** (optional) name of header to get the signal type from */ + const char *signal_type_header; + /** (optional) where to get the signal value from the event */ + const char *signal_value_header; + /** (optional) where to get the signal duration from the event */ + const char *signal_duration_header; + /** detector event to signal type mapping */ + switch_hash_t *signal_type_map; +}; + +/** + * Detection state + */ +struct rayo_cpa_detector_state { + /** reference count */ + int refs; +}; + +/** + * Start detecting + * @param call_uuid call to detect signal on + * @param signal_ns namespace of signal to detect + * @param error_detail on failure, describes the error + * @return 1 if successful, 0 if failed + */ +int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail) +{ + struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns); + if (detector) { + switch_core_session_t *session = switch_core_session_locate(call_uuid); + if (session) { + struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid); + if (detector_state) { + /* detector is already running */ + detector_state->refs++; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Start detector %s, refs = %d\n", detector->name, detector_state->refs); + switch_core_session_rwunlock(session); + return 1; + } + detector_state = switch_core_session_alloc(session, sizeof(*detector_state)); + detector_state->refs = 1; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Starting detector %s, refs = 1\n", detector->name); + switch_channel_set_private(switch_core_session_get_channel(session), detector->uuid, detector_state); + switch_core_session_execute_application_async(session, detector->start_app, zstr(detector->start_app_args) ? NULL : detector->start_app_args); + switch_core_session_rwunlock(session); + return 1; + } else { + *error_detail = "session gone"; + return 0; + } + } + *error_detail = "detector not supported"; + return 0; +} + +/** + * Stop detecting + * @param call_uuid call to stop detecting signal on + * @param signal_ns name of signal to stop detecting + */ +void rayo_cpa_detector_stop(const char *call_uuid, const char *signal_ns) +{ + struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns); + if (detector) { + switch_core_session_t *session = switch_core_session_locate(call_uuid); + if (session) { + struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid); + if (detector_state) { + detector_state->refs--; + if (detector_state->refs < 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Stop detector %s refs = %d\n", detector->name, detector_state->refs); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stop detector %s refs = %d\n", detector->name, detector_state->refs); + } + if (detector_state->refs == 0) { + /* nobody interested in detector events- shut it down */ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stopping detector %s\n", detector->name); + switch_core_session_execute_application_async(session, detector->stop_app, zstr(detector->stop_app_args) ? NULL : detector->stop_app_args); + switch_channel_set_private(switch_core_session_get_channel(session), detector->uuid, NULL); + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Detector %s is already stopped\n", detector->name); + } + switch_core_session_rwunlock(session); + } + } +} + +/** + * Handle event from detector + */ +static void rayo_cpa_detector_event(switch_event_t *event) +{ + struct rayo_cpa_detector *detector = (struct rayo_cpa_detector *)event->bind_user_data; + if (detector) { + const char *signal_type = "rayo_default"; + if (!zstr(detector->signal_type_header)) { + signal_type = switch_event_get_header(event, detector->signal_type_header); + } + if (!zstr(signal_type)) { + signal_type = switch_core_hash_find(detector->signal_type_map, signal_type); + } + if (!zstr(signal_type)) { + switch_event_t *cpa_event; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type); + if (switch_event_create_subclass(&cpa_event, SWITCH_EVENT_CUSTOM, "rayo::cpa") == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name); + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid); + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", signal_type); + if (!zstr(detector->signal_value_header)) { + const char *value = switch_event_get_header(event, detector->signal_value_header); + if (!zstr(value)) { + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "value", value); + } + } + if (!zstr(detector->signal_duration_header)) { + const char *duration = switch_event_get_header(event, detector->signal_duration_header); + if (!zstr(duration)) { + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration); + } + } + switch_event_fire(&cpa_event); + } + } else { + /* couldn't map event to Rayo signal-type */ + const char *event_name = switch_event_get_header(event, "Event-Name"); + const char *event_subclass = switch_event_get_header(event, "Event-Subclass"); + if (zstr(event_subclass)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to find Rayo signal-type for event %s\n", event_name); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to find Rayo signal-type for event %s %s\n", event_name, event_subclass); + } + } + } +} + +#define RAYO_CPA_DETECTOR_SYNTAX "rayo_cpa " +SWITCH_STANDARD_API(rayo_cpa_detector_api) +{ + char *cmd_dup = NULL; + char *argv[4] = { 0 }; + int argc = 0; + + if (zstr(cmd)) { + stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX); + goto done; + } + + cmd_dup = strdup(cmd); + argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); + + if (argc != 3) { + stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX); + } else { + const char *err_reason = NULL; + if (!strcmp(argv[2], "stop")) { + rayo_cpa_detector_stop(argv[0], argv[1]); + stream->write_function(stream, "+OK\n"); + } else if (!strcmp(argv[2], "start")) { + if (!rayo_cpa_detector_start(argv[0], argv[1], &err_reason)) { + if (err_reason) { + stream->write_function(stream, "-ERR: %s\n", err_reason); + } else { + stream->write_function(stream, "-ERR\n"); + } + } else { + stream->write_function(stream, "+OK\n"); + } + } else { + stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX); + } + } + +done: + switch_safe_free(cmd_dup); + + return SWITCH_STATUS_SUCCESS; +} + +/** + * Configure CPA + */ +static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file) +{ + switch_xml_t cfg, xml, cpa_xml; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_hash_t *bound_events; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring CPA\n"); + if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file); + return SWITCH_STATUS_TERM; + } + + switch_core_hash_init(&bound_events, pool); + + cpa_xml = switch_xml_child(cfg, "cpa"); + if (cpa_xml) { + switch_xml_t detector_xml; + + for (detector_xml = switch_xml_child(cpa_xml, "detector"); detector_xml; detector_xml = detector_xml->next) { + switch_xml_t start_xml, stop_xml, event_xml; + struct rayo_cpa_detector *detector; + char id[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + const char *name = switch_xml_attr_soft(detector_xml, "name"); + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing name of CPA detector!\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CPA detector: %s\n", name); + detector = switch_core_alloc(pool, sizeof(*detector)); + switch_core_hash_init(&detector->signal_type_map, pool); + detector->name = switch_core_strdup(pool, name); + switch_uuid_str(id, sizeof(id)); + detector->uuid = switch_core_strdup(pool, id); + + start_xml = switch_xml_child(detector_xml, "start"); + if (start_xml) { + detector->start_app = switch_core_strdup(pool, switch_xml_attr_soft(start_xml, "application")); + detector->start_app_args = switch_core_strdup(pool, switch_xml_attr_soft(start_xml, "data")); + } + + stop_xml = switch_xml_child(detector_xml, "stop"); + if (stop_xml) { + detector->stop_app = switch_core_strdup(pool, switch_xml_attr_soft(stop_xml, "application")); + detector->stop_app_args = switch_core_strdup(pool, switch_xml_attr_soft(stop_xml, "data")); + } + + event_xml = switch_xml_child(detector_xml, "event"); + if (event_xml) { + int event_ok = 0; + switch_xml_t signal_type_xml; + const char *event_class = switch_xml_attr_soft(event_xml, "class"); + const char *event_subclass = switch_xml_attr_soft(event_xml, "subclass"); + switch_event_types_t event_type; + if (zstr(event_class)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing event class for CPA detector: %s\n", detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + + if (switch_name_event(event_class, &event_type) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event class %s for CPA detector: %s\n", event_class, detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + + /* bind detector to event if not already done... */ + { + struct rayo_cpa_detector *bound_detector; + const char *event_name = switch_core_sprintf(pool, "%s %s", event_class, event_subclass); + if (!(bound_detector = switch_core_hash_find(bound_events, event_name))) { + /* not yet bound */ + if (zstr(event_subclass)) { + event_subclass = NULL; + } + switch_event_bind("rayo_cpa_detector", event_type, event_subclass, rayo_cpa_detector_event, detector); + switch_core_hash_insert(bound_events, event_name, detector); /* mark as bound */ + } else if (bound_detector != detector) { + /* can't have multiple detectors generating the same event! */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s attempted to bind to event %s that is already bound by %s\n", detector->name, event_name, bound_detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + } + + /* configure native event -> rayo CPA event mapping */ + detector->signal_type_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "type-header")); + detector->signal_value_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "value-header")); + detector->signal_duration_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "duration-header")); + + /* configure native event type -> rayo CPA signal type mapping */ + for (signal_type_xml = switch_xml_child(event_xml, "signal-type"); signal_type_xml; signal_type_xml = signal_type_xml->next) { + const char *header_value = switch_core_strdup(pool, switch_xml_attr_soft(signal_type_xml, "header-value")); + const char *signal_type = switch_core_strdup(pool, switch_xml_attr_soft(signal_type_xml, "value")); + if (zstr(signal_type)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s missing signal-type value!\n", detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } else { + /* add signal-type to detector mapping */ + const char *signal_type_ns = switch_core_sprintf(pool, "%s%s:%s", RAYO_CPA_BASE, signal_type, RAYO_VERSION); + event_ok = 1; + switch_core_hash_insert(globals.detectors, signal_type_ns, detector); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding CPA %s => %s\n", signal_type_ns, detector->name); + } + + /* map event value to signal-type */ + if (zstr(header_value)) { + switch_core_hash_insert(detector->signal_type_map, "rayo_default", signal_type); + } else { + switch_core_hash_insert(detector->signal_type_map, header_value, signal_type); + } + } + + if (!event_ok) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s is missing Rayo signal-type for event\n", detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + } + } + } + +done: + switch_core_hash_destroy(&bound_events); + switch_xml_free(xml); + + return status; +} + +/** + * Console auto-completion for signal types + */ +static switch_status_t rayo_cpa_detector_signal_types(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + switch_hash_index_t *hi; + void *val; + const void *vvar; + switch_console_callback_match_t *my_matches = NULL; + + for (hi = switch_hash_first(NULL, globals.detectors); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &vvar, NULL, &val); + switch_console_push_match(&my_matches, (const char *) vvar); + } + + if (my_matches) { + *matches = my_matches; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + +/** + * Load CPA signal detection features + * @param module_interface + * @param pool memory pool + * @param config_file + * @return SWITCH_STATUS_SUCCESS if successfully loaded + */ +switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) +{ + switch_api_interface_t *api_interface; + + SWITCH_ADD_API(api_interface, "rayo_cpa", "Query rayo status", rayo_cpa_detector_api, RAYO_CPA_DETECTOR_SYNTAX); + + switch_console_set_complete("add rayo_cpa ::console::list_uuid ::rayo_cpa::list_signal_types start"); + switch_console_set_complete("add rayo_cpa ::console::list_uuid ::rayo_cpa::list_signal_types stop"); + switch_console_add_complete_func("::rayo_cpa::list_signal_types", rayo_cpa_detector_signal_types); + + switch_core_hash_init(&globals.detectors, pool); + if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_TERM; + } + return SWITCH_STATUS_SUCCESS; +} + +/** + * Disable CPA signal detection features + */ +void rayo_cpa_detector_shutdown(void) +{ + switch_console_set_complete("del rayo_cpa"); + switch_console_del_complete_func("::rayo_cpa::list_signal_types"); + switch_core_hash_destroy(&globals.detectors); + switch_event_unbind_callback(rayo_cpa_detector_event); +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet + */ diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h new file mode 100644 index 0000000000..335d1c3a1b --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h @@ -0,0 +1,68 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2014, Grasshopper + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_cpa_detector.h -- Rayo call progress analysis + * + */ +#ifndef RAYO_CPA_DETECTOR_H +#define RAYO_CPA_DETECTOR_H + +#include + +#include "mod_rayo.h" + +#define RAYO_CPA_BASE RAYO_BASE "cpa:" +#define RAYO_CPA_NS RAYO_CPA_BASE RAYO_VERSION + +#define RAYO_CPA_BEEP_NS RAYO_CPA_BASE "beep:" RAYO_VERSION +#define RAYO_CPA_DTMF_NS RAYO_CPA_BASE "dtmf:" RAYO_VERSION +#define RAYO_CPA_SPEECH_NS RAYO_CPA_BASE "speech:" RAYO_VERSION +#define RAYO_CPA_FAX_CED_NS RAYO_CPA_BASE "fax-ced:" RAYO_VERSION +#define RAYO_CPA_FAX_CNG_NS RAYO_CPA_BASE "fax-cng:" RAYO_VERSION +#define RAYO_CPA_RING_NS RAYO_CPA_BASE "ring:" RAYO_VERSION +#define RAYO_CPA_BUSY_NS RAYO_CPA_BASE "busy:" RAYO_VERSION +#define RAYO_CPA_CONGESTION_NS RAYO_CPA_BASE "congestion:" RAYO_VERSION +#define RAYO_CPA_SIT_NS RAYO_CPA_BASE "sit:" RAYO_VERSION +#define RAYO_CPA_MODEM_NS RAYO_CPA_BASE "modem:" RAYO_VERSION +#define RAYO_CPA_OFFHOOK_NS RAYO_CPA_BASE "offhook:" RAYO_VERSION + +extern switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern void rayo_cpa_detector_shutdown(void); +extern int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail); +extern void rayo_cpa_detector_stop(const char *call_uuid, const char *signal_ns); + +#endif + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet + */ diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c index 8238deb48a..4ea0e5f600 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c @@ -27,6 +27,7 @@ * */ #include "rayo_components.h" +#include "rayo_cpa_detector.h" #include "rayo_elements.h" #include "srgs.h" #include "nlsml.h" @@ -760,7 +761,7 @@ switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **m rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "input", "set:"RAYO_INPUT_NS":start-timers", start_timers_call_input_component); switch_event_bind("rayo_input_component", SWITCH_EVENT_DETECTED_SPEECH, SWITCH_EVENT_SUBCLASS_ANY, on_detected_speech_event, NULL); - return SWITCH_STATUS_SUCCESS; + return rayo_cpa_detector_load(module_interface, pool, config_file); } /** @@ -771,6 +772,9 @@ switch_status_t rayo_input_component_shutdown(void) { srgs_parser_destroy(globals.parser); switch_event_unbind_callback(on_detected_speech_event); + + rayo_cpa_detector_shutdown(); + return SWITCH_STATUS_SUCCESS; }