diff --git a/src/include/private/switch_core.h b/src/include/private/switch_core.h new file mode 100644 index 0000000000..90a4a344aa --- /dev/null +++ b/src/include/private/switch_core.h @@ -0,0 +1,149 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * switch_core.h -- Core Library Private Data (not to be installed into the system) + * If the last line didn't make sense, stop reading this file, go away!, + * this file does not exist!!!! + * + */ +#ifndef WIN32 +#include +#endif + +/* for apr_pool_create and apr_pool_destroy */ +/* functions only used in this file so not exposed */ +#include + +/* for apr_hash_make, apr_hash_pool_get, apr_hash_set */ +/* functions only used in this file so not exposed */ +#include + +/* for apr_pvsprintf */ +/* function only used in this file so not exposed */ +#include + +/* for apr_initialize and apr_terminate */ +/* function only used in this file so not exposed */ +#include + +#include +typedef apr_os_thread_t switch_thread_id_t; +#define switch_thread_self apr_os_thread_current + + +#ifdef HAVE_MLOCKALL +#include +#endif + +/* #define DEBUG_ALLOC */ +#define DO_EVENTS + +#ifdef CRASH_PROT +#define __CP "ENABLED" +#else +#define __CP "DISABLED" +#endif + +#define SWITCH_EVENT_QUEUE_LEN 256 +#define SWITCH_MESSAGE_QUEUE_LEN 256 +#define SWITCH_SQL_QUEUE_LEN 2000 + +#define SWITCH_BUFFER_BLOCK_FRAMES 25 +#define SWITCH_BUFFER_START_FRAMES 50 + +typedef enum { + SSF_NONE = 0, + SSF_DESTROYED = (1 << 0) +} switch_session_flag_t; + + +struct switch_core_session { + uint32_t id; + char name[80]; + switch_session_flag_t flags; + int thread_running; + switch_memory_pool_t *pool; + switch_channel_t *channel; + switch_thread_t *thread; + const switch_endpoint_interface_t *endpoint_interface; + switch_io_event_hooks_t event_hooks; + switch_codec_t *read_codec; + switch_codec_t *write_codec; + + switch_buffer_t *raw_write_buffer; + switch_frame_t raw_write_frame; + switch_frame_t enc_write_frame; + uint8_t raw_write_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + uint8_t enc_write_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + + switch_buffer_t *raw_read_buffer; + switch_frame_t raw_read_frame; + switch_frame_t enc_read_frame; + uint8_t raw_read_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + uint8_t enc_read_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + + + switch_audio_resampler_t *read_resampler; + switch_audio_resampler_t *write_resampler; + + switch_mutex_t *mutex; + switch_thread_cond_t *cond; + + switch_thread_rwlock_t *rwlock; + + void *streams[SWITCH_MAX_STREAMS]; + int stream_count; + + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + void *private_info; + switch_queue_t *event_queue; + switch_queue_t *message_queue; + switch_queue_t *private_event_queue; + switch_thread_rwlock_t *bug_rwlock; + switch_media_bug_t *bugs; +}; + +struct switch_media_bug { + switch_buffer_t *raw_write_buffer; + switch_buffer_t *raw_read_buffer; + switch_frame_t *replace_frame_in; + switch_frame_t *replace_frame_out; + switch_media_bug_callback_t callback; + switch_mutex_t *read_mutex; + switch_mutex_t *write_mutex; + switch_core_session_t *session; + void *user_data; + uint32_t flags; + uint8_t ready; + struct switch_media_bug *next; +}; + +SWITCH_DECLARE(void) switch_core_sqldb_start(switch_memory_pool_t *pool); +SWITCH_DECLARE(void) switch_core_sqldb_stop(void); +SWITCH_DECLARE(void) switch_core_session_init(switch_memory_pool_t *pool); +SWITCH_DECLARE(switch_memory_pool_t *) switch_core_memory_init(void); diff --git a/src/include/switch_core_event_hook.h b/src/include/switch_core_event_hook.h new file mode 100644 index 0000000000..436301f8ea --- /dev/null +++ b/src/include/switch_core_event_hook.h @@ -0,0 +1,281 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * switch_core_event_hook.h Core Event Hooks + * + */ +#ifndef SWITCH_EVENT_HOOKS_H +#define SWITCH_EVENT_HOOKS_H + +#include +SWITCH_BEGIN_EXTERN_C typedef struct switch_io_event_hooks switch_io_event_hooks_t; + +typedef struct switch_io_event_hook_outgoing_channel switch_io_event_hook_outgoing_channel_t; +typedef struct switch_io_event_hook_answer_channel switch_io_event_hook_answer_channel_t; +typedef struct switch_io_event_hook_receive_message switch_io_event_hook_receive_message_t; +typedef struct switch_io_event_hook_receive_event switch_io_event_hook_receive_event_t; +typedef struct switch_io_event_hook_read_frame switch_io_event_hook_read_frame_t; +typedef struct switch_io_event_hook_write_frame switch_io_event_hook_write_frame_t; +typedef struct switch_io_event_hook_kill_channel switch_io_event_hook_kill_channel_t; +typedef struct switch_io_event_hook_waitfor_read switch_io_event_hook_waitfor_read_t; +typedef struct switch_io_event_hook_waitfor_write switch_io_event_hook_waitfor_write_t; +typedef struct switch_io_event_hook_send_dtmf switch_io_event_hook_send_dtmf_t; +typedef struct switch_io_event_hook_state_change switch_io_event_hook_state_change_t; + + +typedef switch_status_t (*switch_outgoing_channel_hook_t) (switch_core_session_t *, switch_caller_profile_t *, + switch_core_session_t *); +typedef switch_status_t (*switch_answer_channel_hook_t) (switch_core_session_t *); +typedef switch_status_t (*switch_receive_message_hook_t) (switch_core_session_t *, switch_core_session_message_t *); +typedef switch_status_t (*switch_receive_event_hook_t) (switch_core_session_t *, switch_event_t *); +typedef switch_status_t (*switch_read_frame_hook_t) (switch_core_session_t *, switch_frame_t **, int, switch_io_flag_t, + int); +typedef switch_status_t (*switch_write_frame_hook_t) (switch_core_session_t *, switch_frame_t *, int, switch_io_flag_t, + int); +typedef switch_status_t (*switch_kill_channel_hook_t) (switch_core_session_t *, int); +typedef switch_status_t (*switch_waitfor_read_hook_t) (switch_core_session_t *, int, int); +typedef switch_status_t (*switch_waitfor_write_hook_t) (switch_core_session_t *, int, int); +typedef switch_status_t (*switch_send_dtmf_hook_t) (switch_core_session_t *, char *); +typedef switch_status_t (*switch_state_change_hook_t) (switch_core_session_t *); + + +/*! \brief Node in which to store custom outgoing channel callback hooks */ +struct switch_io_event_hook_outgoing_channel { + /*! the outgoing channel callback hook */ + switch_outgoing_channel_hook_t outgoing_channel; + struct switch_io_event_hook_outgoing_channel *next; +}; + +/*! \brief Node in which to store custom answer channel callback hooks */ +struct switch_io_event_hook_answer_channel { + /*! the answer channel callback hook */ + switch_answer_channel_hook_t answer_channel; + struct switch_io_event_hook_answer_channel *next; +}; + +/*! \brief Node in which to store custom receive message callback hooks */ +struct switch_io_event_hook_receive_message { + /*! the answer channel callback hook */ + switch_receive_message_hook_t receive_message; + struct switch_io_event_hook_receive_message *next; +}; + +/*! \brief Node in which to store custom receive message callback hooks */ +struct switch_io_event_hook_receive_event { + /*! the answer channel callback hook */ + switch_receive_event_hook_t receive_event; + struct switch_io_event_hook_receive_event *next; +}; + +/*! \brief Node in which to store custom read frame channel callback hooks */ +struct switch_io_event_hook_read_frame { + /*! the read frame channel callback hook */ + switch_read_frame_hook_t read_frame; + struct switch_io_event_hook_read_frame *next; +}; + +/*! \brief Node in which to store custom write_frame channel callback hooks */ +struct switch_io_event_hook_write_frame { + /*! the write_frame channel callback hook */ + switch_write_frame_hook_t write_frame; + struct switch_io_event_hook_write_frame *next; +}; + +/*! \brief Node in which to store custom kill channel callback hooks */ +struct switch_io_event_hook_kill_channel { + /*! the kill channel callback hook */ + switch_kill_channel_hook_t kill_channel; + struct switch_io_event_hook_kill_channel *next; +}; + +/*! \brief Node in which to store custom waitfor read channel callback hooks */ +struct switch_io_event_hook_waitfor_read { + /*! the waitfor read channel callback hook */ + switch_waitfor_read_hook_t waitfor_read; + struct switch_io_event_hook_waitfor_read *next; +}; + +/*! \brief Node in which to store custom waitfor write channel callback hooks */ +struct switch_io_event_hook_waitfor_write { + /*! the waitfor write channel callback hook */ + switch_waitfor_write_hook_t waitfor_write; + struct switch_io_event_hook_waitfor_write *next; +}; + +/*! \brief Node in which to store custom send dtmf channel callback hooks */ +struct switch_io_event_hook_send_dtmf { + /*! the send dtmf channel callback hook */ + switch_send_dtmf_hook_t send_dtmf; + struct switch_io_event_hook_send_dtmf *next; +}; + +/*! \brief Node in which to store state change callback hooks */ +struct switch_io_event_hook_state_change { + /*! the send dtmf channel callback hook */ + switch_state_change_hook_t state_change; + struct switch_io_event_hook_state_change *next; +}; + +/*! \brief A table of lists of io_event_hooks to store the event hooks associated with a session */ +struct switch_io_event_hooks { + /*! a list of outgoing channel hooks */ + switch_io_event_hook_outgoing_channel_t *outgoing_channel; + /*! a list of answer channel hooks */ + switch_io_event_hook_answer_channel_t *answer_channel; + /*! a list of receive message hooks */ + switch_io_event_hook_receive_message_t *receive_message; + /*! a list of queue message hooks */ + switch_io_event_hook_receive_event_t *receive_event; + /*! a list of read frame hooks */ + switch_io_event_hook_read_frame_t *read_frame; + /*! a list of write frame hooks */ + switch_io_event_hook_write_frame_t *write_frame; + /*! a list of kill channel hooks */ + switch_io_event_hook_kill_channel_t *kill_channel; + /*! a list of wait for read hooks */ + switch_io_event_hook_waitfor_read_t *waitfor_read; + /*! a list of wait for write hooks */ + switch_io_event_hook_waitfor_write_t *waitfor_write; + /*! a list of send dtmf hooks */ + switch_io_event_hook_send_dtmf_t *send_dtmf; + /*! a list of state change hooks */ + switch_io_event_hook_state_change_t *state_change; +}; + +extern switch_io_event_hooks_t switch_core_session_get_event_hooks(switch_core_session_t *session); + + + +///\defgroup shooks Session Hook Callbacks +///\ingroup core1 +///\{ + +/*! + \brief Add an event hook to be executed when a session requests an outgoing extension + \param session session to bind hook to + \param outgoing_channel hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_outgoing_channel(switch_core_session_t *session, + switch_outgoing_channel_hook_t + outgoing_channel); + +/*! + \brief Add an event hook to be executed when a session answers a channel + \param session session to bind hook to + \param answer_channel hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_answer_channel(switch_core_session_t *session, + switch_answer_channel_hook_t answer_channel); + +/*! + \brief Add an event hook to be executed when a session sends a message + \param session session to bind hook to + \param receive_message hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_receive_message(switch_core_session_t *session, + switch_receive_message_hook_t + receive_message); + +/*! + \brief Add an event hook to be executed when a session reads a frame + \param session session to bind hook to + \param read_frame hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_read_frame(switch_core_session_t *session, + switch_read_frame_hook_t read_frame); + +/*! + \brief Add an event hook to be executed when a session writes a frame + \param session session to bind hook to + \param write_frame hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_write_frame(switch_core_session_t *session, + switch_write_frame_hook_t write_frame); + +/*! + \brief Add an event hook to be executed when a session kills a channel + \param session session to bind hook to + \param kill_channel hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_kill_channel(switch_core_session_t *session, + switch_kill_channel_hook_t kill_channel); + +/*! + \brief Add an event hook to be executed when a session waits for a read event + \param session session to bind hook to + \param waitfor_read hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_waitfor_read(switch_core_session_t *session, + switch_waitfor_read_hook_t waitfor_read); + +/*! + \brief Add an event hook to be executed when a session waits for a write event + \param session session to bind hook to + \param waitfor_write hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_waitfor_write(switch_core_session_t *session, + switch_waitfor_write_hook_t waitfor_write); + +/*! + \brief Add an event hook to be executed when a session sends dtmf + \param session session to bind hook to + \param send_dtmf hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_send_dtmf(switch_core_session_t *session, + switch_send_dtmf_hook_t send_dtmf); + +/*! + \brief Add an event hook to be executed when a session receives a state change signal + \param session session to bind hook to + \param state_change hook to bind + \return SWITCH_STATUS_SUCCESS on suceess +*/ +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_state_change(switch_core_session_t *session, + switch_answer_channel_hook_t state_change); +///\} + +SWITCH_END_EXTERN_C +#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 expandtab: + */ diff --git a/src/include/switch_scheduler.h b/src/include/switch_scheduler.h new file mode 100644 index 0000000000..16bc3a82a9 --- /dev/null +++ b/src/include/switch_scheduler.h @@ -0,0 +1,107 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * switch_scheduler.h -- Scheduler Engine + * + */ + +#ifndef SWITCH_SCHEDULER_H +#define SWITCH_SCHEDULER_H + +#include + +SWITCH_BEGIN_EXTERN_C +///\defgroup sched1 Scheduler +///\ingroup core1 +///\{ + struct switch_scheduler_task { + time_t created; + time_t runtime; + uint32_t cmd_id; + char *group; + void *cmd_arg; + uint32_t task_id; +}; + + +/*! + \brief Schedule a task in the future + \param runtime the time in epoch seconds to execute the task. + \param func the callback function to execute when the task is executed. + \param desc an arbitrary description of the task. + \param group a group id tag to link multiple tasks to a single entity. + \param cmd_id an arbitrary index number be used in the callback. + \param cmd_arg user data to be passed to the callback. + \param flags flags to alter behaviour + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_scheduler_add_task(time_t task_runtime, + switch_scheduler_func_t func, + char *desc, + char *group, + uint32_t cmd_id, void *cmd_arg, switch_scheduler_flag_t flags); + +/*! + \brief Delete a scheduled task + \param task_id the id of the task + \return SWITCH_STATUS_SUCCESS if the task was deleted. +*/ +SWITCH_DECLARE(switch_status_t) switch_scheduler_del_task_id(uint32_t task_id); + +/*! + \brief Delete a scheduled task based on the group name + \param group the group name + \return SWITCH_STATUS_SUCCESS if any tasks were deleted +*/ +SWITCH_DECLARE(switch_status_t) switch_scheduler_del_task_group(char *group); + + +/*! + \brief Start the scheduler system +*/ +SWITCH_DECLARE(void) switch_scheduler_task_thread_start(void); + +/*! + \brief Stop the scheduler system +*/ +SWITCH_DECLARE(void) switch_scheduler_task_thread_stop(void); + +///\} + +SWITCH_END_EXTERN_C +#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 expandtab: + */ diff --git a/src/switch_core_asr.c b/src/switch_core_asr.c new file mode 100644 index 0000000000..5dfeb4157d --- /dev/null +++ b/src/switch_core_asr.c @@ -0,0 +1,139 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_asr.c -- Main Core Library (Speech Detection Interface) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_asr_open(switch_asr_handle_t *ah, + char *module_name, + char *codec, + int rate, + char *dest, switch_asr_flag_t *flags, switch_memory_pool_t *pool) +{ + switch_status_t status; + + assert(ah != NULL); + + if ((ah->asr_interface = switch_loadable_module_get_asr_interface(module_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid asr module [%s]!\n", module_name); + return SWITCH_STATUS_GENERR; + } + + ah->flags = *flags; + + if (pool) { + ah->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&ah->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(ah, SWITCH_ASR_FLAG_FREE_POOL); + } + + ah->rate = rate; + ah->name = switch_core_strdup(ah->memory_pool, module_name); + + return ah->asr_interface->asr_open(ah, codec, rate, dest, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_load_grammar(switch_asr_handle_t *ah, char *grammar, char *path) +{ + char *epath = NULL; + switch_status_t status; + + assert(ah != NULL); + + if (*path != '/') { + epath = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.grammar_dir, SWITCH_PATH_SEPARATOR, path); + path = epath; + } + + status = ah->asr_interface->asr_load_grammar(ah, grammar, path); + switch_safe_free(epath); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_unload_grammar(switch_asr_handle_t *ah, char *grammar) +{ + switch_status_t status; + + assert(ah != NULL); + status = ah->asr_interface->asr_unload_grammar(ah, grammar); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_pause(switch_asr_handle_t *ah) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_pause(ah); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_resume(switch_asr_handle_t *ah) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_resume(ah); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_close(ah, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, + switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_feed(ah, data, len, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_check_results(switch_asr_handle_t *ah, switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_check_results(ah, flags); +} + +SWITCH_DECLARE(switch_status_t) switch_core_asr_get_results(switch_asr_handle_t *ah, char **xmlstr, + switch_asr_flag_t *flags) +{ + assert(ah != NULL); + + return ah->asr_interface->asr_get_results(ah, xmlstr, flags); +} diff --git a/src/switch_core_codec.c b/src/switch_core_codec.c new file mode 100644 index 0000000000..358878fcab --- /dev/null +++ b/src/switch_core_codec.c @@ -0,0 +1,253 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_codec.c -- Main Core Library (codec functions) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_session_set_read_codec(switch_core_session_t *session, + switch_codec_t *codec) +{ + switch_event_t *event; + + assert(session != NULL); + + if (switch_event_create(&event, SWITCH_EVENT_CODEC) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(session->channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "channel-read-codec-name", "%s", + codec->implementation->iananame); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "channel-read-codec-rate", "%d", + codec->implementation->samples_per_second); + switch_event_fire(&event); + } + + session->read_codec = codec; + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_codec_t *) switch_core_session_get_read_codec(switch_core_session_t *session) +{ + return session->read_codec; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_set_write_codec(switch_core_session_t *session, + switch_codec_t *codec) +{ + switch_event_t *event; + assert(session != NULL); + + if (switch_event_create(&event, SWITCH_EVENT_CODEC) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(session->channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "channel-write-codec-name", "%s", + codec->implementation->iananame); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "channel-write-codec-rate", "%d", + codec->implementation->samples_per_second); + switch_event_fire(&event); + } + + session->write_codec = codec; + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_codec_t *) switch_core_session_get_write_codec(switch_core_session_t *session) +{ + return session->write_codec; +} + +SWITCH_DECLARE(switch_status_t) switch_core_codec_init(switch_codec_t *codec, char *codec_name, char *fmtp, + uint32_t rate, int ms, int channels, uint32_t flags, + const switch_codec_settings_t *codec_settings, + switch_memory_pool_t *pool) +{ + const switch_codec_interface_t *codec_interface; + const switch_codec_implementation_t *iptr, *implementation = NULL; + char *mode = fmtp; + + assert(codec != NULL); + assert(codec_name != NULL); + + memset(codec, 0, sizeof(*codec)); + + if ((codec_interface = switch_loadable_module_get_codec_interface(codec_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid codec %s!\n", codec_name); + return SWITCH_STATUS_GENERR; + } + + if (!strcasecmp(codec_name, "ilbc") && mode && strncasecmp(mode, "mode=", 5)) { + int mms; + mode += 5; + if (mode) { + mms = atoi(mode); + if (mms > 0 && mms < 120) { + ms = mms; + } + } + } + + /* If no specific codec interval is requested opt for 20ms above all else because lots of stuff assumes it */ + if (!ms) { + for (iptr = codec_interface->implementations; iptr; iptr = iptr->next) { + if ((!rate || rate == iptr->samples_per_second) && + (20 == (iptr->microseconds_per_frame / 1000)) && (!channels || channels == iptr->number_of_channels)) { + implementation = iptr; + goto found; + } + } + } + + /* Either looking for a specific interval or there was no interval specified and there wasn't one @20ms available */ + for (iptr = codec_interface->implementations; iptr; iptr = iptr->next) { + if ((!rate || rate == iptr->samples_per_second) && + (!ms || ms == (iptr->microseconds_per_frame / 1000)) && + (!channels || channels == iptr->number_of_channels)) { + implementation = iptr; + break; + } + } + + found: + + if (implementation) { + switch_status_t status; + codec->codec_interface = codec_interface; + codec->implementation = implementation; + codec->flags = flags; + + if (pool) { + codec->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&codec->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(codec, SWITCH_CODEC_FLAG_FREE_POOL); + } + + if (fmtp) { + codec->fmtp_in = switch_core_strdup(codec->memory_pool, fmtp); + } + + implementation->init(codec, flags, codec_settings); + + return SWITCH_STATUS_SUCCESS; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Codec %s Exists but not at the desired implementation. %dhz %dms\n", codec_name, rate, ms); + } + + return SWITCH_STATUS_NOTIMPL; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_codec_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, + void *encoded_data, + uint32_t * encoded_data_len, uint32_t * encoded_rate, + unsigned int *flag) +{ + assert(codec != NULL); + assert(encoded_data != NULL); + assert(decoded_data != NULL); + + if (!codec->implementation) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + if (!switch_test_flag(codec, SWITCH_CODEC_FLAG_ENCODE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec's encoder is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + + return codec->implementation->encode(codec, + other_codec, + decoded_data, + decoded_data_len, + decoded_rate, encoded_data, encoded_data_len, encoded_rate, flag); + +} + +SWITCH_DECLARE(switch_status_t) switch_core_codec_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, + void *decoded_data, + uint32_t * decoded_data_len, + uint32_t * decoded_rate, unsigned int *flag) +{ + + assert(codec != NULL); + assert(encoded_data != NULL); + assert(decoded_data != NULL); + + + + if (!codec->implementation) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + if (!switch_test_flag(codec, SWITCH_CODEC_FLAG_DECODE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec's decoder is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + + return codec->implementation->decode(codec, + other_codec, + encoded_data, + encoded_data_len, + encoded_rate, decoded_data, decoded_data_len, decoded_rate, flag); + +} + +SWITCH_DECLARE(switch_status_t) switch_core_codec_destroy(switch_codec_t *codec) +{ + assert(codec != NULL); + + if (!codec->implementation) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Codec is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + codec->implementation->destroy(codec); + + if (switch_test_flag(codec, SWITCH_CODEC_FLAG_FREE_POOL)) { + switch_core_destroy_memory_pool(&codec->memory_pool); + } + + return SWITCH_STATUS_SUCCESS; +} diff --git a/src/switch_core_directory.c b/src/switch_core_directory.c new file mode 100644 index 0000000000..90e0159e56 --- /dev/null +++ b/src/switch_core_directory.c @@ -0,0 +1,107 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_directory.c -- Main Core Library (Directory Interface) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_directory_open(switch_directory_handle_t *dh, + char *module_name, + char *source, + char *dsn, char *passwd, switch_memory_pool_t *pool) +{ + switch_status_t status; + + if ((dh->directory_interface = switch_loadable_module_get_directory_interface(module_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid directory module [%s]!\n", module_name); + return SWITCH_STATUS_GENERR; + } + + if (pool) { + dh->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&dh->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(dh, SWITCH_DIRECTORY_FLAG_FREE_POOL); + } + + return dh->directory_interface->directory_open(dh, source, dsn, passwd); +} + +SWITCH_DECLARE(switch_status_t) switch_core_directory_query(switch_directory_handle_t *dh, char *base, char *query) +{ + return dh->directory_interface->directory_query(dh, base, query); +} + +SWITCH_DECLARE(switch_status_t) switch_core_directory_next(switch_directory_handle_t *dh) +{ + return dh->directory_interface->directory_next(dh); +} + +SWITCH_DECLARE(switch_status_t) switch_core_directory_next_pair(switch_directory_handle_t *dh, char **var, char **val) +{ + return dh->directory_interface->directory_next_pair(dh, var, val); +} + +SWITCH_DECLARE(switch_status_t) switch_core_directory_close(switch_directory_handle_t *dh) +{ + return dh->directory_interface->directory_close(dh); +} + +SWITCH_DECLARE(switch_status_t) switch_core_speech_open(switch_speech_handle_t *sh, + char *module_name, + char *voice_name, + unsigned int rate, + switch_speech_flag_t *flags, switch_memory_pool_t *pool) +{ + switch_status_t status; + + if ((sh->speech_interface = switch_loadable_module_get_speech_interface(module_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid speech module [%s]!\n", module_name); + return SWITCH_STATUS_GENERR; + } + + switch_copy_string(sh->engine, module_name, sizeof(sh->engine)); + sh->flags = *flags; + if (pool) { + sh->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&sh->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(sh, SWITCH_SPEECH_FLAG_FREE_POOL); + } + sh->rate = rate; + sh->name = switch_core_strdup(pool, module_name); + return sh->speech_interface->speech_open(sh, voice_name, rate, flags); +} diff --git a/src/switch_core_event_hook.c b/src/switch_core_event_hook.c new file mode 100644 index 0000000000..1fec028f25 --- /dev/null +++ b/src/switch_core_event_hook.c @@ -0,0 +1,239 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * switch_core_event_hook.c Core Event Hooks + * + */ +#include "switch.h" +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_outgoing(switch_core_session_t *session, + switch_outgoing_channel_hook_t outgoing_channel) +{ + switch_io_event_hook_outgoing_channel_t *hook, *ptr; + + assert(outgoing_channel != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->outgoing_channel = outgoing_channel; + if (!session->event_hooks.outgoing_channel) { + session->event_hooks.outgoing_channel = hook; + } else { + for (ptr = session->event_hooks.outgoing_channel; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_answer_channel(switch_core_session_t *session, + switch_answer_channel_hook_t answer_channel) +{ + switch_io_event_hook_answer_channel_t *hook, *ptr; + + assert(answer_channel != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->answer_channel = answer_channel; + if (!session->event_hooks.answer_channel) { + session->event_hooks.answer_channel = hook; + } else { + for (ptr = session->event_hooks.answer_channel; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_state_change(switch_core_session_t *session, + switch_answer_channel_hook_t state_change) +{ + switch_io_event_hook_state_change_t *hook, *ptr; + + assert(state_change != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->state_change = state_change; + if (!session->event_hooks.state_change) { + session->event_hooks.state_change = hook; + } else { + for (ptr = session->event_hooks.state_change; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_read_frame(switch_core_session_t *session, + switch_read_frame_hook_t read_frame) +{ + switch_io_event_hook_read_frame_t *hook, *ptr; + + assert(read_frame != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->read_frame = read_frame; + if (!session->event_hooks.read_frame) { + session->event_hooks.read_frame = hook; + } else { + for (ptr = session->event_hooks.read_frame; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_write_frame(switch_core_session_t *session, + switch_write_frame_hook_t write_frame) +{ + switch_io_event_hook_write_frame_t *hook, *ptr; + + assert(write_frame != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->write_frame = write_frame; + if (!session->event_hooks.write_frame) { + session->event_hooks.write_frame = hook; + } else { + for (ptr = session->event_hooks.write_frame; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_kill_channel(switch_core_session_t *session, + switch_kill_channel_hook_t kill_channel) +{ + switch_io_event_hook_kill_channel_t *hook, *ptr; + + assert(kill_channel != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->kill_channel = kill_channel; + if (!session->event_hooks.kill_channel) { + session->event_hooks.kill_channel = hook; + } else { + for (ptr = session->event_hooks.kill_channel; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_waitfor_read(switch_core_session_t *session, + switch_waitfor_read_hook_t waitfor_read) +{ + switch_io_event_hook_waitfor_read_t *hook, *ptr; + + assert(waitfor_read != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->waitfor_read = waitfor_read; + if (!session->event_hooks.waitfor_read) { + session->event_hooks.waitfor_read = hook; + } else { + for (ptr = session->event_hooks.waitfor_read; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_waitfor_write(switch_core_session_t *session, + switch_waitfor_write_hook_t waitfor_write) +{ + switch_io_event_hook_waitfor_write_t *hook, *ptr; + + assert(waitfor_write != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->waitfor_write = waitfor_write; + if (!session->event_hooks.waitfor_write) { + session->event_hooks.waitfor_write = hook; + } else { + for (ptr = session->event_hooks.waitfor_write; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} + + +SWITCH_DECLARE(switch_status_t) switch_core_event_hook_add_send_dtmf(switch_core_session_t *session, + switch_send_dtmf_hook_t send_dtmf) +{ + switch_io_event_hook_send_dtmf_t *hook, *ptr; + + assert(send_dtmf != NULL); + if ((hook = switch_core_session_alloc(session, sizeof(*hook))) != 0) { + hook->send_dtmf = send_dtmf; + if (!session->event_hooks.send_dtmf) { + session->event_hooks.send_dtmf = hook; + } else { + for (ptr = session->event_hooks.send_dtmf; ptr && ptr->next; ptr = ptr->next); + ptr->next = hook; + + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; + +} diff --git a/src/switch_core_file.c b/src/switch_core_file.c new file mode 100644 index 0000000000..8611326634 --- /dev/null +++ b/src/switch_core_file.c @@ -0,0 +1,143 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_file.c -- Main Core Library (File I/O Functions) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_file_open(switch_file_handle_t *fh, + char *file_path, + uint8_t channels, + uint32_t rate, unsigned int flags, switch_memory_pool_t *pool) +{ + char *ext; + switch_status_t status; + char stream_name[128] = ""; + char *rhs = NULL; + + if ((rhs = strstr(file_path, SWITCH_URL_SEPARATOR))) { + switch_copy_string(stream_name, file_path, (rhs + 1) - file_path); + ext = stream_name; + file_path = rhs + 3; + } else { + if ((ext = strrchr(file_path, '.')) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Format\n"); + return SWITCH_STATUS_FALSE; + } + ext++; + } + + if ((fh->file_interface = switch_loadable_module_get_file_interface(ext)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid file format [%s]!\n", ext); + return SWITCH_STATUS_GENERR; + } + + fh->flags = flags; + if (pool) { + fh->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&fh->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(fh, SWITCH_FILE_FLAG_FREE_POOL); + } + + if (rhs) { + fh->handler = switch_core_strdup(fh->memory_pool, rhs); + } + + if (rate) { + fh->samplerate = rate; + } else { + rate = 8000; + } + + if (channels) { + fh->channels = channels; + } else { + fh->channels = 1; + } + + if ((status = fh->file_interface->file_open(fh, file_path)) == SWITCH_STATUS_SUCCESS) { + switch_set_flag(fh, SWITCH_FILE_OPEN); + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, void *data, switch_size_t *len) +{ + assert(fh != NULL); + + return fh->file_interface->file_read(fh, data, len); +} + +SWITCH_DECLARE(switch_status_t) switch_core_file_write(switch_file_handle_t *fh, void *data, switch_size_t *len) +{ + assert(fh != NULL); + + return fh->file_interface->file_write(fh, data, len); +} + +SWITCH_DECLARE(switch_status_t) switch_core_file_seek(switch_file_handle_t *fh, unsigned int *cur_pos, int64_t samples, + int whence) +{ + assert(fh != NULL); + + switch_set_flag(fh, SWITCH_FILE_SEEK); + return fh->file_interface->file_seek(fh, cur_pos, samples, whence); +} + +SWITCH_DECLARE(switch_status_t) switch_core_file_set_string(switch_file_handle_t *fh, switch_audio_col_t col, + const char *string) +{ + assert(fh != NULL); + + return fh->file_interface->file_set_string(fh, col, string); +} + +SWITCH_DECLARE(switch_status_t) switch_core_file_get_string(switch_file_handle_t *fh, switch_audio_col_t col, + const char **string) +{ + assert(fh != NULL); + + return fh->file_interface->file_get_string(fh, col, string); + +} + + +SWITCH_DECLARE(switch_status_t) switch_core_file_close(switch_file_handle_t *fh) +{ + switch_clear_flag(fh, SWITCH_FILE_OPEN); + return fh->file_interface->file_close(fh); + +} diff --git a/src/switch_core_hash.c b/src/switch_core_hash.c new file mode 100644 index 0000000000..afd0c5d4ab --- /dev/null +++ b/src/switch_core_hash.c @@ -0,0 +1,75 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_hash.c -- Main Core Library (hash functions) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_hash_init(switch_hash_t **hash, switch_memory_pool_t *pool) +{ + assert(pool != NULL); + + if ((*hash = apr_hash_make(pool)) != 0) { + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_GENERR; +} + +SWITCH_DECLARE(switch_status_t) switch_core_hash_destroy(switch_hash_t *hash) +{ + assert(hash != NULL); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_core_hash_insert_dup(switch_hash_t *hash, const char *key, const void *data) +{ + apr_hash_set(hash, switch_core_strdup(apr_hash_pool_get(hash), key), APR_HASH_KEY_STRING, data); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_core_hash_insert(switch_hash_t *hash, const char *key, const void *data) +{ + apr_hash_set(hash, key, APR_HASH_KEY_STRING, data); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_core_hash_delete(switch_hash_t *hash, const char *key) +{ + apr_hash_set(hash, key, APR_HASH_KEY_STRING, NULL); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(void *) switch_core_hash_find(switch_hash_t *hash, const char *key) +{ + return apr_hash_get(hash, key, APR_HASH_KEY_STRING); +} diff --git a/src/switch_core_io.c b/src/switch_core_io.c new file mode 100644 index 0000000000..4d1e7e91f1 --- /dev/null +++ b/src/switch_core_io.c @@ -0,0 +1,777 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_io.c -- Main Core Library (Media I/O) + * + */ +#include +#include "private/switch_core.h" + + +SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_session_t *session, switch_frame_t **frame, + int timeout, int stream_id) +{ + switch_io_event_hook_read_frame_t *ptr; + switch_status_t status; + int need_codec, perfect, do_bugs = 0; + unsigned int flag = 0; + top: + + status = SWITCH_STATUS_FALSE; + need_codec = perfect = 0; + + assert(session != NULL); + *frame = NULL; + + if (switch_channel_test_flag(session->channel, CF_HOLD)) { + status = SWITCH_STATUS_BREAK; + goto done; + } + + if (session->endpoint_interface->io_routines->read_frame) { + if ((status = session->endpoint_interface->io_routines->read_frame(session, + frame, + timeout, + SWITCH_IO_FLAG_NOOP, + stream_id)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.read_frame; ptr; ptr = ptr->next) { + if ((status = + ptr->read_frame(session, frame, timeout, SWITCH_IO_FLAG_NOOP, + stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + + if (status != SWITCH_STATUS_SUCCESS) { + goto done; + } + + if (!(*frame)) { + goto done; + } + + assert(session != NULL); + assert(*frame != NULL); + + if (switch_test_flag(*frame, SFF_CNG)) { + status = SWITCH_STATUS_SUCCESS; + goto done; + } + + if ((session->read_codec && (*frame)->codec + && session->read_codec->implementation != (*frame)->codec->implementation)) { + need_codec = TRUE; + } + + if (session->read_codec && !(*frame)->codec) { + need_codec = TRUE; + } + + if (!session->read_codec && (*frame)->codec) { + status = SWITCH_STATUS_FALSE; + goto done; + } + + if (session->bugs && !need_codec) { + do_bugs = 1; + need_codec = 1; + } + + if (status == SWITCH_STATUS_SUCCESS && need_codec) { + switch_frame_t *enc_frame, *read_frame = *frame; + + if (read_frame->codec) { + session->raw_read_frame.datalen = session->raw_read_frame.buflen; + status = switch_core_codec_decode(read_frame->codec, + session->read_codec, + read_frame->data, + read_frame->datalen, + session->read_codec->implementation->samples_per_second, + session->raw_read_frame.data, + &session->raw_read_frame.datalen, &session->raw_read_frame.rate, &flag); + + switch (status) { + case SWITCH_STATUS_RESAMPLE: + if (!session->read_resampler) { + if (switch_resample_create(&session->read_resampler, + read_frame->codec->implementation->samples_per_second, + read_frame->codec->implementation->bytes_per_frame * 20, + session->read_codec->implementation->samples_per_second, + session->read_codec->implementation->bytes_per_frame * 20, + session->pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to allocate resampler\n"); + status = SWITCH_STATUS_FALSE; + goto done; + } + } + case SWITCH_STATUS_SUCCESS: + session->raw_read_frame.samples = session->raw_read_frame.datalen / sizeof(int16_t); + session->raw_read_frame.rate = read_frame->rate; + session->raw_read_frame.timestamp = read_frame->timestamp; + read_frame = &session->raw_read_frame; + break; + case SWITCH_STATUS_NOOP: + status = SWITCH_STATUS_SUCCESS; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec %s decoder error!\n", + session->read_codec->codec_interface->interface_name); + goto done; + } + } + if (session->read_resampler) { + short *data = read_frame->data; + + session->read_resampler->from_len = + switch_short_to_float(data, session->read_resampler->from, (int) read_frame->datalen / 2); + session->read_resampler->to_len = + switch_resample_process(session->read_resampler, session->read_resampler->from, + session->read_resampler->from_len, session->read_resampler->to, + session->read_resampler->to_size, 0); + switch_float_to_short(session->read_resampler->to, data, read_frame->datalen); + read_frame->samples = session->read_resampler->to_len; + read_frame->datalen = session->read_resampler->to_len * 2; + read_frame->rate = session->read_resampler->to_rate; + } + + if (session->bugs) { + switch_media_bug_t *bp, *dp, *last = NULL; + + switch_thread_rwlock_rdlock(session->bug_rwlock); + for (bp = session->bugs; bp; bp = bp->next) { + if (bp->ready && switch_test_flag(bp, SMBF_READ_STREAM)) { + switch_mutex_lock(bp->read_mutex); + switch_buffer_write(bp->raw_read_buffer, read_frame->data, read_frame->datalen); + if (bp->callback) { + if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ) == SWITCH_FALSE) { + bp->ready = 0; + if (last) { + last->next = bp->next; + } else { + session->bugs = bp->next; + } + switch_mutex_unlock(bp->read_mutex); + dp = bp; + bp = last; + switch_core_media_bug_close(&dp); + if (!bp) { + break; + } + continue; + } + } + switch_mutex_unlock(bp->read_mutex); + } + last = bp; + } + switch_thread_rwlock_unlock(session->bug_rwlock); + } + + if (do_bugs) { + goto done; + } + + if (session->read_codec) { + if ((*frame)->datalen == session->read_codec->implementation->bytes_per_frame) { + perfect = TRUE; + } else { + if (!session->raw_read_buffer) { + switch_size_t bytes = session->read_codec->implementation->bytes_per_frame; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Engaging Read Buffer at %u bytes\n", + (uint32_t) bytes); + switch_buffer_create_dynamic(&session->raw_read_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, + bytes * SWITCH_BUFFER_START_FRAMES, 0); + } + if (!switch_buffer_write(session->raw_read_buffer, read_frame->data, read_frame->datalen)) { + status = SWITCH_STATUS_MEMERR; + goto done; + } + } + + if (perfect + || switch_buffer_inuse(session->raw_read_buffer) >= + session->read_codec->implementation->bytes_per_frame) { + if (perfect) { + enc_frame = *frame; + session->raw_read_frame.rate = (*frame)->rate; + } else { + session->raw_read_frame.datalen = (uint32_t) switch_buffer_read(session->raw_read_buffer, + session->raw_read_frame.data, + session->read_codec-> + implementation->bytes_per_frame); + + session->raw_read_frame.rate = session->read_codec->implementation->samples_per_second; + enc_frame = &session->raw_read_frame; + } + session->enc_read_frame.datalen = session->enc_read_frame.buflen; + assert(session->read_codec != NULL); + assert(enc_frame != NULL); + assert(enc_frame->data != NULL); + + status = switch_core_codec_encode(session->read_codec, + enc_frame->codec, + enc_frame->data, + enc_frame->datalen, + session->read_codec->implementation->samples_per_second, + session->enc_read_frame.data, + &session->enc_read_frame.datalen, + &session->enc_read_frame.rate, &flag); + + + switch (status) { + case SWITCH_STATUS_RESAMPLE: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "fixme 1\n"); + case SWITCH_STATUS_SUCCESS: + session->enc_read_frame.codec = session->read_codec; + session->enc_read_frame.samples = + session->read_codec->implementation->bytes_per_frame / sizeof(int16_t); + session->enc_read_frame.timestamp = read_frame->timestamp; + session->enc_read_frame.payload = session->read_codec->implementation->ianacode; + *frame = &session->enc_read_frame; + break; + case SWITCH_STATUS_NOOP: + session->raw_read_frame.codec = session->read_codec; + session->raw_read_frame.samples = enc_frame->codec->implementation->samples_per_frame; + session->raw_read_frame.timestamp = read_frame->timestamp; + session->raw_read_frame.payload = enc_frame->codec->implementation->ianacode; + *frame = &session->raw_read_frame; + status = SWITCH_STATUS_SUCCESS; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec %s encoder error!\n", + session->read_codec->codec_interface->interface_name); + *frame = NULL; + status = SWITCH_STATUS_GENERR; + break; + } + } else { + goto top; + } + } + } + + done: + if (!(*frame)) { + status = SWITCH_STATUS_FALSE; + } else { + if (flag & SFF_CNG) { + switch_set_flag((*frame), SFF_CNG); + } + } + + return status; +} + +static switch_status_t perform_write(switch_core_session_t *session, switch_frame_t *frame, int timeout, + switch_io_flag_t flags, int stream_id) +{ + switch_io_event_hook_write_frame_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (session->endpoint_interface->io_routines->write_frame) { + + if ((status = + session->endpoint_interface->io_routines->write_frame(session, frame, timeout, flags, + stream_id)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.write_frame; ptr; ptr = ptr->next) { + if ((status = ptr->write_frame(session, frame, timeout, flags, stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_session_t *session, switch_frame_t *frame, + int timeout, int stream_id) +{ + + switch_status_t status = SWITCH_STATUS_FALSE; + switch_frame_t *enc_frame = NULL, *write_frame = frame; + unsigned int flag = 0, need_codec = 0, perfect = 0, do_bugs = 0, do_write = 0; + switch_io_flag_t io_flag = SWITCH_IO_FLAG_NOOP; + + assert(session != NULL); + assert(frame != NULL); + + + if (switch_channel_test_flag(session->channel, CF_HOLD)) { + return SWITCH_STATUS_SUCCESS; + } + + if (switch_test_flag(frame, SFF_CNG)) { + + if (switch_channel_test_flag(session->channel, CF_ACCEPT_CNG)) { + return perform_write(session, frame, timeout, flag, stream_id); + } + + return SWITCH_STATUS_SUCCESS; + } + + assert(frame->codec != NULL); + + if ((session->write_codec && frame->codec && session->write_codec->implementation != frame->codec->implementation)) { + need_codec = TRUE; + } + + if (session->write_codec && !frame->codec) { + need_codec = TRUE; + } + + if (!session->write_codec && frame->codec) { + return SWITCH_STATUS_FALSE; + } + + if (session->bugs && !need_codec) { + do_bugs = 1; + need_codec = 1; + } + + if (need_codec) { + if (frame->codec) { + session->raw_write_frame.datalen = session->raw_write_frame.buflen; + status = switch_core_codec_decode(frame->codec, + session->write_codec, + frame->data, + frame->datalen, + session->write_codec->implementation->samples_per_second, + session->raw_write_frame.data, + &session->raw_write_frame.datalen, &session->raw_write_frame.rate, &flag); + + + switch (status) { + case SWITCH_STATUS_RESAMPLE: + write_frame = &session->raw_write_frame; + if (!session->write_resampler) { + status = switch_resample_create(&session->write_resampler, + frame->codec->implementation->samples_per_second, + frame->codec->implementation->bytes_per_frame * 20, + session->write_codec->implementation->samples_per_second, + session->write_codec->implementation->bytes_per_frame * 20, + session->pool); + if (status != SWITCH_STATUS_SUCCESS) { + goto done; + } + } + break; + case SWITCH_STATUS_SUCCESS: + session->raw_write_frame.samples = session->raw_write_frame.datalen / sizeof(int16_t); + session->raw_write_frame.timestamp = frame->timestamp; + session->raw_write_frame.rate = frame->rate; + write_frame = &session->raw_write_frame; + break; + case SWITCH_STATUS_BREAK: + return SWITCH_STATUS_SUCCESS; + case SWITCH_STATUS_NOOP: + write_frame = frame; + status = SWITCH_STATUS_SUCCESS; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec %s decoder error!\n", + frame->codec->codec_interface->interface_name); + return status; + } + } + if (session->write_resampler) { + short *data = write_frame->data; + + session->write_resampler->from_len = write_frame->datalen / 2; + switch_short_to_float(data, session->write_resampler->from, session->write_resampler->from_len); + + + + session->write_resampler->to_len = (uint32_t) + switch_resample_process(session->write_resampler, session->write_resampler->from, + session->write_resampler->from_len, session->write_resampler->to, + session->write_resampler->to_size, 0); + + + switch_float_to_short(session->write_resampler->to, data, session->write_resampler->to_len); + + write_frame->samples = session->write_resampler->to_len; + write_frame->datalen = write_frame->samples * 2; + write_frame->rate = session->write_resampler->to_rate; + } + + if (session->bugs) { + switch_media_bug_t *bp, *dp, *last = NULL; + + switch_thread_rwlock_rdlock(session->bug_rwlock); + for (bp = session->bugs; bp; bp = bp->next) { + switch_bool_t ok = SWITCH_TRUE; + if (!bp->ready) { + continue; + } + if (switch_test_flag(bp, SMBF_WRITE_STREAM)) { + switch_mutex_lock(bp->write_mutex); + switch_buffer_write(bp->raw_write_buffer, write_frame->data, write_frame->datalen); + switch_mutex_unlock(bp->write_mutex); + if (bp->callback) { + ok = bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE); + } + } else if (switch_test_flag(bp, SMBF_WRITE_REPLACE)) { + do_bugs = 0; + if (bp->callback) { + bp->replace_frame_in = frame; + bp->replace_frame_out = NULL; + if ((ok = bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE_REPLACE)) == SWITCH_TRUE) { + write_frame = bp->replace_frame_out; + } + } + } + + if (ok == SWITCH_FALSE) { + bp->ready = 0; + if (last) { + last->next = bp->next; + } else { + session->bugs = bp->next; + } + dp = bp; + bp = last; + switch_core_media_bug_close(&dp); + if (!bp) { + break; + } + continue; + } + last = bp; + } + switch_thread_rwlock_unlock(session->bug_rwlock); + } + + if (do_bugs) { + do_write = 1; + write_frame = frame; + goto done; + } + + if (session->write_codec) { + if (write_frame->datalen == session->write_codec->implementation->bytes_per_frame) { + perfect = TRUE; + } else { + if (!session->raw_write_buffer) { + switch_size_t bytes = session->write_codec->implementation->bytes_per_frame; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Engaging Write Buffer at %u bytes to accomodate %u->%u\n", + (uint32_t) bytes, + write_frame->datalen, session->write_codec->implementation->bytes_per_frame); + if ((status = switch_buffer_create_dynamic(&session->raw_write_buffer, + bytes * SWITCH_BUFFER_BLOCK_FRAMES, + bytes * SWITCH_BUFFER_START_FRAMES, + 0)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Write Buffer Failed!\n"); + return status; + } + } + + if (!(switch_buffer_write(session->raw_write_buffer, write_frame->data, write_frame->datalen))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Write Buffer %u bytes Failed!\n", + write_frame->datalen); + return SWITCH_STATUS_MEMERR; + } + } + + + if (perfect) { + enc_frame = write_frame; + session->enc_write_frame.datalen = session->enc_write_frame.buflen; + + status = switch_core_codec_encode(session->write_codec, + frame->codec, + enc_frame->data, + enc_frame->datalen, + session->write_codec->implementation->samples_per_second, + session->enc_write_frame.data, + &session->enc_write_frame.datalen, + &session->enc_write_frame.rate, &flag); + + switch (status) { + case SWITCH_STATUS_RESAMPLE: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "fixme 2\n"); + case SWITCH_STATUS_SUCCESS: + session->enc_write_frame.codec = session->write_codec; + session->enc_write_frame.samples = enc_frame->datalen / sizeof(int16_t); + session->enc_write_frame.timestamp = frame->timestamp; + session->enc_write_frame.payload = session->write_codec->implementation->ianacode; + write_frame = &session->enc_write_frame; + break; + case SWITCH_STATUS_NOOP: + enc_frame->codec = session->write_codec; + enc_frame->samples = enc_frame->datalen / sizeof(int16_t); + enc_frame->timestamp = frame->timestamp; + enc_frame->payload = enc_frame->codec->implementation->ianacode; + write_frame = enc_frame; + status = SWITCH_STATUS_SUCCESS; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec %s encoder error!\n", + session->read_codec->codec_interface->interface_name); + write_frame = NULL; + return status; + } + if (flag & SFF_CNG) { + switch_set_flag(write_frame, SFF_CNG); + } + status = perform_write(session, write_frame, timeout, io_flag, stream_id); + return status; + } else { + switch_size_t used = switch_buffer_inuse(session->raw_write_buffer); + uint32_t bytes = session->write_codec->implementation->bytes_per_frame; + switch_size_t frames = (used / bytes); + + status = SWITCH_STATUS_SUCCESS; + if (!frames) { + return status; + } else { + switch_size_t x; + for (x = 0; x < frames; x++) { + if ((session->raw_write_frame.datalen = (uint32_t) + switch_buffer_read(session->raw_write_buffer, session->raw_write_frame.data, bytes)) != 0) { + enc_frame = &session->raw_write_frame; + session->raw_write_frame.rate = session->write_codec->implementation->samples_per_second; + session->enc_write_frame.datalen = session->enc_write_frame.buflen; + + + status = switch_core_codec_encode(session->write_codec, + frame->codec, + enc_frame->data, + enc_frame->datalen, + frame->codec->implementation->samples_per_second, + session->enc_write_frame.data, + &session->enc_write_frame.datalen, + &session->enc_write_frame.rate, &flag); + + + switch (status) { + case SWITCH_STATUS_RESAMPLE: + session->enc_write_frame.codec = session->write_codec; + session->enc_write_frame.samples = enc_frame->datalen / sizeof(int16_t); + session->enc_write_frame.timestamp = frame->timestamp; + session->enc_write_frame.payload = session->write_codec->implementation->ianacode; + write_frame = &session->enc_write_frame; + if (!session->read_resampler) { + status = switch_resample_create(&session->read_resampler, + frame->codec->implementation->samples_per_second, + frame->codec->implementation->bytes_per_frame * 20, + session->write_codec->implementation-> + samples_per_second, + session->write_codec->implementation-> + bytes_per_frame * 20, session->pool); + if (status != SWITCH_STATUS_SUCCESS) { + goto done; + } + } + break; + case SWITCH_STATUS_SUCCESS: + session->enc_write_frame.codec = session->write_codec; + session->enc_write_frame.samples = enc_frame->datalen / sizeof(int16_t); + session->enc_write_frame.timestamp = frame->timestamp; + session->enc_write_frame.payload = session->write_codec->implementation->ianacode; + write_frame = &session->enc_write_frame; + break; + case SWITCH_STATUS_NOOP: + enc_frame->codec = session->write_codec; + enc_frame->samples = enc_frame->datalen / sizeof(int16_t); + enc_frame->timestamp = frame->timestamp; + enc_frame->payload = enc_frame->codec->implementation->ianacode; + write_frame = enc_frame; + status = SWITCH_STATUS_SUCCESS; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec %s encoder error %d!\n", + session->read_codec->codec_interface->interface_name, status); + write_frame = NULL; + return status; + } + + if (session->read_resampler) { + short *data = write_frame->data; + + session->read_resampler->from_len = switch_short_to_float(data, + session->read_resampler->from, + (int) write_frame->datalen / + 2); + session->read_resampler->to_len = (uint32_t) + switch_resample_process(session->read_resampler, session->read_resampler->from, + session->read_resampler->from_len, + session->read_resampler->to, + session->read_resampler->to_size, 0); + switch_float_to_short(session->read_resampler->to, data, write_frame->datalen * 2); + write_frame->samples = session->read_resampler->to_len; + write_frame->datalen = session->read_resampler->to_len * 2; + write_frame->rate = session->read_resampler->to_rate; + } + if (flag & SFF_CNG) { + switch_set_flag(write_frame, SFF_CNG); + } + if ((status = + perform_write(session, write_frame, timeout, io_flag, + stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + return status; + } + } + } + } else { + do_write = 1; + } + + done: + if (do_write) { + return perform_write(session, frame, timeout, io_flag, stream_id); + } + + return status; +} + +static char *SIG_NAMES[] = { + "NONE", + "KILL", + "XFER", + "BREAK", + NULL +}; + +SWITCH_DECLARE(switch_status_t) switch_core_session_perform_kill_channel(switch_core_session_t *session, + const char *file, + const char *func, + int line, switch_signal_t sig) +{ + switch_io_event_hook_kill_channel_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, SWITCH_LOG_INFO, "Kill %s [%s]\n", + switch_channel_get_name(session->channel), SIG_NAMES[sig]); + + if (session->endpoint_interface->io_routines->kill_channel) { + if ((status = session->endpoint_interface->io_routines->kill_channel(session, sig)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.kill_channel; ptr; ptr = ptr->next) { + if ((status = ptr->kill_channel(session, sig)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + + return status; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_waitfor_read(switch_core_session_t *session, int timeout, + int stream_id) +{ + switch_io_event_hook_waitfor_read_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (session->endpoint_interface->io_routines->waitfor_read) { + if ((status = + session->endpoint_interface->io_routines->waitfor_read(session, timeout, + stream_id)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.waitfor_read; ptr; ptr = ptr->next) { + if ((status = ptr->waitfor_read(session, timeout, stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + + return status; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_waitfor_write(switch_core_session_t *session, int timeout, + int stream_id) +{ + switch_io_event_hook_waitfor_write_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (session->endpoint_interface->io_routines->waitfor_write) { + if ((status = + session->endpoint_interface->io_routines->waitfor_write(session, timeout, + stream_id)) == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.waitfor_write; ptr; ptr = ptr->next) { + if ((status = ptr->waitfor_write(session, timeout, stream_id)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + + return status; +} + + +SWITCH_DECLARE(switch_status_t) switch_core_session_send_dtmf(switch_core_session_t *session, char *dtmf) +{ + switch_io_event_hook_send_dtmf_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (session->endpoint_interface->io_routines->send_dtmf) { + if (strchr(dtmf, 'w') || strchr(dtmf, 'W')) { + char *d; + for (d = dtmf; d && *d; d++) { + char digit[2] = { 0 }; + + if (*d == 'w') { + switch_yield(500000); + continue; + } else if (*d == 'W') { + switch_yield(1000000); + continue; + } + + digit[0] = *d; + if ((status = + session->endpoint_interface->io_routines->send_dtmf(session, digit)) != SWITCH_STATUS_SUCCESS) { + return status; + } + } + } else { + status = session->endpoint_interface->io_routines->send_dtmf(session, dtmf); + } + + if (status == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.send_dtmf; ptr; ptr = ptr->next) { + if ((status = ptr->send_dtmf(session, dtmf)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } + + return status; +} diff --git a/src/switch_core_media_bug.c b/src/switch_core_media_bug.c new file mode 100644 index 0000000000..f1e22085ba --- /dev/null +++ b/src/switch_core_media_bug.c @@ -0,0 +1,279 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_media_bug.c -- Main Core Library (Media Bugs) + * + */ +#include "switch.h" +#include "private/switch_core.h" + +static void switch_core_media_bug_destroy(switch_media_bug_t *bug) +{ + switch_buffer_destroy(&bug->raw_read_buffer); + switch_buffer_destroy(&bug->raw_write_buffer); +} + + +SWITCH_DECLARE(switch_frame_t *) switch_core_media_bug_get_replace_frame(switch_media_bug_t *bug) +{ + return bug->replace_frame_in; +} + +SWITCH_DECLARE(void) switch_core_media_bug_set_replace_frame(switch_media_bug_t *bug, switch_frame_t *frame) +{ + bug->replace_frame_out = frame; +} + +SWITCH_DECLARE(void *) switch_core_media_bug_get_user_data(switch_media_bug_t *bug) +{ + return bug->user_data; +} + +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame) +{ + uint32_t bytes = 0; + uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; + uint32_t datalen = 0; + int16_t *dp, *fp; + uint32_t x; + size_t rlen = 0; + size_t wlen = 0; + uint32_t blen; + size_t rdlen = 0; + uint32_t maxlen; + switch_codec_t *read_codec = switch_core_session_get_read_codec(bug->session); + + if (bug->raw_read_buffer) { + rlen = switch_buffer_inuse(bug->raw_read_buffer); + } + + if (bug->raw_write_buffer) { + wlen = switch_buffer_inuse(bug->raw_write_buffer); + } + + if ((bug->raw_read_buffer && bug->raw_write_buffer) && (!rlen && !wlen)) { + return SWITCH_STATUS_FALSE; + } + + + maxlen = sizeof(data) > frame->buflen ? frame->buflen : sizeof(data); + if ((rdlen = rlen > wlen ? wlen : rlen) > maxlen) { + rdlen = maxlen; + } + + if (!rdlen) { + rdlen = maxlen; + } + + frame->datalen = 0; + + if (rlen) { + switch_mutex_lock(bug->read_mutex); + + frame->datalen = (uint32_t) switch_buffer_read(bug->raw_read_buffer, frame->data, rdlen); + switch_mutex_unlock(bug->read_mutex); + } + + if (wlen) { + switch_mutex_lock(bug->write_mutex); + datalen = (uint32_t) switch_buffer_read(bug->raw_write_buffer, data, rdlen); + switch_mutex_unlock(bug->write_mutex); + } + + + bytes = (datalen > frame->datalen) ? datalen : frame->datalen; + + if (bytes) { + dp = (int16_t *) data; + fp = (int16_t *) frame->data; + + rlen = frame->datalen / 2; + wlen = datalen / 2; + blen = bytes / 2; + + for (x = 0; x < blen; x++) { + int32_t z = 0; + + if (x < rlen) { + z += (int32_t) *(fp + x); + } + if (x < wlen) { + z += (int32_t) *(dp + x); + } + switch_normalize_to_16bit(z); + *(fp + x) = (int16_t) z; + } + + frame->datalen = bytes; + frame->samples = bytes / sizeof(int16_t); + frame->rate = read_codec->implementation->samples_per_second; + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +#define MAX_BUG_BUFFER 1024 * 512 +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session, + switch_media_bug_callback_t callback, + void *user_data, + switch_media_bug_flag_t flags, switch_media_bug_t **new_bug) +{ + switch_media_bug_t *bug, *bp; + switch_size_t bytes; + + if (flags & SMBF_WRITE_REPLACE) { + switch_thread_rwlock_wrlock(session->bug_rwlock); + for (bp = session->bugs; bp; bp = bp->next) { + if (switch_test_flag(bp, SMBF_WRITE_REPLACE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Only one bug of this type allowed!\n"); + switch_thread_rwlock_unlock(session->bug_rwlock); + return SWITCH_STATUS_GENERR; + } + } + switch_thread_rwlock_unlock(session->bug_rwlock); + } + + if (!(bug = switch_core_session_alloc(session, sizeof(*bug)))) { + return SWITCH_STATUS_MEMERR; + } + + bug->callback = callback; + bug->user_data = user_data; + bug->session = session; + bug->flags = flags; + bug->ready = 1; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Attaching BUG to %s\n", + switch_channel_get_name(session->channel)); + bytes = session->read_codec->implementation->bytes_per_frame; + + if (!bug->flags) { + bug->flags = (SMBF_READ_STREAM | SMBF_WRITE_STREAM); + } + + if (switch_test_flag(bug, SMBF_READ_STREAM)) { + switch_buffer_create_dynamic(&bug->raw_read_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, + bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER); + switch_mutex_init(&bug->read_mutex, SWITCH_MUTEX_NESTED, session->pool); + } + + bytes = session->write_codec->implementation->bytes_per_frame; + + if (switch_test_flag(bug, SMBF_WRITE_STREAM)) { + switch_buffer_create_dynamic(&bug->raw_write_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, + bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER); + switch_mutex_init(&bug->write_mutex, SWITCH_MUTEX_NESTED, session->pool); + } + + switch_thread_rwlock_wrlock(session->bug_rwlock); + bug->next = session->bugs; + session->bugs = bug; + switch_thread_rwlock_unlock(session->bug_rwlock); + *new_bug = bug; + + if (bug->callback) { + switch_bool_t result = bug->callback(bug, bug->user_data, SWITCH_ABC_TYPE_INIT); + if (result == SWITCH_FALSE) { + return switch_core_media_bug_remove(session, new_bug); + } + } + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(switch_core_session_t *session) +{ + switch_media_bug_t *bp; + + if (session->bugs) { + switch_thread_rwlock_wrlock(session->bug_rwlock); + for (bp = session->bugs; bp; bp = bp->next) { + if (bp->callback) { + bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE); + } + switch_core_media_bug_destroy(bp); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", + switch_channel_get_name(session->channel)); + } + switch_thread_rwlock_unlock(session->bug_rwlock); + session->bugs = NULL; + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_close(switch_media_bug_t **bug) +{ + switch_media_bug_t *bp = *bug; + if (bp) { + if (bp->callback) { + bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE); + bp->ready = 0; + } + switch_core_media_bug_destroy(bp); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", + switch_channel_get_name(bp->session->channel)); + *bug = NULL; + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove(switch_core_session_t *session, switch_media_bug_t **bug) +{ + switch_media_bug_t *bp = NULL, *last = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (session->bugs) { + switch_thread_rwlock_wrlock(session->bug_rwlock); + for (bp = session->bugs; bp; bp = bp->next) { + if (!bp->ready) { + continue; + } + if (bp == *bug) { + if (last) { + last->next = bp->next; + } else { + session->bugs = bp->next; + } + break; + } + last = bp; + } + switch_thread_rwlock_unlock(session->bug_rwlock); + status = switch_core_media_bug_close(&bp); + } + + return status; +} diff --git a/src/switch_core_memory.c b/src/switch_core_memory.c new file mode 100644 index 0000000000..a773a15044 --- /dev/null +++ b/src/switch_core_memory.c @@ -0,0 +1,221 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_memory.c -- Main Core Library (memory management) + * + */ +#include +#include "private/switch_core.h" + +static struct { + switch_memory_pool_t *memory_pool; +} runtime; + +SWITCH_DECLARE(switch_memory_pool_t *) switch_core_session_get_pool(switch_core_session_t *session) +{ + return session->pool; +} + +/* **ONLY** alloc things with this function that **WILL NOT** outlive + the session itself or expect an earth shattering KABOOM!*/ +SWITCH_DECLARE(void *) switch_core_session_alloc(switch_core_session_t *session, switch_size_t memory) +{ + void *ptr = NULL; + assert(session != NULL); + assert(session->pool != NULL); + +#ifdef DEBUG_ALLOC + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Allocate %d\n", memory); +#endif + + + if ((ptr = apr_palloc(session->pool, memory)) != 0) { + memset(ptr, 0, memory); + } + return ptr; +} + +/* **ONLY** alloc things with these functions that **WILL NOT** need + to be freed *EVER* ie this is for *PERMANENT* memory allocation */ + +SWITCH_DECLARE(void *) switch_core_permanent_alloc(switch_size_t memory) +{ + void *ptr = NULL; + assert(runtime.memory_pool != NULL); + + +#ifdef DEBUG_ALLOC + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Perm Allocate %d\n", memory); +#endif + + if ((ptr = apr_palloc(runtime.memory_pool, memory)) != 0) { + memset(ptr, 0, memory); + } + return ptr; +} + +SWITCH_DECLARE(char *) switch_core_permanent_strdup(const char *todup) +{ + char *duped = NULL; + switch_size_t len; + assert(runtime.memory_pool != NULL); + + if (!todup) + return NULL; + + len = strlen(todup) + 1; + +#ifdef DEBUG_ALLOC + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Perm Allocate %d\n", len); +#endif + + if (todup && (duped = apr_palloc(runtime.memory_pool, len)) != 0) { + strncpy(duped, todup, len); + } + return duped; +} + +SWITCH_DECLARE(char *) switch_core_session_sprintf(switch_core_session_t *session, const char *fmt, ...) +{ + va_list ap; + char *result = NULL; + + assert(session != NULL); + assert(session->pool != NULL); + va_start(ap, fmt); + + result = apr_pvsprintf(session->pool, fmt, ap); + + va_end(ap); + + return result; +} + +SWITCH_DECLARE(char *) switch_core_sprintf(switch_memory_pool_t *pool, const char *fmt, ...) +{ + va_list ap; + char *result = NULL; + + assert(pool != NULL); + va_start(ap, fmt); + + result = apr_pvsprintf(pool, fmt, ap); + + va_end(ap); + + return result; +} + + +SWITCH_DECLARE(char *) switch_core_session_strdup(switch_core_session_t *session, const char *todup) +{ + char *duped = NULL; + switch_size_t len; + assert(session != NULL); + assert(session->pool != NULL); + + if (!todup) { + return NULL; + } + len = strlen(todup) + 1; + +#ifdef DEBUG_ALLOC + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Allocate %d\n", len); +#endif + + if (todup && (duped = apr_palloc(session->pool, len)) != 0) { + strncpy(duped, todup, len); + } + return duped; +} + + +SWITCH_DECLARE(char *) switch_core_strdup(switch_memory_pool_t *pool, const char *todup) +{ + char *duped = NULL; + switch_size_t len; + assert(pool != NULL); + + if (!todup) { + return NULL; + } + + len = strlen(todup) + 1; + +#ifdef DEBUG_ALLOC + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Allocate %d\n", len); +#endif + + if (todup && (duped = apr_palloc(pool, len)) != 0) { + strncpy(duped, todup, len); + } + return duped; +} + +SWITCH_DECLARE(switch_status_t) switch_core_new_memory_pool(switch_memory_pool_t **pool) +{ + + if ((apr_pool_create(pool, NULL)) != SWITCH_STATUS_SUCCESS) { + *pool = NULL; + return SWITCH_STATUS_MEMERR; + } + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_core_destroy_memory_pool(switch_memory_pool_t **pool) +{ + apr_pool_destroy(*pool); + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_DECLARE(void *) switch_core_alloc(switch_memory_pool_t *pool, switch_size_t memory) +{ + void *ptr = NULL; + assert(pool != NULL); + +#ifdef DEBUG_ALLOC + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Allocate %d\n", memory); + /* assert(memory < 600000); */ +#endif + + if ((ptr = apr_palloc(pool, memory)) != 0) { + memset(ptr, 0, memory); + } + return ptr; +} + +SWITCH_DECLARE(switch_memory_pool_t *) switch_core_memory_init(void) +{ + memset(&runtime, 0, sizeof(runtime)); + + apr_pool_create(&runtime.memory_pool, NULL); + return runtime.memory_pool; +} diff --git a/src/switch_core_port_allocator.c b/src/switch_core_port_allocator.c new file mode 100644 index 0000000000..8fdf317534 --- /dev/null +++ b/src/switch_core_port_allocator.c @@ -0,0 +1,112 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_port_allocator.c -- Main Core Library (port allocator) + * + */ +#include +#include "private/switch_core.h" + +struct switch_core_port_allocator { + switch_port_t start; + switch_port_t end; + switch_port_t next; + uint8_t inc; + switch_mutex_t *mutex; + switch_memory_pool_t *pool; +}; + +SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, + switch_port_t end, + uint8_t inc, + switch_core_port_allocator_t **new_allocator) +{ + switch_status_t status; + switch_memory_pool_t *pool; + switch_core_port_allocator_t *alloc; + + if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + if (!(alloc = switch_core_alloc(pool, sizeof(*alloc)))) { + switch_core_destroy_memory_pool(&pool); + return SWITCH_STATUS_MEMERR; + } + + alloc->start = start; + alloc->next = start; + alloc->end = end; + if (!(alloc->inc = inc)) { + alloc->inc = 2; + } + switch_mutex_init(&alloc->mutex, SWITCH_MUTEX_NESTED, pool); + alloc->pool = pool; + *new_allocator = alloc; + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, + switch_management_action_t action, + char *data, switch_size_t datalen) +{ + const switch_management_interface_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + if ((ptr = switch_loadable_module_get_management_interface(relative_oid))) { + status = ptr->management_function(relative_oid, action, data, datalen); + } + + return status; +} + + + +SWITCH_DECLARE(switch_port_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc) +{ + switch_port_t port; + + switch_mutex_lock(alloc->mutex); + port = alloc->next; + alloc->next = alloc->next + alloc->inc; + if (alloc->next > alloc->end) { + alloc->next = alloc->start; + } + switch_mutex_unlock(alloc->mutex); + return port; +} + +SWITCH_DECLARE(void) switch_core_port_allocator_destroy(switch_core_port_allocator_t **alloc) +{ + switch_memory_pool_t *pool = (*alloc)->pool; + switch_core_destroy_memory_pool(&pool); + *alloc = NULL; +} diff --git a/src/switch_core_rwlock.c b/src/switch_core_rwlock.c new file mode 100644 index 0000000000..44c1273c21 --- /dev/null +++ b/src/switch_core_rwlock.c @@ -0,0 +1,91 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_rwlock.c -- Main Core Library (read / write locks) + * + */ +#include +#include "private/switch_core.h" + +#ifdef SWITCH_DEBUG_RWLOCKS +SWITCH_DECLARE(switch_status_t) switch_core_session_perform_read_lock(switch_core_session_t *session, + const char *file, const char *func, int line) +#else +SWITCH_DECLARE(switch_status_t) switch_core_session_read_lock(switch_core_session_t *session) +#endif +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (session->rwlock) { + if (switch_test_flag(session, SSF_DESTROYED)) { + status = SWITCH_STATUS_FALSE; +#ifdef SWITCH_DEBUG_RWLOCKS + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, SWITCH_LOG_ERROR, "%s Read lock FAIL\n", + switch_channel_get_name(session->channel)); +#endif + } else { + status = (switch_status_t) switch_thread_rwlock_tryrdlock(session->rwlock); +#ifdef SWITCH_DEBUG_RWLOCKS + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, SWITCH_LOG_ERROR, "%s Read lock AQUIRED\n", + switch_channel_get_name(session->channel)); +#endif + } + } + + return status; +} + +#ifdef SWITCH_DEBUG_RWLOCKS +SWITCH_DECLARE(void) switch_core_session_perform_write_lock(switch_core_session_t *session, + const char *file, const char *func, int line) +{ + + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, SWITCH_LOG_ERROR, "%s Write lock AQUIRED\n", + switch_channel_get_name(session->channel)); +#else +SWITCH_DECLARE(void) switch_core_session_write_lock(switch_core_session_t *session) +{ +#endif + switch_thread_rwlock_wrlock(session->rwlock); +} + +#ifdef SWITCH_DEBUG_RWLOCKS +SWITCH_DECLARE(void) switch_core_session_perform_rwunlock(switch_core_session_t *session, + const char *file, const char *func, int line) +{ + switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, SWITCH_LOG_ERROR, "%s Read/Write lock CLEARED\n", + switch_channel_get_name(session->channel)); +#else +SWITCH_DECLARE(void) switch_core_session_rwunlock(switch_core_session_t *session) +{ +#endif + switch_thread_rwlock_unlock(session->rwlock); + +} diff --git a/src/switch_core_session.c b/src/switch_core_session.c new file mode 100644 index 0000000000..bae0c5e1ae --- /dev/null +++ b/src/switch_core_session.c @@ -0,0 +1,936 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_session.c -- Main Core Library (session routines) + * + */ +#include +#include "private/switch_core.h" + +static struct { + switch_memory_pool_t *memory_pool; + switch_hash_t *session_table; + switch_mutex_t *session_table_mutex; + uint32_t session_count; + uint32_t session_limit; + uint32_t session_id; +} runtime; + + +#ifdef SWITCH_DEBUG_RWLOCKS +SWITCH_DECLARE(switch_core_session_t *) switch_core_session_perform_locate(char *uuid_str, + const char *file, const char *func, int line) +#else +SWITCH_DECLARE(switch_core_session_t *) switch_core_session_locate(char *uuid_str) +#endif +{ + switch_core_session_t *session = NULL; + + if (uuid_str) { + switch_mutex_lock(runtime.session_table_mutex); + if ((session = switch_core_hash_find(runtime.session_table, uuid_str))) { + /* Acquire a read lock on the session */ +#ifdef SWITCH_DEBUG_RWLOCKS + if (switch_core_session_perform_read_lock(session, file, func, line) != SWITCH_STATUS_SUCCESS) { +#else + if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { +#endif + /* not available, forget it */ + session = NULL; + } + } + switch_mutex_unlock(runtime.session_table_mutex); + } + + /* if its not NULL, now it's up to you to rwunlock this */ + return session; +} + +SWITCH_DECLARE(void) switch_core_session_hupall(switch_call_cause_t cause) +{ + switch_hash_index_t *hi; + void *val; + switch_core_session_t *session; + switch_channel_t *channel; + uint32_t loops = 0; + + switch_mutex_lock(runtime.session_table_mutex); + for (hi = switch_hash_first(runtime.memory_pool, runtime.session_table); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, NULL, NULL, &val); + if (val) { + session = (switch_core_session_t *) val; + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, cause); + switch_core_session_kill_channel(session, SWITCH_SIG_KILL); + } + } + switch_mutex_unlock(runtime.session_table_mutex); + + while (runtime.session_count > 0) { + switch_yield(100000); + if (++loops == 100) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Giving up with %d session%s remaining\n", + runtime.session_count, runtime.session_count == 1 ? "" : "s"); + break; + } + } +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_message_send(char *uuid_str, switch_core_session_message_t *message) +{ + switch_core_session_t *session = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(runtime.session_table_mutex); + if ((session = switch_core_hash_find(runtime.session_table, uuid_str)) != 0) { + /* Acquire a read lock on the session or forget it the channel is dead */ + if (switch_core_session_read_lock(session) == SWITCH_STATUS_SUCCESS) { + if (switch_channel_get_state(session->channel) < CS_HANGUP) { + status = switch_core_session_receive_message(session, message); + } + switch_core_session_rwunlock(session); + } + } + switch_mutex_unlock(runtime.session_table_mutex); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_event_send(char *uuid_str, switch_event_t **event) +{ + switch_core_session_t *session = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(runtime.session_table_mutex); + if ((session = switch_core_hash_find(runtime.session_table, uuid_str)) != 0) { + /* Acquire a read lock on the session or forget it the channel is dead */ + if (switch_core_session_read_lock(session) == SWITCH_STATUS_SUCCESS) { + if (switch_channel_get_state(session->channel) < CS_HANGUP) { + status = switch_core_session_queue_event(session, event); + } + switch_core_session_rwunlock(session); + } + } + switch_mutex_unlock(runtime.session_table_mutex); + + return status; +} + + +SWITCH_DECLARE(void *) switch_core_session_get_private(switch_core_session_t *session) +{ + assert(session != NULL); + return session->private_info; +} + + +SWITCH_DECLARE(switch_status_t) switch_core_session_set_private(switch_core_session_t *session, void *private_info) +{ + assert(session != NULL); + session->private_info = private_info; + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(int) switch_core_session_add_stream(switch_core_session_t *session, void *private_info) +{ + session->streams[session->stream_count++] = private_info; + return session->stream_count - 1; +} + +SWITCH_DECLARE(void *) switch_core_session_get_stream(switch_core_session_t *session, int index) +{ + return session->streams[index]; +} + + +SWITCH_DECLARE(int) switch_core_session_get_stream_count(switch_core_session_t *session) +{ + return session->stream_count; +} + +SWITCH_DECLARE(switch_call_cause_t) switch_core_session_outgoing_channel(switch_core_session_t *session, + char *endpoint_name, + switch_caller_profile_t *caller_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool) +{ + switch_io_event_hook_outgoing_channel_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + const switch_endpoint_interface_t *endpoint_interface; + switch_channel_t *channel = NULL; + switch_caller_profile_t *outgoing_profile = caller_profile; + switch_call_cause_t cause = SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL; + + if ((endpoint_interface = switch_loadable_module_get_endpoint_interface(endpoint_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not locate channel type %s\n", endpoint_name); + return SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED; + } + + if (endpoint_interface->io_routines->outgoing_channel) { + if (session) { + channel = switch_core_session_get_channel(session); + if (caller_profile) { + char *ecaller_id_name = NULL, *ecaller_id_number = NULL; + + ecaller_id_name = switch_channel_get_variable(channel, "effective_caller_id_name"); + ecaller_id_number = switch_channel_get_variable(channel, "effective_caller_id_number"); + + if (ecaller_id_name || ecaller_id_number) { + if (!ecaller_id_name) { + ecaller_id_name = caller_profile->caller_id_name; + } + if (!ecaller_id_number) { + ecaller_id_number = caller_profile->caller_id_number; + } + outgoing_profile = switch_caller_profile_new(switch_core_session_get_pool(session), + caller_profile->username, + caller_profile->dialplan, + ecaller_id_name, + ecaller_id_number, + caller_profile->network_addr, + caller_profile->ani, + caller_profile->aniii, + caller_profile->rdnis, + caller_profile->source, + caller_profile->context, + caller_profile->destination_number); + outgoing_profile->flags = caller_profile->flags; + } + } + if (!outgoing_profile) { + outgoing_profile = switch_channel_get_caller_profile(channel); + } + } + + if ((cause = endpoint_interface->io_routines->outgoing_channel(session, + outgoing_profile, + new_session, pool)) == SWITCH_CAUSE_SUCCESS) { + if (session) { + for (ptr = session->event_hooks.outgoing_channel; ptr; ptr = ptr->next) { + if ((status = + ptr->outgoing_channel(session, caller_profile, *new_session)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + } else { + return cause; + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not locate outgoing channel interface for %s\n", + endpoint_name); + return SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED; + } + + if (*new_session) { + switch_caller_profile_t *profile = NULL, *peer_profile = NULL, *cloned_profile = NULL; + switch_event_t *event; + switch_channel_t *peer_channel = switch_core_session_get_channel(*new_session); + + + if (session && channel) { + profile = switch_channel_get_caller_profile(channel); + } + if (peer_channel) { + peer_profile = switch_channel_get_caller_profile(peer_channel); + } + + if (channel && peer_channel) { + char *export_vars, *val; + switch_codec_t *read_codec = switch_core_session_get_read_codec(session); + + if (read_codec) { + char tmp[80]; + switch_codec2str(read_codec, tmp, sizeof(tmp)); + switch_channel_set_variable(peer_channel, SWITCH_ORIGINATOR_CODEC_VARIABLE, tmp); + } + + switch_channel_set_variable(peer_channel, SWITCH_ORIGINATOR_VARIABLE, + switch_core_session_get_uuid(session)); + switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BOND_VARIABLE, + switch_core_session_get_uuid(session)); + switch_channel_set_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE, + switch_core_session_get_uuid(*new_session)); + + /* A comma (,) separated list of variable names that should ne propagated from originator to originatee */ + if ((export_vars = switch_channel_get_variable(channel, SWITCH_EXPORT_VARS_VARIABLE))) { + char *cptmp = switch_core_session_strdup(session, export_vars); + int argc; + char *argv[256]; + + if ((argc = switch_separate_string(cptmp, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) { + int x; + + for (x = 0; x < argc; x++) { + char *val; + if ((val = switch_channel_get_variable(channel, argv[x]))) { + switch_channel_set_variable(peer_channel, argv[x], val); + } + } + } + } + + if ((val = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE))) { + switch_channel_set_variable(peer_channel, SWITCH_B_SDP_VARIABLE, val); + } + + if ((val = switch_channel_get_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE))) { + switch_channel_set_variable(peer_channel, SWITCH_MAX_FORWARDS_VARIABLE, val); + } + + if (switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_channel_set_flag(peer_channel, CF_NOMEDIA); + } + + if (profile) { + if ((cloned_profile = switch_caller_profile_clone(*new_session, profile)) != 0) { + switch_channel_set_originator_caller_profile(peer_channel, cloned_profile); + } + } + + if (peer_profile) { + if (session && (cloned_profile = switch_caller_profile_clone(session, peer_profile)) != 0) { + switch_channel_set_originatee_caller_profile(channel, cloned_profile); + } + } + } + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_OUTGOING) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(peer_channel, event); + switch_event_fire(&event); + } + } + + return cause; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_answer_channel(switch_core_session_t *session) +{ + switch_io_event_hook_answer_channel_t *ptr; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + assert(session != NULL); + + if (session->endpoint_interface->io_routines->answer_channel) { + status = session->endpoint_interface->io_routines->answer_channel(session); + } + + if (status == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.answer_channel; ptr; ptr = ptr->next) { + if ((status = ptr->answer_channel(session)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_receive_message(switch_core_session_t *session, + switch_core_session_message_t *message) +{ + switch_io_event_hook_receive_message_t *ptr; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + assert(session != NULL); + + if (session->endpoint_interface->io_routines->receive_message) { + status = session->endpoint_interface->io_routines->receive_message(session, message); + + } + + if (status == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.receive_message; ptr; ptr = ptr->next) { + if ((status = ptr->receive_message(session, message)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + + switch_core_session_kill_channel(session, SWITCH_SIG_BREAK); + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_queue_indication(switch_core_session_t *session, + switch_core_session_message_types_t indication) +{ + switch_core_session_message_t *msg; + + if ((msg = malloc(sizeof(*msg)))) { + memset(msg, 0, sizeof(*msg)); + msg->message_id = indication; + msg->from = __FILE__; + switch_core_session_queue_message(session, msg); + switch_set_flag(msg, SCSMF_DYNAMIC); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_queue_message(switch_core_session_t *session, + switch_core_session_message_t *message) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + assert(session != NULL); + + if (!session->message_queue) { + switch_queue_create(&session->message_queue, SWITCH_MESSAGE_QUEUE_LEN, session->pool); + } + + if (session->message_queue) { + if (switch_queue_trypush(session->message_queue, message) == SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_SUCCESS; + } + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_message(switch_core_session_t *session, + switch_core_session_message_t **message) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + void *pop; + + assert(session != NULL); + + if (session->message_queue) { + if ((status = (switch_status_t) switch_queue_trypop(session->message_queue, &pop)) == SWITCH_STATUS_SUCCESS) { + *message = (switch_core_session_message_t *) pop; + } + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_flush_message(switch_core_session_t *session) +{ + switch_core_session_message_t *message; + + if (switch_core_session_dequeue_message(session, &message) == SWITCH_STATUS_SUCCESS) { + if (switch_test_flag(message, SCSMF_DYNAMIC)) { + switch_safe_free(message); + } else { + message = NULL; + } + } + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_DECLARE(switch_status_t) switch_core_session_receive_event(switch_core_session_t *session, + switch_event_t **event) +{ + switch_io_event_hook_receive_event_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + assert(session != NULL); + + /* Acquire a read lock on the session or forget it the channel is dead */ + if (switch_core_session_read_lock(session) == SWITCH_STATUS_SUCCESS) { + if (switch_channel_get_state(session->channel) < CS_HANGUP) { + if (session->endpoint_interface->io_routines->receive_event) { + status = session->endpoint_interface->io_routines->receive_event(session, *event); + } + + if (status == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.receive_event; ptr; ptr = ptr->next) { + if ((status = ptr->receive_event(session, *event)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + + if (status == SWITCH_STATUS_BREAK) { + status = SWITCH_STATUS_SUCCESS; + } + + if (status == SWITCH_STATUS_SUCCESS) { + switch_event_destroy(event); + } + } + switch_core_session_rwunlock(session); + } + + switch_core_session_kill_channel(session, SWITCH_SIG_BREAK); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_queue_event(switch_core_session_t *session, switch_event_t **event) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + assert(session != NULL); + + if (!session->event_queue) { + switch_queue_create(&session->event_queue, SWITCH_EVENT_QUEUE_LEN, session->pool); + } + + if (session->event_queue) { + if (switch_queue_trypush(session->event_queue, *event) == SWITCH_STATUS_SUCCESS) { + *event = NULL; + status = SWITCH_STATUS_SUCCESS; + } + } + + return status; +} + +SWITCH_DECLARE(int32_t) switch_core_session_event_count(switch_core_session_t *session) +{ + if (session->event_queue) { + return (int32_t) switch_queue_size(session->event_queue); + } + + return -1; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_event(switch_core_session_t *session, + switch_event_t **event) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + void *pop; + + assert(session != NULL); + + if (session->event_queue) { + if ((status = (switch_status_t) switch_queue_trypop(session->event_queue, &pop)) == SWITCH_STATUS_SUCCESS) { + *event = (switch_event_t *) pop; + } + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_queue_private_event(switch_core_session_t *session, + switch_event_t **event) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + assert(session != NULL); + + if (!session->private_event_queue) { + switch_queue_create(&session->private_event_queue, SWITCH_EVENT_QUEUE_LEN, session->pool); + } + + if (session->private_event_queue) { + (*event)->event_id = SWITCH_EVENT_PRIVATE_COMMAND; + if (switch_queue_trypush(session->private_event_queue, *event) == SWITCH_STATUS_SUCCESS) { + *event = NULL; + switch_core_session_kill_channel(session, SWITCH_SIG_BREAK); + status = SWITCH_STATUS_SUCCESS; + } + } + + return status; +} + +SWITCH_DECLARE(int32_t) switch_core_session_private_event_count(switch_core_session_t *session) +{ + if (session->private_event_queue) { + return (int32_t) switch_queue_size(session->private_event_queue); + } + + return -1; +} + +SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_private_event(switch_core_session_t *session, + switch_event_t **event) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + void *pop; + switch_channel_t *channel; + + assert(session != NULL); + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (switch_channel_test_flag(channel, CF_EVENT_PARSE)) { + return status; + } + + + if (session->private_event_queue) { + if ((status = + (switch_status_t) switch_queue_trypop(session->private_event_queue, &pop)) == SWITCH_STATUS_SUCCESS) { + *event = (switch_event_t *) pop; + } + } + + return status; +} + +SWITCH_DECLARE(void) switch_core_session_reset(switch_core_session_t *session) +{ + switch_channel_t *channel; + char buf[256]; + switch_size_t has; + + /* sweep theese under the rug, they wont be leaked they will be reclaimed + when the session ends. + */ + session->read_resampler = NULL; + session->write_resampler = NULL; + + /* clear indications */ + switch_core_session_flush_message(session); + + /* wipe theese, they will be recreated if need be */ + switch_buffer_destroy(&session->raw_read_buffer); + switch_buffer_destroy(&session->raw_write_buffer); + + /* flush dtmf */ + channel = switch_core_session_get_channel(session); + + while ((has = switch_channel_has_dtmf(channel))) { + switch_channel_dequeue_dtmf(channel, buf, sizeof(buf)); + } + +} + + +SWITCH_DECLARE(switch_channel_t *) switch_core_session_get_channel(switch_core_session_t *session) +{ + return session->channel; +} + + + +SWITCH_DECLARE(void) switch_core_session_signal_state_change(switch_core_session_t *session) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_io_event_hook_state_change_t *ptr; + + /* If trylock fails the signal is already awake so we needn't bother */ + if (switch_mutex_trylock(session->mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(session->cond); + switch_mutex_unlock(session->mutex); + } + + if (session->endpoint_interface->io_routines->state_change) { + status = session->endpoint_interface->io_routines->state_change(session); + } + + if (status == SWITCH_STATUS_SUCCESS) { + for (ptr = session->event_hooks.state_change; ptr; ptr = ptr->next) { + if ((status = ptr->state_change(session)) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } +} + +SWITCH_DECLARE(unsigned int) switch_core_session_running(switch_core_session_t *session) +{ + return session->thread_running; +} + +#ifdef CRASH_PROT +#if defined (__GNUC__) && defined (LINUX) +#include +#include +#include +#define STACK_LEN 10 + +/* Obtain a backtrace and print it to stdout. */ +static void print_trace(void) +{ + void *array[STACK_LEN]; + size_t size; + char **strings; + size_t i; + + size = backtrace(array, STACK_LEN); + strings = backtrace_symbols(array, size); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Obtained %zd stack frames.\n", size); + + for (i = 0; i < size; i++) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CRIT, "%s\n", strings[i]); + } + + free(strings); +} +#else +static void print_trace(void) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Trace not avaliable =(\n"); +} +#endif + + +static void handle_fatality(int sig) +{ + switch_thread_id_t thread_id; + jmp_buf *env; + + if (sig && (thread_id = switch_thread_self()) + && (env = (jmp_buf *) apr_hash_get(runtime.stack_table, &thread_id, sizeof(thread_id)))) { + print_trace(); + longjmp(*env, sig); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Caught signal %d for unmapped thread!", sig); + abort(); + } +} +#endif + + +SWITCH_DECLARE(void) switch_core_session_destroy(switch_core_session_t **session) +{ + switch_memory_pool_t *pool; + switch_event_t *event; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Close Channel %s\n", + switch_channel_get_name((*session)->channel)); + + switch_scheduler_del_task_group((*session)->uuid_str); + + switch_mutex_lock(runtime.session_table_mutex); + switch_core_hash_delete(runtime.session_table, (*session)->uuid_str); + if (runtime.session_count) { + runtime.session_count--; + } + switch_mutex_unlock(runtime.session_table_mutex); + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DESTROY) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data((*session)->channel, event); + switch_event_fire(&event); + } + + switch_core_media_bug_remove_all(*session); + switch_buffer_destroy(&(*session)->raw_read_buffer); + switch_buffer_destroy(&(*session)->raw_write_buffer); + switch_channel_uninit((*session)->channel); + + pool = (*session)->pool; + *session = NULL; + apr_pool_destroy(pool); + pool = NULL; + +} + +static void *SWITCH_THREAD_FUNC switch_core_session_thread(switch_thread_t *thread, void *obj) +{ + switch_core_session_t *session = obj; + session->thread = thread; + + + + switch_core_session_run(session); + switch_core_media_bug_remove_all(session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session %u (%s) Locked, Waiting on external entities\n", + session->id, switch_channel_get_name(session->channel)); + switch_core_session_write_lock(session); + switch_set_flag(session, SSF_DESTROYED); + switch_core_session_rwunlock(session); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Session %u (%s) Ended\n", session->id, + switch_channel_get_name(session->channel)); + switch_core_session_destroy(&session); + return NULL; +} + + +SWITCH_DECLARE(void) switch_core_session_thread_launch(switch_core_session_t *session) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr;; + switch_threadattr_create(&thd_attr, session->pool); + switch_threadattr_detach_set(thd_attr, 1); + + if (!session->thread_running) { + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + if (switch_thread_create(&thread, thd_attr, switch_core_session_thread, session, session->pool) != + SWITCH_STATUS_SUCCESS) { + switch_core_session_destroy(&session); + } + } +} + + +SWITCH_DECLARE(void) switch_core_session_launch_thread(switch_core_session_t *session, switch_thread_start_t func, + void *obj) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + switch_threadattr_create(&thd_attr, session->pool); + switch_threadattr_detach_set(thd_attr, 1); + + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, func, obj, session->pool); + +} + + +SWITCH_DECLARE(switch_core_session_t *) switch_core_session_request(const switch_endpoint_interface_t + *endpoint_interface, switch_memory_pool_t **pool) +{ + switch_memory_pool_t *usepool; + switch_core_session_t *session; + switch_uuid_t uuid; + uint32_t count = 0; + + assert(endpoint_interface != NULL); + + switch_mutex_lock(runtime.session_table_mutex); + count = runtime.session_count; + switch_mutex_unlock(runtime.session_table_mutex); + + if ((count + 1) > runtime.session_limit) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Over Session Limit!\n"); + return NULL; + } + + if (!switch_core_ready()) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read my lips: no new sessions!\n"); + return NULL; + } + + if (pool && *pool) { + usepool = *pool; + *pool = NULL; + } else if (switch_core_new_memory_pool(&usepool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not allocate memory pool\n"); + return NULL; + } + + if ((session = switch_core_alloc(usepool, sizeof(switch_core_session_t))) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not allocate session\n"); + apr_pool_destroy(usepool); + return NULL; + } + + if (switch_channel_alloc(&session->channel, usepool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate channel structure\n"); + apr_pool_destroy(usepool); + return NULL; + } + + switch_channel_init(session->channel, session, CS_NEW, 0); + + /* The session *IS* the pool you may not alter it because you have no idea how + its all private it will be passed to the thread run function */ + + switch_uuid_get(&uuid); + switch_uuid_format(session->uuid_str, &uuid); + + session->pool = usepool; + session->endpoint_interface = endpoint_interface; + + session->raw_write_frame.data = session->raw_write_buf; + session->raw_write_frame.buflen = sizeof(session->raw_write_buf); + session->raw_read_frame.data = session->raw_read_buf; + session->raw_read_frame.buflen = sizeof(session->raw_read_buf); + + + session->enc_write_frame.data = session->enc_write_buf; + session->enc_write_frame.buflen = sizeof(session->enc_write_buf); + session->enc_read_frame.data = session->enc_read_buf; + session->enc_read_frame.buflen = sizeof(session->enc_read_buf); + + switch_mutex_init(&session->mutex, SWITCH_MUTEX_NESTED, session->pool); + switch_thread_rwlock_create(&session->bug_rwlock, session->pool); + switch_thread_cond_create(&session->cond, session->pool); + switch_thread_rwlock_create(&session->rwlock, session->pool); + + snprintf(session->name, sizeof(session->name), "%u", session->id); + switch_mutex_lock(runtime.session_table_mutex); + session->id = runtime.session_id++; + switch_core_hash_insert(runtime.session_table, session->uuid_str, session); + runtime.session_count++; + switch_mutex_unlock(runtime.session_table_mutex); + + return session; +} + +SWITCH_DECLARE(uint32_t) switch_core_session_count(void) +{ + return runtime.session_count; +} + +SWITCH_DECLARE(switch_core_session_t *) switch_core_session_request_by_name(char *endpoint_name, + switch_memory_pool_t **pool) +{ + const switch_endpoint_interface_t *endpoint_interface; + + if ((endpoint_interface = switch_loadable_module_get_endpoint_interface(endpoint_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not locate channel type %s\n", endpoint_name); + return NULL; + } + + return switch_core_session_request(endpoint_interface, pool); +} + + +#ifndef SWITCH_PREFIX_DIR +#define SWITCH_PREFIX_DIR "." +#endif + + +SWITCH_DECLARE(uint8_t) switch_core_session_compare(switch_core_session_t *a, switch_core_session_t *b) +{ + assert(a != NULL); + assert(b != NULL); + + return (uint8_t) (a->endpoint_interface == b->endpoint_interface); +} + +SWITCH_DECLARE(char *) switch_core_session_get_uuid(switch_core_session_t *session) +{ + return session->uuid_str; +} + + +SWITCH_DECLARE(uint32_t) switch_core_session_limit(uint32_t new_limit) +{ + if (new_limit) { + runtime.session_limit = new_limit; + } + + return runtime.session_limit; +} + + +SWITCH_DECLARE(void) switch_core_session_init(switch_memory_pool_t *pool) +{ + memset(&runtime, 0, sizeof(runtime)); + runtime.session_limit = 1000; + runtime.session_id = 1; + runtime.memory_pool = pool; + switch_core_hash_init(&runtime.session_table, runtime.memory_pool); + switch_mutex_init(&runtime.session_table_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); +} diff --git a/src/switch_core_speech.c b/src/switch_core_speech.c new file mode 100644 index 0000000000..669f8595d6 --- /dev/null +++ b/src/switch_core_speech.c @@ -0,0 +1,95 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_speech.c -- Main Core Library (speech functions) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_speech_feed_tts(switch_speech_handle_t *sh, char *text, + switch_speech_flag_t *flags) +{ + assert(sh != NULL); + + return sh->speech_interface->speech_feed_tts(sh, text, flags); +} + +SWITCH_DECLARE(void) switch_core_speech_flush_tts(switch_speech_handle_t *sh) +{ + assert(sh != NULL); + + if (sh->speech_interface->speech_flush_tts) { + sh->speech_interface->speech_flush_tts(sh); + } +} + +SWITCH_DECLARE(void) switch_core_speech_text_param_tts(switch_speech_handle_t *sh, char *param, char *val) +{ + assert(sh != NULL); + + if (sh->speech_interface->speech_text_param_tts) { + sh->speech_interface->speech_text_param_tts(sh, param, val); + } +} + +SWITCH_DECLARE(void) switch_core_speech_numeric_param_tts(switch_speech_handle_t *sh, char *param, int val) +{ + assert(sh != NULL); + + if (sh->speech_interface->speech_numeric_param_tts) { + sh->speech_interface->speech_numeric_param_tts(sh, param, val); + } +} + +SWITCH_DECLARE(void) switch_core_speech_float_param_tts(switch_speech_handle_t *sh, char *param, double val) +{ + assert(sh != NULL); + + if (sh->speech_interface->speech_float_param_tts) { + sh->speech_interface->speech_float_param_tts(sh, param, val); + } +} + +SWITCH_DECLARE(switch_status_t) switch_core_speech_read_tts(switch_speech_handle_t *sh, + void *data, + switch_size_t *datalen, + uint32_t * rate, switch_speech_flag_t *flags) +{ + assert(sh != NULL); + + return sh->speech_interface->speech_read_tts(sh, data, datalen, rate, flags); +} + + +SWITCH_DECLARE(switch_status_t) switch_core_speech_close(switch_speech_handle_t *sh, switch_speech_flag_t *flags) +{ + return sh->speech_interface->speech_close(sh, flags); +} diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c new file mode 100644 index 0000000000..2406c7cfd1 --- /dev/null +++ b/src/switch_core_sqldb.c @@ -0,0 +1,411 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_sqldb.c -- Main Core Library (statistics tracker) + * + */ +#include +#include "private/switch_core.h" + +static struct { + switch_core_db_t *db; + switch_core_db_t *event_db; + switch_queue_t *sql_queue; + switch_memory_pool_t *memory_pool; +} runtime; + +static switch_status_t switch_core_db_persistant_execute_trans(switch_core_db_t *db, char *sql, uint32_t retries) +{ + char *errmsg; + switch_status_t status = SWITCH_STATUS_FALSE; + uint8_t forever = 0; + unsigned begin_retries = 100; + uint8_t again = 0; + + if (!retries) { + forever = 1; + retries = 1000; + } + + again: + + while (begin_retries > 0) { + again = 0; + + switch_core_db_exec(db, "begin transaction", NULL, NULL, &errmsg); + + if (errmsg) { + begin_retries--; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); + if (strstr(errmsg, "cannot start a transaction within a transaction")) { + again = 1; + } + switch_core_db_free(errmsg); + + if (again) { + switch_core_db_exec(db, "end transaction", NULL, NULL, &errmsg); + goto again; + } + + switch_yield(100000); + + if (begin_retries == 0) { + goto done; + } + } else { + break; + } + + } + + while (retries > 0) { + + + + switch_core_db_exec(db, sql, NULL, NULL, &errmsg); + if (errmsg) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); + switch_core_db_free(errmsg); + switch_yield(100000); + retries--; + if (retries == 0 && forever) { + retries = 1000; + continue; + } + } else { + status = SWITCH_STATUS_SUCCESS; + break; + } + } + + + done: + + switch_core_db_exec(db, "end transaction", NULL, NULL, &errmsg); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_db_persistant_execute(switch_core_db_t *db, char *sql, uint32_t retries) +{ + char *errmsg; + switch_status_t status = SWITCH_STATUS_FALSE; + uint8_t forever = 0; + + if (!retries) { + forever = 1; + retries = 1000; + } + + while (retries > 0) { + switch_core_db_exec(db, sql, NULL, NULL, &errmsg); + if (errmsg) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s]\n", errmsg); + switch_core_db_free(errmsg); + switch_yield(100000); + retries--; + if (retries == 0 && forever) { + retries = 1000; + continue; + } + } else { + status = SWITCH_STATUS_SUCCESS; + break; + } + } + + return status; +} + +#define SQLLEN 1024 * 64 +static void *SWITCH_THREAD_FUNC switch_core_sql_thread(switch_thread_t *thread, void *obj) +{ + void *pop; + uint32_t itterations = 0; + uint8_t trans = 0, nothing_in_queue = 0; + uint32_t target = 1000; + switch_size_t len = 0, sql_len = SQLLEN; + char *sqlbuf = (char *) malloc(sql_len); + char *sql; + switch_size_t newlen; + + if (!runtime.event_db) { + runtime.event_db = switch_core_db_handle(); + } + switch_queue_create(&runtime.sql_queue, SWITCH_SQL_QUEUE_LEN, runtime.memory_pool); + + + + for (;;) { + if (switch_queue_trypop(runtime.sql_queue, &pop) == SWITCH_STATUS_SUCCESS) { + sql = (char *) pop; + + if (sql) { + newlen = strlen(sql) + 2; + + if (itterations == 0) { + trans = 1; + } + + /* ignore abnormally large strings sql strings as potential buffer overflow */ + if (newlen < SQLLEN) { + itterations++; + if (len + newlen > sql_len) { + sql_len = len + SQLLEN; + if (!(sqlbuf = realloc(sqlbuf, sql_len))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SQL thread ending on mem err\n"); + break; + } + } + sprintf(sqlbuf + len, "%s;\n", sql); + len += newlen; + + } + switch_core_db_free(sql); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "SQL thread ending\n"); + break; + } + } else { + nothing_in_queue = 1; + } + + + if (trans && ((itterations == target) || nothing_in_queue)) { + if (switch_core_db_persistant_execute_trans(runtime.event_db, sqlbuf, 1000) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, + "SQL thread unable to commit transaction, records lost!\n"); + } + itterations = 0; + trans = 0; + nothing_in_queue = 0; + len = 0; + *sqlbuf = '\0'; + } + + if (nothing_in_queue) { + switch_yield(1000); + } + } + + + free(sqlbuf); + return NULL; +} + +static void core_event_handler(switch_event_t *event) +{ + char *sql = NULL; + + switch (event->event_id) { + case SWITCH_EVENT_CHANNEL_DESTROY: + sql = switch_mprintf("delete from channels where uuid='%s'", switch_event_get_header(event, "unique-id")); + break; + case SWITCH_EVENT_CHANNEL_CREATE: + sql = switch_mprintf("insert into channels (uuid,created,name,state) values('%q','%q','%q','%q')", + switch_event_get_header(event, "unique-id"), + switch_event_get_header(event, "event-date-local"), + switch_event_get_header(event, "channel-name"), + switch_event_get_header(event, "channel-state") + ); + break; + case SWITCH_EVENT_CODEC: + sql = + switch_mprintf + ("update channels set read_codec='%q',read_rate='%q',write_codec='%q',write_rate='%q' where uuid='%q'", + switch_event_get_header(event, "channel-read-codec-name"), switch_event_get_header(event, + "channel-read-codec-rate"), + switch_event_get_header(event, "channel-write-codec-name"), switch_event_get_header(event, + "channel-write-codec-rate"), + switch_event_get_header(event, "unique-id")); + break; + case SWITCH_EVENT_CHANNEL_EXECUTE: + sql = switch_mprintf("update channels set application='%q',application_data='%q' where uuid='%q'", + switch_event_get_header(event, "application"), + switch_event_get_header(event, "application-data"), + switch_event_get_header(event, "unique-id") + ); + break; + case SWITCH_EVENT_CHANNEL_STATE: + if (event) { + char *state = switch_event_get_header(event, "channel-state-number"); + switch_channel_state_t state_i = atoi(state); + + switch (state_i) { + case CS_HANGUP: + case CS_DONE: + break; + case CS_RING: + sql = switch_mprintf("update channels set state='%s',cid_name='%q',cid_num='%q',ip_addr='%s',dest='%q'" + "where uuid='%s'", + switch_event_get_header(event, "channel-state"), + switch_event_get_header(event, "caller-caller-id-name"), + switch_event_get_header(event, "caller-caller-id-number"), + switch_event_get_header(event, "caller-network-addr"), + switch_event_get_header(event, "caller-destination-number"), + switch_event_get_header(event, "unique-id") + ); + break; + default: + sql = switch_mprintf("update channels set state='%s' where uuid='%s'", + switch_event_get_header(event, "channel-state"), + switch_event_get_header(event, "unique-id") + ); + break; + } + + } + break; + case SWITCH_EVENT_CHANNEL_BRIDGE: + sql = switch_mprintf("insert into calls values ('%s','%q','%q','%q','%q','%s','%q','%q','%q','%q','%s')", + switch_event_get_header(event, "event-calling-function"), + switch_event_get_header(event, "caller-caller-id-name"), + switch_event_get_header(event, "caller-caller-id-number"), + switch_event_get_header(event, "caller-destination-number"), + switch_event_get_header(event, "caller-channel-name"), + switch_event_get_header(event, "caller-unique-id"), + switch_event_get_header(event, "originatee-caller-id-name"), + switch_event_get_header(event, "originatee-caller-id-number"), + switch_event_get_header(event, "originatee-destination-number"), + switch_event_get_header(event, "originatee-channel-name"), + switch_event_get_header(event, "originatee-unique-id") + ); + break; + case SWITCH_EVENT_CHANNEL_UNBRIDGE: + sql = + switch_mprintf("delete from calls where caller_uuid='%s'", + switch_event_get_header(event, "caller-unique-id")); + break; + case SWITCH_EVENT_SHUTDOWN: + sql = switch_mprintf("delete from channels;delete from interfaces;delete from calls"); + break; + case SWITCH_EVENT_LOG: + return; + case SWITCH_EVENT_MODULE_LOAD: + { + const char *type = switch_event_get_header(event, "type"); + const char *name = switch_event_get_header(event, "name"); + const char *description = switch_event_get_header(event, "description"); + const char *syntax = switch_event_get_header(event, "syntax"); + if (!switch_strlen_zero(type) && !switch_strlen_zero(name)) { + sql = + switch_mprintf("insert into interfaces (type,name,description,syntax) values('%q','%q','%q','%q')", + type, name, switch_str_nil(description), switch_str_nil(syntax) + ); + } + break; + } + default: + break; + } + + if (sql) { + switch_queue_push(runtime.sql_queue, sql); + sql = NULL; + } +} + + +SWITCH_DECLARE(void) switch_core_sqldb_start(switch_memory_pool_t *pool) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr;; + + runtime.memory_pool = pool; + + /* Activate SQL database */ + if ((runtime.db = switch_core_db_handle()) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB!\n"); + } else { + char create_channels_sql[] = + "CREATE TABLE channels (\n" + " uuid VARCHAR(255),\n" + " created VARCHAR(255),\n" + " name VARCHAR(255),\n" + " state VARCHAR(255),\n" + " cid_name VARCHAR(255),\n" + " cid_num VARCHAR(255),\n" + " ip_addr VARCHAR(255),\n" + " dest VARCHAR(255),\n" + " application VARCHAR(255),\n" + " application_data VARCHAR(255),\n" + " read_codec VARCHAR(255),\n" + " read_rate VARCHAR(255),\n" " write_codec VARCHAR(255),\n" " write_rate VARCHAR(255)\n" ");\n"; + char create_calls_sql[] = + "CREATE TABLE calls (\n" + " function VARCHAR(255),\n" + " caller_cid_name VARCHAR(255),\n" + " caller_cid_num VARCHAR(255),\n" + " caller_dest_num VARCHAR(255),\n" + " caller_chan_name VARCHAR(255),\n" + " caller_uuid VARCHAR(255),\n" + " callee_cid_name VARCHAR(255),\n" + " callee_cid_num VARCHAR(255),\n" + " callee_dest_num VARCHAR(255),\n" + " callee_chan_name VARCHAR(255),\n" " callee_uuid VARCHAR(255)\n" ");\n"; + char create_interfaces_sql[] = + "CREATE TABLE interfaces (\n" + " type VARCHAR(255),\n" + " name VARCHAR(255),\n" + " description VARCHAR(255),\n" " syntax VARCHAR(255)\n" ");\n"; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening DB\n"); + switch_core_db_exec(runtime.db, "drop table channels", NULL, NULL, NULL); + switch_core_db_exec(runtime.db, "drop table calls", NULL, NULL, NULL); + switch_core_db_exec(runtime.db, "drop table interfaces", NULL, NULL, NULL); + switch_core_db_exec(runtime.db, create_channels_sql, NULL, NULL, NULL); + switch_core_db_exec(runtime.db, create_calls_sql, NULL, NULL, NULL); + switch_core_db_exec(runtime.db, create_interfaces_sql, NULL, NULL, NULL); + if (switch_event_bind("core_db", SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, core_event_handler, NULL) != + SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind event handler!\n"); + } + } + + switch_threadattr_create(&thd_attr, runtime.memory_pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, switch_core_sql_thread, NULL, runtime.memory_pool); + +} + +SWITCH_DECLARE(void) switch_core_sqldb_stop(void) +{ + switch_queue_push(runtime.sql_queue, NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Waiting for unfinished SQL transactions\n"); + while (switch_queue_size(runtime.sql_queue) > 0) { + switch_yield(10000); + } + + switch_core_db_close(runtime.db); + switch_core_db_close(runtime.event_db); + +} diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c new file mode 100644 index 0000000000..b444221d53 --- /dev/null +++ b/src/switch_core_state_machine.c @@ -0,0 +1,626 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_state_maching.c -- Main Core Library (state machine) + * + */ +#include +#include "private/switch_core.h" + +static void switch_core_standard_on_init(switch_core_session_t *session) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard INIT %s\n", + switch_channel_get_name(session->channel)); +} + +static void switch_core_standard_on_hangup(switch_core_session_t *session) +{ + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard HANGUP %s, cause: %s\n", + switch_channel_get_name(session->channel), + switch_channel_cause2str(switch_channel_get_cause(session->channel))); + +} + +static void switch_core_standard_on_ring(switch_core_session_t *session) +{ + switch_dialplan_interface_t *dialplan_interface = NULL; + switch_caller_profile_t *caller_profile; + switch_caller_extension_t *extension = NULL; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard RING %s\n", + switch_channel_get_name(session->channel)); + + if ((caller_profile = switch_channel_get_caller_profile(session->channel)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't get profile!\n"); + switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return; + } else { + char *dp[25]; + char *dpstr; + int argc, x, count = 0; + + if (!switch_strlen_zero(caller_profile->dialplan)) { + if ((dpstr = switch_core_session_strdup(session, caller_profile->dialplan))) { + argc = switch_separate_string(dpstr, ',', dp, (sizeof(dp) / sizeof(dp[0]))); + for (x = 0; x < argc; x++) { + char *dpname = dp[x]; + char *dparg = NULL; + + if (dpname) { + if ((dparg = strchr(dpname, ':'))) { + *dparg++ = '\0'; + } + } + if (!(dialplan_interface = switch_loadable_module_get_dialplan_interface(dpname))) { + continue; + } + + count++; + + if ((extension = dialplan_interface->hunt_function(session, dparg)) != 0) { + switch_channel_set_caller_extension(session->channel, extension); + return; + } + } + } + } + + if (!count) { + if (switch_channel_test_flag(session->channel, CF_OUTBOUND)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "No Dialplan, changing state to HOLD\n"); + switch_channel_set_state(session->channel, CS_HOLD); + return; + } + } + } + + if (!extension) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "No Route, Aborting\n"); + switch_channel_hangup(session->channel, SWITCH_CAUSE_NO_ROUTE_DESTINATION); + } +} + +static void switch_core_standard_on_execute(switch_core_session_t *session) +{ + switch_caller_extension_t *extension; + switch_event_t *event; + const switch_application_interface_t *application_interface; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard EXECUTE\n"); + + if ((extension = switch_channel_get_caller_extension(session->channel)) == 0) { + switch_channel_hangup(session->channel, SWITCH_CAUSE_NORMAL_CLEARING); + return; + } + + while (switch_channel_get_state(session->channel) == CS_EXECUTE && extension->current_application) { + char *expanded = NULL; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Execute %s(%s)\n", + extension->current_application->application_name, + switch_str_nil(extension->current_application->application_data)); + if ((application_interface = + switch_loadable_module_get_application_interface(extension->current_application->application_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Application %s\n", + extension->current_application->application_name); + switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return; + } + + if (!application_interface->application_function) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Function for %s\n", + extension->current_application->application_name); + switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return; + } + + if ((expanded = + switch_channel_expand_variables(session->channel, + extension->current_application->application_data)) != + extension->current_application->application_data) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Expanded String %s(%s)\n", + extension->current_application->application_name, expanded); + } + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(session->channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "%s", + extension->current_application->application_name); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data-Orig", "%s", + extension->current_application->application_data); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", "%s", expanded); + switch_event_fire(&event); + } + + + if (switch_channel_get_variable(session->channel, "presence_id")) { + char *arg = switch_mprintf("%s(%s)", extension->current_application->application_name, expanded); + if (arg) { + switch_channel_presence(session->channel, "unknown", arg); + switch_safe_free(arg); + } + } + + application_interface->application_function(session, expanded); + + if (expanded != extension->current_application->application_data) { + switch_safe_free(expanded); + } + extension->current_application = extension->current_application->next; + } + + if (switch_channel_get_state(session->channel) == CS_EXECUTE) { + switch_channel_hangup(session->channel, SWITCH_CAUSE_NORMAL_CLEARING); + } +} + +static void switch_core_standard_on_loopback(switch_core_session_t *session) +{ + switch_frame_t *frame; + int stream_id; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard LOOPBACK\n"); + + while (switch_channel_get_state(session->channel) == CS_LOOPBACK) { + for (stream_id = 0; stream_id < session->stream_count; stream_id++) { + if (switch_core_session_read_frame(session, &frame, -1, stream_id) == SWITCH_STATUS_SUCCESS) { + switch_core_session_write_frame(session, frame, -1, stream_id); + } + } + } +} + +static void switch_core_standard_on_transmit(switch_core_session_t *session) +{ + assert(session != NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard TRANSMIT\n"); +} + +static void switch_core_standard_on_hold(switch_core_session_t *session) +{ + assert(session != NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard HOLD\n"); +} + +static void switch_core_standard_on_hibernate(switch_core_session_t *session) +{ + assert(session != NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard HIBERNATE\n"); +} + +SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session) +{ + switch_channel_state_t state = CS_NEW, laststate = CS_HANGUP, midstate = CS_DONE, endstate; + const switch_endpoint_interface_t *endpoint_interface; + const switch_state_handler_table_t *driver_state_handler = NULL; + const switch_state_handler_table_t *application_state_handler = NULL; + +#ifdef CRASH_PROT + switch_thread_id_t thread_id = switch_thread_self(); + jmp_buf env; + int sig; + + signal(SIGSEGV, handle_fatality); + signal(SIGFPE, handle_fatality); +#ifndef WIN32 + signal(SIGBUS, handle_fatality); +#endif + + if ((sig = setjmp(env)) != 0) { + switch_event_t *event; + + if (switch_event_create(&event, SWITCH_EVENT_SESSION_CRASH) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(session->channel, event); + switch_event_fire(&event); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Thread has crashed for channel %s\n", + switch_channel_get_name(session->channel)); + switch_channel_hangup(session->channel, SWITCH_CAUSE_CRASH); + } else { + apr_hash_set(runtime.stack_table, &thread_id, sizeof(thread_id), &env); + } +#endif + /* + Life of the channel. you have channel and pool in your session + everywhere you go you use the session to malloc with + switch_core_session_alloc(session, ) + + The enpoint module gets the first crack at implementing the state + if it wants to, it can cancel the default behaviour by returning SWITCH_STATUS_FALSE + + Next comes the channel's event handler table that can be set by an application + which also can veto the next behaviour in line by returning SWITCH_STATUS_FALSE + + Finally the default state behaviour is called. + + + */ + assert(session != NULL); + + session->thread_running = 1; + endpoint_interface = session->endpoint_interface; + assert(endpoint_interface != NULL); + + driver_state_handler = endpoint_interface->state_handler; + assert(driver_state_handler != NULL); + + switch_mutex_lock(session->mutex); + + while ((state = switch_channel_get_state(session->channel)) != CS_DONE) { + uint8_t exception = 0; + if (switch_channel_test_flag(session->channel, CF_REPEAT_STATE)) { + switch_channel_clear_flag(session->channel, CF_REPEAT_STATE); + exception = 1; + } + if (state != laststate || state == CS_HANGUP || exception) { + int index = 0; + int proceed = 1; + midstate = state; + + switch (state) { + case CS_NEW: /* Just created, Waiting for first instructions */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State NEW\n", + switch_channel_get_name(session->channel)); + break; + case CS_DONE: + goto done; + case CS_HANGUP: /* Deactivate and end the thread */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State HANGUP\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_hangup + || (driver_state_handler->on_hangup + && driver_state_handler->on_hangup(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hangup + || (application_state_handler->on_hangup + && application_state_handler->on_hangup(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hangup || + (application_state_handler->on_hangup && + application_state_handler->on_hangup(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + + if (proceed) { + switch_core_standard_on_hangup(session); + } + } + goto done; + case CS_INIT: /* Basic setup tasks */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State INIT\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_init + || (driver_state_handler->on_init && driver_state_handler->on_init(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_init + || (application_state_handler->on_init + && application_state_handler->on_init(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_init || + (application_state_handler->on_init && + application_state_handler->on_init(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_init(session); + } + } + break; + case CS_RING: /* Look for a dialplan and find something to do */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State RING\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_ring + || (driver_state_handler->on_ring && driver_state_handler->on_ring(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_ring + || (application_state_handler->on_ring + && application_state_handler->on_ring(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_ring || + (application_state_handler->on_ring && + application_state_handler->on_ring(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_ring(session); + } + } + break; + case CS_EXECUTE: /* Execute an Operation */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State EXECUTE\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_execute + || (driver_state_handler->on_execute + && driver_state_handler->on_execute(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_execute + || (application_state_handler->on_execute + && application_state_handler->on_execute(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_execute || + (application_state_handler->on_execute && + application_state_handler->on_execute(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_execute(session); + } + } + break; + case CS_LOOPBACK: /* loop all data back to source */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State LOOPBACK\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_loopback + || (driver_state_handler->on_loopback + && driver_state_handler->on_loopback(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_loopback + || (application_state_handler->on_loopback + && application_state_handler->on_loopback(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_loopback || + (application_state_handler->on_loopback && + application_state_handler->on_loopback(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_loopback(session); + } + } + break; + case CS_TRANSMIT: /* send/recieve data to/from another channel */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State TRANSMIT\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_transmit + || (driver_state_handler->on_transmit + && driver_state_handler->on_transmit(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_transmit + || (application_state_handler->on_transmit + && application_state_handler->on_transmit(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_transmit || + (application_state_handler->on_transmit && + application_state_handler->on_transmit(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_transmit(session); + } + } + break; + case CS_HOLD: /* wait in limbo */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State HOLD\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_hold + || (driver_state_handler->on_hold && driver_state_handler->on_hold(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hold + || (application_state_handler->on_hold + && application_state_handler->on_hold(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hold || + (application_state_handler->on_hold && + application_state_handler->on_hold(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_hold(session); + } + } + break; + case CS_HIBERNATE: /* wait in limbo */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State HIBERNATE\n", + switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_hibernate + || (driver_state_handler->on_hibernate + && driver_state_handler->on_hibernate(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + + while ((application_state_handler = + switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hibernate + || (application_state_handler->on_hibernate + && application_state_handler->on_hibernate(session) == SWITCH_STATUS_SUCCESS + && midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while (proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hibernate || + (application_state_handler->on_hibernate && + application_state_handler->on_hibernate(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_hibernate(session); + } + } + break; + } + + if (midstate == CS_DONE) { + break; + } + + laststate = midstate; + } + + + endstate = switch_channel_get_state(session->channel); + + + if (midstate == endstate) { + switch_thread_cond_wait(session->cond, session->mutex); + } + + } + done: + switch_mutex_unlock(session->mutex); + +#ifdef CRASH_PROT + apr_hash_set(runtime.stack_table, &thread_id, sizeof(thread_id), NULL); +#endif + session->thread_running = 0; + +} diff --git a/src/switch_core_timer.c b/src/switch_core_timer.c new file mode 100644 index 0000000000..006f06a9ee --- /dev/null +++ b/src/switch_core_timer.c @@ -0,0 +1,117 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * Paul D. Tinsley + * + * + * switch_core_timer.c -- Main Core Library (timer interface) + * + */ +#include +#include "private/switch_core.h" + +SWITCH_DECLARE(switch_status_t) switch_core_timer_init(switch_timer_t *timer, char *timer_name, int interval, + int samples, switch_memory_pool_t *pool) +{ + switch_timer_interface_t *timer_interface; + switch_status_t status; + memset(timer, 0, sizeof(*timer)); + if ((timer_interface = switch_loadable_module_get_timer_interface(timer_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid timer %s!\n", timer_name); + return SWITCH_STATUS_GENERR; + } + + timer->interval = interval; + timer->samples = samples; + timer->samplecount = 0; + timer->timer_interface = timer_interface; + + if (pool) { + timer->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&timer->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(timer, SWITCH_TIMER_FLAG_FREE_POOL); + } + + timer->timer_interface->timer_init(timer); + return SWITCH_STATUS_SUCCESS; + +} + +SWITCH_DECLARE(switch_status_t) switch_core_timer_next(switch_timer_t *timer) +{ + if (!timer->timer_interface) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Timer is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + if (timer->timer_interface->timer_next(timer) == SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_SUCCESS; + } else { + return SWITCH_STATUS_GENERR; + } + +} + +SWITCH_DECLARE(switch_status_t) switch_core_timer_step(switch_timer_t *timer) +{ + if (!timer->timer_interface) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Timer is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + return timer->timer_interface->timer_step(timer); +} + +SWITCH_DECLARE(switch_status_t) switch_core_timer_check(switch_timer_t *timer) +{ + if (!timer->timer_interface) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Timer is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + return timer->timer_interface->timer_check(timer); +} + + +SWITCH_DECLARE(switch_status_t) switch_core_timer_destroy(switch_timer_t *timer) +{ + if (!timer->timer_interface) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Timer is not initilized!\n"); + return SWITCH_STATUS_GENERR; + } + + timer->timer_interface->timer_destroy(timer); + + if (switch_test_flag(timer, SWITCH_TIMER_FLAG_FREE_POOL)) { + switch_core_destroy_memory_pool(&timer->memory_pool); + } + + return SWITCH_STATUS_SUCCESS; +} diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c new file mode 100644 index 0000000000..853a28aa2f --- /dev/null +++ b/src/switch_ivr_async.c @@ -0,0 +1,797 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * + * switch_ivr_async.c -- IVR Library (async operations) + * + */ +#include + +static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + switch_file_handle_t *fh = (switch_file_handle_t *) user_data; + uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_frame_t frame = { 0 }; + + frame.data = data; + frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + break; + case SWITCH_ABC_TYPE_CLOSE: + if (fh) { + switch_core_file_close(fh); + } + break; + case SWITCH_ABC_TYPE_READ: + if (fh) { + switch_size_t len; + + if (switch_core_media_bug_read(bug, &frame) == SWITCH_STATUS_SUCCESS) { + len = (switch_size_t) frame.datalen / 2; + switch_core_file_write(fh, frame.data, &len); + } + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + return SWITCH_TRUE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_record_session(switch_core_session_t *session, char *file) +{ + switch_media_bug_t *bug; + switch_channel_t *channel = switch_core_session_get_channel(session); + + assert(channel != NULL); + if ((bug = switch_channel_get_private(channel, file))) { + switch_channel_set_private(channel, file, NULL); + switch_core_media_bug_remove(session, &bug); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t *session, char *file, + switch_file_handle_t *fh) +{ + switch_channel_t *channel; + switch_codec_t *read_codec; + char *p; + const char *vval; + switch_media_bug_t *bug; + switch_status_t status; + + if (!fh) { + if (!(fh = switch_core_session_alloc(session, sizeof(*fh)))) { + return SWITCH_STATUS_MEMERR; + } + } + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + fh->channels = read_codec->implementation->number_of_channels; + fh->samplerate = read_codec->implementation->samples_per_second; + + + if (switch_core_file_open(fh, + file, + read_codec->implementation->number_of_channels, + read_codec->implementation->samples_per_second, + SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, + switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + + switch_channel_answer(channel); + + if ((p = switch_channel_get_variable(channel, "RECORD_TITLE"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_TITLE, vval); + switch_channel_set_variable(channel, "RECORD_TITLE", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_COPYRIGHT"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COPYRIGHT, vval); + switch_channel_set_variable(channel, "RECORD_COPYRIGHT", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_SOFTWARE"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_SOFTWARE, vval); + switch_channel_set_variable(channel, "RECORD_SOFTWARE", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_ARTIST"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_ARTIST, vval); + switch_channel_set_variable(channel, "RECORD_ARTIST", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_COMMENT"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COMMENT, vval); + switch_channel_set_variable(channel, "RECORD_COMMENT", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_DATE"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_DATE, vval); + switch_channel_set_variable(channel, "RECORD_DATE", NULL); + } + + + + if ((status = switch_core_media_bug_add(session, record_callback, fh, SMBF_BOTH, &bug)) != SWITCH_STATUS_SUCCESS) { + switch_core_file_close(fh); + return status; + } + + switch_channel_set_private(channel, file, bug); + + return SWITCH_STATUS_SUCCESS; +} + +typedef struct { + switch_core_session_t *session; + teletone_dtmf_detect_state_t dtmf_detect; +} switch_inband_dtmf_t; + +static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + switch_inband_dtmf_t *pvt = (switch_inband_dtmf_t *) user_data; + uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_frame_t frame = { 0 }; + char digit_str[80]; + switch_channel_t *channel = switch_core_session_get_channel(pvt->session); + + assert(channel != NULL); + frame.data = data; + frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + break; + case SWITCH_ABC_TYPE_CLOSE: + break; + case SWITCH_ABC_TYPE_READ: + if (switch_core_media_bug_read(bug, &frame) == SWITCH_STATUS_SUCCESS) { + teletone_dtmf_detect(&pvt->dtmf_detect, frame.data, frame.samples); + teletone_dtmf_get(&pvt->dtmf_detect, digit_str, sizeof(digit_str)); + if (digit_str[0]) { + switch_channel_queue_dtmf(channel, digit_str); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DTMF DETECTED: [%s]\n", digit_str); + } + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + return SWITCH_TRUE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_inband_dtmf_session(switch_core_session_t *session) +{ + switch_media_bug_t *bug; + switch_channel_t *channel = switch_core_session_get_channel(session); + + assert(channel != NULL); + if ((bug = switch_channel_get_private(channel, "dtmf"))) { + switch_channel_set_private(channel, "dtmf", NULL); + switch_core_media_bug_remove(session, &bug); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_session(switch_core_session_t *session) +{ + switch_channel_t *channel; + switch_codec_t *read_codec; + switch_media_bug_t *bug; + switch_status_t status; + switch_inband_dtmf_t *pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) { + return SWITCH_STATUS_MEMERR; + } + + teletone_dtmf_detect_init(&pvt->dtmf_detect, read_codec->implementation->samples_per_second); + + pvt->session = session; + + switch_channel_answer(channel); + + if ((status = switch_core_media_bug_add(session, + inband_dtmf_callback, + pvt, SMBF_READ_STREAM, &bug)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + switch_channel_set_private(channel, "dtmf", bug); + + return SWITCH_STATUS_SUCCESS; +} + +struct speech_thread_handle { + switch_core_session_t *session; + switch_asr_handle_t *ah; + switch_media_bug_t *bug; + switch_mutex_t *mutex; + switch_thread_cond_t *cond; + switch_memory_pool_t *pool; +}; + +static void *SWITCH_THREAD_FUNC speech_thread(switch_thread_t *thread, void *obj) +{ + struct speech_thread_handle *sth = (struct speech_thread_handle *) obj; + switch_channel_t *channel = switch_core_session_get_channel(sth->session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + switch_status_t status; + + switch_thread_cond_create(&sth->cond, sth->pool); + switch_mutex_init(&sth->mutex, SWITCH_MUTEX_NESTED, sth->pool); + + + switch_core_session_read_lock(sth->session); + switch_mutex_lock(sth->mutex); + + while (switch_channel_ready(channel) && !switch_test_flag(sth->ah, SWITCH_ASR_FLAG_CLOSED)) { + char *xmlstr = NULL; + + switch_thread_cond_wait(sth->cond, sth->mutex); + if (switch_core_asr_check_results(sth->ah, &flags) == SWITCH_STATUS_SUCCESS) { + switch_event_t *event; + + status = switch_core_asr_get_results(sth->ah, &xmlstr, &flags); + + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + goto done; + } + + + if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH) == SWITCH_STATUS_SUCCESS) { + if (status == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speech-Type", "detected-speech"); + switch_event_add_body(event, "%s", xmlstr); + } else { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speech-Type", "begin-speaking"); + } + + if (switch_test_flag(sth->ah, SWITCH_ASR_FLAG_FIRE_EVENTS)) { + switch_event_t *dup; + + if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { + switch_event_fire(&dup); + } + + } + + if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Event queue failed!\n"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } + + switch_safe_free(xmlstr); + } + } + done: + + switch_mutex_unlock(sth->mutex); + switch_core_session_rwunlock(sth->session); + + return NULL; +} + +static switch_bool_t speech_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + struct speech_thread_handle *sth = (struct speech_thread_handle *) user_data; + uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_frame_t frame = { 0 }; + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + + frame.data = data; + frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; + + switch (type) { + case SWITCH_ABC_TYPE_INIT:{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + + switch_threadattr_create(&thd_attr, sth->pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, speech_thread, sth, sth->pool); + + } + break; + case SWITCH_ABC_TYPE_CLOSE:{ + switch_core_asr_close(sth->ah, &flags); + switch_mutex_lock(sth->mutex); + switch_thread_cond_signal(sth->cond); + switch_mutex_unlock(sth->mutex); + } + break; + case SWITCH_ABC_TYPE_READ: + if (sth->ah) { + if (switch_core_media_bug_read(bug, &frame) == SWITCH_STATUS_SUCCESS) { + if (switch_core_asr_feed(sth->ah, frame.data, frame.datalen, &flags) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error Feeding Data\n"); + return SWITCH_FALSE; + } + if (switch_core_asr_check_results(sth->ah, &flags) == SWITCH_STATUS_SUCCESS) { + switch_mutex_lock(sth->mutex); + switch_thread_cond_signal(sth->cond); + switch_mutex_unlock(sth->mutex); + } + } + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + return SWITCH_TRUE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_detect_speech(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + switch_channel_set_private(channel, SWITCH_SPEECH_KEY, NULL); + switch_core_media_bug_remove(session, &sth->bug); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + + + +SWITCH_DECLARE(switch_status_t) switch_ivr_pause_detect_speech(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + switch_core_asr_pause(sth->ah); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_resume_detect_speech(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + switch_core_asr_resume(sth->ah); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, char *grammar, + char *path) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + if (switch_core_asr_load_grammar(sth->ah, grammar, path) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_unload_grammar(switch_core_session_t *session, char *grammar) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth; + + assert(channel != NULL); + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + if (switch_core_asr_unload_grammar(sth->ah, grammar) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error unloading Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech(switch_core_session_t *session, + char *mod_name, + char *grammar, char *path, char *dest, switch_asr_handle_t *ah) +{ + switch_channel_t *channel; + switch_codec_t *read_codec; + switch_status_t status; + switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; + struct speech_thread_handle *sth; + char *val; + + if (!ah) { + if (!(ah = switch_core_session_alloc(session, sizeof(*ah)))) { + return SWITCH_STATUS_MEMERR; + } + } + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + + if ((val = switch_channel_get_variable(channel, "fire_asr_events"))) { + switch_set_flag(ah, SWITCH_ASR_FLAG_FIRE_EVENTS); + } + + if ((sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY))) { + if (switch_core_asr_load_grammar(sth->ah, grammar, path) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + switch_core_asr_close(sth->ah, &flags); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + } + + if (switch_core_asr_open(ah, + mod_name, + "L16", + read_codec->implementation->samples_per_second, + dest, &flags, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { + + if (switch_core_asr_load_grammar(ah, grammar, path) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error loading Grammar\n"); + switch_core_asr_close(ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + } else { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + + + sth = switch_core_session_alloc(session, sizeof(*sth)); + sth->pool = switch_core_session_get_pool(session); + sth->session = session; + sth->ah = ah; + + if ((status = switch_core_media_bug_add(session, + speech_callback, + sth, SMBF_READ_STREAM, &sth->bug)) != SWITCH_STATUS_SUCCESS) { + switch_core_asr_close(ah, &flags); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return status; + } + + switch_channel_set_private(channel, SWITCH_SPEECH_KEY, sth); + + return SWITCH_STATUS_SUCCESS; +} + + +struct hangup_helper { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_bool_t bleg; + switch_call_cause_t cause; +}; + +static void sch_hangup_callback(switch_scheduler_task_t *task) +{ + struct hangup_helper *helper; + switch_core_session_t *session, *other_session; + char *other_uuid; + + assert(task); + + helper = (struct hangup_helper *) task->cmd_arg; + + if ((session = switch_core_session_locate(helper->uuid_str))) { + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (helper->bleg) { + if ((other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && + (other_session = switch_core_session_locate(other_uuid))) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + switch_channel_hangup(other_channel, helper->cause); + switch_core_session_rwunlock(other_session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No channel to hangup\n"); + } + } else { + switch_channel_hangup(channel, helper->cause); + } + + switch_core_session_rwunlock(session); + } +} + +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_hangup(time_t runtime, char *uuid, switch_call_cause_t cause, + switch_bool_t bleg) +{ + struct hangup_helper *helper; + size_t len = sizeof(*helper); + + switch_zmalloc(helper, len); + + switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); + helper->cause = cause; + helper->bleg = bleg; + + return switch_scheduler_add_task(runtime, sch_hangup_callback, (char *) __SWITCH_FUNC__, uuid, 0, helper, + SSHF_FREE_ARG); +} + +struct transfer_helper { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + char *extension; + char *dialplan; + char *context; +}; + +static void sch_transfer_callback(switch_scheduler_task_t *task) +{ + struct transfer_helper *helper; + switch_core_session_t *session; + + assert(task); + + helper = (struct transfer_helper *) task->cmd_arg; + + if ((session = switch_core_session_locate(helper->uuid_str))) { + switch_ivr_session_transfer(session, helper->extension, helper->dialplan, helper->context); + switch_core_session_rwunlock(session); + } + +} + +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_transfer(time_t runtime, char *uuid, char *extension, char *dialplan, + char *context) +{ + struct transfer_helper *helper; + size_t len = sizeof(*helper); + char *cur = NULL; + + if (extension) { + len += strlen(extension) + 1; + } + + if (dialplan) { + len += strlen(dialplan) + 1; + } + + if (context) { + len += strlen(context) + 1; + } + + switch_zmalloc(helper, len); + + switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); + + cur = (char *) helper + sizeof(*helper); + + if (extension) { + helper->extension = cur; + switch_copy_string(helper->extension, extension, strlen(extension) + 1); + cur += strlen(helper->extension) + 1; + } + + if (dialplan) { + helper->dialplan = cur; + switch_copy_string(helper->dialplan, dialplan, strlen(dialplan) + 1); + cur += strlen(helper->dialplan) + 1; + } + + if (context) { + helper->context = cur; + switch_copy_string(helper->context, context, strlen(context) + 1); + } + + return switch_scheduler_add_task(runtime, sch_transfer_callback, (char *) __SWITCH_FUNC__, uuid, 0, helper, + SSHF_FREE_ARG); +} + + +struct broadcast_helper { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + char *path; + switch_media_flag_t flags; +}; + +static void sch_broadcast_callback(switch_scheduler_task_t *task) +{ + struct broadcast_helper *helper; + assert(task); + + helper = (struct broadcast_helper *) task->cmd_arg; + switch_ivr_broadcast(helper->uuid_str, helper->path, helper->flags); +} + +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_broadcast(time_t runtime, char *uuid, char *path, + switch_media_flag_t flags) +{ + struct broadcast_helper *helper; + size_t len = sizeof(*helper) + strlen(path) + 1; + + switch_zmalloc(helper, len); + + switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); + helper->flags = flags; + helper->path = (char *) helper + sizeof(*helper); + switch_copy_string(helper->path, path, len - sizeof(helper)); + + + return switch_scheduler_add_task(runtime, sch_broadcast_callback, (char *) __SWITCH_FUNC__, uuid, 0, helper, + SSHF_FREE_ARG); +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, switch_media_flag_t flags) +{ + switch_channel_t *channel; + int nomedia; + switch_core_session_t *session, *master; + switch_event_t *event; + switch_core_session_t *other_session = NULL; + char *other_uuid = NULL; + char *app = "playback"; + + assert(path); + + if ((session = switch_core_session_locate(uuid))) { + char *cause = NULL; + char *mypath = strdup(path); + char *p; + + master = session; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if ((nomedia = switch_channel_test_flag(channel, CF_NOMEDIA))) { + switch_ivr_media(uuid, SMF_REBRIDGE); + } + + if ((p = strchr(mypath, ':'))) { + app = mypath; + *p++ = '\0'; + path = p; + } + + if ((cause = strchr(app, '!'))) { + *cause++ = '\0'; + if (!cause) { + cause = "normal_clearing"; + } + } + + if ((flags & SMF_ECHO_BLEG) && (other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) + && (other_session = switch_core_session_locate(other_uuid))) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", "%s", app); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", path); + switch_core_session_queue_private_event(other_session, &event); + } + switch_core_session_rwunlock(other_session); + master = other_session; + other_session = NULL; + } + + if ((flags & SMF_ECHO_ALEG)) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", "%s", app); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", path); + switch_core_session_queue_private_event(session, &event); + } + master = session; + } + + if (nomedia) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "nomedia"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "nomedia-uuid", "%s", uuid); + switch_core_session_queue_private_event(master, &event); + } + } + + if (cause) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", "hangup"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", cause); + + switch_core_session_queue_private_event(session, &event); + } + } + + switch_core_session_rwunlock(session); + switch_safe_free(mypath); + } + + + return SWITCH_STATUS_SUCCESS; + +} diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c new file mode 100644 index 0000000000..45f66273c3 --- /dev/null +++ b/src/switch_ivr_bridge.c @@ -0,0 +1,719 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * switch_ivr_bridge.c -- IVR Library + * + */ +#include + +static const switch_state_handler_table_t audio_bridge_peer_state_handlers; +static const switch_state_handler_table_t originate_state_handlers; + +/* Bridge Related Stuff*/ +/*********************************************************************************/ +struct audio_bridge_data { + switch_core_session_t *session_a; + switch_core_session_t *session_b; + int running; +}; + +static void *audio_bridge_thread(switch_thread_t *thread, void *obj) +{ + switch_core_thread_session_t *his_thread, *data = obj; + int *stream_id_p; + int stream_id = 0, pre_b = 0, ans_a = 0, ans_b = 0, originator = 0; + switch_input_callback_function_t input_callback; + switch_core_session_message_t *message, msg = { 0 }; + void *user_data; + + switch_channel_t *chan_a, *chan_b; + switch_frame_t *read_frame; + switch_core_session_t *session_a, *session_b; + + assert(!thread || thread); + + session_a = data->objs[0]; + session_b = data->objs[1]; + + stream_id_p = data->objs[2]; + input_callback = data->input_callback; + user_data = data->objs[4]; + his_thread = data->objs[5]; + + if (stream_id_p) { + stream_id = *stream_id_p; + } + + chan_a = switch_core_session_get_channel(session_a); + chan_b = switch_core_session_get_channel(session_b); + + ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED); + if ((originator = switch_channel_test_flag(chan_a, CF_ORIGINATOR))) { + pre_b = switch_channel_test_flag(chan_a, CF_EARLY_MEDIA); + ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED); + } + switch_core_session_read_lock(session_a); + switch_core_session_read_lock(session_b); + + switch_channel_set_flag(chan_a, CF_BRIDGED); + + while (switch_channel_ready(chan_a) && data->running > 0 && his_thread->running > 0) { + switch_channel_state_t b_state = switch_channel_get_state(chan_b); + switch_status_t status; + switch_event_t *event; + + switch (b_state) { + case CS_HANGUP: + case CS_DONE: + switch_mutex_lock(data->mutex); + data->running = -1; + switch_mutex_unlock(data->mutex); + continue; + default: + break; + } + + if (switch_channel_test_flag(chan_a, CF_TRANSFER)) { + switch_channel_clear_flag(chan_a, CF_HOLD); + switch_channel_clear_flag(chan_a, CF_SUSPEND); + break; + } + + if (switch_core_session_dequeue_private_event(session_a, &event) == SWITCH_STATUS_SUCCESS) { + switch_channel_set_flag(chan_b, CF_SUSPEND); + switch_ivr_parse_event(session_a, event); + switch_channel_clear_flag(chan_b, CF_SUSPEND); + switch_event_destroy(&event); + } + + /* if 1 channel has DTMF pass it to the other */ + if (switch_channel_has_dtmf(chan_a)) { + char dtmf[128]; + switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf)); + switch_core_session_send_dtmf(session_b, dtmf); + + if (input_callback) { + if (input_callback(session_a, dtmf, SWITCH_INPUT_TYPE_DTMF, user_data, 0) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s ended call via DTMF\n", + switch_channel_get_name(chan_a)); + switch_mutex_lock(data->mutex); + data->running = -1; + switch_mutex_unlock(data->mutex); + break; + } + } + } + + if (switch_core_session_dequeue_event(session_a, &event) == SWITCH_STATUS_SUCCESS) { + if (input_callback) { + status = input_callback(session_a, event, SWITCH_INPUT_TYPE_EVENT, user_data, 0); + } + + if (event->event_id != SWITCH_EVENT_MESSAGE + || switch_core_session_receive_event(session_b, &event) != SWITCH_STATUS_SUCCESS) { + switch_event_destroy(&event); + } + + } + + if (switch_core_session_dequeue_message(session_b, &message) == SWITCH_STATUS_SUCCESS) { + switch_core_session_receive_message(session_a, message); + if (switch_test_flag(message, SCSMF_DYNAMIC)) { + switch_safe_free(message); + } else { + message = NULL; + } + } + + if (!ans_a && originator) { + + if (!ans_b && switch_channel_test_flag(chan_b, CF_ANSWERED)) { + switch_channel_answer(chan_a); + ans_a++; + } else if (!pre_b && switch_channel_test_flag(chan_b, CF_EARLY_MEDIA)) { + switch_channel_pre_answer(chan_a); + pre_b++; + } + if (!pre_b) { + switch_yield(10000); + continue; + } + } + + + if (switch_channel_test_flag(chan_a, CF_SUSPEND) || switch_channel_test_flag(chan_b, CF_SUSPEND)) { + switch_yield(10000); + continue; + } + + /* read audio from 1 channel and write it to the other */ + status = switch_core_session_read_frame(session_a, &read_frame, -1, stream_id); + + if (SWITCH_READ_ACCEPTABLE(status)) { + if (status != SWITCH_STATUS_BREAK && !switch_channel_test_flag(chan_a, CF_HOLD)) { + if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "write: %s Bad Frame....[%u] Bubye!\n", + switch_channel_get_name(chan_b), read_frame->datalen); + switch_mutex_lock(data->mutex); + data->running = -1; + switch_mutex_unlock(data->mutex); + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "read: %s Bad Frame.... Bubye!\n", + switch_channel_get_name(chan_a)); + switch_mutex_lock(data->mutex); + data->running = -1; + switch_mutex_unlock(data->mutex); + } + } + + switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); + + msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE; + msg.from = __FILE__; + switch_core_session_receive_message(session_a, &msg); + + switch_channel_set_variable(chan_a, SWITCH_BRIDGE_VARIABLE, NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BRIDGE THREAD DONE [%s]\n", + switch_channel_get_name(chan_a)); + + switch_channel_clear_flag(chan_a, CF_BRIDGED); + switch_mutex_lock(data->mutex); + data->running = 0; + switch_mutex_unlock(data->mutex); + + switch_core_session_rwunlock(session_a); + switch_core_session_rwunlock(session_b); + return NULL; +} + +static switch_status_t audio_bridge_on_loopback(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + void *arg; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if ((arg = switch_channel_get_private(channel, "_bridge_"))) { + switch_channel_set_private(channel, "_bridge_", NULL); + audio_bridge_thread(NULL, (void *) arg); + } else { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + switch_channel_clear_state_handler(channel, &audio_bridge_peer_state_handlers); + + if (!switch_channel_test_flag(channel, CF_TRANSFER)) { + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + } + + return SWITCH_STATUS_FALSE; +} + + +static switch_status_t audio_bridge_on_ring(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM RING\n"); + + /* put the channel in a passive state so we can loop audio to it */ + switch_channel_set_state(channel, CS_HOLD); + return SWITCH_STATUS_FALSE; +} + +static switch_status_t audio_bridge_on_hold(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM HOLD\n"); + + /* put the channel in a passive state so we can loop audio to it */ + return SWITCH_STATUS_FALSE; +} + +static const switch_state_handler_table_t audio_bridge_peer_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ audio_bridge_on_ring, + /*.on_execute */ NULL, + /*.on_hangup */ NULL, + /*.on_loopback */ audio_bridge_on_loopback, + /*.on_transmit */ NULL, + /*.on_hold */ audio_bridge_on_hold, +}; + + + +static switch_status_t uuid_bridge_on_transmit(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + switch_core_session_t *other_session; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM TRANSMIT\n"); + switch_channel_clear_state_handler(channel, NULL); + + + if (!switch_channel_test_flag(channel, CF_ORIGINATOR)) { + return SWITCH_STATUS_FALSE; + } + + if ((other_session = switch_channel_get_private(channel, SWITCH_UUID_BRIDGE))) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + switch_channel_state_t state = switch_channel_get_state(other_channel); + switch_channel_state_t mystate = switch_channel_get_state(channel); + switch_event_t *event; + uint8_t ready_a, ready_b; + switch_caller_profile_t *profile, *new_profile; + + + switch_channel_clear_flag(channel, CF_TRANSFER); + switch_channel_set_private(channel, SWITCH_UUID_BRIDGE, NULL); + + while (mystate <= CS_HANGUP && state <= CS_HANGUP && !switch_channel_test_flag(other_channel, CF_TAGGED)) { + switch_yield(1000); + state = switch_channel_get_state(other_channel); + mystate = switch_channel_get_state(channel); + } + + switch_channel_clear_flag(other_channel, CF_TRANSFER); + switch_channel_clear_flag(other_channel, CF_TAGGED); + + + switch_core_session_reset(session); + switch_core_session_reset(other_session); + + ready_a = switch_channel_ready(channel); + ready_b = switch_channel_ready(other_channel); + + if (!ready_a || !ready_b) { + if (!ready_a) { + switch_channel_hangup(other_channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + + if (!ready_b) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + return SWITCH_STATUS_FALSE; + } + + /* add another profile to both sessions for CDR's sake */ + if ((profile = switch_channel_get_caller_profile(channel))) { + new_profile = switch_caller_profile_clone(session, profile); + new_profile->destination_number = + switch_core_session_strdup(session, switch_core_session_get_uuid(other_session)); + switch_channel_set_caller_profile(channel, new_profile); + } + + if ((profile = switch_channel_get_caller_profile(other_channel))) { + new_profile = switch_caller_profile_clone(other_session, profile); + new_profile->destination_number = + switch_core_session_strdup(other_session, switch_core_session_get_uuid(session)); + switch_channel_set_caller_profile(other_channel, new_profile); + } + + /* fire events that will change the data table from "show channels" */ + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", "%s", + switch_core_session_get_uuid(other_session)); + switch_event_fire(&event); + } + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(other_channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", "%s", + switch_core_session_get_uuid(session)); + switch_event_fire(&event); + } + + switch_ivr_multi_threaded_bridge(session, other_session, NULL, NULL, NULL); + } else { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + + + + return SWITCH_STATUS_FALSE; +} + +static const switch_state_handler_table_t uuid_bridge_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ NULL, + /*.on_loopback */ NULL, + /*.on_transmit */ uuid_bridge_on_transmit, + /*.on_hold */ NULL +}; + +static switch_status_t signal_bridge_on_hibernate(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + switch_channel_clear_flag(channel, CF_TRANSFER); + + switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, + switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t signal_bridge_on_hangup(switch_core_session_t *session) +{ + char *uuid; + switch_channel_t *channel = NULL; + switch_core_session_t *other_session; + switch_event_t *event; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (switch_channel_test_flag(channel, CF_ORIGINATOR)) { + switch_channel_clear_flag(channel, CF_ORIGINATOR); + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + } + + + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + switch_channel_t *other_channel = NULL; + + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + + switch_channel_set_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL); + switch_channel_set_variable(other_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL); + + switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, NULL); + switch_channel_set_variable(other_channel, SWITCH_BRIDGE_VARIABLE, NULL); + + if (switch_channel_get_state(other_channel) < CS_HANGUP && + switch_true(switch_channel_get_variable(other_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE))) { + switch_channel_hangup(other_channel, switch_channel_get_cause(channel)); + } else { + switch_channel_set_state(other_channel, CS_EXECUTE); + } + switch_core_session_rwunlock(other_session); + } + + + return SWITCH_STATUS_SUCCESS; +} + +static const switch_state_handler_table_t signal_bridge_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ signal_bridge_on_hangup, + /*.on_loopback */ NULL, + /*.on_transmit */ NULL, + /*.on_hold */ NULL, + /*.on_hibernate */ signal_bridge_on_hibernate +}; + +SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t *session, + switch_core_session_t *peer_session) +{ + switch_channel_t *caller_channel, *peer_channel; + switch_event_t *event; + + caller_channel = switch_core_session_get_channel(session); + assert(caller_channel != NULL); + + peer_channel = switch_core_session_get_channel(peer_session); + assert(peer_channel != NULL); + + if (!switch_channel_ready(peer_channel)) { + switch_channel_hangup(caller_channel, switch_channel_get_cause(peer_channel)); + return SWITCH_STATUS_FALSE; + } + + if (!switch_channel_ready(caller_channel)) { + switch_channel_hangup(peer_channel, SWITCH_CAUSE_ORIGINATOR_CANCEL); + return SWITCH_STATUS_FALSE; + } + + switch_channel_set_variable(caller_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, + switch_core_session_get_uuid(peer_session)); + switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(session)); + + switch_channel_set_flag(caller_channel, CF_ORIGINATOR); + + switch_channel_clear_state_handler(caller_channel, NULL); + switch_channel_clear_state_handler(peer_channel, NULL); + + switch_channel_add_state_handler(caller_channel, &signal_bridge_state_handlers); + switch_channel_add_state_handler(peer_channel, &signal_bridge_state_handlers); + + + /* fire events that will change the data table from "show channels" */ + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(caller_channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "signal_bridge"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", "%s", + switch_core_session_get_uuid(peer_session)); + switch_event_fire(&event); + } + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(peer_channel, event); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application", "signal_bridge"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Application-Data", "%s", + switch_core_session_get_uuid(session)); + switch_event_fire(&event); + } + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(caller_channel, event); + switch_event_fire(&event); + } + + switch_channel_set_state_flag(caller_channel, CF_TRANSFER); + switch_channel_set_state_flag(peer_channel, CF_TRANSFER); + + + switch_channel_set_state(caller_channel, CS_HIBERNATE); + switch_channel_set_state(peer_channel, CS_HIBERNATE); + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_session_t *session, + switch_core_session_t *peer_session, + switch_input_callback_function_t input_callback, + void *session_data, void *peer_session_data) +{ + switch_core_thread_session_t *this_audio_thread, *other_audio_thread; + switch_channel_t *caller_channel, *peer_channel; + int stream_id = 0; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + caller_channel = switch_core_session_get_channel(session); + assert(caller_channel != NULL); + + switch_channel_set_flag(caller_channel, CF_ORIGINATOR); + + peer_channel = switch_core_session_get_channel(peer_session); + assert(peer_channel != NULL); + + other_audio_thread = switch_core_session_alloc(peer_session, sizeof(switch_core_thread_session_t)); + this_audio_thread = switch_core_session_alloc(peer_session, sizeof(switch_core_thread_session_t)); + + other_audio_thread->objs[0] = session; + other_audio_thread->objs[1] = peer_session; + other_audio_thread->objs[2] = &stream_id; + other_audio_thread->input_callback = input_callback; + other_audio_thread->objs[4] = session_data; + other_audio_thread->objs[5] = this_audio_thread; + other_audio_thread->running = 5; + switch_mutex_init(&other_audio_thread->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + + this_audio_thread->objs[0] = peer_session; + this_audio_thread->objs[1] = session; + this_audio_thread->objs[2] = &stream_id; + this_audio_thread->input_callback = input_callback; + this_audio_thread->objs[4] = peer_session_data; + this_audio_thread->objs[5] = other_audio_thread; + this_audio_thread->running = 2; + switch_mutex_init(&this_audio_thread->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(peer_session)); + + switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers); + + if (switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_ANSWERED)) { + switch_channel_answer(caller_channel); + } + + if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { + switch_event_t *event; + switch_core_session_message_t msg = { 0 }; + + switch_channel_set_state(peer_channel, CS_HOLD); + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(caller_channel, event); + switch_event_fire(&event); + } + + if (switch_core_session_read_lock(peer_session) == SWITCH_STATUS_SUCCESS) { + switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_VARIABLE, + switch_core_session_get_uuid(peer_session)); + switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_VARIABLE, switch_core_session_get_uuid(session)); + + msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE; + msg.from = __FILE__; + msg.pointer_arg = session; + + switch_core_session_receive_message(peer_session, &msg); + + if (!msg.pointer_arg) { + status = SWITCH_STATUS_FALSE; + switch_core_session_rwunlock(peer_session); + goto done; + } + + msg.pointer_arg = peer_session; + switch_core_session_receive_message(session, &msg); + + if (!msg.pointer_arg) { + status = SWITCH_STATUS_FALSE; + switch_core_session_rwunlock(peer_session); + goto done; + } + + + switch_channel_set_private(peer_channel, "_bridge_", other_audio_thread); + switch_channel_set_state(peer_channel, CS_LOOPBACK); + audio_bridge_thread(NULL, (void *) this_audio_thread); + + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(caller_channel, event); + switch_event_fire(&event); + } + + this_audio_thread->objs[0] = NULL; + this_audio_thread->objs[1] = NULL; + this_audio_thread->objs[2] = NULL; + this_audio_thread->input_callback = NULL; + this_audio_thread->objs[4] = NULL; + this_audio_thread->objs[5] = NULL; + switch_mutex_lock(this_audio_thread->mutex); + this_audio_thread->running = 0; + switch_mutex_unlock(this_audio_thread->mutex); + + switch_channel_clear_flag(caller_channel, CF_ORIGINATOR); + + if (other_audio_thread->running > 0) { + switch_mutex_lock(other_audio_thread->mutex); + other_audio_thread->running = -1; + switch_mutex_unlock(other_audio_thread->mutex); + while (other_audio_thread->running) { + switch_yield(1000); + } + } + + switch_core_session_rwunlock(peer_session); + + } else { + status = SWITCH_STATUS_FALSE; + } + } else { + status = SWITCH_STATUS_FALSE; + } + + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bridge Failed %s->%s\n", + switch_channel_get_name(caller_channel), switch_channel_get_name(peer_channel) + ); + switch_channel_hangup(peer_channel, SWITCH_CAUSE_NO_ANSWER); + } + + done: + + if (switch_channel_get_state(caller_channel) < CS_HANGUP && + switch_true(switch_channel_get_variable(caller_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE))) { + switch_channel_hangup(caller_channel, switch_channel_get_cause(peer_channel)); + } + + return status; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(char *originator_uuid, char *originatee_uuid) +{ + switch_core_session_t *originator_session, *originatee_session; + switch_channel_t *originator_channel, *originatee_channel; + switch_status_t status = SWITCH_STATUS_FALSE; + + if ((originator_session = switch_core_session_locate(originator_uuid))) { + if ((originatee_session = switch_core_session_locate(originatee_uuid))) { + originator_channel = switch_core_session_get_channel(originator_session); + originatee_channel = switch_core_session_get_channel(originatee_session); + + /* override transmit state for originator_channel to bridge to originatee_channel + * install pointer to originatee_session into originator_channel + * set CF_TRANSFER on both channels and change state to CS_TRANSMIT to + * inturrupt anything they are already doing. + * originatee_session will fall asleep and originator_session will bridge to it + */ + + switch_channel_clear_state_handler(originator_channel, NULL); + switch_channel_clear_state_handler(originatee_channel, NULL); + switch_channel_set_flag(originator_channel, CF_ORIGINATOR); + switch_channel_add_state_handler(originator_channel, &uuid_bridge_state_handlers); + switch_channel_add_state_handler(originatee_channel, &uuid_bridge_state_handlers); + switch_channel_set_flag(originatee_channel, CF_TAGGED); + switch_channel_set_private(originator_channel, SWITCH_UUID_BRIDGE, originatee_session); + + /* switch_channel_set_state_flag sets flags you want to be set when the next state change happens */ + switch_channel_set_state_flag(originator_channel, CF_TRANSFER); + switch_channel_set_state_flag(originatee_channel, CF_TRANSFER); + + /* release the read locks we have on the channels */ + switch_core_session_rwunlock(originator_session); + switch_core_session_rwunlock(originatee_session); + + /* change the states and let the chips fall where they may */ + switch_channel_set_state(originator_channel, CS_TRANSMIT); + switch_channel_set_state(originatee_channel, CS_TRANSMIT); + + status = SWITCH_STATUS_SUCCESS; + + while (switch_channel_get_state(originatee_channel) < CS_HANGUP + && switch_channel_test_flag(originatee_channel, CF_TAGGED)) { + switch_yield(20000); + } + } else { + switch_core_session_rwunlock(originator_session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "no channel for originatee uuid %s\n", + originatee_uuid); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "no channel for originator uuid %s\n", + originator_uuid); + } + + return status; + +} diff --git a/src/switch_ivr_menu.c b/src/switch_ivr_menu.c new file mode 100644 index 0000000000..8eda8a380f --- /dev/null +++ b/src/switch_ivr_menu.c @@ -0,0 +1,686 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Neal Horman + * + * switch_ivr_menu.c -- IVR Library (menu code) + * + */ +#include + +struct switch_ivr_menu_action; + +struct switch_ivr_menu { + char *name; + char *greeting_sound; + char *short_greeting_sound; + char *invalid_sound; + char *exit_sound; + char *tts_engine; + char *tts_voice; + char *phrase_lang; + char *buf; + char *ptr; + int max_failures; + int timeout; + uint32_t inlen; + uint32_t flags; + struct switch_ivr_menu_action *actions; + struct switch_ivr_menu *next; + switch_memory_pool_t *pool; +}; + +struct switch_ivr_menu_action { + switch_ivr_menu_action_function_t *function; + switch_ivr_action_t ivr_action; + char *arg; + char *bind; + struct switch_ivr_menu_action *next; +}; + +static switch_ivr_menu_t *switch_ivr_menu_find(switch_ivr_menu_t *stack, const char *name) +{ + switch_ivr_menu_t *ret; + for (ret = stack; ret; ret = ret->next) { + if (!name || !strcmp(ret->name, name)) + break; + } + return ret; +} + +static void switch_ivr_menu_stack_add(switch_ivr_menu_t **top, switch_ivr_menu_t *bottom) +{ + switch_ivr_menu_t *ptr; + + for (ptr = *top; ptr && ptr->next; ptr = ptr->next); + + if (ptr) { + ptr->next = bottom; + } else { + *top = bottom; + } + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_init(switch_ivr_menu_t **new_menu, + switch_ivr_menu_t *main, + const char *name, + const char *greeting_sound, + const char *short_greeting_sound, + const char *invalid_sound, + const char *exit_sound, + const char *tts_engine, + const char *tts_voice, + const char *phrase_lang, + int timeout, int max_failures, switch_memory_pool_t *pool) +{ + switch_ivr_menu_t *menu; + uint8_t newpool = 0; + + if (!pool) { + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); + return SWITCH_STATUS_MEMERR; + } + newpool = 1; + } + + if (!(menu = switch_core_alloc(pool, sizeof(*menu)))) { + if (newpool) { + switch_core_destroy_memory_pool(&pool); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); + return SWITCH_STATUS_MEMERR; + } + } + + menu->pool = pool; + + if (!switch_strlen_zero(name)) { + menu->name = switch_core_strdup(menu->pool, name); + } + + if (!switch_strlen_zero(greeting_sound)) { + menu->greeting_sound = switch_core_strdup(menu->pool, greeting_sound); + } + + if (!switch_strlen_zero(short_greeting_sound)) { + menu->short_greeting_sound = switch_core_strdup(menu->pool, short_greeting_sound); + } + + if (!switch_strlen_zero(invalid_sound)) { + menu->invalid_sound = switch_core_strdup(menu->pool, invalid_sound); + } + + if (!switch_strlen_zero(exit_sound)) { + menu->exit_sound = switch_core_strdup(menu->pool, exit_sound); + } + + if (!switch_strlen_zero(tts_engine)) { + menu->tts_engine = switch_core_strdup(menu->pool, tts_engine); + } + + if (!switch_strlen_zero(tts_voice)) { + menu->tts_voice = switch_core_strdup(menu->pool, tts_voice); + } + + if (!switch_strlen_zero(phrase_lang)) { + menu->phrase_lang = switch_core_strdup(menu->pool, phrase_lang); + } + + menu->max_failures = max_failures; + + menu->timeout = timeout; + + menu->actions = NULL; + + if (newpool) { + menu->flags |= SWITCH_IVR_MENU_FLAG_FREEPOOL; + } + + if (menu->timeout <= 0) { + menu->timeout = 10000; + } + + if (main) { + switch_ivr_menu_stack_add(&main, menu); + } else { + menu->flags |= SWITCH_IVR_MENU_FLAG_STACK; + } + + *new_menu = menu; + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_bind_action(switch_ivr_menu_t *menu, switch_ivr_action_t ivr_action, + const char *arg, const char *bind) +{ + switch_ivr_menu_action_t *action; + uint32_t len; + + if ((action = switch_core_alloc(menu->pool, sizeof(*action)))) { + action->bind = switch_core_strdup(menu->pool, bind); + action->next = menu->actions; + action->arg = switch_core_strdup(menu->pool, arg); + len = (uint32_t) strlen(action->bind) + 1; + if (len > menu->inlen) { + menu->inlen = len; + } + action->ivr_action = ivr_action; + menu->actions = action; + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_bind_function(switch_ivr_menu_t *menu, + switch_ivr_menu_action_function_t * function, + const char *arg, const char *bind) +{ + switch_ivr_menu_action_t *action; + uint32_t len; + + if ((action = switch_core_alloc(menu->pool, sizeof(*action)))) { + action->bind = switch_core_strdup(menu->pool, bind); + action->next = menu->actions; + action->arg = switch_core_strdup(menu->pool, arg); + len = (uint32_t) strlen(action->bind) + 1; + if (len > menu->inlen) { + menu->inlen = len; + } + action->function = function; + menu->actions = action; + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_MEMERR; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_free(switch_ivr_menu_t *stack) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (stack != NULL && stack->pool != NULL) { + if (switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_STACK) + && switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_FREEPOOL)) { + switch_memory_pool_t *pool = stack->pool; + status = switch_core_destroy_memory_pool(&pool); + } else { + status = SWITCH_STATUS_SUCCESS; + } + } + + return status; +} + +static switch_status_t play_or_say(switch_core_session_t *session, switch_ivr_menu_t *menu, char *sound, uint32_t need) +{ + char terminator; + uint32_t len; + char *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + switch_input_args_t args = { 0 }; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "play_or_say sound=[%s]\n", sound); + + if (session != NULL && menu != NULL && !switch_strlen_zero(sound)) { + memset(menu->buf, 0, menu->inlen); + menu->ptr = menu->buf; + + if (!need) { + len = 1; + ptr = NULL; + } else { + len = menu->inlen; + ptr = menu->ptr; + } + args.buf = ptr; + args.buflen = len; + + if (*sound == '/' || *sound == '\\') { + status = switch_ivr_play_file(session, NULL, sound, &args); + } else { + if (strlen(sound) > 4 && strncmp(sound, "say:", 4) == 0) { + if (menu->tts_engine && menu->tts_voice) { + status = switch_ivr_speak_text(session, menu->tts_engine, menu->tts_voice, 0, sound + 4, &args); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No TTS engine to play sound\n"); + } + } else { + if (strlen(sound) > 7 && strncmp(sound, "phrase:", 7) == 0) { + status = switch_ivr_phrase_macro(session, sound + 7, "", menu->phrase_lang, &args); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "play_or_say: no player for [%s]. Use say: or phrase:\n", sound); + } + } + } + + if (need) { + menu->ptr += strlen(menu->buf); + if (strlen(menu->buf) < need) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "waiting for %u digits\n", need); + status = + switch_ivr_collect_digits_count(session, menu->ptr, menu->inlen - strlen(menu->buf), need, "#", + &terminator, menu->timeout); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "digits '%s'\n", menu->buf); + } + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "play_or_say returning [%d]\n", status); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_execute(switch_core_session_t *session, switch_ivr_menu_t *stack, + char *name, void *obj) +{ + int reps = 0, errs = 0, match = 0, running = 1; + char *greeting_sound = NULL, *aptr = NULL; + char arg[512]; + switch_ivr_action_t todo = SWITCH_IVR_ACTION_DIE; + switch_ivr_menu_action_t *ap; + switch_ivr_menu_t *menu; + switch_channel_t *channel; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (session == NULL || stack == NULL || switch_strlen_zero(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid menu context\n"); + return SWITCH_STATUS_FALSE; + } + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (!(menu = switch_ivr_menu_find(stack, name))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Menu!\n"); + return SWITCH_STATUS_FALSE; + } + + if (!(menu->buf = malloc(menu->inlen))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Memory!\n"); + return SWITCH_STATUS_FALSE; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Executing IVR menu %s\n", menu->name); + + for (reps = 0; (running && status == SWITCH_STATUS_SUCCESS && errs < menu->max_failures); reps++) { + if (!switch_channel_ready(channel)) { + break; + } + + if (reps > 0 && menu->short_greeting_sound) { + greeting_sound = menu->short_greeting_sound; + } else { + greeting_sound = menu->greeting_sound; + } + + match = 0; + aptr = NULL; + + memset(arg, 0, sizeof(arg)); + + memset(menu->buf, 0, menu->inlen); + status = play_or_say(session, menu, greeting_sound, menu->inlen - 1); + + if (!switch_strlen_zero(menu->buf)) { + for (ap = menu->actions; ap; ap = ap->next) { + if (!strcmp(menu->buf, ap->bind)) { + char *membuf; + + match++; + errs = 0; + if (ap->function) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "IVR function on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf, + ap->arg); + todo = ap->function(menu, ap->arg, arg, sizeof(arg), obj); + aptr = arg; + } else { + todo = ap->ivr_action; + aptr = ap->arg; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "IVR action on menu '%s' matched '%s' param '%s'\n", menu->name, menu->buf, + aptr); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "switch_ivr_menu_execute todo=[%d]\n", + todo); + + switch (todo) { + case SWITCH_IVR_ACTION_DIE: + status = SWITCH_STATUS_FALSE; + break; + case SWITCH_IVR_ACTION_PLAYSOUND: + status = switch_ivr_play_file(session, NULL, aptr, NULL); + break; + case SWITCH_IVR_ACTION_SAYTEXT: + status = switch_ivr_speak_text(session, menu->tts_engine, menu->tts_voice, 0, aptr, NULL); + break; + case SWITCH_IVR_ACTION_SAYPHRASE: + status = switch_ivr_phrase_macro(session, aptr, "", menu->phrase_lang, NULL); + break; + case SWITCH_IVR_ACTION_TRANSFER: + switch_ivr_session_transfer(session, aptr, NULL, NULL); + running = 0; + break; + case SWITCH_IVR_ACTION_EXECMENU: + reps = -1; + status = switch_ivr_menu_execute(session, stack, aptr, obj); + break; + case SWITCH_IVR_ACTION_EXECAPP:{ + const switch_application_interface_t *application_interface; + + if ((membuf = strdup(aptr))) { + char *app_name = membuf; + char *app_arg = strchr(app_name, ' '); + + if (app_arg) { + *app_arg = '\0'; + app_arg++; + } + + if (app_name && app_arg) { + if ((application_interface = + switch_loadable_module_get_application_interface(app_name))) { + if (application_interface->application_function) { + application_interface->application_function(session, app_arg); + } + } + } + } + } + break; + case SWITCH_IVR_ACTION_BACK: + running = 0; + status = SWITCH_STATUS_SUCCESS; + break; + case SWITCH_IVR_ACTION_TOMAIN: + switch_set_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN); + status = SWITCH_STATUS_BREAK; + break; + case SWITCH_IVR_ACTION_NOOP: + status = SWITCH_STATUS_SUCCESS; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid TODO!\n"); + break; + } + } + } + + + if (switch_test_flag(menu, SWITCH_IVR_MENU_FLAG_STACK)) { // top level + if (switch_test_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN)) { // catch the fallback and recover + switch_clear_flag(stack, SWITCH_IVR_MENU_FLAG_FALLTOMAIN); + status = SWITCH_STATUS_SUCCESS; + running = 1; + continue; + } + } + } + if (!match) { + if (*menu->buf) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IVR menu '%s' caught invalid input '%s'\n", + menu->name, menu->buf); + if (menu->invalid_sound) { + play_or_say(session, menu, menu->invalid_sound, 0); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IVR menu '%s' no input detected\n", + menu->name); + } + errs++; + if (status == SWITCH_STATUS_SUCCESS) { + status = switch_ivr_sleep(session, 1000); + } + /* breaks are ok too */ + if (SWITCH_STATUS_IS_BREAK(status)) { + status = SWITCH_STATUS_SUCCESS; + } + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "exit-sound '%s'\n", menu->exit_sound); + if (!switch_strlen_zero(menu->exit_sound)) { + play_or_say(session, menu, menu->exit_sound, 0); + } + + switch_safe_free(menu->buf); + + return status; +} + +/******************************************************************************************************/ + +typedef struct switch_ivr_menu_xml_map { + char *name; + switch_ivr_action_t action; + switch_ivr_menu_action_function_t *function; + struct switch_ivr_menu_xml_map *next; +} switch_ivr_menu_xml_map_t; + +struct switch_ivr_menu_xml_ctx { + switch_memory_pool_t *pool; + struct switch_ivr_menu_xml_map *map; + int autocreated; +}; + +static switch_ivr_menu_xml_map_t *switch_ivr_menu_stack_xml_find(switch_ivr_menu_xml_ctx_t *xml_ctx, char *name) +{ + switch_ivr_menu_xml_map_t *map = (xml_ctx != NULL ? xml_ctx->map : NULL); + int rc = -1; + + while (map != NULL && (rc = strcasecmp(map->name, name)) != 0) { + map = map->next; + } + + return (rc == 0 ? map : NULL); +} + +static switch_status_t switch_ivr_menu_stack_xml_add(switch_ivr_menu_xml_ctx_t *xml_ctx, char *name, int action, + switch_ivr_menu_action_function_t * function) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + // if this action/function does not exist yet + if (xml_ctx != NULL && name != NULL && xml_ctx->pool != NULL + && switch_ivr_menu_stack_xml_find(xml_ctx, name) == NULL) { + switch_ivr_menu_xml_map_t *map = switch_core_alloc(xml_ctx->pool, sizeof(switch_ivr_menu_xml_map_t)); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "switch_ivr_menu_stack_xml_add bindng '%s'\n", name); + // and we have memory + if (map != NULL) { + map->name = switch_core_strdup(xml_ctx->pool, name); + map->action = action; + map->function = function; + + if (map->name != NULL) { + // insert map item at top of list + map->next = xml_ctx->map; + xml_ctx->map = map; + status = SWITCH_STATUS_SUCCESS; + } else { + status = SWITCH_STATUS_MEMERR; + } + } else { + status = SWITCH_STATUS_MEMERR; + } + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_init(switch_ivr_menu_xml_ctx_t **xml_menu_ctx, + switch_memory_pool_t *pool) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + int autocreated = 0; + + // build a memory pool ? + if (pool == NULL) { + status = switch_core_new_memory_pool(&pool); + autocreated = 1; + } + // allocate the xml context + if (xml_menu_ctx != NULL && pool != NULL) { + *xml_menu_ctx = switch_core_alloc(pool, sizeof(switch_ivr_menu_xml_ctx_t)); + if (*xml_menu_ctx != NULL) { + (*xml_menu_ctx)->pool = pool; + (*xml_menu_ctx)->autocreated = autocreated; + (*xml_menu_ctx)->map = NULL; + status = SWITCH_STATUS_SUCCESS; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to alloc xml_ctx\n"); + status = SWITCH_STATUS_FALSE; + } + } + // build the standard/default xml menu handler mappings + if (status == SWITCH_STATUS_SUCCESS && xml_menu_ctx != NULL && *xml_menu_ctx != NULL) { + struct iam_s { + char *name; + switch_ivr_action_t action; + } iam[] = { + { + "menu-exit", SWITCH_IVR_ACTION_DIE}, { + "menu-sub", SWITCH_IVR_ACTION_EXECMENU}, { + "menu-exec-api", SWITCH_IVR_ACTION_EXECAPP}, { + "menu-play-sound", SWITCH_IVR_ACTION_PLAYSOUND}, { + "menu-say-text", SWITCH_IVR_ACTION_SAYTEXT}, { + "menu-say-phrase", SWITCH_IVR_ACTION_SAYPHRASE}, { + "menu-back", SWITCH_IVR_ACTION_BACK}, { + "menu-top", SWITCH_IVR_ACTION_TOMAIN}, { + "menu-call-transfer", SWITCH_IVR_ACTION_TRANSFER},}; + int iam_qty = (sizeof(iam) / sizeof(iam[0])); + int i; + + for (i = 0; i < iam_qty && status == SWITCH_STATUS_SUCCESS; i++) { + status = switch_ivr_menu_stack_xml_add(*xml_menu_ctx, iam[i].name, iam[i].action, NULL); + } + } + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_add_custom(switch_ivr_menu_xml_ctx_t *xml_menu_ctx, + char *name, + switch_ivr_menu_action_function_t * function) +{ + return switch_ivr_menu_stack_xml_add(xml_menu_ctx, name, -1, function); +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_build(switch_ivr_menu_xml_ctx_t *xml_menu_ctx, + switch_ivr_menu_t **menu_stack, + switch_xml_t xml_menus, switch_xml_t xml_menu) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + + if (xml_menu_ctx != NULL && menu_stack != NULL && xml_menu != NULL) { + const char *menu_name = switch_xml_attr_soft(xml_menu, "name"); // if the attr doesn't exist, return "" + const char *greet_long = switch_xml_attr(xml_menu, "greet-long"); // if the attr doesn't exist, return NULL + const char *greet_short = switch_xml_attr(xml_menu, "greet-short"); // if the attr doesn't exist, return NULL + const char *invalid_sound = switch_xml_attr(xml_menu, "invalid-sound"); // if the attr doesn't exist, return NULL + const char *exit_sound = switch_xml_attr(xml_menu, "exit-sound"); // if the attr doesn't exist, return NULL + const char *tts_engine = switch_xml_attr(xml_menu, "tts-engine"); // if the attr doesn't exist, return NULL + const char *tts_voice = switch_xml_attr(xml_menu, "tts-voice"); // if the attr doesn't exist, return NULL + const char *phrase_lang = switch_xml_attr(xml_menu, "phrase-lang"); // if the attr doesn't exist, return NULL + const char *timeout = switch_xml_attr_soft(xml_menu, "timeout"); // if the attr doesn't exist, return "" + const char *max_failures = switch_xml_attr_soft(xml_menu, "max-failures"); // if the attr doesn't exist, return "" + switch_ivr_menu_t *menu = NULL; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "building menu '%s'\n", menu_name); + status = switch_ivr_menu_init(&menu, + *menu_stack, + menu_name, + greet_long, + greet_short, + invalid_sound, + exit_sound, + tts_engine, + tts_voice, + phrase_lang, atoi(timeout) * 1000, atoi(max_failures), xml_menu_ctx->pool); + // set the menu_stack for the caller + if (status == SWITCH_STATUS_SUCCESS && *menu_stack == NULL) { + *menu_stack = menu; + } + + if (status == SWITCH_STATUS_SUCCESS && menu != NULL) { + switch_xml_t xml_kvp; + + // build menu entries + for (xml_kvp = switch_xml_child(xml_menu, "entry"); xml_kvp != NULL && status == SWITCH_STATUS_SUCCESS; + xml_kvp = xml_kvp->next) { + const char *action = switch_xml_attr(xml_kvp, "action"); + const char *digits = switch_xml_attr(xml_kvp, "digits"); + const char *param = switch_xml_attr_soft(xml_kvp, "param"); + + if (!switch_strlen_zero(action) && !switch_strlen_zero(digits)) { + switch_ivr_menu_xml_map_t *xml_map = xml_menu_ctx->map; + int found = 0; + + // find and appropriate xml handler + while (xml_map != NULL && !found) { + if (!(found = (strcasecmp(xml_map->name, action) == 0))) { + xml_map = xml_map->next; + } + } + + if (found && xml_map != NULL) { + // do we need to build a new sub-menu ? + if (xml_map->action == SWITCH_IVR_ACTION_EXECMENU + && switch_ivr_menu_find(*menu_stack, param) == NULL) { + if ((xml_menu = switch_xml_find_child(xml_menus, "menu", "name", param)) != NULL) { + status = switch_ivr_menu_stack_xml_build(xml_menu_ctx, menu_stack, xml_menus, xml_menu); + } + } + // finally bind the menu entry + if (status == SWITCH_STATUS_SUCCESS) { + if (xml_map->function != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "binding menu caller control '%s'/'%s' to '%s'\n", xml_map->name, + param, digits); + status = switch_ivr_menu_bind_function(menu, xml_map->function, param, digits); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "binding menu action '%s' to '%s'\n", xml_map->name, digits); + status = switch_ivr_menu_bind_action(menu, xml_map->action, param, digits); + } + } + } + } else { + status = SWITCH_STATUS_FALSE; + } + } + } + } + + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to build xml menu\n"); + } + + return status; +} diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c new file mode 100644 index 0000000000..52908d56bb --- /dev/null +++ b/src/switch_ivr_originate.c @@ -0,0 +1,963 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * + * switch_ivr_originate.c -- IVR Library (originate) + * + */ +#include + +static const switch_state_handler_table_t originate_state_handlers; + +static switch_status_t originate_on_ring(switch_core_session_t *session) +{ + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + /* put the channel in a passive state so we can loop audio to it */ + + /* clear this handler so it only works once (next time (a.k.a. Transfer) we will do the real ring state) */ + switch_channel_clear_state_handler(channel, &originate_state_handlers); + + switch_channel_set_state(channel, CS_HOLD); + + return SWITCH_STATUS_FALSE; +} + +static const switch_state_handler_table_t originate_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ originate_on_ring, + /*.on_execute */ NULL, + /*.on_hangup */ NULL, + /*.on_loopback */ NULL, + /*.on_transmit */ NULL, + /*.on_hold */ NULL +}; + + +typedef enum { + IDX_CANCEL = -2, + IDX_NADA = -1 +} abort_t; + +struct key_collect { + char *key; + char *file; + switch_core_session_t *session; +}; + +static void *SWITCH_THREAD_FUNC collect_thread_run(switch_thread_t *thread, void *obj) +{ + struct key_collect *collect = (struct key_collect *) obj; + switch_channel_t *channel = switch_core_session_get_channel(collect->session); + char buf[10] = ""; + char *p, term; + + + if (!strcasecmp(collect->key, "exec")) { + char *data; + const switch_application_interface_t *application_interface; + char *app_name, *app_data; + + if (!(data = collect->file)) { + goto wbreak; + } + + app_name = data; + + if ((app_data = strchr(app_name, ' '))) { + *app_data++ = '\0'; + } + + if ((application_interface = switch_loadable_module_get_application_interface(app_name)) == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Application %s\n", app_name); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + goto wbreak; + } + + if (!application_interface->application_function) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Function for %s\n", app_name); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + goto wbreak; + } + + application_interface->application_function(collect->session, app_data); + if (switch_channel_get_state(channel) < CS_HANGUP) { + switch_channel_set_flag(channel, CF_WINNER); + } + goto wbreak; + } + + if (!switch_channel_ready(channel)) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + goto wbreak; + } + + while (switch_channel_ready(channel)) { + memset(buf, 0, sizeof(buf)); + + if (collect->file) { + switch_input_args_t args = { 0 }; + args.buf = buf; + args.buflen = sizeof(buf); + switch_ivr_play_file(collect->session, NULL, collect->file, &args); + } else { + switch_ivr_collect_digits_count(collect->session, buf, sizeof(buf), 1, "", &term, 0); + } + + for (p = buf; *p; p++) { + if (*collect->key == *p) { + switch_channel_set_flag(channel, CF_WINNER); + goto wbreak; + } + } + } + wbreak: + + return NULL; +} + +static void launch_collect_thread(struct key_collect *collect) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + + switch_threadattr_create(&thd_attr, switch_core_session_get_pool(collect->session)); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, collect_thread_run, collect, + switch_core_session_get_pool(collect->session)); +} + +static uint8_t check_channel_status(switch_channel_t **peer_channels, + switch_core_session_t **peer_sessions, + uint32_t len, + int32_t *idx, + uint32_t * hups, char *file, char *key, uint8_t early_ok, uint8_t * ring_ready) +{ + + uint32_t i; + *hups = 0; + *idx = IDX_NADA; + + for (i = 0; i < len; i++) { + if (!peer_channels[i]) { + continue; + } + if (!*ring_ready && switch_channel_test_flag(peer_channels[i], CF_RING_READY)) { + *ring_ready = 1; + } + if (switch_channel_get_state(peer_channels[i]) >= CS_HANGUP) { + (*hups)++; + } else if ((switch_channel_test_flag(peer_channels[i], CF_ANSWERED) || + (early_ok && len == 1 && switch_channel_test_flag(peer_channels[i], CF_EARLY_MEDIA))) && + !switch_channel_test_flag(peer_channels[i], CF_TAGGED)) { + + if (key) { + struct key_collect *collect; + + if ((collect = switch_core_session_alloc(peer_sessions[i], sizeof(*collect)))) { + switch_channel_set_flag(peer_channels[i], CF_TAGGED); + collect->key = key; + if (file) { + collect->file = switch_core_session_strdup(peer_sessions[i], file); + } + + collect->session = peer_sessions[i]; + launch_collect_thread(collect); + } + } else { + *idx = i; + return 0; + + } + } else if (switch_channel_test_flag(peer_channels[i], CF_WINNER)) { + *idx = i; + return 0; + } + } + + if (*hups == len) { + return 0; + } else { + return 1; + } + +} + +struct ringback { + switch_buffer_t *audio_buffer; + switch_buffer_t *loop_buffer; + teletone_generation_session_t ts; + switch_file_handle_t fhb; + switch_file_handle_t *fh; + uint8_t asis; +}; + +typedef struct ringback ringback_t; + +static int teletone_handler(teletone_generation_session_t * ts, teletone_tone_map_t * map) +{ + ringback_t *tto = ts->user_data; + int wrote; + + if (!tto) { + return -1; + } + wrote = teletone_mux_tones(ts, map); + switch_buffer_write(tto->audio_buffer, ts->buffer, wrote * 2); + + return 0; +} + +#define MAX_PEERS 256 +SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *session, + switch_core_session_t **bleg, + switch_call_cause_t *cause, + char *bridgeto, + uint32_t timelimit_sec, + const switch_state_handler_table_t *table, + char *cid_name_override, + char *cid_num_override, + switch_caller_profile_t *caller_profile_override) +{ + char *pipe_names[MAX_PEERS] = { 0 }; + char *data = NULL; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_channel_t *caller_channel = NULL; + char *peer_names[MAX_PEERS] = { 0 }; + switch_core_session_t *peer_session, *peer_sessions[MAX_PEERS] = { 0 }; + switch_caller_profile_t *caller_profiles[MAX_PEERS] = { 0 }, *caller_caller_profile; + char *chan_type = NULL, *chan_data; + switch_channel_t *peer_channel = NULL, *peer_channels[MAX_PEERS] = { 0 }; + ringback_t ringback = { 0 }; + time_t start; + switch_frame_t *read_frame = NULL; + switch_memory_pool_t *pool = NULL; + int r = 0, i, and_argc = 0, or_argc = 0; + int32_t sleep_ms = 1000, try = 0, retries = 1, idx = IDX_NADA; + switch_codec_t write_codec = { 0 }; + switch_frame_t write_frame = { 0 }; + uint8_t fdata[1024], pass = 0; + char *file = NULL, *key = NULL, *odata, *var; + switch_call_cause_t reason = SWITCH_CAUSE_UNALLOCATED; + uint8_t to = 0; + char *var_val, *vars = NULL, *ringback_data = NULL; + switch_codec_t *read_codec = NULL; + uint8_t sent_ring = 0, early_ok = 1; + switch_core_session_message_t *message = NULL; + switch_event_t *var_event = NULL; + uint8_t fail_on_single_reject = 0; + uint8_t ring_ready = 0; + char *loop_data = NULL; + + write_frame.data = fdata; + + *bleg = NULL; + odata = strdup(bridgeto); + data = odata; + + /* strip leading spaces */ + while (data && *data && *data == ' ') { + data++; + } + + if (*data == '{') { + vars = data + 1; + if (!(data = strchr(data, '}'))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + status = SWITCH_STATUS_GENERR; + goto done; + } + *data++ = '\0'; + } + + /* strip leading spaces (again) */ + while (data && *data && *data == ' ') { + data++; + } + + if (switch_strlen_zero(data)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + status = SWITCH_STATUS_GENERR; + goto done; + } + + /* Some channel are created from an originating channel and some aren't so not all outgoing calls have a way to get params + so we will normalize dialstring params and channel variables (when there is an originator) into an event that we + will use as a pseudo hash to consult for params as needed. + */ + if (switch_event_create(&var_event, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + status = SWITCH_STATUS_MEMERR; + goto done; + } + + if (session) { + switch_hash_index_t *hi; + void *vval; + const void *vvar; + + caller_channel = switch_core_session_get_channel(session); + assert(caller_channel != NULL); + + /* Copy all the channel variables into the event */ + for (hi = switch_channel_variable_first(caller_channel, switch_core_session_get_pool(session)); hi; + hi = switch_hash_next(hi)) { + switch_hash_this(hi, &vvar, NULL, &vval); + if (vvar && vval) { + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, (void *) vvar, "%s", (char *) vval); + } + } + + } + + if (vars) { /* Parse parameters specified from the dialstring */ + char *var_array[1024] = { 0 }; + int var_count = 0; + if ((var_count = switch_separate_string(vars, ',', var_array, (sizeof(var_array) / sizeof(var_array[0]))))) { + int x = 0; + for (x = 0; x < var_count; x++) { + char *inner_var_array[2]; + int inner_var_count; + if ((inner_var_count = + switch_separate_string(var_array[x], '=', inner_var_array, + (sizeof(inner_var_array) / sizeof(inner_var_array[0])))) == 2) { + + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, inner_var_array[0], "%s", + inner_var_array[1]); + if (caller_channel) { + switch_channel_set_variable(caller_channel, inner_var_array[0], inner_var_array[1]); + } + } + } + } + } + + + if (caller_channel) { /* ringback is only useful when there is an originator */ + ringback_data = switch_channel_get_variable(caller_channel, "ringback"); + switch_channel_set_variable(caller_channel, "originate_disposition", "failure"); + } + + if ((var = switch_event_get_header(var_event, "group_confirm_key"))) { + key = switch_core_session_strdup(session, var); + if ((var = switch_event_get_header(var_event, "group_confirm_file"))) { + file = switch_core_session_strdup(session, var); + } + } + // When using the AND operator, the fail_on_single_reject flag may be set in order to indicate that a single + // rejections should terminate the attempt rather than a timeout, answer, or rejection by all. + if ((var = switch_event_get_header(var_event, "fail_on_single_reject")) && switch_true(var)) { + fail_on_single_reject = 1; + } + + if (file && !strcmp(file, "undef")) { + file = NULL; + } + + if ((var_val = switch_event_get_header(var_event, "ignore_early_media")) && switch_true(var_val)) { + early_ok = 0; + } + + if ((var_val = switch_event_get_header(var_event, "originate_retries")) && switch_true(var_val)) { + int32_t tmp; + tmp = atoi(var_val); + if (tmp > 0 && tmp < 101) { + retries = tmp; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Invalid originate_retries setting of %d ignored, value must be between 1 and 100\n", + tmp); + } + } + + if ((var_val = switch_event_get_header(var_event, "originate_retry_sleep_ms")) && switch_true(var_val)) { + int32_t tmp; + tmp = atoi(var_val); + if (tmp > 500 && tmp < 60000) { + sleep_ms = tmp; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Invalid originate_retry_sleep_ms setting of %d ignored, value must be between 500 and 60000\n", + tmp); + } + } + + if (!cid_name_override) { + cid_name_override = switch_event_get_header(var_event, "origination_caller_id_name"); + } + + if (!cid_num_override) { + cid_num_override = switch_event_get_header(var_event, "origination_caller_id_number"); + } + + + + for (try = 0; try < retries; try++) { + switch_safe_free(loop_data); + assert(loop_data = strdup(data)); + or_argc = switch_separate_string(loop_data, '|', pipe_names, (sizeof(pipe_names) / sizeof(pipe_names[0]))); + + if (caller_channel && or_argc > 1 && !ringback_data) { + switch_channel_ring_ready(caller_channel); + sent_ring = 1; + } + + for (r = 0; r < or_argc; r++) { + uint32_t hups; + reason = SWITCH_CAUSE_UNALLOCATED; + memset(peer_names, 0, sizeof(peer_names)); + peer_session = NULL; + memset(peer_sessions, 0, sizeof(peer_sessions)); + memset(peer_channels, 0, sizeof(peer_channels)); + memset(caller_profiles, 0, sizeof(caller_profiles)); + chan_type = NULL; + chan_data = NULL; + peer_channel = NULL; + start = 0; + read_frame = NULL; + pool = NULL; + pass = 0; + file = NULL; + key = NULL; + var = NULL; + to = 0; + + if (try > 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Originate attempt %d/%d in %d ms\n", try + 1, + retries, sleep_ms); + switch_yield(sleep_ms * 1000); + } + + and_argc = + switch_separate_string(pipe_names[r], ',', peer_names, (sizeof(peer_names) / sizeof(peer_names[0]))); + + if (caller_channel && !sent_ring && and_argc > 1 && !ringback_data) { + switch_channel_ring_ready(caller_channel); + sent_ring = 1; + } + + for (i = 0; i < and_argc; i++) { + + chan_type = peer_names[i]; + if ((chan_data = strchr(chan_type, '/')) != 0) { + *chan_data = '\0'; + chan_data++; + } + + if (session) { + if (!switch_channel_ready(caller_channel)) { + status = SWITCH_STATUS_FALSE; + goto done; + } + + caller_caller_profile = + caller_profile_override ? caller_profile_override : + switch_channel_get_caller_profile(caller_channel); + + if (!cid_name_override) { + cid_name_override = caller_caller_profile->caller_id_name; + } + if (!cid_num_override) { + cid_num_override = caller_caller_profile->caller_id_number; + } + + caller_profiles[i] = switch_caller_profile_new(switch_core_session_get_pool(session), + caller_caller_profile->username, + caller_caller_profile->dialplan, + cid_name_override, + cid_num_override, + caller_caller_profile->network_addr, + NULL, + NULL, + caller_caller_profile->rdnis, + caller_caller_profile->source, + caller_caller_profile->context, chan_data); + caller_profiles[i]->flags = caller_caller_profile->flags; + pool = NULL; + } else { + if (!cid_name_override) { + cid_name_override = "FreeSWITCH"; + } + if (!cid_num_override) { + cid_num_override = "0000000000"; + } + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + + if (caller_profile_override) { + caller_profiles[i] = switch_caller_profile_new(pool, + caller_profile_override->username, + caller_profile_override->dialplan, + caller_profile_override->caller_id_name, + caller_profile_override->caller_id_number, + caller_profile_override->network_addr, + caller_profile_override->ani, + caller_profile_override->aniii, + caller_profile_override->rdnis, + caller_profile_override->source, + caller_profile_override->context, chan_data); + } else { + caller_profiles[i] = switch_caller_profile_new(pool, + NULL, + NULL, + cid_name_override, + cid_num_override, + NULL, + NULL, NULL, NULL, __FILE__, NULL, chan_data); + } + } + + if ((reason = + switch_core_session_outgoing_channel(session, chan_type, caller_profiles[i], &peer_sessions[i], + &pool)) != SWITCH_CAUSE_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Cannot Create Outgoing Channel! cause: %s\n", switch_channel_cause2str(reason)); + if (pool) { + switch_core_destroy_memory_pool(&pool); + } + caller_profiles[i] = NULL; + peer_channels[i] = NULL; + peer_sessions[i] = NULL; + continue; + } + + switch_core_session_read_lock(peer_sessions[i]); + pool = NULL; + + peer_channels[i] = switch_core_session_get_channel(peer_sessions[i]); + assert(peer_channels[i] != NULL); + + if (!table) { + table = &originate_state_handlers; + } + + if (table) { + switch_channel_add_state_handler(peer_channels[i], table); + } + + if (switch_core_session_running(peer_sessions[i])) { + switch_channel_set_state(peer_channels[i], CS_RING); + } else { + switch_core_session_thread_launch(peer_sessions[i]); + } + } + + time(&start); + + for (;;) { + uint32_t valid_channels = 0; + for (i = 0; i < and_argc; i++) { + int state; + + if (!peer_channels[i]) { + continue; + } + valid_channels++; + state = switch_channel_get_state(peer_channels[i]); + + if (state >= CS_HANGUP) { + goto notready; + } else if (state >= CS_RING) { + goto endfor1; + } + + if (caller_channel && !switch_channel_ready(caller_channel)) { + goto notready; + } + + if ((time(NULL) - start) > (time_t) timelimit_sec) { + to++; + idx = IDX_CANCEL; + goto notready; + } + switch_yield(1000); + } + + if (valid_channels == 0) { + status = SWITCH_STATUS_GENERR; + goto done; + } + + } + endfor1: + + if (ringback_data && !switch_channel_test_flag(caller_channel, CF_ANSWERED) + && !switch_channel_test_flag(caller_channel, CF_EARLY_MEDIA)) { + switch_channel_pre_answer(caller_channel); + } + + if (session && (read_codec = switch_core_session_get_read_codec(session)) && + (ringback_data || !switch_channel_test_flag(caller_channel, CF_NOMEDIA))) { + + if (!(pass = (uint8_t) switch_test_flag(read_codec, SWITCH_CODEC_FLAG_PASSTHROUGH))) { + if (switch_core_codec_init(&write_codec, + "L16", + NULL, + read_codec->implementation->samples_per_second, + read_codec->implementation->microseconds_per_frame / 1000, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, pool) == SWITCH_STATUS_SUCCESS) { + + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Raw Codec Activation Success L16@%uhz 1 channel %dms\n", + read_codec->implementation->samples_per_second, + read_codec->implementation->microseconds_per_frame / 1000); + write_frame.codec = &write_codec; + write_frame.datalen = read_codec->implementation->bytes_per_frame; + write_frame.samples = write_frame.datalen / 2; + memset(write_frame.data, 255, write_frame.datalen); + + if (ringback_data) { + char *tmp_data = NULL; + + switch_buffer_create_dynamic(&ringback.audio_buffer, 512, 1024, 0); + switch_buffer_create_dynamic(&ringback.loop_buffer, 512, 1024, 0); + + if (*ringback_data == '/') { + char *ext; + + if ((ext = strrchr(ringback_data, '.'))) { + switch_core_session_set_read_codec(session, &write_codec); + ext++; + } else { + ringback.asis++; + write_frame.codec = read_codec; + ext = read_codec->implementation->iananame; + tmp_data = switch_mprintf("%s.%s", ringback_data, ext); + ringback_data = tmp_data; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Play Ringback File [%s]\n", + ringback_data); + + ringback.fhb.channels = read_codec->implementation->number_of_channels; + ringback.fhb.samplerate = read_codec->implementation->samples_per_second; + if (switch_core_file_open(&ringback.fhb, + ringback_data, + read_codec->implementation->number_of_channels, + read_codec->implementation->samples_per_second, + SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, + switch_core_session_get_pool(session)) != + SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Playing File\n"); + switch_safe_free(tmp_data); + goto notready; + } + ringback.fh = &ringback.fhb; + + + } else { + teletone_init_session(&ringback.ts, 0, teletone_handler, &ringback); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Play Ringback Tone [%s]\n", + ringback_data); + //ringback.ts.debug = 1; + //ringback.ts.debug_stream = switch_core_get_console(); + if (teletone_run(&ringback.ts, ringback_data)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Playing Tone\n"); + teletone_destroy_session(&ringback.ts); + switch_buffer_destroy(&ringback.audio_buffer); + switch_buffer_destroy(&ringback.loop_buffer); + ringback_data = NULL; + } + } + switch_safe_free(tmp_data); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec Error!"); + switch_channel_hangup(caller_channel, SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE); + read_codec = NULL; + } + } + } + + if (ringback_data) { + early_ok = 0; + } + + while ((!caller_channel || switch_channel_ready(caller_channel)) && + check_channel_status(peer_channels, peer_sessions, and_argc, &idx, &hups, file, key, early_ok, + &ring_ready)) { + + // When the AND operator is being used, and fail_on_single_reject is set, a hangup indicates that the call should fail. + if ((to = (uint8_t) ((time(NULL) - start) >= (time_t) timelimit_sec)) + || (fail_on_single_reject && hups)) { + idx = IDX_CANCEL; + goto notready; + } + + if (peer_sessions[0] + && switch_core_session_dequeue_message(peer_sessions[0], &message) == SWITCH_STATUS_SUCCESS) { + if (session && !ringback_data && or_argc == 1 && and_argc == 1) { /* when there is only 1 channel to call and bridge and no ringback */ + switch_core_session_receive_message(session, message); + } + + if (switch_test_flag(message, SCSMF_DYNAMIC)) { + switch_safe_free(message); + } else { + message = NULL; + } + } + + /* read from the channel while we wait if the audio is up on it */ + if (session && (ringback_data || !switch_channel_test_flag(caller_channel, CF_NOMEDIA)) && + (switch_channel_test_flag(caller_channel, CF_ANSWERED) + || switch_channel_test_flag(caller_channel, CF_EARLY_MEDIA))) { + switch_status_t status = switch_core_session_read_frame(session, &read_frame, 1000, 0); + + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + + if (ring_ready && read_frame && !pass && !switch_test_flag(read_frame, SFF_CNG) + && read_frame->datalen > 1) { + if (ringback.fh) { + uint8_t abuf[1024]; + switch_size_t mlen, olen; + unsigned int pos = 0; + + if (ringback.asis) { + mlen = read_frame->datalen; + } else { + mlen = read_frame->datalen / 2; + } + + olen = mlen; + switch_core_file_read(ringback.fh, abuf, &olen); + + if (olen == 0) { + olen = mlen; + ringback.fh->speed = 0; + switch_core_file_seek(ringback.fh, &pos, 0, SEEK_SET); + switch_core_file_read(ringback.fh, abuf, &olen); + if (olen == 0) { + break; + } + } + write_frame.data = abuf; + write_frame.datalen = (uint32_t) (ringback.asis ? olen : olen * 2); + if (switch_core_session_write_frame(session, &write_frame, 1000, 0) != + SWITCH_STATUS_SUCCESS) { + break; + } + } else if (ringback.audio_buffer) { + if ((write_frame.datalen = (uint32_t) switch_buffer_read(ringback.audio_buffer, + write_frame.data, + write_frame.codec->implementation-> + bytes_per_frame)) <= 0) { + switch_buffer_t *tmp; + tmp = ringback.audio_buffer; + ringback.audio_buffer = ringback.loop_buffer; + ringback.loop_buffer = tmp; + if ((write_frame.datalen = (uint32_t) switch_buffer_read(ringback.audio_buffer, + write_frame.data, + write_frame.codec-> + implementation-> + bytes_per_frame)) <= 0) { + break; + } + } + } + + if (switch_core_session_write_frame(session, &write_frame, 1000, 0) != SWITCH_STATUS_SUCCESS) { + break; + } + if (ringback.loop_buffer) { + switch_buffer_write(ringback.loop_buffer, write_frame.data, write_frame.datalen); + } + } + + } else { + switch_yield(1000); + } + + } + + notready: + + if (caller_channel && !switch_channel_ready(caller_channel)) { + idx = IDX_CANCEL; + } + + if (session && (ringback_data || !switch_channel_test_flag(caller_channel, CF_NOMEDIA))) { + switch_core_session_reset(session); + } + + for (i = 0; i < and_argc; i++) { + if (!peer_channels[i]) { + continue; + } + if (i != idx) { + if (idx == IDX_CANCEL) { + if (to) { + reason = SWITCH_CAUSE_NO_ANSWER; + } else { + reason = SWITCH_CAUSE_ORIGINATOR_CANCEL; + } + } else { + if (to) { + reason = SWITCH_CAUSE_NO_ANSWER; + } else if (and_argc > 1) { + reason = SWITCH_CAUSE_LOSE_RACE; + } else { + reason = SWITCH_CAUSE_NO_ANSWER; + } + } + + + switch_channel_hangup(peer_channels[i], reason); + } + } + + + if (idx > IDX_NADA) { + peer_session = peer_sessions[idx]; + peer_channel = peer_channels[idx]; + } else { + status = SWITCH_STATUS_FALSE; + goto done; + } + + if (caller_channel) { + if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) { + switch_channel_answer(caller_channel); + } else if (switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { + switch_channel_pre_answer(caller_channel); + } + } + + if (switch_channel_test_flag(peer_channel, CF_ANSWERED) + || switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { + *bleg = peer_session; + status = SWITCH_STATUS_SUCCESS; + } else { + status = SWITCH_STATUS_FALSE; + } + + done: + *cause = SWITCH_CAUSE_UNALLOCATED; + + if (var_event) { + if (peer_channel && !caller_channel) { /* install the vars from the {} params */ + switch_event_header_t *header; + for (header = var_event->headers; header; header = header->next) { + switch_channel_set_variable(peer_channel, header->name, header->value); + } + } + switch_event_destroy(&var_event); + } + + if (status == SWITCH_STATUS_SUCCESS) { + if (caller_channel) { + switch_channel_set_variable(caller_channel, "originate_disposition", "call accepted"); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Originate Resulted in Success: [%s]\n", + switch_channel_get_name(peer_channel)); + *cause = SWITCH_CAUSE_SUCCESS; + + } else { + if (peer_channel) { + *cause = switch_channel_get_cause(peer_channel); + } else { + for (i = 0; i < and_argc; i++) { + if (!peer_channels[i]) { + continue; + } + *cause = switch_channel_get_cause(peer_channels[i]); + break; + } + } + + if (!*cause) { + if (reason) { + *cause = reason; + } else if (caller_channel) { + *cause = switch_channel_get_cause(caller_channel); + } else { + *cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + } + + if (idx == IDX_CANCEL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Originate Cancelled by originator termination Cause: %d [%s]\n", *cause, + switch_channel_cause2str(*cause)); + + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Originate Resulted in Error Cause: %d [%s]\n", *cause, + switch_channel_cause2str(*cause)); + } + } + + if (caller_channel) { + switch_channel_set_variable(caller_channel, "originate_disposition", switch_channel_cause2str(*cause)); + } + + if (!pass && write_codec.implementation) { + switch_core_codec_destroy(&write_codec); + } + + if (ringback.fh) { + switch_core_file_close(ringback.fh); + ringback.fh = NULL; + if (read_codec && !ringback.asis) { + switch_core_session_set_read_codec(session, read_codec); + switch_core_session_reset(session); + } + } else if (ringback.audio_buffer) { + teletone_destroy_session(&ringback.ts); + switch_buffer_destroy(&ringback.audio_buffer); + switch_buffer_destroy(&ringback.loop_buffer); + } + + for (i = 0; i < and_argc; i++) { + if (!peer_channels[i]) { + continue; + } + switch_core_session_rwunlock(peer_sessions[i]); + } + + if (status == SWITCH_STATUS_SUCCESS) { + goto outer_for; + } + } + } + outer_for: + switch_safe_free(loop_data); + switch_safe_free(odata); + return status; +} diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c new file mode 100644 index 0000000000..a466950b39 --- /dev/null +++ b/src/switch_ivr_play_say.c @@ -0,0 +1,1321 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Paul D. Tinsley + * Neal Horman + * Matt Klein + * Michael Jerris + * + * switch_ivr_play_say.c -- IVR Library (functions to play or say audio) + * + */ +#include + +static char *SAY_METHOD_NAMES[] = { + "N/A", + "PRONOUNCED", + "ITERATED", + "COUNTED", + NULL +}; + +static char *SAY_TYPE_NAMES[] = { + "NUMBER", + "ITEMS", + "PERSONS", + "MESSAGES", + "CURRENCY", + "TIME_MEASUREMENT", + "CURRENT_DATE", + "CURRENT_TIME", + "CURRENT_DATE_TIME", + "TELEPHONE_NUMBER", + "TELEPHONE_EXTENSION", + "URL", + "IP_ADDRESS", + "EMAIL_ADDRESS", + "POSTAL_ADDRESS", + "ACCOUNT_NUMBER", + "NAME_SPELLED", + "NAME_PHONETIC", + NULL +}; + + +static switch_say_method_t get_say_method_by_name(char *name) +{ + int x = 0; + for (x = 0; SAY_METHOD_NAMES[x]; x++) { + if (!strcasecmp(SAY_METHOD_NAMES[x], name)) { + break; + } + } + + return (switch_say_method_t) x; +} + +static switch_say_method_t get_say_type_by_name(char *name) +{ + int x = 0; + for (x = 0; SAY_TYPE_NAMES[x]; x++) { + if (!strcasecmp(SAY_TYPE_NAMES[x], name)) { + break; + } + } + + return (switch_say_method_t) x; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_phrase_macro(switch_core_session_t *session, + char *macro_name, + char *data, char *lang, switch_input_args_t *args) +{ + switch_xml_t cfg, xml = NULL, language, macros, macro, input, action; + char *lname = NULL, *mname = NULL, hint_data[1024] = "", enc_hint[1024] = ""; + switch_status_t status = SWITCH_STATUS_GENERR; + char *old_sound_prefix = NULL, *sound_path = NULL, *tts_engine = NULL, *tts_voice = NULL, *chan_lang = NULL; + switch_channel_t *channel; + uint8_t done = 0; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (!macro_name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No phrase macro specified.\n"); + return status; + } + + if (!lang) { + chan_lang = switch_channel_get_variable(channel, "default_language"); + if (!chan_lang) { + chan_lang = "en"; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No language specified - Using [%s]\n", chan_lang); + } else { + chan_lang = lang; + } + + if (!data) { + data = ""; + } + + switch_url_encode(data, enc_hint, sizeof(enc_hint)); + snprintf(hint_data, sizeof(hint_data), "macro_name=%s&lang=%s&data=%s", macro_name, chan_lang, enc_hint); + + if (switch_xml_locate("phrases", NULL, NULL, NULL, &xml, &cfg, hint_data) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of phrases failed.\n"); + goto done; + } + + if (!(macros = switch_xml_child(cfg, "macros"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find macros tag.\n"); + goto done; + } + + if (!(language = switch_xml_child(macros, "language"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find language tag.\n"); + goto done; + } + + while (language) { + if ((lname = (char *) switch_xml_attr(language, "name")) && !strcasecmp(lname, chan_lang)) { + break; + } + language = language->next; + } + + if (!language) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find language %s.\n", chan_lang); + goto done; + } + + sound_path = (char *) switch_xml_attr_soft(language, "sound_path"); + tts_engine = (char *) switch_xml_attr_soft(language, "tts_engine"); + tts_voice = (char *) switch_xml_attr_soft(language, "tts_voice"); + + old_sound_prefix = switch_channel_get_variable(channel, "sound_prefix"); + switch_channel_set_variable(channel, "sound_prefix", sound_path); + + if (!(macro = switch_xml_child(language, "macro"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find any macro tags.\n"); + goto done; + } + + while (macro) { + if ((mname = (char *) switch_xml_attr(macro, "name")) && !strcasecmp(mname, macro_name)) { + break; + } + macro = macro->next; + } + + if (!macro) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find macro %s.\n", macro_name); + goto done; + } + + if (!(input = switch_xml_child(macro, "input"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find any input tags.\n"); + goto done; + } + + switch_channel_pre_answer(channel); + + while (input && !done) { + char *pattern = (char *) switch_xml_attr(input, "pattern"); + + if (pattern) { + switch_regex_t *re = NULL; + int proceed = 0, ovector[30]; + char *substituted = NULL; + uint32_t len = 0; + char *odata = NULL; + char *expanded = NULL; + switch_xml_t match = NULL; + + if ((proceed = switch_regex_perform(data, pattern, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) { + match = switch_xml_child(input, "match"); + } else { + match = switch_xml_child(input, "nomatch"); + } + + if (match) { + status = SWITCH_STATUS_SUCCESS; + for (action = switch_xml_child(match, "action"); action && status == SWITCH_STATUS_SUCCESS; + action = action->next) { + char *adata = (char *) switch_xml_attr_soft(action, "data"); + char *func = (char *) switch_xml_attr_soft(action, "function"); + + if (strchr(pattern, '(') && strchr(adata, '$')) { + len = (uint32_t) (strlen(data) + strlen(adata) + 10); + if (!(substituted = malloc(len))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + switch_regex_safe_free(re); + switch_safe_free(expanded); + goto done; + } + memset(substituted, 0, len); + switch_perform_substitution(re, proceed, adata, data, substituted, len, ovector); + odata = substituted; + } else { + odata = adata; + } + + expanded = switch_channel_expand_variables(channel, odata); + + if (expanded == odata) { + expanded = NULL; + } else { + odata = expanded; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Handle %s:[%s] (%s)\n", func, odata, + chan_lang); + + if (!strcasecmp(func, "play-file")) { + status = switch_ivr_play_file(session, NULL, odata, args); + } else if (!strcasecmp(func, "break")) { + done = 1; + break; + } else if (!strcasecmp(func, "execute")) { + + } else if (!strcasecmp(func, "say")) { + switch_say_interface_t *si; + if ((si = switch_loadable_module_get_say_interface(chan_lang))) { + char *say_type = (char *) switch_xml_attr_soft(action, "type"); + char *say_method = (char *) switch_xml_attr_soft(action, "method"); + + status = + si->say_function(session, odata, get_say_type_by_name(say_type), + get_say_method_by_name(say_method), args); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid SAY Interface [%s]!\n", + chan_lang); + } + } else if (!strcasecmp(func, "speak-text")) { + switch_codec_t *read_codec; + if ((read_codec = switch_core_session_get_read_codec(session))) { + + status = switch_ivr_speak_text(session, + tts_engine, + tts_voice, + read_codec->implementation->samples_per_second, odata, args); + } + } + } + } + + switch_regex_safe_free(re); + switch_safe_free(expanded); + switch_safe_free(substituted); + } + + if (status != SWITCH_STATUS_SUCCESS) { + done = 1; + break; + } + + input = input->next; + } + + done: + + switch_channel_set_variable(channel, "sound_prefix", old_sound_prefix); + + if (xml) { + switch_xml_free(xml); + } + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *session, + switch_file_handle_t *fh, + char *file, switch_input_args_t *args, uint32_t limit) +{ + switch_channel_t *channel; + char dtmf[128]; + switch_file_handle_t lfh = { 0 }; + switch_frame_t *read_frame; + switch_codec_t codec, *read_codec; + char *codec_name; + switch_status_t status = SWITCH_STATUS_SUCCESS; + char *p; + const char *vval; + time_t start = 0; + uint32_t org_silence_hits = 0; + + if (!fh) { + fh = &lfh; + } + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + fh->channels = read_codec->implementation->number_of_channels; + fh->samplerate = read_codec->implementation->samples_per_second; + + + if (switch_core_file_open(fh, + file, + read_codec->implementation->number_of_channels, + read_codec->implementation->samples_per_second, + SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, + switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + + switch_channel_answer(channel); + + if ((p = switch_channel_get_variable(channel, "RECORD_TITLE"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_TITLE, vval); + switch_channel_set_variable(channel, "RECORD_TITLE", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_COPYRIGHT"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COPYRIGHT, vval); + switch_channel_set_variable(channel, "RECORD_COPYRIGHT", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_SOFTWARE"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_SOFTWARE, vval); + switch_channel_set_variable(channel, "RECORD_SOFTWARE", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_ARTIST"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_ARTIST, vval); + switch_channel_set_variable(channel, "RECORD_ARTIST", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_COMMENT"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COMMENT, vval); + switch_channel_set_variable(channel, "RECORD_COMMENT", NULL); + } + + if ((p = switch_channel_get_variable(channel, "RECORD_DATE"))) { + vval = (const char *) switch_core_session_strdup(session, p); + switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_DATE, vval); + switch_channel_set_variable(channel, "RECORD_DATE", NULL); + } + + codec_name = "L16"; + if (switch_core_codec_init(&codec, + codec_name, + NULL, + read_codec->implementation->samples_per_second, + read_codec->implementation->microseconds_per_frame / 1000, + read_codec->implementation->number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activated\n"); + switch_core_session_set_read_codec(session, &codec); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Raw Codec Activation Failed %s@%uhz %u channels %dms\n", codec_name, fh->samplerate, + fh->channels, read_codec->implementation->microseconds_per_frame / 1000); + switch_core_file_close(fh); + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + + if (limit) { + start = time(NULL); + } + + if (fh->thresh) { + if (fh->silence_hits) { + fh->silence_hits = fh->samplerate * fh->silence_hits / read_codec->implementation->samples_per_frame; + } else { + fh->silence_hits = fh->samplerate * 3 / read_codec->implementation->samples_per_frame; + } + org_silence_hits = fh->silence_hits; + } + + while (switch_channel_ready(channel)) { + switch_size_t len; + switch_event_t *event; + + if (switch_core_session_dequeue_private_event(session, &event) == SWITCH_STATUS_SUCCESS) { + switch_ivr_parse_event(session, event); + switch_event_destroy(&event); + } + + if (start && (time(NULL) - start) > limit) { + break; + } + + if (args && (args->input_callback || args->buf || args->buflen)) { + /* + dtmf handler function you can hook up to be executed when a digit is dialed during playback + if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. + */ + if (switch_channel_has_dtmf(channel)) { + if (!args->input_callback && !args->buf) { + status = SWITCH_STATUS_BREAK; + break; + } + switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); + if (args->input_callback) { + status = args->input_callback(session, dtmf, SWITCH_INPUT_TYPE_DTMF, args->buf, args->buflen); + } else { + switch_copy_string((char *) args->buf, dtmf, args->buflen); + status = SWITCH_STATUS_BREAK; + } + } + + if (args->input_callback) { + if (switch_core_session_dequeue_event(session, &event) == SWITCH_STATUS_SUCCESS) { + status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_event_destroy(&event); + } + } + + + if (status != SWITCH_STATUS_SUCCESS) { + break; + } + } + + status = switch_core_session_read_frame(session, &read_frame, -1, 0); + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + + if (fh->thresh) { + int16_t *fdata = (int16_t *) read_frame->data; + uint32_t samples = read_frame->datalen / sizeof(*fdata); + uint32_t score, count = 0, j = 0; + double energy = 0; + + for (count = 0; count < samples; count++) { + energy += abs(fdata[j]); + j += read_codec->implementation->number_of_channels; + } + + score = (uint32_t) (energy / samples); + if (score < fh->thresh) { + if (!--fh->silence_hits) { + break; + } + } else { + fh->silence_hits = org_silence_hits; + } + } + + if (!switch_test_flag(fh, SWITCH_FILE_PAUSE)) { + len = (switch_size_t) read_frame->datalen / 2; + if (switch_core_file_write(fh, read_frame->data, &len) != SWITCH_STATUS_SUCCESS) { + break; + } + } + } + + switch_core_session_set_read_codec(session, read_codec); + switch_core_file_close(fh); + switch_core_session_reset(session); + return status; +} + +#define FILE_STARTSAMPLES 1024 * 32 +#define FILE_BLOCKSIZE 1024 * 8 +#define FILE_BUFSIZE 1024 * 64 + +SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *session, + switch_file_handle_t *fh, char *file, switch_input_args_t *args) +{ + switch_channel_t *channel; + int16_t abuf[FILE_STARTSAMPLES]; + char dtmf[128]; + uint32_t interval = 0, samples = 0, framelen, sample_start = 0; + uint32_t ilen = 0; + switch_size_t olen = 0, llen = 0; + switch_frame_t write_frame = { 0 }; + switch_timer_t timer = { 0 }; + switch_core_thread_session_t thread_session; + switch_codec_t codec; + switch_memory_pool_t *pool = switch_core_session_get_pool(session); + char *codec_name; + int stream_id = 0; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_file_handle_t lfh; + switch_codec_t *read_codec = switch_core_session_get_read_codec(session); + const char *p; + char *title = "", *copyright = "", *software = "", *artist = "", *comment = "", *date = ""; + uint8_t asis = 0; + char *ext; + char *prefix; + char *timer_name; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + prefix = switch_channel_get_variable(channel, "sound_prefix"); + timer_name = switch_channel_get_variable(channel, "timer_name"); + + if (!file) { + return SWITCH_STATUS_FALSE; + } + + if (!strstr(file, SWITCH_URL_SEPARATOR)) { + if (prefix && *file != '/' && *file != '\\' && *(file + 1) != ':') { + char *new_file; + uint32_t len; + len = (uint32_t) strlen(file) + (uint32_t) strlen(prefix) + 10; + new_file = switch_core_session_alloc(session, len); + snprintf(new_file, len, "%s/%s", prefix, file); + file = new_file; + } + + if ((ext = strrchr(file, '.'))) { + ext++; + } else { + char *new_file; + uint32_t len; + ext = read_codec->implementation->iananame; + len = (uint32_t) strlen(file) + (uint32_t) strlen(ext) + 2; + new_file = switch_core_session_alloc(session, len); + snprintf(new_file, len, "%s.%s", file, ext); + file = new_file; + asis = 1; + } + } + + + if (!fh) { + fh = &lfh; + memset(fh, 0, sizeof(lfh)); + } + + + if (fh->samples > 0) { + sample_start = fh->samples; + fh->samples = 0; + } + + if (switch_core_file_open(fh, + file, + read_codec->implementation->number_of_channels, + read_codec->implementation->samples_per_second, + SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, + switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_core_session_reset(session); + return SWITCH_STATUS_NOTFOUND; + } + if (switch_test_flag(fh, SWITCH_FILE_NATIVE)) { + asis = 1; + } + + + write_frame.data = abuf; + write_frame.buflen = sizeof(abuf); + + if (sample_start > 0) { + uint32_t pos = 0; + switch_core_file_seek(fh, &pos, sample_start, SEEK_CUR); + } + + if (switch_core_file_get_string(fh, SWITCH_AUDIO_COL_STR_TITLE, &p) == SWITCH_STATUS_SUCCESS) { + title = switch_core_session_strdup(session, p); + switch_channel_set_variable(channel, "RECORD_TITLE", p); + } + + if (switch_core_file_get_string(fh, SWITCH_AUDIO_COL_STR_COPYRIGHT, &p) == SWITCH_STATUS_SUCCESS) { + copyright = switch_core_session_strdup(session, p); + switch_channel_set_variable(channel, "RECORD_COPYRIGHT", p); + } + + if (switch_core_file_get_string(fh, SWITCH_AUDIO_COL_STR_SOFTWARE, &p) == SWITCH_STATUS_SUCCESS) { + software = switch_core_session_strdup(session, p); + switch_channel_set_variable(channel, "RECORD_SOFTWARE", p); + } + + if (switch_core_file_get_string(fh, SWITCH_AUDIO_COL_STR_ARTIST, &p) == SWITCH_STATUS_SUCCESS) { + artist = switch_core_session_strdup(session, p); + switch_channel_set_variable(channel, "RECORD_ARTIST", p); + } + + if (switch_core_file_get_string(fh, SWITCH_AUDIO_COL_STR_COMMENT, &p) == SWITCH_STATUS_SUCCESS) { + comment = switch_core_session_strdup(session, p); + switch_channel_set_variable(channel, "RECORD_COMMENT", p); + } + + if (switch_core_file_get_string(fh, SWITCH_AUDIO_COL_STR_DATE, &p) == SWITCH_STATUS_SUCCESS) { + date = switch_core_session_strdup(session, p); + switch_channel_set_variable(channel, "RECORD_DATE", p); + } +#if 0 + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "OPEN FILE %s %uhz %u channels\n" + "TITLE=%s\n" + "COPYRIGHT=%s\n" + "SOFTWARE=%s\n" + "ARTIST=%s\n" + "COMMENT=%s\n" + "DATE=%s\n", file, fh->samplerate, fh->channels, + title, copyright, software, artist, comment, date); +#endif + + assert(read_codec != NULL); + interval = read_codec->implementation->microseconds_per_frame / 1000; + + if (!fh->audio_buffer) { + switch_buffer_create_dynamic(&fh->audio_buffer, FILE_BLOCKSIZE, FILE_BUFSIZE, 0); + if (!fh->audio_buffer) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup buffer failed\n"); + + switch_core_file_close(fh); + switch_core_session_reset(session); + + return SWITCH_STATUS_GENERR; + } + } + + if (asis) { + write_frame.codec = read_codec; + samples = read_codec->implementation->samples_per_frame; + framelen = read_codec->implementation->encoded_bytes_per_frame; + } else { + codec_name = "L16"; + + if (switch_core_codec_init(&codec, + codec_name, + NULL, + fh->samplerate, + interval, + fh->channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, pool) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, + SWITCH_LOG_DEBUG, + "Codec Activated %s@%uhz %u channels %dms\n", + codec_name, fh->samplerate, fh->channels, interval); + + write_frame.codec = &codec; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Raw Codec Activation Failed %s@%uhz %u channels %dms\n", codec_name, fh->samplerate, + fh->channels, interval); + switch_core_file_close(fh); + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + samples = codec.implementation->samples_per_frame; + framelen = codec.implementation->bytes_per_frame; + } + + + if (timer_name) { + uint32_t len; + + len = samples * 2; + if (switch_core_timer_init(&timer, timer_name, interval, samples, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n"); + switch_core_codec_destroy(&codec); + switch_core_file_close(fh); + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "setup timer success %u bytes per %d ms!\n", len, + interval); + } + write_frame.rate = fh->samplerate; + + if (timer_name) { + /* start a thread to absorb incoming audio */ + for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) { + switch_core_service_session(session, &thread_session, stream_id); + } + } + + ilen = samples; + + while (switch_channel_ready(channel)) { + int done = 0; + int do_speed = 1; + int last_speed = -1; + switch_event_t *event; + + + if (switch_core_session_dequeue_private_event(session, &event) == SWITCH_STATUS_SUCCESS) { + switch_ivr_parse_event(session, event); + switch_event_destroy(&event); + } + + if (args && (args->input_callback || args->buf || args->buflen)) { + /* + dtmf handler function you can hook up to be executed when a digit is dialed during playback + if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. + */ + if (switch_channel_has_dtmf(channel)) { + if (!args->input_callback && !args->buf) { + status = SWITCH_STATUS_BREAK; + done = 1; + break; + } + switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); + if (args->input_callback) { + status = args->input_callback(session, dtmf, SWITCH_INPUT_TYPE_DTMF, args->buf, args->buflen); + } else { + switch_copy_string((char *) args->buf, dtmf, args->buflen); + status = SWITCH_STATUS_BREAK; + } + } + + if (args->input_callback) { + if (switch_core_session_dequeue_event(session, &event) == SWITCH_STATUS_SUCCESS) { + status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_event_destroy(&event); + } + } + + if (status != SWITCH_STATUS_SUCCESS) { + done = 1; + break; + } + } + + if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { + memset(abuf, 0, framelen); + olen = ilen; + do_speed = 0; + } else if (fh->audio_buffer && (switch_buffer_inuse(fh->audio_buffer) > (switch_size_t) (framelen))) { + switch_buffer_read(fh->audio_buffer, abuf, framelen); + olen = asis ? framelen : ilen; + do_speed = 0; + } else { + olen = sizeof(abuf); + if (!asis) { + olen /= 2; + } + switch_core_file_read(fh, abuf, &olen); + switch_buffer_write(fh->audio_buffer, abuf, asis ? olen : olen * 2); + olen = switch_buffer_read(fh->audio_buffer, abuf, framelen); + if (!asis) { + olen /= 2; + } + } + + if (done || olen <= 0) { + break; + } + + if (!asis) { + if (fh->speed > 2) { + fh->speed = 2; + } else if (fh->speed < -2) { + fh->speed = -2; + } + } + + if (!asis && fh->audio_buffer && last_speed > -1 && last_speed != fh->speed) { + switch_buffer_zero(fh->audio_buffer); + } + + if (switch_test_flag(fh, SWITCH_FILE_SEEK)) { + /* file position has changed flush the buffer */ + switch_buffer_zero(fh->audio_buffer); + switch_clear_flag(fh, SWITCH_FILE_SEEK); + } + + + if (!asis && fh->speed && do_speed) { + float factor = 0.25f * abs(fh->speed); + switch_size_t newlen, supplement, step; + short *bp = write_frame.data; + switch_size_t wrote = 0; + + + supplement = (int) (factor * olen); + newlen = (fh->speed > 0) ? olen - supplement : olen + supplement; + step = (fh->speed > 0) ? (newlen / supplement) : (olen / supplement); + + while ((wrote + step) < newlen) { + switch_buffer_write(fh->audio_buffer, bp, step * 2); + wrote += step; + bp += step; + if (fh->speed > 0) { + bp++; + } else { + float f; + short s; + f = (float) (*bp + *(bp + 1) + *(bp - 1)); + f /= 3; + s = (short) f; + switch_buffer_write(fh->audio_buffer, &s, 2); + wrote++; + } + } + if (wrote < newlen) { + switch_size_t r = newlen - wrote; + switch_buffer_write(fh->audio_buffer, bp, r * 2); + wrote += r; + } + last_speed = fh->speed; + continue; + } + if (olen < llen) { + uint8_t *dp = (uint8_t *) write_frame.data; + memset(dp + (int) olen, 0, (int) (llen - olen)); + olen = llen; + } + + write_frame.datalen = (uint32_t) (olen * (asis ? 1 : 2)); + write_frame.samples = (uint32_t) olen; + + llen = olen; + + if (timer_name) { + write_frame.timestamp = timer.samplecount; + } +#ifndef WIN32 +#if __BYTE_ORDER == __BIG_ENDIAN + if (!asis) { + switch_swap_linear(write_frame.data, (int) write_frame.datalen / 2); + } +#endif +#endif + stream_id = 0; + + status = switch_core_session_write_frame(session, &write_frame, -1, stream_id); + + if (status == SWITCH_STATUS_MORE_DATA) { + status = SWITCH_STATUS_SUCCESS; + continue; + } else if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n"); + done = 1; + break; + } + + if (done) { + break; + } + + + if (timer_name) { + if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) { + break; + } + } else { /* time off the channel (if you must) */ + switch_frame_t *read_frame; + switch_status_t status; + while (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_HOLD)) { + switch_yield(10000); + } + status = switch_core_session_read_frame(session, &read_frame, -1, 0); + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "done playing file\n"); + switch_core_file_close(fh); + switch_buffer_destroy(&fh->audio_buffer); + if (!asis) { + switch_core_codec_destroy(&codec); + } + if (timer_name) { + /* End the audio absorbing thread */ + switch_core_thread_session_end(&thread_session); + switch_core_timer_destroy(&timer); + } + + switch_core_session_reset(session); + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_play_and_get_digits(switch_core_session_t *session, + uint32_t min_digits, + uint32_t max_digits, + uint32_t max_tries, + uint32_t timeout, + char *valid_terminators, + char *prompt_audio_file, + char *bad_input_audio_file, + void *digit_buffer, + uint32_t digit_buffer_length, char *digits_regex) +{ + + char terminator; //used to hold terminator recieved from + switch_channel_t *channel; //the channel contained in session + switch_status_t status; //used to recieve state out of called functions + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "switch_play_and_get_digits(session, %d, %d, %d, %d, %s, %s, %s, digit_buffer, %d, %s)\n", + min_digits, max_digits, max_tries, timeout, valid_terminators, prompt_audio_file, + bad_input_audio_file, digit_buffer_length, digits_regex); + + //Get the channel + channel = switch_core_session_get_channel(session); + + //Make sure somebody is home + assert(channel != NULL); + + //Answer the channel if it hasn't already been answered + switch_channel_answer(channel); + + //Start pestering the user for input + for (; (switch_channel_get_state(channel) == CS_EXECUTE) && max_tries > 0; max_tries--) { + switch_input_args_t args = { 0 }; + //make the buffer so fresh and so clean clean + memset(digit_buffer, 0, digit_buffer_length); + + args.buf = digit_buffer; + args.buflen = digit_buffer_length; + //Play the file + status = switch_ivr_play_file(session, NULL, prompt_audio_file, &args); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "play gave up %s", (char *) digit_buffer); + + //Make sure we made it out alive + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + break; + } + //we only get one digit out of playback, see if thats all we needed and what we got + if (max_digits == 1 && status == SWITCH_STATUS_BREAK) { + //Check the digit if we have a regex + if (digits_regex != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking regex [%s] on [%s]\n", digits_regex, + (char *) digit_buffer); + + //Make sure the digit is allowed + if (switch_regex_match(digit_buffer, digits_regex) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Match found!\n"); + //jobs done + break; + } else { + //See if a bad input prompt was specified, if so, play it + if (strlen(bad_input_audio_file) > 0) { + status = switch_ivr_play_file(session, NULL, bad_input_audio_file, NULL); + + //Make sure we made it out alive + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + break; + } + } + } + } else { + //jobs done + break; + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Calling more digits try %d\n", max_tries); + + //Try to grab some more digits for the timeout period + status = + switch_ivr_collect_digits_count(session, digit_buffer, digit_buffer_length, max_digits, valid_terminators, + &terminator, timeout); + + //Make sure we made it out alive + if (status != SWITCH_STATUS_SUCCESS) { + //Bail + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + break; + } + //see if we got enough + if (min_digits <= strlen(digit_buffer)) { + //See if we need to test a regex + if (digits_regex != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking regex [%s] on [%s]\n", digits_regex, + (char *) digit_buffer); + //Test the regex + if (switch_regex_match(digit_buffer, digits_regex) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Match found!\n"); + //Jobs done + return SWITCH_STATUS_SUCCESS; + } else { + //See if a bad input prompt was specified, if so, play it + if (strlen(bad_input_audio_file) > 0) { + status = switch_ivr_play_file(session, NULL, bad_input_audio_file, NULL); + + //Make sure we made it out alive + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + break; + } + } + } + } else { + //Jobs done + return SWITCH_STATUS_SUCCESS; + } + } + } + + //if we got here, we got no digits or lost the channel + digit_buffer = "\0"; + return SWITCH_STATUS_FALSE; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session_t *session, + switch_speech_handle_t *sh, + switch_codec_t *codec, + switch_timer_t *timer, + char *text, switch_input_args_t *args) +{ + switch_channel_t *channel; + short abuf[960]; + char dtmf[128]; + uint32_t len = 0; + switch_size_t ilen = 0; + switch_frame_t write_frame = { 0 }; + int x; + int stream_id = 0; + int done = 0; + int lead_in_out = 10; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; + uint32_t rate = 0, samples = 0; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (!sh) { + return SWITCH_STATUS_FALSE; + } + + switch_channel_answer(channel); + + write_frame.data = abuf; + write_frame.buflen = sizeof(abuf); + + samples = (uint32_t) (sh->rate / 50); + len = samples * 2; + + flags = 0; + switch_sleep(200000); + switch_core_speech_feed_tts(sh, text, &flags); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Speaking text: %s\n", text); + + write_frame.rate = sh->rate; + + memset(write_frame.data, 0, len); + write_frame.datalen = len; + write_frame.samples = len / 2; + write_frame.codec = codec; + + for (x = 0; !done && x < lead_in_out; x++) { + switch_yield(codec->implementation->microseconds_per_frame); + if (timer) { + write_frame.timestamp = timer->samplecount; + } + if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n"); + done = 1; + break; + } + } + + ilen = len; + while (switch_channel_ready(channel)) { + switch_event_t *event; + + if (switch_core_session_dequeue_private_event(session, &event) == SWITCH_STATUS_SUCCESS) { + switch_ivr_parse_event(session, event); + switch_event_destroy(&event); + } + + if (args && (args->input_callback || args->buf || args->buflen)) { + /* + dtmf handler function you can hook up to be executed when a digit is dialed during playback + if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. + */ + if (switch_channel_has_dtmf(channel)) { + if (!args->input_callback && !args->buf) { + status = SWITCH_STATUS_BREAK; + done = 1; + break; + } + if (args->buf && !strcasecmp(args->buf, "_break_")) { + status = SWITCH_STATUS_BREAK; + } else { + switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); + if (args->input_callback) { + status = args->input_callback(session, dtmf, SWITCH_INPUT_TYPE_DTMF, args->buf, args->buflen); + } else { + switch_copy_string((char *) args->buf, dtmf, args->buflen); + status = SWITCH_STATUS_BREAK; + } + } + } + + if (args->input_callback) { + if (switch_core_session_dequeue_event(session, &event) == SWITCH_STATUS_SUCCESS) { + status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_event_destroy(&event); + } + } + + if (status != SWITCH_STATUS_SUCCESS) { + done = 1; + break; + } + } + + if (switch_test_flag(sh, SWITCH_SPEECH_FLAG_PAUSE)) { + if (timer) { + if (switch_core_timer_next(timer) != SWITCH_STATUS_SUCCESS) { + break; + } + } else { + switch_frame_t *read_frame; + switch_status_t status = switch_core_session_read_frame(session, &read_frame, -1, 0); + + while (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_HOLD)) { + switch_yield(10000); + } + + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + } + continue; + } + + flags = SWITCH_SPEECH_FLAG_BLOCKING; + status = switch_core_speech_read_tts(sh, abuf, &ilen, &rate, &flags); + + if (status != SWITCH_STATUS_SUCCESS) { + for (x = 0; !done && x < lead_in_out; x++) { + switch_yield(codec->implementation->microseconds_per_frame); + if (timer) { + write_frame.timestamp = timer->samplecount; + } + if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n"); + done = 1; + break; + } + } + if (status == SWITCH_STATUS_BREAK) { + status = SWITCH_STATUS_SUCCESS; + } + done = 1; + } + + if (done) { + break; + } + + write_frame.datalen = (uint32_t) ilen; + write_frame.samples = (uint32_t) (ilen / 2); + if (timer) { + write_frame.timestamp = timer->samplecount; + } + if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n"); + done = 1; + break; + } + + if (done) { + break; + } + + if (timer) { + if (switch_core_timer_next(timer) != SWITCH_STATUS_SUCCESS) { + break; + } + } else { /* time off the channel (if you must) */ + switch_frame_t *read_frame; + switch_status_t status = switch_core_session_read_frame(session, &read_frame, -1, 0); + + while (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_HOLD)) { + switch_yield(10000); + } + + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + } + + } + + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "done speaking text\n"); + flags = 0; + switch_core_speech_flush_tts(sh); + return status; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *session, + char *tts_name, + char *voice_name, + uint32_t rate, char *text, switch_input_args_t *args) +{ + switch_channel_t *channel; + int interval = 0; + uint32_t samples = 0; + uint32_t len = 0; + switch_frame_t write_frame = { 0 }; + switch_timer_t timer; + switch_core_thread_session_t thread_session; + switch_codec_t codec; + switch_memory_pool_t *pool = switch_core_session_get_pool(session); + char *codec_name; + int stream_id = 0; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_speech_handle_t sh; + switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; + switch_codec_t *read_codec; + char *timer_name; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + timer_name = switch_channel_get_variable(channel, "timer_name"); + + if (rate == 0) { + read_codec = switch_core_session_get_read_codec(session); + rate = read_codec->implementation->samples_per_second; + } + + memset(&sh, 0, sizeof(sh)); + if (switch_core_speech_open(&sh, + tts_name, + voice_name, + (uint32_t) rate, + &flags, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module!\n"); + switch_core_session_reset(session); + return SWITCH_STATUS_FALSE; + } + + switch_channel_answer(channel); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "OPEN TTS %s\n", tts_name); + + interval = 20; + samples = (uint32_t) (rate / 50); + len = samples * 2; + + codec_name = "L16"; + + if (switch_core_codec_init(&codec, + codec_name, + NULL, + (int) rate, + interval, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, pool) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activated\n"); + write_frame.codec = &codec; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Failed %s@%uhz 1 channel %dms\n", + codec_name, rate, interval); + flags = 0; + switch_core_speech_close(&sh, &flags); + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + + if (timer_name) { + if (switch_core_timer_init(&timer, timer_name, interval, (int) samples, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n"); + switch_core_codec_destroy(&codec); + flags = 0; + switch_core_speech_close(&sh, &flags); + + switch_core_session_reset(session); + return SWITCH_STATUS_GENERR; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "setup timer success %u bytes per %d ms!\n", len, + interval); + + /* start a thread to absorb incoming audio */ + for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) { + switch_core_service_session(session, &thread_session, stream_id); + } + } + + status = switch_ivr_speak_text_handle(session, &sh, &codec, timer_name ? &timer : NULL, text, args); + flags = 0; + switch_core_speech_close(&sh, &flags); + switch_core_codec_destroy(&codec); + + if (timer_name) { + /* End the audio absorbing thread */ + switch_core_thread_session_end(&thread_session); + switch_core_timer_destroy(&timer); + } + + switch_core_session_reset(session); + return status; +} diff --git a/src/switch_scheduler.c b/src/switch_scheduler.c new file mode 100644 index 0000000000..3b6fbb5e26 --- /dev/null +++ b/src/switch_scheduler.c @@ -0,0 +1,234 @@ +#include + +struct switch_scheduler_task_container { + switch_scheduler_task_t task; + time_t executed; + int in_thread; + int destroyed; + switch_scheduler_func_t func; + switch_memory_pool_t *pool; + uint32_t flags; + char *desc; + struct switch_scheduler_task_container *next; +}; +typedef struct switch_scheduler_task_container switch_scheduler_task_container_t; + + +static struct { + switch_scheduler_task_container_t *task_list; + switch_mutex_t *task_mutex; + uint32_t task_id; + int task_thread_running; + switch_memory_pool_t *memory_pool; +} globals; + +static void switch_scheduler_execute(switch_scheduler_task_container_t * tp) +{ + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Executing task %u %s (%s)\n", tp->task.task_id, tp->desc, switch_str_nil(tp->task.group)); + + tp->func(&tp->task); + + if (tp->task.runtime > tp->executed) { + tp->executed = 0; + } else { + tp->destroyed = 1; + } +} + +static void *SWITCH_THREAD_FUNC task_own_thread(switch_thread_t *thread, void *obj) +{ + switch_scheduler_task_container_t *tp = (switch_scheduler_task_container_t *) obj; + switch_memory_pool_t *pool; + + pool = tp->pool; + tp->pool = NULL; + + switch_scheduler_execute(tp); + switch_core_destroy_memory_pool(&pool); + tp->in_thread = 0; + + return NULL; +} + +static int task_thread_loop(int done) +{ + switch_scheduler_task_container_t *tofree, *tp, *last = NULL; + + + switch_mutex_lock(globals.task_mutex); + for (tp = globals.task_list; tp; tp = tp->next) { + if (done) { + tp->destroyed = 1; + } else { + time_t now = time(NULL); + if (now >= tp->task.runtime && !tp->in_thread) { + tp->executed = now; + if (switch_test_flag(tp, SSHF_OWN_THREAD)) { + switch_thread_t *thread; + switch_threadattr_t *thd_attr; + switch_core_new_memory_pool(&tp->pool); + switch_threadattr_create(&thd_attr, tp->pool); + switch_threadattr_detach_set(thd_attr, 1); + tp->in_thread = 1; + switch_thread_create(&thread, thd_attr, task_own_thread, tp, tp->pool); + } else { + switch_scheduler_execute(tp); + } + } + } + } + switch_mutex_unlock(globals.task_mutex); + switch_mutex_lock(globals.task_mutex); + for (tp = globals.task_list; tp;) { + if (tp->destroyed && !tp->in_thread) { + tofree = tp; + tp = tp->next; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleting task %u %s (%s)\n", + tofree->task.task_id, tofree->desc, switch_str_nil(tofree->task.group)); + if (last) { + last->next = tofree->next; + } else { + globals.task_list = tofree->next; + } + switch_safe_free(tofree->task.group); + if (tofree->task.cmd_arg && switch_test_flag(tofree, SSHF_FREE_ARG)) { + free(tofree->task.cmd_arg); + } + switch_safe_free(tofree->desc); + free(tofree); + } else { + last = tp; + tp = tp->next; + } + } + switch_mutex_unlock(globals.task_mutex); + + return done; +} + +static void *SWITCH_THREAD_FUNC switch_scheduler_task_thread(switch_thread_t *thread, void *obj) +{ + + globals.task_thread_running = 1; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Starting task thread\n"); + while (globals.task_thread_running == 1) { + if (task_thread_loop(0)) { + break; + } + switch_yield(500000); + } + + task_thread_loop(1); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Task thread ending\n"); + globals.task_thread_running = 0; + + return NULL; +} + +SWITCH_DECLARE(uint32_t) switch_scheduler_add_task(time_t task_runtime, + switch_scheduler_func_t func, + char *desc, + char *group, + uint32_t cmd_id, void *cmd_arg, switch_scheduler_flag_t flags) +{ + switch_scheduler_task_container_t *container, *tp; + + switch_mutex_lock(globals.task_mutex); + switch_zmalloc(container, sizeof(*container)); + assert(func); + container->func = func; + time(&container->task.created); + container->task.runtime = task_runtime; + container->task.group = strdup(group ? group : "none"); + container->task.cmd_id = cmd_id; + container->task.cmd_arg = cmd_arg; + container->flags = flags; + container->desc = strdup(desc ? desc : "none"); + + for (tp = globals.task_list; tp && tp->next; tp = tp->next); + + if (tp) { + tp->next = container; + } else { + globals.task_list = container; + } + + for (container->task.task_id = 0; !container->task.task_id; container->task.task_id = ++globals.task_id); + + switch_mutex_unlock(globals.task_mutex); + + tp = container; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Added task %u %s (%s) to run at %" TIME_T_FMT "\n", + tp->task.task_id, tp->desc, switch_str_nil(tp->task.group), task_runtime); + + + return container->task.task_id; +} + +SWITCH_DECLARE(switch_status_t) switch_scheduler_del_task_id(uint32_t task_id) +{ + switch_scheduler_task_container_t *tp; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(globals.task_mutex); + for (tp = globals.task_list; tp; tp = tp->next) { + if (tp->task.task_id == task_id) { + tp->destroyed++; + status = SWITCH_STATUS_SUCCESS; + break; + } + } + switch_mutex_unlock(globals.task_mutex); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_scheduler_del_task_group(char *group) +{ + switch_scheduler_task_container_t *tp; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(globals.task_mutex); + for (tp = globals.task_list; tp; tp = tp->next) { + if (!strcmp(tp->task.group, group)) { + tp->destroyed++; + status = SWITCH_STATUS_SUCCESS; + } + } + switch_mutex_unlock(globals.task_mutex); + + return status; +} + +SWITCH_DECLARE(void) switch_scheduler_task_thread_start(void) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr; + + switch_core_new_memory_pool(&globals.memory_pool); + switch_threadattr_create(&thd_attr, globals.memory_pool); + switch_mutex_init(&globals.task_mutex, SWITCH_MUTEX_NESTED, globals.memory_pool); + + switch_threadattr_detach_set(thd_attr, 1); + switch_thread_create(&thread, thd_attr, switch_scheduler_task_thread, NULL, globals.memory_pool); +} + + +SWITCH_DECLARE(void) switch_scheduler_task_thread_stop(void) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Stopping Task Thread\n"); + if (globals.task_thread_running == 1) { + int sanity = 0; + + globals.task_thread_running = -1; + + while (globals.task_thread_running) { + switch_yield(100000); + if (++sanity > 10) { + break; + } + } + } +}