]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add video support to lib/mod.dingaling this needs testing, google voice won't work...
authorAnthony Minessale <anthm@freeswitch.org>
Fri, 22 Jun 2012 23:14:53 +0000 (18:14 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Fri, 22 Jun 2012 23:15:06 +0000 (18:15 -0500)
conf/vanilla/autoload_configs/dingaling.conf.xml
conf/vanilla/jingle_profiles/client.xml
libs/libdingaling/src/libdingaling.c
libs/libdingaling/src/libdingaling.h
src/mod/endpoints/mod_dingaling/Makefile
src/mod/endpoints/mod_dingaling/mod_dingaling.c

index e68c8b4b44547e990d868ce7c67e616fdeb3c854..dd6c9a5da1aae1557e7bacf9b3430217a70e5522 100644 (file)
@@ -1,7 +1,7 @@
 <configuration name="dingaling.conf" description="XMPP Jingle Endpoint">
   <settings>
     <param name="debug" value="0"/>
-    <param name="codec-prefs" value="PCMU"/>
+    <param name="codec-prefs" value="H264,PCMU"/>
   </settings>
 
   <X-PRE-PROCESS cmd="include" data="../jingle_profiles/*.xml"/>
index cac70692e848cfd4b03d8ae582e9cf31efe07bae..20a8dd809d1778ad3641d076147ec211839f6ecc 100644 (file)
@@ -28,5 +28,9 @@
     <!--<param name="avatar" value="/path/to/tiny.jpg"/>-->
     <!--<param name="candidate-acl" value="wan.auto"/>-->
     <param name="local-network-acl" value="localnet.auto"/>
+
+    <!-- google voice does not work on this yet ....ikr... -->
+    <!--<param name="use-jingle" value="true"/>-->
+
   </x-profile>
 </include>
index 0b11411ffd6eb48b1c7ce2c0d60e70bee393319d..e2b32a0637e6fcb9cc144e0b61bb3077657566ca 100644 (file)
  *
  * libdingaling.c -- Main Library Code
  *
+ * QMOD: XMPP Video Signaling + Presentation (video-v1 & camera-v1)
+ *
  */
 
+
 #ifndef  _MSC_VER
 #include <config.h>
 #include <unistd.h>
@@ -157,8 +160,10 @@ struct ldl_session {
        char *login;
        ldl_payload_t payloads[LDL_MAX_PAYLOADS];
        unsigned int payload_len;
-       ldl_candidate_t candidates[LDL_MAX_CANDIDATES];
-       unsigned int candidate_len;
+       /*! \brief Transport candidates, organized per type */
+       ldl_candidate_t candidates[LDL_TPORT_MAX][LDL_MAX_CANDIDATES];
+       /*! \brief Length of the candidate list, per transport type */
+       unsigned int candidate_len[LDL_TPORT_MAX];
        apr_pool_t *pool;
        apr_hash_t *variables;
        apr_time_t created;
@@ -181,6 +186,8 @@ typedef struct ldl_feature ldl_feature_t;
 #define FEATURE_VERSION "jabber:iq:version"
 #define FEATURE_VCARD "vcard-temp"
 #define FEATURE_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
+#define FEATURE_VIDEO "http://www.google.com/xmpp/protocol/video/v1"
+#define FEATURE_CAMERA "http://www.google.com/xmpp/protocol/camera/v1"
 #define FEATURE_LAST "jabber:iq:last"
 
 static ldl_feature_t FEATURES[] = { 
@@ -189,6 +196,8 @@ static ldl_feature_t FEATURES[] = {
        { FEATURE_VERSION, on_disco_default },
        { FEATURE_VCARD, on_vcard},
        { FEATURE_VOICE, on_disco_default },
+       { FEATURE_VIDEO, on_disco_default },
+       { FEATURE_CAMERA, on_disco_default },
        { FEATURE_LAST, on_disco_default },
        { NULL, NULL}
 };
@@ -253,7 +262,6 @@ static unsigned int next_id(void)
        return globals.id++;
 }
 
-
 static char *iks_name_nons(iks *x)
 {
        char *r = iks_name(x);
@@ -296,7 +304,7 @@ ldl_status ldl_session_destroy(ldl_session_t **session_p)
                apr_hash_t *hash = session->handle->sessions;
 
                if (globals.debug) {
-                       globals.logger(DL_LOG_DEBUG, "Destroyed Session %s\n", session->id);
+                       globals.logger(DL_LOG_CRIT, "Destroyed Session %s\n", session->id);
                }
 
                if (session->id) {
@@ -322,7 +330,7 @@ ldl_status ldl_session_create(ldl_session_t **session_p, ldl_handle_t *handle, c
        ldl_session_t *session = NULL;
        
        if (!(session = apr_palloc(handle->pool, sizeof(ldl_session_t)))) {
-               globals.logger(DL_LOG_DEBUG, "Memory ERROR!\n");
+               globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
                *session_p = NULL;
                return LDL_STATUS_MEMERR;
        }
@@ -352,7 +360,7 @@ ldl_status ldl_session_create(ldl_session_t **session_p, ldl_handle_t *handle, c
 
 
        if (globals.debug) {
-               globals.logger(DL_LOG_DEBUG, "Created Session %s\n", id);
+               globals.logger(DL_LOG_CRIT, "Created Session %s\n", id);
        }
 
        return LDL_STATUS_SUCCESS;
@@ -374,21 +382,22 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
 
        if (!session) {
                if (globals.debug) {
-                       globals.logger(DL_LOG_DEBUG, "Non-Existent Session %s!\n", id);
+                       globals.logger(DL_LOG_CRIT, "Non-Existent Session %s!\n", id);
                }
                return LDL_STATUS_MEMERR;
        }
        
        if (globals.debug) {
-               globals.logger(DL_LOG_DEBUG, "Message for Session %s\n", id);
+               globals.logger(DL_LOG_CRIT, "Message for Session %s\n", id);
        }
 
        while(xml) {
                char *type = NULL;
                iks *tag;
 
-               if (iks_type(xml)!=IKS_CDATA)
+               if (iks_type(xml) != IKS_CDATA) {
                        type = xtype ? xtype : iks_find_attrib(xml, "type");
+               }
 
                if (type) {
                        
@@ -429,18 +438,28 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
                                                                char *name = iks_find_attrib(itag, "name");
                                                                char *id = iks_find_attrib(itag, "id");
                                                                char *rate = iks_find_attrib(itag, "clockrate");
+                                                               char *ptime = iks_find_attrib(itag, "ptime");
                                                                if (name && id) {
                                                                        session->payloads[session->payload_len].name = apr_pstrdup(session->pool, name);
                                                                        session->payloads[session->payload_len].id = atoi(id);
+                                                                       if (ptime) {
+                                                                               session->payloads[session->payload_len].ptime = atoi(ptime);
+                                                                       } else {
+                                                                               session->payloads[session->payload_len].ptime = 20;
+                                                                       }
                                                                        if (rate) {
                                                                                session->payloads[session->payload_len].rate = atoi(rate);
                                                                        } else {
-                                        session->payloads[session->payload_len].rate = 8000;
+                                                                               if (!strncasecmp(iks_name(itag), "vid", 3)) {
+                                                                                       session->payloads[session->payload_len].rate = 90000;
+                                                                               } else {
+                                                                                       session->payloads[session->payload_len].rate = 8000;
+                                                                               }
                                     }
                                                                        session->payload_len++;
                                                                
                                                                        if (globals.debug) {
-                                                                               globals.logger(DL_LOG_DEBUG, "Add Payload [%s] id='%s'\n", name, id);
+                                                                               globals.logger(DL_LOG_CRIT, "Add Payload [%s] id='%s'\n", name, id);
                                                                        }
                                                                }
                                                        }
@@ -462,27 +481,46 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
                                        tag = iks_child(tag);
                                }
                                
-                               while(tag) {
+                               for (;tag;tag = iks_next_tag(tag)) {
                                        if (!strcasecmp(iks_name_nons(tag), "info_element")) {
                                                char *name = iks_find_attrib(tag, "name");
                                                char *value = iks_find_attrib(tag, "value");
                                                if (globals.debug) {
-                                                       globals.logger(DL_LOG_DEBUG, "Info Element [%s]=[%s]\n", name, value);
+                                                       globals.logger(DL_LOG_CRIT, "Info Element [%s]=[%s]\n", name, value);
                                                }
                                                ldl_session_set_value(session, name, value);
                                                
-                                       } else if (!strcasecmp(iks_name_nons(tag), "candidate") && session->candidate_len < LDL_MAX_CANDIDATES) {
+                                       } else if (!strcasecmp(iks_name_nons(tag), "candidate") /*&& session->candidate_len < LDL_MAX_CANDIDATES*/) {
                                                char *key;
                                                double pref = 0.0;
                                                int index = -1;
+                                               ldl_transport_type_t tport;
+                                               unsigned int *candidate_len = NULL;
+                                               ldl_candidate_t (*candidates)[LDL_MAX_CANDIDATES] = NULL;
+                                               
                                                if ((key = iks_find_attrib(tag, "preference"))) {
                                                        unsigned int x;
+                                                       
                                                        pref = strtod(key, NULL);
                                                        
-                                                       for (x = 0; x < session->candidate_len; x++) {
-                                                               if (session->candidates[x].pref == pref) {
+                                                       /* Check what kind of candidate this is */
+                                                       if ((key = iks_find_attrib(tag, "name")) && ((tport = ldl_transport_type_parse(key)) != LDL_TPORT_MAX)) {
+                                                               candidates = &(session->candidates[tport]);
+                                                               candidate_len = &(session->candidate_len[tport]);
+                                                       } else {
+                                                               globals.logger(DL_LOG_WARNING, "No such transport type: %s\n", key);
+                                                               continue;
+                                                       }
+                                                       
+                                                       if (*candidate_len >= LDL_MAX_CANDIDATES) {
+                                                               globals.logger(DL_LOG_WARNING, "Too many %s candidates\n", key);
+                                                               continue;
+                                                       }
+                                                       
+                                                       for (x = 0; x < *candidate_len; x++) {
+                                                               if ((*candidates)[x].pref == pref) {
                                                                        if (globals.debug) {
-                                                                               globals.logger(DL_LOG_DEBUG, "Duplicate Pref!\n");
+                                                                               globals.logger(DL_LOG_CRIT, "Duplicate Pref! Updating...\n");
                                                                        }
                                                                        index = x;
                                                                        break;
@@ -491,44 +529,44 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
                                                }
                                                
                                                if (index < 0) {
-                                                       index = session->candidate_len++;
+                                                       index = (*candidate_len)++;
                                                }
                                                
-                                               session->candidates[index].pref = pref;
+                                               (*candidates)[index].pref = pref;
 
                                                if (tid) {
-                                                       session->candidates[index].tid = apr_pstrdup(session->pool, tid);
+                                                       (*candidates)[index].tid = apr_pstrdup(session->pool, tid);
                                                }
 
                                                if ((key = iks_find_attrib(tag, "name"))) {
-                                                       session->candidates[index].name = apr_pstrdup(session->pool, key);
+                                                       (*candidates)[index].name = apr_pstrdup(session->pool, key);
                                                }
                                                if ((key = iks_find_attrib(tag, "type"))) {
-                                                       session->candidates[index].type = apr_pstrdup(session->pool, key);
+                                                       (*candidates)[index].type = apr_pstrdup(session->pool, key);
                                                }
                                                if ((key = iks_find_attrib(tag, "protocol"))) {
-                                                       session->candidates[index].protocol = apr_pstrdup(session->pool, key);
+                                                       (*candidates)[index].protocol = apr_pstrdup(session->pool, key);
                                                }
                                                if ((key = iks_find_attrib(tag, "username"))) {
-                                                       session->candidates[index].username = apr_pstrdup(session->pool, key);
+                                                       (*candidates)[index].username = apr_pstrdup(session->pool, key);
                                                }
                                                if ((key = iks_find_attrib(tag, "password"))) {
-                                                       session->candidates[index].password = apr_pstrdup(session->pool, key);
+                                                       (*candidates)[index].password = apr_pstrdup(session->pool, key);
                                                }
                                                if ((key = iks_find_attrib(tag, "address"))) {
-                                                       session->candidates[index].address = apr_pstrdup(session->pool, key);
+                                                       (*candidates)[index].address = apr_pstrdup(session->pool, key);
                                                }
                                                if ((key = iks_find_attrib(tag, "port"))) {
-                                                       session->candidates[index].port = (uint16_t)atoi(key);
+                                                       (*candidates)[index].port = (uint16_t)atoi(key);
                                                }
                                                
-                                               if (!session->candidates[index].type) {
-                                                       session->candidates[index].type = apr_pstrdup(session->pool, "stun");
+                                               if (!(*candidates)[index].type) {
+                                                       (*candidates)[index].type = apr_pstrdup(session->pool, "stun");
                                                }
 
 
                                                if (globals.debug) {
-                                                       globals.logger(DL_LOG_DEBUG
+                                                       globals.logger(DL_LOG_CRIT
                                                                        "New Candidate %d\n"
                                                                        "name=%s\n"
                                                                        "type=%s\n"
@@ -538,19 +576,18 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
                                                                        "address=%s\n"
                                                                        "port=%d\n"
                                                                        "pref=%0.2f\n",
-                                                                       session->candidate_len,
-                                                                       session->candidates[index].name,
-                                                                       session->candidates[index].type,
-                                                                       session->candidates[index].protocol,
-                                                                       session->candidates[index].username,
-                                                                       session->candidates[index].password,
-                                                                       session->candidates[index].address,
-                                                                       session->candidates[index].port,
-                                                                       session->candidates[index].pref
+                                                                       *candidate_len,
+                                                                       (*candidates)[index].name,
+                                                                       (*candidates)[index].type,
+                                                                       (*candidates)[index].protocol,
+                                                                       (*candidates)[index].username,
+                                                                       (*candidates)[index].password,
+                                                                       (*candidates)[index].address,
+                                                                       (*candidates)[index].port,
+                                                                       (*candidates)[index].pref
                                                                        );
                                                }
                                        }
-                                       tag = iks_next_tag(tag);
                                }
                        } else if (!strcasecmp(type, "terminate")) {
                                dl_signal = LDL_SIGNAL_TERMINATE;
@@ -569,6 +606,301 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
        return LDL_STATUS_SUCCESS;
 }
 
+
+static ldl_status parse_jingle_code(ldl_handle_t *handle, iks *xml, char *to, char *from, char *type)
+{
+       ldl_session_t *session = NULL;
+       ldl_signal_t dl_signal = LDL_SIGNAL_NONE;
+       char *initiator = iks_find_attrib(xml, "initiator");
+       char *msg = NULL;
+       char *id = iks_find_attrib(xml, "sid");
+       char *action = iks_find_attrib(xml, "action");
+       iks *tag;
+
+
+       if (!strcasecmp(type, "error")) {
+               action = type;
+       }
+
+
+       if (!(id && action)) {
+               globals.logger(DL_LOG_CRIT, "missing required params\n");  
+               return LDL_STATUS_FALSE;
+       }
+
+       if (!(session = apr_hash_get(handle->sessions, id, APR_HASH_KEY_STRING))) {
+               ldl_session_create(&session, handle, id, from, to, LDL_FLAG_NONE);
+               if (!session) {
+                       return LDL_STATUS_MEMERR;
+               }
+       }
+
+       if (!session) {
+               if (globals.debug) {
+                       globals.logger(DL_LOG_CRIT, "Non-Existent Session %s!\n", id);
+               }
+               return LDL_STATUS_MEMERR;
+       }
+       
+       if (globals.debug) {
+               globals.logger(DL_LOG_CRIT, "Message for Session %s\n", id);
+       }
+
+
+       if (action) {
+                       
+               if (!strcasecmp(action, "redirect")) {
+                       apr_hash_t *hash = session->handle->sessions;
+                       char *p = to;
+                       if ((p = strchr(to, ':'))) {
+                               p++;
+                       } else {
+                               p = to;
+                       }
+                                               
+
+                       apr_hash_set(hash, session->them, APR_HASH_KEY_STRING, NULL);
+                       apr_hash_set(hash, session->id, APR_HASH_KEY_STRING, NULL);
+                       session->them = apr_pstrdup(session->pool, p);
+                       apr_hash_set(handle->sessions, session->them, APR_HASH_KEY_STRING, session);
+                       apr_hash_set(handle->sessions, session->id, APR_HASH_KEY_STRING, session);
+
+                       dl_signal = LDL_SIGNAL_REDIRECT;
+               } else if (!strcasecmp(action, "session-initiate") || !strcasecmp(action, "session-accept")) {
+
+                       dl_signal = LDL_SIGNAL_INITIATE;
+
+                       if (!strcasecmp(action, "session-accept")) {
+                               msg = "accept";
+                       }
+                       
+                       if (!session->initiator && initiator) {
+                               session->initiator = apr_pstrdup(session->pool, initiator);
+                       }
+                       tag = iks_child (xml);
+                               
+                       while(tag) {
+
+                               if (!strcasecmp(iks_name_nons(tag), "content")) {
+                                       iks *dtag = iks_child (tag);
+                                       char key[512];
+
+                                       if (!strcasecmp(iks_name_nons(dtag), "description")) {
+                                               iks *itag = iks_child (dtag);
+                                               char *name = iks_find_attrib(tag, "name");
+                                               char *media = iks_find_attrib(dtag, "media");
+
+                                               if (globals.debug) {
+                                                       globals.logger(DL_LOG_CRIT, "Found description of type '%s' media type '%s'\n", name, media);
+                                                       
+                                               }
+                                               
+                                               while(itag) {
+                                                       if (!strcasecmp(iks_name_nons(itag), "rtcp-mux")) {
+                                                               snprintf(key, sizeof(key), "%s:rtcp-mux", media);
+                                                               ldl_session_set_value(session, key, "true");
+                                                       }
+
+                                                       if (!strcasecmp(iks_name_nons(itag), "encryption")) {
+                                                               iks *etag = iks_child (itag); 
+
+                                                               while(etag) { 
+                                                                       char *suite = iks_find_attrib(etag, "crypto-suite"); 
+                                                                       char *params = iks_find_attrib(etag, "key-params"); 
+                                                                       char *tag = iks_find_attrib(etag, "tag"); 
+                                                                       char val[512];
+                                                                       
+                                                                       if (suite && params && tag) {
+                                                                               snprintf(key, sizeof(key), "%s:crypto:%s", media, tag);
+                                                                               snprintf(val, sizeof(val), "%s %s %s", tag, suite, params);
+                                                                               
+                                                                               ldl_session_set_value(session, key, val);
+                                                                       }
+                                                                       
+                                                                       etag = iks_next_tag(etag);
+                                                               }
+                                                       }
+
+
+                                                       if (!strcasecmp(iks_name_nons(itag), "payload-type") && session->payload_len < LDL_MAX_PAYLOADS) {
+                                                               char *name = iks_find_attrib(itag, "name");
+                                                               char *id = iks_find_attrib(itag, "id");
+                                                               char *rate = iks_find_attrib(itag, "clockrate");
+
+                                                               if (name && id) {
+                                                                       session->payloads[session->payload_len].name = apr_pstrdup(session->pool, name);
+                                                                       session->payloads[session->payload_len].id = atoi(id);
+                                                                       if (rate) {
+                                                                               session->payloads[session->payload_len].rate = atoi(rate);
+                                                                       } else {
+                                                                               if (!strcasecmp(media, "video")) {
+                                                                                       session->payloads[session->payload_len].rate = 90000;
+                                                                               } else {
+                                                                                       session->payloads[session->payload_len].rate = 8000;
+                                                                               }
+                                                                       }
+                                                                       session->payload_len++;
+                                                               
+                                                                       if (globals.debug) {
+                                                                               globals.logger(DL_LOG_CRIT, "Add Payload [%s] id='%s'\n", name, id);
+                                                                       }
+                                                               }
+                                                       }
+                                                       itag = iks_next_tag(itag);
+                                               }
+                                               
+                                       }
+                               }
+                               
+                               tag = iks_next_tag(tag);
+                       }
+               } else if (!strcasecmp(action, "transport-accept")) {
+                       dl_signal = LDL_SIGNAL_TRANSPORT_ACCEPT;
+               } else if (!strcasecmp(action, "reject")) {
+                       dl_signal = LDL_SIGNAL_REJECT;
+               } else if (!strcasecmp(action, "transport-info")) {
+
+                       tag = iks_child (xml);
+                               
+                       while(tag) {
+
+                               if (!strcasecmp(iks_name_nons(tag), "content")) {
+                                       iks *ttag = iks_child (tag);
+
+                                       dl_signal = LDL_SIGNAL_CANDIDATES;
+
+                                       id = action;
+
+                                       if (ttag && !strcasecmp(iks_name_nons(ttag), "transport")) {
+                                               ttag = iks_child(ttag);
+                                       }
+                               
+                                       for (;ttag;ttag = iks_next_tag(ttag)) {
+                                               if (!strcasecmp(iks_name_nons(ttag), "info_element")) {
+                                                       char *name = iks_find_attrib(ttag, "name");
+                                                       char *value = iks_find_attrib(ttag, "value");
+                                                       if (globals.debug) {
+                                                               globals.logger(DL_LOG_CRIT, "Info Element [%s]=[%s]\n", name, value);
+                                                       }
+                                                       ldl_session_set_value(session, name, value);
+                                               
+                                               } else if (!strcasecmp(iks_name_nons(ttag), "candidate")) {
+                                                       char *key;
+                                                       double pref = 0.0;
+                                                       int index = -1;
+                                                       ldl_transport_type_t tport;
+                                                       unsigned int *candidate_len = NULL;
+                                                       ldl_candidate_t (*candidates)[LDL_MAX_CANDIDATES] = NULL;
+                                               
+                                                       if ((key = iks_find_attrib(ttag, "preference"))) {
+                                                               unsigned int x;
+                                                       
+                                                               pref = strtod(key, NULL);
+                                                       
+                                                               /* Check what kind of candidate this is */
+                                                               if ((key = iks_find_attrib(ttag, "name")) && ((tport = ldl_transport_type_parse(key)) != LDL_TPORT_MAX)) {
+                                                                       candidates = &(session->candidates[tport]);
+                                                                       candidate_len = &(session->candidate_len[tport]);
+                                                               } else {
+                                                                       globals.logger(DL_LOG_WARNING, "No such transport type: %s\n", key);
+                                                                       continue;
+                                                               }
+                                                       
+                                                               if (*candidate_len >= LDL_MAX_CANDIDATES) {
+                                                                       globals.logger(DL_LOG_WARNING, "Too many %s candidates\n", key);
+                                                                       continue;
+                                                               }
+                                                       
+                                                               for (x = 0; x < *candidate_len; x++) {
+                                                                       if ((*candidates)[x].pref == pref) {
+                                                                               if (globals.debug) {
+                                                                                       globals.logger(DL_LOG_CRIT, "Duplicate Pref!\n");
+                                                                               }
+                                                                               index = x;
+                                                                               break;
+                                                                       }
+                                                               }
+                                                       }
+                                               
+                                                       if (index < 0) {
+                                                               index = (*candidate_len)++;
+                                                       }
+                                               
+                                                       (*candidates)[index].pref = pref;
+
+                                                       if ((key = iks_find_attrib(ttag, "name"))) {
+                                                               (*candidates)[index].name = apr_pstrdup(session->pool, key);
+                                                       }
+                                                       if ((key = iks_find_attrib(ttag, "type"))) {
+                                                               (*candidates)[index].type = apr_pstrdup(session->pool, key);
+                                                       }
+                                                       if ((key = iks_find_attrib(ttag, "protocol"))) {
+                                                               (*candidates)[index].protocol = apr_pstrdup(session->pool, key);
+                                                       }
+                                                       if ((key = iks_find_attrib(ttag, "username"))) {
+                                                               (*candidates)[index].username = apr_pstrdup(session->pool, key);
+                                                       }
+                                                       if ((key = iks_find_attrib(ttag, "password"))) {
+                                                               (*candidates)[index].password = apr_pstrdup(session->pool, key);
+                                                       }
+                                                       if ((key = iks_find_attrib(ttag, "address"))) {
+                                                               (*candidates)[index].address = apr_pstrdup(session->pool, key);
+                                                       }
+                                                       if ((key = iks_find_attrib(ttag, "port"))) {
+                                                               (*candidates)[index].port = (uint16_t)atoi(key);
+                                                       }
+                                               
+                                                       if (!(*candidates)[index].type) {
+                                                               (*candidates)[index].type = apr_pstrdup(session->pool, "stun");
+                                                       }
+
+
+                                                       if (globals.debug) {
+                                                               globals.logger(DL_LOG_CRIT, 
+                                                                                          "New Candidate %d\n"
+                                                                                          "name=%s\n"
+                                                                                          "type=%s\n"
+                                                                                          "protocol=%s\n"
+                                                                                          "username=%s\n"
+                                                                                          "password=%s\n"
+                                                                                          "address=%s\n"
+                                                                                          "port=%d\n"
+                                                                                          "pref=%0.2f\n",
+                                                                                          *candidate_len,
+                                                                                          (*candidates)[index].name,
+                                                                                          (*candidates)[index].type,
+                                                                                          (*candidates)[index].protocol,
+                                                                                          (*candidates)[index].username,
+                                                                                          (*candidates)[index].password,
+                                                                                          (*candidates)[index].address,
+                                                                                          (*candidates)[index].port,
+                                                                                          (*candidates)[index].pref
+                                                                                          );
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               tag = iks_next_tag(tag); 
+                       }
+               } else if (!strcasecmp(action, "session-terminate")) {
+                       dl_signal = LDL_SIGNAL_TERMINATE;
+               } else if (!strcasecmp(action, "error")) {
+                       dl_signal = LDL_SIGNAL_ERROR;
+               }
+       }
+               
+
+
+       if (handle->session_callback && dl_signal) {
+               handle->session_callback(handle, session, dl_signal, to, from, id, msg); 
+       }
+
+       return LDL_STATUS_SUCCESS;
+}
+
+
+
 const char *marker = "TRUE";
 
 
@@ -601,12 +933,12 @@ static int on_disco_default(void *user_data, ikspak *pak)
        }
 
        if (pak->subtype == IKS_TYPE_RESULT) {
-               globals.logger(DL_LOG_DEBUG, "FixME!!! node=[%s]\n", node?node:"");             
+               globals.logger(DL_LOG_CRIT, "FixME!!! node=[%s]\n", node?node:"");              
        } else if (pak->subtype == IKS_TYPE_GET) {
                if ((iq = iks_new("iq"))) {
                        int all = 0;
                        
-                       iks_insert_attrib(iq, "from", iks_find_attrib(pak->x, "to"));
+                       iks_insert_attrib(iq, "from", handle->login);
                        if (pak->from) {
                                iks_insert_attrib(iq, "to", pak->from->full);
                        }
@@ -673,7 +1005,7 @@ static int on_disco_default(void *user_data, ikspak *pak)
                }
 
                if (!send) {
-                       globals.logger(DL_LOG_DEBUG, "Memory Error!\n");
+                       globals.logger(DL_LOG_CRIT, "Memory Error!\n");
                }
        }
 
@@ -838,12 +1170,12 @@ static void do_presence(ldl_handle_t *handle, char *from, char *to, char *type,
        char buf[512];
        iks *tag;
 
-       if (from && !strchr(from, '/')) {
+       if (!strchr(from, '/')) {
                snprintf(buf, sizeof(buf), "%s/talk", from);
                from = buf;
        }
 
-       if (ldl_test_flag(handle, LDL_FLAG_COMPONENT) && from && to && ldl_jid_domcmp(from, to)) {
+       if (ldl_test_flag(handle, LDL_FLAG_COMPONENT) && ldl_jid_domcmp(from, to)) {
                globals.logger(DL_LOG_ERR, "Refusal to send presence from and to the same domain in component mode [%s][%s]\n", from, to);
                return;
        }
@@ -890,7 +1222,7 @@ static void do_presence(ldl_handle_t *handle, char *from, char *to, char *type,
                        if ((tag = iks_insert(pres, "c"))) {
                                iks_insert_attrib(tag, "node", "http://www.freeswitch.org/xmpp/client/caps");
                                iks_insert_attrib(tag, "ver", LDL_CAPS_VER);
-                               iks_insert_attrib(tag, "ext", "sidebar voice-v1");
+                               iks_insert_attrib(tag, "ext", "sidebar voice-v1 video-v1 camera-v1");
                                iks_insert_attrib(tag, "client", "libdingaling");
                                iks_insert_attrib(tag, "xmlns", "http://jabber.org/protocol/caps");
                        }
@@ -970,7 +1302,7 @@ static void cancel_retry(ldl_handle_t *handle, char *id)
        apr_thread_mutex_lock(handle->lock);
        if ((packet_node = apr_hash_get(handle->retry_hash, id, APR_HASH_KEY_STRING))) {
                if (globals.debug) {
-                       globals.logger(DL_LOG_DEBUG, "Cancel packet %s\n", packet_node->id);
+                       globals.logger(DL_LOG_CRIT, "Cancel packet %s\n", packet_node->id);
                }
                packet_node->retries = 0;
        }
@@ -989,6 +1321,30 @@ static iks* working_find(iks *tag, const char *name)
        return NULL;
 }
 
+static iks* working_find_nons(iks *tag, const char *name) 
+{
+       while(tag) {
+               char *a = iks_name(tag);
+               char *b = (char *)name;
+               char *p;
+
+               if ((p = strchr(a, ':'))) {
+                       a = p+1;
+               }
+
+               if ((p = strchr(b, ':'))) {
+                       b = p+1;
+               }
+
+               if (!strcasecmp(a,b)) {
+                       return tag;
+               }
+               tag = iks_next_tag(tag);
+       }
+       
+       return NULL;
+}
+
 static int on_commands(void *user_data, ikspak *pak)
 {
        ldl_handle_t *handle = user_data;
@@ -999,6 +1355,8 @@ static int on_commands(void *user_data, ikspak *pak)
        uint8_t is_result = strcasecmp(type, "result") ? 0 : 1;
        uint8_t is_error = strcasecmp(type, "error") ? 0 : 1;
        iks *xml, *xsession, *xerror = NULL, *xredir = NULL;
+       iks *xjingle;
+
 
        xml = iks_child (pak->x);
 
@@ -1062,8 +1420,21 @@ static int on_commands(void *user_data, ikspak *pak)
                }
        }
        
-       
-       if ((xsession = working_find(xml, "ses:session")) || (xsession = working_find(xml, "session"))) {
+
+
+       if ((handle->flags & LDL_FLAG_JINGLE) && (xjingle = working_find_nons(xml, "jin:jingle"))) {
+               if (parse_jingle_code(handle, xjingle, to, from, type) == LDL_STATUS_SUCCESS) {
+                       iks *reply;
+                       if ((reply = iks_make_iq(IKS_TYPE_RESULT, NULL))) {
+                               iks_insert_attrib(reply, "to", from);
+                               iks_insert_attrib(reply, "from", to);
+                               iks_insert_attrib(reply, "id", iqid);
+                               apr_queue_push(handle->queue, reply);
+                               reply = NULL;
+                       }
+               }
+               
+       } else if ((xsession = working_find_nons(xml, "ses:session"))) {
                char *id;
 
                id = iks_find_attrib(xsession, "id");
@@ -1102,7 +1473,7 @@ static int on_result(void *user_data, ikspak *pak)
                ctag = iks_insert(msg, "c");
                iks_insert_attrib(ctag, "node", "http://www.freeswitch.org/xmpp/client/caps");
                iks_insert_attrib(ctag, "ver", "1.0.0.1");
-               iks_insert_attrib(ctag, "ext", "sidebar voice-v1");
+               iks_insert_attrib(ctag, "ext", "sidebar voice-v1 video-v1");
                iks_insert_attrib(ctag, "client", "libdingaling");
                iks_insert_attrib(ctag, "xmlns", "http://jabber.org/protocol/caps");
 
@@ -1239,7 +1610,7 @@ static int on_stream(ldl_handle_t *handle, int type, iks *node)
                        if (iks_has_tls()) {
                                iks_start_tls(handle->parser);
                        } else {
-                               globals.logger(DL_LOG_DEBUG, "TLS NOT SUPPORTED IN THIS BUILD!\n");
+                               globals.logger(DL_LOG_WARNING, "TLS NOT SUPPORTED IN THIS BUILD!\n");
                        }
                }
                break;
@@ -1284,19 +1655,19 @@ static int on_stream(ldl_handle_t *handle, int type, iks *node)
                                                apr_queue_push(handle->queue, x);
                                                x = NULL;
                                        } else {
-                                               globals.logger(DL_LOG_DEBUG, "Memory ERROR!\n");
+                                               globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
                                                break;
                                        }
                                        
                                }
                        }
                } else if (node && strcmp("failure", iks_name_nons(node)) == 0) {
-                       globals.logger(DL_LOG_DEBUG, "sasl authentication failed\n");
+                       globals.logger(DL_LOG_CRIT, "sasl authentication failed\n");
                        if (handle->session_callback) {
                                handle->session_callback(handle, NULL, LDL_SIGNAL_LOGIN_FAILURE, "user", "core", "Login Failure", handle->login);
                        }
                } else if (node && strcmp("success", iks_name_nons(node)) == 0) {
-                       globals.logger(DL_LOG_DEBUG, "XMPP server connected\n");
+                       globals.logger(DL_LOG_NOTICE, "XMPP server connected\n");
                        iks_send_header(handle->parser, handle->acc->server);
                        ldl_set_flag_locked(handle, LDL_FLAG_CONNECTED);
                        if (handle->session_callback) {
@@ -1308,7 +1679,7 @@ static int on_stream(ldl_handle_t *handle, int type, iks *node)
                                if (handle->session_callback) {
                                        handle->session_callback(handle, NULL, LDL_SIGNAL_LOGIN_SUCCESS, "user", "core", "Login Success", handle->login);
                                }
-                               globals.logger(DL_LOG_DEBUG, "XMPP authenticated\n");
+                               globals.logger(DL_LOG_NOTICE, "XMPP authenticated\n");
                                ldl_set_flag_locked(handle, LDL_FLAG_AUTHORIZED);
                        }
             if (node) {
@@ -1360,18 +1731,20 @@ static int on_msg(void *user_data, ikspak *pak)
 
 static int on_error(void *user_data, ikspak * pak)
 {
-       globals.logger(DL_LOG_DEBUG, "authorization failed\n");
+       globals.logger(DL_LOG_ERR, "authorization failed\n");
        return IKS_FILTER_EAT;
 }
 
 static void on_log(ldl_handle_t *handle, const char *data, size_t size, int is_incoming)
 {
+
        if (globals.debug) {
                if (is_incoming) {
                        globals.logger(DL_LOG_INFO, "+xml:%s%s:%s", iks_is_secure(handle->parser) ? "Sec" : "", is_incoming ? "RECV" : "SEND", data);
                } else {
                        globals.logger(DL_LOG_NOTICE, "+xml:%s%s:%s", iks_is_secure(handle->parser) ? "Sec" : "", is_incoming ? "RECV" : "SEND", data);
                }
+                                                  
        }
 }
 
@@ -1443,11 +1816,7 @@ static ldl_queue_t ldl_flush_queue(ldl_handle_t *handle, int done)
        while(apr_queue_trypop(handle->queue, &pop) == APR_SUCCESS) {
                if (pop) {
                        msg = (iks *) pop;
-                       if (!done) {
-                               if (iks_send(handle->parser, msg) != IKS_OK) {
-                                       globals.logger(DL_LOG_DEBUG, "Failed sending data!\n");
-                               };
-                       };
+                       if (!done) iks_send(handle->parser, msg);
                        iks_delete(msg);
                        pop = NULL;
                        sent_data = LDL_QUEUE_SENT;
@@ -1458,7 +1827,7 @@ static ldl_queue_t ldl_flush_queue(ldl_handle_t *handle, int done)
 
        len = apr_queue_size(handle->retry_queue); 
        if (globals.debug && len) {
-               globals.logger(DL_LOG_DEBUG, "Processing %u packets in retry queue\n", len);
+               globals.logger(DL_LOG_CRIT, "Processing %u packets in retry queue\n", len);
        }
 
        pop = NULL;
@@ -1475,18 +1844,16 @@ static ldl_queue_t ldl_flush_queue(ldl_handle_t *handle, int done)
                                if (packet_node->retries > 0) {
                                        packet_node->retries--;
                                        if (globals.debug) {
-                                               globals.logger(DL_LOG_DEBUG, "Sending packet %s (%d left)\n", packet_node->id, packet_node->retries);
+                                               globals.logger(DL_LOG_CRIT, "Sending packet %s (%d left)\n", packet_node->id, packet_node->retries);
                                        }
-                                       if (iks_send(handle->parser, packet_node->xml) != IKS_OK) {
-                                               globals.logger(DL_LOG_DEBUG, "Failed trying re-sending data!\n");
-                                       };
+                                       iks_send(handle->parser, packet_node->xml);
                                        packet_node->next = now + 5000000;
                                        sent_data = LDL_QUEUE_SENT;
                                }
                        }
                        if (packet_node->retries == 0 || done) {
                                if (globals.debug) {
-                                       globals.logger(DL_LOG_DEBUG, "Discarding packet %s\n", packet_node->id);
+                                       globals.logger(DL_LOG_CRIT, "Discarding packet %s\n", packet_node->id);
                                }
                                apr_hash_set(handle->retry_hash, packet_node->id, APR_HASH_KEY_STRING, NULL);
                                iks_delete(packet_node->xml);
@@ -1505,8 +1872,8 @@ static ldl_queue_t ldl_flush_queue(ldl_handle_t *handle, int done)
 
 static void xmpp_connect(ldl_handle_t *handle, char *jabber_id, char *pass)
 {
-       int count_ka = LDL_KEEPALIVE_TIMEOUT;   
-       time_t tstart, tnow;
+       int timeout_ka = LDL_KEEPALIVE_TIMEOUT;
+       int count_ka = timeout_ka;      
 
        while (ldl_test_flag((&globals), LDL_FLAG_READY) && ldl_test_flag(handle, LDL_FLAG_RUNNING)) {
                int e;
@@ -1537,8 +1904,6 @@ static void xmpp_connect(ldl_handle_t *handle, char *jabber_id, char *pass)
 
                j_setup_filter(handle);
 
-               globals.logger(DL_LOG_DEBUG, "xmpp connecting\n");
-
                e = iks_connect_via(handle->parser,
                                                        handle->server ? handle->server : handle->acc->server,
                                                        handle->port ? handle->port : IKS_JABBER_PORT,
@@ -1548,27 +1913,31 @@ static void xmpp_connect(ldl_handle_t *handle, char *jabber_id, char *pass)
                case IKS_OK:
                        break;
                case IKS_NET_NODNS:
-                       globals.logger(DL_LOG_DEBUG, "hostname lookup failed\n");
+                       globals.logger(DL_LOG_CRIT, "hostname lookup failed\n");
                        microsleep(1000);
                        goto fail;
                case IKS_NET_NOCONN:
-                       globals.logger(DL_LOG_DEBUG, "connection failed\n");
+                       globals.logger(DL_LOG_CRIT, "connection failed\n");
                        microsleep(1000);
                        goto fail;
                default:
-                       globals.logger(DL_LOG_DEBUG, "io error 1 %d\n", e);
+                       globals.logger(DL_LOG_CRIT, "io error 1 %d\n", e);
                        microsleep(1000);
                        goto fail;
                }
 
                handle->counter = opt_timeout;
-               if ((tstart = time(NULL)) == -1) {
-                       globals.logger(DL_LOG_DEBUG, "error determining connection time");
-               }
 
                while (ldl_test_flag((&globals), LDL_FLAG_READY) && ldl_test_flag(handle, LDL_FLAG_RUNNING)) {
                        e = iks_recv(handle->parser, 1);
 
+                       if (count_ka-- <= 0) {
+                               if( iks_send_raw(handle->parser, " ") == IKS_OK) {
+                                       count_ka = timeout_ka;
+                                       globals.logger(DL_LOG_DEBUG, "Sent keep alive signal\n");
+                               }
+                       }
+
                        if (handle->loop_callback) {
                                if (handle->loop_callback(handle) != LDL_STATUS_SUCCESS) {
                                        ldl_clear_flag_locked(handle, LDL_FLAG_RUNNING);        
@@ -1585,51 +1954,31 @@ static void xmpp_connect(ldl_handle_t *handle, char *jabber_id, char *pass)
                        }
 
                        if (IKS_OK != e || ldl_test_flag(handle, LDL_FLAG_BREAK)) {
-                               globals.logger(DL_LOG_DEBUG, "io error 2 %d retry in %d second(s)", e, ++handle->fail_count);
-                               if ((tnow = time(NULL)) == -1) {
-                                       globals.logger(DL_LOG_DEBUG, "error deterniming io error time");
-                               }
-                               if (difftime(tnow, tstart) > 30) {
-                                       /* this is a new error situation: reset counter */ 
-                                       globals.logger(DL_LOG_DEBUG, "resetting fail count");
-                                       handle->fail_count = 1;
-                               }
+                               globals.logger(DL_LOG_DEBUG, "io error 2 %d retry in %d second(s)\n", e, ++handle->fail_count);
                                microsleep(1000 * handle->fail_count);
                                goto fail;
                        }
 
                        if (ldl_test_flag(handle, LDL_FLAG_RUNNING)) {
-                               if (ldl_flush_queue(handle, 0) == LDL_QUEUE_SENT) {
-                                       count_ka = LDL_KEEPALIVE_TIMEOUT;
-                               }
-                       } 
+                               ldl_flush_queue(handle, 0);
+                       }
 
                        handle->counter--;
                        if (!ldl_test_flag(handle, LDL_FLAG_CONNECTED)) {
                                if (IKS_NET_TLSFAIL == e) {
-                                       globals.logger(DL_LOG_DEBUG, "tls handshake failed\n");
+                                       globals.logger(DL_LOG_CRIT, "tls handshake failed\n");
                                        microsleep(500);
                                        break;
                                }
 
                                if (handle->counter == 0) {
-                                       globals.logger(DL_LOG_DEBUG, "network timeout\n");
-                                       microsleep(500);
-                                       break;
-                               }
-                       }
-
-                       if (count_ka-- <= 0) {
-                               if( iks_send_raw(handle->parser, " ") == IKS_OK) {
-                                       globals.logger(DL_LOG_DEBUG, "Sent keep alive signal");
-                                       count_ka = LDL_KEEPALIVE_TIMEOUT;
-                               } else {
-                                       globals.logger(DL_LOG_DEBUG, "Failed sending keep alive signal");
+                                       globals.logger(DL_LOG_CRIT, "network timeout\n");
                                        microsleep(500);
                                        break;
                                }
                        }
 
+                       microsleep(100);
                }
 
        fail:
@@ -1657,7 +2006,7 @@ static void xmpp_connect(ldl_handle_t *handle, char *jabber_id, char *pass)
 static void add_elements(ldl_session_t *session, iks *tag)
 {
        apr_hash_index_t *hi;
-
+       return;
        for (hi = apr_hash_first(session->pool, session->variables); hi; hi = apr_hash_next(hi)) {
                void *val = NULL;
                const void *key = NULL;
@@ -1672,6 +2021,42 @@ static void add_elements(ldl_session_t *session, iks *tag)
        }
 }
 
+
+static iks *ldl_set_jingle_tag(ldl_session_t *session, iks *iq, char *action)
+{
+       iks *jin = iks_insert (iq, "jin:jingle");
+       iks_insert_attrib(jin, "xmlns:jin", "urn:xmpp:jingle:1");
+       iks_insert_attrib(jin, "action", action);
+       iks_insert_attrib(jin, "sid", session->id);
+       //iks_insert_attrib(jin, "initiator", session->initiator ? session->initiator : session->them);
+
+       return jin;
+}
+
+static ldl_status new_jingle_iq(ldl_session_t *session, iks **iqp, iks **jinp, unsigned int *id, char *action)
+{
+       iks *iq , *jin;
+       unsigned int myid;
+       char idbuf[80];
+
+       myid = next_id();
+       snprintf(idbuf, sizeof(idbuf), "%u", myid);
+       iq = iks_new("iq");
+       iks_insert_attrib(iq, "xmlns", "jabber:client");
+       iks_insert_attrib(iq, "from", session->login);
+       iks_insert_attrib(iq, "to", session->them);
+       iks_insert_attrib(iq, "type", "set");
+       iks_insert_attrib(iq, "id", idbuf);
+
+       jin = ldl_set_jingle_tag(session, iq, action);
+
+       *jinp = jin;
+       *iqp = iq;
+       *id = myid;
+       return LDL_STATUS_SUCCESS;
+}
+
+
 static ldl_status new_session_iq(ldl_session_t *session, iks **iqp, iks **sessp, unsigned int *id, char *type)
 {
        iks *iq, *sess;
@@ -1973,6 +2358,11 @@ unsigned int ldl_session_terminate(ldl_session_t *session)
        apr_hash_t *hash = session->handle->sessions;
 
        new_session_iq(session, &iq, &sess, &id, "terminate");
+
+       if ((session->handle->flags & LDL_FLAG_JINGLE)) {
+               ldl_set_jingle_tag(session, iq, "session-terminate");
+       }
+
        schedule_packet(session->handle, id, iq, LDL_RETRY);
 
        if (session->id) {
@@ -1998,6 +2388,12 @@ unsigned int ldl_session_transport(ldl_session_t *session,
        unsigned int x, id = 0;
 
 
+       if ((session->handle->flags & LDL_FLAG_JINGLE)) {
+               return ldl_session_candidates(session, candidates, clen);
+       }
+
+
+
        for (x = 0; x < clen; x++) {
                char buf[512];
                iq = NULL;
@@ -2005,13 +2401,13 @@ unsigned int ldl_session_transport(ldl_session_t *session,
                id = 0;
                
                new_session_iq(session, &iq, &sess, &id, "transport-info");
-               //tag = iks_insert(sess, "transport");
-               //iks_insert_attrib(tag, "xmlns", "http://www.google.com/transport/p2p");
+               
                tag = sess;
 
-               if (0) add_elements(session, tag);
+               //if (0) add_elements(session, tag);
                tag = iks_insert(tag, "transport");
                iks_insert_attrib(tag, "xmlns", "http://www.google.com/transport/p2p");
+               //iks_insert_attrib(tag, "xmlns", "urn:xmpp:jingle:transports:raw-udp:1");
 
                tag = iks_insert(tag, "candidate");
 
@@ -2047,7 +2443,6 @@ unsigned int ldl_session_transport(ldl_session_t *session,
                schedule_packet(session->handle, id, iq, LDL_RETRY);
        }
 
-
        return id;
 }
 
@@ -2056,23 +2451,103 @@ unsigned int ldl_session_candidates(ldl_session_t *session,
                                                                        unsigned int clen)
 
 {
-       iks *iq, *sess, *tag;
-       unsigned int x, id = 0;
+       iks *iq = NULL, *sess = NULL, *tag = NULL;
+       unsigned int x = 0, id = 0;
 
 
-       for (x = 0; x < clen; x++) {
-               char buf[512];
+       unsigned int pass = 0;
+       iks *jingle = NULL, *jin_content = NULL, *p_trans = NULL;
+       const char *tname = "";
+       const char *type = "";   
+
+       if ((session->handle->flags & LDL_FLAG_JINGLE)) {
+
+
+               new_jingle_iq(session, &iq, &jingle, &id, "transport-info");
+
+               for (pass = 0; pass < 2; pass++) {
+               
+                       if (pass == 0) {
+                               type = "rtp";
+                               tname = "audio";
+                       } else {
+                               type = "video_rtp";
+                               tname = "video";
+                       }
+               
+                       jin_content = iks_insert(jingle, "jin:content");
+                       iks_insert_attrib(jin_content, "name", tname);
+                       iks_insert_attrib(jin_content, "creator", "initiator");         
+
+                       for (x = 0; x < clen; x++) {
+                               char buf[512];
+                       
+                               if (strcasecmp(candidates[x].name, type)) {
+                                       continue;
+                               }    
+                       
+                               p_trans = iks_insert(jin_content, "p:transport");
+                               iks_insert_attrib(p_trans, "xmlns:p", "http://www.google.com/transport/p2p");
+                       
+                       
+                       
+                               tag = iks_insert(p_trans, "candidate");
+
+                               if (candidates[x].name) {
+                                       iks_insert_attrib(tag, "name", candidates[x].name);
+                               }
+                               if (candidates[x].address) {
+                                       iks_insert_attrib(tag, "address", candidates[x].address);
+                               }
+                               if (candidates[x].port) {
+                                       snprintf(buf, sizeof(buf), "%u", candidates[x].port);
+                                       iks_insert_attrib(tag, "port", buf);
+                               }
+                               if (candidates[x].username) {
+                                       iks_insert_attrib(tag, "username", candidates[x].username);
+                               }
+                               if (candidates[x].password) {
+                                       iks_insert_attrib(tag, "password", candidates[x].password);
+                               }
+                               if (candidates[x].pref) {
+                                       snprintf(buf, sizeof(buf), "%0.1f", candidates[x].pref);
+                                       iks_insert_attrib(tag, "preference", buf);
+                               }
+                               if (candidates[x].protocol) {
+                                       iks_insert_attrib(tag, "protocol", candidates[x].protocol);
+                               }
+                               if (candidates[x].type) {
+                                       iks_insert_attrib(tag, "type", candidates[x].type);
+                               }
+
+                               iks_insert_attrib(tag, "network", "0");
+                               iks_insert_attrib(tag, "generation", "0");
+                       }
+               }
+               
+
+               schedule_packet(session->handle, id, iq, LDL_RETRY);
+
                iq = NULL;
                sess = NULL;
+               tag = NULL;
+               x = 0;
                id = 0;
+       }
+
+
+       new_session_iq(session, &iq, &sess, &id, "candidates");
+       add_elements(session, sess);
+
+       for (x = 0; x < clen; x++) {
+               char buf[512];
+               //iq = NULL;
+               //sess = NULL;
+               //id = 0;
                
-               new_session_iq(session, &iq, &sess, &id, "candidates");
-               //tag = iks_insert(sess, "transport");
-               //iks_insert_attrib(tag, "xmlns", "http://www.google.com/transport/p2p");
-               tag = sess;
+               tag = iks_insert(sess, "ses:candidate");
+
 
-               add_elements(session, tag);
-               tag = iks_insert(tag, "ses:candidate");
 
                if (candidates[x].name) {
                        iks_insert_attrib(tag, "name", candidates[x].name);
@@ -2103,9 +2578,10 @@ unsigned int ldl_session_candidates(ldl_session_t *session,
 
                iks_insert_attrib(tag, "network", "0");
                iks_insert_attrib(tag, "generation", "0");
-               schedule_packet(session->handle, id, iq, LDL_RETRY);
+
        }
 
+       schedule_packet(session->handle, id, iq, LDL_RETRY);
 
        return id;
 }
@@ -2192,11 +2668,11 @@ char *ldl_handle_disco(ldl_handle_t *handle, char *id, char *from, char *buf, un
                        iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
                } else {
                        iks_delete(iq);
-                       globals.logger(DL_LOG_DEBUG, "Memory ERROR!\n");
+                       globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
                        return NULL;
                }
        } else {
-               globals.logger(DL_LOG_DEBUG, "Memory ERROR!\n");
+               globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
                return NULL;
        }
        
@@ -2236,42 +2712,274 @@ char *ldl_handle_disco(ldl_handle_t *handle, char *id, char *from, char *buf, un
 }
 
 
+
 unsigned int ldl_session_describe(ldl_session_t *session,
-                                                               ldl_payload_t *payloads,
-                                                               unsigned int plen,
-                                                               ldl_description_t description)
+                                                                 ldl_payload_t *payloads,
+                                                                 unsigned int plen,
+                                                                 ldl_description_t description, unsigned int *audio_ssrc, unsigned int *video_ssrc, 
+                                                                 ldl_crypto_data_t *audio_crypto_data, ldl_crypto_data_t *video_crypto_data)
 {
-       iks *iq, *sess, *tag, *payload, *tp;
+       iks *iq;
+       iks *sess, *payload = NULL, *tag = NULL;//, *u = NULL;
+
        unsigned int x, id;
+       int video_call = 0;
+       int compat = 1;
+       //char *vid_mux = ldl_session_get_value(session, "video:rtcp-mux");
+       //char *aud_mux = ldl_session_get_value(session, "audio:rtcp-mux");
+       char tmp[80];
+       iks *jpayload = NULL, *tp = NULL;
+       iks *jingle, *jin_audio, *jin_audio_desc = NULL, *jin_video = NULL, *jin_video_desc = NULL, *crypto;
+
+
+       if (!*audio_ssrc) {
+               *audio_ssrc = (uint32_t) ((intptr_t) session + (uint32_t) time(NULL));
+       }
+
+       if (!*video_ssrc) {
+               *video_ssrc = (uint32_t) ((intptr_t) payloads + (uint32_t) time(NULL));
+       }
+
+       if ((session->handle->flags & LDL_FLAG_JINGLE)) {
+               new_jingle_iq(session, &iq, &jingle, &id, description == LDL_DESCRIPTION_ACCEPT ? "session-accept" : "session-initiate");
+               iks_insert_attrib(jingle, "initiator", session->initiator ? session->initiator : session->them);
+               
+               if (compat) {
+                       sess = iks_insert (iq, "ses:session");
+                       iks_insert_attrib(sess, "xmlns:ses", "http://www.google.com/session");
+                       
+                       iks_insert_attrib(sess, "type", description == LDL_DESCRIPTION_ACCEPT ? "accept" : "initiate");
+                       iks_insert_attrib(sess, "id", session->id);
+                       iks_insert_attrib(sess, "initiator", session->initiator ? session->initiator : session->them);
+               }
+
+       } else {
+               new_session_iq(session, &iq, &sess, &id, description == LDL_DESCRIPTION_ACCEPT ? "accept" : "initiate");
+       }
+
+
+       /* Check if this is a video call */
+       for (x = 0; x < plen; x++) {
+               if (payloads[x].type == LDL_PAYLOAD_VIDEO) {
+                       video_call = 1;
+                       if ((session->handle->flags & LDL_FLAG_JINGLE)) {
+                               jin_video = iks_insert(jingle, "jin:content");
+                               iks_insert_attrib(jin_video, "name", "video");
+                               iks_insert_attrib(jin_video, "creator", "initiator");
+                               //iks_insert_attrib(jin_video, "senders", "both");
+                               jin_video_desc = iks_insert(jin_video, "rtp:description");
+                               iks_insert_attrib(jin_video_desc, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
+                               iks_insert_attrib(jin_video_desc, "media", "video");
+                               snprintf(tmp, sizeof(tmp), "%u", *video_ssrc);
+                               iks_insert_attrib(jin_video_desc, "ssrc", tmp);
+                               tp = iks_insert(jin_video, "p:transport");
+                               iks_insert_attrib(tp, "xmlns:p", "http://www.google.com/transport/p2p");
+
+                       }
+
+                       break;
+               }
+       }
+
        
+       if ((session->handle->flags & LDL_FLAG_JINGLE)) {       
+               jin_audio = iks_insert(jingle, "jin:content");
+               iks_insert_attrib(jin_audio, "name", "audio");
+               iks_insert_attrib(jin_audio, "creator", "initiator");
+               //iks_insert_attrib(jin_audio, "senders", "both");
+               jin_audio_desc = iks_insert(jin_audio, "rtp:description");
+               iks_insert_attrib(jin_audio_desc, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
+               iks_insert_attrib(jin_audio_desc, "media", "audio");
+               snprintf(tmp, sizeof(tmp), "%u", *audio_ssrc);
+               iks_insert_attrib(jin_audio_desc, "ssrc", tmp);
+               tp = iks_insert(jin_audio, "p:transport");
+               iks_insert_attrib(tp, "xmlns:p", "http://www.google.com/transport/p2p");
+       }
+       
+       if (compat) {
+
+               if (video_call) {
+                       tag = iks_insert(sess, "vid:description");
+                       iks_insert_attrib(tag, "xmlns:vid", "http://www.google.com/session/video");
+               } else {
+                       tag = iks_insert(sess, "pho:description");
+                       iks_insert_attrib(tag, "xmlns:pho", "http://www.google.com/session/phone");
+               }
+
+               if (video_call) {
+                       
+                       for (x = 0; x < plen; x++) {
+                               char idbuf[80];
+
+                               if (payloads[x].type != LDL_PAYLOAD_VIDEO) {
+                                       continue;
+                               }
+               
+                               sprintf(idbuf, "%d", payloads[x].id);
+
+
+                               payload = iks_insert(tag, "vid:payload-type");
+
+                               iks_insert_attrib(payload, "id", idbuf);
+                               iks_insert_attrib(payload, "name", payloads[x].name);
+               
+                               if (payloads[x].type == LDL_PAYLOAD_VIDEO && video_call) {
+                                       if (payloads[x].width) {
+                                               sprintf(idbuf, "%d", payloads[x].width);
+                                               iks_insert_attrib(payload, "width", idbuf);
+                                       }
+                                       if (payloads[x].height) {
+                                               sprintf(idbuf, "%d", payloads[x].height);
+                                               iks_insert_attrib(payload, "height", idbuf);
+                                       }
+                                       if (payloads[x].framerate) {
+                                               sprintf(idbuf, "%d", payloads[x].framerate);
+                                               iks_insert_attrib(payload, "framerate", idbuf);
+                                       }
+                               }
+                       
+                       }
+
+
+                       //if (vid_mux) {
+                       //      iks_insert(tag, "rtcp-mux"); 
+                       //}
+
+                       //payload = iks_insert(tag, "vid:src-id"); 
+                       //iks_insert_cdata(payload, "123456789", 0); 
+
+
+                       //iks_insert_attrib(payload, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
+                       //iks_insert(payload, "vid:usage");
+               }
+       }
 
-       new_session_iq(session, &iq, &sess, &id, description == LDL_DESCRIPTION_ACCEPT ? "accept" : "initiate");
-       tag = iks_insert(sess, "pho:description");
-       iks_insert_attrib(tag, "xmlns:pho", "http://www.google.com/session/phone");
-       iks_insert_attrib(tag, "xml:lang", "en");
        for (x = 0; x < plen; x++) {
                char idbuf[80];
-               payload = iks_insert(tag, "pho:payload-type");
-               iks_insert_attrib(payload, "xmlns:pho", "http://www.google.com/session/phone");
 
+               if (payloads[x].type == LDL_PAYLOAD_VIDEO && !video_call) {
+                       continue;
+               }
+               
                sprintf(idbuf, "%d", payloads[x].id);
-               iks_insert_attrib(payload, "id", idbuf);
-               iks_insert_attrib(payload, "name", payloads[x].name);
-               if (payloads[x].rate) {
-                       sprintf(idbuf, "%d", payloads[x].rate);
-                       iks_insert_attrib(payload, "clockrate", idbuf);
+               
+               if (payloads[x].type == LDL_PAYLOAD_AUDIO) {
+
+                       if ((session->handle->flags & LDL_FLAG_JINGLE)) {       
+                               char ratebuf[80];
+                               char buf[80];
+                               iks *param;
+                               
+                               jpayload = iks_insert(jin_audio_desc, "rtp:payload-type");
+                               iks_insert_attrib(jpayload, "id", idbuf);
+                               sprintf(ratebuf, "%d", payloads[x].rate);
+                               iks_insert_attrib(jpayload, "name", payloads[x].name);
+                               iks_insert_attrib(jpayload, "clockrate", ratebuf);
+                               param = iks_insert(jpayload, "rtp:parameter");
+                               iks_insert_attrib(param, "name", "bitrate");
+                               sprintf(buf, "%d", payloads[x].bps);
+                               iks_insert_attrib(param, "value", buf);
+                               
+                               sprintf(buf, "%d", payloads[x].ptime);
+                               iks_insert_attrib(jpayload, "ptime", ratebuf);
+                               iks_insert_attrib(jpayload, "maxptime", ratebuf);
+                               
+                       }
+                       
+               } else  if (payloads[x].type == LDL_PAYLOAD_VIDEO && video_call) {
+       
+                       if ((session->handle->flags & LDL_FLAG_JINGLE)) {       
+                               char buf[80];
+                               iks *param;
+
+                               jpayload = iks_insert(jin_video_desc, "rtp:payload-type");
+                               iks_insert_attrib(jpayload, "id", idbuf);
+                               iks_insert_attrib(jpayload, "name", payloads[x].name);
+                               param = iks_insert(jpayload, "rtp:parameter");
+                               iks_insert_attrib(param, "name", "width");
+                               sprintf(buf, "%d", payloads[x].width);
+                               iks_insert_attrib(param, "value", buf);
+
+
+                               param = iks_insert(jpayload, "rtp:parameter");
+                               iks_insert_attrib(param, "name", "height");
+                               sprintf(buf, "%d", payloads[x].height);
+                               iks_insert_attrib(param, "value", buf);
+                               
+                               param = iks_insert(jpayload, "rtp:parameter");
+                               iks_insert_attrib(param, "name", "framerate");
+                               sprintf(buf, "%d", payloads[x].framerate);
+                               iks_insert_attrib(param, "value", buf);
+                       
+                       }
+               }
+
+               if (compat) {
+
+                       if (payloads[x].type == LDL_PAYLOAD_AUDIO) {
+
+                               payload = iks_insert(tag, "pho:payload-type");
+
+                               iks_insert_attrib(payload, "id", idbuf);
+                               iks_insert_attrib(payload, "name", payloads[x].name);
+
+                               if (payloads[x].rate) {
+                                       sprintf(idbuf, "%d", payloads[x].rate);
+                                       iks_insert_attrib(payload, "clockrate", idbuf);
+                               }
+                       
+                               if (payloads[x].bps) {
+                                       sprintf(idbuf, "%d", payloads[x].bps);
+                                       iks_insert_attrib(payload, "bitrate", idbuf);
+                               }
+
+                               iks_insert_attrib(payload, "xmlns:pho", "http://www.google.com/session/phone"); 
+                       }
                }
-               if (payloads[x].bps) {
-                       sprintf(idbuf, "%d", payloads[x].bps);
-                       iks_insert_attrib(payload, "bitrate", idbuf);
+               //if (payloads[x].id == 34) payloads[x].id = 98; /* XXX */
+
+       }
+
+       if ((session->handle->flags & LDL_FLAG_JINGLE)) {       
+               if (jin_video_desc && video_crypto_data) {
+                       payload = iks_insert(jin_video_desc, "rtp:encryption");
+                       crypto = iks_insert(payload, "rtp:crypto");
+                       iks_insert_attrib(crypto, "crypto-suite", video_crypto_data->suite);
+                       iks_insert_attrib(crypto, "key-params", video_crypto_data->key);
+                       iks_insert_attrib(crypto, "tag", video_crypto_data->tag);
+               }
+
+
+               if (jin_audio_desc && audio_crypto_data) {
+                       payload = iks_insert(jin_audio_desc, "rtp:encryption");
+                       crypto = iks_insert(payload, "rtp:crypto");
+                       iks_insert_attrib(crypto, "crypto-suite", audio_crypto_data->suite);
+                       iks_insert_attrib(crypto, "key-params", audio_crypto_data->key);
+                       iks_insert_attrib(crypto, "tag", audio_crypto_data->tag);
                }
        }
 
+       //if (aud_mux) {
+       //      iks_insert(tag, "rtcp-mux"); 
+       //}
+
+       //payload = iks_insert(tag, "pho:src-id");
+       //iks_insert_cdata(payload, "987654321", 0); 
+       //iks_insert_attrib(payload, "xmlns:pho", "http://www.google.com/session/phone");
+
+       //payload = iks_insert(tag, "rtp:encryption");
+       //iks_insert_attrib(payload, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
+       //u = iks_insert(payload, "pho:usage");
+       //iks_insert_attrib(u, "xmlns:pho", "http://www.google.com/session/phone");
+
+#if 0
        if (description == LDL_DESCRIPTION_INITIATE) {
                tp = iks_insert (sess, "transport");
                iks_insert_attrib(tp, "xmlns", "http://www.google.com/transport/p2p");
        }
-       
+#endif
+
+
        schedule_packet(session->handle, id, iq, LDL_RETRY);
 
        return id;
@@ -2282,11 +2990,13 @@ ldl_state_t ldl_session_get_state(ldl_session_t *session)
        return session->state;
 }
 
-ldl_status ldl_session_get_candidates(ldl_session_t *session, ldl_candidate_t **candidates, unsigned int *len)
+ldl_status ldl_session_get_candidates(ldl_session_t *session, ldl_transport_type_t tport, ldl_candidate_t **candidates, unsigned int *len)
 {
+       assert(tport < LDL_TPORT_MAX);
+       
        if (session->candidate_len) {
-               *candidates = session->candidates;
-               *len = session->candidate_len;
+               *candidates = session->candidates[tport];
+               *len = session->candidate_len[tport];
                return LDL_STATUS_SUCCESS;
        } else {
                *candidates = NULL;
@@ -2332,7 +3042,7 @@ ldl_status ldl_global_init(int debug)
        memset(&globals, 0, sizeof(globals));
 
        if (apr_pool_create(&globals.memory_pool, NULL) != LDL_STATUS_SUCCESS) {
-               globals.logger(DL_LOG_DEBUG, "Could not allocate memory pool\n");
+               globals.logger(DL_LOG_CRIT, "Could not allocate memory pool\n");
                return LDL_STATUS_MEMERR;
        }
 
index 6cf8bd846b0d8009e0db69a2ddce276a688001ec..e76be86ecf97d6f8fc3af487c8def1bbf2da2ba3 100644 (file)
@@ -28,6 +28,7 @@
  * libdingaling.h -- Main Header File
  *
  */
+
 /*! \file libdingaling.h
     \brief Main Header File
 */
@@ -60,12 +61,18 @@ extern "C" {
 #endif
 
 #define LDL_HANDLE_QLEN 2000
-#define LDL_MAX_CANDIDATES 10
+#define LDL_MAX_CANDIDATES 25
 #define LDL_MAX_PAYLOADS 50
 #define LDL_RETRY 3
 #define IKS_NS_COMPONENT "jabber:component:accept"
-/* period between keep alive signals in 1sec units*/
-#define LDL_KEEPALIVE_TIMEOUT 300
+/* period between keep alive signals in 0.1sec units*/
+#define LDL_KEEPALIVE_TIMEOUT 6000
+
+typedef struct ldl_crypto_data_s {
+       char *tag;
+       char *suite;
+       char *key;
+} ldl_crypto_data_t;
 
 /*! \brief A structure to store a jingle candidate */
 struct ldl_candidate {
@@ -90,19 +97,89 @@ struct ldl_candidate {
 };
 typedef struct ldl_candidate ldl_candidate_t;
 
-/*! \brief A structure to store a jingle payload */
+typedef enum {
+       LDL_PAYLOAD_AUDIO,
+       LDL_PAYLOAD_VIDEO
+} ldl_payload_type_t;
+
+/*! \brief A structure to store a jingle audio payload */
 struct ldl_payload {
+       /*! the type of the payload */
+       ldl_payload_type_t type;
        /*! the iana name of the payload type */
        char *name;
        /*! the iana id of the payload type */
        unsigned int id;
+       
+       /* Audio */
+       
        /*! the transfer rate of the payload type */
        unsigned int rate;
        /*! the bits per second of the payload type */
        unsigned int bps;
+       
+       /* Video */
+       
+       /*! the width of the video payload type */
+       unsigned int width;
+       /*! the width of the video payload type */
+       unsigned int height;
+       /*! the framerate of the video payload type */
+       unsigned int framerate;
+
+       unsigned int ptime;
 };
 typedef struct ldl_payload ldl_payload_t;
 
+
+enum ldl_transport_type  {
+       LDL_TPORT_RTP,
+       LDL_TPORT_VIDEO_RTP,
+       LDL_TPORT_RTCP,
+       LDL_TPORT_VIDEO_RTCP,
+       
+       /* Nothing below that line */
+       LDL_TPORT_MAX
+};
+typedef enum ldl_transport_type ldl_transport_type_t;
+
+static inline const char *ldl_transport_type_str(ldl_transport_type_t type) 
+{
+       static const char *name[] = { "rtp", "video_rtp", "rtcp", "video_rtcp" };
+       return  type >= LDL_TPORT_MAX ? NULL : name[type];
+}
+
+static inline ldl_transport_type_t ldl_transport_type_parse(const char *type) {
+       if (!strcasecmp(type, "rtp")) {
+               return LDL_TPORT_RTP;
+       } else if (!strcasecmp(type, "rtcp")) {
+               return LDL_TPORT_RTCP;
+       } else if (!strcasecmp(type, "video_rtp")) {
+               return LDL_TPORT_VIDEO_RTP;
+       } else if (!strcasecmp(type, "video_rtcp")) {
+               return LDL_TPORT_VIDEO_RTCP;
+       } else {
+               return LDL_TPORT_MAX;
+       }
+}
+
+#if 0
+/*! \brief A structure to store a jingle video payload */
+struct ldl_vpayload {
+       /*! the iana name of the video payload type */
+       char *name;
+       /*! the iana id of the video payload type */
+       unsigned int id;
+       /*! the width of the video payload type */
+       unsigned int width;
+       /*! the width of the video payload type */
+       unsigned int height;
+       /*! the framerate of the video payload type */
+       unsigned int framerate;
+};
+typedef struct ldl_vpayload ldl_vpayload_t;
+#endif
+
 struct ldl_handle;
 typedef struct ldl_handle ldl_handle_t;
 
@@ -132,7 +209,8 @@ typedef enum {
        LDL_FLAG_SASL_MD5 = (1 << 12),
        LDL_FLAG_COMPONENT = (1 << 13),
        LDL_FLAG_OUTBOUND = (1 << 14),
-       LDL_FLAG_GATEWAY = (1 << 15)
+       LDL_FLAG_GATEWAY = (1 << 15),
+       LDL_FLAG_JINGLE = (1 << 16)
 } ldl_user_flag_t;
 
 typedef enum {
@@ -514,9 +592,10 @@ unsigned int ldl_session_transport(ldl_session_t *session,
   \return the message_id of the generated xmpp request
 */
 unsigned int ldl_session_describe(ldl_session_t *session,
-                                                               ldl_payload_t *payloads,
-                                                               unsigned int plen,
-                                                               ldl_description_t description);
+                                                                 ldl_payload_t *payloads,
+                                                                 unsigned int plen,
+                                                                 ldl_description_t description, unsigned int *audio_ssrc, unsigned int *video_ssrc, 
+                                                                 ldl_crypto_data_t *audio_crypto_data, ldl_crypto_data_t *video_crypto_data);
 
 
 /*!
@@ -530,11 +609,12 @@ ldl_state_t ldl_session_get_state(ldl_session_t *session);
 /*!
   \brief get the candidates
   \param session the session
+  \param tport type of transport (rtp,rtcp,video_rtp,video_rtcp,etc.)
   \param candidates pointer to point at array of the candidates
   \param len the resulting len of the array pointer
   \return success or failure
 */
-ldl_status ldl_session_get_candidates(ldl_session_t *session, ldl_candidate_t **candidates, unsigned int *len);
+ldl_status ldl_session_get_candidates(ldl_session_t *session, ldl_transport_type_t tport, ldl_candidate_t **candidates, unsigned int *len);
 
 /*!
   \brief get the payloads
index f2ac13bb7a8b295d601b8a4b0d7202b415c599dc..c56dc228cba72fad546aaa6d37110b04b165e053 100644 (file)
@@ -9,7 +9,7 @@ IKS_LA=$(IKS_DIR)/src/libiksemel.la
 DING_DIR=$(BASE)/libs/libdingaling
 LOCAL_CFLAGS += -I$(DING_DIR)/src -I$(BASE)/libs/iksemel/include
 LOCAL_OBJS=$(DING_DIR)/src/libdingaling.o $(DING_DIR)/src/sha1.o $(IKS_LA)
-LOCAL_SOURCES=$(DING_DIR)/src/libdingaling.c $(DING_DIR)/src/sha1.c
+LOCAL_SOURCES=$(DING_DIR)/src/libdingaling.c $(DING_DIR)/src/sha1.c $(DING_DIR)/src/libdingaling.h
 LOCAL_LDFLAGS=$(LIBGNUTLS_LIBS)
 include $(BASE)/build/modmake.rules
 
index 735a68efc7f95bc0cc33c3f41b4c6ff8e38022c3..6bf84be1f29625b720461e30c4223990a73a80c0 100644 (file)
 #include <switch_stun.h>
 #include <libdingaling.h>
 
+#define MDL_RTCP_DUR 5000
 #define DL_CAND_WAIT 10000000
 #define DL_CAND_INITIAL_WAIT 2000000
+//#define DL_CAND_WAIT 2000000
+//#define DL_CAND_INITIAL_WAIT 5000000
 
 #define DL_EVENT_LOGIN_SUCCESS "dingaling::login_success"
 #define DL_EVENT_LOGIN_FAILURE "dingaling::login_failure"
@@ -79,7 +82,8 @@ typedef enum {
        TFLAG_TERM = (1 << 21),
        TFLAG_TRANSPORT_ACCEPT = (1 << 22),
        TFLAG_READY = (1 << 23),
-       TFLAG_NAT_MAP = (1 << 24)
+       TFLAG_NAT_MAP = (1 << 24),
+       TFLAG_SECURE = (1 << 25)
 } TFLAGS;
 
 typedef enum {
@@ -144,47 +148,103 @@ struct mdl_profile {
 };
 typedef struct mdl_profile mdl_profile_t;
 
-struct private_object {
-       unsigned int flags;
+/*! \brief The required components to setup a jingle transport */
+typedef struct mdl_transport {
+       char *remote_ip;
+       switch_port_t remote_port;
+
+       switch_port_t local_port;       /*!< The real local port */
+    switch_port_t adv_local_port;
+       unsigned int ssrc;
+
+       char local_user[17];
+    char local_pass[17];    
+       char *remote_user;
+       char *remote_pass;
+       int ptime;
+       int payload_count;
+       int restart_rtp;
+
        switch_codec_t read_codec;
        switch_codec_t write_codec;
+       
        switch_frame_t read_frame;
+       
+       uint32_t codec_rate;
+       char *codec_name;
+       
+       switch_payload_t codec_num;
+       switch_payload_t r_codec_num;
+       
+       char *stun_ip;
+       uint16_t stun_port;
+       
+       switch_rtp_t *rtp_session;
+       ldl_transport_type_t type;
+
+       int total;
+       int accepted;
+
+       int ready;
+
+       int codec_index;
+
+       int vid_width;
+       int vid_height;
+       int vid_rate;
+
+       switch_byte_t has_crypto;
+       int crypto_tag;
+       unsigned char local_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN];
+       unsigned char remote_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN];
+       switch_rtp_crypto_key_type_t crypto_send_type;
+       switch_rtp_crypto_key_type_t crypto_recv_type;
+       switch_rtp_crypto_key_type_t crypto_type;
+
+       char *local_crypto_key;
+       char *remote_crypto_key;
+       
+       ldl_crypto_data_t *local_crypto_data;
+       
+} mdl_transport_t;
+
+
+struct private_object {
+       unsigned int flags;
        mdl_profile_t *profile;
        switch_core_session_t *session;
+       switch_channel_t *channel;
+
        switch_caller_profile_t *caller_profile;
        unsigned short samprate;
        switch_mutex_t *mutex;
        const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];
        unsigned int num_codecs;
-       int codec_index;
-       switch_rtp_t *rtp_session;
+
+       mdl_transport_t transports[LDL_TPORT_MAX+1];    
+
        ldl_session_t *dlsession;
-       char *remote_ip;
-       switch_port_t local_port;
-       switch_port_t adv_local_port;
-       switch_port_t remote_port;
-       char local_user[17];
-       char local_pass[17];
-       char *remote_user;
+
        char *us;
        char *them;
        unsigned int cand_id;
        unsigned int desc_id;
        unsigned int dc;
+
        uint32_t timestamp_send;
        int32_t timestamp_recv;
        uint32_t last_read;
-       char *codec_name;
-       switch_payload_t codec_num;
-       switch_payload_t r_codec_num;
-       uint32_t codec_rate;
+
        switch_time_t next_desc;
        switch_time_t next_cand;
-       char *stun_ip;
+
        char *recip;
        char *dnis;
-       uint16_t stun_port;
        switch_mutex_t *flag_mutex;
+
+       int read_count;
+
+
 };
 
 struct rfc2833_digit {
@@ -744,8 +804,12 @@ static void terminate_session(switch_core_session_t **session, int line, switch_
                tech_pvt = switch_core_session_get_private(*session);
 
 
-               if (tech_pvt && tech_pvt->profile && tech_pvt->profile->ip && tech_pvt->local_port) {
-                       switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->local_port);
+               if (tech_pvt && tech_pvt->profile && tech_pvt->profile->ip && tech_pvt->transports[LDL_TPORT_RTP].local_port) {
+                       switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->transports[LDL_TPORT_RTP].local_port);
+               }
+
+               if (tech_pvt && tech_pvt->profile && tech_pvt->profile->ip && tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port) {
+                       switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port);
                }
 
                if (!switch_core_session_running(*session) && (!tech_pvt || !switch_test_flag(tech_pvt, TFLAG_READY))) {
@@ -811,16 +875,35 @@ static void dl_logger(char *file, const char *func, int line, int level, char *f
 
 static int get_codecs(struct private_object *tech_pvt)
 {
+       char *codec_string = NULL;
+       const char *var;
+       char *codec_order[SWITCH_MAX_CODECS];
+       int codec_order_last;
+       char **codec_order_p = NULL;
+
+
        switch_assert(tech_pvt != NULL);
        switch_assert(tech_pvt->session != NULL);
 
        if (!tech_pvt->num_codecs) {
-               if (globals.codec_string) {
+
+               if ((var = switch_channel_get_variable(tech_pvt->channel, "absolute_codec_string"))) {
+                       codec_string = (char *)var;
+                       codec_order_last = switch_separate_string(codec_string, ',', codec_order, SWITCH_MAX_CODECS);
+                       codec_order_p = codec_order;
+               } else {
+                       codec_string = globals.codec_string;
+                       codec_order_last = globals.codec_order_last;
+                       codec_order_p = globals.codec_order;
+               }
+               
+               if (codec_string) {
                        if ((tech_pvt->num_codecs = switch_loadable_module_get_codecs_sorted(tech_pvt->codecs,
-                                                                                                                                                                SWITCH_MAX_CODECS, globals.codec_order, globals.codec_order_last)) <= 0) {
+                                                                                                                                                                SWITCH_MAX_CODECS, codec_order_p, codec_order_last)) <= 0) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO codecs?\n");
                                return 0;
                        }
+                       
                } else if (((tech_pvt->num_codecs = switch_loadable_module_get_codecs(tech_pvt->codecs, SWITCH_MAX_CODECS))) <= 0) {
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO codecs?\n");
                        return 0;
@@ -864,76 +947,246 @@ static void handle_thread_launch(ldl_handle_t *handle)
 }
 
 
-static int activate_rtp(struct private_object *tech_pvt)
+switch_status_t mdl_build_crypto(struct private_object *tech_pvt, ldl_transport_type_t ttype, 
+                                                                       int index, switch_rtp_crypto_key_type_t type, switch_rtp_crypto_direction_t direction)
+{
+       unsigned char b64_key[512] = "";
+       const char *type_str;
+       unsigned char *key;
+       char *p;
+
+
+       if (!switch_test_flag(tech_pvt, TFLAG_SECURE)) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+
+       if (type == AES_CM_128_HMAC_SHA1_80) {
+               type_str = SWITCH_RTP_CRYPTO_KEY_80;
+       } else {
+               type_str = SWITCH_RTP_CRYPTO_KEY_32;
+       }
+
+       if (direction == SWITCH_RTP_CRYPTO_SEND) {
+               key = tech_pvt->transports[ttype].local_raw_key;
+       } else {
+               key = tech_pvt->transports[ttype].remote_raw_key;
+
+       }
+
+       switch_rtp_get_random(key, SWITCH_RTP_KEY_LEN);
+       switch_b64_encode(key, SWITCH_RTP_KEY_LEN, b64_key, sizeof(b64_key));
+       p = strrchr((char *) b64_key, '=');
+
+       while (p && *p && *p == '=') {
+               *p-- = '\0';
+       }
+
+       tech_pvt->transports[ttype].local_crypto_key = switch_core_session_sprintf(tech_pvt->session, "%d %s inline:%s", index, type_str, b64_key);
+       tech_pvt->transports[ttype].local_crypto_data = switch_core_session_alloc(tech_pvt->session, sizeof(ldl_crypto_data_t));
+       tech_pvt->transports[ttype].local_crypto_data->tag = switch_core_session_sprintf(tech_pvt->session, "%d", index);
+       tech_pvt->transports[ttype].local_crypto_data->suite = switch_core_session_strdup(tech_pvt->session, type_str);
+       tech_pvt->transports[ttype].local_crypto_data->key = switch_core_session_sprintf(tech_pvt->session, "inline:%s", (char *)b64_key);
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Local Key [%s]\n", tech_pvt->transports[ttype].local_crypto_key);
+
+       tech_pvt->transports[ttype].crypto_type = AES_CM_128_NULL_AUTH;
+
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t mdl_add_crypto(struct private_object *tech_pvt, 
+                                                                         ldl_transport_type_t ttype, const char *key_str, switch_rtp_crypto_direction_t direction)
+{
+       unsigned char key[SWITCH_RTP_MAX_CRYPTO_LEN];
+       switch_rtp_crypto_key_type_t type;
+       char *p;
+
+
+       p = strchr(key_str, ' ');
+
+       if (p && *p && *(p + 1)) {
+               p++;
+               if (!strncasecmp(p, SWITCH_RTP_CRYPTO_KEY_32, strlen(SWITCH_RTP_CRYPTO_KEY_32))) {
+                       type = AES_CM_128_HMAC_SHA1_32;
+               } else if (!strncasecmp(p, SWITCH_RTP_CRYPTO_KEY_80, strlen(SWITCH_RTP_CRYPTO_KEY_80))) {
+                       type = AES_CM_128_HMAC_SHA1_80;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Parse Error near [%s]\n", p);
+                       goto bad;
+               }
+
+               p = strchr(p, ' ');
+               if (p && *p && *(p + 1)) {
+                       p++;
+                       if (strncasecmp(p, "inline:", 7)) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Parse Error near [%s]\n", p);
+                               goto bad;
+                       }
+
+                       p += 7;
+                       switch_b64_decode(p, (char *) key, sizeof(key));
+
+                       if (direction == SWITCH_RTP_CRYPTO_SEND) {
+                               tech_pvt->transports[ttype].crypto_send_type = type;
+                               memcpy(tech_pvt->transports[ttype].local_raw_key, key, SWITCH_RTP_KEY_LEN);
+                       } else {
+                               tech_pvt->transports[ttype].crypto_recv_type = type;
+                               memcpy(tech_pvt->transports[ttype].remote_raw_key, key, SWITCH_RTP_KEY_LEN);
+                       }
+
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_NOTICE, 
+                                                         "%s Setting %s crypto key\n", ldl_transport_type_str(ttype), switch_core_session_get_name(tech_pvt->session));
+                       tech_pvt->transports[ttype].has_crypto++;
+
+                       
+                       return SWITCH_STATUS_SUCCESS;
+               }
+
+       }
+
+ bad:
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Error!\n");
+       return SWITCH_STATUS_FALSE;
+
+}
+
+static void try_secure(struct private_object *tech_pvt, ldl_transport_type_t ttype) 
+{
+
+       if (!switch_test_flag(tech_pvt, TFLAG_SECURE)) {
+               return;
+       }
+
+
+       //if (tech_pvt->transports[ttype].crypto_type) {
+               switch_rtp_add_crypto_key(tech_pvt->transports[ttype].rtp_session, 
+                                                                 SWITCH_RTP_CRYPTO_SEND, 1, tech_pvt->transports[ttype].crypto_type, 
+                                                                 tech_pvt->transports[ttype].local_raw_key, SWITCH_RTP_KEY_LEN);
+                       
+
+               switch_rtp_add_crypto_key(tech_pvt->transports[ttype].rtp_session, 
+                                                                 SWITCH_RTP_CRYPTO_RECV, tech_pvt->transports[ttype].crypto_tag, 
+                                                                 tech_pvt->transports[ttype].crypto_type, 
+                                                                 tech_pvt->transports[ttype].remote_raw_key, SWITCH_RTP_KEY_LEN);
+                       
+               switch_channel_set_variable(tech_pvt->channel, "jingle_secure_audio_confirmed", "true");
+
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_NOTICE, 
+                                                 "%s %s crypto confirmed\n", ldl_transport_type_str(ttype), switch_core_session_get_name(tech_pvt->session));
+
+               //}
+
+}
+
+
+
+static int activate_audio_rtp(struct private_object *tech_pvt)
 {
        switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session);
        const char *err;
-       int ms = 0;
+       int ms = tech_pvt->transports[LDL_TPORT_RTP].ptime;
        switch_rtp_flag_t flags;
+       int locked = 0;
+       int r = 1;
 
-       if (switch_rtp_ready(tech_pvt->rtp_session)) {
+
+       if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
                return 1;
        }
 
-       if (!(tech_pvt->remote_ip && tech_pvt->remote_port)) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "No valid candidates received!\n");
+       if (!(tech_pvt->transports[LDL_TPORT_RTP].remote_ip && tech_pvt->transports[LDL_TPORT_RTP].remote_port)) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "No valid rtp candidates received!\n");
                return 0;
        }
 
-       if (switch_core_codec_init(&tech_pvt->read_codec,
-                                                          tech_pvt->codec_name,
-                                                          NULL,
-                                                          tech_pvt->codec_rate,
-                                                          ms,
-                                                          1,
-                                                          SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
-                                                          NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Can't load codec?\n");
-               switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-               return 0;
-       }
-       tech_pvt->read_frame.rate = tech_pvt->read_codec.implementation->samples_per_second;
-       tech_pvt->read_frame.codec = &tech_pvt->read_codec;
-
-       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Read Codec to %s@%d\n",
-                                         tech_pvt->codec_name, (int) tech_pvt->read_codec.implementation->samples_per_second);
-
-       if (switch_core_codec_init(&tech_pvt->write_codec,
-                                                          tech_pvt->codec_name,
-                                                          NULL,
-                                                          tech_pvt->codec_rate,
-                                                          ms,
-                                                          1,
-                                                          SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
-                                                          NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Can't load codec?\n");
-               switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-               return 0;
-       }
-       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Write Codec to %s@%d\n",
-                                         tech_pvt->codec_name, (int) tech_pvt->write_codec.implementation->samples_per_second);
+       if (switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_RTP].read_codec)) {
+               locked = 1;
+               switch_mutex_lock(tech_pvt->transports[LDL_TPORT_RTP].read_codec.mutex);
+               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
+                       switch_rtp_kill_socket(tech_pvt->transports[LDL_TPORT_RTP].rtp_session);
+                       switch_rtp_destroy(&tech_pvt->transports[LDL_TPORT_RTP].rtp_session);
+               }
 
-       switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec);
-       switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec);
 
-       if (globals.auto_nat && tech_pvt->profile->local_network && !switch_check_network_list_ip(tech_pvt->remote_ip, tech_pvt->profile->local_network)) {
+       } else {
+               if (switch_core_codec_init(&tech_pvt->transports[LDL_TPORT_RTP].read_codec,
+                                                                  tech_pvt->transports[LDL_TPORT_RTP].codec_name,
+                                                                  NULL,
+                                                                  tech_pvt->transports[LDL_TPORT_RTP].codec_rate,
+                                                                  ms,
+                                                                  1,
+                                                                  SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+                                                                  NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Can't load codec?\n");
+                       switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                       r = 0;
+                       goto end;
+               }
+               tech_pvt->transports[LDL_TPORT_RTP].read_frame.rate = tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_second;
+               tech_pvt->transports[LDL_TPORT_RTP].read_frame.codec = &tech_pvt->transports[LDL_TPORT_RTP].read_codec;
+
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Read Codec to %s@%d\n",
+                                                 tech_pvt->transports[LDL_TPORT_RTP].codec_name, (int) tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_second);
+
+               if (switch_core_codec_init(&tech_pvt->transports[LDL_TPORT_RTP].write_codec,
+                                                                  tech_pvt->transports[LDL_TPORT_RTP].codec_name,
+                                                                  NULL,
+                                                                  tech_pvt->transports[LDL_TPORT_RTP].codec_rate,
+                                                                  ms,
+                                                                  1,
+                                                                  SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+                                                                  NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Can't load codec?\n");
+                       switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                       r = 0;
+                       goto end;
+               }
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Write Codec to %s@%d\n",
+                                                 tech_pvt->transports[LDL_TPORT_RTP].codec_name, (int) tech_pvt->transports[LDL_TPORT_RTP].write_codec.implementation->samples_per_second);
+               
+               switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->transports[LDL_TPORT_RTP].read_codec);
+               switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->transports[LDL_TPORT_RTP].write_codec);
+       }
+
+       if (globals.auto_nat && tech_pvt->profile->local_network && !switch_check_network_list_ip(tech_pvt->transports[LDL_TPORT_RTP].remote_ip, tech_pvt->profile->local_network)) {
                switch_port_t external_port = 0;
-               switch_nat_add_mapping((switch_port_t) tech_pvt->local_port, SWITCH_NAT_UDP, &external_port, SWITCH_FALSE);
+               switch_nat_add_mapping((switch_port_t) tech_pvt->transports[LDL_TPORT_RTP].local_port, SWITCH_NAT_UDP, &external_port, SWITCH_FALSE);
 
                if (external_port) {
-                       tech_pvt->adv_local_port = external_port;
+                       tech_pvt->transports[LDL_TPORT_RTP].adv_local_port = external_port;
                        switch_set_flag(tech_pvt, TFLAG_NAT_MAP);
                } else {
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "NAT mapping returned 0. Run freeswitch with -nonat since it's not working right.\n");
                }
        }
 
-       if (tech_pvt->adv_local_port != tech_pvt->local_port) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "SETUP RTP %s:%d(%d) -> %s:%d\n", tech_pvt->profile->ip,
-                                                 tech_pvt->local_port, tech_pvt->adv_local_port, tech_pvt->remote_ip, tech_pvt->remote_port);
+       if (tech_pvt->transports[LDL_TPORT_RTP].adv_local_port != tech_pvt->transports[LDL_TPORT_RTP].local_port) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "SETUP AUDIO RTP %s:%d(%d) -> %s:%d codec: %s(%d) %dh %di\n", 
+                                                 tech_pvt->profile->ip,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].local_port, 
+                                                 tech_pvt->transports[LDL_TPORT_RTP].adv_local_port, 
+                                                 tech_pvt->transports[LDL_TPORT_RTP].remote_ip, 
+                                                 tech_pvt->transports[LDL_TPORT_RTP].remote_port,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->iananame,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->ianacode,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_packet,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->microseconds_per_packet
+                                                 
+                                                 );
        } else {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "SETUP RTP %s:%d -> %s:%d\n", tech_pvt->profile->ip,
-                                                 tech_pvt->local_port, tech_pvt->remote_ip, tech_pvt->remote_port);
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "SETUP AUDIO RTP %s:%d -> %s:%d codec: %s(%d) %dh %di\n", 
+                                                 tech_pvt->profile->ip,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].local_port, 
+                                                 tech_pvt->transports[LDL_TPORT_RTP].remote_ip, 
+                                                 tech_pvt->transports[LDL_TPORT_RTP].remote_port,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->iananame,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->ianacode,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_packet,
+                                                 tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->microseconds_per_packet
+                                                 );
        }
 
        flags = SWITCH_RTP_FLAG_DATAWAIT | SWITCH_RTP_FLAG_GOOGLEHACK | SWITCH_RTP_FLAG_AUTOADJ | SWITCH_RTP_FLAG_RAW_WRITE | SWITCH_RTP_FLAG_AUTO_CNG;
@@ -947,128 +1200,390 @@ static int activate_rtp(struct private_object *tech_pvt)
                flags &= ~SWITCH_RTP_FLAG_AUTOADJ;
        }
 
-       if (!(tech_pvt->rtp_session = switch_rtp_new(tech_pvt->profile->ip,
-                                                                                                tech_pvt->local_port,
-                                                                                                tech_pvt->remote_ip,
-                                                                                                tech_pvt->remote_port,
-                                                                                                tech_pvt->codec_num,
-                                                                                                tech_pvt->read_codec.implementation->samples_per_packet,
-                                                                                                tech_pvt->read_codec.implementation->microseconds_per_packet,
-                                                                                                flags, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)))) {
+       if (!(tech_pvt->transports[LDL_TPORT_RTP].rtp_session = switch_rtp_new(tech_pvt->profile->ip,
+                                                                                                                                                  tech_pvt->transports[LDL_TPORT_RTP].local_port,
+                                                                                                                                                  tech_pvt->transports[LDL_TPORT_RTP].remote_ip,
+                                                                                                                                                  tech_pvt->transports[LDL_TPORT_RTP].remote_port,
+                                                                                                                                                  tech_pvt->transports[LDL_TPORT_RTP].codec_num,
+                                                                                                                                                  tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_packet,
+                                                                                                                                                  tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->microseconds_per_packet,
+                                                                                                                                                  flags, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)))) {
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "RTP ERROR %s\n", err);
                switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-               return 0;
+               r = 0;
+               goto end;
        } else {
                uint8_t vad_in = switch_test_flag(tech_pvt, TFLAG_VAD_IN) ? 1 : 0;
                uint8_t vad_out = switch_test_flag(tech_pvt, TFLAG_VAD_OUT) ? 1 : 0;
                uint8_t inb = switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? 0 : 1;
-               switch_rtp_activate_ice(tech_pvt->rtp_session, tech_pvt->remote_user, tech_pvt->local_user);
+
+               switch_rtp_set_ssrc(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, tech_pvt->transports[LDL_TPORT_RTP].ssrc);
+
+               if (tech_pvt->transports[LDL_TPORT_RTCP].remote_port) {
+                       switch_rtp_activate_rtcp(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, MDL_RTCP_DUR, 
+                                                                        tech_pvt->transports[LDL_TPORT_RTCP].remote_port);
+
+               }
+
+               try_secure(tech_pvt, LDL_TPORT_RTP);
+
+               switch_rtp_activate_ice(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, 
+                                                               tech_pvt->transports[LDL_TPORT_RTP].remote_user, 
+                                                               tech_pvt->transports[LDL_TPORT_RTP].local_user,
+                                                               tech_pvt->transports[LDL_TPORT_RTP].remote_pass);
+               
                if ((vad_in && inb) || (vad_out && !inb)) {
-                       if (switch_rtp_enable_vad(tech_pvt->rtp_session, tech_pvt->session, &tech_pvt->read_codec, SWITCH_VAD_FLAG_TALKING) != SWITCH_STATUS_SUCCESS) {
+                       if (switch_rtp_enable_vad(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, tech_pvt->session, &tech_pvt->transports[LDL_TPORT_RTP].read_codec, SWITCH_VAD_FLAG_TALKING) != SWITCH_STATUS_SUCCESS) {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "VAD ERROR %s\n", err);
                                switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                               return 0;
+                               r = 0;
+                               goto end;
                        }
                        switch_set_flag_locked(tech_pvt, TFLAG_VAD);
                }
-               switch_rtp_set_cng_pt(tech_pvt->rtp_session, 13);
-               switch_rtp_set_telephony_event(tech_pvt->rtp_session, 101);
+               //switch_rtp_set_cng_pt(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, 13);
+               switch_rtp_set_telephony_event(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, 101);
+               
+               if (tech_pvt->transports[LDL_TPORT_RTCP].remote_port) {
+                       switch_rtp_activate_rtcp_ice(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, 
+                                                                                tech_pvt->transports[LDL_TPORT_RTCP].remote_user, 
+                                                                                tech_pvt->transports[LDL_TPORT_RTCP].local_user,
+                                                                                tech_pvt->transports[LDL_TPORT_RTCP].remote_pass);
+                       
+               }
+
+               
+
        }
 
-       return 1;
-}
+ end:
 
+       if (locked) {
+               switch_mutex_unlock(tech_pvt->transports[LDL_TPORT_RTP].read_codec.mutex);
+       }
 
+       return r;
+}
 
-static int do_candidates(struct private_object *tech_pvt, int force)
+
+static int activate_video_rtp(struct private_object *tech_pvt)
 {
        switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session);
+       const char *err;
+       int ms = 0;
+       switch_rtp_flag_t flags;
+       int r = 1, locked = 0;
 
-       if (switch_test_flag(tech_pvt, TFLAG_DO_CAND)) {
-               return 1;
+       
+       if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session)) {
+                       r = 1; goto end;
        }
 
-       tech_pvt->next_cand += DL_CAND_WAIT;
-       if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
-               return 0;
+       if (!(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_ip && tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_port)) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "No valid video_rtp candidates received!\n");
+               r = 0; goto end;
        }
-       switch_set_flag_locked(tech_pvt, TFLAG_DO_CAND);
 
-       if (force || !switch_test_flag(tech_pvt, TFLAG_RTP_READY)) {
-               ldl_candidate_t cand[1];
-               char *advip = tech_pvt->profile->extip ? tech_pvt->profile->extip : tech_pvt->profile->ip;
-               char *err = NULL, *address = NULL;
+       if (zstr(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_name)) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "No valid video_rtp codecs received!\n");
+               r = 0; goto end;                
+       }
+
+       if (switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec)) {
+               locked = 1;
+               switch_mutex_lock(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.mutex);
+               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session)) {
+                       switch_rtp_kill_socket(tech_pvt->transports[LDL_TPORT_RTP].rtp_session);
+                       switch_rtp_destroy(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session);
+               }
+
+
 
-               memset(cand, 0, sizeof(cand));
-               switch_stun_random_string(tech_pvt->local_user, 16, NULL);
-               switch_stun_random_string(tech_pvt->local_pass, 16, NULL);
 
-               if (switch_test_flag(tech_pvt, TFLAG_LANADDR)) {
-                       advip = tech_pvt->profile->ip;
+
+       } else {
+               if (switch_core_codec_init(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec,
+                                                                  tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_name,
+                                                                  NULL,
+                                                                  tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_rate,
+                                                                  ms,
+                                                                  1,
+                                                                  SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+                                                                  NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Can't load codec?\n");
+                       switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                       r = 0; goto end;
+               }
+               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.rate = tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->samples_per_second;
+               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.codec = &tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec;
+
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Read Codec to %s@%d\n",
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_name, (int) tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->samples_per_second);
+
+               if (switch_core_codec_init(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].write_codec,
+                                                                  tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_name,
+                                                                  NULL,
+                                                                  tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_rate,
+                                                                  ms,
+                                                                  1,
+                                                                  SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+                                                                  NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Can't load codec?\n");
+                       switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                       r = 0; goto end;
                }
-               address = advip;
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Write Codec to %s@%d\n",
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_name, (int) tech_pvt->transports[LDL_TPORT_VIDEO_RTP].write_codec.implementation->samples_per_second);
 
-               if(address && !strncasecmp(address, "host:", 5)) {
-                       address = address + 5;
+               switch_core_session_set_video_read_codec(tech_pvt->session, &tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec);
+               switch_core_session_set_video_write_codec(tech_pvt->session, &tech_pvt->transports[LDL_TPORT_VIDEO_RTP].write_codec);
+       }
+
+       if (globals.auto_nat && tech_pvt->profile->local_network && !switch_check_network_list_ip(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_ip, tech_pvt->profile->local_network)) {
+               switch_port_t external_port = 0;
+               switch_nat_add_mapping((switch_port_t) tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port, SWITCH_NAT_UDP, &external_port, SWITCH_FALSE);
+
+               if (external_port) {
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].adv_local_port = external_port;
+                       switch_set_flag(tech_pvt, TFLAG_NAT_MAP);
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "NAT mapping returned 0. Run freeswitch with -nonat since it's not working right.\n");
                }
+       }
 
-               cand[0].port = tech_pvt->adv_local_port;
-               cand[0].address = address;
 
-               if (!strncasecmp(advip, "stun:", 5)) {
-                       char *stun_ip = advip + 5;
+       if (tech_pvt->transports[LDL_TPORT_VIDEO_RTP].adv_local_port != tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "SETUP VIDEO RTP %s:%d(%d) -> %s:%d codec: %s(%d) %dh %di\n", 
+                                                 tech_pvt->profile->ip,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port, 
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].adv_local_port, 
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_ip, 
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_port,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->iananame,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->ianacode,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->samples_per_packet,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->microseconds_per_packet
+                                                 
+                                                 );
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "SETUP VIDEO RTP %s:%d -> %s:%d codec: %s(%d) %dh %di\n", 
+                                                 tech_pvt->profile->ip,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port, 
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_ip, 
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_port,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->iananame,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->ianacode,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->samples_per_packet,
+                                                 tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation->microseconds_per_packet
+                                                 );
+       }
 
-                       if (tech_pvt->stun_ip) {
-                               cand[0].address = tech_pvt->stun_ip;
-                               cand[0].port = tech_pvt->stun_port;
-                       } else {
-                               if (!stun_ip) {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Stun Failed! NO STUN SERVER!\n");
-                                       switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                                       return 0;
-                               }
 
-                               cand[0].address = tech_pvt->profile->ip;
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Stun Lookup Local %s:%d\n", cand[0].address,
-                                                                 cand[0].port);
-                               if (switch_stun_lookup
-                                       (&cand[0].address, &cand[0].port, stun_ip, SWITCH_STUN_DEFAULT_PORT, &err,
-                                        switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Stun Failed! %s:%d [%s]\n", stun_ip,
-                                                                         SWITCH_STUN_DEFAULT_PORT, err);
-                                       switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                                       return 0;
-                               }
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_INFO, "Stun Success %s:%d\n", cand[0].address, cand[0].port);
-                       }
-                       cand[0].type = "stun";
-                       tech_pvt->stun_ip = switch_core_session_strdup(tech_pvt->session, cand[0].address);
-                       tech_pvt->stun_port = cand[0].port;
+       flags = SWITCH_RTP_FLAG_DATAWAIT | SWITCH_RTP_FLAG_GOOGLEHACK | SWITCH_RTP_FLAG_AUTOADJ | SWITCH_RTP_FLAG_RAW_WRITE | SWITCH_RTP_FLAG_VIDEO;
+
+
+       if (switch_true(switch_channel_get_variable(channel, "disable_rtp_auto_adjust"))) {
+               flags &= ~SWITCH_RTP_FLAG_AUTOADJ;
+       }
+
+       if (!(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session = switch_rtp_new(tech_pvt->profile->ip,
+                                                                                                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port,
+                                                                                                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_ip,
+                                                                                                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_port,
+                                                                                                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_num,
+                                                                                                                                                                1,
+                                                                                                                                                                90000,
+                                                                                                                                                                flags, NULL, &err, switch_core_session_get_pool(tech_pvt->session)))) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "RTP ERROR %s\n", err);
+               switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               r = 0; goto end;
+       } else {
+               switch_rtp_set_ssrc(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, tech_pvt->transports[LDL_TPORT_VIDEO_RTP].ssrc);
+
+               if (tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].remote_port) {
+                       switch_rtp_activate_rtcp(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, MDL_RTCP_DUR, 
+                                                                        tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].remote_port);
+               }
+               try_secure(tech_pvt, LDL_TPORT_VIDEO_RTP);
+
+
+               switch_rtp_activate_ice(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, 
+                                                               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_user, 
+                                                               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_user,
+                                                               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].remote_pass);
+               switch_channel_set_flag(channel, CF_VIDEO);
+               //switch_rtp_set_default_payload(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, tech_pvt->transports[LDL_TPORT_VIDEO_RTP].r_codec_num);
+               
+
+               if (tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].remote_port) {
+                       
+                       switch_rtp_activate_rtcp_ice(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, 
+                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].remote_user, 
+                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].local_user,
+                                                                                tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].remote_pass);
+               }
+
+
+               
+       }
+
+ end:
+       if (locked) {
+               switch_mutex_unlock(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.mutex);
+       }
+
+       return r;
+}
+
+
+
+static int activate_rtp(struct private_object *tech_pvt)
+{
+       int r = 0;
+
+       if (tech_pvt->transports[LDL_TPORT_RTP].ready) {
+               r += activate_audio_rtp(tech_pvt);
+       }
+
+       if (tech_pvt->transports[LDL_TPORT_VIDEO_RTP].ready) {
+               r += activate_video_rtp(tech_pvt);
+       }
+
+       return r;
+}
+
+
+static int do_tport_candidates(struct private_object *tech_pvt, ldl_transport_type_t ttype, ldl_candidate_t *cand, int force)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session);
+       char *advip = tech_pvt->profile->extip ? tech_pvt->profile->extip : tech_pvt->profile->ip;
+       char *err = NULL, *address = NULL;
+       
+       if (!force && tech_pvt->transports[ttype].ready) {
+               return 0;
+       }
+
+       if (switch_test_flag(tech_pvt, TFLAG_LANADDR)) {
+               advip = tech_pvt->profile->ip;
+       }
+       address = advip;
+
+       if(address && !strncasecmp(address, "host:", 5)) {
+               address = address + 5;
+       }
+
+       memset(cand, 0, sizeof(*cand));
+       switch_stun_random_string(tech_pvt->transports[ttype].local_user, 16, NULL);
+       switch_stun_random_string(tech_pvt->transports[ttype].local_pass, 16, NULL);
+
+       cand->port = tech_pvt->transports[ttype].adv_local_port;
+       cand->address = address;
+
+       if (!strncasecmp(advip, "stun:", 5)) {
+               char *stun_ip = advip + 5;
+
+               if (tech_pvt->transports[ttype].stun_ip) {
+                       cand->address = tech_pvt->transports[ttype].stun_ip;
+                       cand->port = tech_pvt->transports[ttype].stun_port;
                } else {
-                       cand[0].type = "local";
+                       if (!stun_ip) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Stun Failed! NO STUN SERVER!\n");
+                               switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                               return 0;
+                       }
+
+                       cand->address = tech_pvt->profile->ip;
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Stun Lookup Local %s:%d\n", cand->address,
+                                                         cand->port);
+                       if (switch_stun_lookup
+                               (&cand->address, &cand->port, stun_ip, SWITCH_STUN_DEFAULT_PORT, &err,
+                                switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Stun Failed! %s:%d [%s]\n", stun_ip,
+                                                                 SWITCH_STUN_DEFAULT_PORT, err);
+                               switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                               return 0;
+                       }
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_INFO, "Stun Success %s:%d\n", cand->address, cand->port);
                }
+               cand->type = "stun";
+               tech_pvt->transports[ttype].stun_ip = switch_core_session_strdup(tech_pvt->session, cand->address);
+               tech_pvt->transports[ttype].stun_port = cand->port;
+       } else {
+               cand->type = "local";
+       }
+
+       cand->name = (char *)ldl_transport_type_str(ttype);
+       cand->username = tech_pvt->transports[ttype].local_user;
+       cand->password = tech_pvt->transports[ttype].local_pass;
+       cand->pref = 1;
+       cand->protocol = "udp";
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, 
+                                         "Send %s Candidate %s:%d [%s]\n", ldl_transport_type_str(ttype), cand->address, cand->port,
+                                         cand->username);
+
+
+               
+       tech_pvt->transports[ttype].ready = 1;
+       
+       return 1;
+}
+
+
+static int do_candidates(struct private_object *tech_pvt, int force)
+{
+       ldl_candidate_t cand[4] = {{0}};
+       int idx = 0;
+
+       if (switch_test_flag(tech_pvt, TFLAG_DO_CAND)) {
+               return 1;
+       }
+
+       tech_pvt->next_cand += DL_CAND_WAIT;
+       if (switch_test_flag(tech_pvt, TFLAG_BYE) || !tech_pvt->dlsession) {
+               return 0;
+       }
+       switch_set_flag_locked(tech_pvt, TFLAG_DO_CAND);
 
-               cand[0].name = "rtp";
-               cand[0].username = tech_pvt->local_user;
-               cand[0].password = tech_pvt->local_pass;
-               cand[0].pref = 1;
-               cand[0].protocol = "udp";
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Send Candidate %s:%d [%s]\n", cand[0].address, cand[0].port,
-                                                 cand[0].username);
+       idx += do_tport_candidates(tech_pvt, LDL_TPORT_RTP, &cand[idx], force);
+       idx += do_tport_candidates(tech_pvt, LDL_TPORT_RTCP, &cand[idx], force);
+       idx += do_tport_candidates(tech_pvt, LDL_TPORT_VIDEO_RTP, &cand[idx], force);
+       idx += do_tport_candidates(tech_pvt, LDL_TPORT_VIDEO_RTCP, &cand[idx], force);
 
+       if (idx && cand[0].name) {
                if (ldl_session_gateway(tech_pvt->dlsession) && switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
-                       tech_pvt->cand_id = ldl_session_transport(tech_pvt->dlsession, cand, 1);
+                       tech_pvt->cand_id = ldl_session_transport(tech_pvt->dlsession, cand, idx);
                } else {
-                       tech_pvt->cand_id = ldl_session_candidates(tech_pvt->dlsession, cand, 1);
+                       tech_pvt->cand_id = ldl_session_candidates(tech_pvt->dlsession, cand, idx);
                }
+       }
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Accepted %u of %u rtp candidates.\n", 
+                                         tech_pvt->transports[LDL_TPORT_RTP].accepted, tech_pvt->transports[LDL_TPORT_RTP].total);
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Accepted %u of %u rtcp candidates.\n", 
+                                         tech_pvt->transports[LDL_TPORT_RTCP].accepted, tech_pvt->transports[LDL_TPORT_RTCP].total);
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Accepted %u of %u video_rtp candidates\n", 
+                                         tech_pvt->transports[LDL_TPORT_VIDEO_RTP].accepted, tech_pvt->transports[LDL_TPORT_VIDEO_RTP].total);
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Accepted %u of %u video_rctp candidates\n", 
+                                         tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].accepted, tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].total);
 
+
+
+       if ((tech_pvt->transports[LDL_TPORT_RTP].ready && tech_pvt->transports[LDL_TPORT_RTCP].ready)) {
                switch_set_flag_locked(tech_pvt, TFLAG_TRANSPORT);
                switch_set_flag_locked(tech_pvt, TFLAG_RTP_READY);
        }
+
+
        switch_clear_flag_locked(tech_pvt, TFLAG_DO_CAND);
        return 1;
+
 }
 
+
+
+
 static char *lame(char *in)
 {
        if (!strncasecmp(in, "ilbc", 4)) {
@@ -1078,25 +1593,124 @@ static char *lame(char *in)
        }
 }
 
+
+static void setup_codecs(struct private_object *tech_pvt)
+{
+       ldl_payload_t payloads[LDL_MAX_PAYLOADS] = { {0} };
+       int idx = 0, i = 0;
+       int dft_audio = -1, dft_video = -1;
+
+       memset(payloads, 0, sizeof(payloads));
+
+       for (idx = 0; idx < tech_pvt->num_codecs && (dft_audio == -1 || dft_video == -1); idx++) {
+               if (dft_audio < 0 && tech_pvt->codecs[idx]->codec_type == SWITCH_CODEC_TYPE_AUDIO) {
+                       dft_audio = idx;
+               }
+               if (dft_video < 0 && tech_pvt->codecs[idx]->codec_type == SWITCH_CODEC_TYPE_VIDEO) {
+                       dft_video = idx;
+               }
+       }
+       
+       if (dft_audio == -1 && dft_video == -1) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Cannot find a codec.\n");  
+               return;
+       }
+
+       idx = 0;
+
+       payloads[0].type = LDL_TPORT_RTP;
+       if (tech_pvt->transports[LDL_TPORT_RTP].codec_index < 0) {
+               if (dft_audio > -1) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Don't have my audio codec yet here's one\n");
+                       tech_pvt->transports[LDL_TPORT_RTP].codec_name = lame(tech_pvt->codecs[dft_audio]->iananame);
+                       tech_pvt->transports[LDL_TPORT_RTP].codec_num = tech_pvt->codecs[dft_audio]->ianacode;
+                       tech_pvt->transports[LDL_TPORT_RTP].codec_rate = tech_pvt->codecs[dft_audio]->samples_per_second;
+                       tech_pvt->transports[LDL_TPORT_RTP].r_codec_num = tech_pvt->codecs[dft_audio]->ianacode;
+                       tech_pvt->transports[LDL_TPORT_RTP].codec_index = dft_audio;
+
+                       payloads[0].name = lame(tech_pvt->codecs[dft_audio]->iananame);
+                       payloads[0].id = tech_pvt->codecs[dft_audio]->ianacode;
+                       payloads[0].rate = tech_pvt->codecs[dft_audio]->samples_per_second;
+                       payloads[0].bps = tech_pvt->codecs[dft_audio]->bits_per_second;
+                       payloads[0].ptime = tech_pvt->codecs[dft_audio]->microseconds_per_packet / 1000;
+                       idx++;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Don't have an audio codec.\n");
+               }
+       } else {
+               payloads[0].name = lame(tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_RTP].codec_index]->iananame);
+               payloads[0].id = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_RTP].codec_index]->ianacode;
+               payloads[0].rate = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_RTP].codec_index]->samples_per_second;
+               payloads[0].bps = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_RTP].codec_index]->bits_per_second;
+               payloads[0].ptime = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_RTP].codec_index]->microseconds_per_packet / 1000;
+               idx++;
+       }
+
+
+       payloads[1].type = LDL_TPORT_VIDEO_RTP;
+       if (tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index < 0) {
+               if (dft_video > -1) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Don't have my video codec yet here's one\n");
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_name = lame(tech_pvt->codecs[dft_video]->iananame);
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_num = tech_pvt->codecs[dft_video]->ianacode;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_rate = tech_pvt->codecs[dft_video]->samples_per_second;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].r_codec_num = tech_pvt->codecs[dft_video]->ianacode;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index = dft_video;
+
+                       payloads[1].name = lame(tech_pvt->codecs[dft_video]->iananame);
+                       payloads[1].id = tech_pvt->codecs[dft_video]->ianacode;
+                       payloads[1].rate = tech_pvt->codecs[dft_video]->samples_per_second;
+                       payloads[1].bps = tech_pvt->codecs[dft_video]->bits_per_second;
+                       payloads[1].width = 600;
+                       payloads[1].height = 400;
+                       payloads[1].framerate = 30;
+                       
+                       idx++;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Don't have video codec.\n");
+               }
+       } else {
+               payloads[1].name = lame(tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index]->iananame);
+               payloads[1].id = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index]->ianacode;
+               payloads[1].rate = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index]->samples_per_second;
+               payloads[1].bps = tech_pvt->codecs[tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index]->bits_per_second;
+               idx++;
+       }
+
+       for(i = 0; i < idx; i++) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Send Describe [%s@%d]\n", payloads[i].name, payloads[i].rate);
+       }
+
+
+       if (!payloads[1].id) {
+               switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port);
+               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port = 0;
+       }
+
+
+       tech_pvt->desc_id = ldl_session_describe(tech_pvt->dlsession, payloads, idx,
+                                                                                        switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? LDL_DESCRIPTION_INITIATE : LDL_DESCRIPTION_ACCEPT,
+                                                                                        &tech_pvt->transports[LDL_TPORT_RTP].ssrc, &tech_pvt->transports[LDL_TPORT_VIDEO_RTP].ssrc, 
+                                                                                        tech_pvt->transports[LDL_TPORT_RTP].local_crypto_data, 
+                                                                                        tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_crypto_data);
+
+
+}
+
 static int do_describe(struct private_object *tech_pvt, int force)
 {
-       ldl_payload_t payloads[5];
 
        if (!tech_pvt->session) {
                return 0;
        }
 
-       if (switch_test_flag(tech_pvt, TFLAG_DO_DESC)) {
-               return 1;
-       }
-
        tech_pvt->next_desc += DL_CAND_WAIT;
 
        if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
                return 0;
        }
 
-       memset(payloads, 0, sizeof(payloads));
+
        switch_set_flag_locked(tech_pvt, TFLAG_DO_CAND);
        if (!get_codecs(tech_pvt)) {
                terminate_session(&tech_pvt->session, __LINE__, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
@@ -1107,31 +1721,7 @@ static int do_describe(struct private_object *tech_pvt, int force)
 
 
        if (force || !switch_test_flag(tech_pvt, TFLAG_CODEC_READY)) {
-               if (tech_pvt->codec_index < 0) {
-                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Don't have my codec yet here's one\n");
-                       tech_pvt->codec_name = lame(tech_pvt->codecs[0]->iananame);
-                       tech_pvt->codec_num = tech_pvt->codecs[0]->ianacode;
-                       tech_pvt->codec_rate = tech_pvt->codecs[0]->samples_per_second;
-                       tech_pvt->r_codec_num = tech_pvt->codecs[0]->ianacode;
-                       tech_pvt->codec_index = 0;
-
-                       payloads[0].name = lame(tech_pvt->codecs[0]->iananame);
-                       payloads[0].id = tech_pvt->codecs[0]->ianacode;
-                       payloads[0].rate = tech_pvt->codecs[0]->samples_per_second;
-                       payloads[0].bps = tech_pvt->codecs[0]->bits_per_second;
-
-               } else {
-                       payloads[0].name = lame(tech_pvt->codecs[tech_pvt->codec_index]->iananame);
-                       payloads[0].id = tech_pvt->codecs[tech_pvt->codec_index]->ianacode;
-                       payloads[0].rate = tech_pvt->codecs[tech_pvt->codec_index]->samples_per_second;
-                       payloads[0].bps = tech_pvt->codecs[tech_pvt->codec_index]->bits_per_second;
-               }
-
-
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Send Describe [%s@%d]\n", payloads[0].name, payloads[0].rate);
-               tech_pvt->desc_id =
-                       ldl_session_describe(tech_pvt->dlsession, payloads, 1,
-                                                                switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? LDL_DESCRIPTION_INITIATE : LDL_DESCRIPTION_ACCEPT);
+               setup_codecs(tech_pvt);
                switch_set_flag_locked(tech_pvt, TFLAG_CODEC_READY);
        }
        switch_clear_flag_locked(tech_pvt, TFLAG_DO_CAND);
@@ -1167,8 +1757,8 @@ static switch_status_t negotiate_media(switch_core_session_t *session)
 
        while (!(switch_test_flag(tech_pvt, TFLAG_CODEC_READY) &&
                         switch_test_flag(tech_pvt, TFLAG_RTP_READY) &&
-                        switch_test_flag(tech_pvt, TFLAG_ANSWER) && switch_test_flag(tech_pvt, TFLAG_TRANSPORT_ACCEPT) &&
-                        tech_pvt->remote_ip && tech_pvt->remote_port && switch_test_flag(tech_pvt, TFLAG_TRANSPORT))) {
+                        switch_test_flag(tech_pvt, TFLAG_ANSWER) && switch_test_flag(tech_pvt, TFLAG_TRANSPORT_ACCEPT) && //tech_pvt->read_count &&
+                        tech_pvt->transports[LDL_TPORT_RTP].remote_ip && tech_pvt->transports[LDL_TPORT_RTP].remote_port && switch_test_flag(tech_pvt, TFLAG_TRANSPORT))) {
                now = switch_micro_time_now();
                elapsed = (unsigned int) ((now - started) / 1000);
 
@@ -1195,10 +1785,20 @@ static switch_status_t negotiate_media(switch_core_session_t *session)
                        switch_clear_flag_locked(tech_pvt, TFLAG_IO);
                        goto done;
                }
+
                if (switch_test_flag(tech_pvt, TFLAG_BYE) || !switch_test_flag(tech_pvt, TFLAG_IO)) {
                        goto done;
                }
-               switch_cond_next();
+
+               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
+                       switch_rtp_ping(tech_pvt->transports[LDL_TPORT_RTP].rtp_session);
+               }
+
+               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session)) {
+                       switch_rtp_ping(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session);
+               }               
+
+               switch_yield(20000);
        }
 
        if (switch_channel_down(channel) || switch_test_flag(tech_pvt, TFLAG_BYE)) {
@@ -1219,6 +1819,9 @@ static switch_status_t negotiate_media(switch_core_session_t *session)
        }
        ret = SWITCH_STATUS_SUCCESS;
 
+       switch_channel_audio_sync(channel); 
+
+
        goto done;
 
   out:
@@ -1241,7 +1844,7 @@ static switch_status_t channel_on_init(switch_core_session_t *session)
        tech_pvt = switch_core_session_get_private(session);
        switch_assert(tech_pvt != NULL);
 
-       tech_pvt->read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+       tech_pvt->transports[LDL_TPORT_RTP].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
 
        switch_set_flag(tech_pvt, TFLAG_READY);
 
@@ -1295,23 +1898,37 @@ static switch_status_t channel_on_destroy(switch_core_session_t *session)
        tech_pvt = switch_core_session_get_private(session);
 
        if (tech_pvt) {
-               if (tech_pvt->rtp_session) {
-                       switch_rtp_destroy(&tech_pvt->rtp_session);
+               if (tech_pvt->transports[LDL_TPORT_RTP].rtp_session) {
+                       switch_rtp_destroy(&tech_pvt->transports[LDL_TPORT_RTP].rtp_session);
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NUKE RTP\n");
+                       tech_pvt->transports[LDL_TPORT_RTP].rtp_session = NULL;
+               }
+
+               if (tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session) {
+                       switch_rtp_destroy(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session);
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NUKE RTP\n");
-                       tech_pvt->rtp_session = NULL;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session = NULL;
                }
 
                if (switch_test_flag(tech_pvt, TFLAG_NAT_MAP)) {
-                       switch_nat_del_mapping((switch_port_t) tech_pvt->adv_local_port, SWITCH_NAT_UDP);
+                       switch_nat_del_mapping((switch_port_t) tech_pvt->transports[LDL_TPORT_RTP].adv_local_port, SWITCH_NAT_UDP);
                        switch_clear_flag(tech_pvt, TFLAG_NAT_MAP);
                }
 
-               if (switch_core_codec_ready(&tech_pvt->read_codec)) {
-                       switch_core_codec_destroy(&tech_pvt->read_codec);
+               if (switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_RTP].read_codec)) {
+                       switch_core_codec_destroy(&tech_pvt->transports[LDL_TPORT_RTP].read_codec);
                }
 
-               if (switch_core_codec_ready(&tech_pvt->write_codec)) {
-                       switch_core_codec_destroy(&tech_pvt->write_codec);
+               if (switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_RTP].write_codec)) {
+                       switch_core_codec_destroy(&tech_pvt->transports[LDL_TPORT_RTP].write_codec);
+               }
+
+               if (switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec)) {
+                       switch_core_codec_destroy(&tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec);
+               }
+
+               if (switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_RTP].write_codec)) {
+                       switch_core_codec_destroy(&tech_pvt->transports[LDL_TPORT_RTP].write_codec);
                }
 
                if (tech_pvt->dlsession) {
@@ -1340,8 +1957,12 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session)
        tech_pvt = switch_core_session_get_private(session);
        switch_assert(tech_pvt != NULL);
 
-       if (tech_pvt->profile->ip && tech_pvt->local_port) {
-               switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->local_port);
+       if (tech_pvt->profile->ip && tech_pvt->transports[LDL_TPORT_RTP].local_port) {
+               switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->transports[LDL_TPORT_RTP].local_port);
+       }
+
+       if (tech_pvt->profile->ip && tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port) {
+               switch_rtp_release_port(tech_pvt->profile->ip, tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port);
        }
 
        switch_clear_flag_locked(tech_pvt, TFLAG_IO);
@@ -1382,13 +2003,13 @@ static switch_status_t channel_kill_channel(switch_core_session_t *session, int
                switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
                switch_set_flag_locked(tech_pvt, TFLAG_BYE);
 
-               if (switch_rtp_ready(tech_pvt->rtp_session)) {
-                       switch_rtp_kill_socket(tech_pvt->rtp_session);
+               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
+                       switch_rtp_kill_socket(tech_pvt->transports[LDL_TPORT_RTP].rtp_session);
                }
                break;
        case SWITCH_SIG_BREAK:
-               if (switch_rtp_ready(tech_pvt->rtp_session)) {
-                       switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_BREAK);
+               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
+                       switch_rtp_set_flag(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, SWITCH_RTP_FLAG_BREAK);
                }
                break;
        }
@@ -1421,7 +2042,7 @@ static switch_status_t channel_send_dtmf(switch_core_session_t *session, const s
 
        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "DTMF [%c]\n", dtmf->digit);
 
-       return switch_rtp_queue_rfc2833(tech_pvt->rtp_session, dtmf);
+       return switch_rtp_queue_rfc2833(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, dtmf);
 
 }
 
@@ -1434,7 +2055,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
        tech_pvt = (struct private_object *) switch_core_session_get_private(session);
        switch_assert(tech_pvt != NULL);
 
-       while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) {
+       while (!(tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation && switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session))) {
                if (switch_channel_ready(channel)) {
                        switch_yield(10000);
                } else {
@@ -1443,7 +2064,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
        }
 
 
-       tech_pvt->read_frame.datalen = 0;
+       tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen = 0;
        switch_set_flag_locked(tech_pvt, TFLAG_READING);
 
 #if 0
@@ -1457,23 +2078,25 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
 
 
        if (switch_test_flag(tech_pvt, TFLAG_IO)) {
-               switch_status_t status;
+               //switch_status_t status;
+               
+               switch_assert(tech_pvt->transports[LDL_TPORT_RTP].rtp_session != NULL);
+               tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen = 0;
 
-               switch_assert(tech_pvt->rtp_session != NULL);
-               tech_pvt->read_frame.datalen = 0;
 
+               while (switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen == 0) {
+                       tech_pvt->transports[LDL_TPORT_RTP].read_frame.flags = SFF_NONE;
 
-               while (switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
-                       tech_pvt->read_frame.flags = SFF_NONE;
+                       switch_rtp_zerocopy_read_frame(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, &tech_pvt->transports[LDL_TPORT_RTP].read_frame, flags);
 
-                       status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags);
-                       if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
-                               return SWITCH_STATUS_FALSE;
+                       tech_pvt->read_count++;
+#if 0
+                       if (tech_pvt->read_count == 1 && !switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+                               setup_codecs(tech_pvt);
                        }
+#endif
 
-
-
-                       //payload = tech_pvt->read_frame.payload;
+                       //payload = tech_pvt->transports[LDL_TPORT_RTP].read_frame.payload;
 
 #if 0
                        elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000);
@@ -1489,22 +2112,22 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
                                return SWITCH_STATUS_BREAK;
                        }
 #endif
-                       if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) {
+                       if (switch_rtp_has_dtmf(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
                                switch_dtmf_t dtmf = { 0 };
-                               switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, &dtmf);
+                               switch_rtp_dequeue_dtmf(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, &dtmf);
                                switch_channel_queue_dtmf(channel, &dtmf);
                        }
 
 
-                       if (tech_pvt->read_frame.datalen > 0) {
+                       if (tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen > 0) {
                                size_t bytes = 0;
                                int frames = 1;
 
-                               if (!switch_test_flag((&tech_pvt->read_frame), SFF_CNG)) {
-                                       if ((bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_packet)) {
-                                               frames = (tech_pvt->read_frame.datalen / bytes);
+                               if (!switch_test_flag((&tech_pvt->transports[LDL_TPORT_RTP].read_frame), SFF_CNG)) {
+                                       if ((bytes = tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->encoded_bytes_per_packet)) {
+                                               frames = (tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen / bytes);
                                        }
-                                       tech_pvt->read_frame.samples = (int) (frames * tech_pvt->read_codec.implementation->samples_per_packet);
+                                       tech_pvt->transports[LDL_TPORT_RTP].read_frame.samples = (int) (frames * tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_packet);
                                }
                                break;
                        }
@@ -1513,12 +2136,12 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
 
        switch_clear_flag_locked(tech_pvt, TFLAG_READING);
 
-       if (tech_pvt->read_frame.datalen == 0) {
-               *frame = NULL;
-               return SWITCH_STATUS_GENERR;
+       if (tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen == 0) {
+               switch_set_flag((&tech_pvt->transports[LDL_TPORT_RTP].read_frame), SFF_CNG);
+               tech_pvt->transports[LDL_TPORT_RTP].read_frame.datalen = 2;
        }
 
-       *frame = &tech_pvt->read_frame;
+       *frame = &tech_pvt->transports[LDL_TPORT_RTP].read_frame;
 
        return SWITCH_STATUS_SUCCESS;
 }
@@ -1533,7 +2156,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
        tech_pvt = (struct private_object *) switch_core_session_get_private(session);
        switch_assert(tech_pvt != NULL);
 
-       while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) {
+       while (!(tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation && switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session))) {
                if (switch_channel_ready(channel)) {
                        switch_yield(10000);
                } else {
@@ -1541,7 +2164,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
                }
        }
 
-       if (!switch_core_codec_ready(&tech_pvt->read_codec) || !tech_pvt->read_codec.implementation) {
+       if (!switch_core_codec_ready(&tech_pvt->transports[LDL_TPORT_RTP].read_codec) || !tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation) {
                return SWITCH_STATUS_GENERR;
        }
 
@@ -1552,13 +2175,13 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
        switch_set_flag_locked(tech_pvt, TFLAG_WRITING);
 
        if (!switch_test_flag(frame, SFF_CNG)) {
-               if (tech_pvt->read_codec.implementation->encoded_bytes_per_packet) {
-                       bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_packet;
+               if (tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->encoded_bytes_per_packet) {
+                       bytes = tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->encoded_bytes_per_packet;
                        frames = ((int) frame->datalen / bytes);
                } else
                        frames = 1;
 
-               samples = frames * tech_pvt->read_codec.implementation->samples_per_packet;
+               samples = frames * tech_pvt->transports[LDL_TPORT_RTP].read_codec.implementation->samples_per_packet;
        }
 #if 0
        printf("%s %s->%s send %d bytes %d samples in %d frames ts=%d\n",
@@ -1567,13 +2190,81 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
 #endif
 
        tech_pvt->timestamp_send += samples;
-       //switch_rtp_write_frame(tech_pvt->rtp_session, frame, tech_pvt->timestamp_send);
-       if (switch_rtp_write_frame(tech_pvt->rtp_session, frame) < 0) {
+       //switch_rtp_write_frame(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, frame, tech_pvt->timestamp_send);
+       if (switch_rtp_write_frame(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, frame) < 0) {
                status = SWITCH_STATUS_GENERR;
        }
 
-       switch_clear_flag_locked(tech_pvt, TFLAG_WRITING);
-       return status;
+       switch_clear_flag_locked(tech_pvt, TFLAG_WRITING);
+       return status;
+}
+
+
+static switch_status_t channel_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
+{
+       struct private_object *tech_pvt = NULL;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       //int payload = 0;
+       //switch_status_t status;
+
+       tech_pvt = (struct private_object *) switch_core_session_get_private(session);
+       switch_assert(tech_pvt != NULL);
+       
+       if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
+               return SWITCH_STATUS_GENERR;
+       }
+
+       while (!(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation && switch_rtp_ready(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session))) {
+               if (switch_channel_ready(channel)) {
+                       switch_yield(10000);
+               } else {
+                       return SWITCH_STATUS_GENERR;
+               }
+       }
+       
+       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.datalen = 0;
+       
+       while (switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.datalen == 0) {
+               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.flags = SFF_NONE;
+               switch_rtp_zerocopy_read_frame(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, &tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame, flags);
+       }
+
+
+       if (tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.datalen == 0) {
+               switch_set_flag((&tech_pvt->transports[LDL_TPORT_RTP].read_frame), SFF_CNG);
+               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame.datalen = 2;
+       }
+
+       *frame = &tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_frame;
+       
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t channel_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
+{
+       struct private_object *tech_pvt = (struct private_object *)switch_core_session_get_private(session);
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       int wrote = 0;
+
+       switch_assert(tech_pvt != NULL);
+
+       while (!(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].read_codec.implementation && switch_rtp_ready(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session))) {
+               if (switch_channel_ready(channel)) {
+                       switch_yield(10000);
+               } else {
+                       return SWITCH_STATUS_GENERR;
+               }
+       }
+
+       if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       if (!switch_test_flag(frame, SFF_CNG)) {
+               wrote = switch_rtp_write_frame(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].rtp_session, frame);
+       }
+
+       return wrote > 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_GENERR;
 }
 
 static switch_status_t channel_answer_channel(switch_core_session_t *session)
@@ -1603,10 +2294,13 @@ static switch_status_t channel_receive_message(switch_core_session_t *session, s
                channel_answer_channel(session);
                break;
        case SWITCH_MESSAGE_INDICATE_BRIDGE:
-               rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_STICK);
+               rtp_flush_read_buffer(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, SWITCH_RTP_FLUSH_STICK);
                break;
        case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
-               rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_UNSTICK);
+               rtp_flush_read_buffer(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, SWITCH_RTP_FLUSH_UNSTICK);
+               break;
+       case SWITCH_MESSAGE_INDICATE_STUN_ERROR:
+               //switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_NORMAL_CLEARING);
                break;
        default:
                break;
@@ -1654,12 +2348,15 @@ switch_state_handler_table_t dingaling_event_handlers = {
 
 switch_io_routines_t dingaling_io_routines = {
        /*.outgoing_channel */ channel_outgoing_channel,
-       /*.read_frame */ channel_read_frame,
+       /*.transports[LDL_TPORT_RTP].read_frame */ channel_read_frame,
        /*.write_frame */ channel_write_frame,
        /*.kill_channel */ channel_kill_channel,
        /*.send_dtmf */ channel_send_dtmf,
        /*.receive_message */ channel_receive_message,
-       /*.receive_event */ channel_receive_event
+       /*.receive_event */ channel_receive_event,
+       /*.state_change */ NULL,
+       /*.read_video_frame */ channel_read_video_frame,
+       /*.write_video_frame */ channel_write_video_frame
 };
 
 
@@ -1688,6 +2385,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi
                char *p, *u, ubuf[512] = "", *user = NULL, *f_cid_msg = NULL;
                const char *cid_msg = NULL;
                ldl_user_flag_t flags = LDL_FLAG_OUTBOUND;
+               const char *var;
 
                switch_copy_string(workspace, outbound_profile->destination_number, sizeof(workspace));
                profile_name = workspace;
@@ -1781,13 +2479,30 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi
                        channel = switch_core_session_get_channel(*new_session);
                        switch_core_session_set_private(*new_session, tech_pvt);
                        tech_pvt->session = *new_session;
-                       tech_pvt->codec_index = -1;
-                       if (!(tech_pvt->local_port = switch_rtp_request_port(mdl_profile->ip))) {
+                       tech_pvt->channel = switch_core_session_get_channel(tech_pvt->session);
+                       tech_pvt->transports[LDL_TPORT_RTP].codec_index = -1;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index = -1;
+
+                       mdl_build_crypto(tech_pvt, LDL_TPORT_RTP, 1, AES_CM_128_HMAC_SHA1_80, SWITCH_RTP_CRYPTO_SEND);
+                       mdl_build_crypto(tech_pvt, LDL_TPORT_VIDEO_RTP, 1, AES_CM_128_HMAC_SHA1_80, SWITCH_RTP_CRYPTO_SEND);
+
+                       if (!(tech_pvt->transports[LDL_TPORT_RTP].local_port = switch_rtp_request_port(mdl_profile->ip))) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "No RTP port available!\n");
+                               terminate_session(new_session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                               return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
+                       }
+                       tech_pvt->transports[LDL_TPORT_RTP].adv_local_port = tech_pvt->transports[LDL_TPORT_RTP].local_port;
+                       tech_pvt->transports[LDL_TPORT_RTCP].adv_local_port = tech_pvt->transports[LDL_TPORT_RTP].local_port + 1;
+                       if (!(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port = switch_rtp_request_port(mdl_profile->ip))) {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "No RTP port available!\n");
                                terminate_session(new_session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
                                return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
                        }
-                       tech_pvt->adv_local_port = tech_pvt->local_port;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTP].adv_local_port = tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port;
+                       tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].adv_local_port = tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port + 1;
+
+                       
+
                        tech_pvt->recip = switch_core_session_strdup(*new_session, full_id);
                        if (dnis) {
                                tech_pvt->dnis = switch_core_session_strdup(*new_session, dnis);
@@ -1854,6 +2569,11 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi
                ldl_session_set_value(dlsession, "caller_id_name", outbound_profile->caller_id_name);
                ldl_session_set_value(dlsession, "caller_id_number", outbound_profile->caller_id_number);
                tech_pvt->dlsession = dlsession;
+
+               if ((var = switch_event_get_header(var_event, "absolute_codec_string"))) {
+                       switch_channel_set_variable(channel, "absolute_codec_string", var);
+               }
+
                if (!get_codecs(tech_pvt)) {
                        terminate_session(new_session, __LINE__, SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL);
                        return SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL;
@@ -2117,6 +2837,8 @@ static void set_profile_val(mdl_profile_t *profile, char *var, char *val)
                } else if (val && !strcasecmp(val, "md5")) {
                        profile->user_flags |= LDL_FLAG_SASL_MD5;
                }
+       } else if (!strcasecmp(var, "use-jingle") && !zstr(val)) {
+               profile->user_flags |= LDL_FLAG_JINGLE;
        } else if (!strcasecmp(var, "exten") && !zstr(val)) {
                profile->exten = switch_core_strdup(module_pool, val);
        } else if (!strcasecmp(var, "context") && !zstr(val)) {
@@ -2757,6 +3479,308 @@ static void do_vcard(ldl_handle_t *handle, char *to, char *from, char *id)
        switch_safe_free(xmlstr);
 }
 
+static switch_status_t parse_candidates(ldl_session_t *dlsession, switch_core_session_t *session, ldl_transport_type_t ttype, const char *subject) 
+{
+       
+       ldl_candidate_t *candidates;
+       unsigned int len = 0;
+       unsigned int x, choice = 0, ok = 0;
+       uint8_t lanaddr = 0;
+       struct private_object *tech_pvt = NULL;
+       switch_status_t status = LDL_STATUS_SUCCESS;
+
+       if (!(tech_pvt = switch_core_session_get_private(session))) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (ldl_session_get_candidates(dlsession, ttype, &candidates, &len) != LDL_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Candidate Error!\n");
+               switch_set_flag(tech_pvt, TFLAG_BYE);
+               switch_clear_flag(tech_pvt, TFLAG_IO);
+               status = LDL_STATUS_FALSE;
+               goto end;
+       }
+
+
+       tech_pvt->transports[ttype].total = len;
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%u %s candidates\n", len, ldl_transport_type_str(ttype));
+
+       if (tech_pvt->profile->acl_count) {
+               for (x = 0; x < len; x++) {
+                       uint32_t y = 0;
+
+                       if (strcasecmp(candidates[x].protocol, "udp")) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidate %s:%d has an unsupported protocol!\n",
+                                                                 candidates[x].address, candidates[x].port);
+                               continue;
+                       }
+
+                       for (y = 0; y < tech_pvt->profile->acl_count; y++) {
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
+                               if (switch_check_network_list_ip(candidates[x].address, tech_pvt->profile->acl[y])) {
+                                       choice = x;
+                                       ok = 1;
+                               }
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
+                               if (ok) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidate %s:%d PASS ACL %s\n",
+                                                                         candidates[x].address, candidates[x].port, tech_pvt->profile->acl[y]);
+                                       goto end_candidates;
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidate %s:%d FAIL ACL %s\n",
+                                                                         candidates[x].address, candidates[x].port, tech_pvt->profile->acl[y]);
+                               }
+                       }
+               }
+       } else {
+               for (x = 0; x < len; x++) {
+                       
+
+                       if (tech_pvt->profile->lanaddr) {
+                               lanaddr = strncasecmp(candidates[x].address, tech_pvt->profile->lanaddr, strlen(tech_pvt->profile->lanaddr)) ? 0 : 1;
+                       }
+
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s candidates %s:%d\n", 
+                                                         ldl_transport_type_str(ttype), candidates[x].address,
+                                                         candidates[x].port);
+
+
+                       // 192.0.0.0 - 192.0.127.255 is marked as reserved, should we filter all of them?
+                       if (!strcasecmp(candidates[x].protocol, "udp") &&
+                               (!strcasecmp(candidates[x].type, "local") || !strcasecmp(candidates[x].type, "stun") || !strcasecmp(candidates[x].type, "drelay")) &&
+                               ((tech_pvt->profile->lanaddr &&
+                                 lanaddr) || (strncasecmp(candidates[x].address, "10.", 3) &&
+                                                          strncasecmp(candidates[x].address, "192.168.", 8) &&
+                                                          strncasecmp(candidates[x].address, "127.", 4) &&
+                                                          strncasecmp(candidates[x].address, "255.", 4) &&
+                                                          strncasecmp(candidates[x].address, "0.", 2) &&
+                                                          strncasecmp(candidates[x].address, "1.", 2) &&
+                                                          strncasecmp(candidates[x].address, "2.", 2) &&
+                                                          strncasecmp(candidates[x].address, "172.16.", 7) &&
+                                                          strncasecmp(candidates[x].address, "172.17.", 7) &&
+                                                          strncasecmp(candidates[x].address, "172.18.", 7) &&
+                                                          strncasecmp(candidates[x].address, "172.19.", 7) &&
+                                                          strncasecmp(candidates[x].address, "172.2", 5) &&
+                                                          strncasecmp(candidates[x].address, "172.30.", 7) &&
+                                                          strncasecmp(candidates[x].address, "172.31.", 7) &&
+                                                          strncasecmp(candidates[x].address, "192.0.2.", 8) && strncasecmp(candidates[x].address, "169.254.", 8)
+                                                          ))) {
+                               choice = x;
+                               ok = 1;
+                       }
+               }
+       }
+
+
+ end_candidates:
+       
+       if (ok) {
+               ldl_payload_t payloads[5];
+               char *key;
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
+                                                 "Acceptable %s Candidate %s:%d\n", ldl_transport_type_str(ttype), candidates[choice].address, candidates[choice].port);
+
+
+               if (tech_pvt->transports[ttype].accepted) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Already Accepted [%s:%d]\n", 
+                                                         tech_pvt->transports[ttype].remote_ip, tech_pvt->transports[ttype].remote_port);
+                       goto end;
+               }
+
+
+               if (tech_pvt->transports[ttype].remote_ip) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Already picked an IP [%s]\n", tech_pvt->transports[ttype].remote_ip);
+                       goto end;
+               }
+
+               
+               memset(payloads, 0, sizeof(payloads));
+
+               tech_pvt->transports[ttype].accepted++;
+
+               if (ttype == LDL_TPORT_VIDEO_RTP) {
+                       if ((key = ldl_session_get_value(dlsession, "video:crypto:1"))) {
+                               mdl_add_crypto(tech_pvt, ttype, key, SWITCH_RTP_CRYPTO_RECV);
+                       } else {
+                               tech_pvt->transports[ttype].crypto_type = 0;
+                       }
+               } else if (ttype == LDL_TPORT_RTP) {
+                       if ((key = ldl_session_get_value(dlsession, "audio:crypto:1"))) {
+                               mdl_add_crypto(tech_pvt, ttype, key, SWITCH_RTP_CRYPTO_RECV);
+                       } else {
+                               tech_pvt->transports[ttype].crypto_type = 0;
+                       }
+               }
+               
+               if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+                       switch_set_flag_locked(tech_pvt, TFLAG_TRANSPORT_ACCEPT);
+                       //ldl_session_accept_candidate(dlsession, &candidates[choice]);
+               }
+
+               if (!strcasecmp(subject, "candidates")) {
+                       //switch_set_flag_locked(tech_pvt, TFLAG_TRANSPORT_ACCEPT);
+                       switch_set_flag_locked(tech_pvt, TFLAG_ANSWER);
+               }
+
+               if (lanaddr) {
+                       switch_set_flag_locked(tech_pvt, TFLAG_LANADDR);
+               }
+
+               if (!get_codecs(tech_pvt)) {
+                       terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                       status = LDL_STATUS_FALSE;
+                       goto end;
+               }
+
+
+               tech_pvt->transports[ttype].remote_ip = switch_core_session_strdup(session, candidates[choice].address);
+               ldl_session_set_ip(dlsession, tech_pvt->transports[ttype].remote_ip);
+               tech_pvt->transports[ttype].remote_port = candidates[choice].port;
+               tech_pvt->transports[ttype].remote_user = switch_core_session_strdup(session, candidates[choice].username);
+               tech_pvt->transports[ttype].remote_pass = switch_core_session_strdup(session, candidates[choice].password);
+
+               if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+                       if (!do_candidates(tech_pvt, 0)) {
+                               terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                               status = LDL_STATUS_FALSE;
+
+                               goto end;
+                       }
+               }
+
+               if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND) && (ttype == LDL_TPORT_VIDEO_RTP || ttype == LDL_TPORT_VIDEO_RTP) &&
+                       tech_pvt->transports[ttype].accepted == 1 && (1||switch_test_flag(tech_pvt, TFLAG_RTP_READY))) {
+
+                       if (ttype == LDL_TPORT_VIDEO_RTP) {
+                               activate_video_rtp(tech_pvt);
+                       }
+
+                       if (ttype == LDL_TPORT_VIDEO_RTP) {
+                               activate_audio_rtp(tech_pvt);
+                       }
+
+                       tech_pvt->transports[ttype].restart_rtp++;
+               }
+
+
+               status = LDL_STATUS_SUCCESS;
+       }
+
+ end:
+
+       return status;
+
+}
+
+
+static ldl_status parse_payloads_type(ldl_session_t *dlsession, switch_core_session_t *session, 
+                                                                               ldl_transport_type_t ttype, ldl_payload_t *payloads, unsigned int len)
+{
+       struct private_object *tech_pvt = NULL;
+       switch_status_t status = LDL_STATUS_SUCCESS;
+       unsigned int x, y;
+       int match = 0;
+
+       tech_pvt = switch_core_session_get_private(session);    
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%u payloads\n", len);
+       for (x = 0; x < len; x++) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Available Payload %s %u\n", payloads[x].name,
+                                                 payloads[x].id);
+               for (y = 0; y < tech_pvt->num_codecs; y++) {
+                       char *name = tech_pvt->codecs[y]->iananame;
+
+                       if ((ttype == LDL_TPORT_VIDEO_RTP && tech_pvt->codecs[y]->codec_type != SWITCH_CODEC_TYPE_VIDEO) || 
+                               (ttype == LDL_TPORT_RTP && tech_pvt->codecs[y]->codec_type != SWITCH_CODEC_TYPE_AUDIO)) {
+                               continue;
+                       }
+
+                       if (!strncasecmp(name, "ilbc", 4)) {
+                               name = "ilbc";
+                       }
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "compare %s %d/%d to %s %d/%d\n",
+                                                         payloads[x].name, payloads[x].id, payloads[x].rate,
+                                                         name, tech_pvt->codecs[y]->ianacode, tech_pvt->codecs[y]->samples_per_second);
+
+                       if (tech_pvt->codecs[y]->ianacode > 95) {
+                               match = strcasecmp(name, payloads[x].name) ? 0 : 1;
+                       } else {
+                               match = (payloads[x].id == tech_pvt->codecs[y]->ianacode) ? 1 : 0;
+                       }
+                                               
+                       if (match && payloads[x].rate == tech_pvt->codecs[y]->samples_per_second) {
+                               tech_pvt->transports[ttype].codec_index = y;
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Choosing %s Payload index %u %s %u\n", 
+                                                                 ldl_transport_type_str(ttype),
+                                                                 y,
+                                                                 payloads[x].name, payloads[x].id);
+                               tech_pvt->transports[ttype].codec_name = tech_pvt->codecs[y]->iananame;
+                               tech_pvt->transports[ttype].codec_num = tech_pvt->codecs[y]->ianacode;
+                               tech_pvt->transports[ttype].r_codec_num = (switch_payload_t) (payloads[x].id);
+                               tech_pvt->transports[ttype].codec_rate = payloads[x].rate;
+                               tech_pvt->transports[ttype].ptime = payloads[x].ptime;
+                               tech_pvt->transports[ttype].payload_count++;
+
+                               if (ttype == LDL_TPORT_VIDEO_RTP) {
+                                       tech_pvt->transports[ttype].vid_width = payloads[x].width;
+                                       tech_pvt->transports[ttype].vid_height = payloads[x].height;
+                                       tech_pvt->transports[ttype].vid_rate = payloads[x].framerate;
+                               }
+
+                               if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+
+
+                                       if (!do_describe(tech_pvt, 0)) {
+                                               terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                                               status = LDL_STATUS_FALSE;
+                                               goto done;
+                                       }
+                               }
+                               status = LDL_STATUS_SUCCESS;
+                               goto done;
+                       }
+               }
+       }
+
+ done:
+
+       return status;
+
+}
+
+static ldl_status parse_payloads(ldl_session_t *dlsession, switch_core_session_t *session, ldl_payload_t *payloads, unsigned int len)
+{
+       int match = 0;
+       struct private_object *tech_pvt = NULL;
+       ldl_status status;
+
+       tech_pvt = switch_core_session_get_private(session);
+
+       
+       if ((status = parse_payloads_type(dlsession, session, LDL_TPORT_RTP, payloads, len)) == LDL_STATUS_SUCCESS) {
+               match++;
+       }
+
+       if (tech_pvt->transports[LDL_TPORT_VIDEO_RTP].ready) {
+               if ((status = parse_payloads_type(dlsession, session, LDL_TPORT_VIDEO_RTP, payloads, len)) == LDL_STATUS_SUCCESS) {
+                       match++;
+               }
+       }
+
+       if (!match && !switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+               if (!do_describe(tech_pvt, 0)) {
+                       terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                       status = LDL_STATUS_FALSE;
+               }
+       }
+
+
+       return status;
+
+}
+               
+
 static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsession, ldl_signal_t dl_signal, char *to, char *from, char *subject,
                                                                        char *msg)
 {
@@ -3060,15 +4084,35 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
                                tech_pvt->dlsession = dlsession;
 
                                tech_pvt->session = session;
-                               tech_pvt->codec_index = -1;
+                               tech_pvt->channel = switch_core_session_get_channel(session);
+                               tech_pvt->transports[LDL_TPORT_RTP].codec_index = -1;
+                               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].codec_index = -1;
                                tech_pvt->profile = profile;
-                               if (!(tech_pvt->local_port = switch_rtp_request_port(profile->ip))) {
+
+
+                               mdl_build_crypto(tech_pvt, LDL_TPORT_RTP, 1, AES_CM_128_HMAC_SHA1_80, SWITCH_RTP_CRYPTO_SEND);
+                               mdl_build_crypto(tech_pvt, LDL_TPORT_VIDEO_RTP, 1, AES_CM_128_HMAC_SHA1_80, SWITCH_RTP_CRYPTO_SEND);
+
+                               if (!(tech_pvt->transports[LDL_TPORT_RTP].local_port = switch_rtp_request_port(profile->ip))) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "No RTP port available!\n");
+                                       terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                                       status = LDL_STATUS_FALSE;
+                                       goto done;
+                               }
+                               tech_pvt->transports[LDL_TPORT_RTP].adv_local_port = tech_pvt->transports[LDL_TPORT_RTP].local_port;
+                               tech_pvt->transports[LDL_TPORT_RTCP].adv_local_port = tech_pvt->transports[LDL_TPORT_RTP].local_port + 1;
+
+                               if (!(tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port = switch_rtp_request_port(profile->ip))) {
                                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "No RTP port available!\n");
                                        terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
                                        status = LDL_STATUS_FALSE;
                                        goto done;
                                }
-                               tech_pvt->adv_local_port = tech_pvt->local_port;
+                               tech_pvt->transports[LDL_TPORT_VIDEO_RTP].adv_local_port = tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port;
+                               tech_pvt->transports[LDL_TPORT_VIDEO_RTCP].adv_local_port = tech_pvt->transports[LDL_TPORT_VIDEO_RTP].local_port + 1;
+
+
+
                                switch_set_flag_locked(tech_pvt, TFLAG_ANSWER);
                                tech_pvt->recip = switch_core_session_strdup(session, from);
                                if (!(exten = ldl_session_get_value(dlsession, "dnis"))) {
@@ -3205,8 +4249,8 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
                                        p++;
                                }
                                switch_set_flag_locked(tech_pvt, TFLAG_DTMF);
-                               if (switch_rtp_ready(tech_pvt->rtp_session)) {
-                                       switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_BREAK);
+                               if (switch_rtp_ready(tech_pvt->transports[LDL_TPORT_RTP].rtp_session)) {
+                                       switch_rtp_set_flag(tech_pvt->transports[LDL_TPORT_RTP].rtp_session, SWITCH_RTP_FLAG_BREAK);
                                }
                        }
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "SESSION MSG [%s]\n", msg);
@@ -3252,10 +4296,9 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
                if (dl_signal) {
                        ldl_payload_t *payloads;
                        unsigned int len = 0;
-                       int match = 0;
-
+                       
                        if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
-                               if (!strcasecmp(msg, "accept")) {
+                               if (msg && !strcasecmp(msg, "accept")) {
                                        switch_set_flag_locked(tech_pvt, TFLAG_ANSWER);
                                        switch_set_flag_locked(tech_pvt, TFLAG_TRANSPORT_ACCEPT);
                                        if (!do_candidates(tech_pvt, 0)) {
@@ -3266,7 +4309,7 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
                                }
                        }
 
-                       if (tech_pvt->codec_index > -1) {
+                       if (tech_pvt->transports[LDL_TPORT_RTP].codec_index > -1) {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Already decided on a codec\n");
                                break;
                        }
@@ -3278,55 +4321,9 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
                                goto done;
                        }
 
-
                        if (ldl_session_get_payloads(dlsession, &payloads, &len) == LDL_STATUS_SUCCESS) {
-                               unsigned int x, y;
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%u payloads\n", len);
-                               for (x = 0; x < len; x++) {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Available Payload %s %u\n", payloads[x].name,
-                                                                         payloads[x].id);
-                                       for (y = 0; y < tech_pvt->num_codecs; y++) {
-                                               char *name = tech_pvt->codecs[y]->iananame;
-
-                                               if (!strncasecmp(name, "ilbc", 4)) {
-                                                       name = "ilbc";
-                                               }
-                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "compare %s %d/%d to %s %d/%d\n",
-                                                                                 payloads[x].name, payloads[x].id, payloads[x].rate,
-                                                                                 name, tech_pvt->codecs[y]->ianacode, tech_pvt->codecs[y]->samples_per_second);
-                                               if (tech_pvt->codecs[y]->ianacode > 95) {
-                                                       match = strcasecmp(name, payloads[x].name) ? 0 : 1;
-                                               } else {
-                                                       match = (payloads[x].id == tech_pvt->codecs[y]->ianacode) ? 1 : 0;
-                                               }
-
-                                               if (match && payloads[x].rate == tech_pvt->codecs[y]->samples_per_second) {
-                                                       tech_pvt->codec_index = y;
-                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Choosing Payload index %u %s %u\n", y,
-                                                                                         payloads[x].name, payloads[x].id);
-                                                       tech_pvt->codec_name = tech_pvt->codecs[y]->iananame;
-                                                       tech_pvt->codec_num = tech_pvt->codecs[y]->ianacode;
-                                                       tech_pvt->r_codec_num = (switch_payload_t) (payloads[x].id);
-                                                       tech_pvt->codec_rate = payloads[x].rate;
-                                                       if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
-                                                               if (!do_describe(tech_pvt, 0)) {
-                                                                       terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                                                                       status = LDL_STATUS_FALSE;
-                                                                       goto done;
-                                                               }
-                                                       }
-                                                       status = LDL_STATUS_SUCCESS;
-                                                       goto done;
-                                               }
-                                       }
-                               }
-                               if (!match && !switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
-                                       if (!do_describe(tech_pvt, 0)) {
-                                               terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                                               status = LDL_STATUS_FALSE;
-                                               goto done;
-                                       }
-                               }
+                               status = parse_payloads(dlsession, session, payloads, len);
+                               goto done;
                        }
 
                }
@@ -3334,141 +4331,14 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi
                break;
        case LDL_SIGNAL_CANDIDATES:
                if (dl_signal) {
-                       ldl_candidate_t *candidates;
-                       unsigned int len = 0;
-                       unsigned int x, choice = 0, ok = 0;
-                       uint8_t lanaddr = 0;
-
-                       if (ldl_session_get_candidates(dlsession, &candidates, &len) != LDL_STATUS_SUCCESS) {
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Candidate Error!\n");
-                               switch_set_flag(tech_pvt, TFLAG_BYE);
-                               switch_clear_flag(tech_pvt, TFLAG_IO);
-                               status = LDL_STATUS_FALSE;
-                               goto done;
-                       }
-
-
-                       if (tech_pvt->remote_ip) {
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Already picked an IP [%s]\n", tech_pvt->remote_ip);
-                               break;
-                       }
-
-                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%u candidates\n", len);
-
-                       if (profile->acl_count) {
-                               for (x = 0; x < len; x++) {
-                                       uint32_t y = 0;
-
-                                       if (strcasecmp(candidates[x].protocol, "udp")) {
-                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidate %s:%d has an unsupported protocol!\n",
-                                                                                 candidates[x].address, candidates[x].port);
-                                               continue;
-                                       }
-
-                                       for (y = 0; y < profile->acl_count; y++) {
-                                               
-                                               if (switch_check_network_list_ip(candidates[x].address, profile->acl[y])) {
-                                                       choice = x;
-                                                       ok = 1;
-                                               }
-                                               
-                                               if (ok) {
-                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidate %s:%d PASS ACL %s\n",
-                                                                                         candidates[x].address, candidates[x].port, profile->acl[y]);
-                                                       goto end_candidates;
-                                               } else {
-                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidate %s:%d FAIL ACL %s\n",
-                                                                                         candidates[x].address, candidates[x].port, profile->acl[y]);
-                                               }
-                                       }
-                               }
-                       } else {
-                               for (x = 0; x < len; x++) {
-
-                                       if (profile->lanaddr) {
-                                               lanaddr = strncasecmp(candidates[x].address, profile->lanaddr, strlen(profile->lanaddr)) ? 0 : 1;
-                                       }
-
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "candidates %s:%d\n", candidates[x].address,
-                                                                         candidates[x].port);
-
-                                       // 192.0.0.0 - 192.0.127.255 is marked as reserved, should we filter all of them?
-                                       if (!strcasecmp(candidates[x].protocol, "udp") &&
-                                               (!strcasecmp(candidates[x].type, "local") || !strcasecmp(candidates[x].type, "stun")) &&
-                                               ((profile->lanaddr &&
-                                                 lanaddr) || (strncasecmp(candidates[x].address, "10.", 3) &&
-                                                                          strncasecmp(candidates[x].address, "192.168.", 8) &&
-                                                                          strncasecmp(candidates[x].address, "127.", 4) &&
-                                                                          strncasecmp(candidates[x].address, "255.", 4) &&
-                                                                          strncasecmp(candidates[x].address, "0.", 2) &&
-                                                                          strncasecmp(candidates[x].address, "1.", 2) &&
-                                                                          strncasecmp(candidates[x].address, "2.", 2) &&
-                                                                          strncasecmp(candidates[x].address, "172.16.", 7) &&
-                                                                          strncasecmp(candidates[x].address, "172.17.", 7) &&
-                                                                          strncasecmp(candidates[x].address, "172.18.", 7) &&
-                                                                          strncasecmp(candidates[x].address, "172.19.", 7) &&
-                                                                          strncasecmp(candidates[x].address, "172.2", 5) &&
-                                                                          strncasecmp(candidates[x].address, "172.30.", 7) &&
-                                                                          strncasecmp(candidates[x].address, "172.31.", 7) &&
-                                                                          strncasecmp(candidates[x].address, "192.0.2.", 8) && strncasecmp(candidates[x].address, "169.254.", 8)
-                                                ))) {
-                                               choice = x;
-                                               ok = 1;
-                                       }
-                               }
-                       }
-
-               end_candidates:
-
-                       if (ok) {
-                               ldl_payload_t payloads[5];
-
-                               memset(payloads, 0, sizeof(payloads));
-
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
-                                                                 "Acceptable Candidate %s:%d\n", candidates[choice].address, candidates[choice].port);
-
-                               if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
-                                       switch_set_flag_locked(tech_pvt, TFLAG_TRANSPORT_ACCEPT);
-                                       //ldl_session_accept_candidate(dlsession, &candidates[choice]);
-                               }
-
-                               if (!strcasecmp(subject, "candidates")) {
-                                       //switch_set_flag_locked(tech_pvt, TFLAG_TRANSPORT_ACCEPT);
-                                       switch_set_flag_locked(tech_pvt, TFLAG_ANSWER);
-                               }
-
-                               if (lanaddr) {
-                                       switch_set_flag_locked(tech_pvt, TFLAG_LANADDR);
-                               }
-
-                               if (!get_codecs(tech_pvt)) {
-                                       terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                                       status = LDL_STATUS_FALSE;
-                                       goto done;
-                               }
-
-
-                               tech_pvt->remote_ip = switch_core_session_strdup(session, candidates[choice].address);
-                               ldl_session_set_ip(dlsession, tech_pvt->remote_ip);
-                               tech_pvt->remote_port = candidates[choice].port;
-                               tech_pvt->remote_user = switch_core_session_strdup(session, candidates[choice].username);
-
-
-                               if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
-                                       if (!do_candidates(tech_pvt, 0)) {
-                                               terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-                                               status = LDL_STATUS_FALSE;
-                                               goto done;
-                                       }
-                               }
-
-                               status = LDL_STATUS_SUCCESS;
-                       }
-
-                       goto done;
+                       status = SWITCH_STATUS_SUCCESS;
 
+                       status = parse_candidates(dlsession, session, LDL_TPORT_RTP, subject);
+                       status = parse_candidates(dlsession, session, LDL_TPORT_VIDEO_RTP, subject); 
+                       status = parse_candidates(dlsession, session, LDL_TPORT_RTCP, subject);
+                       status = parse_candidates(dlsession, session, LDL_TPORT_VIDEO_RTCP, subject); 
                }
+
                break;
        case LDL_SIGNAL_REJECT:
                if (channel) {