From 4a172402d2f10aba78317ccb192c891b393117bd Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Wed, 16 Oct 2013 02:24:32 +0500
Subject: [PATCH] some refactoring and plumbing for 1.4

---
 src/include/private/switch_core_pvt.h         |   2 +-
 src/include/switch_channel.h                  |   2 +
 src/include/switch_core.h                     |   7 +-
 src/include/switch_core_media.h               |   3 +
 src/include/switch_event.h                    |  33 +-
 src/include/switch_json.h                     |  52 +-
 src/include/switch_loadable_module.h          |  34 +-
 src/include/switch_module_interfaces.h        |  18 +
 src/include/switch_rtp.h                      |   1 +
 src/include/switch_types.h                    |  27 +-
 src/mod/endpoints/mod_alsa/mod_alsa.c         |   3 -
 .../endpoints/mod_dingaling/mod_dingaling.c   |   6 +-
 .../mod_gsmopen/mod_gsmopen.c                 |   5 -
 src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp |   5 -
 src/mod/endpoints/mod_h323/mod_h323.cpp       |   1 -
 src/mod/endpoints/mod_loopback/mod_loopback.c |   4 +-
 src/mod/endpoints/mod_opal/mod_opal.cpp       |   2 +-
 .../endpoints/mod_portaudio/mod_portaudio.c   |   5 -
 .../endpoints/mod_reference/mod_reference.c   |   5 -
 src/mod/endpoints/mod_rtmp/mod_rtmp.c         |   7 -
 src/mod/endpoints/mod_skinny/mod_skinny.c     |   3 +-
 src/mod/endpoints/mod_skypopen/mod_skypopen.c |  11 +-
 src/mod/endpoints/mod_sofia/mod_sofia.c       |  56 +-
 src/mod/endpoints/mod_sofia/rtp.c             |   2 +-
 src/mod/endpoints/mod_sofia/sofia_glue.c      |   3 -
 src/mod/endpoints/mod_unicall/mod_unicall.c   |   5 -
 src/switch_channel.c                          |  30 +-
 src/switch_core_codec.c                       |   2 +
 src/switch_core_io.c                          |   4 +-
 src/switch_core_media.c                       | 407 +++++---
 src/switch_core_session.c                     |  17 +-
 src/switch_core_sqldb.c                       |  27 +-
 src/switch_core_state_machine.c               |  49 +
 src/switch_event.c                            | 876 +++++++++++++++++-
 src/switch_json.c                             |  39 +-
 src/switch_loadable_module.c                  | 137 +++
 src/switch_rtp.c                              | 126 ++-
 37 files changed, 1729 insertions(+), 287 deletions(-)

diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h
index f6d3474386..bff9b72a78 100644
--- a/src/include/private/switch_core_pvt.h
+++ b/src/include/private/switch_core_pvt.h
@@ -144,7 +144,7 @@ struct switch_core_session {
 	int stream_count;
 
 	char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
-	void *private_info;
+	void *private_info[SWITCH_CORE_SESSION_MAX_PRIVATES];
 	switch_queue_t *event_queue;
 	switch_queue_t *message_queue;
 	switch_queue_t *signal_data_queue;
diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h
index 2e53ef6107..234b692114 100644
--- a/src/include/switch_channel.h
+++ b/src/include/switch_channel.h
@@ -633,6 +633,8 @@ SWITCH_DECLARE(int) switch_channel_test_app_flag_key(const char *app, switch_cha
 SWITCH_DECLARE(void) switch_channel_set_bridge_time(switch_channel_t *channel);
 SWITCH_DECLARE(void) switch_channel_set_hangup_time(switch_channel_t *channel);
 SWITCH_DECLARE(switch_call_direction_t) switch_channel_direction(switch_channel_t *channel);
+SWITCH_DECLARE(void) switch_channel_set_direction(switch_channel_t *channel, switch_call_direction_t direction);
+
 SWITCH_DECLARE(switch_core_session_t *) switch_channel_get_session(switch_channel_t *channel);
 SWITCH_DECLARE(char *) switch_channel_get_flag_string(switch_channel_t *channel);
 SWITCH_DECLARE(char *) switch_channel_get_cap_string(switch_channel_t *channel);
diff --git a/src/include/switch_core.h b/src/include/switch_core.h
index 064ab56324..11340c211c 100644
--- a/src/include/switch_core.h
+++ b/src/include/switch_core.h
@@ -1070,7 +1070,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_receive_event(_In_ switch_co
   \param session the session to retrieve from
   \return a pointer to the private data
 */
-SWITCH_DECLARE(void *) switch_core_session_get_private(_In_ switch_core_session_t *session);
+SWITCH_DECLARE(void *) switch_core_session_get_private_class(_In_ switch_core_session_t *session, _In_ switch_pvt_class_t index);
+#define switch_core_session_get_private(_s) switch_core_session_get_private_class(_s, SWITCH_PVT_PRIMARY)
 
 /*! 
   \brief Add private user data to a session
@@ -1078,7 +1079,8 @@ SWITCH_DECLARE(void *) switch_core_session_get_private(_In_ switch_core_session_
   \param private_info the used data to add
   \return SWITCH_STATUS_SUCCESS if data is added
 */
-SWITCH_DECLARE(switch_status_t) switch_core_session_set_private(_In_ switch_core_session_t *session, _In_ void *private_info);
+SWITCH_DECLARE(switch_status_t) switch_core_session_set_private_class(_In_ switch_core_session_t *session, _In_ void *private_info, _In_ switch_pvt_class_t index);
+#define switch_core_session_set_private(_s, _p) switch_core_session_set_private_class(_s, _p, SWITCH_PVT_PRIMARY)
 
 /*!
   \brief Add a logical stream to a session
@@ -2575,6 +2577,7 @@ SWITCH_DECLARE(int) switch_system(const char *cmd, switch_bool_t wait);
 SWITCH_DECLARE(int) switch_stream_system_fork(const char *cmd, switch_stream_handle_t *stream);
 SWITCH_DECLARE(int) switch_stream_system(const char *cmd, switch_stream_handle_t *stream);
 
+SWITCH_DECLARE(switch_call_direction_t) switch_ice_direction(switch_core_session_t *session);
 
 SWITCH_END_EXTERN_C
 #endif
diff --git a/src/include/switch_core_media.h b/src/include/switch_core_media.h
index 122f9f2b65..9641718f07 100644
--- a/src/include/switch_core_media.h
+++ b/src/include/switch_core_media.h
@@ -207,6 +207,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_write_frame(switch_core_sessio
 SWITCH_DECLARE(int) switch_core_media_check_nat(switch_media_handle_t *smh, const char *network_ip);
 
 SWITCH_DECLARE(switch_status_t) switch_core_media_choose_port(switch_core_session_t *session, switch_media_type_t type, int force);
+SWITCH_DECLARE(switch_status_t) switch_core_media_choose_ports(switch_core_session_t *session, switch_bool_t audio, switch_bool_t video);
 SWITCH_DECLARE(void) switch_core_media_check_dtmf_type(switch_core_session_t *session);
 SWITCH_DECLARE(void) switch_core_media_absorb_sdp(switch_core_session_t *session);
 SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_session_t *session, const char *sdp_str);
@@ -219,6 +220,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 SWITCH_DECLARE(void)switch_core_media_set_local_sdp(switch_core_session_t *session, const char *sdp_str, switch_bool_t dup);
 SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session);
 SWITCH_DECLARE(void) switch_core_media_set_udptl_image_sdp(switch_core_session_t *session, switch_t38_options_t *t38_options, int insist);
+SWITCH_DECLARE(switch_core_media_params_t *) switch_core_media_get_mparams(switch_media_handle_t *smh);
 SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *session, switch_bool_t force);
 SWITCH_DECLARE(void) switch_core_media_start_udptl(switch_core_session_t *session, switch_t38_options_t *t38_options);
 SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg);
@@ -254,6 +256,7 @@ SWITCH_DECLARE(void) switch_core_media_init(void);
 SWITCH_DECLARE(void) switch_core_media_deinit(void);
 SWITCH_DECLARE(void) switch_core_media_set_stats(switch_core_session_t *session);
 SWITCH_DECLARE(void) switch_core_session_wake_video_thread(switch_core_session_t *session);
+SWITCH_DECLARE(void) switch_core_session_clear_crypto(switch_core_session_t *session);
 
 SWITCH_END_EXTERN_C
 #endif
diff --git a/src/include/switch_event.h b/src/include/switch_event.h
index 48c942dfaa..be60c17cd9 100644
--- a/src/include/switch_event.h
+++ b/src/include/switch_event.h
@@ -309,10 +309,11 @@ SWITCH_DECLARE(switch_status_t) switch_event_free_subclass_detailed(const char *
   \return SWITCH_STATUS_SUCCESS if the operation was successful
   \note you must free the resulting string when you are finished with it
 */
-SWITCH_DECLARE(switch_status_t) switch_event_binary_deserialize(switch_event_t **eventp, void **data, switch_size_t len, switch_bool_t destroy);
+SWITCH_DECLARE(switch_status_t) switch_event_binary_deserialize(switch_event_t **eventp, void **data, switch_size_t len, switch_bool_t duplicate);
 SWITCH_DECLARE(switch_status_t) switch_event_binary_serialize(switch_event_t *event, void **data, switch_size_t *len);
 SWITCH_DECLARE(switch_status_t) switch_event_serialize(switch_event_t *event, char **str, switch_bool_t encode);
 SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *event, char **str);
+SWITCH_DECLARE(switch_status_t) switch_event_serialize_json_obj(switch_event_t *event, cJSON **json);
 SWITCH_DECLARE(switch_status_t) switch_event_create_json(switch_event_t **event, const char *json);
 SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, char b, char c, switch_event_t **event, char **new_data, switch_bool_t dup);
 SWITCH_DECLARE(switch_status_t) switch_event_create_array_pair(switch_event_t **event, char **names, char **vals, int len);
@@ -420,8 +421,38 @@ SWITCH_DECLARE(void) switch_event_deliver(switch_event_t **event);
 SWITCH_DECLARE(char *) switch_event_build_param_string(switch_event_t *event, const char *prefix, switch_hash_t *vars_map);
 SWITCH_DECLARE(int) switch_event_check_permission_list(switch_event_t *list, const char *name);
 SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *channel, switch_event_t *event, const char *prefix);
+SWITCH_DECLARE(void) switch_json_add_presence_data_cols(switch_event_t *event, cJSON *json, const char *prefix);
+
 SWITCH_DECLARE(void) switch_event_launch_dispatch_threads(uint32_t max);
 
+SWITCH_DECLARE(uint32_t) switch_event_channel_broadcast(const char *event_channel, cJSON **json, const char *key, switch_event_channel_id_t id);
+SWITCH_DECLARE(uint32_t) switch_event_channel_unbind(const char *event_channel, switch_event_channel_func_t func);
+SWITCH_DECLARE(switch_status_t) switch_event_channel_bind(const char *event_channel, switch_event_channel_func_t func, switch_event_channel_id_t *id);
+														  
+
+typedef void (*switch_live_array_command_handler_t)(switch_live_array_t *la, const char *cmd, const char *sessid, cJSON *jla, void *user_data);
+
+#define NO_EVENT_CHANNEL_ID 0
+#define SWITCH_EVENT_CHANNEL_GLOBAL "__global__"
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_clear(switch_live_array_t *la);
+SWITCH_DECLARE(switch_status_t) switch_live_array_bootstrap(switch_live_array_t *la, const char *sessid, switch_event_channel_id_t channel_id);
+SWITCH_DECLARE(switch_status_t) switch_live_array_destroy(switch_live_array_t **live_arrayP);
+SWITCH_DECLARE(switch_status_t) switch_live_array_create(const char *event_channel, const char *name,
+														 switch_event_channel_id_t channel_id, switch_live_array_t **live_arrayP);
+SWITCH_DECLARE(cJSON *) switch_live_array_get(switch_live_array_t *la, const char *name);
+SWITCH_DECLARE(cJSON *) switch_live_array_get_idx(switch_live_array_t *la, int idx);
+SWITCH_DECLARE(switch_status_t) switch_live_array_del(switch_live_array_t *la, const char *name);
+SWITCH_DECLARE(switch_status_t) switch_live_array_add(switch_live_array_t *la, const char *name, int index, cJSON **obj, switch_bool_t destroy);
+SWITCH_DECLARE(switch_status_t) switch_live_array_visible(switch_live_array_t *la, switch_bool_t visible, switch_bool_t force);
+SWITCH_DECLARE(switch_bool_t) switch_live_array_isnew(switch_live_array_t *la);
+SWITCH_DECLARE(void) switch_live_array_lock(switch_live_array_t *la);
+SWITCH_DECLARE(void) switch_live_array_unlock(switch_live_array_t *la);
+SWITCH_DECLARE(void) switch_live_array_set_user_data(switch_live_array_t *la, void *user_data);
+SWITCH_DECLARE(void) switch_live_array_set_command_handler(switch_live_array_t *la, switch_live_array_command_handler_t command_handler);
+SWITCH_DECLARE(void) switch_live_array_parse_json(cJSON *json, switch_event_channel_id_t channel_id);
+
+
 ///\}
 
 SWITCH_END_EXTERN_C
diff --git a/src/include/switch_json.h b/src/include/switch_json.h
index 00f4e23df4..e1d7f1e51b 100755
--- a/src/include/switch_json.h
+++ b/src/include/switch_json.h
@@ -76,7 +76,8 @@ SWITCH_DECLARE(int)	  cJSON_GetArraySize(cJSON *array);
 /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
 SWITCH_DECLARE(cJSON *)cJSON_GetArrayItem(cJSON *array,int item);
 /* Get item "string" from object. Case insensitive. */
-SWITCH_DECLARE(cJSON *)cJSON_GetObjectItem(cJSON *object,const char *string);
+SWITCH_DECLARE(cJSON *)cJSON_GetObjectItem(const cJSON *object,const char *string);
+SWITCH_DECLARE(const char *)cJSON_GetObjectCstr(const cJSON *object, const char *string);
 
 /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
 SWITCH_DECLARE(const char *)cJSON_GetErrorPtr(void);
@@ -127,6 +128,55 @@ SWITCH_DECLARE(cJSON *) cJSON_Duplicate(cJSON *item,int recurse);
 #define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
 #define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
 
+SWITCH_DECLARE(cJSON *) cJSON_CreateStringPrintf(const char *fmt, ...);
+
+static inline cJSON *json_add_child_obj(cJSON *json, const char *name, cJSON *obj)
+{
+	cJSON *new_json = NULL;
+
+	switch_assert(json);
+
+	if (obj) {
+		new_json = obj;
+	} else {
+		new_json = cJSON_CreateObject();
+	}
+
+	switch_assert(new_json);
+
+	cJSON_AddItemToObject(json, name, new_json);
+
+	return new_json;
+}
+
+static inline cJSON *json_add_child_array(cJSON *json, const char *name)
+{
+	cJSON *new_json = NULL;
+
+	switch_assert(json);
+
+	new_json = cJSON_CreateArray();
+	switch_assert(new_json);
+
+	cJSON_AddItemToObject(json, name, new_json);
+
+	return new_json;
+}
+
+static inline cJSON *json_add_child_string(cJSON *json, const char *name, const char *val)
+{
+	cJSON *new_json = NULL;
+
+	switch_assert(json);
+
+	new_json = cJSON_CreateString(val);
+	switch_assert(new_json);
+
+	cJSON_AddItemToObject(json, name, new_json);
+
+	return new_json;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/switch_loadable_module.h b/src/include/switch_loadable_module.h
index 43e692c05b..e76e0e5ab1 100644
--- a/src/include/switch_loadable_module.h
+++ b/src/include/switch_loadable_module.h
@@ -69,6 +69,8 @@ SWITCH_BEGIN_EXTERN_C
 	switch_chat_application_interface_t *chat_application_interface;
 	/*! the table of api functions the module has implemented */
 	switch_api_interface_t *api_interface;
+	/*! the table of json api functions the module has implemented */
+	switch_json_api_interface_t *json_api_interface;
 	/*! the table of file formats the module has implemented */
 	switch_file_interface_t *file_interface;
 	/*! the table of speech interfaces the module has implemented */
@@ -189,6 +191,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_execute_chat_app(switch_event_t *mes
  */
 SWITCH_DECLARE(switch_api_interface_t *) switch_loadable_module_get_api_interface(const char *name);
 
+/*!
+  \brief Retrieve the JSON API interface by it's registered name
+  \param name the name of the API
+  \return the desired API interface
+ */
+SWITCH_DECLARE(switch_json_api_interface_t *) switch_loadable_module_get_json_api_interface(const char *name);
+
 /*!
   \brief Retrieve the file format interface by it's registered name
   \param name the name of the file format
@@ -275,6 +284,16 @@ SWITCH_DECLARE(int) switch_loadable_module_get_codecs_sorted(const switch_codec_
 */
 SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *arg, switch_core_session_t *session, switch_stream_handle_t *stream);
 
+/*!
+  \brief Execute a registered JSON API command
+  \param json the name of the JSON API command to execute
+  \param arg the optional arguement to the command
+  \param session an optional session
+  \param stream stream for output
+  \return the status returned by the API call
+*/
+SWITCH_DECLARE(switch_status_t) switch_json_api_execute(cJSON *json, switch_core_session_t *session, cJSON **retval);
+
 /*!
   \brief Load a module
   \param dir the directory where the module resides
@@ -328,6 +347,16 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void);
 	break; \
 	}
 
+#define SWITCH_ADD_JSON_API(json_api_int, int_name, descript, funcptr, syntax_string) \
+	for (;;) { \
+	json_api_int = (switch_json_api_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_JSON_API_INTERFACE); \
+	json_api_int->interface_name = int_name; \
+	json_api_int->desc = descript; \
+	json_api_int->function = funcptr; \
+	json_api_int->syntax = syntax_string; \
+	break; \
+	}
+
 #define SWITCH_ADD_CHAT(chat_int, int_name, funcptr) \
 	for (;;) { \
 	chat_int = (switch_chat_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_CHAT_INTERFACE); \
@@ -510,8 +539,9 @@ static inline switch_bool_t switch_core_codec_ready(switch_codec_t *codec)
 }
 
 
-
-
+SWITCH_DECLARE(switch_core_recover_callback_t) switch_core_get_secondary_recover_callback(const char *key);
+SWITCH_DECLARE(switch_status_t) switch_core_register_secondary_recover_callback(const char *key, switch_core_recover_callback_t cb);
+SWITCH_DECLARE(void) switch_core_unregister_secondary_recover_callback(const char *key);
 
 SWITCH_END_EXTERN_C
 #endif
diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h
index b6f4f11554..ad6b4aefd4 100644
--- a/src/include/switch_module_interfaces.h
+++ b/src/include/switch_module_interfaces.h
@@ -743,6 +743,24 @@ struct switch_api_interface {
 	struct switch_api_interface *next;
 };
 
+
+/*! \brief A module interface to implement a json api function */
+struct switch_json_api_interface {
+	/*! the name of the interface */
+	const char *interface_name;
+	/*! a description of the api function */
+	const char *desc;
+	/*! function the api call uses */
+	switch_json_api_function_t function;
+	/*! an example of the api syntax */
+	const char *syntax;
+	switch_thread_rwlock_t *rwlock;
+	int refs;
+	switch_mutex_t *reflock;
+	switch_loadable_module_interface_t *parent;
+	struct switch_json_api_interface *next;
+};
+
 #define PROTECT_INTERFACE(_it) if (_it) {switch_mutex_lock(_it->reflock); switch_thread_rwlock_rdlock(_it->parent->rwlock); switch_thread_rwlock_rdlock(_it->rwlock); _it->refs++; _it->parent->refs++; switch_mutex_unlock(_it->reflock);}	//if (!strcmp(_it->interface_name, "user")) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "+++++++++++LOCK %s %d/%d\n", _it->interface_name, _it->refs, _it->parent->refs);
 #define UNPROTECT_INTERFACE(_it) if (_it) {switch_mutex_lock(_it->reflock); switch_thread_rwlock_unlock(_it->rwlock); switch_thread_rwlock_unlock(_it->parent->rwlock); _it->refs--; _it->parent->refs--; switch_mutex_unlock(_it->reflock);}	//if (!strcmp(_it->interface_name, "user")) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "---------UNLOCK %s %d/%d\n", _it->interface_name, _it->refs, _it->parent->refs);
 
diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h
index f704d943d1..bd509cc29a 100644
--- a/src/include/switch_rtp.h
+++ b/src/include/switch_rtp.h
@@ -508,6 +508,7 @@ SWITCH_DECLARE(switch_byte_t) switch_rtp_check_auto_adj(switch_rtp_t *rtp_sessio
 SWITCH_DECLARE(void) switch_rtp_set_interdigit_delay(switch_rtp_t *rtp_session, uint32_t delay);
 
 SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, dtls_fingerprint_t *local_fp, dtls_fingerprint_t *remote_fp, dtls_type_t type);
+SWITCH_DECLARE(switch_status_t) switch_rtp_del_dtls(switch_rtp_t *rtp_session, dtls_type_t type);
 
 SWITCH_DECLARE(int) switch_rtp_has_dtls(void);
 SWITCH_DECLARE(void) switch_rtp_video_refresh(switch_rtp_t *rtp_session);
diff --git a/src/include/switch_types.h b/src/include/switch_types.h
index 27a88dcc73..ee42de2eee 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -38,6 +38,7 @@
 #define SWITCH_TYPES_H
 
 #include <switch.h>
+#include <switch_json.h>
 
 SWITCH_BEGIN_EXTERN_C
 #define SWITCH_ENT_ORIGINATE_DELIM ":_:"
@@ -223,8 +224,16 @@ SWITCH_BEGIN_EXTERN_C
 #define SWITCH_DEFAULT_FILE_BUFFER_LEN 65536
 #define SWITCH_DTMF_LOG_LEN 1000
 #define SWITCH_MAX_TRANS 2000
+#define SWITCH_CORE_SESSION_MAX_PRIVATES 2
+
 typedef uint8_t switch_byte_t;
 
+typedef enum {
+	SWITCH_PVT_PRIMARY = 0,
+	SWITCH_PVT_SECONDARY
+} switch_pvt_class_t;
+
+
 /*!
   \enum switch_dtmf_source_t
   \brief DTMF sources
@@ -357,7 +366,8 @@ typedef enum {
 	SWITCH_ASR_INTERFACE,
 	SWITCH_MANAGEMENT_INTERFACE,
 	SWITCH_LIMIT_INTERFACE,
-	SWITCH_CHAT_APPLICATION_INTERFACE
+	SWITCH_CHAT_APPLICATION_INTERFACE,
+	SWITCH_JSON_API_INTERFACE,
 } switch_module_interface_name_t;
 
 typedef enum {
@@ -653,6 +663,7 @@ typedef enum {
 	SWITCH_RTP_FLAG_ENABLE_RTCP,
 	SWITCH_RTP_FLAG_RTCP_MUX,
 	SWITCH_RTP_FLAG_KILL_JB,
+	SWITCH_RTP_FLAG_VIDEO_BREAK,
 	SWITCH_RTP_FLAG_INVALID
 } switch_rtp_flag_t;
 
@@ -2000,6 +2011,7 @@ typedef struct switch_codec_interface switch_codec_interface_t;
 typedef struct switch_application_interface switch_application_interface_t;
 typedef struct switch_chat_application_interface switch_chat_application_interface_t;
 typedef struct switch_api_interface switch_api_interface_t;
+typedef struct switch_json_api_interface switch_json_api_interface_t;
 typedef struct switch_file_interface switch_file_interface_t;
 typedef struct switch_speech_interface switch_speech_interface_t;
 typedef struct switch_asr_interface switch_asr_interface_t;
@@ -2083,6 +2095,12 @@ typedef switch_status_t (*switch_api_function_t) (_In_opt_z_ const char *cmd, _I
 
 #define SWITCH_STANDARD_API(name) static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream)
 
+
+typedef switch_status_t (*switch_json_api_function_t) (const cJSON *json, _In_opt_ switch_core_session_t *session, cJSON **json_reply);
+
+
+#define SWITCH_STANDARD_JSON_API(name) static switch_status_t name (const cJSON *json, _In_opt_ switch_core_session_t *session, cJSON **json_reply)
+
 typedef switch_status_t (*switch_input_callback_function_t) (switch_core_session_t *session, void *input,
 															 switch_input_type_t input_type, void *buf, unsigned int buflen);
 typedef switch_status_t (*switch_read_frame_callback_function_t) (switch_core_session_t *session, switch_frame_t *frame, void *user_data);
@@ -2236,6 +2254,13 @@ struct sql_queue_manager;
 struct switch_media_handle_s;
 typedef struct switch_media_handle_s switch_media_handle_t;
 
+typedef uint32_t switch_event_channel_id_t;
+typedef void (*switch_event_channel_func_t)(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id);
+
+struct switch_live_array_s;
+typedef struct switch_live_array_s switch_live_array_t;
+
+
 
 SWITCH_END_EXTERN_C
 #endif
diff --git a/src/mod/endpoints/mod_alsa/mod_alsa.c b/src/mod/endpoints/mod_alsa/mod_alsa.c
index dcdf215fe4..55f4ca4ba3 100644
--- a/src/mod/endpoints/mod_alsa/mod_alsa.c
+++ b/src/mod/endpoints/mod_alsa/mod_alsa.c
@@ -297,9 +297,6 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 
 	switch_set_flag_locked(tech_pvt, TFLAG_IO);
 
-	/* Move channel's state machine to ROUTING */
-	switch_channel_set_state(channel, CS_ROUTING);
-
 	return SWITCH_STATUS_SUCCESS;
 
 }
diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c
index 5fe5d89000..e7ecaaf949 100644
--- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c
+++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c
@@ -1916,6 +1916,7 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 {
 	switch_channel_t *channel = switch_core_session_get_channel(session);
 	struct private_object *tech_pvt = NULL;
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
 
 	tech_pvt = switch_core_session_get_private(session);
 	switch_assert(tech_pvt != NULL);
@@ -1928,11 +1929,10 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 		if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
 			switch_channel_mark_answered(channel);
 		}
-		/* Move channel's state machine to ROUTING */
-		switch_channel_set_state(channel, CS_ROUTING);
+		status = SWITCH_STATUS_SUCCESS;
 	}
 
-	return SWITCH_STATUS_SUCCESS;
+	return status;
 }
 
 static switch_status_t channel_on_routing(switch_core_session_t *session)
diff --git a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.c b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.c
index 9f25ee1d96..f6c9605cb2 100644
--- a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.c
+++ b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.c
@@ -586,11 +586,6 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 	//ERRORA("%s CHANNEL INIT\n", GSMOPEN_P_LOG, tech_pvt->name);
 	switch_set_flag(tech_pvt, TFLAG_IO);
 
-	/* Move channel's state machine to ROUTING. This means the call is trying
-	   to get from the initial start where the call because, to the point
-	   where a destination has been identified. If the channel is simply
-	   left in the initial state, nothing will happen. */
-	switch_channel_set_state(channel, CS_ROUTING);
 	switch_mutex_lock(globals.mutex);
 	globals.calls++;
 
diff --git a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp
index 9e78b8f55b..b3b3c1546c 100644
--- a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp
+++ b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp
@@ -416,11 +416,6 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 	switch_set_flag(tech_pvt, TFLAG_IO);
 	switch_mutex_unlock(tech_pvt->flag_mutex);
 
-	/* Move channel's state machine to ROUTING. This means the call is trying
-	   to get from the initial start where the call because, to the point
-	   where a destination has been identified. If the channel is simply
-	   left in the initial state, nothing will happen. */
-	switch_channel_set_state(channel, CS_ROUTING);
 	switch_mutex_lock(globals.mutex);
 	globals.calls++;
 
diff --git a/src/mod/endpoints/mod_h323/mod_h323.cpp b/src/mod/endpoints/mod_h323/mod_h323.cpp
index 17619129cf..adaf38deb1 100644
--- a/src/mod/endpoints/mod_h323/mod_h323.cpp
+++ b/src/mod/endpoints/mod_h323/mod_h323.cpp
@@ -1655,7 +1655,6 @@ switch_status_t FSH323Connection::on_init()
 	}
 
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"Started routing for connection [%p]\n",this);
-	switch_channel_set_state(channel, CS_ROUTING);
 
 	return SWITCH_STATUS_SUCCESS;
 }
diff --git a/src/mod/endpoints/mod_loopback/mod_loopback.c b/src/mod/endpoints/mod_loopback/mod_loopback.c
index c6cbfc8994..ee354ad90b 100644
--- a/src/mod/endpoints/mod_loopback/mod_loopback.c
+++ b/src/mod/endpoints/mod_loopback/mod_loopback.c
@@ -231,6 +231,7 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 	switch_caller_profile_t *caller_profile;
 	switch_event_t *vars = NULL;
 	const char *var;
+	switch_status_t status = SWITCH_STATUS_FALSE;
 
 	tech_pvt = switch_core_session_get_private(session);
 	switch_assert(tech_pvt != NULL);
@@ -354,11 +355,12 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 	}
 
 	switch_channel_set_variable(channel, "loopback_leg", switch_test_flag(tech_pvt, TFLAG_BLEG) ? "B" : "A");
+	status = SWITCH_STATUS_SUCCESS;
 	switch_channel_set_state(channel, CS_ROUTING);
 
   end:
 
-	return SWITCH_STATUS_SUCCESS;
+	return status;
 }
 
 static void do_reset(loopback_private_t *tech_pvt)
diff --git a/src/mod/endpoints/mod_opal/mod_opal.cpp b/src/mod/endpoints/mod_opal/mod_opal.cpp
index 633bcdbd3a..6f0fb3addd 100644
--- a/src/mod/endpoints/mod_opal/mod_opal.cpp
+++ b/src/mod/endpoints/mod_opal/mod_opal.cpp
@@ -853,7 +853,7 @@ switch_status_t FSConnection::on_init()
         return SWITCH_STATUS_FALSE;
 
     PTRACE(4, "mod_opal\tStarted routing for connection " << *this);
-    switch_channel_set_state(m_fsChannel, CS_ROUTING);
+
     return SWITCH_STATUS_SUCCESS;
 }
 
diff --git a/src/mod/endpoints/mod_portaudio/mod_portaudio.c b/src/mod/endpoints/mod_portaudio/mod_portaudio.c
index 9ce6730d9e..7f66558c61 100644
--- a/src/mod/endpoints/mod_portaudio/mod_portaudio.c
+++ b/src/mod/endpoints/mod_portaudio/mod_portaudio.c
@@ -282,11 +282,6 @@ SWITCH_STANDARD_API(pa_cmd);
 */
 static switch_status_t channel_on_init(switch_core_session_t *session)
 {
-	switch_channel_t *channel = switch_core_session_get_channel(session);
-
-	/* Move channel's state machine to ROUTING */
-	switch_channel_set_state(channel, CS_ROUTING);
-
 	return SWITCH_STATUS_SUCCESS;
 }
 
diff --git a/src/mod/endpoints/mod_reference/mod_reference.c b/src/mod/endpoints/mod_reference/mod_reference.c
index 08c27da0eb..ece46f8d63 100644
--- a/src/mod/endpoints/mod_reference/mod_reference.c
+++ b/src/mod/endpoints/mod_reference/mod_reference.c
@@ -141,11 +141,6 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 	assert(channel != NULL);
 	switch_set_flag_locked(tech_pvt, TFLAG_IO);
 
-	/* Move channel's state machine to ROUTING. This means the call is trying
-	   to get from the initial start where the call because, to the point
-	   where a destination has been identified. If the channel is simply
-	   left in the initial state, nothing will happen. */
-	switch_channel_set_state(channel, CS_ROUTING);
 	switch_mutex_lock(globals.mutex);
 	globals.calls++;
 	switch_mutex_unlock(globals.mutex);
diff --git a/src/mod/endpoints/mod_rtmp/mod_rtmp.c b/src/mod/endpoints/mod_rtmp/mod_rtmp.c
index 339ce81873..e25f38b861 100644
--- a/src/mod/endpoints/mod_rtmp/mod_rtmp.c
+++ b/src/mod/endpoints/mod_rtmp/mod_rtmp.c
@@ -163,13 +163,6 @@ switch_status_t rtmp_on_init(switch_core_session_t *session)
 	
 	switch_set_flag_locked(tech_pvt, TFLAG_IO);
 	
-	/* Move channel's state machine to ROUTING. This means the call is trying
-	   to get from the initial start where the call because, to the point
-	   where a destination has been identified. If the channel is simply
-	   left in the initial state, nothing will happen. */
-	switch_channel_set_state(channel, CS_ROUTING);
-
-	
 	switch_mutex_lock(rsession->profile->mutex);
 	rsession->profile->calls++;
 	switch_mutex_unlock(rsession->profile->mutex);
diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c
index cc94f1129f..0e1e700264 100644
--- a/src/mod/endpoints/mod_skinny/mod_skinny.c
+++ b/src/mod/endpoints/mod_skinny/mod_skinny.c
@@ -648,7 +648,8 @@ switch_status_t channel_on_init(switch_core_session_t *session)
 
 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL INIT\n", switch_channel_get_name(channel));
 
-	return SWITCH_STATUS_SUCCESS;
+	/* This does not set the state to routing like most modules do, this now happens in the default state handeler so return FALSE TO BLOCK IT*/
+	return SWITCH_STATUS_FALSE;
 }
 
 struct channel_on_routing_helper {
diff --git a/src/mod/endpoints/mod_skypopen/mod_skypopen.c b/src/mod/endpoints/mod_skypopen/mod_skypopen.c
index 024942e00e..b586bd0489 100644
--- a/src/mod/endpoints/mod_skypopen/mod_skypopen.c
+++ b/src/mod/endpoints/mod_skypopen/mod_skypopen.c
@@ -457,11 +457,6 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
 	switch_set_flag(tech_pvt, TFLAG_IO);
 	switch_mutex_unlock(tech_pvt->flag_mutex);
 
-	/* Move channel's state machine to ROUTING. This means the call is trying
-	   to get from the initial start where the call because, to the point
-	   where a destination has been identified. If the channel is simply
-	   left in the initial state, nothing will happen. */
-	switch_channel_set_state(channel, CS_ROUTING);
 	DEBUGA_SKYPE("%s CHANNEL INIT %s\n", SKYPOPEN_P_LOG, tech_pvt->name, switch_core_session_get_uuid(session));
 	switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(session), sizeof(tech_pvt->session_uuid_str));
 
@@ -670,11 +665,11 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session)
 
 static switch_status_t channel_on_routing(switch_core_session_t *session)
 {
-	switch_channel_t *channel = NULL;
+	//switch_channel_t *channel = NULL;
 	private_t *tech_pvt = NULL;
 
-	channel = switch_core_session_get_channel(session);
-	switch_assert(channel != NULL);
+	//channel = switch_core_session_get_channel(session);
+	//switch_assert(channel != NULL);
 
 	tech_pvt = switch_core_session_get_private(session);
 	switch_assert(tech_pvt != NULL);
diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c
index a4c8913ee5..b99c7aae96 100644
--- a/src/mod/endpoints/mod_sofia/mod_sofia.c
+++ b/src/mod/endpoints/mod_sofia/mod_sofia.c
@@ -102,20 +102,6 @@ static switch_status_t sofia_on_init(switch_core_session_t *session)
 		}
 	}
 
-
-
-	if (switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING_BRIDGE)) {
-		switch_channel_set_state(channel, CS_RESET);
-	} else {
-		if (switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING)) {
-			switch_channel_set_state(channel, CS_EXECUTE);
-		} else {
-			/* Move channel's state machine to ROUTING */
-			switch_channel_set_state(channel, CS_ROUTING);
-			assert(switch_channel_get_state(channel) != CS_INIT);
-		}
-	}
-
   end:
 
 	switch_mutex_unlock(tech_pvt->sofia_mutex);
@@ -156,43 +142,6 @@ static switch_status_t sofia_on_reset(switch_core_session_t *session)
 					  switch_channel_get_name(switch_core_session_get_channel(session)));
 
 
-	if (switch_channel_test_flag(tech_pvt->channel, CF_RECOVERING_BRIDGE)) {
-		switch_core_session_t *other_session = NULL;
-		const char *uuid = switch_core_session_get_uuid(session);
-
-		if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
-			const char *other_uuid = switch_channel_get_partner_uuid(channel);
-			int x = 0;
-
-			if (other_uuid) {
-				for (x = 0; other_session == NULL && x < 20; x++) {
-					if (!switch_channel_up(channel)) {
-						break;
-					}
-					other_session = switch_core_session_locate(other_uuid);
-					switch_yield(100000);
-				}
-			}
-
-			if (other_session) {
-				switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
-				switch_channel_clear_flag(channel, CF_BRIDGE_ORIGINATOR);
-				switch_channel_wait_for_state_timeout(other_channel, CS_RESET, 5000);
-				switch_channel_wait_for_flag(other_channel, CF_MEDIA_ACK, SWITCH_TRUE, 2000, NULL);
-
-				if (switch_channel_test_flag(channel, CF_PROXY_MODE) && switch_channel_test_flag(other_channel, CF_PROXY_MODE)) {
-					switch_ivr_signal_bridge(session, other_session);
-				} else {
-					switch_ivr_uuid_bridge(uuid, other_uuid);
-				}
-				switch_core_session_rwunlock(other_session);
-			}
-		}
-
-		switch_channel_clear_flag(tech_pvt->channel, CF_RECOVERING_BRIDGE);
-	}
-
-
 	return SWITCH_STATUS_SUCCESS;
 }
 
@@ -221,12 +170,11 @@ static switch_status_t sofia_on_execute(switch_core_session_t *session)
 	switch_channel_t *channel = switch_core_session_get_channel(session);
 	switch_assert(tech_pvt != NULL);
 
-	switch_channel_clear_flag(tech_pvt->channel, CF_RECOVERING);
-
 	if (!sofia_test_flag(tech_pvt, TFLAG_HOLD_LOCK)) {
 		sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
 		switch_channel_clear_flag(channel, CF_LEG_HOLDING);
 	}
+
 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s SOFIA EXECUTE\n",
 					  switch_channel_get_name(switch_core_session_get_channel(session)));
 
@@ -873,8 +821,6 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
 			tech_pvt->session_refresher = nua_no_refresher;
 		}
 
-
-
 		if (sofia_use_soa(tech_pvt)) {
 			nua_respond(tech_pvt->nh, SIP_200_OK,
 						NUTAG_AUTOANSWER(0),
diff --git a/src/mod/endpoints/mod_sofia/rtp.c b/src/mod/endpoints/mod_sofia/rtp.c
index ea4921052e..953ea6d8f0 100644
--- a/src/mod/endpoints/mod_sofia/rtp.c
+++ b/src/mod/endpoints/mod_sofia/rtp.c
@@ -300,7 +300,7 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
     
     switch_channel_set_state(channel, CS_CONSUME_MEDIA);
     
-    return SWITCH_STATUS_SUCCESS;
+    return SWITCH_STATUS_FALSE;
 }
 
 static switch_status_t channel_on_destroy(switch_core_session_t *session)
diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c
index f569a12dd2..833a725d44 100644
--- a/src/mod/endpoints/mod_sofia/sofia_glue.c
+++ b/src/mod/endpoints/mod_sofia/sofia_glue.c
@@ -1907,9 +1907,6 @@ int sofia_recover_callback(switch_core_session_t *session)
 								  switch_channel_get_name(channel), use_uuid);
 			}
 		}
-	
-		switch_core_media_recover_session(session);
-	
 	}
 
 	r++;
diff --git a/src/mod/endpoints/mod_unicall/mod_unicall.c b/src/mod/endpoints/mod_unicall/mod_unicall.c
index 18cd5d11d5..35c3cde286 100644
--- a/src/mod/endpoints/mod_unicall/mod_unicall.c
+++ b/src/mod/endpoints/mod_unicall/mod_unicall.c
@@ -850,11 +850,6 @@ static switch_status_t unicall_on_init(switch_core_session_t *session)
 
 	switch_set_flag_locked(tech_pvt, TFLAG_IO);
 
-	/* Move channel's state machine to ROUTING. This means the call is trying
-	   to get from the initial state to the point where a destination has been
-	   identified. If the channel is simply left in the initial state, nothing
-	   will happen. */
-	switch_channel_set_state(channel, CS_ROUTING);
 	switch_mutex_lock(globals.mutex);
 	globals.calls++;
 	switch_mutex_unlock(globals.mutex);
diff --git a/src/switch_channel.c b/src/switch_channel.c
index ae94061c26..76d3d49f97 100644
--- a/src/switch_channel.c
+++ b/src/switch_channel.c
@@ -385,6 +385,13 @@ SWITCH_DECLARE(switch_channel_timetable_t *) switch_channel_get_timetable(switch
 	return times;
 }
 
+SWITCH_DECLARE(void) switch_channel_set_direction(switch_channel_t *channel, switch_call_direction_t direction)
+{
+	if (!switch_core_session_in_thread(channel->session)) {
+		channel->direction = direction;
+	}
+}
+
 SWITCH_DECLARE(switch_call_direction_t) switch_channel_direction(switch_channel_t *channel)
 {
 	return channel->direction;
@@ -491,6 +498,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf(switch_channel_t *chan
 		switch_zmalloc(dt, sizeof(*dt));
 		*dt = new_dtmf;
 
+
 		while (switch_queue_trypush(channel->dtmf_queue, dt) != SWITCH_STATUS_SUCCESS) {
 			if (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
 				free(pop);
@@ -509,6 +517,8 @@ SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf(switch_channel_t *chan
 
 	switch_mutex_unlock(channel->dtmf_mutex);
 
+	switch_core_media_break(channel->session, SWITCH_MEDIA_TYPE_AUDIO);
+
 	return status;
 }
 
@@ -2951,19 +2961,33 @@ SWITCH_DECLARE(void) switch_channel_invert_cid(switch_channel_t *channel)
 SWITCH_DECLARE(void) switch_channel_flip_cid(switch_channel_t *channel)
 {
 	switch_event_t *event;
+	const char *tmp = NULL;
 
 	switch_mutex_lock(channel->profile_mutex);
 	if (channel->caller_profile->callee_id_name) {
+		tmp = channel->caller_profile->caller_id_name;
 		switch_channel_set_variable(channel, "pre_transfer_caller_id_name", channel->caller_profile->caller_id_name);
 		channel->caller_profile->caller_id_name = switch_core_strdup(channel->caller_profile->pool, channel->caller_profile->callee_id_name);
 	}
-	channel->caller_profile->callee_id_name = SWITCH_BLANK_STRING;
+
+	if (switch_channel_test_flag(channel, CF_BRIDGED)) {
+		channel->caller_profile->callee_id_name = SWITCH_BLANK_STRING;
+	} else if (tmp) {
+		channel->caller_profile->callee_id_name = tmp;
+	}
 	
 	if (channel->caller_profile->callee_id_number) {
+		tmp = channel->caller_profile->caller_id_number;
 		switch_channel_set_variable(channel, "pre_transfer_caller_id_number", channel->caller_profile->caller_id_number);
 		channel->caller_profile->caller_id_number = switch_core_strdup(channel->caller_profile->pool, channel->caller_profile->callee_id_number);
 	}
-	channel->caller_profile->callee_id_number = SWITCH_BLANK_STRING;
+
+	if (switch_channel_test_flag(channel, CF_BRIDGED)) {
+		channel->caller_profile->callee_id_number = SWITCH_BLANK_STRING;
+	} else if (tmp) {
+		channel->caller_profile->callee_id_number = tmp;
+	}
+
 	switch_mutex_unlock(channel->profile_mutex);
 
 
@@ -3260,7 +3284,7 @@ static void check_secure(switch_channel_t *channel)
 {
 	const char *var, *sec;
 
-	if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+	if (!switch_channel_media_ready(channel) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
 		if ((sec = switch_channel_get_variable(channel, "rtp_secure_media")) && switch_true(sec)) {
 			if (!(var = switch_channel_get_variable(channel, "rtp_has_crypto"))) {
 				switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "rtp_secure_media invalid in this context.\n");
diff --git a/src/switch_core_codec.c b/src/switch_core_codec.c
index 0c27fdcb46..bd3b521a98 100644
--- a/src/switch_core_codec.c
+++ b/src/switch_core_codec.c
@@ -171,8 +171,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_set_real_read_codec(switch_c
 		}
 
 		switch_channel_set_variable(channel, "read_codec", session->read_impl.iananame);
+		switch_channel_set_variable(channel, "original_read_codec", session->read_impl.iananame);
 		switch_snprintf(tmp, sizeof(tmp), "%d", session->read_impl.actual_samples_per_second);
 		switch_channel_set_variable(channel, "read_rate", tmp);
+		switch_channel_set_variable(channel, "original_read_rate", tmp);
 
 		session->raw_read_frame.codec = session->read_codec;
 		session->raw_write_frame.codec = session->read_codec;
diff --git a/src/switch_core_io.c b/src/switch_core_io.c
index aad6192ece..47f6289a73 100644
--- a/src/switch_core_io.c
+++ b/src/switch_core_io.c
@@ -638,8 +638,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
 				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec init error!\n");
 				goto done;
 			default:
-				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec %s decoder error!\n",
-								  session->read_codec->codec_interface->interface_name);
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec %s decoder error! [%d]\n",
+								  session->read_codec->codec_interface->interface_name, status);
 				goto done;
 			}
 		}
diff --git a/src/switch_core_media.c b/src/switch_core_media.c
index d8553d0766..b412a518f5 100644
--- a/src/switch_core_media.c
+++ b/src/switch_core_media.c
@@ -509,6 +509,31 @@ SWITCH_DECLARE(const char *)switch_core_media_get_codec_string(switch_core_sessi
 	return !zstr(preferred) ? preferred : fallback;
 }
 
+SWITCH_DECLARE(void) switch_core_session_clear_crypto(switch_core_session_t *session)
+{
+	int i;
+	switch_media_handle_t *smh;
+
+	const char *vars[] = { "rtp_last_audio_local_crypto_key",
+						   "srtp_remote_audio_crypto_key",
+						   "srtp_remote_audio_crypto_tag",
+						   "srtp_remote_video_crypto_key",
+						   "srtp_remote_video_crypto_tag",
+						   "rtp_secure_media",
+						   NULL};
+
+	for(i = 0; vars[i] ;i++) {
+		switch_channel_set_variable(session->channel, vars[i], NULL);
+	}
+
+	if (!(smh = session->media_handle)) {
+		return;
+	}
+
+	memset(&smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec, 0, sizeof(smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec));
+	memset(&smh->engines[SWITCH_MEDIA_TYPE_VIDEO].ssec, 0, sizeof(smh->engines[SWITCH_MEDIA_TYPE_VIDEO].ssec));
+
+}
 
 SWITCH_DECLARE(const char *) switch_core_session_local_crypto_key(switch_core_session_t *session, switch_media_type_t type)
 {
@@ -1036,15 +1061,23 @@ SWITCH_DECLARE(switch_status_t) switch_media_handle_create(switch_media_handle_t
 
 	*smhp = NULL;
 
+	if (zstr(params->sdp_username)) {
+		params->sdp_username = "FreeSWITCH";
+	}
+
+
 	if ((session->media_handle = switch_core_session_alloc(session, (sizeof(*smh))))) {
 		session->media_handle->session = session;
 		*smhp = session->media_handle;
 		switch_set_flag(session->media_handle, SMF_INIT);
 		session->media_handle->media_flags[SCMF_RUNNING] = 1;
 		session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+		session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].type = SWITCH_MEDIA_TYPE_AUDIO;
 		session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+		session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].type = SWITCH_MEDIA_TYPE_VIDEO;
 		session->media_handle->mparams = params;
 
+
 		switch_mutex_init(&session->media_handle->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
 
 		session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].ssrc = 
@@ -1098,18 +1131,18 @@ SWITCH_DECLARE(int32_t) switch_media_handle_test_media_flag(switch_media_handle_
 
 SWITCH_DECLARE(switch_status_t) switch_core_session_media_handle_ready(switch_core_session_t *session)
 {
-
 	if (session->media_handle && switch_test_flag(session->media_handle, SMF_INIT)) {
 		return SWITCH_STATUS_SUCCESS;
 	}
-	
+
 	return SWITCH_STATUS_FALSE;
 }
 
 
+
 SWITCH_DECLARE(switch_media_handle_t *) switch_core_session_get_media_handle(switch_core_session_t *session)
 {
-	if (switch_core_session_media_handle_ready(session)) {
+	if (switch_core_session_media_handle_ready(session) == SWITCH_STATUS_SUCCESS) {
 		return session->media_handle;
 	}
 
@@ -1125,6 +1158,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_clear_media_handle(switch_co
 	return SWITCH_STATUS_SUCCESS;
 }
 
+SWITCH_DECLARE(switch_core_media_params_t *) switch_core_media_get_mparams(switch_media_handle_t *smh)
+{
+	switch_assert(smh);
+	return smh->mparams;
+}
 
 SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *session, switch_bool_t force)
 {
@@ -1753,8 +1791,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_set_video_codec(switch_core_se
 			switch_core_session_set_video_write_codec(session, &v_engine->write_codec);
 
 
-			switch_channel_set_variable_printf(session->channel, "rtp_last_video_codec_string", "%s@%dh@%di", 
-											   v_engine->codec_params.iananame, v_engine->codec_params.rm_rate, v_engine->codec_params.codec_ms);
+			switch_channel_set_variable_printf(session->channel, "rtp_last_video_codec_string", "%s@%dh", 
+											   v_engine->codec_params.rm_encoding, v_engine->codec_params.rm_rate);
 
 
 			if (switch_rtp_ready(v_engine->rtp_session)) {
@@ -2018,12 +2056,26 @@ static int dtls_ok(switch_core_session_t *session)
 #pragma warning(disable:4702)
 #endif
 
+//?
+SWITCH_DECLARE(switch_call_direction_t) switch_ice_direction(switch_core_session_t *session)
+{
+	switch_call_direction_t r = switch_channel_direction(session->channel);
+
+	if ((switch_channel_test_flag(session->channel, CF_REINVITE) || switch_channel_test_flag(session->channel, CF_RECOVERING)) 
+		&& switch_channel_test_flag(session->channel, CF_WEBRTC)) {
+		r = SWITCH_CALL_DIRECTION_OUTBOUND;
+	}
+
+	return r;
+}
+
 //?
 static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_session_t *sdp, sdp_media_t *m)
 {
 	switch_rtp_engine_t *engine = &smh->engines[type];
 	sdp_attribute_t *attr;
 	int i = 0, got_rtcp_mux = 0;
+	const char *val;
 
 	if (engine->ice_in.chosen[0] && engine->ice_in.chosen[1] && !switch_channel_test_flag(smh->session->channel, CF_REINVITE)) {
 		return;
@@ -2078,6 +2130,12 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 
 		} else if (!engine->remote_ssrc && !strcasecmp(attr->a_name, "ssrc") && attr->a_value) {
 			engine->remote_ssrc = (uint32_t) atol(attr->a_value);
+
+			if (engine->rtp_session && engine->remote_ssrc) {
+				switch_rtp_set_remote_ssrc(engine->rtp_session, engine->remote_ssrc);
+			}
+	
+
 #ifdef RTCP_MUX
 		} else if (!strcasecmp(attr->a_name, "rtcp-mux")) {
 			engine->rtcp_mux = SWITCH_TRUE;
@@ -2139,7 +2197,8 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 				engine->ice_in.cands[engine->ice_in.cand_idx][cid].priority = atol(fields[3]);
 				engine->ice_in.cands[engine->ice_in.cand_idx][cid].con_addr = switch_core_session_strdup(smh->session, fields[4]);
 				engine->ice_in.cands[engine->ice_in.cand_idx][cid].con_port = (switch_port_t)atoi(fields[5]);
-						
+
+
 				j = 6;
 
 				while(j < argc && fields[j+1]) {
@@ -2155,6 +2214,7 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 					
 					j += 2;
 				} 
+				
 
 				if (engine->ice_in.chosen[cid]) {
 					engine->ice_in.cands[engine->ice_in.chosen[cid]][cid].ready++;
@@ -2206,6 +2266,19 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 		}
 	}
 
+	/* Got RTP but not RTCP, probably mux */
+	if (engine->ice_in.chosen[0] && !engine->ice_in.chosen[1] && got_rtcp_mux) {
+		engine->ice_in.chosen[1] = engine->ice_in.chosen[0];
+
+		memcpy(&engine->ice_in.cands[engine->ice_in.chosen[1]][1], &engine->ice_in.cands[engine->ice_in.chosen[0]][0], 
+			   sizeof(engine->ice_in.cands[engine->ice_in.chosen[0]][0]));
+		engine->ice_in.cands[engine->ice_in.chosen[1]][1].ready++;
+		
+		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_NOTICE,
+						  "No %s RTCP candidate found; defaulting to the same as RTP [%s:%d]\n", type2str(type),
+						  engine->ice_in.cands[engine->ice_in.chosen[1]][1].con_addr, engine->ice_in.cands[engine->ice_in.chosen[1]][1].con_port);
+	}
+
 	/* look for any candidates and hope for auto-adjust */
 	if (!engine->ice_in.chosen[0] || !engine->ice_in.chosen[1]) {
 		for (i = 0; i <= engine->ice_in.cand_idx && (!engine->ice_in.chosen[0] || !engine->ice_in.chosen[1]); i++) {
@@ -2246,7 +2319,9 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 
 		engine->codec_params.remote_sdp_ip = switch_core_session_strdup(smh->session, (char *) engine->ice_in.cands[engine->ice_in.chosen[0]][0].con_addr);
 		engine->codec_params.remote_sdp_port = (switch_port_t) engine->ice_in.cands[engine->ice_in.chosen[0]][0].con_port;
-
+		if (engine->remote_rtcp_port) {
+			engine->remote_rtcp_port = engine->codec_params.remote_sdp_port;
+		}
 																 
 		switch_snprintf(tmp, sizeof(tmp), "%d", engine->codec_params.remote_sdp_port);
 		switch_channel_set_variable(smh->session->channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, engine->codec_params.remote_sdp_ip);
@@ -2271,6 +2346,7 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 
 	
 	if (switch_channel_test_flag(smh->session->channel, CF_REINVITE)) {
+
 		if (switch_rtp_ready(engine->rtp_session) && engine->ice_in.cands[engine->ice_in.chosen[0]][0].ready) {
 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_INFO, "RE-Activating %s ICE\n", type2str(type));
 
@@ -2284,7 +2360,7 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 									ICE_GOOGLE_JINGLE,
 									NULL
 #else
-									switch_channel_direction(smh->session->channel) == 
+									switch_ice_direction(smh->session) == 
 									SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
 									&engine->ice_in
 #endif
@@ -2295,7 +2371,37 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 		}
 		
 
+		if (engine->rtp_session && ((val = switch_channel_get_variable(smh->session->channel,
+																	   type == SWITCH_MEDIA_TYPE_VIDEO ? 
+																	   "rtcp_video_interval_msec" : "rtcp_audio_interval_msec")) 
+									|| (val = type == SWITCH_MEDIA_TYPE_VIDEO ? 
+										smh->mparams->rtcp_video_interval_msec : smh->mparams->rtcp_audio_interval_msec))) {
+											   
+			const char *rport = switch_channel_get_variable(smh->session->channel, 
+															type == SWITCH_MEDIA_TYPE_VIDEO ? "rtp_remote_video_rtcp_port" : "rtp_remote_audio_rtcp_port");
+			switch_port_t remote_rtcp_port = engine->remote_rtcp_port;
 
+			if (!remote_rtcp_port && rport) {
+				remote_rtcp_port = (switch_port_t)atoi(rport);
+			}
+			
+			if (!strcasecmp(val, "passthru")) {
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_INFO, "Activating %s RTCP PASSTHRU PORT %d\n", 
+								  type2str(type), remote_rtcp_port);
+				switch_rtp_activate_rtcp(engine->rtp_session, -1, remote_rtcp_port, engine->rtcp_mux > 0);
+			} else {
+				int interval = atoi(val);
+				if (interval < 100 || interval > 500000) {
+					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_ERROR,
+									  "Invalid rtcp interval spec [%d] must be between 100 and 500000\n", interval);
+					interval = 10000;
+				}
+
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_INFO, "Activating %s RTCP PORT %d\n", type2str(type), remote_rtcp_port);
+				switch_rtp_activate_rtcp(engine->rtp_session, interval, remote_rtcp_port, engine->rtcp_mux > 0);
+			}
+		}
+			
 		if (engine->ice_in.cands[engine->ice_in.chosen[1]][1].ready) {
 			if (!strcmp(engine->ice_in.cands[engine->ice_in.chosen[1]][1].con_addr, engine->ice_in.cands[engine->ice_in.chosen[0]][0].con_addr)
 				&& engine->ice_in.cands[engine->ice_in.chosen[1]][1].con_port == engine->ice_in.cands[engine->ice_in.chosen[0]][0].con_port) {
@@ -2313,7 +2419,7 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
 										ICE_GOOGLE_JINGLE,
 										NULL
 #else
-										switch_channel_direction(smh->session->channel) == 
+										switch_ice_direction(smh->session) == 
 										SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
 										&engine->ice_in
 #endif
@@ -3350,6 +3456,116 @@ SWITCH_DECLARE(int) switch_core_media_toggle_hold(switch_core_session_t *session
 	return changed;
 }
 
+static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, void *obj)
+{
+	struct media_helper *mh = obj;
+	switch_core_session_t *session = mh->session;
+	switch_channel_t *channel = switch_core_session_get_channel(session);
+	switch_status_t status;
+	switch_frame_t *read_frame;
+	switch_media_handle_t *smh;
+
+	if (!(smh = session->media_handle)) {
+		return NULL;
+	}
+
+	switch_core_session_read_lock(session);
+
+	mh->up = 1;
+	switch_mutex_lock(mh->cond_mutex);
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread started. Echo is %s\n", 
+					  switch_channel_get_name(session->channel), switch_channel_test_flag(channel, CF_VIDEO_ECHO) ? "on" : "off");
+	switch_core_session_refresh_video(session);
+
+	while (switch_channel_up_nosig(channel)) {
+
+		if (switch_channel_test_flag(channel, CF_VIDEO_PASSIVE)) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread paused. Echo is %s\n", 
+							  switch_channel_get_name(session->channel), switch_channel_test_flag(channel, CF_VIDEO_ECHO) ? "on" : "off");
+			switch_thread_cond_wait(mh->cond, mh->cond_mutex);
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread resumed  Echo is %s\n", 
+							  switch_channel_get_name(session->channel), switch_channel_test_flag(channel, CF_VIDEO_ECHO) ? "on" : "off");
+			switch_core_session_refresh_video(session);
+		}
+
+		if (switch_channel_test_flag(channel, CF_VIDEO_PASSIVE)) {
+			continue;
+		}
+
+		if (!switch_channel_media_up(session->channel)) {
+			switch_yield(10000);
+			continue;
+		}
+
+		
+		status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+		
+		if (!SWITCH_READ_ACCEPTABLE(status)) {
+			switch_cond_next();
+			continue;
+		}
+		
+
+		if (switch_channel_test_flag(channel, CF_VIDEO_REFRESH_REQ)) {
+			switch_core_session_refresh_video(session);
+			switch_channel_clear_flag(channel, CF_VIDEO_REFRESH_REQ);
+		}
+
+		if (switch_test_flag(read_frame, SFF_CNG)) {
+			continue;
+		}
+
+		if (switch_channel_test_flag(channel, CF_VIDEO_ECHO)) {
+			switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0);
+		}
+
+	}
+
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread ended\n", switch_channel_get_name(session->channel));
+
+	switch_mutex_unlock(mh->cond_mutex);
+	switch_core_session_rwunlock(session);
+
+	mh->up = 0;
+	return NULL;
+}
+
+
+static switch_status_t start_video_thread(switch_core_session_t *session)
+{
+	switch_threadattr_t *thd_attr = NULL;
+	switch_memory_pool_t *pool = switch_core_session_get_pool(session);
+	switch_rtp_engine_t *v_engine = NULL;
+	switch_media_handle_t *smh;
+
+	if (!(smh = session->media_handle)) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+
+	if (v_engine->media_thread) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "%s Starting Video thread\n", switch_core_session_get_name(session));
+
+	switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->codec_params.agreed_pt);
+	v_engine->mh.session = session;
+	switch_threadattr_create(&thd_attr, pool);
+	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+	
+	switch_thread_cond_create(&v_engine->mh.cond, pool);
+	switch_mutex_init(&v_engine->mh.cond_mutex, SWITCH_MUTEX_NESTED, pool);
+	switch_mutex_init(&v_engine->read_mutex, SWITCH_MUTEX_NESTED, pool);
+	switch_thread_create(&v_engine->media_thread, thd_attr, video_helper_thread, &v_engine->mh, switch_core_session_get_pool(session));
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
 
 //?
 #define RA_PTR_LEN 512
@@ -3470,7 +3686,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
 					}
 				}
 
-
+				
 				if (switch_rtp_set_remote_address(v_engine->rtp_session, v_engine->codec_params.remote_sdp_ip,
 												  v_engine->codec_params.remote_sdp_port, remote_rtcp_port, SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
 					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "VIDEO RTP REPORTS ERROR: [%s]\n", err);
@@ -3482,6 +3698,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
 						!switch_channel_test_flag(session->channel, CF_WEBRTC)) {
 						/* Reactivate the NAT buster flag. */
 						switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+						start_video_thread(session);
+				
 					}
 					if (switch_media_handle_test_media_flag(smh, SCMF_AUTOFIX_TIMING) || switch_media_handle_test_media_flag(smh, SCMF_AUTOFIX_PT)) {
 						v_engine->check_frames = 0;
@@ -3735,6 +3953,24 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_choose_port(switch_core_sessio
 	return SWITCH_STATUS_SUCCESS;
 }
 
+SWITCH_DECLARE(switch_status_t) switch_core_media_choose_ports(switch_core_session_t *session, switch_bool_t audio, switch_bool_t video)
+{
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+	if (audio && (status = switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_AUDIO, 0)) == SWITCH_STATUS_SUCCESS) {
+		if (video) {
+			switch_core_media_check_video_codecs(session);
+			if (switch_channel_test_flag(session->channel, CF_VIDEO_POSSIBLE)) {
+				switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_VIDEO, 0);
+			}
+		}
+	}
+
+	return status;
+}
+
+
+
 //?
 SWITCH_DECLARE(void) switch_core_media_deactivate_rtp(switch_core_session_t *session)
 {
@@ -3882,85 +4118,28 @@ SWITCH_DECLARE(void) switch_core_session_wake_video_thread(switch_core_session_t
 	}
 }
 
-static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, void *obj)
+static void check_dtls_reinvite(switch_core_session_t *session, switch_rtp_engine_t *engine)
 {
-	struct media_helper *mh = obj;
-	switch_core_session_t *session = mh->session;
-	switch_channel_t *channel = switch_core_session_get_channel(session);
-	switch_status_t status;
-	switch_frame_t *read_frame;
-	switch_media_handle_t *smh;
+	if (switch_channel_test_flag(session->channel, CF_REINVITE)) {
 
-	if (!(smh = session->media_handle)) {
-		return NULL;
+		if (!zstr(engine->local_dtls_fingerprint.str) && switch_rtp_has_dtls() && dtls_ok(session)) {
+			dtls_type_t xtype, dtype = switch_ice_direction(session) == SWITCH_CALL_DIRECTION_INBOUND ? DTLS_TYPE_CLIENT : DTLS_TYPE_SERVER;
+			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "RE-SETTING %s DTLS\n", type2str(engine->type));
+		
+			xtype = DTLS_TYPE_RTP;
+			if (engine->rtcp_mux > 0) xtype |= DTLS_TYPE_RTCP;
+			
+			switch_rtp_add_dtls(engine->rtp_session, &engine->local_dtls_fingerprint, &engine->remote_dtls_fingerprint, dtype | xtype);
+		
+			if (engine->rtcp_mux < 1) {
+				xtype = DTLS_TYPE_RTCP;
+				switch_rtp_add_dtls(engine->rtp_session, &engine->local_dtls_fingerprint, &engine->remote_dtls_fingerprint, dtype | xtype);
+			}
+		
+		}
 	}
-
-	switch_core_session_read_lock(session);
-
-	mh->up = 1;
-	switch_mutex_lock(mh->cond_mutex);
-
-	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread started. Echo is %s\n", 
-					  switch_channel_get_name(session->channel), switch_channel_test_flag(channel, CF_VIDEO_ECHO) ? "on" : "off");
-	switch_core_session_refresh_video(session);
-
-	while (switch_channel_up_nosig(channel)) {
-
-		if (switch_channel_test_flag(channel, CF_VIDEO_PASSIVE)) {
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread paused. Echo is %s\n", 
-							  switch_channel_get_name(session->channel), switch_channel_test_flag(channel, CF_VIDEO_ECHO) ? "on" : "off");
-			switch_thread_cond_wait(mh->cond, mh->cond_mutex);
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread resumed  Echo is %s\n", 
-							  switch_channel_get_name(session->channel), switch_channel_test_flag(channel, CF_VIDEO_ECHO) ? "on" : "off");
-			switch_core_session_refresh_video(session);
-		}
-
-		if (switch_channel_test_flag(channel, CF_VIDEO_PASSIVE)) {
-			continue;
-		}
-
-		if (!switch_channel_media_up(session->channel)) {
-			switch_yield(10000);
-			continue;
-		}
-
-		
-		status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
-		
-		if (!SWITCH_READ_ACCEPTABLE(status)) {
-			switch_cond_next();
-			continue;
-		}
-		
-
-		if (switch_channel_test_flag(channel, CF_VIDEO_REFRESH_REQ)) {
-			switch_core_session_refresh_video(session);
-			switch_channel_clear_flag(channel, CF_VIDEO_REFRESH_REQ);
-		}
-
-		if (switch_test_flag(read_frame, SFF_CNG)) {
-			continue;
-		}
-
-		if (switch_channel_test_flag(channel, CF_VIDEO_ECHO)) {
-			switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0);
-		}
-
-	}
-
-
-	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread ended\n", switch_channel_get_name(session->channel));
-
-	switch_mutex_unlock(mh->cond_mutex);
-	switch_core_session_rwunlock(session);
-
-	mh->up = 0;
-	return NULL;
 }
 
-
-
-
 //?
 SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_session_t *session)
 
@@ -4151,6 +4330,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 				switch_rtp_set_flag(a_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
 			}
 		}
+
+		if (session && a_engine) {
+			check_dtls_reinvite(session, a_engine);
+		}
+
 		goto video;
 	}
 
@@ -4278,7 +4462,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 									ICE_GOOGLE_JINGLE,
 									NULL
 #else
-									switch_channel_direction(session->channel) == 
+									switch_ice_direction(session) == 
 									SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
 									&a_engine->ice_in
 #endif
@@ -4331,7 +4515,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 											ICE_GOOGLE_JINGLE,
 											NULL
 #else
-											switch_channel_direction(session->channel) == 
+											switch_ice_direction(session) == 
 											SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
 											&a_engine->ice_in
 #endif
@@ -4373,6 +4557,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 				}
 			}
 
+			if (jb_msec < 0 && jb_msec > -10) {
+				jb_msec = (a_engine->read_codec.implementation->microseconds_per_packet / 1000) * abs(jb_msec);
+			}
+
 			if (jb_msec < 20 || jb_msec > 10000) {
 				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR,
 								  "Invalid Jitterbuffer spec [%d] must be between 20 and 10000\n", jb_msec);
@@ -4535,7 +4723,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 									  v_engine->codec_params.remote_sdp_port, v_engine->codec_params.agreed_pt, 
 									  a_engine->read_impl.microseconds_per_packet / 1000);
 
-				
+					start_video_thread(session);
 					switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->codec_params.agreed_pt);
 				}
 			}
@@ -4549,7 +4737,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 				const char *rport = NULL;
 				switch_port_t remote_rtcp_port = v_engine->remote_rtcp_port;
 
-				switch_channel_clear_flag(session->channel, CF_REINVITE);
+				//switch_channel_clear_flag(session->channel, CF_REINVITE);
 
 				if (!remote_rtcp_port) {
 					if ((rport = switch_channel_get_variable(session->channel, "rtp_remote_video_rtcp_port"))) {
@@ -4568,6 +4756,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 						!((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val))) {
 						/* Reactivate the NAT buster flag. */
 						switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+						start_video_thread(session);
 					}
 
 				}
@@ -4655,18 +4844,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 
 
 			if (switch_rtp_ready(v_engine->rtp_session)) {
-				switch_threadattr_t *thd_attr = NULL;
-				switch_memory_pool_t *pool = switch_core_session_get_pool(session);
-
-				switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->codec_params.agreed_pt);
-				v_engine->mh.session = session;
-				switch_threadattr_create(&thd_attr, pool);
-				switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
-
-				switch_thread_cond_create(&v_engine->mh.cond, pool);
-				switch_mutex_init(&v_engine->mh.cond_mutex, SWITCH_MUTEX_NESTED, pool);
-				switch_mutex_init(&v_engine->read_mutex, SWITCH_MUTEX_NESTED, pool);
-				switch_thread_create(&v_engine->media_thread, thd_attr, video_helper_thread, &v_engine->mh, switch_core_session_get_pool(session));
+				start_video_thread(session);
 			}
 
 			if (switch_rtp_ready(v_engine->rtp_session)) {
@@ -4700,7 +4878,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 											ICE_GOOGLE_JINGLE,
 											NULL
 #else
-											switch_channel_direction(session->channel) == 
+											switch_ice_direction(session) == 
 											SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
 											&v_engine->ice_in
 #endif
@@ -4750,7 +4928,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 													ICE_GOOGLE_JINGLE,
 													NULL
 #else
-													switch_channel_direction(session->channel) == 
+													switch_ice_direction(session) == 
 													SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
 													
 													&v_engine->ice_in
@@ -4819,6 +4997,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 
  video_up:
 
+	if (session && v_engine) {
+		check_dtls_reinvite(session, v_engine);
+	}
+
 	status = SWITCH_STATUS_SUCCESS;
 
  end:
@@ -5275,7 +5457,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 		switch_channel_clear_flag(smh->session->channel, CF_DTLS);
 	}
 
-	if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+	if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND || switch_channel_test_flag(session->channel, CF_RECOVERING)) {
 		if (!switch_channel_test_flag(session->channel, CF_WEBRTC) && 
 			switch_true(switch_channel_get_variable(session->channel, "media_webrtc"))) {
 			switch_channel_set_flag(session->channel, CF_WEBRTC);
@@ -5460,6 +5642,10 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 
 		rate = a_engine->codec_params.rm_rate;
 
+		if (!strcasecmp(a_engine->codec_params.rm_encoding, "opus")) {
+			a_engine->codec_params.adv_channels = 2;
+		}
+
 		if (a_engine->codec_params.adv_channels > 1) {
 			switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d/%d\n", 
 							a_engine->codec_params.agreed_pt, a_engine->codec_params.rm_encoding, rate, a_engine->codec_params.adv_channels);
@@ -5577,7 +5763,8 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 			}
 
 
-			if (a_engine->rtcp_mux < 1 || switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+			if (a_engine->rtcp_mux < 1 || switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND || 
+				switch_channel_test_flag(session->channel, CF_RECOVERING)) {
 
 				switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=candidate:%s 2 %s %u %s %d typ host generation 0\n", 
 								tmp1, ice_out->cands[0][0].transport, c2,
@@ -5765,7 +5952,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 			
 			if (v_engine->codec_params.rm_encoding) {
 				const char *of;
-				
+
 				if (!strcasecmp(v_engine->codec_params.rm_encoding, "VP8")) {
 					vp8 = v_engine->codec_params.pt;
 				}
@@ -5866,7 +6053,8 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 				
 			}
 
-			if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND && switch_channel_test_flag(smh->session->channel, CF_DTLS)) {
+			if ((switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND || switch_channel_test_flag(session->channel, CF_RECOVERING))
+				&& switch_channel_test_flag(smh->session->channel, CF_DTLS)) {
 				generate_local_fingerprint(smh, SWITCH_MEDIA_TYPE_VIDEO);
 			}
 
@@ -5949,7 +6137,8 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 				}
 
 
-				if (v_engine->rtcp_mux < 1 || switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+				if (v_engine->rtcp_mux < 1 || switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND || 
+					switch_channel_test_flag(session->channel, CF_RECOVERING)) {
 
 					switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=candidate:%s 2 %s %u %s %d typ host generation 0\n", 
 									tmp1, ice_out->cands[0][0].transport, c2,
@@ -7607,8 +7796,6 @@ SWITCH_DECLARE (void) switch_core_media_recover_session(switch_core_session_t *s
 	ip = switch_channel_get_variable(session->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE);
 	port = switch_channel_get_variable(session->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE);
 
-	switch_channel_set_flag(session->channel, CF_RECOVERING);
-
 	if (switch_channel_test_flag(session->channel, CF_PROXY_MODE)  || !(ip && port)) {
 		return;
 	} else {
@@ -7728,7 +7915,7 @@ SWITCH_DECLARE (void) switch_core_media_recover_session(switch_core_session_t *s
 	switch_core_session_get_recovery_crypto_key(session, SWITCH_MEDIA_TYPE_VIDEO);
 
 
-	if ((tmp = switch_channel_get_variable(session->channel, "rtp_last_audio_local_crypto_key"))) {
+	if ((tmp = switch_channel_get_variable(session->channel, "rtp_last_audio_local_crypto_key")) && a_engine->ssec.remote_crypto_key) {
 		int idx = atoi(tmp);
 
 		a_engine->ssec.local_crypto_key = switch_core_session_strdup(session, tmp);
diff --git a/src/switch_core_session.c b/src/switch_core_session.c
index 430b20d013..52cb871aa9 100644
--- a/src/switch_core_session.c
+++ b/src/switch_core_session.c
@@ -445,17 +445,26 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_event_send(const char *uuid_
 }
 
 
-SWITCH_DECLARE(void *) switch_core_session_get_private(switch_core_session_t *session)
+SWITCH_DECLARE(void *) switch_core_session_get_private_class(switch_core_session_t *session, switch_pvt_class_t index)
 {
+	if (index >= SWITCH_CORE_SESSION_MAX_PRIVATES) {
+		return NULL;
+	}
+
 	switch_assert(session != NULL);
-	return session->private_info;
+	return session->private_info[index];
 }
 
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_set_private(switch_core_session_t *session, void *private_info)
+SWITCH_DECLARE(switch_status_t) switch_core_session_set_private_class(switch_core_session_t *session, void *private_info, switch_pvt_class_t index)
 {
 	switch_assert(session != NULL);
-	session->private_info = private_info;
+
+	if (index >= SWITCH_CORE_SESSION_MAX_PRIVATES) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	session->private_info[index] = private_info;
 	return SWITCH_STATUS_SUCCESS;
 }
 
diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c
index a9c8d22fbc..7dd02df818 100644
--- a/src/switch_core_sqldb.c
+++ b/src/switch_core_sqldb.c
@@ -2717,14 +2717,35 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
 
 	if (ep->recover_callback) {
 		switch_caller_extension_t *extension = NULL;
+		switch_channel_t *channel = switch_core_session_get_channel(session);
+		int r = 0;
 
+		if ((r = ep->recover_callback(session)) > 0) {
+			const char *cbname;
 
-		if (ep->recover_callback(session) > 0) {
-			switch_channel_t *channel = switch_core_session_get_channel(session);
+			switch_channel_set_flag(session->channel, CF_RECOVERING);
+			
 
 			if (switch_channel_get_partner_uuid(channel)) {
 				switch_channel_set_flag(channel, CF_RECOVERING_BRIDGE);
-			} else {
+			}
+
+			switch_core_media_recover_session(session);
+
+			if ((cbname = switch_channel_get_variable(channel, "secondary_recovery_module"))) {
+				switch_core_recover_callback_t recover_callback;
+				
+				if ((recover_callback = switch_core_get_secondary_recover_callback(cbname))) {
+					r = recover_callback(session);
+				}
+			}
+			
+			
+		}
+		
+		if (r > 0) {
+
+			if (!switch_channel_test_flag(channel, CF_RECOVERING_BRIDGE)) {
 				switch_xml_t callflow, param, x_extension;
 				if ((extension = switch_caller_extension_new(session, "recovery", "recovery")) == 0) {
 					abort();
diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c
index 5926e52586..6de6b9798e 100644
--- a/src/switch_core_state_machine.c
+++ b/src/switch_core_state_machine.c
@@ -38,6 +38,16 @@
 static void switch_core_standard_on_init(switch_core_session_t *session)
 {
 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Standard INIT\n", switch_channel_get_name(session->channel));
+
+	if (switch_channel_test_flag(session->channel, CF_RECOVERING_BRIDGE)) {
+		switch_channel_set_state(session->channel, CS_RESET);
+	} else {
+		if (switch_channel_test_flag(session->channel, CF_RECOVERING)) {
+			switch_channel_set_state(session->channel, CS_EXECUTE);
+		} else {
+			switch_channel_set_state(session->channel, CS_ROUTING);
+		}
+	}
 }
 
 static void switch_core_standard_on_hangup(switch_core_session_t *session)
@@ -104,6 +114,43 @@ static void switch_core_standard_on_reset(switch_core_session_t *session)
 	switch_channel_set_variable(session->channel, "call_uuid", switch_core_session_get_uuid(session));
 
 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Standard RESET\n", switch_channel_get_name(session->channel));
+
+	if (switch_channel_test_flag(session->channel, CF_RECOVERING_BRIDGE)) {
+		switch_core_session_t *other_session = NULL;
+		const char *uuid = switch_core_session_get_uuid(session);
+
+		if (switch_channel_test_flag(session->channel, CF_BRIDGE_ORIGINATOR)) {
+			const char *other_uuid = switch_channel_get_partner_uuid(session->channel);
+			int x = 0;
+
+			if (other_uuid) {
+				for (x = 0; other_session == NULL && x < 20; x++) {
+					if (!switch_channel_up(session->channel)) {
+						break;
+					}
+					other_session = switch_core_session_locate(other_uuid);
+					switch_yield(100000);
+				}
+			}
+
+			if (other_session) {
+				switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
+				switch_channel_clear_flag(session->channel, CF_BRIDGE_ORIGINATOR);
+				switch_channel_wait_for_state_timeout(other_channel, CS_RESET, 5000);
+				switch_channel_wait_for_flag(other_channel, CF_MEDIA_ACK, SWITCH_TRUE, 2000, NULL);
+
+				if (switch_channel_test_flag(session->channel, CF_PROXY_MODE) && switch_channel_test_flag(other_channel, CF_PROXY_MODE)) {
+					switch_ivr_signal_bridge(session, other_session);
+				} else {
+					switch_ivr_uuid_bridge(uuid, other_uuid);
+				}
+				switch_core_session_rwunlock(other_session);
+			}
+		}
+
+		switch_channel_clear_flag(session->channel, CF_RECOVERING_BRIDGE);
+	}
+
 }
 
 static void switch_core_standard_on_routing(switch_core_session_t *session)
@@ -208,6 +255,8 @@ static void switch_core_standard_on_execute(switch_core_session_t *session)
 
 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Standard EXECUTE\n", switch_channel_get_name(session->channel));
 
+	switch_channel_clear_flag(session->channel, CF_RECOVERING);
+
 	switch_channel_set_variable(session->channel, "call_uuid", switch_core_session_get_uuid(session));
 
 	if (switch_channel_get_variable(session->channel, "recovered") && !switch_channel_test_flag(session->channel, CF_RECOVERED)) {
diff --git a/src/switch_event.c b/src/switch_event.c
index 080a331dcd..926b3d85f6 100644
--- a/src/switch_event.c
+++ b/src/switch_event.c
@@ -67,6 +67,15 @@ struct switch_event_subclass {
 	int bind;
 };
 
+
+static struct {
+	switch_event_channel_id_t ID;
+	switch_thread_rwlock_t *rwlock;
+	switch_hash_t *hash;
+	switch_hash_t *lahash;
+	switch_mutex_t *lamutex;
+} event_channel_manager;
+
 #define MAX_DISPATCH_VAL 64
 static unsigned int MAX_DISPATCH = MAX_DISPATCH_VAL;
 static unsigned int SOFT_MAX_DISPATCH = 0;
@@ -81,10 +90,12 @@ static switch_memory_pool_t *THRUNTIME_POOL = NULL;
 static switch_thread_t *EVENT_DISPATCH_QUEUE_THREADS[MAX_DISPATCH_VAL] = { 0 };
 static uint8_t EVENT_DISPATCH_QUEUE_RUNNING[MAX_DISPATCH_VAL] = { 0 };
 static switch_queue_t *EVENT_DISPATCH_QUEUE = NULL;
+static switch_queue_t *EVENT_CHANNEL_DISPATCH_QUEUE = NULL;
 static switch_mutex_t *EVENT_QUEUE_MUTEX = NULL;
 static switch_hash_t *CUSTOM_HASH = NULL;
 static int THREAD_COUNT = 0;
 static int DISPATCH_THREAD_COUNT = 0;
+static int EVENT_CHANNEL_DISPATCH_THREAD_COUNT = 0;
 static int SYSTEM_RUNNING = 0;
 static uint64_t EVENT_SEQUENCE_NR = 0;
 #ifdef SWITCH_EVENT_RECYCLE
@@ -92,6 +103,8 @@ static switch_queue_t *EVENT_RECYCLE_QUEUE = NULL;
 static switch_queue_t *EVENT_HEADER_RECYCLE_QUEUE = NULL;
 #endif
 
+static void unsub_all_switch_event_channel(void);
+
 static char *my_dup(const char *s)
 {
 	size_t len = strlen(s) + 1;
@@ -518,6 +531,13 @@ SWITCH_DECLARE(switch_status_t) switch_event_shutdown(void)
 	SYSTEM_RUNNING = 0;
 	switch_mutex_unlock(EVENT_QUEUE_MUTEX);
 
+	unsub_all_switch_event_channel();
+
+	if (EVENT_CHANNEL_DISPATCH_QUEUE) {
+		switch_queue_trypush(EVENT_CHANNEL_DISPATCH_QUEUE, NULL);
+		switch_queue_interrupt_all(EVENT_CHANNEL_DISPATCH_QUEUE);
+	}
+
 	if (runtime.events_use_dispatch) {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Stopping dispatch queues\n");
 
@@ -565,6 +585,8 @@ SWITCH_DECLARE(switch_status_t) switch_event_shutdown(void)
 		}
 	}
 
+	switch_core_hash_destroy(&event_channel_manager.lahash);
+
 	switch_core_hash_destroy(&CUSTOM_HASH);
 	switch_core_memory_reclaim_events();
 
@@ -633,14 +655,6 @@ SWITCH_DECLARE(void) switch_event_launch_dispatch_threads(uint32_t max)
 
 SWITCH_DECLARE(switch_status_t) switch_event_init(switch_memory_pool_t *pool)
 {
-	//switch_threadattr_t *thd_attr;
-
-	/*
-	   This statement doesn't do anything commenting it out for now.
-
-	   switch_assert(switch_arraylen(EVENT_NAMES)  == SWITCH_EVENT_ALL + 1);
-	 */
-
 	/* don't need any more dispatch threads than we have CPU's*/
 	MAX_DISPATCH = (switch_core_cpu_count() / 2) + 1;
 	if (MAX_DISPATCH < 2) {
@@ -656,6 +670,13 @@ SWITCH_DECLARE(switch_status_t) switch_event_init(switch_memory_pool_t *pool)
 	switch_mutex_init(&EVENT_QUEUE_MUTEX, SWITCH_MUTEX_NESTED, RUNTIME_POOL);
 	switch_core_hash_init(&CUSTOM_HASH, RUNTIME_POOL);
 
+	switch_core_hash_init(&event_channel_manager.lahash, RUNTIME_POOL);
+	switch_mutex_init(&event_channel_manager.lamutex, SWITCH_MUTEX_NESTED, RUNTIME_POOL);
+
+	switch_thread_rwlock_create(&event_channel_manager.rwlock, RUNTIME_POOL);
+	switch_core_hash_init(&event_channel_manager.hash, RUNTIME_POOL);
+	event_channel_manager.ID = 1;
+
 	switch_mutex_lock(EVENT_QUEUE_MUTEX);
 	SYSTEM_RUNNING = -1;
 	switch_mutex_unlock(EVENT_QUEUE_MUTEX);
@@ -1733,13 +1754,11 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_json(switch_event_t **event,
 	return SWITCH_STATUS_SUCCESS;
 }
 
-SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *event, char **str)
+SWITCH_DECLARE(switch_status_t) switch_event_serialize_json_obj(switch_event_t *event, cJSON **json)
 {
 	switch_event_header_t *hp;
 	cJSON *cj;
 
-	*str = NULL;
-
 	cj = cJSON_CreateObject();
 
 	for (hp = event->headers; hp; hp = hp->next) {
@@ -1768,12 +1787,27 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *even
 		cJSON_AddItemToObject(cj, "_body", cJSON_CreateString(event->body));
 	}
 
-	*str = cJSON_Print(cj);
-	cJSON_Delete(cj);
+	*json = cj;
 
 	return SWITCH_STATUS_SUCCESS;
 }
 
+SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *event, char **str)
+{
+
+	cJSON *cj;
+	*str = NULL;
+
+	if (switch_event_serialize_json_obj(event, &cj) == SWITCH_STATUS_SUCCESS) {
+		*str = cJSON_PrintUnformatted(cj);
+		cJSON_Delete(cj);
+		
+		return SWITCH_STATUS_SUCCESS;
+	}
+
+	return SWITCH_STATUS_FALSE;
+}
+
 static switch_xml_t add_xml_header(switch_xml_t xml, char *name, char *value, int offset)
 {
 	switch_xml_t header = switch_xml_add_child_d(xml, name, offset);
@@ -2528,6 +2562,36 @@ SWITCH_DECLARE(int) switch_event_check_permission_list(switch_event_t *list, con
 	return r;
 }
 
+SWITCH_DECLARE(void) switch_json_add_presence_data_cols(switch_event_t *event, cJSON *json, const char *prefix)
+{
+	const char *data;
+
+	if (!prefix) prefix = "";
+
+	if ((data = switch_event_get_header(event, "presence_data_cols"))) {
+		char *cols[128] = { 0 };
+		char header_name[128] = "";
+		int col_count = 0, i = 0;
+		char *data_copy = NULL;
+
+		data_copy = strdup(data);
+
+		col_count = switch_split(data_copy, ':', cols);
+
+		for (i = 0; i < col_count; i++) {
+			const char *val = NULL;
+			switch_snprintf(header_name, sizeof(header_name), "%s%s", prefix, cols[i]);
+
+			val = switch_event_get_header(event, cols[i]);
+			json_add_child_string(json, header_name, val);
+		}
+
+		switch_safe_free(data_copy);
+	}
+
+}
+
+
 SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *channel, switch_event_t *event, const char *prefix)
 {
 	const char *data;
@@ -2557,6 +2621,792 @@ SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *chann
 
 }
 
+struct switch_event_channel_sub_node_head_s;
+
+typedef struct switch_event_channel_sub_node_s {
+	switch_event_channel_func_t func;
+	switch_event_channel_id_t id;
+	struct switch_event_channel_sub_node_head_s *head;
+	struct switch_event_channel_sub_node_s *next;
+} switch_event_channel_sub_node_t;
+
+typedef struct switch_event_channel_sub_node_head_s {
+	switch_event_channel_sub_node_t *node;
+	switch_event_channel_sub_node_t *tail;
+	char *event_channel;
+} switch_event_channel_sub_node_head_t;
+
+static uint32_t switch_event_channel_unsub_head(switch_event_channel_func_t func, switch_event_channel_sub_node_head_t *head)
+{
+	uint32_t x = 0;
+
+	switch_event_channel_sub_node_t *thisnp = NULL, *np, *last = NULL;
+
+	np = head->tail = head->node;
+
+	while (np) {
+
+		thisnp = np;
+		np = np->next;
+
+		if (!func || thisnp->func == func) {
+			x++;
+			
+			if (last) {
+				last->next = np;
+			} else {
+				head->node = np;
+			}
+
+
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "UNSUBBING %p [%s]\n", (void *)(intptr_t)thisnp->func, thisnp->head->event_channel);
+
+
+			thisnp->func = NULL;
+			free(thisnp);
+		} else {
+			last = thisnp;
+			head->tail = last;
+		}
+	}
+
+	return x;
+}
+
+static void unsub_all_switch_event_channel(void)
+{
+	switch_hash_index_t *hi;
+	void *val;
+	switch_event_channel_sub_node_head_t *head;
+
+	switch_thread_rwlock_wrlock(event_channel_manager.rwlock);
+ top:
+	head = NULL;
+
+	for (hi = switch_hash_first(NULL, event_channel_manager.hash); hi; hi = switch_hash_next(hi)) {
+		switch_hash_this(hi, NULL, NULL, &val);
+		head = (switch_event_channel_sub_node_head_t *) val;
+		switch_event_channel_unsub_head(NULL, head);
+		switch_core_hash_delete(event_channel_manager.hash, head->event_channel);
+		free(head->event_channel);
+		free(head);
+		goto top;
+	}
+
+	switch_thread_rwlock_unlock(event_channel_manager.rwlock);
+}
+
+static uint32_t switch_event_channel_unsub_channel(switch_event_channel_func_t func, const char *event_channel)
+{
+	switch_event_channel_sub_node_head_t *head;
+	uint32_t x = 0;
+
+	switch_thread_rwlock_wrlock(event_channel_manager.rwlock);
+
+	if (!event_channel) {
+		switch_hash_index_t *hi;
+		void *val;
+
+		for (hi = switch_hash_first(NULL, event_channel_manager.hash); hi; hi = switch_hash_next(hi)) {
+			switch_hash_this(hi, NULL, NULL, &val);
+
+			if (val) {
+				head = (switch_event_channel_sub_node_head_t *) val;
+				x += switch_event_channel_unsub_head(func, head);
+			}
+		}
+
+	} else {
+		if ((head = switch_core_hash_find(event_channel_manager.hash, event_channel))) {
+			x += switch_event_channel_unsub_head(func, head);
+		}
+	}
+
+	switch_thread_rwlock_unlock(event_channel_manager.rwlock);
+
+	return x;
+}
+
+static switch_status_t switch_event_channel_sub_channel(const char *event_channel, switch_event_channel_func_t func, switch_event_channel_id_t id)
+														
+{
+	switch_event_channel_sub_node_t *node, *np;
+	switch_event_channel_sub_node_head_t *head;
+	switch_status_t status = SWITCH_STATUS_FALSE;
+
+	switch_thread_rwlock_wrlock(event_channel_manager.rwlock);
+
+	if (!(head = switch_core_hash_find(event_channel_manager.hash, event_channel))) {
+		switch_zmalloc(head, sizeof(*head));
+		head->event_channel = strdup(event_channel);
+		switch_core_hash_insert(event_channel_manager.hash, event_channel, head);
+
+		switch_zmalloc(node, sizeof(*node));
+		node->func = func;
+		node->id = id;
+
+		node->head = head;
+		head->node = node;
+		head->tail = node;
+		status = SWITCH_STATUS_SUCCESS;
+	} else {
+		int exist = 0;
+
+		for (np = head->node; np; np = np->next) {
+			if (np->func == func) {
+				exist = 1;
+				break;
+			}
+		}
+
+		if (!exist) {
+			switch_zmalloc(node, sizeof(*node));
+
+			node->func = func;
+			node->id = id;
+			node->head = head;
+
+
+			if (!head->node) {
+				head->node = node;
+				head->tail = node;
+			} else {
+				head->tail->next = node;
+				head->tail = head->tail->next;
+			}
+			status = SWITCH_STATUS_SUCCESS;
+		}
+	}
+
+	switch_thread_rwlock_unlock(event_channel_manager.rwlock);
+
+	return status;
+}
+
+typedef struct {
+	char *event_channel;
+	cJSON *json;
+	char *key;
+	switch_event_channel_id_t id;
+} event_channel_data_t;
+
+
+
+static uint32_t _switch_event_channel_broadcast(const char *event_channel, const char *broadcast_channel, 
+												cJSON *json, const char *key, switch_event_channel_id_t id)
+{
+	switch_event_channel_sub_node_t *np;
+	switch_event_channel_sub_node_head_t *head;
+	uint32_t x = 0;
+
+	switch_thread_rwlock_rdlock(event_channel_manager.rwlock);
+	if ((head = switch_core_hash_find(event_channel_manager.hash, event_channel))) {
+		for (np = head->node; np; np = np->next) {
+			if (np->id == id) {
+				continue;
+			}
+			np->func(broadcast_channel, json, key, id);
+			x++;
+		}
+	}
+	switch_thread_rwlock_unlock(event_channel_manager.rwlock);
+
+	return x;
+}
+
+static void destroy_ecd(event_channel_data_t **ecdP)
+{
+	event_channel_data_t *ecd = *ecdP;
+	*ecdP = NULL;
+
+	switch_safe_free(ecd->event_channel);
+	switch_safe_free(ecd->key);
+	if (ecd->json) {
+		cJSON_Delete(ecd->json);
+		ecd->json = NULL;
+	}
+
+	free(ecd);
+}
+
+static void ecd_deliver(event_channel_data_t **ecdP)
+{
+	event_channel_data_t *ecd = *ecdP;
+	char *p;
+
+	*ecdP = NULL;
+
+	_switch_event_channel_broadcast(ecd->event_channel, ecd->event_channel, ecd->json, ecd->key, ecd->id);
+	
+	if ((p = strchr(ecd->event_channel, '.'))) {
+		char *main_channel = strdup(ecd->event_channel);
+		p = strchr(main_channel, '.');
+		*p = '\0';
+		_switch_event_channel_broadcast(main_channel, ecd->event_channel, ecd->json, ecd->key, ecd->id);
+		free(main_channel);
+	}
+	_switch_event_channel_broadcast(SWITCH_EVENT_CHANNEL_GLOBAL, ecd->event_channel, ecd->json, ecd->key, ecd->id);
+
+	destroy_ecd(&ecd);
+}
+
+static void *SWITCH_THREAD_FUNC switch_event_channel_deliver_thread(switch_thread_t *thread, void *obj)
+{
+	switch_queue_t *queue = (switch_queue_t *) obj;
+	void *pop = NULL;
+	event_channel_data_t *ecd = NULL;
+
+	switch_mutex_lock(EVENT_QUEUE_MUTEX);
+	THREAD_COUNT++;
+	EVENT_CHANNEL_DISPATCH_THREAD_COUNT++;
+	switch_mutex_unlock(EVENT_QUEUE_MUTEX);
+
+	while(SYSTEM_RUNNING) {
+		
+		if (switch_queue_pop(queue, &pop) != SWITCH_STATUS_SUCCESS) {
+			continue;
+		}
+
+		if (!pop) {
+			break;
+		}
+
+		ecd = (event_channel_data_t *) pop;
+		ecd_deliver(&ecd);
+		switch_os_yield();
+	}
+
+	while (switch_queue_trypop(queue, &pop) == SWITCH_STATUS_SUCCESS) {
+		ecd = (event_channel_data_t *) pop;
+		destroy_ecd(&ecd);
+	}
+
+	switch_mutex_lock(EVENT_QUEUE_MUTEX);
+	THREAD_COUNT--;
+	EVENT_CHANNEL_DISPATCH_THREAD_COUNT--;
+	switch_mutex_unlock(EVENT_QUEUE_MUTEX);
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Event Channel Dispatch Thread Ended.\n");
+	return NULL;	
+}
+
+SWITCH_DECLARE(switch_status_t) switch_event_channel_broadcast(const char *event_channel, cJSON **json, const char *key, switch_event_channel_id_t id)
+{
+	event_channel_data_t *ecd = NULL;
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+	if (!SYSTEM_RUNNING) {
+		cJSON_Delete(*json);
+		*json = NULL;
+		return SWITCH_STATUS_FALSE;
+	}
+
+	switch_zmalloc(ecd, sizeof(*ecd));
+
+	ecd->event_channel = strdup(event_channel);
+	ecd->json = *json;
+	ecd->key = strdup(key);
+	ecd->id = id;
+
+	*json = NULL;
+
+	if (!EVENT_CHANNEL_DISPATCH_THREAD_COUNT && SYSTEM_RUNNING) {
+		switch_thread_data_t *td;
+	
+		if (!EVENT_CHANNEL_DISPATCH_QUEUE) {
+			switch_queue_create(&EVENT_CHANNEL_DISPATCH_QUEUE, DISPATCH_QUEUE_LEN * MAX_DISPATCH, THRUNTIME_POOL);
+		}
+
+		td = malloc(sizeof(*td));
+		switch_assert(td);
+		
+		td->alloc = 1;
+		td->func = switch_event_channel_deliver_thread;
+		td->obj = EVENT_CHANNEL_DISPATCH_QUEUE;
+		td->pool = NULL;
+		
+		switch_thread_pool_launch_thread(&td);
+	}
+
+	if ((status = switch_queue_trypush(EVENT_CHANNEL_DISPATCH_QUEUE, ecd) != SWITCH_STATUS_SUCCESS)) {
+		cJSON_Delete(ecd->json);
+		ecd->json = NULL;
+		destroy_ecd(&ecd);
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Event Channel Queue failure for channel %s\n", event_channel);
+	} else {
+		ecd = NULL;
+	}
+	
+	return status;
+}
+
+SWITCH_DECLARE(uint32_t) switch_event_channel_unbind(const char *event_channel, switch_event_channel_func_t func)
+{
+	return switch_event_channel_unsub_channel(func, event_channel);
+}
+
+SWITCH_DECLARE(switch_status_t) switch_event_channel_bind(const char *event_channel, switch_event_channel_func_t func, switch_event_channel_id_t *id)
+														  
+{
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+	switch_assert(id);
+
+	if (!*id) {
+		switch_thread_rwlock_wrlock(event_channel_manager.rwlock);
+		*id = event_channel_manager.ID++;
+		switch_thread_rwlock_unlock(event_channel_manager.rwlock);
+	}
+
+	status = switch_event_channel_sub_channel(event_channel, func, *id);
+
+	return status;
+}
+
+typedef struct la_node_s {
+	char *name;
+	cJSON *obj;
+	struct la_node_s *next;
+	int pos;
+} la_node_t;
+
+struct switch_live_array_s {
+	char *event_channel;
+	char *name;
+	char *key;
+	la_node_t *head;
+	la_node_t *tail;
+	switch_memory_pool_t *pool;
+	switch_hash_t *hash;
+	switch_mutex_t *mutex;
+	uint32_t serno;
+	int pos;
+	switch_bool_t visible;
+	switch_bool_t new;
+	switch_event_channel_id_t channel_id;
+	switch_live_array_command_handler_t command_handler;
+	void *user_data;
+};
+
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_visible(switch_live_array_t *la, switch_bool_t visible, switch_bool_t force)
+{
+	switch_status_t status = SWITCH_STATUS_FALSE;
+
+	switch_mutex_lock(la->mutex);
+	if (la->visible != visible || force) {
+		cJSON *msg, *data;
+		
+		msg = cJSON_CreateObject();
+		data = json_add_child_obj(msg, "data", NULL);
+
+		cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+		cJSON_AddItemToObject(data, "action", cJSON_CreateString(visible ? "hide" : "show"));
+		cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(la->serno++));
+		
+		switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, la->channel_id);
+
+		la->visible = visible;
+	}
+	switch_mutex_unlock(la->mutex);
+
+	return status;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_clear(switch_live_array_t *la)
+{
+	la_node_t *cur, *np;
+	cJSON *msg, *data;
+
+	switch_mutex_lock(la->mutex);
+	np = la->head;
+
+	msg = cJSON_CreateObject();
+	data = json_add_child_obj(msg, "data", NULL);
+
+	cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+	cJSON_AddItemToObject(data, "action", cJSON_CreateString("clear"));
+	cJSON_AddItemToObject(data, "name", cJSON_CreateString(la->name));
+	cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(-1));
+	cJSON_AddItemToObject(data, "data", cJSON_CreateObject());
+	
+	switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, la->channel_id);
+
+	while(np) {
+		cur = np;
+		cJSON_Delete(cur->obj);
+		free(cur->name);
+		free(cur);
+		np = np->next;
+	}
+
+	la->head = la->tail = NULL;
+	
+	switch_mutex_unlock(la->mutex);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_bootstrap(switch_live_array_t *la, const char *sessid, switch_event_channel_id_t channel_id)
+{
+	la_node_t *np;
+	cJSON *msg, *data;
+
+	switch_mutex_lock(la->mutex);
+
+#if OLD_WAY
+	msg = cJSON_CreateObject();
+	data = json_add_child_obj(msg, "data", NULL);
+
+	cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+	cJSON_AddItemToObject(data, "action", cJSON_CreateString("clear"));
+	cJSON_AddItemToObject(data, "name", cJSON_CreateString(la->name));
+	cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(-1));
+	cJSON_AddItemToObject(data, "data", cJSON_CreateObject());
+	
+	switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, channel_id);
+
+	for (np = la->head; np; np = np->next) {
+		msg = cJSON_CreateObject();
+		data = json_add_child_obj(msg, "data", NULL);
+
+		cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+		cJSON_AddItemToObject(data, "action", cJSON_CreateString("add"));
+		cJSON_AddItemToObject(data, "name", cJSON_CreateString(la->name));
+		cJSON_AddItemToObject(data, "hashKey", cJSON_CreateString(np->name));
+		cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(la->serno++));
+		cJSON_AddItemToObject(data, "data", cJSON_Duplicate(np->obj, 1));
+		if (sessid) {
+			cJSON_AddItemToObject(msg, "sessid", cJSON_CreateString(sessid));
+		}
+		switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, channel_id);
+	}
+#else
+
+
+	msg = cJSON_CreateObject();
+	data = json_add_child_obj(msg, "data", NULL);
+	
+	cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+	cJSON_AddItemToObject(data, "action", cJSON_CreateString("bootObj"));
+	cJSON_AddItemToObject(data, "name", cJSON_CreateString(la->name));
+	cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(la->serno++));
+
+	if (sessid) {
+		cJSON_AddItemToObject(msg, "sessid", cJSON_CreateString(sessid));
+	}
+
+	data = json_add_child_array(data, "data");
+
+	for (np = la->head; np; np = np->next) {
+		cJSON *row = cJSON_CreateArray();
+		cJSON_AddItemToArray(row, cJSON_CreateString(np->name));
+		cJSON_AddItemToArray(row, cJSON_Duplicate(np->obj, 1));
+		cJSON_AddItemToArray(data, row);
+	}
+	
+	switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, channel_id);
+
+
+#endif
+
+	if (!la->visible) {
+		switch_live_array_visible(la, SWITCH_FALSE, SWITCH_TRUE);
+	}
+
+	switch_mutex_unlock(la->mutex);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_destroy(switch_live_array_t **live_arrayP)
+{
+	switch_live_array_t *la = *live_arrayP;
+	switch_memory_pool_t *pool;
+
+	*live_arrayP = NULL;
+
+	pool = la->pool;
+
+	switch_live_array_clear(la);
+	
+	switch_core_hash_destroy(&la->hash);
+
+	switch_mutex_lock(event_channel_manager.lamutex);
+	switch_core_hash_delete(event_channel_manager.lahash, la->key);
+	switch_mutex_unlock(event_channel_manager.lamutex);
+
+	switch_core_destroy_memory_pool(&pool);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(switch_bool_t) switch_live_array_isnew(switch_live_array_t *la)
+{
+	return la->new;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_create(const char *event_channel, const char *name, 
+														 switch_event_channel_id_t channel_id, switch_live_array_t **live_arrayP)
+{
+	switch_live_array_t *la = NULL;
+	switch_memory_pool_t *pool;
+	char *key = NULL;
+
+	switch_core_new_memory_pool(&pool);
+	key = switch_core_sprintf(pool, "%s.%s", event_channel, name);
+
+	switch_mutex_lock(event_channel_manager.lamutex); 
+	la = switch_core_hash_find(event_channel_manager.lahash, key);
+	switch_mutex_unlock(event_channel_manager.lamutex);
+	
+	if (la) {
+		la->new = SWITCH_FALSE;
+	} else {
+		la = switch_core_alloc(pool, sizeof(*la));
+		la->pool = pool;
+		la->serno = 1;
+		la->visible = SWITCH_TRUE;
+		la->event_channel = switch_core_strdup(la->pool, event_channel);
+		la->name = switch_core_strdup(la->pool, name);
+		la->key = key;
+		la->new = SWITCH_TRUE;
+		la->channel_id = channel_id;
+		switch_core_hash_init(&la->hash, la->pool);
+		switch_mutex_init(&la->mutex, SWITCH_MUTEX_NESTED, la->pool);
+		
+		switch_mutex_lock(event_channel_manager.lamutex);
+		switch_core_hash_insert(event_channel_manager.lahash, la->key, la);
+		switch_mutex_unlock(event_channel_manager.lamutex);
+	}
+
+	*live_arrayP = la;
+	
+	return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(cJSON *) switch_live_array_get(switch_live_array_t *la, const char *name)
+{
+	la_node_t *node;
+	cJSON *dup = NULL;
+
+	switch_mutex_lock(la->mutex);
+	if ((node = switch_core_hash_find(la->hash, name))) {
+		dup = cJSON_Duplicate(node->obj, 1); 
+	}
+	switch_mutex_unlock(la->mutex);
+
+	return dup;
+}
+
+SWITCH_DECLARE(cJSON *) switch_live_array_get_idx(switch_live_array_t *la, int idx)
+{
+	la_node_t *node;
+	cJSON *dup = NULL;
+
+	switch_mutex_lock(la->mutex);
+	for (node = la->head; node; node = node->next) {
+		if (node->pos == idx) {
+			dup = cJSON_Duplicate(node->obj, 1); 
+			break;
+		}
+	}
+	switch_mutex_unlock(la->mutex);
+	
+	return dup;
+}
+
+SWITCH_DECLARE(void) switch_live_array_lock(switch_live_array_t *la)
+{
+	switch_mutex_lock(la->mutex);
+}
+
+SWITCH_DECLARE(void) switch_live_array_unlock(switch_live_array_t *la)
+{
+	switch_mutex_unlock(la->mutex);
+}
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_del(switch_live_array_t *la, const char *name)
+{
+	switch_status_t status = SWITCH_STATUS_FALSE;
+	la_node_t *node, *cur, *np, *last = NULL;
+	cJSON *msg, *data = NULL;
+
+	switch_mutex_lock(la->mutex);
+	if ((node = switch_core_hash_find(la->hash, name))) {
+		np = la->head;
+		
+		while(np) {
+			cur = np;
+			np = np->next;
+
+			if (cur == node) {
+				if (last) {
+					last->next = cur->next;
+				} else {
+					la->head = cur->next;
+				}
+				switch_core_hash_delete(la->hash, name);
+
+				msg = cJSON_CreateObject();
+				data = json_add_child_obj(msg, "data", NULL);
+
+				cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+				cJSON_AddItemToObject(data, "name", cJSON_CreateString(la->name));
+				cJSON_AddItemToObject(data, "action", cJSON_CreateString("del"));
+				cJSON_AddItemToObject(data, "hashKey", cJSON_CreateString(cur->name));
+				cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(la->serno++));
+				cJSON_AddItemToObject(data, "data", cur->obj);
+				cur->obj = NULL;
+
+				switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, la->channel_id);
+				free(cur->name);
+				free(cur);
+			} else {
+				cur->pos = la->pos++;
+				la->tail = cur;
+				last = cur;
+			}
+		}
+	}
+	switch_mutex_unlock(la->mutex);
+
+	return status;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_live_array_add(switch_live_array_t *la, const char *name, int index, cJSON **obj, switch_bool_t duplicate)
+{
+	la_node_t *node;
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+	const char *action = "add";
+	cJSON *msg = NULL, *data = NULL;
+
+	switch_mutex_lock(la->mutex);
+
+	if ((node = switch_core_hash_find(la->hash, name))) {
+
+		action = "modify";
+
+		if (node->obj) {
+			if (duplicate) {
+				cJSON_Delete(node->obj);
+				node->obj = NULL;
+			}
+		}
+	} else {
+		switch_zmalloc(node, sizeof(*node));
+
+		node->name = strdup(name);
+		switch_core_hash_insert(la->hash, name, node);
+
+		if (index > -1 && index < la->pos && la->head) {
+			la_node_t *np, *last = NULL;
+			int i = 0;
+			
+			for(np = la->head; np; np = np->next) {
+				
+				if (i == index) {
+					if (last) {
+						node->next = last->next;
+						last->next = node;
+						np = node;
+					} else {
+						node->next = la->head;
+						la->head = node;
+						np = node;
+					}
+				}
+
+				np->pos = i;
+				la->tail = np;
+				last = np;
+				i++;
+			}
+			
+
+		} else {
+
+			node->pos = la->pos++;
+			index = node->pos;
+
+			if (!la->head) {
+				la->head = node;
+			} else {
+				la->tail->next = node;
+			}
+
+			la->tail = node;
+		}
+	}
+
+	if (duplicate) {
+		node->obj = cJSON_Duplicate(*obj, 1);
+	} else {
+		node->obj = *obj;
+	}
+
+	msg = cJSON_CreateObject();
+	data = json_add_child_obj(msg, "data", NULL);
+	
+	cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(la->event_channel));
+	cJSON_AddItemToObject(data, "action", cJSON_CreateString(action));
+
+	if (index > -1) {
+		cJSON_AddItemToObject(data, "arrIndex", cJSON_CreateNumber(index));
+	}
+
+	cJSON_AddItemToObject(data, "name", cJSON_CreateString(la->name));
+	cJSON_AddItemToObject(data, "hashKey", cJSON_CreateString(node->name));
+	cJSON_AddItemToObject(data, "wireSerno", cJSON_CreateNumber(la->serno++));
+	cJSON_AddItemToObject(data, "data", cJSON_Duplicate(node->obj, 1));
+
+	switch_event_channel_broadcast(la->event_channel, &msg, __FILE__, la->channel_id);
+	
+	switch_mutex_unlock(la->mutex);
+
+	return status;
+}
+
+SWITCH_DECLARE(void) switch_live_array_set_user_data(switch_live_array_t *la, void *user_data)
+{
+	switch_assert(la);
+	la->user_data = user_data;
+}
+
+SWITCH_DECLARE(void) switch_live_array_set_command_handler(switch_live_array_t *la, switch_live_array_command_handler_t command_handler)
+{
+	switch_assert(la);
+	la->command_handler = command_handler;
+}
+
+
+SWITCH_DECLARE(void) switch_live_array_parse_json(cJSON *json, switch_event_channel_id_t channel_id)
+{
+	const char *context = NULL, *name = NULL;
+	switch_live_array_t *la = NULL;
+	cJSON *jla = NULL;
+	
+	if ((jla = cJSON_GetObjectItem(json, "data")) && (jla = cJSON_GetObjectItem(jla, "liveArray"))) {
+
+		if ((context = cJSON_GetObjectCstr(jla, "context")) && (name = cJSON_GetObjectCstr(jla, "name"))) {
+			const char *command = cJSON_GetObjectCstr(jla, "command");
+			const char *sessid = cJSON_GetObjectCstr(json, "sessid");
+			
+			if (command) {
+				switch_live_array_create(context, name, channel_id, &la);
+				
+				if (!strcasecmp(command, "bootstrap")) {
+					switch_live_array_bootstrap(la, sessid, channel_id);
+				} else {
+					if (la->command_handler) {
+						la->command_handler(la, command, sessid, jla, la->user_data);
+					}
+				}
+			}
+		}
+	}
+
+}
 
 /* For Emacs:
  * Local Variables:
diff --git a/src/switch_json.c b/src/switch_json.c
index 3819cda2d2..4467692dba 100644
--- a/src/switch_json.c
+++ b/src/switch_json.c
@@ -487,7 +487,20 @@ static char *print_object(cJSON *item,int depth,int fmt)
 /* Get Array size/item / object item. */
 SWITCH_DECLARE(int)   cJSON_GetArraySize(cJSON *array)							{cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
 SWITCH_DECLARE(cJSON *)cJSON_GetArrayItem(cJSON *array,int item)				{cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
-SWITCH_DECLARE(cJSON *)cJSON_GetObjectItem(cJSON *object,const char *string)	{cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+SWITCH_DECLARE(cJSON *)cJSON_GetObjectItem(const cJSON *object,const char *string)	{cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+
+
+SWITCH_DECLARE(const char *)cJSON_GetObjectCstr(const cJSON *object, const char *string)	
+{
+	cJSON *cj = cJSON_GetObjectItem(object, string);
+
+	if (!cj || cj->type != cJSON_String || !cj->valuestring) return NULL;
+
+	return cj->valuestring;
+}
+
+
+
 
 /* Utility for array list handling. */
 static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
@@ -554,3 +567,27 @@ SWITCH_DECLARE(cJSON *) cJSON_Duplicate(cJSON *item,int recurse)
 	}
 	return newitem;
 }
+
+
+SWITCH_DECLARE(cJSON *) cJSON_CreateStringPrintf(const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+	cJSON *item;
+
+	va_start(ap, fmt);
+	str = switch_vmprintf(fmt, ap);
+	va_end(ap);
+
+	if (!str) return NULL;
+
+	if ((item = cJSON_New_Item())) {
+		item->type=cJSON_String;
+		item->valuestring = str;
+	} else {
+		free(str);
+	}
+
+	return item;
+}
+
diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c
index 8ffe90ea11..c9fdda1da7 100644
--- a/src/switch_loadable_module.c
+++ b/src/switch_loadable_module.c
@@ -65,6 +65,7 @@ struct switch_loadable_module_container {
 	switch_hash_t *application_hash;
 	switch_hash_t *chat_application_hash;
 	switch_hash_t *api_hash;
+	switch_hash_t *json_api_hash;
 	switch_hash_t *file_hash;
 	switch_hash_t *speech_hash;
 	switch_hash_t *asr_hash;
@@ -73,6 +74,7 @@ struct switch_loadable_module_container {
 	switch_hash_t *say_hash;
 	switch_hash_t *management_hash;
 	switch_hash_t *limit_hash;
+	switch_hash_t *secondary_recover_hash;
 	switch_mutex_t *mutex;
 	switch_memory_pool_t *pool;
 };
@@ -318,6 +320,29 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
 		}
 	}
 
+	if (new_module->module_interface->json_api_interface) {
+		const switch_json_api_interface_t *ptr;
+
+		for (ptr = new_module->module_interface->json_api_interface; ptr; ptr = ptr->next) {
+			if (!ptr->interface_name) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load JSON api interface from %s due to no interface name.\n", key);
+			} else {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding JSON API Function '%s'\n", ptr->interface_name);
+				if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "json_api");
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc));
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
+					switch_event_fire(&event);
+					added++;
+				}
+				switch_core_hash_insert(loadable_modules.json_api_hash, ptr->interface_name, (const void *) ptr);
+			}
+		}
+	}
+
 	if (new_module->module_interface->file_interface) {
 		const switch_file_interface_t *ptr;
 
@@ -1047,6 +1072,36 @@ static switch_status_t switch_loadable_module_unprocess(switch_loadable_module_t
 		}
 	}
 
+	if (old_module->module_interface->json_api_interface) {
+		const switch_json_api_interface_t *ptr;
+
+		for (ptr = old_module->module_interface->json_api_interface; ptr; ptr = ptr->next) {
+			if (ptr->interface_name) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting API Function '%s'\n", ptr->interface_name);
+
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
+								  ptr->interface_name);
+
+				if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
+					switch_thread_rwlock_unlock(ptr->rwlock);
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
+				}
+
+
+				if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api");
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc));
+					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
+					switch_event_fire(&event);
+					removed++;
+				}
+				switch_core_hash_delete(loadable_modules.json_api_hash, ptr->interface_name);
+			}
+		}
+	}
+
 	if (old_module->module_interface->file_interface) {
 		const switch_file_interface_t *ptr;
 
@@ -1721,6 +1776,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autolo
 	switch_core_hash_init_nocase(&loadable_modules.application_hash, loadable_modules.pool);
 	switch_core_hash_init_nocase(&loadable_modules.chat_application_hash, loadable_modules.pool);
 	switch_core_hash_init_nocase(&loadable_modules.api_hash, loadable_modules.pool);
+	switch_core_hash_init_nocase(&loadable_modules.json_api_hash, loadable_modules.pool);
 	switch_core_hash_init(&loadable_modules.file_hash, loadable_modules.pool);
 	switch_core_hash_init_nocase(&loadable_modules.speech_hash, loadable_modules.pool);
 	switch_core_hash_init_nocase(&loadable_modules.asr_hash, loadable_modules.pool);
@@ -1730,6 +1786,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autolo
 	switch_core_hash_init_nocase(&loadable_modules.management_hash, loadable_modules.pool);
 	switch_core_hash_init_nocase(&loadable_modules.limit_hash, loadable_modules.pool);
 	switch_core_hash_init_nocase(&loadable_modules.dialplan_hash, loadable_modules.pool);
+	switch_core_hash_init(&loadable_modules.secondary_recover_hash, loadable_modules.pool);
 	switch_mutex_init(&loadable_modules.mutex, SWITCH_MUTEX_NESTED, loadable_modules.pool);
 
 	if (!autoload) return SWITCH_STATUS_SUCCESS;
@@ -1945,6 +2002,7 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
 	switch_core_hash_destroy(&loadable_modules.application_hash);
 	switch_core_hash_destroy(&loadable_modules.chat_application_hash);
 	switch_core_hash_destroy(&loadable_modules.api_hash);
+	switch_core_hash_destroy(&loadable_modules.json_api_hash);
 	switch_core_hash_destroy(&loadable_modules.file_hash);
 	switch_core_hash_destroy(&loadable_modules.speech_hash);
 	switch_core_hash_destroy(&loadable_modules.asr_hash);
@@ -2014,6 +2072,7 @@ HASH_FUNC(timer)
 HASH_FUNC(application)
 HASH_FUNC(chat_application)
 HASH_FUNC(api)
+HASH_FUNC(json_api)
 HASH_FUNC(file)
 HASH_FUNC(speech)
 HASH_FUNC(asr)
@@ -2369,6 +2428,46 @@ SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *
 	return status;
 }
 
+SWITCH_DECLARE(switch_status_t) switch_json_api_execute(cJSON *json, switch_core_session_t *session, cJSON **retval)
+{
+	switch_json_api_interface_t *json_api;
+	switch_status_t status;
+	cJSON *function, *json_reply = NULL;
+
+	switch_assert(json);
+
+	function = cJSON_GetObjectItem(json, "command");
+
+	if (function && function->valuestring 
+		&& cJSON_GetObjectItem(json, "data") && (json_api = switch_loadable_module_get_json_api_interface(function->valuestring)) != 0) {
+		if ((status = json_api->function(json, session, &json_reply)) != SWITCH_STATUS_SUCCESS) {
+			cJSON_AddItemToObject(json, "status", cJSON_CreateString("error"));
+			cJSON_AddItemToObject(json, "message", cJSON_CreateString("The command returned an error"));
+		} else {
+			cJSON_AddItemToObject(json, "status", cJSON_CreateString("success"));
+		}
+		
+		if (!json_reply) {
+			json_reply = cJSON_CreateNull();
+		}
+
+		if (retval) {
+			*retval = json_reply;
+		} else {
+			cJSON_AddItemToObject(json, "response", json_reply);
+		}
+		
+		UNPROTECT_INTERFACE(json_api);
+	} else {
+		status = SWITCH_STATUS_FALSE;
+		cJSON_AddItemToObject(json, "status", cJSON_CreateString("error"));
+		cJSON_AddItemToObject(json, "message", cJSON_CreateString("Invalid request or non-existant command"));
+		cJSON_AddItemToObject(json, "response", cJSON_CreateNull());
+	}
+
+	return status;
+}
+
 
 SWITCH_DECLARE(switch_loadable_module_interface_t *) switch_loadable_module_create_module_interface(switch_memory_pool_t *pool, const char *name)
 {
@@ -2425,6 +2524,9 @@ SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_m
 	case SWITCH_API_INTERFACE:
 		ALLOC_INTERFACE(api)
 
+	case SWITCH_JSON_API_INTERFACE:
+		ALLOC_INTERFACE(json_api)
+
 	case SWITCH_FILE_INTERFACE:
 		ALLOC_INTERFACE(file)
 
@@ -2549,7 +2651,42 @@ SWITCH_DECLARE(void) switch_say_file(switch_say_file_handle_t *sh, const char *f
 	va_end(ap);
 }
 
+SWITCH_DECLARE(switch_core_recover_callback_t) switch_core_get_secondary_recover_callback(const char *key)
+{
+	switch_core_recover_callback_t cb;
 
+	switch_mutex_lock(loadable_modules.mutex);
+	cb = (switch_core_recover_callback_t) (intptr_t) switch_core_hash_find(loadable_modules.secondary_recover_hash, key);
+	switch_mutex_unlock(loadable_modules.mutex);
+
+	return cb;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_register_secondary_recover_callback(const char *key, switch_core_recover_callback_t cb)
+{
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+	switch_assert(cb);
+
+	switch_mutex_lock(loadable_modules.mutex);
+	if (switch_core_hash_find(loadable_modules.secondary_recover_hash, key)) {
+		status = SWITCH_STATUS_FALSE;
+	} else {
+		switch_core_hash_insert(loadable_modules.secondary_recover_hash, key, (void *)(intptr_t) cb);
+	}
+	switch_mutex_unlock(loadable_modules.mutex);
+
+	return status;
+}
+
+
+SWITCH_DECLARE(void) switch_core_unregister_secondary_recover_callback(const char *key)
+{
+	switch_mutex_lock(loadable_modules.mutex);
+	switch_core_hash_delete(loadable_modules.secondary_recover_hash, key);
+	switch_mutex_unlock(loadable_modules.mutex);
+}
 
 
 /* For Emacs:
diff --git a/src/switch_rtp.c b/src/switch_rtp.c
index 47590b1430..c3027b8ba3 100644
--- a/src/switch_rtp.c
+++ b/src/switch_rtp.c
@@ -61,7 +61,7 @@
 #define WRITE_INC(rtp_session)  switch_mutex_lock(rtp_session->write_mutex); rtp_session->writing++
 #define WRITE_DEC(rtp_session) switch_mutex_unlock(rtp_session->write_mutex); rtp_session->writing--
 
-#define RTP_STUN_FREQ 2000000
+#define RTP_STUN_FREQ 1000000
 #define rtp_header_len 12
 #define RTP_START_PORT 16384
 #define RTP_END_PORT 32768
@@ -713,7 +713,7 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice)
 		elapsed = (unsigned int) ((switch_micro_time_now() - rtp_session->last_stun) / 1000);
 
 		if (elapsed > 30000) {
-			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "No stun for a long time!\n");
+			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "No %s stun for a long time!\n", rtp_type(rtp_session));
 			rtp_session->last_stun = switch_micro_time_now();
 			//status = SWITCH_STATUS_GENERR;
 			//goto end;
@@ -912,7 +912,7 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d
 			char *host = NULL;
 
 			ice->missed_count++;
-			//switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "missed %d\n", ice->missed_count);
+			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "missed %d\n", ice->missed_count);
 
 			if (elapsed > 20000 && pri) {
 				int i, j;
@@ -991,6 +991,8 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d
 	}
 
 	if (ice->missed_count > 5) {
+		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "missed too many: %d, looking for new ICE dest.\n", 
+						  ice->missed_count);
 		ice->rready = 0;
 		ok = 1;
 	}
@@ -1039,6 +1041,7 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d
 				char buf[80] = "";
 				char buf2[80] = "";
 				const char *err = "";
+				int i = 0;
 
 				ice->missed_count = 0;
 				ice->rready = 1;
@@ -1049,6 +1052,21 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d
 				host2 = switch_get_addr(buf2, sizeof(buf2), ice->addr);
 				port2 = switch_sockaddr_get_port(ice->addr);
 				
+				for (i = 0; i <= ice->ice_params->cand_idx; i++) {
+					if (ice->ice_params->cands[i][ice->proto].con_port == port) {
+						if (!strcmp(ice->ice_params->cands[i][ice->proto].con_addr, host) && 
+							!strcmp(ice->ice_params->cands[i][ice->proto].cand_type, "relay")) {
+
+							switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING,
+											  "Skiping RELAY stun/%s/dtls port change from %s:%u to %s:%u\n", is_rtcp ? "rtcp" : "rtp", 
+											  host2, port2,
+											  host, port);
+
+							goto end;
+						}
+					}
+				}
+
 				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_NOTICE,
 								  "Auto Changing stun/%s/dtls port from %s:%u to %s:%u\n", is_rtcp ? "rtcp" : "rtp", 
 								  host2, port2,
@@ -1056,7 +1074,8 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d
 				
 				ice->ice_params->cands[ice->ice_params->chosen[ice->proto]][ice->proto].con_addr = switch_core_strdup(rtp_session->pool, host);
 				ice->ice_params->cands[ice->ice_params->chosen[ice->proto]][ice->proto].con_port = port;
-				
+				ice->missed_count = 0;
+
 				switch_sockaddr_info_get(&ice->addr, host, SWITCH_UNSPEC, port, 0, rtp_session->pool);
 
 				if (!is_rtcp || rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) {
@@ -1369,7 +1388,7 @@ static void send_fir(switch_rtp_t *rtp_session)
 		fir->ssrc = htonl(rtp_session->remote_ssrc);
 		fir->seq = ++rtp_session->fir_seq;
 		fir->r1 = fir->r2 = fir->r3 = 0;
-		
+
 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Sending RTCP FIR %d\n", rtp_session->fir_seq);
 		
 		rtcp_bytes = sizeof(switch_rtcp_ext_hdr_t) + sizeof(rtcp_fir_t);
@@ -2377,6 +2396,26 @@ static int dtls_state_handshake(switch_rtp_t *rtp_session, switch_dtls_t *dtls)
 	return 0;
 }
 
+static void free_dtls(switch_dtls_t **dtlsp)
+{
+	switch_dtls_t *dtls;
+
+	if (!dtlsp) {
+		return;
+	}
+
+	dtls = *dtlsp;
+	*dtlsp = NULL;
+
+	if (dtls->ssl) {
+		SSL_free(dtls->ssl);
+	}
+
+	if (dtls->ssl_ctx) {
+		SSL_CTX_free(dtls->ssl_ctx);
+	}
+}
+
 static int do_dtls(switch_rtp_t *rtp_session, switch_dtls_t *dtls)
 {
 	int r = 0, ret = 0, len;
@@ -2384,16 +2423,17 @@ static int do_dtls(switch_rtp_t *rtp_session, switch_dtls_t *dtls)
 	switch_size_t bytes;
 
 	if (dtls->bytes) {
+
+		//if (dtls->state == DS_READY) {
+		//	
+		//}
+
 		if ((ret = BIO_write(dtls->read_bio, dtls->data, dtls->bytes)) != (int)dtls->bytes) {
 			ret = SSL_get_error(dtls->ssl, ret);
 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "%s DTLS packet read err %d\n", rtp_type(rtp_session), ret);
 			dtls_set_state(dtls, DS_FAIL);
 			return -1;
 		}
-
-		if (dtls->state == DS_READY) {
-			dtls_set_state(dtls, DS_HANDSHAKE);
-		}
 	}
 
 	if (SSL_read(dtls->ssl, dtls->data, dtls->bytes) == (int)dtls->bytes) {
@@ -2453,6 +2493,30 @@ SWITCH_DECLARE(int) switch_rtp_has_dtls(void) {
 #endif
 }
 
+SWITCH_DECLARE(switch_status_t) switch_rtp_del_dtls(switch_rtp_t *rtp_session, dtls_type_t type)
+{
+
+	if (!rtp_session->dtls && !rtp_session->rtcp_dtls) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	if ((type & DTLS_TYPE_RTP)) {
+		if (rtp_session->dtls && rtp_session->dtls == rtp_session->rtcp_dtls) {
+			rtp_session->rtcp_dtls = NULL;
+		}
+		
+		if (rtp_session->dtls) {
+			free_dtls(&rtp_session->dtls);
+		}
+	}
+
+	if ((type & DTLS_TYPE_RTCP) && rtp_session->rtcp_dtls) {
+		free_dtls(&rtp_session->rtcp_dtls);
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
 SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, dtls_fingerprint_t *local_fp, dtls_fingerprint_t *remote_fp, dtls_type_t type)
 {
 	switch_dtls_t *dtls;
@@ -2471,6 +2535,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, d
 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "INVALID TYPE!\n");
 	}
 
+	switch_rtp_del_dtls(rtp_session, type);
+	
 	if ((type & DTLS_TYPE_RTP) && (type & DTLS_TYPE_RTCP)) {
 		kind = "RTP/RTCP";
 	} else if ((type & DTLS_TYPE_RTP)) {
@@ -2487,8 +2553,6 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, d
 		return SWITCH_STATUS_FALSE;
 	}
 
-
-
 	dtls = switch_core_alloc(rtp_session->pool, sizeof(*dtls));
 
 		
@@ -2579,6 +2643,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, d
 		SSL_set_connect_state(dtls->ssl);
 	}
 
+	rtp_session->flags[SWITCH_RTP_FLAG_VIDEO_BREAK] = 1;
+	switch_rtp_break(rtp_session);
 		
 	return SWITCH_STATUS_SUCCESS;
 	
@@ -3328,6 +3394,12 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_ice(switch_rtp_t *rtp_sessio
 
 	rtp_session->rtp_bugs |= RTP_BUG_ACCEPT_ANY_PACKETS;
 
+
+	if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) {
+		rtp_session->flags[SWITCH_RTP_FLAG_VIDEO_BREAK] = 1;
+		switch_rtp_break(rtp_session);
+	}
+
 	return SWITCH_STATUS_SUCCESS;
 }
 
@@ -3360,7 +3432,10 @@ SWITCH_DECLARE(void) switch_rtp_break(switch_rtp_t *rtp_session)
 	if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) {
 		int ret = 1;
 
-		if (rtp_session->session) {
+		if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO_BREAK]) {
+			rtp_session->flags[SWITCH_RTP_FLAG_VIDEO_BREAK] = 0;
+			ret = 0;
+		} else if (rtp_session->session) {
 			switch_channel_t *channel = switch_core_session_get_channel(rtp_session->session);
 			if (switch_channel_test_flag(channel, CF_VIDEO_BREAK)) {
 				switch_channel_clear_flag(channel, CF_VIDEO_BREAK);
@@ -3369,6 +3444,8 @@ SWITCH_DECLARE(void) switch_rtp_break(switch_rtp_t *rtp_session)
 		}
 
 		if (ret) return;
+
+		switch_rtp_video_refresh(rtp_session);
 	}
 
 	switch_mutex_lock(rtp_session->flag_mutex);
@@ -3429,27 +3506,6 @@ SWITCH_DECLARE(uint8_t) switch_rtp_ready(switch_rtp_t *rtp_session)
 	return ret;
 }
 
-static void free_dtls(switch_dtls_t **dtlsp)
-{
-	switch_dtls_t *dtls;
-
-	if (!dtlsp) {
-		return;
-	}
-
-	dtls = *dtlsp;
-	*dtlsp = NULL;
-
-	if (dtls->ssl) {
-		SSL_free(dtls->ssl);
-	}
-
-	if (dtls->ssl_ctx) {
-		SSL_CTX_free(dtls->ssl_ctx);
-	}
-}
-
-
 SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session)
 {
 	void *pop;
@@ -4635,7 +4691,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 				pt = 100000;
 			}
 
-
+			
 			poll_status = switch_poll(rtp_session->read_pollfd, 1, &fdr, pt);
 
 
@@ -4651,10 +4707,12 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 
 		}
 		
+			
 		if (poll_status == SWITCH_STATUS_SUCCESS) {
 			if (read_pretriggered) {
 				read_pretriggered = 0;
 			} else {
+
 				status = read_rtp_packet(rtp_session, &bytes, flags, SWITCH_TRUE);
 
 				if (status == SWITCH_STATUS_GENERR) {