]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-9576 #resolve [Add Realtime Text]
authorAnthony Minessale <anthm@freeswitch.org>
Mon, 11 Jul 2016 03:25:14 +0000 (22:25 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Tue, 27 Sep 2016 21:40:43 +0000 (16:40 -0500)
46 files changed:
configure.ac
html5/verto/js/src/jquery.verto.js
html5/verto/video_demo/index.html
html5/verto/video_demo/js/verto-min.js
html5/verto/video_demo/verto.js
libs/esl/src/esl_event.c
libs/esl/src/include/esl_event.h
libs/sofia-sip/libsofia-sip-ua/sdp/sdp.bnf
libs/sofia-sip/libsofia-sip-ua/sdp/sdp_parse.c
libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c
libs/sofia-sip/libsofia-sip-ua/sdp/sofia-sip/sdp.h
src/include/private/switch_core_pvt.h
src/include/switch_buffer.h
src/include/switch_channel.h
src/include/switch_core.h
src/include/switch_core_event_hook.h
src/include/switch_core_media.h
src/include/switch_ivr.h
src/include/switch_jitterbuffer.h
src/include/switch_module_interfaces.h
src/include/switch_types.h
src/include/switch_utils.h
src/mod/applications/mod_av/avformat.c
src/mod/applications/mod_commands/mod_commands.c
src/mod/applications/mod_conference/conference_member.c
src/mod/applications/mod_conference/mod_conference.c
src/mod/applications/mod_conference/mod_conference.h
src/mod/applications/mod_dptools/mod_dptools.c
src/mod/applications/mod_fsv/mod_fsv.c
src/mod/endpoints/mod_rtc/mod_rtc.c
src/mod/endpoints/mod_sofia/mod_sofia.c
src/mod/endpoints/mod_sofia/rtp.c
src/mod/endpoints/mod_sofia/sofia_glue.c
src/mod/endpoints/mod_verto/mcast/mcast.c
src/mod/endpoints/mod_verto/mod_verto.c
src/mod/endpoints/mod_verto/mod_verto.h
src/switch_buffer.c
src/switch_core_media.c
src/switch_core_media_bug.c
src/switch_core_session.c
src/switch_event.c
src/switch_ivr.c
src/switch_ivr_async.c
src/switch_ivr_bridge.c
src/switch_jitterbuffer.c
src/switch_rtp.c

index 7932b835ca1a7021a3f9ae3088d7978d8831201c..bc3cafa0bd0bb41f7617878da7211571608e3efc 100644 (file)
@@ -3,9 +3,9 @@
 
 # Must change all of the below together
 # For a release, set revision for that tagged release as well and uncomment
-AC_INIT([freeswitch], [1.7.0], bugs@freeswitch.org)
+AC_INIT([freeswitch], [1.9.0], bugs@freeswitch.org)
 AC_SUBST(SWITCH_VERSION_MAJOR, [1])
-AC_SUBST(SWITCH_VERSION_MINOR, [7])
+AC_SUBST(SWITCH_VERSION_MINOR, [9])
 AC_SUBST(SWITCH_VERSION_MICRO, [0])
 AC_SUBST(SWITCH_VERSION_REVISION, [])
 AC_SUBST(SWITCH_VERSION_REVISION_HUMAN, [])
index d0c3563c123b95e4072c8261e3f7110e335ebfb6..0e0e38bf38b7fe0328ee6320d566336a4550441a 100644 (file)
         obj.dialogParams = {};
 
         for (var i in dialog.params) {
-            if (i == "sdp" && method != "verto.invite" && method != "verto.attach") {
+           if (i == "sdp" && method != "verto.invite" && method != "verto.attach") {
                 continue;
-            }
+           }
+           
+           if ((obj.noDialogParams && i != "callID")) {
+               continue;
+           }
 
-            obj.dialogParams[i] = dialog.params[i];
+           obj.dialogParams[i] = dialog.params[i];
         }
+       
+       delete obj.noDialogParams;
 
         dialog.verto.rpcClient.call(method, obj,
 
         }
     };
 
+    $.verto.dialog.prototype.rtt = function(obj) {
+        var dialog = this;
+       var pobj = {};
+
+       if (!obj) {
+           return false;
+       }
+
+       pobj.code = obj.code;
+       pobj.chars = obj.chars;
+
+
+        if (pobj.chars || pobj.code) {
+            dialog.sendMethod("verto.info", {
+                txt: obj,
+               noDialogParams: true
+            });
+        }
+    };
+
     $.verto.dialog.prototype.transfer = function(dest, params) {
         var dialog = this;
         if (dest) {
     $.verto.dialog.prototype.handleInfo = function(params) {
         var dialog = this;
 
-        dialog.sendMessage($.verto.enum.message.info, params.msg);
+        dialog.sendMessage($.verto.enum.message.info, params);
+
     };
 
     $.verto.dialog.prototype.handleDisplay = function(params) {
index 229275e9cf0019604bd28060cd928b08c741cbce..edd80e27f2e6b9ce4bf33afeb728c00323d9b560 100644 (file)
@@ -244,6 +244,14 @@ div#preload { display: none; }
 
 
       <video id="webcam" autoplay="autoplay" style="width:100%;height:100%;object-fit:inherit;"></video>
+<br><br>
+       <table><tr><td>
+      <div style="width:500px;max-height:300px;overflow-y:scroll;display:block;unicode-bidi:embed;font-family:tahoma;white-space:pre;size:14pt;text-align:left" id="rtt_in"></div>
+</td></tr>
+<tr><td>
+      <textarea style="width:500px;height:200px" id="rtt"></textarea>
+</td></tr>
+</table>
       <!--<video id="local_webcam" autoplay="autoplay" style="width:100%;height:100%;object-fit:inherit;"></video>-->
 
 </div><!-- rows -->
index dbe001713759415a963abb649b902b228164334c..95d5cf1111a3bc02e830bc35ebcf80e9a02fb1c2 100644 (file)
@@ -255,8 +255,9 @@ if(!dialog.params.remote_caller_id_number){dialog.params.remote_caller_id_number
 RTCcallbacks.onMessage=function(rtc,msg){console.debug(msg);};RTCcallbacks.onAnswerSDP=function(rtc,sdp){console.error("answer sdp",sdp);};}else{dialog.params.remote_caller_id_name="Outbound Call";dialog.params.remote_caller_id_number=dialog.params.destination_number;}
 RTCcallbacks.onICESDP=function(rtc){console.log("RECV "+rtc.type+" SDP",rtc.mediaData.SDP);if(dialog.state==$.verto.enum.state.requesting||dialog.state==$.verto.enum.state.answering||dialog.state==$.verto.enum.state.active){location.reload();return;}
 if(rtc.type=="offer"){if(dialog.state==$.verto.enum.state.active){dialog.setState($.verto.enum.state.requesting);dialog.sendMethod("verto.attach",{sdp:rtc.mediaData.SDP});}else{dialog.setState($.verto.enum.state.requesting);dialog.sendMethod("verto.invite",{sdp:rtc.mediaData.SDP});}}else{dialog.setState($.verto.enum.state.answering);dialog.sendMethod(dialog.attach?"verto.attach":"verto.answer",{sdp:dialog.rtc.mediaData.SDP});}};RTCcallbacks.onICE=function(rtc){if(rtc.type=="offer"){console.log("offer",rtc.mediaData.candidate);return;}};RTCcallbacks.onStream=function(rtc,stream){console.log("stream started");};RTCcallbacks.onError=function(e){console.error("ERROR:",e);dialog.hangup({cause:"Device or Permission Error"});};dialog.rtc=new $.FSRTC({callbacks:RTCcallbacks,localVideo:dialog.screenShare?null:dialog.localVideo,useVideo:dialog.params.useVideo?dialog.videoStream:null,useAudio:dialog.audioStream,useStereo:dialog.params.useStereo,videoParams:dialog.params.videoParams,audioParams:verto.options.audioParams,iceServers:verto.options.iceServers,screenShare:dialog.screenShare,useCamera:dialog.useCamera,useMic:dialog.useMic,useSpeak:dialog.useSpeak});dialog.rtc.verto=dialog.verto;if(dialog.direction==$.verto.enum.direction.inbound){if(dialog.attach){dialog.answer();}else{dialog.ring();}}};$.verto.dialog.prototype.invite=function(){var dialog=this;dialog.rtc.call();};$.verto.dialog.prototype.sendMethod=function(method,obj){var dialog=this;obj.dialogParams={};for(var i in dialog.params){if(i=="sdp"&&method!="verto.invite"&&method!="verto.attach"){continue;}
+if((obj.noDialogParams&&i!="callID")){continue;}
 obj.dialogParams[i]=dialog.params[i];}
-dialog.verto.rpcClient.call(method,obj,function(e){dialog.processReply(method,true,e);},function(e){dialog.processReply(method,false,e);});};function checkStateChange(oldS,newS){if(newS==$.verto.enum.state.purge||$.verto.enum.states[oldS.name][newS.name]){return true;}
+delete obj.noDialogParams;dialog.verto.rpcClient.call(method,obj,function(e){dialog.processReply(method,true,e);},function(e){dialog.processReply(method,false,e);});};function checkStateChange(oldS,newS){if(newS==$.verto.enum.state.purge||$.verto.enum.states[oldS.name][newS.name]){return true;}
 return false;}
 function find_name(id){for(var i in $.verto.audioOutDevices){var source=$.verto.audioOutDevices[i];if(source.id===id){return(source.label);}}
 return id;}
@@ -280,7 +281,8 @@ if(success){}
 break;default:break;}};$.verto.dialog.prototype.hangup=function(params){var dialog=this;if(params){if(params.causeCode){dialog.causeCode=params.causeCode;}
 if(params.cause){dialog.cause=params.cause;}}
 if(dialog.state.val>=$.verto.enum.state.new.val&&dialog.state.val<$.verto.enum.state.hangup.val){dialog.setState($.verto.enum.state.hangup);}else if(dialog.state.val<$.verto.enum.state.destroy){dialog.setState($.verto.enum.state.destroy);}};$.verto.dialog.prototype.stopRinging=function(){var dialog=this;if(dialog.verto.ringer){dialog.verto.ringer.stop();}};$.verto.dialog.prototype.indicateRing=function(){var dialog=this;if(dialog.verto.ringer){dialog.verto.ringer.attr("src",dialog.verto.options.ringFile)[0].play();setTimeout(function(){dialog.stopRinging();if(dialog.state==$.verto.enum.state.ringing){dialog.indicateRing();}},dialog.verto.options.ringSleep);}};$.verto.dialog.prototype.ring=function(){var dialog=this;dialog.setState($.verto.enum.state.ringing);dialog.indicateRing();};$.verto.dialog.prototype.useVideo=function(on){var dialog=this;dialog.params.useVideo=on;if(on){dialog.videoStream=dialog.audioStream;}else{dialog.videoStream=null;}
-dialog.rtc.useVideo(dialog.videoStream,dialog.localVideo);};$.verto.dialog.prototype.setMute=function(what){var dialog=this;return dialog.rtc.setMute(what);};$.verto.dialog.prototype.getMute=function(){var dialog=this;return dialog.rtc.getMute();};$.verto.dialog.prototype.setVideoMute=function(what){var dialog=this;return dialog.rtc.setVideoMute(what);};$.verto.dialog.prototype.getVideoMute=function(){var dialog=this;return dialog.rtc.getVideoMute();};$.verto.dialog.prototype.useStereo=function(on){var dialog=this;dialog.params.useStereo=on;dialog.rtc.useStereo(on);};$.verto.dialog.prototype.dtmf=function(digits){var dialog=this;if(digits){dialog.sendMethod("verto.info",{dtmf:digits});}};$.verto.dialog.prototype.transfer=function(dest,params){var dialog=this;if(dest){dialog.sendMethod("verto.modify",{action:"transfer",destination:dest,params:params});}};$.verto.dialog.prototype.hold=function(params){var dialog=this;dialog.sendMethod("verto.modify",{action:"hold",params:params});};$.verto.dialog.prototype.unhold=function(params){var dialog=this;dialog.sendMethod("verto.modify",{action:"unhold",params:params});};$.verto.dialog.prototype.toggleHold=function(params){var dialog=this;dialog.sendMethod("verto.modify",{action:"toggleHold",params:params});};$.verto.dialog.prototype.message=function(msg){var dialog=this;var err=0;msg.from=dialog.params.login;if(!msg.to){console.error("Missing To");err++;}
+dialog.rtc.useVideo(dialog.videoStream,dialog.localVideo);};$.verto.dialog.prototype.setMute=function(what){var dialog=this;return dialog.rtc.setMute(what);};$.verto.dialog.prototype.getMute=function(){var dialog=this;return dialog.rtc.getMute();};$.verto.dialog.prototype.setVideoMute=function(what){var dialog=this;return dialog.rtc.setVideoMute(what);};$.verto.dialog.prototype.getVideoMute=function(){var dialog=this;return dialog.rtc.getVideoMute();};$.verto.dialog.prototype.useStereo=function(on){var dialog=this;dialog.params.useStereo=on;dialog.rtc.useStereo(on);};$.verto.dialog.prototype.dtmf=function(digits){var dialog=this;if(digits){dialog.sendMethod("verto.info",{dtmf:digits});}};$.verto.dialog.prototype.rtt=function(obj){var dialog=this;var pobj={};if(!obj){return false;}
+pobj.code=obj.code;pobj.chars=obj.chars;if(pobj.chars||pobj.code){dialog.sendMethod("verto.info",{txt:obj,noDialogParams:true});}};$.verto.dialog.prototype.transfer=function(dest,params){var dialog=this;if(dest){dialog.sendMethod("verto.modify",{action:"transfer",destination:dest,params:params});}};$.verto.dialog.prototype.hold=function(params){var dialog=this;dialog.sendMethod("verto.modify",{action:"hold",params:params});};$.verto.dialog.prototype.unhold=function(params){var dialog=this;dialog.sendMethod("verto.modify",{action:"unhold",params:params});};$.verto.dialog.prototype.toggleHold=function(params){var dialog=this;dialog.sendMethod("verto.modify",{action:"toggleHold",params:params});};$.verto.dialog.prototype.message=function(msg){var dialog=this;var err=0;msg.from=dialog.params.login;if(!msg.to){console.error("Missing To");err++;}
 if(!msg.body){console.error("Missing Body");err++;}
 if(err){return false;}
 dialog.sendMethod("verto.info",{msg:msg});return true;};$.verto.dialog.prototype.answer=function(params){var dialog=this;if(!dialog.answered){if(!params){params={};}
@@ -289,7 +291,7 @@ dialog.params.callee_id_name=params.callee_id_name;dialog.params.callee_id_numbe
 if(params.useMic){dialog.useMic=params.useMic;}
 if(params.useSpeak){dialog.useSpeak=params.useSpeak;}}
 dialog.rtc.createAnswer(params);dialog.answered=true;}};$.verto.dialog.prototype.handleAnswer=function(params){var dialog=this;dialog.gotAnswer=true;if(dialog.state.val>=$.verto.enum.state.active.val){return;}
-if(dialog.state.val>=$.verto.enum.state.early.val){dialog.setState($.verto.enum.state.active);}else{if(dialog.gotEarly){console.log("Dialog "+dialog.callID+" Got answer while still establishing early media, delaying...");}else{console.log("Dialog "+dialog.callID+" Answering Channel");dialog.rtc.answer(params.sdp,function(){dialog.setState($.verto.enum.state.active);},function(e){console.error(e);dialog.hangup();});console.log("Dialog "+dialog.callID+"ANSWER SDP",params.sdp);}}};$.verto.dialog.prototype.cidString=function(enc){var dialog=this;var party=dialog.params.remote_caller_id_name+(enc?" &lt;":" <")+dialog.params.remote_caller_id_number+(enc?"&gt;":">");return party;};$.verto.dialog.prototype.sendMessage=function(msg,params){var dialog=this;if(dialog.callbacks.onMessage){dialog.callbacks.onMessage(dialog.verto,dialog,msg,params);}};$.verto.dialog.prototype.handleInfo=function(params){var dialog=this;dialog.sendMessage($.verto.enum.message.info,params.msg);};$.verto.dialog.prototype.handleDisplay=function(params){var dialog=this;if(params.display_name){dialog.params.remote_caller_id_name=params.display_name;}
+if(dialog.state.val>=$.verto.enum.state.early.val){dialog.setState($.verto.enum.state.active);}else{if(dialog.gotEarly){console.log("Dialog "+dialog.callID+" Got answer while still establishing early media, delaying...");}else{console.log("Dialog "+dialog.callID+" Answering Channel");dialog.rtc.answer(params.sdp,function(){dialog.setState($.verto.enum.state.active);},function(e){console.error(e);dialog.hangup();});console.log("Dialog "+dialog.callID+"ANSWER SDP",params.sdp);}}};$.verto.dialog.prototype.cidString=function(enc){var dialog=this;var party=dialog.params.remote_caller_id_name+(enc?" &lt;":" <")+dialog.params.remote_caller_id_number+(enc?"&gt;":">");return party;};$.verto.dialog.prototype.sendMessage=function(msg,params){var dialog=this;if(dialog.callbacks.onMessage){dialog.callbacks.onMessage(dialog.verto,dialog,msg,params);}};$.verto.dialog.prototype.handleInfo=function(params){var dialog=this;dialog.sendMessage($.verto.enum.message.info,params);};$.verto.dialog.prototype.handleDisplay=function(params){var dialog=this;if(params.display_name){dialog.params.remote_caller_id_name=params.display_name;}
 if(params.display_number){dialog.params.remote_caller_id_number=params.display_number;}
 dialog.sendMessage($.verto.enum.message.display,{});};$.verto.dialog.prototype.handleMedia=function(params){var dialog=this;if(dialog.state.val>=$.verto.enum.state.early.val){return;}
 dialog.gotEarly=true;dialog.rtc.answer(params.sdp,function(){console.log("Dialog "+dialog.callID+"Establishing early media");dialog.setState($.verto.enum.state.early);if(dialog.gotAnswer){console.log("Dialog "+dialog.callID+"Answering Channel");dialog.setState($.verto.enum.state.active);}},function(e){console.error(e);dialog.hangup();});console.log("Dialog "+dialog.callID+"EARLY SDP",params.sdp);};$.verto.ENUM=function(s){var i=0,o={};s.split(" ").map(function(x){o[x]={name:x,val:i++};});return Object.freeze(o);};$.verto.enum={};$.verto.enum.states=Object.freeze({new:{requesting:1,recovering:1,ringing:1,destroy:1,answering:1,hangup:1},requesting:{trying:1,hangup:1,active:1},recovering:{answering:1,hangup:1},trying:{active:1,early:1,hangup:1},ringing:{answering:1,hangup:1},answering:{active:1,hangup:1},active:{answering:1,requesting:1,hangup:1,held:1},held:{hangup:1,active:1},early:{hangup:1,active:1},hangup:{destroy:1},destroy:{},purge:{destroy:1}});$.verto.enum.state=$.verto.ENUM("new requesting trying recovering ringing answering early active held hangup destroy purge");$.verto.enum.direction=$.verto.ENUM("inbound outbound");$.verto.enum.message=$.verto.ENUM("display info pvtEvent");$.verto.enum=Object.freeze($.verto.enum);$.verto.saved=[];$.verto.unloadJobs=[];$(window).bind('beforeunload',function(){for(var f in $.verto.unloadJobs){$.verto.unloadJobs[f]();}
index e0ee4aec19b37467a2c2f3106862e1d5d52fe7e7..3b06234439260df6ee5aea233bb8aac4a5a8f626 100644 (file)
@@ -403,41 +403,67 @@ var callbacks = {
             }
             break;
         case $.verto.enum.message.info:
-           var body = data.body;
-
+           if (data.msg) {
+               data = data.msg;
+               var body = data.body;
+               
                /*
                // This section has been replaced with messageTextToJQ function
 
-           if (body.match(/\.gif|\.jpg|\.jpeg|\.png/)) {
+               if (body.match(/\.gif|\.jpg|\.jpeg|\.png/)) {
                var mod = "";
                if (body.match(/dropbox.com/)) {
-                   mod = "?dl=1";
+               mod = "?dl=1";
                }
                body = body.replace(/(http[s]{0,1}:\/\/\S+)/g, "<a target='_blank' href='$1'>$1<br><img border='0' class='chatimg' src='$1'" + mod + "><\/a>");
-           } else {
+               } else {
                body = body.replace(/(http[s]{0,1}:\/\/\S+)/g, "<a target='_blank' href='$1'>$1<\/a>");
-           }
+               }
 
-           if (body.slice(-1) !== "\n") {
+               if (body.slice(-1) !== "\n") {
                body += "\n";
-           }
-           body = body.replace(/(?:\r\n|\r|\n)/g, '<br />');
-           
-           var from = data.from_msg_name || data.from;
+               }
+               body = body.replace(/(?:\r\n|\r|\n)/g, '<br />');
+               
+               var from = data.from_msg_name || data.from;
 
-            $("#chatwin").append("<span class=chatuid>" + from + ":</span><br>" + body);
-           $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast");
+               $("#chatwin").append("<span class=chatuid>" + from + ":</span><br>" + body);
+               $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast");
                */
                
-                       var from = data.from_msg_name || data.from;
-                       
-                       $('#chatwin')
-                               .append($('<span class="chatuid" />').text(from + ':'))
-                               .append($('<br />'))
-                               .append(messageTextToJQ(body))
-                               .append($('<br />'));
-                       $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast");
+               var from = data.from_msg_name || data.from;
                
+               $('#chatwin')
+                   .append($('<span class="chatuid" />').text(from + ':'))
+                   .append($('<br />'))
+                   .append(messageTextToJQ(body))
+                   .append($('<br />'));
+               $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast");
+           }
+
+           if (data.txt) {
+               console.log(data.txt);
+               if (data.txt.chars) {
+                   var a = [...data.txt.chars];
+                   //console.log(a);
+                   for (var x in a) {
+                       if(a[x] == "\r") {
+                           $("#rtt_in").append("\n");
+                           continue;
+                       } else if (a[x] == "\b") {
+                           $("#rtt_in").text($("#rtt_in").text().slice(0, -1));
+                           continue;
+                       }
+                       console.log("[" + a[x] + "]");
+                       $("#rtt_in").append(a[x]);
+                   }
+
+                   var psconsole = $('#rtt_in');
+                   if(psconsole.length)
+                       psconsole.scrollTop(psconsole[0].scrollHeight - psconsole.height());
+               }
+           }
+
             break;
         case $.verto.enum.message.display:
             var party = dialog.params.remote_caller_id_name + "<" + dialog.params.remote_caller_id_number + ">";
@@ -1558,6 +1584,30 @@ function init() {
 
     setupChat();
 
+    $("#rtt").val("");
+    $("#rtt_in").text("");
+
+
+    $("#rtt").keyup(function (event) {
+       console.error(event);
+       console.log("KEY (" + event.which + ")\n");
+
+       if (event.which == 8) {
+           cur_call.rtt({code: event.which});
+       }
+
+       if (event.which == 13) {
+           $("#rtt").val("");
+       }
+
+    });
+
+    $("#rtt").keypress(function (event) {
+       console.error(event);
+       console.log("TEXT (" + event.which + ")\n");
+       cur_call.rtt({code: event.which});
+    });
+
     $("#ext").keyup(function (event) {
        if (event.keyCode == 13) {
            $( "#callbtn" ).trigger( "click" );   
index 24f716397697a3c5e823ef0f7abd494aba3deb3a..d097f74569d006eb37f437de11d4246925908e10 100644 (file)
@@ -147,6 +147,7 @@ static const char *EVENT_NAMES[] = {
        "CALL_SETUP_RESULT",
        "CALL_DETAIL",
        "DEVICE_STATE",
+       "REAL_TIME_TEXT",
        "ALL"
 };
 
index 47c38b6cf747c9e2ba8494cc10b0781e6af55686..1380f68092f0dd0112b1c7345fddbbaee1a72a72 100644 (file)
@@ -137,6 +137,7 @@ typedef enum {
        ESL_EVENT_CALL_SETUP_RESULT,
        ESL_EVENT_CALL_DETAIL,
        ESL_EVENT_DEVICE_STATE,
+       ESL_EVENT_REAL_TIME_TEXT,
        ESL_EVENT_ALL
 } esl_event_types_t;
 
index 8a2b08ab7cd9d980b40c9051ba4eb211de1dc787..42281c1ec2aca181dd2bddc4849587898e922b0e 100644 (file)
@@ -85,7 +85,7 @@
 
    media =               1*(alpha-numeric)
                          ;typically "audio", "video", "application"
-                         ;or "data"
+                         ;or "data" or "text"
 
    fmt =                 1*(alpha-numeric)
                          ;typically an RTP payload type for audio
index 887e4e818ae51e76150cddbd788cecf7711441f1..3b1190ee5f90f0224077920f9f6a829eb975b34e 100644 (file)
@@ -1275,7 +1275,7 @@ static void parse_media(sdp_parser_t *p, char *r, sdp_media_t **result)
 
    media =               token
                          ;typically "audio", "video", "application"
-                         ;or "data"
+                         ;or "data" or "text"
 
    fmt =                 token
                          ;typically an RTP payload type for audio
@@ -1378,6 +1378,8 @@ void sdp_media_type(sdp_media_t *m, char const *s)
     m->m_type = sdp_media_image, m->m_type_name = "image";
   else if (su_casematch(s, "red"))
     m->m_type = sdp_media_red, m->m_type_name = "red";
+  else if (su_casematch(s, "text"))
+    m->m_type = sdp_media_text, m->m_type_name = "text";
   else
     m->m_type = sdp_media_x, m->m_type_name = s;
 }
index 0f8e390ebf9f13c1d193eed43925674b24879359..767556d7ac9e78179161f5020fbaa2bc61bc240b 100644 (file)
@@ -583,6 +583,7 @@ static void print_media(sdp_printer_t *p,
     case sdp_media_control:     media = "control"; break;
     case sdp_media_message:     media = "message"; break;
     case sdp_media_image  :     media = "image"; break;
+    case sdp_media_text   :     media = "text"; break;
     default:                    media = m->m_type_name;
     }
 
index 6034a7a37a59b263d38657889cf6ad6ab6617d4a..bf0741e0449112ecfcfc872d18711d46e4249114 100644 (file)
@@ -232,7 +232,8 @@ typedef enum
   sdp_media_message,                   /**< Messaging sessions*/
   sdp_media_image,                     /**< Image browsing sessions,
                                         *   e.g., JPIP or T.38. */
-  sdp_media_red                                /**< Redundancy. @NEW_1_12_4. */
+  sdp_media_red,                       /**< Redundancy. @NEW_1_12_4. */
+  sdp_media_text,                      /**< Realtime Text */
 } sdp_media_e;
 
 /** Media transport protocol. */
index 4204fcd0d5612d646b1c807825857c484595b6c2..bf4dd351e196df88f0d0bf14992fec63e61d86c8 100644 (file)
@@ -189,7 +189,13 @@ struct switch_core_session {
        uint32_t decoder_errors;
        switch_core_video_thread_callback_func_t video_read_callback;
        void *video_read_user_data;
+       switch_core_video_thread_callback_func_t text_read_callback;
+       void *text_read_user_data;
+       switch_io_routines_t *io_override;
        switch_slin_data_t *sdata;
+
+       switch_buffer_t *text_buffer;
+       switch_mutex_t *text_mutex;
 };
 
 struct switch_media_bug {
@@ -228,6 +234,11 @@ struct switch_media_bug {
        switch_image_t *spy_img[2];
        switch_vid_spy_fmt_t spy_fmt;
        switch_thread_t *video_bug_thread;
+       
+       switch_buffer_t *text_buffer;
+       char *text_framedata;
+       uint32_t text_framesize;
+       
        struct switch_media_bug *next;
 };
 
index a3c503234313e24345e9f7ded099125aeaee5af2..e5af0baf0a203e5d779fc024c9fca32a24b088ca 100644 (file)
@@ -162,6 +162,8 @@ SWITCH_DECLARE(void) switch_buffer_destroy(switch_buffer_t **buffer);
 SWITCH_DECLARE(switch_size_t) switch_buffer_zwrite(_In_ switch_buffer_t *buffer, _In_bytecount_(datalen)
                                                                                                   const void *data, _In_ switch_size_t datalen);
 
+SWITCH_DECLARE(void *) switch_buffer_get_head_pointer(switch_buffer_t *buffer);
+
 /** @} */
 
 SWITCH_END_EXTERN_C
index d3267ae122f8072f2907ba7455e0d1e9d15cda21..b1f4b2d219bb8f6c55bb9257320f466ebb2422a1 100644 (file)
@@ -317,6 +317,11 @@ SWITCH_DECLARE(switch_status_t) switch_channel_get_variables(switch_channel_t *c
 
 SWITCH_DECLARE(switch_status_t) switch_channel_pass_callee_id(switch_channel_t *channel, switch_channel_t *other_channel);
 
+
+static inline int switch_channel_var_true(switch_channel_t *channel, const char *variable) {
+       return switch_true(switch_channel_get_variable_dup(channel, variable, SWITCH_FALSE, -1));
+}
+
 /*!
  * \brief Start iterating over the entries in the channel variable list.
  * \param channel the channel to iterate the variables for
index 86763f9713493c0b0ded131a99664f48b33f3486..3d8b2e898897427dda7266c279f813ccc1a3a73f 100644 (file)
@@ -351,6 +351,8 @@ SWITCH_DECLARE(void) switch_core_media_bug_set_read_demux_frame(_In_ switch_medi
 */
 SWITCH_DECLARE(switch_core_session_t *) switch_core_media_bug_get_session(_In_ switch_media_bug_t *bug);
 
+SWITCH_DECLARE(const char *) switch_core_media_bug_get_text(switch_media_bug_t *bug);
+
 /*!
   \brief Test for the existance of a flag on an media bug
   \param bug the object to test
@@ -1163,6 +1165,8 @@ SWITCH_DECLARE(void *) switch_core_session_get_stream(_In_ switch_core_session_t
 */
 SWITCH_DECLARE(int) switch_core_session_get_stream_count(_In_ switch_core_session_t *session);
 
+SWITCH_DECLARE(const char *) switch_core_session_get_text_buffer(switch_core_session_t *session);
+
 /*! 
   \brief Launch a thread designed to exist within the scope of a given session
   \param session a session to allocate the thread from
@@ -2741,6 +2745,8 @@ SWITCH_DECLARE(int) switch_stream_system(const char *cmd, switch_stream_handle_t
 SWITCH_DECLARE(switch_call_direction_t) switch_ice_direction(switch_core_session_t *session);
 SWITCH_DECLARE(void) switch_core_session_debug_pool(switch_stream_handle_t *stream);
 
+SWITCH_DECLARE(switch_status_t) switch_core_session_override_io_routines(switch_core_session_t *session, switch_io_routines_t *ior);
+
 SWITCH_DECLARE(const char *)switch_version_major(void);
 SWITCH_DECLARE(const char *)switch_version_minor(void);
 SWITCH_DECLARE(const char *)switch_version_micro(void);
@@ -2752,6 +2758,9 @@ SWITCH_DECLARE(const char *)switch_version_full_human(void);
 
 SWITCH_DECLARE(void) switch_core_autobind_cpu(void);
 
+SWITCH_DECLARE(switch_status_t) switch_core_session_start_text_thread(switch_core_session_t *session);
+
+
 SWITCH_END_EXTERN_C
 #endif
 /* For Emacs:
index 26dfb0c87d7a2f0b15b0c72181bd8f2d1f795a4c..f991955bd42346f761dd5312c3a3d5a7af61d090 100644 (file)
@@ -41,6 +41,8 @@ typedef struct switch_io_event_hook_read_frame switch_io_event_hook_read_frame_t
 typedef struct switch_io_event_hook_video_read_frame switch_io_event_hook_video_read_frame_t;
 typedef struct switch_io_event_hook_write_frame switch_io_event_hook_write_frame_t;
 typedef struct switch_io_event_hook_video_write_frame switch_io_event_hook_video_write_frame_t;
+typedef struct switch_io_event_hook_text_read_frame switch_io_event_hook_text_read_frame_t;
+typedef struct switch_io_event_hook_text_write_frame switch_io_event_hook_text_write_frame_t;
 typedef struct switch_io_event_hook_kill_channel switch_io_event_hook_kill_channel_t;
 typedef struct switch_io_event_hook_send_dtmf switch_io_event_hook_send_dtmf_t;
 typedef struct switch_io_event_hook_recv_dtmf switch_io_event_hook_recv_dtmf_t;
@@ -54,6 +56,8 @@ typedef switch_status_t (*switch_read_frame_hook_t) (switch_core_session_t *, sw
 typedef switch_status_t (*switch_video_read_frame_hook_t) (switch_core_session_t *, switch_frame_t **, switch_io_flag_t, int);
 typedef switch_status_t (*switch_write_frame_hook_t) (switch_core_session_t *, switch_frame_t *, switch_io_flag_t, int);
 typedef switch_status_t (*switch_video_write_frame_hook_t) (switch_core_session_t *, switch_frame_t *, switch_io_flag_t, int);
+typedef switch_status_t (*switch_text_read_frame_hook_t) (switch_core_session_t *, switch_frame_t **, switch_io_flag_t, int);
+typedef switch_status_t (*switch_text_write_frame_hook_t) (switch_core_session_t *, switch_frame_t *, switch_io_flag_t, int);
 typedef switch_status_t (*switch_kill_channel_hook_t) (switch_core_session_t *, int);
 typedef switch_status_t (*switch_send_dtmf_hook_t) (switch_core_session_t *, const switch_dtmf_t *, switch_dtmf_direction_t direction);
 typedef switch_status_t (*switch_recv_dtmf_hook_t) (switch_core_session_t *, const switch_dtmf_t *, switch_dtmf_direction_t direction);
@@ -108,6 +112,20 @@ struct switch_io_event_hook_video_write_frame {
        struct switch_io_event_hook_video_write_frame *next;
 };
 
+/*! \brief Node in which to store custom read frame channel callback hooks */
+struct switch_io_event_hook_text_read_frame {
+       /*! the read frame channel callback hook */
+       switch_read_frame_hook_t text_read_frame;
+       struct switch_io_event_hook_text_read_frame *next;
+};
+
+/*! \brief Node in which to store custom video_write_frame channel callback hooks */
+struct switch_io_event_hook_text_write_frame {
+       /*! the video_write_frame channel callback hook */
+       switch_video_write_frame_hook_t text_write_frame;
+       struct switch_io_event_hook_text_write_frame *next;
+};
+
 /*! \brief Node in which to store custom kill channel callback hooks */
 struct switch_io_event_hook_kill_channel {
        /*! the kill channel callback hook */
@@ -158,8 +176,12 @@ struct switch_io_event_hooks {
        switch_io_event_hook_video_read_frame_t *video_read_frame;
        /*! a list of write frame hooks */
        switch_io_event_hook_write_frame_t *write_frame;
-       /*! a list of video write frame hooks */
+       /*! a list of text write frame hooks */
        switch_io_event_hook_video_write_frame_t *video_write_frame;
+       /*! a list of text write frame hooks */
+       switch_io_event_hook_text_write_frame_t *text_write_frame;
+       /*! a list of text read frame hooks */
+       switch_io_event_hook_text_read_frame_t *text_read_frame;
        /*! a list of kill channel hooks */
        switch_io_event_hook_kill_channel_t *kill_channel;
        /*! a list of send dtmf hooks */
@@ -225,6 +247,8 @@ NEW_HOOK_DECL_ADD_P(read_frame);
 NEW_HOOK_DECL_ADD_P(write_frame);
 NEW_HOOK_DECL_ADD_P(video_read_frame);
 NEW_HOOK_DECL_ADD_P(video_write_frame);
+NEW_HOOK_DECL_ADD_P(text_read_frame);
+NEW_HOOK_DECL_ADD_P(text_write_frame);
 NEW_HOOK_DECL_ADD_P(kill_channel);
 NEW_HOOK_DECL_ADD_P(send_dtmf);
 NEW_HOOK_DECL_ADD_P(recv_dtmf);
@@ -238,6 +262,8 @@ NEW_HOOK_DECL_REM_P(read_frame);
 NEW_HOOK_DECL_REM_P(write_frame);
 NEW_HOOK_DECL_REM_P(video_read_frame);
 NEW_HOOK_DECL_REM_P(video_write_frame);
+NEW_HOOK_DECL_REM_P(text_read_frame);
+NEW_HOOK_DECL_REM_P(text_write_frame);
 NEW_HOOK_DECL_REM_P(kill_channel);
 NEW_HOOK_DECL_REM_P(send_dtmf);
 NEW_HOOK_DECL_REM_P(recv_dtmf);
index 096bc815fc7de1952cf800da4494e5ada5101129..5bca68d6c010a6e6ddd88cf6ef8fbb95ae13800a 100644 (file)
@@ -122,9 +122,11 @@ typedef struct switch_core_media_params_s {
 
        switch_rtp_bug_flag_t manual_rtp_bugs;
        switch_rtp_bug_flag_t manual_video_rtp_bugs;
+       switch_rtp_bug_flag_t manual_text_rtp_bugs;
 
        char *rtcp_audio_interval_msec;
        char *rtcp_video_interval_msec;
+       char *rtcp_text_interval_msec;
 
 
        char *extrtpip;
@@ -331,10 +333,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_codec_control(switch_core_sess
 
 
 SWITCH_DECLARE(switch_timer_t *) switch_core_media_get_timer(switch_core_session_t *session, switch_media_type_t mtype);
-SWITCH_DECLARE(void) switch_core_media_start_video_function(switch_core_session_t *session, switch_video_function_t video_function, void *user_data);
-SWITCH_DECLARE(void) switch_core_media_end_video_function(switch_core_session_t *session);
+SWITCH_DECLARE(void) switch_core_media_start_engine_function(switch_core_session_t *session, switch_media_type_t type, switch_engine_function_t engine_function, void *user_data);
+SWITCH_DECLARE(void) switch_core_media_end_engine_function(switch_core_session_t *session, switch_media_type_t type);
 SWITCH_DECLARE(switch_status_t) switch_core_session_start_video_thread(switch_core_session_t *session);
-SWITCH_DECLARE(int) switch_core_media_check_video_function(switch_core_session_t *session);
+SWITCH_DECLARE(int) switch_core_media_check_engine_function(switch_core_session_t *session, switch_media_type_t type);
 SWITCH_DECLARE(void) switch_core_session_video_reinit(switch_core_session_t *session);
 SWITCH_DECLARE(switch_status_t) switch_core_media_read_lock_unlock(switch_core_session_t *session, switch_media_type_t type, switch_bool_t lock);
 
@@ -353,7 +355,23 @@ SWITCH_DECLARE(switch_bool_t) switch_core_media_check_dtls(switch_core_session_t
 SWITCH_DECLARE(switch_status_t) switch_core_media_set_outgoing_bitrate(switch_core_session_t *session, switch_media_type_t type, uint32_t bitrate);
 SWITCH_DECLARE(switch_status_t) switch_core_media_reset_jb(switch_core_session_t *session, switch_media_type_t type);
 SWITCH_DECLARE(switch_status_t) switch_core_session_wait_for_video_input_params(switch_core_session_t *session, uint32_t timeout_ms);
-                                                                                                                               
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_set_text_read_callback(switch_core_session_t *session, 
+                                                                                                                                                  switch_core_text_thread_callback_func_t func, void *user_data);
+SWITCH_DECLARE(switch_status_t) switch_core_session_text_read_callback(switch_core_session_t *session, switch_frame_t *frame);
+SWITCH_DECLARE(switch_status_t) switch_core_session_read_text_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags,
+                                                                                                                                       int stream_id);
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags,
+                                                                                                                                        int stream_id);
+
+SWITCH_DECLARE(switch_status_t) switch_rtp_text_factory_create(switch_rtp_text_factory_t **tfP, switch_memory_pool_t *pool);
+SWITCH_DECLARE(switch_status_t) switch_rtp_text_factory_destroy(switch_rtp_text_factory_t **tfP);                                                                                                                      
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_print(switch_core_session_t *session, const char *data);
+SWITCH_DECLARE(switch_status_t) switch_core_session_printf(switch_core_session_t *session, const char *fmt, ...);
+       
 SWITCH_END_EXTERN_C
 #endif
 /* For Emacs:
index 06fa8c137aad0931c6b816277129dbb0314a67ce..712179d7838b56fb9685adf3ec60b95b0eaaaeb2 100644 (file)
@@ -1020,6 +1020,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_stop_video_write_overlay_session(swit
 SWITCH_DECLARE(switch_status_t) switch_ivr_video_write_overlay_session(switch_core_session_t *session, const char *img_path, 
                                                                                                                                           switch_img_position_t pos, uint8_t alpha);
 
+SWITCH_DECLARE(switch_status_t) switch_ivr_capture_text(switch_core_session_t *session, switch_bool_t on);
 
 /** @} */
 
index de423fd7fd68d06d218ea46eb6905ef1354baefb..e55bcbec28572b77c5185fab49d8bce6a9e0c0ee 100644 (file)
@@ -39,7 +39,8 @@ typedef enum {
 
 typedef enum {
        SJB_VIDEO = 0,
-       SJB_AUDIO
+       SJB_AUDIO,
+       SJB_TEXT
 } switch_jb_type_t;
 
 
index 1802f872e3fc2ceda8baf7d42a7e720d3565c12e..0997bbeb0548a2c75f496cec95d96e02859b98d1 100644 (file)
@@ -119,6 +119,8 @@ typedef switch_status_t (*switch_io_state_change_t) (switch_core_session_t *);
 typedef switch_status_t (*switch_io_state_run_t) (switch_core_session_t *);
 typedef switch_status_t (*switch_io_read_video_frame_t) (switch_core_session_t *, switch_frame_t **, switch_io_flag_t, int);
 typedef switch_status_t (*switch_io_write_video_frame_t) (switch_core_session_t *, switch_frame_t *, switch_io_flag_t, int);
+typedef switch_status_t (*switch_io_read_text_frame_t) (switch_core_session_t *, switch_frame_t **, switch_io_flag_t, int);
+typedef switch_status_t (*switch_io_write_text_frame_t) (switch_core_session_t *, switch_frame_t *, switch_io_flag_t, int);
 typedef switch_jb_t *(*switch_io_get_jb_t) (switch_core_session_t *, switch_media_type_t);
 
 typedef enum {
@@ -132,6 +134,8 @@ typedef enum {
        SWITCH_IO_STATE_CHANGE,
        SWITCH_IO_READ_VIDEO_FRAME,
        SWITCH_IO_WRITE_VIDEO_FRAME,
+       SWITCH_IO_READ_TEXT_FRAME,
+       SWITCH_IO_WRITE_TEXT_FRAME,
        SWITCH_IO_GET_JB,
 } switch_io_routine_name_t;
 
@@ -157,6 +161,10 @@ struct switch_io_routines {
        switch_io_read_video_frame_t read_video_frame;
        /*! write a video frame to a session */
        switch_io_write_video_frame_t write_video_frame;
+       /*! read a video frame from a session */
+       switch_io_read_text_frame_t read_text_frame;
+       /*! write a video frame to a session */
+       switch_io_write_text_frame_t write_text_frame;
        /*! change a sessions channel run state */
        switch_io_state_run_t state_run;
        /*! get sessions jitterbuffer */
index cc8539085e6a853efcafa3d846280c3a6a0ee8a2..7b81a07e8f6e28629ddde57d680eb3d946135ff5 100644 (file)
@@ -213,6 +213,8 @@ SWITCH_BEGIN_EXTERN_C
 #define SWITCH_REMOTE_VIDEO_PORT_VARIABLE "remote_video_port"
 #define SWITCH_LOCAL_VIDEO_IP_VARIABLE "local_video_ip"
 #define SWITCH_LOCAL_VIDEO_PORT_VARIABLE "local_video_port"
+#define SWITCH_LOCAL_TEXT_IP_VARIABLE "local_text_ip"
+#define SWITCH_LOCAL_TEXT_PORT_VARIABLE "local_text_port"
 #define SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE "hangup_after_bridge"
 #define SWITCH_PARK_AFTER_BRIDGE_VARIABLE "park_after_bridge"
 #define SWITCH_PARK_AFTER_EARLY_BRIDGE_VARIABLE "park_after_early_bridge"
@@ -234,6 +236,7 @@ SWITCH_BEGIN_EXTERN_C
 #define SWITCH_RTCP_AUDIO_INTERVAL_MSEC "5000"
 #define SWITCH_RTCP_VIDEO_INTERVAL_MSEC "2000"
 
+#define TEXT_UNICODE_LINEFEED {0xe2, 0x80, 0xa8}
 #define MAX_FMTP_LEN 256
 
 /* Jitter */
@@ -496,7 +499,8 @@ typedef enum {
        SWITCH_ABC_TYPE_READ_VIDEO_PING,
        SWITCH_ABC_TYPE_WRITE_VIDEO_PING,
        SWITCH_ABC_TYPE_STREAM_VIDEO_PING,
-       SWITCH_ABC_TYPE_VIDEO_PATCH
+       SWITCH_ABC_TYPE_VIDEO_PATCH,
+       SWITCH_ABC_TYPE_READ_TEXT
 } switch_abc_type_t;
 
 typedef struct {
@@ -769,6 +773,7 @@ typedef enum {
        SWITCH_RTP_FLAG_TMMBR,
        SWITCH_RTP_FLAG_GEN_TS_DELTA,
        SWITCH_RTP_FLAG_DETECT_SSRC,
+       SWITCH_RTP_FLAG_TEXT,
        SWITCH_RTP_FLAG_INVALID
 } switch_rtp_flag_t;
 
@@ -1369,6 +1374,8 @@ typedef enum {
        CC_JITTERBUFFER,
        CC_FS_RTP,
        CC_QUEUEABLE_DTMF_DELAY,
+       CC_IO_OVERRIDE,
+       CC_RTP_RTT,
        /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
        CC_FLAG_MAX
 } switch_channel_cap_t;
@@ -1514,6 +1521,13 @@ typedef enum {
        CF_3P_NOMEDIA_REQUESTED_BLEG,
        CF_IMAGE_SDP,
        CF_VIDEO_SDP_RECVD,
+       CF_TEXT_SDP_RECVD,
+       CF_TEXT,
+       CF_TEXT_POSSIBLE,
+       CF_TEXT_PASSIVE,
+       CF_TEXT_ECHO,
+       CF_TEXT_ACTIVE,
+       CF_TEXT_IDLE,
        /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
        /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */
        CF_FLAG_MAX
@@ -1570,7 +1584,8 @@ typedef enum {
        SFF_PICTURE_RESET = (1 << 14),
        SFF_SAME_IMAGE = (1 << 15),
        SFF_USE_VIDEO_TIMESTAMP = (1 << 16),
-       SFF_ENCODED = (1 << 17)
+       SFF_ENCODED = (1 << 17),
+       SFF_TEXT_LINE_BREAK = (1 << 18)
 } switch_frame_flag_enum_t;
 typedef uint32_t switch_frame_flag_t;
 
@@ -1714,9 +1729,10 @@ typedef enum {
 
 typedef enum {
        SWITCH_MEDIA_TYPE_AUDIO,
-       SWITCH_MEDIA_TYPE_VIDEO
+       SWITCH_MEDIA_TYPE_VIDEO,
+       SWITCH_MEDIA_TYPE_TEXT
 } switch_media_type_t;
-#define SWITCH_MEDIA_TYPE_TOTAL 2
+#define SWITCH_MEDIA_TYPE_TOTAL 3
 
 
 /*!
@@ -1775,7 +1791,8 @@ typedef enum {
        SMBF_VIDEO_PATCH = (1 << 21),
        SMBF_SPY_VIDEO_STREAM = (1 << 22),
        SMBF_SPY_VIDEO_STREAM_BLEG = (1 << 23),
-       SMBF_READ_VIDEO_PATCH = (1 << 24)
+       SMBF_READ_VIDEO_PATCH = (1 << 24),
+       SMBF_READ_TEXT_STREAM = (1 << 25)
 } switch_media_bug_flag_enum_t;
 typedef uint32_t switch_media_bug_flag_t;
 
@@ -2021,6 +2038,7 @@ typedef enum {
        SWITCH_EVENT_CALL_SETUP_RESULT,
        SWITCH_EVENT_CALL_DETAIL,
        SWITCH_EVENT_DEVICE_STATE,
+       SWITCH_EVENT_REAL_TIME_TEXT,
        SWITCH_EVENT_ALL
 } switch_event_types_t;
 
@@ -2236,13 +2254,15 @@ typedef struct switch_console_callback_match switch_console_callback_match_t;
 typedef void (*switch_media_bug_exec_cb_t)(switch_media_bug_t *bug, void *user_data);
 
 typedef switch_status_t (*switch_core_video_thread_callback_func_t) (switch_core_session_t *session, switch_frame_t *frame, void *user_data);
+typedef switch_status_t (*switch_core_text_thread_callback_func_t) (switch_core_session_t *session, switch_frame_t *frame, void *user_data);
 typedef void (*switch_cap_callback_t) (const char *var, const char *val, void *user_data);
 typedef switch_status_t (*switch_console_complete_callback_t) (const char *, const char *, switch_console_callback_match_t **matches);
 typedef switch_bool_t (*switch_media_bug_callback_t) (switch_media_bug_t *, void *, switch_abc_type_t);
 typedef switch_bool_t (*switch_tone_detect_callback_t) (switch_core_session_t *, const char *, const char *);
 typedef struct switch_xml_binding switch_xml_binding_t;
 
-typedef void (*switch_video_function_t) (switch_core_session_t *session, void *user_data);
+typedef void (*switch_engine_function_t) (switch_core_session_t *session, void *user_data);
+
 
 typedef switch_status_t (*switch_core_codec_encode_func_t) (switch_codec_t *codec,
                                                                                                                        switch_codec_t *other_codec,
@@ -2609,6 +2629,10 @@ typedef enum {
        SCFC_PAUSE_READ
 } switch_file_command_t;
 
+
+struct switch_rtp_text_factory_s;
+typedef struct switch_rtp_text_factory_s  switch_rtp_text_factory_t;
+
 SWITCH_END_EXTERN_C
 #endif
 /* For Emacs:
index 691395f362ca0e9785f8a05cae7edb550beb3543..1c423b202be9a6766daea1979e8260189d367b54 100644 (file)
@@ -373,6 +373,33 @@ SWITCH_DECLARE(switch_status_t) switch_b64_encode(unsigned char *in, switch_size
 SWITCH_DECLARE(switch_size_t) switch_b64_decode(char *in, char *out, switch_size_t olen);
 SWITCH_DECLARE(char *) switch_amp_encode(char *s, char *buf, switch_size_t len);
 
+
+
+static inline char *switch_print_bits(const unsigned char *byte, char *buf, switch_size_t buflen)
+{
+
+       int i, j = 0, k = 0, l = 0;
+
+       while(k < buflen) {
+               l = 0;
+               for (i = 7; i >= 0; i--) {
+                       buf[j++] = (*byte & (1 << i)) ? '1' : '0';
+                       if (++l % 4 == 0) {
+                               buf[j++] = ' ';
+                       }
+               }
+               k++;
+               byte++;
+       }
+       
+       if (buf[j-1] == ' ') j--;
+       buf[j++] = '\0';
+       return buf;
+}
+
+
+
+
 static inline switch_bool_t switch_is_digit_string(const char *s)
 {
 
index 883a5be51f25035c09eaf1bcd235bbc5a7d80ac4..04d35096e1c34fa39a043cde43182f72c7c678c2 100644 (file)
@@ -1140,7 +1140,7 @@ SWITCH_STANDARD_APP(record_av_function)
                switch_core_timer_destroy(&timer);
        }
 
-       switch_core_media_end_video_function(session);
+       switch_core_media_end_engine_function(session, SWITCH_MEDIA_TYPE_VIDEO);
        switch_core_session_set_read_codec(session, NULL);
        switch_core_codec_destroy(&codec);
 
index 39bffd58ab422a7e9443fab92392d180e3431fac..f44b4ab55bc2b627c99801350cf83b188ed1da75 100644 (file)
@@ -3066,6 +3066,61 @@ SWITCH_STANDARD_API(uuid_chat)
        return SWITCH_STATUS_SUCCESS;
 }
 
+#define UUID_CAPTURE_TEXT_SYNTAX "<uuid> <on|off>"
+SWITCH_STANDARD_API(uuid_capture_text)
+{
+       switch_core_session_t *tsession = NULL;
+       char *uuid = NULL, *onoff = NULL;
+
+       if (!zstr(cmd) && (uuid = strdup(cmd))) {
+               if ((onoff = strchr(uuid, ' '))) {
+                       *onoff++ = '\0';
+               }
+       }
+
+       if (zstr(uuid) || zstr(onoff)) {
+               stream->write_function(stream, "-USAGE: %s\n", UUID_CAPTURE_TEXT_SYNTAX);
+       } else {
+               if ((tsession = switch_core_session_locate(uuid))) {
+                       switch_ivr_capture_text(tsession, switch_true(onoff));
+               } else {
+                       stream->write_function(stream, "-ERR No such channel %s!\n", uuid);
+               }
+       }
+
+       switch_safe_free(uuid);
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+#define UUID_SEND_TEXT_SYNTAX "<uuid> <text>"
+SWITCH_STANDARD_API(uuid_send_text)
+{
+       switch_core_session_t *tsession = NULL;
+       char *uuid = NULL, *text = NULL;
+
+       if (!zstr(cmd) && (uuid = strdup(cmd))) {
+               if ((text = strchr(uuid, ' '))) {
+                       *text++ = '\0';
+               }
+       }
+
+       if (zstr(uuid) || zstr(text)) {
+               stream->write_function(stream, "-USAGE: %s\n", UUID_SEND_TEXT_SYNTAX);
+       } else {
+               if ((tsession = switch_core_session_locate(uuid))) {
+                       switch_core_session_print(tsession, text);
+                       switch_core_session_print(tsession, "\r\n");
+                       switch_core_session_rwunlock(tsession);
+               } else {
+                       stream->write_function(stream, "-ERR No such channel %s!\n", uuid);
+               }
+       }
+
+       switch_safe_free(uuid);
+       return SWITCH_STATUS_SUCCESS;
+}
+
 #define UUID_DROP_DTMF_SYNTAX "<uuid> [on | off ] [ mask_digits <digits> | mask_file <file>]"
 SWITCH_STANDARD_API(uuid_drop_dtmf)
 {
@@ -7197,6 +7252,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        SWITCH_ADD_API(commands_api_interface, "uuid_broadcast", "Execute dialplan application", uuid_broadcast_function, BROADCAST_SYNTAX);
        SWITCH_ADD_API(commands_api_interface, "uuid_buglist", "List media bugs on a session", uuid_buglist_function, BUGLIST_SYNTAX);
        SWITCH_ADD_API(commands_api_interface, "uuid_chat", "Send a chat message", uuid_chat, UUID_CHAT_SYNTAX);
+       SWITCH_ADD_API(commands_api_interface, "uuid_send_text", "Send text in real-time", uuid_send_text, UUID_SEND_TEXT_SYNTAX);
+       SWITCH_ADD_API(commands_api_interface, "uuid_capture_text", "start/stop capture_text", uuid_capture_text, UUID_CAPTURE_TEXT_SYNTAX);
        SWITCH_ADD_API(commands_api_interface, "uuid_codec_debug", "Send codec a debug message", uuid_codec_debug_function, CODEC_DEBUG_SYNTAX);
        SWITCH_ADD_API(commands_api_interface, "uuid_codec_param", "Send codec a param", uuid_codec_param_function, CODEC_PARAM_SYNTAX);
        SWITCH_ADD_API(commands_api_interface, "uuid_debug_media", "Debug media", uuid_debug_media_function, DEBUG_MEDIA_SYNTAX);
@@ -7376,6 +7433,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        switch_console_set_complete("add uuid_broadcast ::console::list_uuid");
        switch_console_set_complete("add uuid_buglist ::console::list_uuid");
        switch_console_set_complete("add uuid_chat ::console::list_uuid");
+       switch_console_set_complete("add uuid_send_text ::console::list_uuid");
+       switch_console_set_complete("add uuid_capture_text ::console::list_uuid");
        switch_console_set_complete("add uuid_codec_debug ::console::list_uuid audio");
        switch_console_set_complete("add uuid_codec_debug ::console::list_uuid video");
        switch_console_set_complete("add uuid_codec_param ::console::list_uuid audio read");
index b90ab477c536089b336ab0d1236d6e5008217fd3..064d12edac6bfe8f5afc67db2f87e5607f6d7348 100644 (file)
@@ -1148,6 +1148,13 @@ switch_status_t conference_member_del(conference_obj_t *conference, conference_m
        lock_member(member);
        conference_utils_member_clear_flag(member, MFLAG_INTREE);
 
+       
+       switch_safe_free(member->text_framedata);
+       member->text_framesize = 0;
+       if (member->text_buffer) {
+               switch_buffer_destroy(&member->text_buffer);
+       }
+
        if (member->rec) {
                conference->recording_members--;
        }
index dbcb80299a1c20e1e81e010c92fa52e6009c3d75..75b2e3b27efd8109f71e7fd67f669d3527e5e42f 100644 (file)
@@ -249,6 +249,42 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob
 
                floor_holder = conference->floor_holder;
 
+               for (imember = conference->members; imember; imember = imember->next) {
+                       if (!zstr(imember->text_framedata)) {
+                               switch_frame_t frame = { 0 };
+                               char *framedata;
+                               uint32_t framedatalen;
+                               const char *caller_id_name = switch_channel_get_variable(imember->channel, "caller_id_name");
+                               unsigned char CR[3] = TEXT_UNICODE_LINEFEED;
+
+                               
+                               switch_mutex_lock(imember->text_mutex);
+
+                               framedatalen = strlen(imember->text_framedata) + strlen(caller_id_name) + 6;
+
+                               switch_zmalloc(framedata, framedatalen);
+                               
+                               switch_snprintf(framedata, framedatalen, "%s::\n%s", caller_id_name, imember->text_framedata);
+                               memcpy(framedata + strlen(framedata), CR, sizeof(CR));
+                               
+
+                               frame.data = framedata;
+                               frame.datalen = framedatalen;
+
+                               for (omember = conference->members; omember; omember = omember->next) {
+                                       if (omember != imember) {
+                                               switch_core_session_write_text_frame(omember->session, &frame, 0, 0);
+                                       }
+                               }
+                               
+                               free(framedata);
+                               
+                               imember->text_framedata[0] = '\0';
+
+                               switch_mutex_unlock(imember->text_mutex);
+                       }
+               }
+
                /* Read one frame of audio from each member channel and save it for redistribution */
                for (imember = conference->members; imember; imember = imember->next) {
                        uint32_t buf_read = 0;
@@ -1610,6 +1646,65 @@ SWITCH_STANDARD_APP(conference_auto_function)
 }
 
 
+switch_status_t conference_text_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
+{
+       conference_member_t *member = (conference_member_t *)user_data;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_size_t inuse = 0;
+
+       if (!member) return SWITCH_STATUS_FALSE;
+
+
+       switch_mutex_lock(member->text_mutex);
+       if (!member->text_buffer) {
+               switch_buffer_create_dynamic(&member->text_buffer, 512, 1024, 0);
+               switch_zmalloc(member->text_framedata, 1024);
+               member->text_framesize = 1024;
+       }
+
+       if (frame->data && frame->datalen && !(frame->flags & SFF_CNG)) {
+               switch_buffer_write(member->text_buffer, frame->data, frame->datalen);
+       }
+
+       inuse = switch_buffer_inuse(member->text_buffer);
+
+       if (zstr(member->text_framedata) && inuse && (switch_channel_test_flag(channel, CF_TEXT_IDLE) || switch_test_flag(frame, SFF_TEXT_LINE_BREAK))) {
+               int bytes = 0, ok = 0;
+               char *p;
+
+               if (inuse + 1 > member->text_framesize) {
+                       void *tmp = malloc(inuse + 1024);
+                       memcpy(tmp, member->text_framedata, member->text_framesize);
+
+                       switch_assert(tmp);
+                       
+                       member->text_framesize = inuse + 1024;
+                       
+                       free(member->text_framedata);
+                       member->text_framedata = tmp;
+
+               }
+
+               bytes = switch_buffer_read(member->text_buffer, member->text_framedata, inuse);
+               *(member->text_framedata + bytes) = '\0'; 
+
+               for(p = member->text_framedata; p && *p; p++) {
+                       if (*p > 32 && *p < 127) {
+                               ok++;
+                       }
+               }
+
+               if (!ok) {
+                       member->text_framedata[0] = '\0';
+               }
+
+       }
+       
+       switch_mutex_unlock(member->text_mutex);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
 /* Application interface function that is called from the dialplan to join the channel to a conference */
 SWITCH_STANDARD_APP(conference_function)
 {
@@ -2123,6 +2218,7 @@ SWITCH_STANDARD_APP(conference_function)
        switch_mutex_init(&member.fnode_mutex, SWITCH_MUTEX_NESTED, member.pool);
        switch_mutex_init(&member.audio_in_mutex, SWITCH_MUTEX_NESTED, member.pool);
        switch_mutex_init(&member.audio_out_mutex, SWITCH_MUTEX_NESTED, member.pool);
+       switch_mutex_init(&member.text_mutex, SWITCH_MUTEX_NESTED, member.pool);
        switch_thread_rwlock_create(&member.rwlock, member.pool);
 
        if (conference_member_setup_media(&member, conference)) {
@@ -2197,6 +2293,7 @@ SWITCH_STANDARD_APP(conference_function)
 
        /* Chime in the core video thread */
        switch_core_session_set_video_read_callback(session, conference_video_thread_callback, (void *)&member);
+       switch_core_session_set_text_read_callback(session, conference_text_thread_callback, (void *)&member);
 
        if (switch_channel_test_flag(channel, CF_VIDEO_ONLY)) {
                while(conference_utils_member_test_flag((&member), MFLAG_RUNNING) && switch_channel_ready(channel)) {
@@ -2211,6 +2308,7 @@ SWITCH_STANDARD_APP(conference_function)
        }
 
        switch_core_session_set_video_read_callback(session, NULL, NULL);
+       switch_core_session_set_text_read_callback(session, NULL, NULL);
 
        switch_channel_set_private(channel, "_conference_autocall_list_", NULL);
 
index 02503f611052a75d6fed06791472ef030be7fa65..a4aca869fc9ec7626d3199aaa5d19da2c5b77c6c 100644 (file)
@@ -790,6 +790,13 @@ struct conference_member {
        int reset_media;
        int flip;
        int flip_count;
+
+       switch_mutex_t *text_mutex;
+       switch_buffer_t *text_buffer;
+       char *text_framedata;
+       uint32_t text_framesize;
+
+
 };
 
 typedef enum {
@@ -971,6 +978,7 @@ void conference_video_fnode_check(conference_file_node_t *fnode, int canvas_id);
 switch_status_t conference_video_set_canvas_bgimg(mcu_canvas_t *canvas, const char *img_path);
 switch_status_t conference_al_parse_position(al_handle_t *al, const char *data);
 switch_status_t conference_video_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data);
+switch_status_t conference_text_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data);
 void *SWITCH_THREAD_FUNC conference_video_muxing_write_thread_run(switch_thread_t *thread, void *obj);
 void conference_member_check_agc_levels(conference_member_t *member);
 void conference_member_clear_avg(conference_member_t *member);
index 0545ac07c41451466a4af8386b82d902b690a9c7..d3f26e33d26682e292f27361bfb4631dcb867ab6 100644 (file)
@@ -1012,6 +1012,10 @@ SWITCH_STANDARD_APP(set_mute_function)
 
 }
 
+SWITCH_STANDARD_APP(capture_text_function)
+{
+       switch_ivr_capture_text(session, switch_true((char *)data));
+}
 
 SWITCH_STANDARD_APP(ring_ready_function)
 {
@@ -6127,6 +6131,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
        SWITCH_ADD_CHAT(chat_interface, "event", event_chat_send);
        SWITCH_ADD_CHAT(chat_interface, "api", api_chat_send);
 
+
        SWITCH_ADD_API(api_interface, "strepoch", "Convert a date string into epoch time", strepoch_api_function, "<string>");
        SWITCH_ADD_API(api_interface, "page", "Send a file as a page", page_api_function, "(var1=val1,var2=val2)<var1=val1,var2=val2><chan1>[:_:<chanN>]");
        SWITCH_ADD_API(api_interface, "strmicroepoch", "Convert a date string into micoepoch time", strmicroepoch_api_function, "<string>");
@@ -6216,6 +6221,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
        SWITCH_ADD_APP(app_interface, "multiunset", "Unset many channel variables", SET_LONG_DESC, multiunset_function, "[^^<delim>]<varname> <var2> <var3>",
                                   SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
 
+       SWITCH_ADD_APP(app_interface, "capture_text", "capture text", "capture text", capture_text_function, "", SAF_NONE);
        SWITCH_ADD_APP(app_interface, "ring_ready", "Indicate Ring_Ready", "Indicate Ring_Ready on a channel.", ring_ready_function, "", SAF_SUPPORT_NOMEDIA);
        SWITCH_ADD_APP(app_interface, "remove_bugs", "Remove media bugs", "Remove all media bugs from a channel.", remove_bugs_function, "[<function>]", SAF_NONE);
        SWITCH_ADD_APP(app_interface, "break", "Break", "Set the break flag.", break_function, "", SAF_SUPPORT_NOMEDIA);
index e2c6f5af4d371336c5d84ce1913599018f960036..e99022cee8fee81b09a90e1d13b6a7e7e9bd1e8c 100644 (file)
@@ -183,7 +183,7 @@ SWITCH_STANDARD_APP(record_fsv_function)
                switch_mutex_init(&mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
                eh.mutex = mutex;
                eh.fd = fd;
-               switch_core_media_start_video_function(session, record_video_thread, &eh);
+               switch_core_media_start_engine_function(session, SWITCH_MEDIA_TYPE_VIDEO, record_video_thread, &eh);
        }
 
 
@@ -257,7 +257,7 @@ SWITCH_STANDARD_APP(record_fsv_function)
                close(fd);
        }
 
-       switch_core_media_end_video_function(session);
+       switch_core_media_end_engine_function(session, SWITCH_MEDIA_TYPE_VIDEO);
        switch_core_session_set_read_codec(session, NULL);
        switch_core_codec_destroy(&codec);
 
@@ -751,7 +751,7 @@ SWITCH_STANDARD_APP(decode_video_function)
 
        switch_channel_set_flag(channel, CF_VIDEO_DECODED_READ);
 
-       switch_core_media_start_video_function(session, decode_video_thread, &max_pictures);
+       switch_core_media_start_engine_function(session, SWITCH_MEDIA_TYPE_VIDEO, decode_video_thread, &max_pictures);
 
        switch_ivr_play_file(session, NULL, moh, NULL);
 
@@ -762,7 +762,7 @@ SWITCH_STANDARD_APP(decode_video_function)
        switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "OK");
 
 
-       switch_core_media_end_video_function(session);
+       switch_core_media_end_engine_function(session, SWITCH_MEDIA_TYPE_VIDEO);
        switch_core_session_video_reset(session);
 }
 
index 4f964827c81cd413790dc542868c7e24c77e591f..2ef2bac87f8041717d412206b77c61dc09d79bba 100644 (file)
@@ -287,6 +287,8 @@ switch_io_routines_t rtc_io_routines = {
        /*.state_change */ NULL,
        /*.read_video_frame */ rtc_read_video_frame,
        /*.write_video_frame */ rtc_write_video_frame,
+       /*.read_text_frame */ NULL,
+       /*.write_text_frame */ NULL,
        /*.state_run*/ NULL,
        /*.get_jb*/ rtc_get_jb
 };
@@ -330,6 +332,7 @@ void rtc_attach_private(switch_core_session_t *session, private_object_t *tech_p
        switch_core_media_check_dtmf_type(session);
        switch_channel_set_cap(tech_pvt->channel, CC_JITTERBUFFER);
        switch_channel_set_cap(tech_pvt->channel, CC_FS_RTP);
+       switch_channel_set_cap(tech_pvt->channel, CC_IO_OVERRIDE);
        switch_media_handle_create(&tech_pvt->media_handle, session, &tech_pvt->mparams);
        switch_core_session_set_private(session, tech_pvt);
 
index 36ef8b06b9803aa29df14bffaabd46c889ab1ee5..017150d6ed50f1b80374c97ec98679c737bcd929 100644 (file)
@@ -62,6 +62,8 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
 static switch_status_t sofia_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
 static switch_status_t sofia_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
 static switch_status_t sofia_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
+static switch_status_t sofia_read_text_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
+static switch_status_t sofia_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
 static switch_status_t sofia_kill_channel(switch_core_session_t *session, int sig);
 
 /* BODY OF THE MODULE */
@@ -918,6 +920,16 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session)
        return SWITCH_STATUS_SUCCESS;
 }
 
+static switch_status_t sofia_read_text_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
+{
+       return switch_core_media_read_frame(session, frame, flags, stream_id, SWITCH_MEDIA_TYPE_TEXT);
+}
+
+static switch_status_t sofia_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
+{
+       return switch_core_media_write_frame(session, frame, flags, stream_id, SWITCH_MEDIA_TYPE_TEXT);
+}
+
 static switch_status_t sofia_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
 {
        private_object_t *tech_pvt = (private_object_t *) switch_core_session_get_private(session);
@@ -4279,6 +4291,8 @@ switch_io_routines_t sofia_io_routines = {
        /*.state_change */ NULL,
        /*.read_video_frame */ sofia_read_video_frame,
        /*.write_video_frame */ sofia_write_video_frame,
+       /*.read_text_frame */ sofia_read_text_frame,
+       /*.write_text_frame */ sofia_write_text_frame,
        /*.state_run*/ NULL,
        /*.get_jb*/ sofia_get_jb
 };
index ce42dc1875449c71963a137162b98dbd39252d8d..eafde4671646673165b5b2cf38949148e6834dd8 100644 (file)
@@ -122,6 +122,8 @@ switch_io_routines_t crtp_io_routines = {
        /*state_change*/ NULL,
        /*read_video_frame*/ NULL,
        /*write_video_frame*/ NULL,
+       /*read_text_frame*/ NULL,
+       /*write_text_frame*/ NULL,
        /*state_run*/ NULL
 
 
index 10fe226649e5456e100dbf960e99cb0b171ea982..9c493bf9a90dcc2859961d9331f161a2aee0c45e 100644 (file)
@@ -159,6 +159,7 @@ void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t *
        switch_channel_set_cap(tech_pvt->channel, CC_PROXY_MEDIA);
        switch_channel_set_cap(tech_pvt->channel, CC_JITTERBUFFER);
        switch_channel_set_cap(tech_pvt->channel, CC_FS_RTP);
+       switch_channel_set_cap(tech_pvt->channel, CC_RTP_RTT);
        switch_channel_set_cap(tech_pvt->channel, CC_QUEUEABLE_DTMF_DELAY);
 
 
index f15ff6efc5917171bf21eabda1995fc9ce9e6efc..984d1d4f226a6b92066c8e8fba6513253092bd61 100644 (file)
@@ -53,7 +53,7 @@
 #include <poll.h>
 #define closesocket(x) close(x)
 #endif
-#include <switch_utils.h>
+#include <switch.h>
 #include "mcast.h"
 
 
index b839660082427477d1ca0d286ff6bb2946b605e2..15a5d6825d12513bceeb91d4e00e0af38b153052 100644 (file)
@@ -130,6 +130,10 @@ static switch_bool_t check_name(const char *name)
 }
 
 
+static switch_status_t verto_read_text_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
+static switch_status_t verto_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
+static void set_text_funcs(switch_core_session_t *session);
+
 static verto_profile_t *find_profile(const char *name);
 static jsock_t *get_jsock(const char *uuid);
 
@@ -2116,6 +2120,11 @@ switch_endpoint_interface_t *verto_endpoint_interface = NULL;
 
 static switch_status_t verto_on_destroy(switch_core_session_t *session)
 {
+       verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+       switch_buffer_destroy(&tech_pvt->text_read_buffer);
+       switch_buffer_destroy(&tech_pvt->text_write_buffer);
+
        UNPROTECT_INTERFACE(verto_endpoint_interface);
        return SWITCH_STATUS_SUCCESS;
 }
@@ -2576,6 +2585,7 @@ static int verto_recover_callback(switch_core_session_t *session)
        }
 
        tech_pvt = switch_core_session_alloc(session, sizeof(*tech_pvt));
+       tech_pvt->pool = switch_core_session_get_pool(session);
        tech_pvt->session = session;
        tech_pvt->channel = channel;
        tech_pvt->jsock_uuid = (char *) jsock_uuid_str;
@@ -3261,7 +3271,7 @@ static void parse_user_vars(cJSON *obj, switch_core_session_t *session)
 
 static switch_bool_t verto__info_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
 {
-       cJSON *msg = NULL, *dialog = NULL;
+       cJSON *msg = NULL, *dialog = NULL, *txt = NULL;
        const char *call_id = NULL, *dtmf = NULL;
        switch_bool_t r = SWITCH_TRUE;
        char *proto = VERTO_CHAT_PROTO;
@@ -3298,6 +3308,43 @@ static switch_bool_t verto__info_func(const char *method, cJSON *params, jsock_t
                }
        }
        
+       if ((txt = cJSON_GetObjectItem(params, "txt"))) {
+               switch_core_session_t *session;
+
+               if ((session = switch_core_session_locate(call_id))) {
+                       verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+                       char charbuf[2] = "";
+                       char *chardata = NULL;
+                       cJSON *data;
+                       
+                       if ((data = cJSON_GetObjectItem(txt, "code"))) {
+                               charbuf[0] = data->valueint;
+                               chardata = charbuf;
+                       } else if ((data = cJSON_GetObjectItem(txt, "chars"))) {
+                               if (data->valuestring) {
+                                       chardata = data->valuestring;
+                               } else if (data->valueint) {
+                                       charbuf[0] = data->valueint;
+                                       chardata = charbuf;
+                               }
+                       }
+
+
+                       if (chardata) {
+                               switch_mutex_lock(tech_pvt->text_read_mutex);
+                               switch_buffer_write(tech_pvt->text_read_buffer, chardata, strlen(chardata));
+                               switch_mutex_unlock(tech_pvt->text_read_mutex);
+                       
+                               if ((switch_mutex_trylock(tech_pvt->text_cond_mutex) == SWITCH_STATUS_SUCCESS)) {
+                                       switch_thread_cond_signal(tech_pvt->text_cond);
+                                       switch_mutex_unlock(tech_pvt->text_cond_mutex);
+                               }
+                       }
+                       switch_core_session_rwunlock(session);
+               }
+               
+       }
+
        if ((msg = cJSON_GetObjectItem(params, "msg"))) {
                switch_event_t *event;
                char *to = (char *) cJSON_GetObjectCstr(msg, "to");
@@ -3380,6 +3427,8 @@ static switch_bool_t verto__info_func(const char *method, cJSON *params, jsock_t
        return r;
 }
 
+
+
 static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock_t *jsock, cJSON **response)
 {
        cJSON *obj = cJSON_CreateObject(), *screenShare = NULL, *dedEnc = NULL, *mirrorInput, *bandwidth = NULL, *canvas = NULL;
@@ -3431,12 +3480,13 @@ static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock
 
        tech_pvt = switch_core_session_alloc(session, sizeof(*tech_pvt));
        tech_pvt->session = session;
+       tech_pvt->pool = switch_core_session_get_pool(session);
        tech_pvt->channel = channel;
        tech_pvt->jsock_uuid = switch_core_session_strdup(session, jsock->uuid_str);
        tech_pvt->r_sdp = switch_core_session_strdup(session, sdp);
        switch_core_media_set_sdp_codec_string(session, sdp, SDP_TYPE_REQUEST);
        switch_core_session_set_private_class(session, tech_pvt, SWITCH_PVT_SECONDARY);
-
+       set_text_funcs(session);
 
        tech_pvt->call_id = switch_core_session_strdup(session, call_id);
        if ((tech_pvt->smh = switch_core_session_get_media_handle(session))) {
@@ -5042,6 +5092,115 @@ switch_io_routines_t verto_io_routines = {
        /*.outgoing_channel */ verto_outgoing_channel
 };
 
+
+switch_io_routines_t verto_io_override = {
+       /*.outgoing_channel */ NULL,
+       /*.read_frame */ NULL,
+       /*.write_frame */ NULL,
+       /*.kill_channel */ NULL,
+       /*.send_dtmf */ NULL,
+       /*.receive_message */ NULL,
+       /*.receive_event */ NULL,
+       /*.state_change */ NULL,
+       /*.read_video_frame */ NULL,
+       /*.write_video_frame */ NULL,
+       /*.read_text_frame */ verto_read_text_frame,
+       /*.write_text_frame */ verto_write_text_frame,
+       /*.state_run*/ NULL,
+       /*.get_jb*/ NULL
+};
+
+
+static switch_status_t verto_read_text_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
+{
+       verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+       switch_status_t status;
+       
+       switch_mutex_lock(tech_pvt->text_cond_mutex);
+
+       status = switch_thread_cond_timedwait(tech_pvt->text_cond, tech_pvt->text_cond_mutex, 100000);
+       switch_mutex_unlock(tech_pvt->text_cond_mutex);
+
+       *frame = &tech_pvt->text_read_frame;
+       (*frame)->flags = 0;
+
+       switch_mutex_lock(tech_pvt->text_read_mutex);
+       if (switch_buffer_inuse(tech_pvt->text_read_buffer)) {
+               status = SWITCH_STATUS_SUCCESS;
+               tech_pvt->text_read_frame.datalen = switch_buffer_read(tech_pvt->text_read_buffer, tech_pvt->text_read_frame.data, 100);
+       } else {
+               (*frame)->flags |= SFF_CNG;
+               tech_pvt->text_read_frame.datalen = 2;
+               status = SWITCH_STATUS_BREAK;
+       }
+       switch_mutex_unlock(tech_pvt->text_read_mutex);
+
+
+
+       return status;
+}
+
+static switch_status_t verto_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
+{
+       verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+       switch_mutex_lock(tech_pvt->text_write_mutex);
+
+       
+       if (frame) {
+               switch_buffer_write(tech_pvt->text_write_buffer, frame->data, frame->datalen);
+       }
+
+       if (switch_buffer_inuse(tech_pvt->text_write_buffer)) {
+               uint32_t datalen;
+               switch_byte_t data[SWITCH_RTP_MAX_BUF_LEN] = "";
+
+               if ((datalen = switch_buffer_read(tech_pvt->text_write_buffer, data, 100))) {
+                       cJSON *obj = NULL, *txt = NULL, *params = NULL;
+                       jsock_t *jsock;
+
+                       obj = jrpc_new_req("verto.info", tech_pvt->call_id, &params);
+                       txt = json_add_child_obj(params, "txt", NULL);
+                       cJSON_AddItemToObject(txt, "chars", cJSON_CreateString((char *)data));
+
+                       if ((jsock = get_jsock(tech_pvt->jsock_uuid))) {
+                               jsock_queue_event(jsock, &obj, SWITCH_TRUE);
+                               switch_thread_rwlock_unlock(jsock->rwlock);
+                       } else {
+                               cJSON_Delete(obj);
+                       }
+               }
+       }
+       
+
+       switch_mutex_unlock(tech_pvt->text_write_mutex);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+static void set_text_funcs(switch_core_session_t *session)
+{
+       if ((switch_core_session_override_io_routines(session, &verto_io_override) == SWITCH_STATUS_SUCCESS)) {
+               verto_pvt_t *tech_pvt = switch_core_session_get_private_class(session, SWITCH_PVT_SECONDARY);
+
+               tech_pvt->text_read_frame.data = tech_pvt->text_read_frame_data;
+
+               switch_mutex_init(&tech_pvt->text_read_mutex, SWITCH_MUTEX_NESTED, tech_pvt->pool);
+               switch_mutex_init(&tech_pvt->text_write_mutex, SWITCH_MUTEX_NESTED, tech_pvt->pool);
+               switch_mutex_init(&tech_pvt->text_cond_mutex, SWITCH_MUTEX_NESTED, tech_pvt->pool);
+               switch_thread_cond_create(&tech_pvt->text_cond, tech_pvt->pool);
+
+               switch_buffer_create_dynamic(&tech_pvt->text_read_buffer, 512, 1024, 0);
+               switch_buffer_create_dynamic(&tech_pvt->text_write_buffer, 512, 1024, 0);
+
+               switch_channel_set_flag(switch_core_session_get_channel(session), CF_TEXT);
+               switch_core_session_start_text_thread(session);
+       }
+}
+
+
 static char *verto_get_dial_string(const char *uid, switch_stream_handle_t *rstream)
 {
        jsock_t *jsock;
@@ -5187,11 +5346,14 @@ static switch_call_cause_t verto_outgoing_channel(switch_core_session_t *session
                char name[512];
                
                tech_pvt = switch_core_session_alloc(*new_session, sizeof(*tech_pvt));
+               tech_pvt->pool = switch_core_session_get_pool(*new_session);
                tech_pvt->session = *new_session;
                tech_pvt->channel = channel;
                tech_pvt->jsock_uuid = switch_core_session_strdup(*new_session, jsock_uuid_str);
+
                switch_core_session_set_private_class(*new_session, tech_pvt, SWITCH_PVT_SECONDARY);
-               
+               set_text_funcs(*new_session);
+
                if (session) {
                        switch_channel_t *ochannel = switch_core_session_get_channel(session);
                        
index c39c74d6817c53b41a2af855a1e5341c46ad9675..404529e0bfa4380bfcc705367ddc7ec7ec240d99 100644 (file)
@@ -173,6 +173,7 @@ typedef enum {
 } tflag_t;
 
 typedef struct verto_pvt_s {
+       switch_memory_pool_t *pool;
        char *jsock_uuid;
        char *call_id;
        char *r_sdp;
@@ -184,6 +185,17 @@ typedef struct verto_pvt_s {
        switch_call_cause_t remote_hangup_cause;
        time_t detach_time;
        struct verto_pvt_s *next;
+       switch_byte_t text_read_frame_data[SWITCH_RTP_MAX_BUF_LEN];
+       switch_frame_t text_read_frame;
+
+       switch_thread_cond_t *text_cond;
+       switch_mutex_t *text_cond_mutex; 
+       switch_mutex_t *text_read_mutex; 
+       switch_mutex_t *text_write_mutex; 
+
+       switch_buffer_t *text_read_buffer;
+       switch_buffer_t *text_write_buffer;
+
 } verto_pvt_t;
 
 typedef struct verto_vhost_s {
index b2fd1482fe5e6d3b26e12fa8f500500c8a82257e..01745cc6b19df8e07eaaab3b4d5a6d77c8a2dcb6 100644 (file)
@@ -53,6 +53,12 @@ struct switch_buffer {
        int32_t loops;
 };
 
+
+SWITCH_DECLARE(void *) switch_buffer_get_head_pointer(switch_buffer_t *buffer)
+{
+       return buffer->head;
+}
+
 SWITCH_DECLARE(switch_status_t) switch_buffer_reset_partition_data(switch_buffer_t *buffer)
 {
        if (!switch_test_flag(buffer, SWITCH_BUFFER_FLAG_PARTITION)) {
index 121c12bef0eb778f685266a409fbc6709fd22f3d..06b4d312f84fdfd2bb3750e5cf29c258f53870ca 100644 (file)
@@ -47,9 +47,16 @@ static void gen_ice(switch_core_session_t *session, switch_media_type_t type, co
 #define RTCP_MUX
 #define MAX_CODEC_CHECK_FRAMES 50//x:mod_sofia.h
 #define MAX_MISMATCH_FRAMES 5//x:mod_sofia.h
-#define type2str(type) type == SWITCH_MEDIA_TYPE_VIDEO ? "video" : "audio"
+#define type2str(type) type == SWITCH_MEDIA_TYPE_VIDEO ? "video" : (type == SWITCH_MEDIA_TYPE_AUDIO ? "audio" : "text")
 #define VIDEO_REFRESH_FREQ 1000000
 
+#define TEXT_TIMER_MS 100
+#define TEXT_TIMER_SAMPLES 10
+#define TEXT_PERIOD_TIMEOUT 3000
+#define MAX_RED_FRAMES 25
+#define RED_PACKET_SIZE 100
+
+
 typedef enum {
        SMF_INIT = (1 << 0),
        SMF_READY = (1 << 1),
@@ -92,6 +99,24 @@ typedef enum {
        CRYPTO_MODE_FORBIDDEN
 } switch_rtp_crypto_mode_t;
 
+struct switch_rtp_text_factory_s {
+       switch_memory_pool_t *pool;
+       switch_frame_t text_frame;
+       int red_level;
+       switch_byte_t *text_write_frame_data;
+       switch_frame_t text_write_frame;
+       switch_buffer_t *write_buffer;
+       int write_empty;
+       switch_byte_t *red_buf[MAX_RED_FRAMES];
+       int red_bufsize;
+       int red_buflen[MAX_RED_FRAMES];
+       uint32_t red_ts[MAX_RED_FRAMES];
+       int red_pos;
+       int red_max;
+       switch_timer_t timer;
+};
+
+
 typedef struct switch_rtp_engine_s {
        switch_secure_settings_t ssec[CRYPTO_INVALID+1];
        switch_rtp_crypto_key_type_t crypto_type;
@@ -176,6 +201,14 @@ typedef struct switch_rtp_engine_s {
        uint8_t new_dtls;
        uint32_t sdp_bw;
        uint8_t reject_avp;
+       int t140_pt;
+       int red_pt;
+       switch_rtp_text_factory_t *tf;
+
+       switch_engine_function_t engine_function;
+       void *engine_user_data;
+       int8_t engine_function_running;
+
 } switch_rtp_engine_t;
 
 struct switch_media_handle_s {
@@ -184,8 +217,8 @@ struct switch_media_handle_s {
        switch_core_media_flag_t media_flags[SCMF_MAX];
        smh_flag_t flags;
        switch_rtp_engine_t engines[SWITCH_MEDIA_TYPE_TOTAL];
-       switch_mutex_t *read_mutex[2];
-       switch_mutex_t *write_mutex[2];
+       switch_mutex_t *read_mutex[SWITCH_MEDIA_TYPE_TOTAL];
+       switch_mutex_t *write_mutex[SWITCH_MEDIA_TYPE_TOTAL];
        char *codec_order[SWITCH_MAX_CODECS];
        int codec_order_last;
        const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];
@@ -223,9 +256,8 @@ struct switch_media_handle_s {
        switch_time_t last_codec_refresh;
        switch_time_t last_video_refresh_req;
        switch_timer_t video_timer;
-       switch_video_function_t video_function;
-       void *video_user_data;
-       int8_t video_function_running;
+
+
        switch_vid_params_t vid_params; 
        switch_file_handle_t *video_read_fh;
        switch_file_handle_t *video_write_fh;
@@ -236,6 +268,9 @@ struct switch_media_handle_s {
 
        switch_thread_t *video_write_thread;
        int video_write_thread_running;
+
+       switch_time_t last_text_frame;
+
 };
 
 static switch_srtp_crypto_suite_t SUITES[CRYPTO_INVALID] = {
@@ -381,6 +416,7 @@ SWITCH_DECLARE(void) switch_core_media_pass_zrtp_hash2(switch_core_session_t *al
 {
        _switch_core_media_pass_zrtp_hash2(aleg_session, bleg_session, SWITCH_MEDIA_TYPE_AUDIO);
        _switch_core_media_pass_zrtp_hash2(aleg_session, bleg_session, SWITCH_MEDIA_TYPE_VIDEO);
+       _switch_core_media_pass_zrtp_hash2(aleg_session, bleg_session, SWITCH_MEDIA_TYPE_TEXT);
 }
 
 
@@ -424,19 +460,21 @@ static void switch_core_media_find_zrtp_hash(switch_core_session_t *session, sdp
        switch_channel_t *channel = switch_core_session_get_channel(session);
        switch_rtp_engine_t *audio_engine;
        switch_rtp_engine_t *video_engine;
+       switch_rtp_engine_t *text_engine;
        sdp_media_t *m;
        sdp_attribute_t *attr;
-       int got_audio = 0, got_video = 0;
+       int got_audio = 0, got_video = 0, got_text = 0;
 
        if (!session->media_handle) return;
 
        audio_engine = &session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO];
        video_engine = &session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       text_engine = &session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO];
 
 
        switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG1, "Looking for zrtp-hash\n");
        for (m = sdp->sdp_media; m; m = m->m_next) {
-               if (got_audio && got_video) break;
+               if (got_audio && got_video && got_text) break;
                if (m->m_port && ((m->m_type == sdp_media_audio && !got_audio)
                                                  || (m->m_type == sdp_media_video && !got_video))) {
                        for (attr = m->m_attributes; attr; attr = attr->a_next) {
@@ -454,6 +492,12 @@ static void switch_core_media_find_zrtp_hash(switch_core_session_t *session, sdp
                                        switch_channel_set_variable(channel, "r_sdp_video_zrtp_hash", attr->a_value);
                                        video_engine->remote_sdp_zrtp_hash = switch_core_session_strdup(session, attr->a_value);
                                        got_video++;
+                               } else if (m->m_type == sdp_media_text) {
+                                       switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG,
+                                                                         "Found text zrtp-hash; setting r_sdp_video_zrtp_hash=%s\n", attr->a_value);
+                                       switch_channel_set_variable(channel, "r_sdp_text_zrtp_hash", attr->a_value);
+                                       text_engine->remote_sdp_zrtp_hash = switch_core_session_strdup(session, attr->a_value);
+                                       got_text++;
                                }
                                switch_channel_set_flag(channel, CF_ZRTP_HASH);
                                break;
@@ -534,6 +578,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_check_autoadj(switch_core_sess
 {
        switch_rtp_engine_t *a_engine;
        switch_rtp_engine_t *v_engine;
+       switch_rtp_engine_t *t_engine;
        switch_media_handle_t *smh;
        const char *val;
        int x = 0;
@@ -546,6 +591,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_check_autoadj(switch_core_sess
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];      
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];       
        
        if (!switch_media_handle_test_media_flag(smh, SCMF_DISABLE_RTP_AUTOADJ) &&
                !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val)) && 
@@ -561,6 +607,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_check_autoadj(switch_core_sess
                        switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
                        x++;
                }
+
+               if (t_engine->rtp_session) {
+                       switch_rtp_set_flag(t_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+                       x++;
+               }
        }
 
        return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
@@ -758,11 +809,15 @@ SWITCH_DECLARE(payload_map_t *) switch_core_media_add_payload_map(switch_core_se
        switch_mutex_lock(smh->sdp_mutex);
 
        for (pmap = engine->payload_map; pmap && pmap->allocated; pmap = pmap->next) {
-               exists = (!strcasecmp(name, pmap->iananame) && pmap->pt == pt && (!pmap->rate || rate == pmap->rate) && (!pmap->ptime || pmap->ptime == ptime));
+
+               if (type == SWITCH_MEDIA_TYPE_TEXT) {
+                       exists = (type == pmap->type && !strcasecmp(name, pmap->iananame) && pmap->pt == pt);
+               } else {
+                       exists = (type == pmap->type && !strcasecmp(name, pmap->iananame) && pmap->pt == pt && (!pmap->rate || rate == pmap->rate) && (!pmap->ptime || pmap->ptime == ptime));
+               }
 
                if (exists) {
-                       
-                       if (!zstr(fmtp) && !zstr(pmap->rm_fmtp)) {
+                       if (type != SWITCH_MEDIA_TYPE_TEXT && !zstr(fmtp) && !zstr(pmap->rm_fmtp)) {
                                if (strcmp(pmap->rm_fmtp, fmtp)) {
                                        exists = 0;
                                        local_pt = pmap->pt;
@@ -774,7 +829,6 @@ SWITCH_DECLARE(payload_map_t *) switch_core_media_add_payload_map(switch_core_se
                }
        }
 
-
        if (!exists) {
                switch_ssize_t hlen = -1;
 
@@ -888,6 +942,9 @@ SWITCH_DECLARE(void) switch_core_session_clear_crypto(switch_core_session_t *ses
                                                   "srtp_remote_video_crypto_key",
                                                   "srtp_remote_video_crypto_tag",
                                                   "srtp_remote_video_crypto_type",
+                                                  "srtp_remote_text_crypto_key",
+                                                  "srtp_remote_text_crypto_tag",
+                                                  "srtp_remote_text_crypto_type",
                                                   "rtp_secure_media",
                                                   "rtp_secure_media_inbound",
                                                   "rtp_secure_media_outbound",
@@ -903,6 +960,7 @@ SWITCH_DECLARE(void) switch_core_session_clear_crypto(switch_core_session_t *ses
        for (i = 0; i < CRYPTO_INVALID; i++) {
                memset(&smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec[i], 0, sizeof(smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec[i]));
                memset(&smh->engines[SWITCH_MEDIA_TYPE_VIDEO].ssec[i], 0, sizeof(smh->engines[SWITCH_MEDIA_TYPE_VIDEO].ssec[i]));
+               memset(&smh->engines[SWITCH_MEDIA_TYPE_TEXT].ssec[i], 0, sizeof(smh->engines[SWITCH_MEDIA_TYPE_TEXT].ssec[i]));
        }
 
 }
@@ -1177,11 +1235,15 @@ static void switch_core_session_get_recovery_crypto_key(switch_core_session_t *s
                keyvar = "srtp_remote_audio_crypto_key";
                tagvar = "srtp_remote_audio_crypto_tag";
                ctypevar = "srtp_remote_audio_crypto_type";
-       } else {
+       } else if (type == SWITCH_MEDIA_TYPE_VIDEO) {
                keyvar = "srtp_remote_video_crypto_key";
                tagvar = "srtp_remote_video_crypto_tag";
                ctypevar = "srtp_remote_video_crypto_type";
-       }
+       } else if (type == SWITCH_MEDIA_TYPE_TEXT) {
+               keyvar = "srtp_remote_text_crypto_key";
+               tagvar = "srtp_remote_text_crypto_tag";
+               ctypevar = "srtp_remote_text_crypto_type";
+       } else return;
 
        if ((tmp = switch_channel_get_variable(session->channel, keyvar))) {
                if ((tmp = switch_channel_get_variable(session->channel, ctypevar))) {
@@ -1209,8 +1271,12 @@ static void switch_core_session_apply_crypto(switch_core_session_t *session, swi
 
        if (type == SWITCH_MEDIA_TYPE_AUDIO) {
                varname = "rtp_secure_audio_confirmed";
-       } else {
+       } else if (type == SWITCH_MEDIA_TYPE_VIDEO) {
                varname = "rtp_secure_video_confirmed";
+       } else if (type == SWITCH_MEDIA_TYPE_TEXT) {
+               varname = "rtp_secure_text_confirmed";
+       } else {
+               return;
        }
 
        if (!session->media_handle) return;
@@ -1402,6 +1468,10 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio
                                        switch_channel_set_variable(session->channel, "srtp_remote_video_crypto_key", crypto);
                                        switch_channel_set_variable_printf(session->channel, "srtp_remote_video_crypto_tag", "%d", crypto_tag);
                                        switch_channel_set_variable_printf(session->channel, "srtp_remote_video_crypto_type", "%s", switch_core_media_crypto_type2str(ctype));
+                               } else if (engine->type == SWITCH_MEDIA_TYPE_TEXT) {
+                                       switch_channel_set_variable(session->channel, "srtp_remote_text_crypto_key", crypto);
+                                       switch_channel_set_variable_printf(session->channel, "srtp_remote_text_crypto_tag", "%d", crypto_tag);
+                                       switch_channel_set_variable_printf(session->channel, "srtp_remote_text_crypto_type", "%s", switch_core_media_crypto_type2str(ctype));
                                }
 
                                engine->ssec[engine->crypto_type].crypto_tag = crypto_tag;
@@ -1434,6 +1504,9 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio
                } else if (engine->type == SWITCH_MEDIA_TYPE_VIDEO) {
                        switch_channel_set_variable(session->channel, "srtp_remote_video_crypto_key", crypto);
                        switch_channel_set_variable_printf(session->channel, "srtp_remote_video_crypto_type", "%s", switch_core_media_crypto_type2str(ctype));
+               } else if (engine->type == SWITCH_MEDIA_TYPE_TEXT) {
+                       switch_channel_set_variable(session->channel, "srtp_remote_text_crypto_key", crypto);
+                       switch_channel_set_variable_printf(session->channel, "srtp_remote_text_crypto_type", "%s", switch_core_media_crypto_type2str(ctype));
                }
 
                engine->ssec[engine->crypto_type].crypto_tag = crypto_tag;
@@ -1483,6 +1556,9 @@ SWITCH_DECLARE(void) switch_core_session_check_outgoing_crypto(switch_core_sessi
 
                switch_core_media_build_crypto(session->media_handle,
                                                                           SWITCH_MEDIA_TYPE_VIDEO, SWITCH_NO_CRYPTO_TAG, smh->crypto_suite_order[i], SWITCH_RTP_CRYPTO_SEND, 0);
+
+               switch_core_media_build_crypto(session->media_handle,
+                                                                          SWITCH_MEDIA_TYPE_TEXT, SWITCH_NO_CRYPTO_TAG, smh->crypto_suite_order[i], SWITCH_RTP_CRYPTO_SEND, 0);
        }
 
 }
@@ -1544,7 +1620,7 @@ static void set_stats(switch_core_session_t *session, switch_media_type_t type,
 SWITCH_DECLARE(void) switch_core_media_sync_stats(switch_core_session_t *session)
 {
        switch_media_handle_t *smh;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
 
        switch_assert(session);
 
@@ -1554,6 +1630,7 @@ SWITCH_DECLARE(void) switch_core_media_sync_stats(switch_core_session_t *session
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];      
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];       
 
        if (a_engine->rtp_session) {
                switch_rtp_sync_stats(a_engine->rtp_session);
@@ -1563,6 +1640,10 @@ SWITCH_DECLARE(void) switch_core_media_sync_stats(switch_core_session_t *session
                switch_rtp_sync_stats(v_engine->rtp_session);
        }
 
+       if (t_engine->rtp_session) {
+               switch_rtp_sync_stats(t_engine->rtp_session);
+       }
+
 }
 
 SWITCH_DECLARE(void) switch_core_media_set_stats(switch_core_session_t *session)
@@ -1576,6 +1657,7 @@ SWITCH_DECLARE(void) switch_core_media_set_stats(switch_core_session_t *session)
 
        set_stats(session, SWITCH_MEDIA_TYPE_AUDIO, "audio");
        set_stats(session, SWITCH_MEDIA_TYPE_VIDEO, "video");
+       set_stats(session, SWITCH_MEDIA_TYPE_TEXT, "text");
 }
 
 
@@ -1583,7 +1665,7 @@ SWITCH_DECLARE(void) switch_core_media_set_stats(switch_core_session_t *session)
 SWITCH_DECLARE(void) switch_media_handle_destroy(switch_core_session_t *session)
 {
        switch_media_handle_t *smh;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine;//, *t_engine;
 
        switch_assert(session);
 
@@ -1593,6 +1675,7 @@ SWITCH_DECLARE(void) switch_media_handle_destroy(switch_core_session_t *session)
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];      
+       //t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];     
 
        
        if (smh->video_timer.timer_interface) {
@@ -1644,6 +1727,7 @@ SWITCH_DECLARE(switch_status_t) switch_media_handle_create(switch_media_handle_t
                *smhp = session->media_handle;
                switch_set_flag(session->media_handle, SMF_INIT);
                session->media_handle->media_flags[SCMF_RUNNING] = 1;
+
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].type = SWITCH_MEDIA_TYPE_AUDIO;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].crypto_type = CRYPTO_INVALID;
@@ -1652,16 +1736,30 @@ SWITCH_DECLARE(switch_status_t) switch_media_handle_create(switch_media_handle_t
                        session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec[i].crypto_type = i;
                }
 
+
+
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].type = SWITCH_MEDIA_TYPE_AUDIO;
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].crypto_type = CRYPTO_INVALID;
+
+               for (i = 0; i < CRYPTO_INVALID; i++) {
+                       session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].ssec[i].crypto_type = i;
+               }
+
+
+               
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].type = SWITCH_MEDIA_TYPE_VIDEO;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].crypto_type = CRYPTO_INVALID;
-
+               
 
                switch_channel_set_variable(session->channel, "video_media_flow", "sendrecv");
                switch_channel_set_variable(session->channel, "audio_media_flow", "sendrecv");
+               switch_channel_set_variable(session->channel, "text_media_flow", "sendrecv");
 
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].smode = SWITCH_MEDIA_FLOW_SENDRECV;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].smode = SWITCH_MEDIA_FLOW_SENDRECV;
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].smode = SWITCH_MEDIA_FLOW_SENDRECV;
 
                for (i = 0; i < CRYPTO_INVALID; i++) {
                        session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].ssec[i].crypto_type = i;
@@ -1692,14 +1790,25 @@ SWITCH_DECLARE(switch_status_t) switch_media_handle_create(switch_media_handle_t
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].ssrc = 
                        (uint32_t) ((intptr_t) &session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO] + (uint32_t) time(NULL) / 2);
 
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].ssrc = 
+                       (uint32_t) ((intptr_t) &session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT] + (uint32_t) time(NULL) / 2);
+
+
+
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].payload_map = switch_core_alloc(session->pool, sizeof(payload_map_t));
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].cur_payload_map = session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].payload_map;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].cur_payload_map->current = 1;
+
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].payload_map = switch_core_alloc(session->pool, sizeof(payload_map_t));
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].cur_payload_map = session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].payload_map;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].cur_payload_map->current = 1;
                session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].codec_settings.video.try_hardware_encoder = 1;
 
+
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].payload_map = switch_core_alloc(session->pool, sizeof(payload_map_t));
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].cur_payload_map = session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].payload_map;
+               session->media_handle->engines[SWITCH_MEDIA_TYPE_TEXT].cur_payload_map->current = 1;
+
                switch_channel_set_flag(session->channel, CF_DTLS_OK);
 
                status = SWITCH_STATUS_SUCCESS;
@@ -1874,7 +1983,7 @@ static void check_jb(switch_core_session_t *session, const char *input, int32_t
 {
        const char *val;
        switch_media_handle_t *smh;
-       switch_rtp_engine_t *a_engine = NULL, *v_engine = NULL;
+       switch_rtp_engine_t *a_engine = NULL, *v_engine = NULL, *t_engine = NULL;
 
        switch_assert(session);
 
@@ -1884,6 +1993,7 @@ static void check_jb(switch_core_session_t *session, const char *input, int32_t
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
 
        if (!zstr(input)) {
@@ -1935,6 +2045,32 @@ static void check_jb(switch_core_session_t *session, const char *input, int32_t
                                return;
                        }
                }
+
+               if (t_engine->rtp_session) {
+                       if (!strncasecmp(input, "tbsize:", 7)) {
+                               int frames = 0, max_frames = 0;
+                               s = input + 7;
+                               
+                               frames = atoi(s);
+
+                               if ((s = strchr(s, ':')) && *(s+1) != '\0') {
+                                       max_frames = atoi(s+1);
+                               }
+                               
+                               if (frames > 0) {
+                                       switch_rtp_set_video_buffer_size(t_engine->rtp_session, frames, max_frames);
+                               }
+                               return;
+                       } else if (!strncasecmp(input, "tdebug:", 7)) {
+                               s = input + 7;
+                               
+                               if (s && !strcmp(s, "off")) {
+                                       s = NULL;
+                               }
+                               switch_rtp_debug_jitter_buffer(t_engine->rtp_session, s);
+                               return;
+                       }
+               }
        }
        
 
@@ -2134,6 +2270,133 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_lock_unlock(switch_core_s
 }
 
 
+
+
+//?
+SWITCH_DECLARE(switch_status_t) switch_rtp_text_factory_create(switch_rtp_text_factory_t **tfP, switch_memory_pool_t *pool)
+{
+       int x;
+
+       *tfP = switch_core_alloc(pool, sizeof(**tfP));
+       
+       switch_buffer_create_dynamic(&(*tfP)->write_buffer,  512, 1024, 0);
+       (*tfP)->pool = pool;
+       (*tfP)->text_write_frame_data = switch_core_alloc(pool, SWITCH_RTP_MAX_BUF_LEN);
+       (*tfP)->text_write_frame.packet = (*tfP)->text_write_frame_data;
+       (*tfP)->text_write_frame.data = (switch_byte_t *)(*tfP)->text_write_frame.packet + 12;
+       (*tfP)->text_write_frame.buflen = SWITCH_RTP_MAX_BUF_LEN - 12;
+
+       (*tfP)->red_max = 5;
+       (*tfP)->red_bufsize = SWITCH_RTP_MAX_BUF_LEN;
+
+       switch_core_timer_init(&(*tfP)->timer, "soft", TEXT_TIMER_MS, TEXT_TIMER_SAMPLES, pool);
+
+       for(x = 0; x < (*tfP)->red_max; x++) {
+               (*tfP)->red_buf[x] = switch_core_alloc(pool, SWITCH_RTP_MAX_BUF_LEN);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_rtp_text_factory_destroy(switch_rtp_text_factory_t **tfP)
+{
+       switch_core_timer_destroy(&(*tfP)->timer);
+       switch_buffer_destroy(&(*tfP)->write_buffer);
+
+       return SWITCH_STATUS_SUCCESS;;
+}
+
+#include <wchar.h>
+
+static int get_rtt_red_seq(int want_seq, void *data, switch_size_t datalen, int seq, switch_payload_t *new_payload, void *new_data, uint32_t *new_datalen)
+{
+       unsigned char *buf = data;
+       int count = 0;
+       unsigned char *e = (buf + datalen);
+
+       int len[MAX_RED_FRAMES] = { 0 };
+       int pt[MAX_RED_FRAMES] = { 0 };
+       int idx = 0, x = 0;
+
+       *new_datalen = datalen;
+
+       *(buf + datalen) = '\0';
+       
+       while (*buf & 0x80) {
+               if (buf + 3 > e) {
+                       *new_datalen = 0;
+                       return 0;
+               }
+               
+               pt[count] = *buf & 0x7F;
+               len[count] = (ntohs(*(uint16_t *)(buf + 2)) & 0x03ff);
+               buf += 4;
+               count++;
+       }
+
+       buf++;
+
+       idx = count - (seq - want_seq);
+
+       if (idx < 0) {
+               *new_datalen = 0;
+               return 0;
+       }
+
+       if (!len[idx]) {
+               *new_datalen = len[idx];
+               return 0;
+       }
+
+       for(x = 0; x < idx; x++) {
+               buf += len[x];
+       }
+
+       *new_datalen = len[idx];
+       *new_payload = pt[idx];
+
+       memcpy(new_data, buf, len[idx]);
+       
+       *(((char *)new_data) + len[idx]) = '\0';
+
+       return 1;
+       
+}
+
+static void *get_rtt_payload(void *data, switch_size_t datalen, switch_payload_t *new_payload, uint32_t *new_datalen, int *red_level)
+{
+       unsigned char *buf = data;
+       int bytes = 0, count = 0, pt = 0, len = 0;//, ts = 0;
+       unsigned char *e = (buf + datalen);
+
+       *new_datalen = datalen;
+       *red_level = 1;
+
+       while (*buf & 0x80) {
+               if (buf + 3 > e) {
+                       *new_datalen = 0;
+                       return NULL;
+               }
+               count++;
+               pt = *buf & 0x7F;
+               //ts = ntohs(*(uint16_t *)(buf + 1)) >> 2;
+               len  = (ntohs(*(uint16_t *)(buf + 2)) & 0x03ff);
+               buf += 4;
+               bytes += len;
+       }
+
+       *new_datalen = datalen - bytes - 1 - (count *4);
+       *new_payload = pt;
+       buf += bytes + 1;
+       
+       if (buf > e) {
+               *new_datalen = 0;
+               return NULL;
+       }
+       
+       return buf;
+}
+
 //?
 SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session_t *session, switch_frame_t **frame,
                                                                                                                         switch_io_flag_t flags, int stream_id, switch_media_type_t type)
@@ -2156,7 +2419,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
 
        engine = &smh->engines[type];
 
-       if (!engine->read_codec.implementation || !switch_core_codec_ready(&engine->read_codec)) {
+       if (type != SWITCH_MEDIA_TYPE_TEXT && (!engine->read_codec.implementation || !switch_core_codec_ready(&engine->read_codec))) {
                return SWITCH_STATUS_FALSE;
        }
 
@@ -2176,10 +2439,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
        engine->read_frame.flags = SFF_NONE;
        engine->read_frame.m = SWITCH_FALSE;
        engine->read_frame.img = NULL;
-
+       engine->read_frame.payload = 0;
+       
        while (smh->media_flags[SCMF_RUNNING] && engine->read_frame.datalen == 0) {
                engine->read_frame.flags = SFF_NONE;
                status = switch_rtp_zerocopy_read_frame(engine->rtp_session, &engine->read_frame, flags);
+
                if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
                        if (status == SWITCH_STATUS_TIMEOUT) {
 
@@ -2225,7 +2490,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
 
 
                /* re-set codec if necessary */
-               if (engine->reset_codec > 0) {
+               if (type != SWITCH_MEDIA_TYPE_TEXT && engine->reset_codec > 0) {
                        const char *val;
                        int rtp_timeout_sec = 0;
                        int rtp_hold_timeout_sec = 0;
@@ -2283,6 +2548,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
                        do_cng = 1;
                }
 
+
                if (do_cng) {
                        /* return CNG for now */
                        *frame = &engine->read_frame;
@@ -2379,7 +2645,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
                        switch_channel_queue_dtmf(session->channel, &dtmf);
                }
                
-               if (engine->read_frame.datalen > 0) {
+               if (type != SWITCH_MEDIA_TYPE_TEXT && engine->read_frame.datalen > 0) {
                        uint32_t bytes = 0;
                        int frames = 1;
 
@@ -2466,8 +2732,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
                                                engine->last_seq = engine->read_frame.seq;
 
                                        } else if (smh->media_flags[SCMF_AUTOFIX_TIMING] && is_vbr && switch_rtp_get_jitter_buffer(engine->rtp_session) 
-                                                                                                                                                       && engine->read_frame.timestamp && engine->read_frame.seq) {
-
+                                                          && engine->read_frame.timestamp && engine->read_frame.seq && engine->read_impl.samples_per_second) {
                                                uint32_t codec_ms = (int) (engine->read_frame.timestamp -
                                                                   engine->last_ts) / (engine->read_impl.samples_per_second / 1000);
 
@@ -2582,12 +2847,72 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session
                        break;
                }
        }
-       
+
        if (engine->read_frame.datalen == 0) {
                *frame = NULL;
        }
 
-       *frame = &engine->read_frame;
+
+       if (type == SWITCH_MEDIA_TYPE_TEXT && !switch_test_flag((&engine->read_frame), SFF_CNG)) {
+               if (engine->red_pt) {
+                       unsigned char *p = engine->read_frame.data;
+                       
+                       *(p + engine->read_frame.datalen) = '\0';
+                       engine->tf->text_frame = engine->read_frame;
+                       
+                       if (switch_test_flag((&engine->read_frame), SFF_PLC)) {
+                               switch_jb_t *jb = switch_core_session_get_jb(session, SWITCH_MEDIA_TYPE_TEXT);
+                               int i = 0;
+                                       
+                               engine->tf->text_frame.datalen = 0;
+
+                               for (i = 1; i < 3; i++) {
+                                       switch_frame_t frame = { 0 };
+                                       uint8_t buf[SWITCH_RTP_MAX_BUF_LEN];
+                                       frame.data = buf;
+                                       frame.buflen = sizeof(buf);
+
+                                       if (switch_jb_peek_frame(jb, 0, engine->read_frame.seq, i, &frame) == SWITCH_STATUS_SUCCESS) {
+                                               if (get_rtt_red_seq(engine->read_frame.seq,
+                                                                                       frame.data, 
+                                                                                       frame.datalen, 
+                                                                                       frame.seq,
+                                                                                       &engine->tf->text_frame.payload,
+                                                                                       engine->tf->text_frame.data,
+                                                                                       &engine->tf->text_frame.datalen)) {
+                                                       break;
+                                                       
+                                               }
+                                       }
+
+                               }
+
+                               if (engine->tf->text_frame.datalen == 0) {
+                                       engine->tf->text_frame.data = "� ";
+                                       engine->tf->text_frame.datalen = strlen(engine->tf->text_frame.data);
+                               }
+
+                       } else {
+                               if (!(engine->tf->text_frame.data = get_rtt_payload(engine->read_frame.data, 
+                                                                                                                                       engine->tf->text_frame.datalen, 
+                                                                                                                                       &engine->tf->text_frame.payload,
+                                                                                                                                       &engine->tf->text_frame.datalen,
+                                                                                                                                       &engine->tf->red_level))) {
+                                       engine->tf->text_frame.datalen = 0;
+                               }
+                       }
+
+                       *frame = &engine->tf->text_frame;
+
+                       if ((*frame)->datalen == 0) {
+                               (*frame)->flags |= SFF_CNG;
+                               (*frame)->data = "";
+                       }
+               }
+
+       } else {
+               *frame = &engine->read_frame;
+       }
 
        status = SWITCH_STATUS_SUCCESS;
 
@@ -2626,26 +2951,29 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_write_frame(switch_core_sessio
                return SWITCH_STATUS_SUCCESS;
        }  
 
-       while (!(engine->read_codec.implementation && switch_rtp_ready(engine->rtp_session))) {
-               if (switch_channel_ready(session->channel)) {
-                       switch_yield(10000);
-               } else {
-                       return SWITCH_STATUS_GENERR;
+       if (type != SWITCH_MEDIA_TYPE_TEXT) {
+
+               while (!(engine->read_codec.implementation && switch_rtp_ready(engine->rtp_session))) {
+                       if (switch_channel_ready(session->channel)) {
+                               switch_yield(10000);
+                       } else {
+                               return SWITCH_STATUS_GENERR;
+                       }
                }
-       }
 
-       if (!engine->read_codec.implementation || !switch_core_codec_ready(&engine->read_codec)) {
-               return SWITCH_STATUS_GENERR;
-       }
+               if (!engine->read_codec.implementation || !switch_core_codec_ready(&engine->read_codec)) {
+                       return SWITCH_STATUS_GENERR;
+               }
 
-       if (!switch_test_flag(frame, SFF_CNG) && !switch_test_flag(frame, SFF_PROXY_PACKET)) {
-               if (engine->read_impl.encoded_bytes_per_packet) {
-                       bytes = engine->read_impl.encoded_bytes_per_packet;
-                       frames = ((int) frame->datalen / bytes);
-               } else
-                       frames = 1;
+               if (!switch_test_flag(frame, SFF_CNG) && !switch_test_flag(frame, SFF_PROXY_PACKET)) {
+                       if (engine->read_impl.encoded_bytes_per_packet) {
+                               bytes = engine->read_impl.encoded_bytes_per_packet;
+                               frames = ((int) frame->datalen / bytes);
+                       } else
+                               frames = 1;
 
-               samples = frames * engine->read_impl.samples_per_packet;
+                       samples = frames * engine->read_impl.samples_per_packet;
+               }
        }
 
        engine->timestamp_send += samples;
@@ -3083,7 +3411,7 @@ SWITCH_DECLARE(void) switch_core_media_clear_ice(switch_core_session_t *session)
 
 SWITCH_DECLARE(void) switch_core_media_pause(switch_core_session_t *session)
 {
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
 
        switch_assert(session);
@@ -3094,6 +3422,7 @@ SWITCH_DECLARE(void) switch_core_media_pause(switch_core_session_t *session)
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        if (a_engine->rtp_session) {
                switch_rtp_set_flag(a_engine->rtp_session, SWITCH_RTP_FLAG_PAUSE);
@@ -3102,11 +3431,15 @@ SWITCH_DECLARE(void) switch_core_media_pause(switch_core_session_t *session)
        if (v_engine->rtp_session) {
                switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_PAUSE);
        }
+
+       if (t_engine->rtp_session) {
+               switch_rtp_set_flag(t_engine->rtp_session, SWITCH_RTP_FLAG_PAUSE);
+       }
 }
 
 SWITCH_DECLARE(void) switch_core_media_resume(switch_core_session_t *session)
 {
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
 
        switch_assert(session);
@@ -3117,6 +3450,7 @@ SWITCH_DECLARE(void) switch_core_media_resume(switch_core_session_t *session)
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        if (a_engine->rtp_session) {
                switch_rtp_clear_flag(a_engine->rtp_session, SWITCH_RTP_FLAG_PAUSE);
@@ -3125,6 +3459,10 @@ SWITCH_DECLARE(void) switch_core_media_resume(switch_core_session_t *session)
        if (v_engine->rtp_session) {
                switch_rtp_clear_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_PAUSE);
        }
+
+       if (t_engine->rtp_session) {
+               switch_rtp_clear_flag(t_engine->rtp_session, SWITCH_RTP_FLAG_PAUSE);
+       }
 }
 
 
@@ -3713,13 +4051,13 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s
        switch_channel_t *channel = switch_core_session_get_channel(session);
        const char *val;
        const char *crypto = NULL;
-       int got_crypto = 0, got_video_crypto = 0, got_audio = 0, saw_audio = 0, got_avp = 0, got_video_avp = 0, got_video_savp = 0, got_savp = 0, got_udptl = 0, got_webrtc = 0;
+       int got_crypto = 0, got_video_crypto = 0, got_audio = 0, saw_audio = 0, got_avp = 0, got_video_avp = 0, got_video_savp = 0, got_savp = 0, got_udptl = 0, got_webrtc = 0, got_text = 0, got_text_crypto = 0;
        int scrooge = 0;
        sdp_parser_t *parser = NULL;
        sdp_session_t *sdp;
        const switch_codec_implementation_t **codec_array;
        int total_codecs;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
        uint32_t near_rate = 0;
        const switch_codec_implementation_t *mimp = NULL, *near_match = NULL;
@@ -3749,6 +4087,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        codec_array = smh->codecs;
        total_codecs = smh->mparams->num_codecs;
@@ -3843,6 +4182,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s
 
        check_ice(smh, SWITCH_MEDIA_TYPE_AUDIO, sdp, NULL);
        check_ice(smh, SWITCH_MEDIA_TYPE_VIDEO, sdp, NULL);
+       check_ice(smh, SWITCH_MEDIA_TYPE_TEXT, sdp, NULL);
 
        if ((sdp->sdp_connection && sdp->sdp_connection->c_address && !strcmp(sdp->sdp_connection->c_address, "0.0.0.0"))) {
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "RFC2543 from March 1999 called; They want their 0.0.0.0 hold method back.....\n");
@@ -4629,6 +4969,102 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s
                                }
                        }
 
+               } else if (switch_channel_var_true(session->channel, "rtp_enable_text") && !got_text && m->m_type == sdp_media_text && m->m_port) {
+                       sdp_rtpmap_t *map;
+                       payload_map_t *red_pmap = NULL;
+
+
+                       connection = sdp->sdp_connection;
+                       if (m->m_connections) {
+                               connection = m->m_connections;
+                       }
+                       
+                       if (!connection) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot find a c= line in the sdp at media or session level!\n");
+                               match = 0;
+                               break;
+                       }
+
+                       switch_channel_set_variable(session->channel, "text_possible", "true");
+                       switch_channel_set_flag(session->channel, CF_TEXT_SDP_RECVD);
+                       switch_channel_set_flag(session->channel, CF_TEXT_POSSIBLE);
+
+                       got_text++;
+                       
+                       for (map = m->m_rtpmaps; map; map = map->rm_next) {
+                               payload_map_t *pmap;
+
+                               pmap = switch_core_media_add_payload_map(session,
+                                                                                                                SWITCH_MEDIA_TYPE_TEXT,
+                                                                                                                map->rm_encoding,
+                                                                                                                NULL,
+                                                                                                                NULL,
+                                                                                                                SDP_TYPE_REQUEST,
+                                                                                                                map->rm_pt,
+                                                                                                                1000,
+                                                                                                                0,
+                                                                                                                1,
+                                                                                                                SWITCH_TRUE);
+
+
+                               pmap->remote_sdp_ip = switch_core_session_strdup(session, (char *) connection->c_address);
+                               pmap->remote_sdp_port = (switch_port_t) m->m_port;
+                               pmap->rm_fmtp = switch_core_session_strdup(session, (char *) mmap->rm_fmtp);
+                               
+                               pmap->agreed_pt = (switch_payload_t) map->rm_pt;
+                               pmap->recv_pt = (switch_payload_t) map->rm_pt;
+
+
+                               t_engine->cur_payload_map = pmap;
+                               
+                               if (!strcasecmp(map->rm_encoding, "red")) {
+                                       red_pmap = pmap;
+                               }
+                       }
+
+                       t_engine->cur_payload_map = red_pmap;
+
+                       for (attr = m->m_attributes; attr; attr = attr->a_next) {
+                               if (!strcasecmp(attr->a_name, "rtcp") && attr->a_value) {
+                                       switch_channel_set_variable(session->channel, "sip_remote_text_rtcp_port", attr->a_value);
+                                       
+                               } else if (!got_text_crypto && !strcasecmp(attr->a_name, "crypto") && !zstr(attr->a_value)) {
+                                       int crypto_tag;
+                                               
+                                       if (!(smh->mparams->ndlb & SM_NDLB_ALLOW_CRYPTO_IN_AVP) && 
+                                               !switch_true(switch_channel_get_variable(session->channel, "rtp_allow_crypto_in_avp"))) {
+                                               if (m->m_proto != sdp_proto_srtp && !got_webrtc) {
+                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "a=crypto in RTP/AVP, refer to rfc3711\n");
+                                                       match = 0;
+                                                       goto done;
+                                               }
+                                       }
+                                               
+                                       crypto = attr->a_value;
+                                       crypto_tag = atoi(crypto);
+                                               
+                                       got_text_crypto = switch_core_session_check_incoming_crypto(session, 
+                                                                                                                                                               "rtp_has_text_crypto", 
+                                                                                                                                                               SWITCH_MEDIA_TYPE_TEXT, crypto, crypto_tag, sdp_type);
+                                       
+                               }
+                       }
+
+
+                       //map->rm_encoding
+                       //map->rm_fmtp
+                       //map->rm_pt
+                       //t_engine->cur_payload_map = pmap;
+
+                       t_engine->codec_negotiated = 1;
+
+                       if (!t_engine->local_sdp_port) {
+                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_TEXT, 1);
+                       }
+
+                       check_ice(smh, SWITCH_MEDIA_TYPE_TEXT, sdp, m);
+                       //parse rtt
+
                } else if (m->m_type == sdp_media_video && m->m_port) {
                        sdp_rtpmap_t *map;
                        const char *rm_encoding;
@@ -5433,7 +5869,9 @@ SWITCH_DECLARE(void) switch_core_autobind_cpu(void)
 }
 
 
-static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, void *obj)
+
+
+static void *SWITCH_THREAD_FUNC text_helper_thread(switch_thread_t *thread, void *obj)
 {
        struct media_helper *mh = obj;
        switch_core_session_t *session = mh->session;
@@ -5441,24 +5879,154 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi
        switch_status_t status;
        switch_frame_t *read_frame = NULL;
        switch_media_handle_t *smh;
-       uint32_t loops = 0, xloops = 0, vloops = 0;
-       switch_image_t *blank_img = NULL;
-       switch_frame_t fr = { 0 };
-       unsigned char *buf = NULL;
-       switch_rgb_color_t bgcolor;
-       switch_rtp_engine_t *v_engine = NULL;
-       const char *var;
-       int buflen = SWITCH_RTP_MAX_BUF_LEN;
+       switch_rtp_engine_t *t_engine = NULL;
+       unsigned char CR[] = TEXT_UNICODE_LINEFEED;
+       switch_frame_t cr_frame = { 0 };
 
        if (!(smh = session->media_handle)) {
                return NULL;
        }
 
-       switch_core_autobind_cpu();
+       cr_frame.data = CR;
+       cr_frame.datalen = 3;
+       
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
+       t_engine->thread_id = switch_thread_self();
 
-       if ((var = switch_channel_get_variable(session->channel, "core_video_blank_image"))) {
-               blank_img = switch_img_read_png(var, SWITCH_IMG_FMT_I420);
-       }
+       switch_core_session_read_lock(session);
+
+       mh->up = 1;
+
+       switch_core_media_check_dtls(session, SWITCH_MEDIA_TYPE_TEXT);
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Text thread started.\n", switch_channel_get_name(session->channel));
+
+       switch_core_session_write_text_frame(session, &cr_frame, 0, 0);
+
+       while (switch_channel_up_nosig(channel)) {
+
+               if (t_engine->engine_function) {
+                       int run = 0;
+
+                       switch_mutex_lock(smh->control_mutex);
+                       if (t_engine->engine_function_running == 0) {
+                               t_engine->engine_function_running = 1;
+                               run = 1;
+                       }
+                       switch_mutex_unlock(smh->control_mutex);
+                       
+                       if (run) {
+                               t_engine->engine_function(session, t_engine->engine_user_data);
+                               switch_mutex_lock(smh->control_mutex);
+                               t_engine->engine_function = NULL;
+                               t_engine->engine_user_data = NULL;
+                               t_engine->engine_function_running = 0;
+                               switch_mutex_unlock(smh->control_mutex);
+                       }
+               }
+
+               if (!switch_channel_test_flag(session->channel, CF_TEXT_PASSIVE)) {
+
+                       status = switch_core_session_read_text_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+                       
+                       if (!SWITCH_READ_ACCEPTABLE(status)) {
+                               switch_cond_next();
+                               continue;
+                       }
+
+                       if (!switch_test_flag(read_frame, SFF_CNG)) {
+                               if (switch_channel_test_flag(session->channel, CF_TEXT_ECHO)) {
+                                       switch_core_session_write_text_frame(session, read_frame, 0, 0);
+                               }
+                       }
+               }
+
+               switch_core_session_write_text_frame(session, NULL, 0, 0);
+
+
+       }
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Text thread ended\n", switch_channel_get_name(session->channel));
+
+       switch_core_session_rwunlock(session);
+
+       mh->up = 0;
+       return NULL;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_start_text_thread(switch_core_session_t *session)
+{
+       switch_threadattr_t *thd_attr = NULL;
+       switch_memory_pool_t *pool = switch_core_session_get_pool(session);
+       switch_rtp_engine_t *t_engine = NULL;
+       switch_media_handle_t *smh;
+
+       if (!switch_channel_test_flag(session->channel, CF_TEXT)) {
+               return SWITCH_STATUS_NOTIMPL;
+       }
+
+       if (!(smh = session->media_handle)) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
+
+       switch_mutex_lock(smh->control_mutex);
+
+       if (t_engine->media_thread) {
+               switch_mutex_unlock(smh->control_mutex);
+               return SWITCH_STATUS_FALSE;
+       }
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Starting Text thread\n", switch_core_session_get_name(session));
+
+       if (t_engine->rtp_session) {
+               switch_rtp_set_default_payload(t_engine->rtp_session, t_engine->cur_payload_map->agreed_pt);
+       }
+
+       t_engine->mh.session = session;
+       switch_threadattr_create(&thd_attr, pool);
+       switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+       
+       switch_thread_cond_create(&t_engine->mh.cond, pool);
+       switch_mutex_init(&t_engine->mh.cond_mutex, SWITCH_MUTEX_NESTED, pool);
+       //switch_mutex_init(&t_engine->mh.file_read_mutex, SWITCH_MUTEX_NESTED, pool);
+       //switch_mutex_init(&t_engine->mh.file_write_mutex, SWITCH_MUTEX_NESTED, pool);
+       //switch_mutex_init(&smh->read_mutex[SWITCH_MEDIA_TYPE_TEXT], SWITCH_MUTEX_NESTED, pool);
+       //switch_mutex_init(&smh->write_mutex[SWITCH_MEDIA_TYPE_TEXT], SWITCH_MUTEX_NESTED, pool);
+       switch_thread_create(&t_engine->media_thread, thd_attr, text_helper_thread, &t_engine->mh, switch_core_session_get_pool(session));
+
+       switch_mutex_unlock(smh->control_mutex);
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, void *obj)
+{
+       struct media_helper *mh = obj;
+       switch_core_session_t *session = mh->session;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_status_t status;
+       switch_frame_t *read_frame = NULL;
+       switch_media_handle_t *smh;
+       uint32_t loops = 0, xloops = 0, vloops = 0;
+       switch_image_t *blank_img = NULL;
+       switch_frame_t fr = { 0 };
+       unsigned char *buf = NULL;
+       switch_rgb_color_t bgcolor;
+       switch_rtp_engine_t *v_engine = NULL;
+       const char *var;
+       int buflen = SWITCH_RTP_MAX_BUF_LEN;
+
+       if (!(smh = session->media_handle)) {
+               return NULL;
+       }
+
+       switch_core_autobind_cpu();
+
+       if ((var = switch_channel_get_variable(session->channel, "core_video_blank_image"))) {
+               blank_img = switch_img_read_png(var, SWITCH_IMG_FMT_I420);
+       }
 
        if (!blank_img) {
                switch_color_set_rgb(&bgcolor, "#000000");
@@ -5522,23 +6090,23 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi
                        switch_yield(10000);
                        continue;
                }
-
-               if (smh->video_function) {
+               
+               if (v_engine->engine_function) {
                        int run = 0;
 
                        switch_mutex_lock(smh->control_mutex);
-                       if (smh->video_function_running == 0) {
-                               smh->video_function_running = 1;
+                       if (v_engine->engine_function_running == 0) {
+                               v_engine->engine_function_running = 1;
                                run = 1;
                        }
                        switch_mutex_unlock(smh->control_mutex);
 
                        if (run) {
-                               smh->video_function(session, smh->video_user_data);
+                               v_engine->engine_function(session, v_engine->engine_user_data);
                                switch_mutex_lock(smh->control_mutex);
-                               smh->video_function = NULL;
-                               smh->video_user_data = NULL;
-                               smh->video_function_running = 0;
+                               v_engine->engine_function = NULL;
+                               v_engine->engine_user_data = NULL;
+                               v_engine->engine_function_running = 0;
                                switch_mutex_unlock(smh->control_mutex);
                        }
                }
@@ -5635,56 +6203,71 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_start_video_thread(switch_co
        return SWITCH_STATUS_SUCCESS;
 }
 
-SWITCH_DECLARE(void) switch_core_media_start_video_function(switch_core_session_t *session, switch_video_function_t video_function, void *user_data)
+SWITCH_DECLARE(void) switch_core_media_start_engine_function(switch_core_session_t *session, switch_media_type_t type, switch_engine_function_t engine_function, void *user_data)
 {
        switch_media_handle_t *smh;
+       switch_rtp_engine_t *engine;
 
        if (!(smh = session->media_handle)) {
                return;
        }
 
-       switch_core_session_start_video_thread(session);
+       engine = &smh->engines[type];
+
+       if (type == SWITCH_MEDIA_TYPE_VIDEO) {
+               switch_core_session_start_video_thread(session);
+       }
+
+       if (type == SWITCH_MEDIA_TYPE_TEXT) {
+               switch_core_session_start_text_thread(session);
+       }
 
        switch_mutex_lock(smh->control_mutex);
-       if (!smh->video_function_running) {
-               smh->video_function = video_function;
-               smh->video_user_data = user_data;
+       if (!engine->engine_function_running) {
+               engine->engine_function = engine_function;
+               engine->engine_user_data = user_data;
                switch_core_session_video_reset(session);
        }
        switch_mutex_unlock(smh->control_mutex);
 }
 
-SWITCH_DECLARE(int) switch_core_media_check_video_function(switch_core_session_t *session)
+SWITCH_DECLARE(int) switch_core_media_check_engine_function(switch_core_session_t *session, switch_media_type_t type)
 {
        switch_media_handle_t *smh;
        int r;
+       switch_rtp_engine_t *engine;
 
        if (!(smh = session->media_handle)) {
                return 0;
        }
+
+       engine = &smh->engines[type];
        
        switch_mutex_lock(smh->control_mutex);
-       r = (smh->video_function_running > 0);
+       r = (engine->engine_function_running > 0);
        switch_mutex_unlock(smh->control_mutex);
 
        return r;
 }
 
-SWITCH_DECLARE(void) switch_core_media_end_video_function(switch_core_session_t *session)
+SWITCH_DECLARE(void) switch_core_media_end_engine_function(switch_core_session_t *session, switch_media_type_t type)
 {
        switch_media_handle_t *smh;
+       switch_rtp_engine_t *engine;
 
        if (!(smh = session->media_handle)) {
                return;
        }
+
+       engine = &smh->engines[type];
        
        switch_mutex_lock(smh->control_mutex);
-       if (smh->video_function_running > 0) {
-               smh->video_function_running = -1;
+       if (engine->engine_function_running > 0) {
+               engine->engine_function_running = -1;
        }
        switch_mutex_unlock(smh->control_mutex);
 
-       while(smh->video_function_running != 0) {
+       while(engine->engine_function_running != 0) {
                switch_yield(10000);
        }
 }
@@ -5732,11 +6315,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
        char rip[RA_PTR_LEN] = "";
        char rp[RA_PTR_LEN] = "";
        char rvp[RA_PTR_LEN] = "";
-       char *p, *ip_ptr = NULL, *port_ptr = NULL, *vid_port_ptr = NULL, *pe;
+       char rtp[RA_PTR_LEN] = "";
+       char *p, *ip_ptr = NULL, *port_ptr = NULL, *vid_port_ptr = NULL, *text_port_ptr = NULL, *pe;
        int x;
        const char *val;
        switch_status_t status = SWITCH_STATUS_FALSE;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
 
        switch_assert(session);
@@ -5747,6 +6331,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
        
        if (zstr(sdp_str)) {
                sdp_str = smh->mparams->remote_sdp_str;
@@ -5776,6 +6361,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
                vid_port_ptr = p + 8;
        }
 
+       if ((p = (char *) switch_stristr("m=text ", sdp_str))) {
+               text_port_ptr = p + 7;
+       }
+
        if (!(ip_ptr && port_ptr)) {
                goto end;
        }
@@ -5811,6 +6400,16 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
                }
        }
 
+       p = text_port_ptr;
+       x = 0;
+       while (x < sizeof(rtp) - 1 && p && *p && (*p >= '0' && *p <= '9')) {
+               rtp[x++] = *p;
+               p++;
+               if (p >= pe) {
+                       goto end;
+               }
+       }
+
        if (!(*rip && *rp)) {
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "invalid SDP\n");
                goto end;
@@ -5826,6 +6425,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
                switch_channel_set_flag(session->channel, CF_VIDEO);
        }
 
+       if (*rtp) {
+               t_engine->cur_payload_map->remote_sdp_ip = switch_core_session_strdup(session, rip);
+               t_engine->cur_payload_map->remote_sdp_port = (switch_port_t) atoi(rtp);
+               switch_channel_set_flag(session->channel, CF_TEXT);
+               switch_channel_set_flag(session->channel, CF_TEXT_POSSIBLE);
+       }
+
        if (v_engine->cur_payload_map->remote_sdp_ip && v_engine->cur_payload_map->remote_sdp_port) {
                if (!strcmp(v_engine->cur_payload_map->remote_sdp_ip, rip) && atoi(rvp) == v_engine->cur_payload_map->remote_sdp_port) {
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote video address:port [%s:%d] has not changed.\n",
@@ -5864,6 +6470,44 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_
                }
        }
 
+       if (t_engine->cur_payload_map->remote_sdp_ip && t_engine->cur_payload_map->remote_sdp_port) {
+               if (!strcmp(t_engine->cur_payload_map->remote_sdp_ip, rip) && atoi(rvp) == t_engine->cur_payload_map->remote_sdp_port) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote text address:port [%s:%d] has not changed.\n",
+                                                         t_engine->cur_payload_map->remote_sdp_ip, t_engine->cur_payload_map->remote_sdp_port);
+               } else {
+                       switch_channel_set_flag(session->channel, CF_TEXT);
+                       switch_channel_set_flag(session->channel, CF_TEXT_POSSIBLE);
+                       if (switch_rtp_ready(t_engine->rtp_session)) {
+                               const char *rport = NULL;
+                               switch_port_t remote_rtcp_port = t_engine->remote_rtcp_port;
+
+                               if (!remote_rtcp_port) {
+                                       if ((rport = switch_channel_get_variable(session->channel, "rtp_remote_text_rtcp_port"))) {
+                                               remote_rtcp_port = (switch_port_t)atoi(rport);
+                                       }
+                               }
+
+                               
+                               if (switch_rtp_set_remote_address(t_engine->rtp_session, t_engine->cur_payload_map->remote_sdp_ip,
+                                                                                                 t_engine->cur_payload_map->remote_sdp_port, remote_rtcp_port, SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "TEXT RTP REPORTS ERROR: [%s]\n", err);
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "TEXT RTP CHANGING DEST TO: [%s:%d]\n",
+                                                                         t_engine->cur_payload_map->remote_sdp_ip, t_engine->cur_payload_map->remote_sdp_port);
+                                       if (!switch_media_handle_test_media_flag(smh, SCMF_DISABLE_RTP_AUTOADJ) && !switch_channel_test_flag(session->channel, CF_PROXY_MODE) &&
+                                               !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val)) && 
+                                               !switch_channel_test_flag(session->channel, CF_AVPF)) {
+                                               /* Reactivate the NAT buster flag. */
+                                               switch_rtp_set_flag(t_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+                                       }
+                                       if (switch_media_handle_test_media_flag(smh, SCMF_AUTOFIX_TIMING)) {
+                                               t_engine->check_frames = 0;
+                                       }
+                               }
+                       }
+               }
+       }
+
        if (switch_rtp_ready(a_engine->rtp_session)) {
                char *remote_host = switch_rtp_get_remote_host(a_engine->rtp_session);
                switch_port_t remote_port = switch_rtp_get_remote_port(a_engine->rtp_session);
@@ -6113,16 +6757,19 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_choose_port(switch_core_sessio
        engine->adv_sdp_port = sdp_port;
        engine->adv_sdp_ip = smh->mparams->adv_sdp_audio_ip = smh->mparams->extrtpip = switch_core_session_strdup(session, use_ip);
 
-
        if (type == SWITCH_MEDIA_TYPE_AUDIO) {
                switch_channel_set_variable(session->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, engine->local_sdp_ip);
                switch_channel_set_variable_printf(session->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, "%d", sdp_port);
                switch_channel_set_variable(session->channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE, engine->adv_sdp_ip);
-       } else {
+       } else if (type == SWITCH_MEDIA_TYPE_VIDEO) {
                switch_channel_set_variable(session->channel, SWITCH_LOCAL_VIDEO_IP_VARIABLE, engine->adv_sdp_ip);
                switch_channel_set_variable_printf(session->channel, SWITCH_LOCAL_VIDEO_PORT_VARIABLE, "%d", sdp_port);
+       } else if (type == SWITCH_MEDIA_TYPE_TEXT) {
+               switch_channel_set_variable(session->channel, SWITCH_LOCAL_TEXT_IP_VARIABLE, engine->adv_sdp_ip);
+               switch_channel_set_variable_printf(session->channel, SWITCH_LOCAL_TEXT_PORT_VARIABLE, "%d", sdp_port);
        }
 
+
        return SWITCH_STATUS_SUCCESS;
 }
 
@@ -6161,7 +6808,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_choose_ports(switch_core_sessi
 //?
 SWITCH_DECLARE(void) switch_core_media_deactivate_rtp(switch_core_session_t *session)
 {
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
 
        switch_assert(session);
@@ -6172,6 +6819,11 @@ SWITCH_DECLARE(void) switch_core_media_deactivate_rtp(switch_core_session_t *ses
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
+
+       if (t_engine->tf) {
+               switch_rtp_text_factory_destroy(&t_engine->tf);
+       }
 
        if (v_engine->media_thread) {
                switch_status_t st;
@@ -6196,6 +6848,29 @@ SWITCH_DECLARE(void) switch_core_media_deactivate_rtp(switch_core_session_t *ses
        }
 
 
+       if (t_engine->media_thread) {
+               switch_status_t st;
+
+               t_engine->mh.up = 0;
+               switch_thread_join(&st, t_engine->media_thread);
+               t_engine->media_thread = NULL;
+       }
+
+
+       if (t_engine->rtp_session) {
+               switch_rtp_destroy(&t_engine->rtp_session);
+       } else if (t_engine->local_sdp_port) {
+               switch_rtp_release_port(smh->mparams->rtpip, t_engine->local_sdp_port);
+       }
+
+
+       if (t_engine->local_sdp_port > 0 && !zstr(smh->mparams->remote_ip) && 
+               switch_core_media_check_nat(smh, smh->mparams->remote_ip)) {
+               switch_nat_del_mapping((switch_port_t) t_engine->local_sdp_port, SWITCH_NAT_UDP);
+               switch_nat_del_mapping((switch_port_t) t_engine->local_sdp_port + 1, SWITCH_NAT_UDP);
+       }
+
+
        if (a_engine->rtp_session) {
                switch_rtp_destroy(&a_engine->rtp_session);
        } else if (a_engine->local_sdp_port) {
@@ -6351,7 +7026,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
        char tmp[50];
        char *timer_name = NULL;
        const char *var;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
 
        switch_assert(session);
@@ -6362,6 +7037,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
 
        if (switch_channel_down(session->channel)) {
@@ -6389,6 +7065,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
                                goto video;
                        }
 
+                       if (switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE) && !switch_rtp_ready(t_engine->rtp_session)) {
+                               goto text;
+                       }
+
                        status = SWITCH_STATUS_SUCCESS;
                        goto end;
                } 
@@ -6826,80 +7506,75 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 
 
                
-       
+       text:
 
-       
-       video:
-       
-               if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
-                       switch_core_media_check_video_codecs(session);
-               }
 
-               if (switch_channel_test_flag(session->channel, CF_VIDEO_POSSIBLE) && v_engine->cur_payload_map->rm_encoding && v_engine->cur_payload_map->remote_sdp_port) {
+
+               if (switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE) && t_engine->cur_payload_map->rm_encoding && t_engine->cur_payload_map->remote_sdp_port) {
                        /******************************************************************************************/
-                       if (v_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) {
+                       if (t_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) {
                                //const char *ip = switch_channel_get_variable(session->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE);
                                //const char *port = switch_channel_get_variable(session->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE);
-                               char *remote_host = switch_rtp_get_remote_host(v_engine->rtp_session);
-                               switch_port_t remote_port = switch_rtp_get_remote_port(v_engine->rtp_session);
+                               char *remote_host = switch_rtp_get_remote_host(t_engine->rtp_session);
+                               switch_port_t remote_port = switch_rtp_get_remote_port(t_engine->rtp_session);
                                
 
 
-                               if (remote_host && remote_port && !strcmp(remote_host, v_engine->cur_payload_map->remote_sdp_ip) && remote_port == v_engine->cur_payload_map->remote_sdp_port) {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Video params are unchanged for %s.\n",
+                               if (remote_host && remote_port && !strcmp(remote_host, t_engine->cur_payload_map->remote_sdp_ip) && remote_port == t_engine->cur_payload_map->remote_sdp_port) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Text params are unchanged for %s.\n",
                                                                          switch_channel_get_name(session->channel));
-                                       v_engine->cur_payload_map->negotiated = 1;
-                                       goto video_up;
+                                       t_engine->cur_payload_map->negotiated = 1;
+                                       goto text_up;
                                } else {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Video params changed for %s from %s:%d to %s:%d\n",
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Text params changed for %s from %s:%d to %s:%d\n",
                                                                          switch_channel_get_name(session->channel),
-                                                                         remote_host, remote_port, v_engine->cur_payload_map->remote_sdp_ip, v_engine->cur_payload_map->remote_sdp_port);
+                                                                         remote_host, remote_port, t_engine->cur_payload_map->remote_sdp_ip, t_engine->cur_payload_map->remote_sdp_port);
                                }
                        }
 
                        if (!switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
-                               if (switch_rtp_ready(v_engine->rtp_session)) {
+                               if (switch_rtp_ready(t_engine->rtp_session)) {
                                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
-                                                                         "VIDEO RTP [%s] %s port %d -> %s port %d codec: %u\n", switch_channel_get_name(session->channel),
-                                                                         v_engine->local_sdp_ip, v_engine->local_sdp_port, v_engine->cur_payload_map->remote_sdp_ip,
-                                                                         v_engine->cur_payload_map->remote_sdp_port, v_engine->cur_payload_map->agreed_pt);
+                                                                         "TEXT RTP [%s] %s port %d -> %s port %d codec: %u\n", switch_channel_get_name(session->channel),
+                                                                         t_engine->local_sdp_ip, t_engine->local_sdp_port, t_engine->cur_payload_map->remote_sdp_ip,
+                                                                         t_engine->cur_payload_map->remote_sdp_port, t_engine->cur_payload_map->agreed_pt);
 
-                                       switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->cur_payload_map->agreed_pt);
+                                       switch_rtp_set_default_payload(t_engine->rtp_session, t_engine->cur_payload_map->agreed_pt);
                                }
                        }
                        
-                       switch_snprintf(tmp, sizeof(tmp), "%d", v_engine->local_sdp_port);
-                       switch_channel_set_variable(session->channel, SWITCH_LOCAL_VIDEO_IP_VARIABLE, a_engine->adv_sdp_ip);
-                       switch_channel_set_variable(session->channel, SWITCH_LOCAL_VIDEO_PORT_VARIABLE, tmp);
+                       switch_snprintf(tmp, sizeof(tmp), "%d", t_engine->local_sdp_port);
+                       switch_channel_set_variable(session->channel, SWITCH_LOCAL_TEXT_IP_VARIABLE, a_engine->adv_sdp_ip);
+                       switch_channel_set_variable(session->channel, SWITCH_LOCAL_TEXT_PORT_VARIABLE, tmp);
 
 
-                       if (v_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) {
+                       if (t_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) {
                                const char *rport = NULL;
-                               switch_port_t remote_rtcp_port = v_engine->remote_rtcp_port;
+                               switch_port_t remote_rtcp_port = t_engine->remote_rtcp_port;
 
                                //switch_channel_clear_flag(session->channel, CF_REINVITE);
 
                                if (!remote_rtcp_port) {
-                                       if ((rport = switch_channel_get_variable(session->channel, "rtp_remote_video_rtcp_port"))) {
+                                       if ((rport = switch_channel_get_variable(session->channel, "rtp_remote_text_rtcp_port"))) {
                                                remote_rtcp_port = (switch_port_t)atoi(rport);
                                        }
                                }
                                
                                if (switch_rtp_set_remote_address
-                                       (v_engine->rtp_session, v_engine->cur_payload_map->remote_sdp_ip, v_engine->cur_payload_map->remote_sdp_port, remote_rtcp_port, SWITCH_TRUE,
+                                       (t_engine->rtp_session, t_engine->cur_payload_map->remote_sdp_ip, t_engine->cur_payload_map->remote_sdp_port, remote_rtcp_port, SWITCH_TRUE,
                                         &err) != SWITCH_STATUS_SUCCESS) {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "VIDEO RTP REPORTS ERROR: [%s]\n", err);
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "TEXT RTP REPORTS ERROR: [%s]\n", err);
                                } else {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "VIDEO RTP CHANGING DEST TO: [%s:%d]\n",
-                                                                         v_engine->cur_payload_map->remote_sdp_ip, v_engine->cur_payload_map->remote_sdp_port);
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "TEXT RTP CHANGING DEST TO: [%s:%d]\n",
+                                                                         t_engine->cur_payload_map->remote_sdp_ip, t_engine->cur_payload_map->remote_sdp_port);
                                        if (!switch_media_handle_test_media_flag(smh, SCMF_DISABLE_RTP_AUTOADJ) && !switch_channel_test_flag(session->channel, CF_AVPF) &&
                                                !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val))) {
                                                /* Reactivate the NAT buster flag. */
-                                               switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+                                               switch_rtp_set_flag(t_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
                                        }
 
                                }
-                               goto video_up;
+                               goto text_up;
                        }
 
                        if (switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
@@ -6916,15 +7591,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
                                timer_name = NULL;
 
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
-                                                                 "PROXY VIDEO RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n",
+                                                                 "PROXY TEXT RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n",
                                                                  switch_channel_get_name(session->channel),
                                                                  a_engine->cur_payload_map->remote_sdp_ip,
-                                                                 v_engine->local_sdp_port,
-                                                                 v_engine->cur_payload_map->remote_sdp_ip,
-                                                                 v_engine->cur_payload_map->remote_sdp_port, v_engine->cur_payload_map->agreed_pt, v_engine->read_impl.microseconds_per_packet / 1000);
+                                                                 t_engine->local_sdp_port,
+                                                                 t_engine->cur_payload_map->remote_sdp_ip,
+                                                                 t_engine->cur_payload_map->remote_sdp_port, t_engine->cur_payload_map->agreed_pt, t_engine->read_impl.microseconds_per_packet / 1000);
 
-                               if (switch_rtp_ready(v_engine->rtp_session)) {
-                                       switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->cur_payload_map->agreed_pt);
+                               if (switch_rtp_ready(t_engine->rtp_session)) {
+                                       switch_rtp_set_default_payload(t_engine->rtp_session, t_engine->cur_payload_map->agreed_pt);
                                }
                        } else {
                                timer_name = smh->mparams->timer_name;
@@ -6936,13 +7611,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 
                        /******************************************************************************************/
 
-                       if (v_engine->rtp_session) {
-                               goto video_up;
+                       if (t_engine->rtp_session) {
+                               goto text_up;
                        }
 
 
-                       if (!v_engine->local_sdp_port) {
-                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_VIDEO, 1);
+                       if (!t_engine->local_sdp_port) {
+                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_TEXT, 1);
                        }
 
                        memset(flags, 0, sizeof(flags));
@@ -6958,84 +7633,68 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
                        if (switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
                                flags[SWITCH_RTP_FLAG_PROXY_MEDIA]++;
                        }
-                       switch_core_media_set_video_codec(session, 0);
+                       //TEXT switch_core_media_set_text_codec(session, 0);
 
-                       flags[SWITCH_RTP_FLAG_USE_TIMER] = 0;
+                       flags[SWITCH_RTP_FLAG_USE_TIMER] = 1;
                        flags[SWITCH_RTP_FLAG_NOBLOCK] = 0;
-                       flags[SWITCH_RTP_FLAG_VIDEO]++;
+                       flags[SWITCH_RTP_FLAG_TEXT]++;
+                       //flags[SWITCH_RTP_FLAG_VIDEO]++;
                        
-                       if (v_engine->fir) {
-                               flags[SWITCH_RTP_FLAG_FIR]++;
-                       }
-
-                       if (v_engine->pli) {
-                               flags[SWITCH_RTP_FLAG_PLI]++;
-                       }
-
-                       if (v_engine->nack) {
-                               flags[SWITCH_RTP_FLAG_NACK]++;
-                       }
-
-                       if (v_engine->tmmbr) {
-                               flags[SWITCH_RTP_FLAG_TMMBR]++;
-                       }
-
-                       v_engine->rtp_session = switch_rtp_new(a_engine->local_sdp_ip,
-                                                                                                                v_engine->local_sdp_port,
-                                                                                                                v_engine->cur_payload_map->remote_sdp_ip,
-                                                                                                                v_engine->cur_payload_map->remote_sdp_port,
-                                                                                                                v_engine->cur_payload_map->agreed_pt,
-                                                                                                                1, 90000, flags, NULL, &err, switch_core_session_get_pool(session));
+                       t_engine->rtp_session = switch_rtp_new(a_engine->local_sdp_ip,
+                                                                                                  t_engine->local_sdp_port,
+                                                                                                  t_engine->cur_payload_map->remote_sdp_ip,
+                                                                                                  t_engine->cur_payload_map->remote_sdp_port,
+                                                                                                  t_engine->cur_payload_map->agreed_pt,
+                                                                                                  TEXT_TIMER_SAMPLES, TEXT_TIMER_MS * 1000, flags, NULL, &err, switch_core_session_get_pool(session));
 
 
-                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%sVIDEO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n",
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%sTEXT RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n",
                                                          switch_channel_test_flag(session->channel, CF_PROXY_MEDIA) ? "PROXY " : "",
                                                          switch_channel_get_name(session->channel),
                                                          a_engine->local_sdp_ip,
-                                                         v_engine->local_sdp_port,
-                                                         v_engine->cur_payload_map->remote_sdp_ip,
-                                                         v_engine->cur_payload_map->remote_sdp_port, v_engine->cur_payload_map->agreed_pt,
-                                                         0, switch_rtp_ready(v_engine->rtp_session) ? "SUCCESS" : err);
+                                                         t_engine->local_sdp_port,
+                                                         t_engine->cur_payload_map->remote_sdp_ip,
+                                                         t_engine->cur_payload_map->remote_sdp_port, t_engine->cur_payload_map->agreed_pt,
+                                                         0, switch_rtp_ready(t_engine->rtp_session) ? "SUCCESS" : err);
 
 
-                       if (switch_rtp_ready(v_engine->rtp_session)) {
+                       if (switch_rtp_ready(t_engine->rtp_session)) {
                                const char *ssrc;
+                               
 
-                               if (v_engine->fir) {
-                                       switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_FIR);
+                               if (!t_engine->tf) {
+                                       switch_rtp_text_factory_create(&t_engine->tf, switch_core_session_get_pool(session));
                                }
 
-                               if (v_engine->pli) {
-                                       switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_PLI);
+                               switch_rtp_set_video_buffer_size(t_engine->rtp_session, 2, 2048);
+
+                               switch_rtp_set_payload_map(t_engine->rtp_session, &t_engine->payload_map);
+                               switch_channel_set_flag(session->channel, CF_TEXT);
+                               switch_core_session_start_text_thread(session);
+
+                               if ((ssrc = switch_channel_get_variable(session->channel, "rtp_use_text_ssrc"))) {
+                                       uint32_t ssrc_ul = (uint32_t) strtoul(ssrc, NULL, 10);
+                                       switch_rtp_set_ssrc(t_engine->rtp_session, ssrc_ul);
+                                       t_engine->ssrc = ssrc_ul;
+                               } else {
+                                       switch_rtp_set_ssrc(t_engine->rtp_session, t_engine->ssrc);
                                }
                                
-                               switch_rtp_set_payload_map(v_engine->rtp_session, &v_engine->payload_map);
-                               switch_channel_set_flag(session->channel, CF_VIDEO);
-                               switch_core_session_start_video_thread(session);
-
-                               if ((ssrc = switch_channel_get_variable(session->channel, "rtp_use_video_ssrc"))) {
-                                       uint32_t ssrc_ul = (uint32_t) strtoul(ssrc, NULL, 10);
-                                       switch_rtp_set_ssrc(v_engine->rtp_session, ssrc_ul);
-                                       v_engine->ssrc = ssrc_ul;
-                               } else {
-                                       switch_rtp_set_ssrc(v_engine->rtp_session, v_engine->ssrc);
-                               }
-                               
-                               if (v_engine->remote_ssrc) {
-                                       switch_rtp_set_remote_ssrc(v_engine->rtp_session, v_engine->remote_ssrc);
+                               if (t_engine->remote_ssrc) {
+                                       switch_rtp_set_remote_ssrc(t_engine->rtp_session, t_engine->remote_ssrc);
                                }
 
-                               if (v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].ready) {
+                               if (t_engine->ice_in.cands[t_engine->ice_in.chosen[0]][0].ready) {
                                        
-                                       gen_ice(session, SWITCH_MEDIA_TYPE_VIDEO, NULL, 0);
+                                       gen_ice(session, SWITCH_MEDIA_TYPE_TEXT, NULL, 0);
 
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating Video ICE\n");
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating Text ICE\n");
                                                
-                                       switch_rtp_activate_ice(v_engine->rtp_session, 
-                                                                                       v_engine->ice_in.ufrag,
-                                                                                       v_engine->ice_out.ufrag,
-                                                                                       v_engine->ice_out.pwd,
-                                                                                       v_engine->ice_in.pwd,
+                                       switch_rtp_activate_ice(t_engine->rtp_session, 
+                                                                                       t_engine->ice_in.ufrag,
+                                                                                       t_engine->ice_out.ufrag,
+                                                                                       t_engine->ice_out.pwd,
+                                                                                       t_engine->ice_in.pwd,
                                                                                        IPR_RTP,
 #ifdef GOOGLE_ICE
                                                                                        ICE_GOOGLE_JINGLE,
@@ -7043,23 +7702,23 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 #else
                                                                                        switch_ice_direction(session) == 
                                                                                        SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
-                                                                                       &v_engine->ice_in
+                                                                                       &t_engine->ice_in
 #endif
                                                                                        );
                                                
                                        
                                }
 
-                               if ((val = switch_channel_get_variable(session->channel, "rtcp_video_interval_msec")) || (val = smh->mparams->rtcp_video_interval_msec)) {
-                                       const char *rport = switch_channel_get_variable(session->channel, "rtp_remote_video_rtcp_port");
-                                       switch_port_t remote_port = v_engine->remote_rtcp_port;
+                               if ((val = switch_channel_get_variable(session->channel, "rtcp_text_interval_msec")) || (val = smh->mparams->rtcp_text_interval_msec)) {
+                                       const char *rport = switch_channel_get_variable(session->channel, "rtp_remote_text_rtcp_port");
+                                       switch_port_t remote_port = t_engine->remote_rtcp_port;
 
                                        if (rport) {
                                                remote_port = (switch_port_t)atoi(rport);
                                        }
                                        if (!strcasecmp(val, "passthru")) {
-                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating VIDEO RTCP PASSTHRU PORT %d\n", remote_port);
-                                               switch_rtp_activate_rtcp(v_engine->rtp_session, -1, remote_port, v_engine->rtcp_mux > 0);
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating TEXT RTCP PASSTHRU PORT %d\n", remote_port);
+                                               switch_rtp_activate_rtcp(t_engine->rtp_session, -1, remote_port, t_engine->rtcp_mux > 0);
                                        } else {
                                                int interval = atoi(val);
                                                if (interval < 100 || interval > 500000) {
@@ -7068,27 +7727,24 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
                                                        interval = 5000;
                                                }
                                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
-                                                                                 "Activating VIDEO RTCP PORT %d interval %d mux %d\n", remote_port, interval, v_engine->rtcp_mux);
-                                               switch_rtp_activate_rtcp(v_engine->rtp_session, interval, remote_port, v_engine->rtcp_mux > 0);
+                                                                                 "Activating TEXT RTCP PORT %d interval %d mux %d\n", remote_port, interval, t_engine->rtcp_mux);
+                                               switch_rtp_activate_rtcp(t_engine->rtp_session, interval, remote_port, t_engine->rtcp_mux > 0);
                                                        
                                        }
                                        
 
-                                       if (v_engine->ice_in.cands[v_engine->ice_in.chosen[1]][1].ready) {
-
-                                               if (v_engine->rtcp_mux > 0 && v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].ready &&
-                                                       !strcmp(v_engine->ice_in.cands[v_engine->ice_in.chosen[1]][1].con_addr, 
-                                                                       v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].con_addr) && 
-                                                       v_engine->ice_in.cands[v_engine->ice_in.chosen[1]][1].con_port == v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].con_port) {
-                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Skipping VIDEO RTCP ICE (Same as VIDEO RTP)\n");
+                                       if (t_engine->ice_in.cands[t_engine->ice_in.chosen[1]][1].ready) {
+                                               if (t_engine->rtcp_mux > 0 && !strcmp(t_engine->ice_in.cands[t_engine->ice_in.chosen[1]][1].con_addr, 
+                                                                                                                         t_engine->ice_in.cands[t_engine->ice_in.chosen[0]][0].con_addr) &&
+                                                       t_engine->ice_in.cands[t_engine->ice_in.chosen[1]][1].con_port == t_engine->ice_in.cands[t_engine->ice_in.chosen[0]][0].con_port) {
+                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Skipping TEXT RTCP ICE (Same as TEXT RTP)\n");
                                                } else {
-
-                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating VIDEO RTCP ICE\n");
-                                                       switch_rtp_activate_ice(v_engine->rtp_session, 
-                                                                                                       v_engine->ice_in.ufrag,
-                                                                                                       v_engine->ice_out.ufrag,
-                                                                                                       v_engine->ice_out.pwd,
-                                                                                                       v_engine->ice_in.pwd,
+                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating TEXT RTCP ICE\n");
+                                                       switch_rtp_activate_ice(t_engine->rtp_session, 
+                                                                                                       t_engine->ice_in.ufrag,
+                                                                                                       t_engine->ice_out.ufrag,
+                                                                                                       t_engine->ice_out.pwd,
+                                                                                                       t_engine->ice_in.pwd,
                                                                                                        IPR_RTCP,
 #ifdef GOOGLE_ICE
                                                                                                        ICE_GOOGLE_JINGLE,
@@ -7096,7 +7752,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
 #else
                                                                                                        switch_ice_direction(session) == 
                                                                                                        SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
-                                                                                                       &v_engine->ice_in
+                                                                                                       &t_engine->ice_in
 #endif
                                                                                                        );
                                                
@@ -7107,171 +7763,497 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
                                        }
                                }
                                
-                               if (!zstr(v_engine->local_dtls_fingerprint.str) && switch_rtp_has_dtls() && dtls_ok(smh->session)) {
+                               if (!zstr(t_engine->local_dtls_fingerprint.str) && switch_rtp_has_dtls() && dtls_ok(smh->session)) {
                                        dtls_type_t xtype, 
-                                               dtype = v_engine->dtls_controller ? DTLS_TYPE_CLIENT : DTLS_TYPE_SERVER;
+                                               dtype = t_engine->dtls_controller ? DTLS_TYPE_CLIENT : DTLS_TYPE_SERVER;
                                        xtype = DTLS_TYPE_RTP;
-                                       if (v_engine->rtcp_mux > 0 && smh->mparams->rtcp_video_interval_msec) xtype |= DTLS_TYPE_RTCP;
+                                       if (t_engine->rtcp_mux > 0 && smh->mparams->rtcp_text_interval_msec) xtype |= DTLS_TYPE_RTCP;
                                        
-                                       switch_rtp_add_dtls(v_engine->rtp_session, &v_engine->local_dtls_fingerprint, &v_engine->remote_dtls_fingerprint, dtype | xtype);
+                                       switch_rtp_add_dtls(t_engine->rtp_session, &t_engine->local_dtls_fingerprint, &t_engine->remote_dtls_fingerprint, dtype | xtype);
                                        
-                                       if (v_engine->rtcp_mux < 1 && smh->mparams->rtcp_video_interval_msec) {
+                                       if (t_engine->rtcp_mux < 1 && smh->mparams->rtcp_text_interval_msec) {
                                                xtype = DTLS_TYPE_RTCP;
-                                               switch_rtp_add_dtls(v_engine->rtp_session, &v_engine->local_dtls_fingerprint, &v_engine->remote_dtls_fingerprint, dtype | xtype);
+                                               switch_rtp_add_dtls(t_engine->rtp_session, &t_engine->local_dtls_fingerprint, &t_engine->remote_dtls_fingerprint, dtype | xtype);
                                        }
                                }
                                        
                                        
-                               if ((val = switch_channel_get_variable(session->channel, "rtp_manual_video_rtp_bugs"))) {
-                                       switch_core_media_parse_rtp_bugs(&v_engine->rtp_bugs, val);
+                               if ((val = switch_channel_get_variable(session->channel, "rtp_manual_text_rtp_bugs"))) {
+                                       switch_core_media_parse_rtp_bugs(&t_engine->rtp_bugs, val);
                                }
                                
+
                                //if (switch_channel_test_flag(session->channel, CF_AVPF)) {
                                        //smh->mparams->manual_video_rtp_bugs = RTP_BUG_SEND_LINEAR_TIMESTAMPS;
                                //}
                                
-                               switch_rtp_intentional_bugs(v_engine->rtp_session, v_engine->rtp_bugs | smh->mparams->manual_video_rtp_bugs);
+                               switch_rtp_intentional_bugs(t_engine->rtp_session, t_engine->rtp_bugs | smh->mparams->manual_text_rtp_bugs);
 
                                //XX
 
 
-                               switch_channel_set_variable_printf(session->channel, "rtp_use_video_pt", "%d", v_engine->cur_payload_map->agreed_pt);
-                               v_engine->ssrc = switch_rtp_get_ssrc(v_engine->rtp_session);
-                               switch_channel_set_variable_printf(session->channel, "rtp_use_video_ssrc", "%u", v_engine->ssrc);
+                               switch_channel_set_variable_printf(session->channel, "rtp_use_text_pt", "%d", t_engine->cur_payload_map->agreed_pt);
+                               t_engine->ssrc = switch_rtp_get_ssrc(t_engine->rtp_session);
+                               switch_channel_set_variable_printf(session->channel, "rtp_use_text_ssrc", "%u", t_engine->ssrc);
 
-                               switch_core_session_apply_crypto(session, SWITCH_MEDIA_TYPE_VIDEO);
+                               switch_core_session_apply_crypto(session, SWITCH_MEDIA_TYPE_TEXT);
 
                                
                                if (switch_channel_test_flag(session->channel, CF_ZRTP_PASSTHRU)) {
-                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Activating video UDPTL mode\n");
-                                       switch_rtp_udptl_mode(v_engine->rtp_session);
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Activating text UDPTL mode\n");
+                                       switch_rtp_udptl_mode(t_engine->rtp_session);
                                }
 
                        } else {
-                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "VIDEO RTP REPORTS ERROR: [%s]\n", switch_str_nil(err));
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "TEXT RTP REPORTS ERROR: [%s]\n", switch_str_nil(err));
                                switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
                                goto end;
                        }
                }
+       
 
-       } else {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "AUDIO RTP REPORTS ERROR: [%s]\n", switch_str_nil(err));
-               switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
-               status = SWITCH_STATUS_FALSE;
-               goto end;
-       }
+       text_up:
+       video:
+       
+               if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+                       switch_core_media_check_video_codecs(session);
+               }
 
- video_up:
+               if (switch_channel_test_flag(session->channel, CF_VIDEO_POSSIBLE) && v_engine->cur_payload_map->rm_encoding && v_engine->cur_payload_map->remote_sdp_port) {
+                       /******************************************************************************************/
+                       if (v_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) {
+                               //const char *ip = switch_channel_get_variable(session->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE);
+                               //const char *port = switch_channel_get_variable(session->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE);
+                               char *remote_host = switch_rtp_get_remote_host(v_engine->rtp_session);
+                               switch_port_t remote_port = switch_rtp_get_remote_port(v_engine->rtp_session);
+                               
 
-       if (session && v_engine) {
-               check_dtls_reinvite(session, v_engine);
-       }
 
-       status = SWITCH_STATUS_SUCCESS;
+                               if (remote_host && remote_port && !strcmp(remote_host, v_engine->cur_payload_map->remote_sdp_ip) && remote_port == v_engine->cur_payload_map->remote_sdp_port) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Video params are unchanged for %s.\n",
+                                                                         switch_channel_get_name(session->channel));
+                                       v_engine->cur_payload_map->negotiated = 1;
+                                       goto video_up;
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Video params changed for %s from %s:%d to %s:%d\n",
+                                                                         switch_channel_get_name(session->channel),
+                                                                         remote_host, remote_port, v_engine->cur_payload_map->remote_sdp_ip, v_engine->cur_payload_map->remote_sdp_port);
+                               }
+                       }
 
- end:
+                       if (!switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
+                               if (switch_rtp_ready(v_engine->rtp_session)) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
+                                                                         "VIDEO RTP [%s] %s port %d -> %s port %d codec: %u\n", switch_channel_get_name(session->channel),
+                                                                         v_engine->local_sdp_ip, v_engine->local_sdp_port, v_engine->cur_payload_map->remote_sdp_ip,
+                                                                         v_engine->cur_payload_map->remote_sdp_port, v_engine->cur_payload_map->agreed_pt);
 
-       switch_channel_clear_flag(session->channel, CF_REINVITE);
+                                       switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->cur_payload_map->agreed_pt);
+                               }
+                       }
+                       
+                       switch_snprintf(tmp, sizeof(tmp), "%d", v_engine->local_sdp_port);
+                       switch_channel_set_variable(session->channel, SWITCH_LOCAL_VIDEO_IP_VARIABLE, a_engine->adv_sdp_ip);
+                       switch_channel_set_variable(session->channel, SWITCH_LOCAL_VIDEO_PORT_VARIABLE, tmp);
 
-       switch_core_recovery_track(session);
 
-       return status;
+                       if (v_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) {
+                               const char *rport = NULL;
+                               switch_port_t remote_rtcp_port = v_engine->remote_rtcp_port;
 
-}
+                               //switch_channel_clear_flag(session->channel, CF_REINVITE);
 
-static const char *get_media_profile_name(switch_core_session_t *session, int secure)
-{
-       switch_assert(session);
+                               if (!remote_rtcp_port) {
+                                       if ((rport = switch_channel_get_variable(session->channel, "rtp_remote_video_rtcp_port"))) {
+                                               remote_rtcp_port = (switch_port_t)atoi(rport);
+                                       }
+                               }
+                               
+                               if (switch_rtp_set_remote_address
+                                       (v_engine->rtp_session, v_engine->cur_payload_map->remote_sdp_ip, v_engine->cur_payload_map->remote_sdp_port, remote_rtcp_port, SWITCH_TRUE,
+                                        &err) != SWITCH_STATUS_SUCCESS) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "VIDEO RTP REPORTS ERROR: [%s]\n", err);
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "VIDEO RTP CHANGING DEST TO: [%s:%d]\n",
+                                                                         v_engine->cur_payload_map->remote_sdp_ip, v_engine->cur_payload_map->remote_sdp_port);
+                                       if (!switch_media_handle_test_media_flag(smh, SCMF_DISABLE_RTP_AUTOADJ) && !switch_channel_test_flag(session->channel, CF_AVPF) &&
+                                               !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val))) {
+                                               /* Reactivate the NAT buster flag. */
+                                               switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ);
+                                       }
 
-       if (switch_channel_test_flag(session->channel, CF_AVPF)) {
-               if (switch_channel_test_flag(session->channel, CF_DTLS) || secure) {
-                       if (switch_channel_test_flag(session->channel, CF_AVPF_MOZ)) {
-                               return "UDP/TLS/RTP/SAVPF";
-                       } else {
-                               return "RTP/SAVPF";
-                       }
-               } else {
-                       if (switch_channel_test_flag(session->channel, CF_AVPF_MOZ)) {
-                               return "UDP/AVPF";
-                       } else {
-                               return "RTP/AVPF";
+                               }
+                               goto video_up;
                        }
-               }
-       }
-
-       if (secure) {
-               return "RTP/SAVP";
-       }
-
-       return "RTP/AVP";
-       
-}
 
-static char *get_setup(switch_rtp_engine_t *engine, switch_core_session_t *session, switch_sdp_type_t sdp_type)
-{
+                       if (switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
+                               switch_core_media_proxy_remote_addr(session, NULL);
 
-       if (sdp_type == SDP_TYPE_REQUEST) {
-               engine->dtls_controller = 0;
-               engine->new_dtls = 1;
-               engine->new_ice = 1;
-               return "actpass";
-       } else {
-               return engine->dtls_controller ? "active" : "passive";
-       }
-}
+                               memset(flags, 0, sizeof(flags));
+                               flags[SWITCH_RTP_FLAG_PROXY_MEDIA]++;
+                               flags[SWITCH_RTP_FLAG_DATAWAIT]++;
 
+                               if (!switch_media_handle_test_media_flag(smh, SCMF_DISABLE_RTP_AUTOADJ) && !switch_channel_test_flag(session->channel, CF_AVPF) &&
+                                       !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val))) {
+                                       flags[SWITCH_RTP_FLAG_AUTOADJ]++;
+                               }
+                               timer_name = NULL;
 
-//?
-static void generate_m(switch_core_session_t *session, char *buf, size_t buflen, 
-                                          switch_port_t port, const char *family, const char *ip,
-                                          int cur_ptime, const char *append_audio, const char *sr, int use_cng, int cng_type, switch_event_t *map, int secure, 
-                                          switch_sdp_type_t sdp_type)
-{
-       int i = 0;
-       int rate;
-       int already_did[128] = { 0 };
-       int ptime = 0, noptime = 0;
-       const char *local_sdp_audio_zrtp_hash;
-       switch_media_handle_t *smh;
-       switch_rtp_engine_t *a_engine;
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
+                                                                 "PROXY VIDEO RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n",
+                                                                 switch_channel_get_name(session->channel),
+                                                                 a_engine->cur_payload_map->remote_sdp_ip,
+                                                                 v_engine->local_sdp_port,
+                                                                 v_engine->cur_payload_map->remote_sdp_ip,
+                                                                 v_engine->cur_payload_map->remote_sdp_port, v_engine->cur_payload_map->agreed_pt, v_engine->read_impl.microseconds_per_packet / 1000);
 
-       switch_assert(session);
+                               if (switch_rtp_ready(v_engine->rtp_session)) {
+                                       switch_rtp_set_default_payload(v_engine->rtp_session, v_engine->cur_payload_map->agreed_pt);
+                               }
+                       } else {
+                               timer_name = smh->mparams->timer_name;
 
-       if (!(smh = session->media_handle)) {
-               return;
-       }
+                               if ((var = switch_channel_get_variable(session->channel, "rtp_timer_name"))) {
+                                       timer_name = (char *) var;
+                               }
+                       }
 
-       a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
+                       /******************************************************************************************/
 
-       //switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "m=audio %d RTP/%sAVP%s", 
-       //port, secure ? "S" : "", switch_channel_test_flag(session->channel, CF_AVPF) ? "F" : "");
+                       if (v_engine->rtp_session) {
+                               goto video_up;
+                       }
 
-       switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "m=audio %d %s", port, 
-                                       get_media_profile_name(session, secure || a_engine->crypto_type != CRYPTO_INVALID));
 
-       for (i = 0; i < smh->mparams->num_codecs; i++) {
-               const switch_codec_implementation_t *imp = smh->codecs[i];
-               int this_ptime = (imp->microseconds_per_packet / 1000);
+                       if (!v_engine->local_sdp_port) {
+                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_VIDEO, 1);
+                       }
 
-               if (!strcasecmp(imp->iananame, "ilbc") || !strcasecmp(imp->iananame, "isac") ) {
-                       this_ptime = 20;
-               }
+                       memset(flags, 0, sizeof(flags));
+                       flags[SWITCH_RTP_FLAG_DATAWAIT]++;
+                       flags[SWITCH_RTP_FLAG_RAW_WRITE]++;
 
-               if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) {
-                       continue;
-               }
+                       if (!switch_media_handle_test_media_flag(smh, SCMF_DISABLE_RTP_AUTOADJ) && !switch_channel_test_flag(session->channel, CF_PROXY_MODE) &&
+                               !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val)) && 
+                               !switch_channel_test_flag(session->channel, CF_AVPF)) {
+                               flags[SWITCH_RTP_FLAG_AUTOADJ]++;                               
+                       }
 
-               if (!noptime) {
-                       if (!cur_ptime) {
-                               if (!ptime) {
-                                       ptime = this_ptime;
-                               }
-                       } else {
-                               if (this_ptime != cur_ptime) {
-                                       continue;
-                               }
+                       if (switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
+                               flags[SWITCH_RTP_FLAG_PROXY_MEDIA]++;
                        }
-               }
+                       switch_core_media_set_video_codec(session, 0);
+
+                       flags[SWITCH_RTP_FLAG_USE_TIMER] = 0;
+                       flags[SWITCH_RTP_FLAG_NOBLOCK] = 0;
+                       flags[SWITCH_RTP_FLAG_VIDEO]++;
+                       
+                       if (v_engine->fir) {
+                               flags[SWITCH_RTP_FLAG_FIR]++;
+                       }
+
+                       if (v_engine->pli) {
+                               flags[SWITCH_RTP_FLAG_PLI]++;
+                       }
+
+                       if (v_engine->nack) {
+                               flags[SWITCH_RTP_FLAG_NACK]++;
+                       }
+
+                       if (v_engine->tmmbr) {
+                               flags[SWITCH_RTP_FLAG_TMMBR]++;
+                       }
+
+                       v_engine->rtp_session = switch_rtp_new(a_engine->local_sdp_ip,
+                                                                                                                v_engine->local_sdp_port,
+                                                                                                                v_engine->cur_payload_map->remote_sdp_ip,
+                                                                                                                v_engine->cur_payload_map->remote_sdp_port,
+                                                                                                                v_engine->cur_payload_map->agreed_pt,
+                                                                                                                1, 90000, flags, NULL, &err, switch_core_session_get_pool(session));
+
+
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%sVIDEO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n",
+                                                         switch_channel_test_flag(session->channel, CF_PROXY_MEDIA) ? "PROXY " : "",
+                                                         switch_channel_get_name(session->channel),
+                                                         a_engine->local_sdp_ip,
+                                                         v_engine->local_sdp_port,
+                                                         v_engine->cur_payload_map->remote_sdp_ip,
+                                                         v_engine->cur_payload_map->remote_sdp_port, v_engine->cur_payload_map->agreed_pt,
+                                                         0, switch_rtp_ready(v_engine->rtp_session) ? "SUCCESS" : err);
+
+
+                       if (switch_rtp_ready(v_engine->rtp_session)) {
+                               const char *ssrc;
+
+                               if (v_engine->fir) {
+                                       switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_FIR);
+                               }
+
+                               if (v_engine->pli) {
+                                       switch_rtp_set_flag(v_engine->rtp_session, SWITCH_RTP_FLAG_PLI);
+                               }
+                               
+                               switch_rtp_set_payload_map(v_engine->rtp_session, &v_engine->payload_map);
+                               switch_channel_set_flag(session->channel, CF_VIDEO);
+                               switch_core_session_start_video_thread(session);
+
+                               if ((ssrc = switch_channel_get_variable(session->channel, "rtp_use_video_ssrc"))) {
+                                       uint32_t ssrc_ul = (uint32_t) strtoul(ssrc, NULL, 10);
+                                       switch_rtp_set_ssrc(v_engine->rtp_session, ssrc_ul);
+                                       v_engine->ssrc = ssrc_ul;
+                               } else {
+                                       switch_rtp_set_ssrc(v_engine->rtp_session, v_engine->ssrc);
+                               }
+                               
+                               if (v_engine->remote_ssrc) {
+                                       switch_rtp_set_remote_ssrc(v_engine->rtp_session, v_engine->remote_ssrc);
+                               }
+
+                               if (v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].ready) {
+                                       
+                                       gen_ice(session, SWITCH_MEDIA_TYPE_VIDEO, NULL, 0);
+
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating Video ICE\n");
+                                               
+                                       switch_rtp_activate_ice(v_engine->rtp_session, 
+                                                                                       v_engine->ice_in.ufrag,
+                                                                                       v_engine->ice_out.ufrag,
+                                                                                       v_engine->ice_out.pwd,
+                                                                                       v_engine->ice_in.pwd,
+                                                                                       IPR_RTP,
+#ifdef GOOGLE_ICE
+                                                                                       ICE_GOOGLE_JINGLE,
+                                                                                       NULL
+#else
+                                                                                       switch_ice_direction(session) == 
+                                                                                       SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
+                                                                                       &v_engine->ice_in
+#endif
+                                                                                       );
+                                               
+                                       
+                               }
+
+                               if ((val = switch_channel_get_variable(session->channel, "rtcp_video_interval_msec")) || (val = smh->mparams->rtcp_video_interval_msec)) {
+                                       const char *rport = switch_channel_get_variable(session->channel, "rtp_remote_video_rtcp_port");
+                                       switch_port_t remote_port = v_engine->remote_rtcp_port;
+
+                                       if (rport) {
+                                               remote_port = (switch_port_t)atoi(rport);
+                                       }
+                                       if (!strcasecmp(val, "passthru")) {
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating VIDEO RTCP PASSTHRU PORT %d\n", remote_port);
+                                               switch_rtp_activate_rtcp(v_engine->rtp_session, -1, remote_port, v_engine->rtcp_mux > 0);
+                                       } else {
+                                               int interval = atoi(val);
+                                               if (interval < 100 || interval > 500000) {
+                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR,
+                                                                                         "Invalid rtcp interval spec [%d] must be between 100 and 500000\n", interval);
+                                                       interval = 5000;
+                                               }
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
+                                                                                 "Activating VIDEO RTCP PORT %d interval %d mux %d\n", remote_port, interval, v_engine->rtcp_mux);
+                                               switch_rtp_activate_rtcp(v_engine->rtp_session, interval, remote_port, v_engine->rtcp_mux > 0);
+                                                       
+                                       }
+                                       
+
+                                       if (v_engine->ice_in.cands[v_engine->ice_in.chosen[1]][1].ready) {
+
+                                               if (v_engine->rtcp_mux > 0 && !strcmp(v_engine->ice_in.cands[v_engine->ice_in.chosen[1]][1].con_addr, v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].con_addr)
+                                                       && v_engine->ice_in.cands[v_engine->ice_in.chosen[1]][1].con_port == v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].con_port) {
+                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Skipping VIDEO RTCP ICE (Same as VIDEO RTP)\n");
+                                               } else {
+
+                                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating VIDEO RTCP ICE\n");
+                                                       switch_rtp_activate_ice(v_engine->rtp_session, 
+                                                                                                       v_engine->ice_in.ufrag,
+                                                                                                       v_engine->ice_out.ufrag,
+                                                                                                       v_engine->ice_out.pwd,
+                                                                                                       v_engine->ice_in.pwd,
+                                                                                                       IPR_RTCP,
+#ifdef GOOGLE_ICE
+                                                                                                       ICE_GOOGLE_JINGLE,
+                                                                                                       NULL
+#else
+                                                                                                       switch_ice_direction(session) == 
+                                                                                                       SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED),
+                                                                                                       &v_engine->ice_in
+#endif
+                                                                                                       );
+                                               
+                                               
+                                               
+                                               }
+                               
+                                       }
+                               }
+                               
+                               if (!zstr(v_engine->local_dtls_fingerprint.str) && switch_rtp_has_dtls() && dtls_ok(smh->session)) {
+                                       dtls_type_t xtype, 
+                                               dtype = v_engine->dtls_controller ? DTLS_TYPE_CLIENT : DTLS_TYPE_SERVER;
+                                       xtype = DTLS_TYPE_RTP;
+                                       if (v_engine->rtcp_mux > 0 && smh->mparams->rtcp_video_interval_msec) xtype |= DTLS_TYPE_RTCP;
+                                       
+                                       switch_rtp_add_dtls(v_engine->rtp_session, &v_engine->local_dtls_fingerprint, &v_engine->remote_dtls_fingerprint, dtype | xtype);
+                                       
+                                       if (v_engine->rtcp_mux < 1 && smh->mparams->rtcp_video_interval_msec) {
+                                               xtype = DTLS_TYPE_RTCP;
+                                               switch_rtp_add_dtls(v_engine->rtp_session, &v_engine->local_dtls_fingerprint, &v_engine->remote_dtls_fingerprint, dtype | xtype);
+                                       }
+                               }
+                                       
+                                       
+                               if ((val = switch_channel_get_variable(session->channel, "rtp_manual_video_rtp_bugs"))) {
+                                       switch_core_media_parse_rtp_bugs(&v_engine->rtp_bugs, val);
+                               }
+                               
+                               if (switch_channel_test_flag(session->channel, CF_AVPF)) {
+                                       smh->mparams->manual_video_rtp_bugs = RTP_BUG_SEND_LINEAR_TIMESTAMPS;
+                               }
+                               
+                               switch_rtp_intentional_bugs(v_engine->rtp_session, v_engine->rtp_bugs | smh->mparams->manual_video_rtp_bugs);
+
+                               //XX
+
+
+                               switch_channel_set_variable_printf(session->channel, "rtp_use_video_pt", "%d", v_engine->cur_payload_map->agreed_pt);
+                               v_engine->ssrc = switch_rtp_get_ssrc(v_engine->rtp_session);
+                               switch_channel_set_variable_printf(session->channel, "rtp_use_video_ssrc", "%u", v_engine->ssrc);
+
+                               switch_core_session_apply_crypto(session, SWITCH_MEDIA_TYPE_VIDEO);
+
+                               
+                               if (switch_channel_test_flag(session->channel, CF_ZRTP_PASSTHRU)) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Activating video UDPTL mode\n");
+                                       switch_rtp_udptl_mode(v_engine->rtp_session);
+                               }
+
+                       } else {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "VIDEO RTP REPORTS ERROR: [%s]\n", switch_str_nil(err));
+                               switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                               goto end;
+                       }
+               }
+
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "AUDIO RTP REPORTS ERROR: [%s]\n", switch_str_nil(err));
+               switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               status = SWITCH_STATUS_FALSE;
+               goto end;
+       }
+
+ video_up:
+
+       if (session && v_engine) {
+               check_dtls_reinvite(session, v_engine);
+       }
+
+       status = SWITCH_STATUS_SUCCESS;
+
+ end:
+
+       switch_channel_clear_flag(session->channel, CF_REINVITE);
+
+       switch_core_recovery_track(session);
+
+       return status;
+
+}
+
+static const char *get_media_profile_name(switch_core_session_t *session, int secure)
+{
+       switch_assert(session);
+
+       if (switch_channel_test_flag(session->channel, CF_AVPF)) {
+               if (switch_channel_test_flag(session->channel, CF_DTLS) || secure) {
+                       if (switch_channel_test_flag(session->channel, CF_AVPF_MOZ)) {
+                               return "UDP/TLS/RTP/SAVPF";
+                       } else {
+                               return "RTP/SAVPF";
+                       }
+               } else {
+                       if (switch_channel_test_flag(session->channel, CF_AVPF_MOZ)) {
+                               return "UDP/AVPF";
+                       } else {
+                               return "RTP/AVPF";
+                       }
+               }
+       }
+
+       if (secure) {
+               return "RTP/SAVP";
+       }
+
+       return "RTP/AVP";
+       
+}
+
+static char *get_setup(switch_rtp_engine_t *engine, switch_core_session_t *session, switch_sdp_type_t sdp_type)
+{
+
+       if (sdp_type == SDP_TYPE_REQUEST) {
+               engine->dtls_controller = 0;
+               engine->new_dtls = 1;
+               engine->new_ice = 1;
+               return "actpass";
+       } else {
+               return engine->dtls_controller ? "active" : "passive";
+       }
+}
+
+
+//?
+static void generate_m(switch_core_session_t *session, char *buf, size_t buflen, 
+                                          switch_port_t port, const char *family, const char *ip,
+                                          int cur_ptime, const char *append_audio, const char *sr, int use_cng, int cng_type, switch_event_t *map, int secure, 
+                                          switch_sdp_type_t sdp_type)
+{
+       int i = 0;
+       int rate;
+       int already_did[128] = { 0 };
+       int ptime = 0, noptime = 0;
+       const char *local_sdp_audio_zrtp_hash;
+       switch_media_handle_t *smh;
+       switch_rtp_engine_t *a_engine;
+
+       switch_assert(session);
+
+       if (!(smh = session->media_handle)) {
+               return;
+       }
+
+       a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
+
+       //switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "m=audio %d RTP/%sAVP%s", 
+       //port, secure ? "S" : "", switch_channel_test_flag(session->channel, CF_AVPF) ? "F" : "");
+
+       switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "m=audio %d %s", port, 
+                                       get_media_profile_name(session, secure || a_engine->crypto_type != CRYPTO_INVALID));
+
+       for (i = 0; i < smh->mparams->num_codecs; i++) {
+               const switch_codec_implementation_t *imp = smh->codecs[i];
+               int this_ptime = (imp->microseconds_per_packet / 1000);
+
+               if (!strcasecmp(imp->iananame, "ilbc") || !strcasecmp(imp->iananame, "isac") ) {
+                       this_ptime = 20;
+               }
+
+               if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) {
+                       continue;
+               }
+
+               if (!noptime) {
+                       if (!cur_ptime) {
+                               if (!ptime) {
+                                       ptime = this_ptime;
+                               }
+                       } else {
+                               if (this_ptime != cur_ptime) {
+                                       continue;
+                               }
+                       }
+               }
 
                if (smh->ianacodes[i] < 128) {
                        if (already_did[smh->ianacodes[i]]) {
@@ -7655,7 +8637,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
        char *buf;
        int ptime = 0;
        uint32_t rate = 0;
-       uint32_t v_port;
+       uint32_t v_port, t_port;
        int use_cng = 1;
        const char *val;
        const char *family;
@@ -7673,8 +8655,9 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
        //const char *local_audio_crypto_key = switch_core_session_local_crypto_key(session, SWITCH_MEDIA_TYPE_AUDIO);
        const char *local_sdp_audio_zrtp_hash = switch_core_media_get_zrtp_hash(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_TRUE);
        const char *local_sdp_video_zrtp_hash = switch_core_media_get_zrtp_hash(session, SWITCH_MEDIA_TYPE_VIDEO, SWITCH_TRUE);
+       const char *local_sdp_text_zrtp_hash = switch_core_media_get_zrtp_hash(session, SWITCH_MEDIA_TYPE_TEXT, SWITCH_TRUE);
        const char *tmp;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
        ice_t *ice_out;
        //int vp8 = 0;
@@ -7693,6 +8676,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        if ((!a_engine->rtcp_mux && !v_engine->rtcp_mux) && 
                (sdp_type == SDP_TYPE_REQUEST || switch_true(switch_channel_get_variable(session->channel, "rtcp_mux")))) {
@@ -8745,6 +9729,273 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
 
        }
 
+       ///TEXT 
+
+       if (sdp_type == SDP_TYPE_RESPONSE && !switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE)) {
+               if (switch_channel_test_flag(session->channel, CF_TEXT_SDP_RECVD)) {
+                       switch_channel_clear_flag(session->channel, CF_TEXT_SDP_RECVD);
+                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "m=text 0 %s 19\r\n", 
+                                                       get_media_profile_name(session, 
+                                                                                                  (switch_channel_test_flag(session->channel, CF_SECURE) 
+                                                                                                       && switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) || 
+                                                                                                  a_engine->crypto_type != CRYPTO_INVALID || switch_channel_test_flag(session->channel, CF_DTLS)));
+               }
+       } else if ((switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE) || switch_channel_var_true(session->channel, "rtp_enable_text")) &&
+                          switch_channel_test_cap(session->channel, CC_RTP_RTT)) {
+               t_engine->t140_pt = 0;
+               t_engine->red_pt = 0;
+               
+
+               if (sdp_type == SDP_TYPE_REQUEST) {
+                       t_engine->t140_pt = 96;
+                       t_engine->red_pt = 97;
+
+                       switch_core_media_add_payload_map(session,
+                                                                                         SWITCH_MEDIA_TYPE_TEXT,
+                                                                                         "red",
+                                                                                         NULL,
+                                                                                         NULL,
+                                                                                         SDP_TYPE_REQUEST,
+                                                                                         t_engine->red_pt,
+                                                                                         1000,
+                                                                                         0,
+                                                                                         1,
+                                                                                         SWITCH_TRUE);
+
+                       switch_core_media_add_payload_map(session,
+                                                                                         SWITCH_MEDIA_TYPE_TEXT,
+                                                                                         "t140",
+                                                                                         NULL,
+                                                                                         NULL,
+                                                                                         SDP_TYPE_REQUEST,
+                                                                                         t_engine->t140_pt,
+                                                                                         1000,
+                                                                                         0,
+                                                                                         1,
+                                                                                         SWITCH_TRUE);
+
+                       t_engine->codec_negotiated = 1;
+               }
+
+               if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+                       if (switch_channel_test_flag(smh->session->channel, CF_DTLS)) {
+                               t_engine->no_crypto = 1;
+                       }
+               }
+
+               
+               if (!t_engine->local_sdp_port) {
+                       switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_TEXT, 0);
+               }
+
+               if ((t_port = t_engine->adv_sdp_port)) {
+                       int loops;
+
+                       for (loops = 0; loops < 2; loops++) {
+
+                               if (switch_channel_test_flag(smh->session->channel, CF_ICE)) {
+                                       gen_ice(session, SWITCH_MEDIA_TYPE_TEXT, ip, (switch_port_t)t_port);
+                               }
+
+
+                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "m=text %d %s", 
+                                                               t_port, 
+                                                               get_media_profile_name(session, 
+                                                                                                          (loops == 0 && switch_channel_test_flag(session->channel, CF_SECURE) 
+                                                                                                               && switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) || 
+                                                                                                          a_engine->crypto_type != CRYPTO_INVALID || switch_channel_test_flag(session->channel, CF_DTLS)));
+                       
+                                                       
+                               /*****************************/
+                               if (t_engine->codec_negotiated) {
+                                       
+                                       switch_mutex_lock(smh->sdp_mutex);
+                                       for (pmap = t_engine->payload_map; pmap && pmap->allocated; pmap = pmap->next) {
+
+                                               if (pmap->type != SWITCH_MEDIA_TYPE_TEXT || !pmap->negotiated) {
+                                                       continue;
+                                               }
+
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), " %d", pmap->pt);
+                                               
+                                       }
+                                       switch_mutex_unlock(smh->sdp_mutex);
+                                       
+
+                                       //TEXT switch_core_media_set_text_codec(session, 0);
+                                       //switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), " %d", t_engine->cur_payload_map->agreed_pt);
+                               }
+
+                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "\r\n");
+
+                               if (t_engine->codec_negotiated) {
+                                       switch_mutex_lock(smh->sdp_mutex);
+                                       for (pmap = t_engine->payload_map; pmap && pmap->allocated; pmap = pmap->next) {
+
+                                               if (pmap->type != SWITCH_MEDIA_TYPE_TEXT || !pmap->negotiated) {
+                                                       continue;
+                                               }
+                                       
+                                               if (!strcasecmp(pmap->iananame, "t140")) {
+                                                       t_engine->t140_pt = pmap->pt;
+                                               }
+
+                                               if (!strcasecmp(pmap->iananame, "red")) {
+                                                       t_engine->red_pt = pmap->pt;
+                                               }
+                                               
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%ld\r\n",
+                                                                               pmap->pt, pmap->iananame, pmap->rate);
+                                               
+                                       }
+                                       switch_mutex_unlock(smh->sdp_mutex);
+
+
+                                       if (t_engine->t140_pt && t_engine->red_pt) {
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=fmtp:%d %d/%d/%d\r\n", t_engine->red_pt, t_engine->t140_pt, t_engine->t140_pt, t_engine->t140_pt);
+                                       }
+
+                                       
+                                       if (t_engine->smode == SWITCH_MEDIA_FLOW_SENDONLY) {
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "%s", "a=sendonly\r\n");
+                                       } else if (t_engine->smode == SWITCH_MEDIA_FLOW_RECVONLY) {
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "%s", "a=recvonly\r\n");
+                                       }
+
+                               }
+
+                               if ((is_outbound || switch_channel_test_flag(session->channel, CF_RECOVERING))
+                                       && switch_channel_test_flag(smh->session->channel, CF_DTLS)) {
+                                       generate_local_fingerprint(smh, SWITCH_MEDIA_TYPE_TEXT);
+                               }
+
+
+                               if (!zstr(t_engine->local_dtls_fingerprint.type)) {
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=fingerprint:%s %s\na=setup:%s\r\n", t_engine->local_dtls_fingerprint.type, 
+                                                                       t_engine->local_dtls_fingerprint.str, get_setup(t_engine, session, sdp_type));
+                               }
+
+
+                               if (smh->mparams->rtcp_text_interval_msec) {
+                                       if (t_engine->rtcp_mux > 0) {
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp-mux\r\n");
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp:%d IN %s %s\r\n", t_port, family, ip);
+                                       } else {
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp:%d IN %s %s\r\n", t_port + 1, family, ip);
+                                       }
+                               }
+
+                               //switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u\r\n", t_engine->ssrc);
+                               
+                               if (t_engine->ice_out.cands[0][0].ready) {
+                                       char tmp1[11] = "";
+                                       char tmp2[11] = "";
+                                       uint32_t c1 = (2^24)*126 + (2^8)*65535 + (2^0)*(256 - 1);
+                                       //uint32_t c2 = (2^24)*126 + (2^8)*65535 + (2^0)*(256 - 2);
+                                       //uint32_t c3 = (2^24)*126 + (2^8)*65534 + (2^0)*(256 - 1);
+                                       //uint32_t c4 = (2^24)*126 + (2^8)*65534 + (2^0)*(256 - 2);
+
+                                       uint32_t c2 = c1 - 1;
+                                       uint32_t c3 = c1 - 2;
+                                       uint32_t c4 = c1 - 3;
+                               
+                                       tmp1[10] = '\0';
+                                       tmp2[10] = '\0';
+                                       switch_stun_random_string(tmp1, 10, "0123456789");
+                                       switch_stun_random_string(tmp2, 10, "0123456789");
+
+                                       ice_out = &t_engine->ice_out;
+                                       
+                                       
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u cname:%s\r\n", t_engine->ssrc, smh->cname);
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u msid:%s v0\r\n", t_engine->ssrc, smh->msid);
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u mslabel:%s\r\n", t_engine->ssrc, smh->msid);
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u label:%sv0\r\n", t_engine->ssrc, smh->msid);
+                               
+
+                               
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ice-ufrag:%s\r\n", ice_out->ufrag);
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ice-pwd:%s\r\n", ice_out->pwd);
+
+
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=candidate:%s 1 %s %u %s %d typ host generation 0\r\n", 
+                                                                       tmp1, ice_out->cands[0][0].transport, c1,
+                                                                       ice_out->cands[0][0].con_addr, ice_out->cands[0][0].con_port
+                                                                       );
+
+                                       if (!zstr(t_engine->local_sdp_ip) && !zstr(ice_out->cands[0][0].con_addr) && 
+                                               strcmp(t_engine->local_sdp_ip, ice_out->cands[0][0].con_addr)
+                                               && t_engine->local_sdp_port != ice_out->cands[0][0].con_port) {
+
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=candidate:%s 1 %s %u %s %d typ srflx raddr %s rport %d generation 0\r\n", 
+                                                                               tmp2, ice_out->cands[0][0].transport, c3,
+                                                                               ice_out->cands[0][0].con_addr, ice_out->cands[0][0].con_port,
+                                                                               t_engine->local_sdp_ip, t_engine->local_sdp_port
+                                                                               );
+                                       }
+
+
+                                       if (t_engine->rtcp_mux < 1 || is_outbound || switch_channel_test_flag(session->channel, CF_RECOVERING)) {
+
+                                               switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=candidate:%s 2 %s %u %s %d typ host generation 0\r\n", 
+                                                                               tmp1, ice_out->cands[0][0].transport, c2,
+                                                                               ice_out->cands[0][0].con_addr, ice_out->cands[0][0].con_port + (t_engine->rtcp_mux > 0 ? 0 : 1)
+                                                                               );
+                                       
+                                       
+                                               if (!zstr(t_engine->local_sdp_ip) && !zstr(ice_out->cands[0][1].con_addr) && 
+                                                       strcmp(t_engine->local_sdp_ip, ice_out->cands[0][1].con_addr)
+                                                       && t_engine->local_sdp_port != ice_out->cands[0][1].con_port) {
+                                               
+                                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=candidate:%s 2 %s %u %s %d typ srflx generation 0\r\n", 
+                                                                                       tmp2, ice_out->cands[0][0].transport, c4,
+                                                                                       ice_out->cands[0][0].con_addr, ice_out->cands[0][0].con_port + (t_engine->rtcp_mux > 0 ? 0 : 1),
+                                                                                       t_engine->local_sdp_ip, t_engine->local_sdp_port + (t_engine->rtcp_mux > 0 ? 0 : 1)
+                                                                                       );
+                                               }
+                                       }
+
+                       
+                               
+#ifdef GOOGLE_ICE
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ice-options:google-ice\r\n");
+#endif
+                               }
+
+                               
+
+                               if (loops == 0 && switch_channel_test_flag(session->channel, CF_SECURE) && !switch_channel_test_flag(session->channel, CF_DTLS)) {
+                                       int i;
+                               
+                                       for (i = 0; smh->crypto_suite_order[i] != CRYPTO_INVALID; i++) {
+                                               switch_rtp_crypto_key_type_t j = SUITES[smh->crypto_suite_order[i]].type;
+                                       
+                                               if ((t_engine->crypto_type == j || t_engine->crypto_type == CRYPTO_INVALID) && !zstr(t_engine->ssec[j].local_crypto_key)) {
+                                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=crypto:%s\r\n", t_engine->ssec[j].local_crypto_key);
+                                               }
+                                       }
+                                       //switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=encryption:optional\r\n");
+                               }
+
+
+                               if (local_sdp_text_zrtp_hash) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Adding text a=zrtp-hash:%s\n", local_sdp_text_zrtp_hash);
+                                       switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=zrtp-hash:%s\r\n", local_sdp_text_zrtp_hash);
+                               }
+
+
+                               if (switch_channel_test_flag(session->channel, CF_DTLS) || 
+                                       !switch_channel_test_flag(session->channel, CF_SECURE) || 
+                                       smh->crypto_mode == CRYPTO_MODE_MANDATORY || smh->crypto_mode == CRYPTO_MODE_FORBIDDEN) {
+                                       break;
+                               }
+                       }
+               }
+
+       }
+       
+
+
 
        if (map) {
                switch_event_destroy(&map);
@@ -8965,13 +10216,14 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
 {
        switch_size_t len;
        char *p, *q, *pe, *qe;
-       int has_video = 0, has_audio = 0, has_ip = 0;
+       int has_video = 0, has_audio = 0, has_text = 0, has_ip = 0;
        char port_buf[25] = "";
        char vport_buf[25] = "";
+       char tport_buf[25] = "";
        char *new_sdp;
        int bad = 0;
        switch_media_handle_t *smh;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        payload_map_t *pmap;
 
        switch_assert(session);
@@ -8982,6 +10234,7 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        if (zstr(smh->mparams->local_sdp_str)) {
                return;
@@ -9123,48 +10376,111 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
 
                        }
 
-               } else if ((!strncmp("m=audio ", p, 8) && *(p + 8) != '0') || (!strncmp("m=image ", p, 8) && *(p + 8) != '0')) {
+               } else if ((!strncmp("m=audio ", p, 8) && *(p + 8) != '0') || (!strncmp("m=image ", p, 8) && *(p + 8) != '0')) {
+                       strncpy(q, p, 8);
+                       p += 8;
+
+                       if (p >= pe) {
+                               bad = 4;
+                               goto end;
+                       }
+
+
+                       q += 8;
+
+                       if (q >= qe) {
+                               bad = 5;
+                               goto end;
+                       }
+
+
+                       strncpy(q, port_buf, strlen(port_buf));
+                       q += strlen(port_buf);
+
+                       if (q >= qe) {
+                               bad = 6;
+                               goto end;
+                       }
+
+                       while (p && *p && (*p >= '0' && *p <= '9')) {
+                               if (p >= pe) {
+                                       bad = 7;
+                                       goto end;
+                               }
+                               p++;
+                       }
+
+                       has_audio++;
+
+               } else if (!strncmp("m=video ", p, 8) && *(p + 8) != '0') {
+                       if (!has_video) {
+                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_VIDEO, 1);
+                               clear_pmaps(v_engine);
+                               pmap = switch_core_media_add_payload_map(session,
+                                                                                                                SWITCH_MEDIA_TYPE_VIDEO,
+                                                                                                                "PROXY-VID",
+                                                                                                                NULL,
+                                                                                                                NULL,
+                                                                                                                SDP_TYPE_RESPONSE,
+                                                                                                                0,
+                                                                                                                90000,
+                                                                                                                90000,
+                                                                                                                1,
+                                                                                                                SWITCH_TRUE);
+                               v_engine->cur_payload_map = pmap;
+
+                               switch_snprintf(vport_buf, sizeof(vport_buf), "%u", v_engine->adv_sdp_port);
+                               
+                               if (switch_channel_media_ready(session->channel) && !switch_rtp_ready(v_engine->rtp_session)) {
+                                       switch_channel_set_flag(session->channel, CF_VIDEO_POSSIBLE);
+                                       switch_channel_set_flag(session->channel, CF_REINVITE);
+                                       switch_core_media_activate_rtp(session);
+                               }
+
+                               v_engine->codec_negotiated = 1;
+                               switch_core_media_set_video_codec(session, SWITCH_FALSE);
+                       }
+
                        strncpy(q, p, 8);
                        p += 8;
 
                        if (p >= pe) {
-                               bad = 4;
+                               bad = 8;
                                goto end;
                        }
 
-
                        q += 8;
 
                        if (q >= qe) {
-                               bad = 5;
+                               bad = 9;
                                goto end;
                        }
 
-
-                       strncpy(q, port_buf, strlen(port_buf));
-                       q += strlen(port_buf);
+                       strncpy(q, vport_buf, strlen(vport_buf));
+                       q += strlen(vport_buf);
 
                        if (q >= qe) {
-                               bad = 6;
+                               bad = 10;
                                goto end;
                        }
 
                        while (p && *p && (*p >= '0' && *p <= '9')) {
+
                                if (p >= pe) {
-                                       bad = 7;
+                                       bad = 11;
                                        goto end;
                                }
+
                                p++;
                        }
 
-                       has_audio++;
-
-               } else if (!strncmp("m=video ", p, 8) && *(p + 8) != '0') {
-                       if (!has_video) {
-                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_VIDEO, 1);
-                               clear_pmaps(v_engine);
+                       has_video++;
+                               } else if (!strncmp("m=text ", p, 8) && *(p + 8) != '0') {
+                       if (!has_text) {
+                               switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_TEXT, 1);
+                               clear_pmaps(t_engine);
                                pmap = switch_core_media_add_payload_map(session,
-                                                                                                                SWITCH_MEDIA_TYPE_VIDEO,
+                                                                                                                SWITCH_MEDIA_TYPE_TEXT,
                                                                                                                 "PROXY-VID",
                                                                                                                 NULL,
                                                                                                                 NULL,
@@ -9174,18 +10490,18 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
                                                                                                                 90000,
                                                                                                                 1,
                                                                                                                 SWITCH_TRUE);
-                               v_engine->cur_payload_map = pmap;
+                               t_engine->cur_payload_map = pmap;
 
-                               switch_snprintf(vport_buf, sizeof(vport_buf), "%u", v_engine->adv_sdp_port);
+                               switch_snprintf(tport_buf, sizeof(tport_buf), "%u", t_engine->adv_sdp_port);
                                
-                               if (switch_channel_media_ready(session->channel) && !switch_rtp_ready(v_engine->rtp_session)) {
-                                       switch_channel_set_flag(session->channel, CF_VIDEO_POSSIBLE);
+                               if (switch_channel_media_ready(session->channel) && !switch_rtp_ready(t_engine->rtp_session)) {
+                                       switch_channel_set_flag(session->channel, CF_TEXT_POSSIBLE);
                                        switch_channel_set_flag(session->channel, CF_REINVITE);
                                        switch_core_media_activate_rtp(session);
                                }
 
-                               v_engine->codec_negotiated = 1;
-                               switch_core_media_set_video_codec(session, SWITCH_FALSE);
+                               t_engine->codec_negotiated = 1;
+                               //TEXT switch_core_media_set_text_codec(session, SWITCH_FALSE);
                        }
 
                        strncpy(q, p, 8);
@@ -9203,8 +10519,8 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
                                goto end;
                        }
 
-                       strncpy(q, vport_buf, strlen(vport_buf));
-                       q += strlen(vport_buf);
+                       strncpy(q, tport_buf, strlen(tport_buf));
+                       q += strlen(tport_buf);
 
                        if (q >= qe) {
                                bad = 10;
@@ -9221,9 +10537,10 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
                                p++;
                        }
 
-                       has_video++;
+                       has_text++;
                }
 
+
                while (p && *p && *p != '\n') {
 
                        if (p >= pe) {
@@ -9450,7 +10767,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_reset_jb(switch_core_session_t
 SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
 {
        switch_media_handle_t *smh;
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine;//, *t_engine;
        switch_status_t status = SWITCH_STATUS_SUCCESS;
 
        switch_assert(session);
@@ -9465,6 +10782,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_se
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       //t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        switch (msg->message_id) {
 
@@ -10515,7 +11833,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_codec_chosen(switch_core_sessi
 
 SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *session)
 {
-       switch_rtp_engine_t *a_engine, *v_engine;
+       switch_rtp_engine_t *a_engine, *v_engine, *t_engine;
        switch_media_handle_t *smh;
        int type;
 
@@ -10527,6 +11845,7 @@ SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *sessi
 
        a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO];
        v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 
        if (switch_core_codec_ready(&v_engine->read_codec)) {
                type = 1;
@@ -10546,6 +11865,10 @@ SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *sessi
                switch_rtp_reset(v_engine->rtp_session);
        }
 
+       if (t_engine->rtp_session) {
+               switch_rtp_reset(t_engine->rtp_session);
+       }
+
 
        smh->msid = NULL;
        smh->cname = NULL;
@@ -10554,6 +11877,11 @@ SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *sessi
        v_engine->ice_out.cands[0][0].foundation = NULL;
        v_engine->ice_out.cands[0][0].component_id = 0;
 
+       t_engine->ice_out.ufrag = NULL;
+       t_engine->ice_out.pwd = NULL;
+       t_engine->ice_out.cands[0][0].foundation = NULL;
+       t_engine->ice_out.cands[0][0].component_id = 0;
+
 
        a_engine->ice_out.ufrag = NULL;
        a_engine->ice_out.pwd = NULL;
@@ -10564,6 +11892,10 @@ SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *sessi
                gen_ice(smh->session, SWITCH_MEDIA_TYPE_VIDEO, NULL, 0);
        }
 
+       if (t_engine->ice_in.cands[t_engine->ice_in.chosen[0]][0].ready) {
+               gen_ice(smh->session, SWITCH_MEDIA_TYPE_TEXT, NULL, 0);
+       }
+
        if (a_engine->ice_in.cands[a_engine->ice_in.chosen[0]][0].ready) {
                gen_ice(smh->session, SWITCH_MEDIA_TYPE_AUDIO, NULL, 0);
        }
@@ -10573,9 +11905,11 @@ SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *sessi
        
        a_engine->local_dtls_fingerprint.len = 0;
        v_engine->local_dtls_fingerprint.len = 0;
+       t_engine->local_dtls_fingerprint.len = 0;
 
        a_engine->remote_ssrc = 0;
        v_engine->remote_ssrc = 0;
+       t_engine->remote_ssrc = 0;
 
        switch_channel_clear_flag(smh->session->channel, CF_VIDEO_READY);
        switch_core_session_wake_video_thread(smh->session);
@@ -11185,180 +12519,476 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_codec_control(switch_core_sess
        switch_media_handle_t *smh = NULL;
        switch_codec_t *codec = NULL;
 
-       switch_assert(session);
+       switch_assert(session);
+
+       if (!(smh = session->media_handle)) {
+               return SWITCH_STATUS_FALSE;
+       }
+       
+       if (!(engine = &smh->engines[mtype])) {
+               return SWITCH_STATUS_NOTIMPL;
+       }
+
+       if (iotype == SWITCH_IO_READ) {
+               codec = &engine->read_codec;
+       } else {
+               codec = &engine->write_codec;
+       }
+
+       if (!switch_core_codec_ready(codec)) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (mtype == SWITCH_MEDIA_TYPE_VIDEO) {
+               if (!switch_channel_test_flag(session->channel, CF_VIDEO)) {
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
+       if (codec) {
+               if (cmd == SCC_VIDEO_GEN_KEYFRAME) {
+                       switch_time_t now = switch_micro_time_now();
+
+                       if (smh->last_codec_refresh && (now - smh->last_codec_refresh) < VIDEO_REFRESH_FREQ) {
+                               return SWITCH_STATUS_BREAK;
+                       }
+
+                       smh->last_codec_refresh = now;
+                       switch_channel_set_flag(session->channel, CF_VIDEO_REFRESH_REQ);
+               }
+
+               return switch_core_codec_control(codec, cmd, ctype, cmd_data, atype, cmd_arg, rtype, ret_data);
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_write_encoded_video_frame(switch_core_session_t *session, 
+                                                                                                                                               switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
+{
+       switch_io_event_hook_video_write_frame_t *ptr;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+
+       if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_INACTIVE) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG3, "Writing video to RECVONLY/INACTIVE session\n");
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       if (session->endpoint_interface->io_routines->write_video_frame) {
+               if ((status = session->endpoint_interface->io_routines->write_video_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
+                       for (ptr = session->event_hooks.video_write_frame; ptr; ptr = ptr->next) {
+                               if ((status = ptr->video_write_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return status;
+}
+
+SWITCH_DECLARE(void) switch_core_session_video_reinit(switch_core_session_t *session)
+{
+       switch_media_handle_t *smh;
+       int type;
+
+       switch_assert(session);
+
+       if (!(smh = session->media_handle)) {
+               return;
+       }
+
+       if (switch_channel_down(session->channel)) {
+               return;
+       }
+
+       smh->video_init = 0;
+       smh->video_last_key_time = 0;
+       switch_core_session_send_and_request_video_refresh(session);
+       
+       type = 1;
+       switch_core_media_codec_control(session, SWITCH_MEDIA_TYPE_VIDEO, SWITCH_IO_READ, SCC_VIDEO_RESET, SCCT_INT, (void *)&type, SCCT_NONE, NULL, NULL, NULL);
+       switch_core_session_request_video_refresh(session);
+
+}
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags,
+                                                                                                                                         int stream_id)
+{
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       switch_time_t now = switch_micro_time_now();
+       switch_codec_t *codec = switch_core_session_get_video_write_codec(session);
+       switch_timer_t *timer;
+       switch_media_handle_t *smh;
+       switch_image_t *dup_img = NULL, *img = frame->img;
+       switch_status_t encode_status;
+       switch_frame_t write_frame = {0};
+       //switch_rtp_engine_t *v_engine;
+
+       switch_assert(session);
+
+       if (!(smh = session->media_handle)) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (switch_channel_down(session->channel)) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (!codec) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s has no video codec\n", switch_core_session_get_name(session));
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_INACTIVE) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG3, "Writing video to RECVONLY/INACTIVE session\n");
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       if (switch_channel_test_flag(session->channel, CF_VIDEO_PAUSE_WRITE)) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       if (!(switch_channel_test_flag(session->channel, CF_VIDEO_READY) || (flags & SWITCH_IO_FLAG_FORCE))) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       //v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];    
+       if (smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO] && switch_mutex_trylock(smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO]) != SWITCH_STATUS_SUCCESS) {
+               /* return CNG, another thread is already writing  */
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s is already being written to for %s\n", 
+                                                 switch_channel_get_name(session->channel), type2str(SWITCH_MEDIA_TYPE_VIDEO));
+               return SWITCH_STATUS_INUSE;
+       }
+
+       if (!smh->video_init && smh->mparams->video_key_first && (now - smh->video_last_key_time) > smh->mparams->video_key_first) {
+               switch_core_media_gen_key_frame(session);
+
+               if (smh->video_last_key_time) {
+                       smh->video_init = 1;
+               }
+
+               smh->video_last_key_time = now;
+       }       
+
+       if (smh->mparams->video_key_freq && (now - smh->video_last_key_time) > smh->mparams->video_key_freq) {
+               switch_core_media_gen_key_frame(smh->session);
+               smh->video_last_key_time = now;
+       }       
+
+       if (!img) {
+               switch_status_t vstatus = switch_core_session_write_encoded_video_frame(session, frame, flags, stream_id);
+               switch_goto_status(vstatus, done);
+       }
+
+
+       /* When desired, scale video to match the input signal (if output is bigger) */
+       if (switch_channel_test_flag(session->channel, CF_VIDEO_READY) && smh->vid_params.width && 
+               switch_channel_test_flag(session->channel, CF_VIDEO_MIRROR_INPUT) && 
+               ((smh->vid_params.width != img->d_w) || (smh->vid_params.height != img->d_h))) {
+
+               switch_img_letterbox(img, &dup_img, smh->vid_params.width, smh->vid_params.height, "#000000f");
+
+               img = dup_img;
+       }
+       
+       if (session->bugs) {
+               switch_media_bug_t *bp;
+               int prune = 0;
+               int patched = 0;
+
+               switch_thread_rwlock_rdlock(session->bug_rwlock);
+               for (bp = session->bugs; bp; bp = bp->next) {
+                       switch_bool_t ok = SWITCH_TRUE;
+
+                       if (switch_channel_test_flag(session->channel, CF_PAUSE_BUGS) && !switch_core_media_bug_test_flag(bp, SMBF_NO_PAUSE)) {
+                               continue;
+                       }
+               
+                       if (!switch_channel_test_flag(session->channel, CF_ANSWERED) && switch_core_media_bug_test_flag(bp, SMBF_ANSWER_REQ)) {
+                               continue;
+                       }
+               
+                       if (switch_test_flag(bp, SMBF_PRUNE)) {
+                               prune++;
+                               continue;
+                       }
+               
+                       if (bp->ready && switch_test_flag(bp, SMBF_WRITE_VIDEO_STREAM)) {
+                               switch_image_t *dimg = NULL;
+                               
+                               switch_img_copy(img, &dimg);
+                               switch_queue_push(bp->write_video_queue, dimg);
+
+                               if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM_BLEG)) {
+                                       switch_core_media_bug_patch_spy_frame(bp, img, SWITCH_RW_WRITE);
+                                       patched = 1;
+                               }
+
+                       }
 
-       if (!(smh = session->media_handle)) {
-               return SWITCH_STATUS_FALSE;
-       }
-       
-       if (!(engine = &smh->engines[mtype])) {
-               return SWITCH_STATUS_NOTIMPL;
-       }
+                       if (bp->ready && img && 
+                               (switch_test_flag(bp, SMBF_WRITE_VIDEO_PING) || (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched))) {
+                               switch_frame_t bug_frame = { 0 };
 
-       if (iotype == SWITCH_IO_READ) {
-               codec = &engine->read_codec;
-       } else {
-               codec = &engine->write_codec;
-       }
+                               bug_frame.img = img;
 
-       if (!switch_core_codec_ready(codec)) {
-               return SWITCH_STATUS_FALSE;
-       }
+                               if (bp->callback && switch_test_flag(bp, SMBF_WRITE_VIDEO_PING)) {
+                                       bp->video_ping_frame = &bug_frame;
+                                       if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE_VIDEO_PING) == SWITCH_FALSE
+                                               || (bp->stop_time && bp->stop_time <= switch_epoch_time_now(NULL))) {
+                                               ok = SWITCH_FALSE;
+                                       }
+                                       bp->video_ping_frame = NULL;
+                               }
 
-       if (mtype == SWITCH_MEDIA_TYPE_VIDEO) {
-               if (!switch_channel_test_flag(session->channel, CF_VIDEO)) {
-                       return SWITCH_STATUS_FALSE;
-               }
-       }
+                               if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM_BLEG) && !patched) {
+                                       switch_core_media_bug_patch_spy_frame(bp, img, SWITCH_RW_WRITE);
+                               }
 
-       if (codec) {
-               if (cmd == SCC_VIDEO_GEN_KEYFRAME) {
-                       switch_time_t now = switch_micro_time_now();
+                               
+                       }
 
-                       if (smh->last_codec_refresh && (now - smh->last_codec_refresh) < VIDEO_REFRESH_FREQ) {
-                               return SWITCH_STATUS_BREAK;
+                       if (ok == SWITCH_FALSE) {
+                               switch_set_flag(bp, SMBF_PRUNE);
+                               prune++;
                        }
 
-                       smh->last_codec_refresh = now;
-                       switch_channel_set_flag(session->channel, CF_VIDEO_REFRESH_REQ);
                }
 
-               return switch_core_codec_control(codec, cmd, ctype, cmd_data, atype, cmd_arg, rtype, ret_data);
+               switch_thread_rwlock_unlock(session->bug_rwlock);
+
+               if (prune) {
+                       switch_core_media_bug_prune(session);
+               }
+               
        }
 
-       return SWITCH_STATUS_FALSE;
-}
+       write_frame = *frame;
+       frame = &write_frame;
+       frame->img = img;
 
+       if (!switch_test_flag(frame, SFF_USE_VIDEO_TIMESTAMP)) {
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_write_encoded_video_frame(switch_core_session_t *session, 
-                                                                                                                                               switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
-{
-       switch_io_event_hook_video_write_frame_t *ptr;
-       switch_status_t status = SWITCH_STATUS_FALSE;
+               if (!(timer = switch_core_media_get_timer(session, SWITCH_MEDIA_TYPE_VIDEO))) {
+                       
+                       if (!smh->video_timer.timer_interface) {
+                               switch_core_timer_init(&smh->video_timer, "soft", 1, 90, switch_core_session_get_pool(session));
+                       }
+                       switch_core_timer_sync(&smh->video_timer);
+                       timer = &smh->video_timer;
+               }
 
-       if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_INACTIVE) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG3, "Writing video to RECVONLY/INACTIVE session\n");
-               return SWITCH_STATUS_SUCCESS;
+               frame->timestamp = timer->samplecount;
        }
 
-       if (session->endpoint_interface->io_routines->write_video_frame) {
-               if ((status = session->endpoint_interface->io_routines->write_video_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
-                       for (ptr = session->event_hooks.video_write_frame; ptr; ptr = ptr->next) {
-                               if ((status = ptr->video_write_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
-                                       break;
-                               }
+       switch_clear_flag(frame, SFF_SAME_IMAGE);
+       frame->m = 0;
+
+       do {
+               frame->datalen = SWITCH_DEFAULT_VIDEO_SIZE;
+               encode_status = switch_core_codec_encode_video(codec, frame);
+               
+               if (encode_status == SWITCH_STATUS_SUCCESS || encode_status == SWITCH_STATUS_MORE_DATA) {
+
+                       switch_assert((encode_status == SWITCH_STATUS_SUCCESS && frame->m) || !frame->m);
+                       
+                       if (frame->flags & SFF_PICTURE_RESET) {
+                               switch_core_session_video_reinit(session);
+                               frame->flags &= ~SFF_PICTURE_RESET;
                        }
+
+                       if (frame->datalen == 0) break;
+
+                       switch_set_flag(frame, SFF_RAW_RTP_PARSE_FRAME);
+                       status = switch_core_session_write_encoded_video_frame(session, frame, flags, stream_id);
                }
+               
+       } while(status == SWITCH_STATUS_SUCCESS && encode_status == SWITCH_STATUS_MORE_DATA);
+
+ done:
+
+       if (smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO]) {
+               switch_mutex_unlock(smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO]);
        }
 
+       switch_img_free(&dup_img);
+       
        return status;
 }
 
-SWITCH_DECLARE(void) switch_core_session_video_reinit(switch_core_session_t *session)
+SWITCH_DECLARE(switch_status_t) switch_core_session_wait_for_video_input_params(switch_core_session_t *session, uint32_t timeout_ms)
 {
        switch_media_handle_t *smh;
-       int type;
+       switch_codec_implementation_t read_impl = { 0 };
+       switch_rtp_engine_t *v_engine = NULL;
 
-       switch_assert(session);
+       switch_assert(session != NULL);
 
        if (!(smh = session->media_handle)) {
-               return;
+               return SWITCH_STATUS_FALSE;
+       }
+       
+       if (!switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ)) {
+               return SWITCH_STATUS_GENERR;;
        }
 
-       if (switch_channel_down(session->channel)) {
-               return;
+       v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+
+       if (v_engine->smode == SWITCH_MEDIA_FLOW_SENDONLY) {
+               return SWITCH_STATUS_NOTIMPL;
        }
 
-       smh->video_init = 0;
-       smh->video_last_key_time = 0;
-       switch_core_session_send_and_request_video_refresh(session);
-       
-       type = 1;
-       switch_core_media_codec_control(session, SWITCH_MEDIA_TYPE_VIDEO, SWITCH_IO_READ, SCC_VIDEO_RESET, SCCT_INT, (void *)&type, SCCT_NONE, NULL, NULL, NULL);
-       switch_core_session_request_video_refresh(session);
+       switch_core_session_get_read_impl(session, &read_impl);
+               
+       while(switch_channel_ready(session->channel) && timeout_ms > 0) {
+               switch_frame_t *read_frame;
+               switch_status_t status;
+               
+               if (video_globals.synced && 
+                       switch_channel_test_flag(session->channel, CF_VIDEO_READY) && smh->vid_params.width && smh->vid_params.height && smh->vid_params.fps) {
+                       return SWITCH_STATUS_SUCCESS;
+               }
+
+               switch_core_session_request_video_refresh(session);
+               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+               if (!SWITCH_READ_ACCEPTABLE(status)) {
+                       return SWITCH_STATUS_FALSE;
+               }
+
+               timeout_ms -= (read_impl.microseconds_per_packet / 1000);
+       }
 
+       return SWITCH_STATUS_TIMEOUT;
+       
 }
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags,
-                                                                                                                                         int stream_id)
+SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags,
+                                                                                                                                        int stream_id)
 {
        switch_status_t status = SWITCH_STATUS_FALSE;
-       switch_time_t now = switch_micro_time_now();
-       switch_codec_t *codec = switch_core_session_get_video_write_codec(session);
-       switch_timer_t *timer;
+       switch_io_event_hook_video_read_frame_t *ptr;
+       uint32_t loops = 0;
        switch_media_handle_t *smh;
-       switch_image_t *dup_img = NULL, *img = frame->img;
-       switch_status_t encode_status;
-       switch_frame_t write_frame = {0};
-       //switch_rtp_engine_t *v_engine;
-
-       switch_assert(session);
+       int patchers = 0;
+               
+       switch_assert(session != NULL);
 
        if (!(smh = session->media_handle)) {
                return SWITCH_STATUS_FALSE;
        }
 
-       if (switch_channel_down(session->channel)) {
+ top:
+
+       loops++;
+
+       if (switch_channel_down_nosig(session->channel)) {
                return SWITCH_STATUS_FALSE;
        }
 
-       if (!codec) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s has no video codec\n", switch_core_session_get_name(session));
-               return SWITCH_STATUS_FALSE;
+       if (session->endpoint_interface->io_routines->read_video_frame) {
+               if ((status = session->endpoint_interface->io_routines->read_video_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
+                       for (ptr = session->event_hooks.video_read_frame; ptr; ptr = ptr->next) {
+                               if ((status = ptr->video_read_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
+                                       break;
+                               }
+                       }
+               }
        }
 
-       if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_INACTIVE) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG3, "Writing video to RECVONLY/INACTIVE session\n");
+       if (switch_channel_test_flag(session->channel, CF_VIDEO_PAUSE_READ)) {
+               *frame = &runtime.dummy_cng_frame;
+               switch_cond_next();
                return SWITCH_STATUS_SUCCESS;
        }
 
-       if (switch_channel_test_flag(session->channel, CF_VIDEO_PAUSE_WRITE)) {
+       if (status == SWITCH_STATUS_INUSE) {
+               *frame = &runtime.dummy_cng_frame;
+               switch_cond_next();
                return SWITCH_STATUS_SUCCESS;
        }
 
-       if (!(switch_channel_test_flag(session->channel, CF_VIDEO_READY) || (flags & SWITCH_IO_FLAG_FORCE))) {
-               return SWITCH_STATUS_SUCCESS;
+       if (status != SWITCH_STATUS_SUCCESS) {
+               goto done;
        }
 
-       //v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];    
-       if (smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO] && switch_mutex_trylock(smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO]) != SWITCH_STATUS_SUCCESS) {
-               /* return CNG, another thread is already writing  */
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s is already being written to for %s\n", 
-                                                 switch_channel_get_name(session->channel), type2str(SWITCH_MEDIA_TYPE_VIDEO));
-               return SWITCH_STATUS_INUSE;
+       if (!(*frame)) {
+               goto done;
+       }
+       
+       if (switch_channel_test_flag(session->channel, CF_VIDEO_DEBUG_READ)) {
+               if (switch_test_flag((*frame), SFF_CNG)) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "VIDEO: CNG\n");
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, 
+                                                         "VIDEO: seq: %d ts: %u len: %4d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x mark: %d\n",
+                                                         (*frame)->seq, (*frame)->timestamp, (*frame)->datalen,
+                                                         *((uint8_t *)(*frame)->data), *((uint8_t *)(*frame)->data + 1),
+                                                         *((uint8_t *)(*frame)->data + 2), *((uint8_t *)(*frame)->data + 3),
+                                                         *((uint8_t *)(*frame)->data + 4), *((uint8_t *)(*frame)->data + 5),
+                                                         *((uint8_t *)(*frame)->data + 6), *((uint8_t *)(*frame)->data + 7),
+                                                         *((uint8_t *)(*frame)->data + 8), *((uint8_t *)(*frame)->data + 9),
+                                                         *((uint8_t *)(*frame)->data + 10), (*frame)->m);
+               }
        }
 
-       if (!smh->video_init && smh->mparams->video_key_first && (now - smh->video_last_key_time) > smh->mparams->video_key_first) {
-               switch_core_media_gen_key_frame(session);
 
-               if (smh->video_last_key_time) {
-                       smh->video_init = 1;
+       if (switch_test_flag(*frame, SFF_CNG)) {
+               status = SWITCH_STATUS_SUCCESS;
+               goto done;
+       }
+
+       if (switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ) && (*frame)->img == NULL) {
+               switch_status_t decode_status;
+
+               (*frame)->img = NULL;
+
+               decode_status = switch_core_codec_decode_video((*frame)->codec, *frame);
+               
+               if ((*frame)->img && switch_channel_test_flag(session->channel, CF_VIDEO_DEBUG_READ)) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "IMAGE %dx%d %dx%d\n", 
+                                                         (*frame)->img->w, (*frame)->img->h, (*frame)->img->d_w, (*frame)->img->d_h);
+               }
+
+               if ((*frame)->img && (*frame)->img->d_w && (*frame)->img->d_h) {
+
+                       if ((*frame)->img->d_w != smh->vid_params.width) {
+                               switch_channel_set_variable_printf(session->channel, "video_width", "%d", (*frame)->img->d_w);
+                               smh->vid_params.width = (*frame)->img->d_w;
+                       }
+
+                       if ((*frame)->img->d_h != smh->vid_params.height) {
+                               switch_channel_set_variable_printf(session->channel, "video_height", "%d", (*frame)->img->d_h);
+                               smh->vid_params.height = (*frame)->img->d_h;
+                       }
                }
 
-               smh->video_last_key_time = now;
-       }       
+               if (switch_test_flag((*frame), SFF_WAIT_KEY_FRAME)) {
+                       switch_core_session_request_video_refresh(session);
+                       switch_clear_flag((*frame), SFF_WAIT_KEY_FRAME);
 
-       if (smh->mparams->video_key_freq && (now - smh->video_last_key_time) > smh->mparams->video_key_freq) {
-               switch_core_media_gen_key_frame(smh->session);
-               smh->video_last_key_time = now;
-       }       
+                       if (!(*frame)->img) {
+                               *frame = &runtime.dummy_cng_frame;
+                               switch_cond_next();
+                               return SWITCH_STATUS_SUCCESS;
+                       }
+               }
 
-       if (!img) {
-               switch_status_t vstatus = switch_core_session_write_encoded_video_frame(session, frame, flags, stream_id);
-               switch_goto_status(vstatus, done);
+               if (decode_status == SWITCH_STATUS_MORE_DATA || !(*frame)->img) {
+                       goto top;
+               }               
        }
 
+       if (!switch_channel_test_flag(session->channel, CF_VIDEO_READY) && *frame) {
+               if (((switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ) && (*frame)->img) || (*frame)->m) && ++smh->ready_loops > 5) {
+                       switch_channel_set_flag(session->channel, CF_VIDEO_READY);
+               }
+       }
 
-       /* When desired, scale video to match the input signal (if output is bigger) */
-       if (switch_channel_test_flag(session->channel, CF_VIDEO_READY) && smh->vid_params.width && 
-               switch_channel_test_flag(session->channel, CF_VIDEO_MIRROR_INPUT) && 
-               ((smh->vid_params.width != img->d_w) || (smh->vid_params.height != img->d_h))) {
-
-               switch_img_letterbox(img, &dup_img, smh->vid_params.width, smh->vid_params.height, "#000000f");
+  done:
 
-               img = dup_img;
-       }
-       
        if (session->bugs) {
                switch_media_bug_t *bp;
                int prune = 0;
@@ -11371,203 +13001,213 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_cor
                        if (switch_channel_test_flag(session->channel, CF_PAUSE_BUGS) && !switch_core_media_bug_test_flag(bp, SMBF_NO_PAUSE)) {
                                continue;
                        }
-               
+
                        if (!switch_channel_test_flag(session->channel, CF_ANSWERED) && switch_core_media_bug_test_flag(bp, SMBF_ANSWER_REQ)) {
                                continue;
                        }
-               
+
                        if (switch_test_flag(bp, SMBF_PRUNE)) {
                                prune++;
                                continue;
                        }
-               
-                       if (bp->ready && switch_test_flag(bp, SMBF_WRITE_VIDEO_STREAM)) {
-                               switch_image_t *dimg = NULL;
-                               
-                               switch_img_copy(img, &dimg);
-                               switch_queue_push(bp->write_video_queue, dimg);
 
-                               if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM_BLEG)) {
-                                       switch_core_media_bug_patch_spy_frame(bp, img, SWITCH_RW_WRITE);
-                                       patched = 1;
+                       if (bp->ready && switch_test_flag(bp, SMBF_READ_VIDEO_STREAM)) {
+                               if ((*frame) && (*frame)->img) {
+                                       switch_image_t *img = NULL;
+                                       switch_img_copy((*frame)->img, &img);
+                                       switch_queue_push(bp->read_video_queue, img);
+                                       if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM)) {
+                                               switch_core_media_bug_patch_spy_frame(bp, (*frame)->img, SWITCH_RW_READ);
+                                               patched = 1;
+                                       }
                                }
-
                        }
 
-                       if (bp->ready && img && 
-                               (switch_test_flag(bp, SMBF_WRITE_VIDEO_PING) || (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched))) {
-                               switch_frame_t bug_frame = { 0 };
+                       if (bp->ready && (*frame) && (*frame)->img && 
+                               (switch_test_flag(bp, SMBF_READ_VIDEO_PING) || (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched))) {
+                               
 
-                               bug_frame.img = img;
+                               if (bp->callback && switch_test_flag(bp, SMBF_READ_VIDEO_PING)) {
+                                       if (switch_test_flag(bp, SMBF_READ_VIDEO_PATCH)) {
+                                               patchers++;
+                                       }
 
-                               if (bp->callback && switch_test_flag(bp, SMBF_WRITE_VIDEO_PING)) {
-                                       bp->video_ping_frame = &bug_frame;
-                                       if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE_VIDEO_PING) == SWITCH_FALSE
+                                       bp->video_ping_frame = *frame;
+
+                                       if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ_VIDEO_PING) == SWITCH_FALSE
                                                || (bp->stop_time && bp->stop_time <= switch_epoch_time_now(NULL))) {
                                                ok = SWITCH_FALSE;
                                        }
                                        bp->video_ping_frame = NULL;
                                }
 
-                               if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM_BLEG) && !patched) {
-                                       switch_core_media_bug_patch_spy_frame(bp, img, SWITCH_RW_WRITE);
+                               if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched) {
+                                       switch_core_media_bug_patch_spy_frame(bp, (*frame)->img, SWITCH_RW_READ);
                                }
-
-                               
                        }
 
                        if (ok == SWITCH_FALSE) {
                                switch_set_flag(bp, SMBF_PRUNE);
                                prune++;
                        }
-
                }
-
+               
                switch_thread_rwlock_unlock(session->bug_rwlock);
 
                if (prune) {
                        switch_core_media_bug_prune(session);
                }
-               
        }
 
-       write_frame = *frame;
-       frame = &write_frame;
-       frame->img = img;
+       if ((*frame)->codec) {
+               if (patchers) {
+                       switch_set_flag((*frame)->codec, SWITCH_CODEC_FLAG_VIDEO_PATCHING);
+               } else {
+                       switch_clear_flag((*frame)->codec, SWITCH_CODEC_FLAG_VIDEO_PATCHING);
+               }
+       }
 
-       if (!switch_test_flag(frame, SFF_USE_VIDEO_TIMESTAMP)) {
+       if (status == SWITCH_STATUS_SUCCESS) {
+               switch_core_session_video_read_callback(session, *frame);
+       }
 
-               if (!(timer = switch_core_media_get_timer(session, SWITCH_MEDIA_TYPE_VIDEO))) {
-                       
-                       if (!smh->video_timer.timer_interface) {
-                               switch_core_timer_init(&smh->video_timer, "soft", 1, 90, switch_core_session_get_pool(session));
-                       }
-                       switch_core_timer_sync(&smh->video_timer);
-                       timer = &smh->video_timer;
-               }
+       return status;
+}
 
-               frame->timestamp = timer->samplecount;
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_set_video_read_callback(switch_core_session_t *session, 
+                                                                                                                                                       switch_core_video_thread_callback_func_t func, void *user_data)
+{
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       switch_media_handle_t *smh;
+
+       if (!(smh = session->media_handle)) {
+               return SWITCH_STATUS_FALSE;
        }
 
-       switch_clear_flag(frame, SFF_SAME_IMAGE);
-       frame->m = 0;
+       switch_mutex_lock(smh->control_mutex);
+       if (!func) {
+               session->video_read_callback = NULL;
+               session->video_read_user_data = NULL;
+       } else if (session->video_read_callback) {
+               status = SWITCH_STATUS_FALSE;
+       } else {
+               session->video_read_callback = func;
+               session->video_read_user_data = user_data;
+       }
 
-       do {
-               frame->datalen = SWITCH_DEFAULT_VIDEO_SIZE;
-               encode_status = switch_core_codec_encode_video(codec, frame);
-               
-               if (encode_status == SWITCH_STATUS_SUCCESS || encode_status == SWITCH_STATUS_MORE_DATA) {
+       switch_core_session_start_video_thread(session);
+       switch_mutex_unlock(smh->control_mutex);
 
-                       switch_assert((encode_status == SWITCH_STATUS_SUCCESS && frame->m) || !frame->m);
-                       
-                       if (frame->flags & SFF_PICTURE_RESET) {
-                               switch_core_session_video_reinit(session);
-                               frame->flags &= ~SFF_PICTURE_RESET;
-                       }
+       return status;
+}
 
-                       if (frame->datalen == 0) break;
+SWITCH_DECLARE(switch_status_t) switch_core_session_video_read_callback(switch_core_session_t *session, switch_frame_t *frame)
+{
+       switch_media_handle_t *smh;
+       switch_status_t status = SWITCH_STATUS_CONTINUE;
 
-                       switch_set_flag(frame, SFF_RAW_RTP_PARSE_FRAME);
-                       status = switch_core_session_write_encoded_video_frame(session, frame, flags, stream_id);
-               }
-               
-       } while(status == SWITCH_STATUS_SUCCESS && encode_status == SWITCH_STATUS_MORE_DATA);
+       if (!(smh = session->media_handle)) {
+               return SWITCH_STATUS_FALSE;
+       }
 
- done:
+       switch_mutex_lock(smh->control_mutex);
 
-       if (smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO]) {
-               switch_mutex_unlock(smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO]);
+       if (session->video_read_callback) {
+               status = session->video_read_callback(session, frame, session->video_read_user_data);
        }
 
-       switch_img_free(&dup_img);
-       
+       switch_mutex_unlock(smh->control_mutex);
+
        return status;
 }
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_wait_for_video_input_params(switch_core_session_t *session, uint32_t timeout_ms)
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_set_text_read_callback(switch_core_session_t *session, 
+                                                                                                                                                       switch_core_text_thread_callback_func_t func, void *user_data)
 {
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
        switch_media_handle_t *smh;
-       switch_codec_implementation_t read_impl = { 0 };
-       switch_rtp_engine_t *v_engine = NULL;
-
-       switch_assert(session != NULL);
 
        if (!(smh = session->media_handle)) {
                return SWITCH_STATUS_FALSE;
        }
-       
-       if (!switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ)) {
-               return SWITCH_STATUS_GENERR;;
+
+       switch_mutex_lock(smh->control_mutex);
+       if (!func) {
+               session->text_read_callback = NULL;
+               session->text_read_user_data = NULL;
+       } else if (session->text_read_callback) {
+               status = SWITCH_STATUS_FALSE;
+       } else {
+               session->text_read_callback = func;
+               session->text_read_user_data = user_data;
        }
 
-       v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
+       switch_core_session_start_text_thread(session);
+       switch_mutex_unlock(smh->control_mutex);
 
-       if (v_engine->smode == SWITCH_MEDIA_FLOW_SENDONLY) {
-               return SWITCH_STATUS_NOTIMPL;
-       }
+       return status;
+}
 
-       switch_core_session_get_read_impl(session, &read_impl);
-               
-       while(switch_channel_ready(session->channel) && timeout_ms > 0) {
-               switch_frame_t *read_frame;
-               switch_status_t status;
-               
-               if (video_globals.synced && 
-                       switch_channel_test_flag(session->channel, CF_VIDEO_READY) && smh->vid_params.width && smh->vid_params.height && smh->vid_params.fps) {
-                       return SWITCH_STATUS_SUCCESS;
-               }
 
-               switch_core_session_request_video_refresh(session);
-               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
 
-               if (!SWITCH_READ_ACCEPTABLE(status)) {
-                       return SWITCH_STATUS_FALSE;
-               }
+SWITCH_DECLARE(switch_status_t) switch_core_session_text_read_callback(switch_core_session_t *session, switch_frame_t *frame)
+{
+       switch_media_handle_t *smh;
+       switch_status_t status = SWITCH_STATUS_CONTINUE;
 
-               timeout_ms -= (read_impl.microseconds_per_packet / 1000);
+       if (!(smh = session->media_handle)) {
+               return SWITCH_STATUS_FALSE;
        }
 
-       return SWITCH_STATUS_TIMEOUT;
-       
+       switch_mutex_lock(smh->control_mutex);
+
+       if (session->text_read_callback) {
+               status = session->text_read_callback(session, frame, session->text_read_user_data);
+       }
+
+       switch_mutex_unlock(smh->control_mutex);
+
+       return status;
 }
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags,
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_read_text_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags,
                                                                                                                                         int stream_id)
 {
        switch_status_t status = SWITCH_STATUS_FALSE;
-       switch_io_event_hook_video_read_frame_t *ptr;
-       uint32_t loops = 0;
+       switch_io_event_hook_text_read_frame_t *ptr;
        switch_media_handle_t *smh;
-       int patchers = 0;
-               
+       switch_io_read_text_frame_t read_text_frame = NULL;
+       switch_time_t now;
+
        switch_assert(session != NULL);
 
        if (!(smh = session->media_handle)) {
                return SWITCH_STATUS_FALSE;
        }
 
- top:
-
-       loops++;
-
        if (switch_channel_down_nosig(session->channel)) {
                return SWITCH_STATUS_FALSE;
        }
 
-       if (session->endpoint_interface->io_routines->read_video_frame) {
-               if ((status = session->endpoint_interface->io_routines->read_video_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
-                       for (ptr = session->event_hooks.video_read_frame; ptr; ptr = ptr->next) {
-                               if ((status = ptr->video_read_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
-                                       break;
-                               }
-                       }
+       if (!(read_text_frame = session->endpoint_interface->io_routines->read_text_frame)) {
+               if (session->io_override) {
+                       read_text_frame = session->io_override->read_text_frame;
                }
        }
 
-       if (switch_channel_test_flag(session->channel, CF_VIDEO_PAUSE_READ)) {
-               *frame = &runtime.dummy_cng_frame;
-               switch_cond_next();
-               return SWITCH_STATUS_SUCCESS;
+       if (read_text_frame) {
+               if ((status = read_text_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
+                       for (ptr = session->event_hooks.text_read_frame; ptr; ptr = ptr->next) {
+                               if ((status = ptr->text_read_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
+                                       break;
+                               }
+                       }
+               }
        }
 
        if (status == SWITCH_STATUS_INUSE) {
@@ -11576,7 +13216,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
                return SWITCH_STATUS_SUCCESS;
        }
 
-       if (status != SWITCH_STATUS_SUCCESS) {
+       if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
                goto done;
        }
 
@@ -11584,81 +13224,47 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
                goto done;
        }
        
-       if (switch_channel_test_flag(session->channel, CF_VIDEO_DEBUG_READ)) {
-               if (switch_test_flag((*frame), SFF_CNG)) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "VIDEO: CNG\n");
-               } else {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, 
-                                                         "VIDEO: seq: %d ts: %u len: %4d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x mark: %d\n",
-                                                         (*frame)->seq, (*frame)->timestamp, (*frame)->datalen,
-                                                         *((uint8_t *)(*frame)->data), *((uint8_t *)(*frame)->data + 1),
-                                                         *((uint8_t *)(*frame)->data + 2), *((uint8_t *)(*frame)->data + 3),
-                                                         *((uint8_t *)(*frame)->data + 4), *((uint8_t *)(*frame)->data + 5),
-                                                         *((uint8_t *)(*frame)->data + 6), *((uint8_t *)(*frame)->data + 7),
-                                                         *((uint8_t *)(*frame)->data + 8), *((uint8_t *)(*frame)->data + 9),
-                                                         *((uint8_t *)(*frame)->data + 10), (*frame)->m);
-               }
-       }
-
-
-       if (switch_test_flag(*frame, SFF_CNG)) {
-               status = SWITCH_STATUS_SUCCESS;
-               goto done;
-       }
-
-       if (switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ) && (*frame)->img == NULL) {
-               switch_status_t decode_status;
+       now = switch_micro_time_now();
 
-               (*frame)->img = NULL;
-
-               decode_status = switch_core_codec_decode_video((*frame)->codec, *frame);
-               
-               if ((*frame)->img && switch_channel_test_flag(session->channel, CF_VIDEO_DEBUG_READ)) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "IMAGE %dx%d %dx%d\n", 
-                                                         (*frame)->img->w, (*frame)->img->h, (*frame)->img->d_w, (*frame)->img->d_h);
+       if (switch_test_flag((*frame), SFF_CNG)) {
+               if (smh->last_text_frame && now - smh->last_text_frame > TEXT_PERIOD_TIMEOUT * 1000) {
+                       switch_channel_set_flag(session->channel, CF_TEXT_IDLE);
+                       switch_channel_clear_flag(session->channel, CF_TEXT_ACTIVE);
+                       smh->last_text_frame = 0;
                }
+       } else {
+               unsigned char *p = (*frame)->data;
 
-               if ((*frame)->img && (*frame)->img->d_w && (*frame)->img->d_h) {
-
-                       if ((*frame)->img->d_w != smh->vid_params.width) {
-                               switch_channel_set_variable_printf(session->channel, "video_width", "%d", (*frame)->img->d_w);
-                               smh->vid_params.width = (*frame)->img->d_w;
-                       }
+               smh->last_text_frame = now;
+               switch_channel_set_flag(session->channel, CF_TEXT_ACTIVE);
+               switch_channel_clear_flag(session->channel, CF_TEXT_IDLE);
 
-                       if ((*frame)->img->d_h != smh->vid_params.height) {
-                               switch_channel_set_variable_printf(session->channel, "video_height", "%d", (*frame)->img->d_h);
-                               smh->vid_params.height = (*frame)->img->d_h;
+               while(p && *p) {
+                       if (*p == '\r' || *p == '\n') {
+                               switch_set_flag((*frame), SFF_TEXT_LINE_BREAK);
+                               break;
                        }
-               }
-
-               if (switch_test_flag((*frame), SFF_WAIT_KEY_FRAME)) {
-                       switch_core_session_request_video_refresh(session);
-                       switch_clear_flag((*frame), SFF_WAIT_KEY_FRAME);
-
-                       if (!(*frame)->img) {
-                               *frame = &runtime.dummy_cng_frame;
-                               switch_cond_next();
-                               return SWITCH_STATUS_SUCCESS;
+                       
+                       if (*p == 0xE2 && *(p+1) == 0x80 && *(p+2) == 0xA8) {
+                               switch_set_flag((*frame), SFF_TEXT_LINE_BREAK);
+                               break;
                        }
+                       
+                       p++;
                }
-
-               if (decode_status == SWITCH_STATUS_MORE_DATA || !(*frame)->img) {
-                       goto top;
-               }               
        }
-
-       if (!switch_channel_test_flag(session->channel, CF_VIDEO_READY) && *frame) {
-               if (((switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ) && (*frame)->img) || (*frame)->m) && ++smh->ready_loops > 5) {
-                       switch_channel_set_flag(session->channel, CF_VIDEO_READY);
+       
+       if ((*frame)->data && (*frame)->datalen && !((*frame)->flags & SFF_CNG)) {
+               if (!session->text_buffer) {
+                       switch_mutex_init(&session->text_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
+                       switch_buffer_create_dynamic(&session->text_buffer, 512, 1024, 0);
                }
+               switch_buffer_write(session->text_buffer, (*frame)->data, (*frame)->datalen);
        }
 
-  done:
-
        if (session->bugs) {
                switch_media_bug_t *bp;
                int prune = 0;
-               int patched = 0;
 
                switch_thread_rwlock_rdlock(session->bug_rwlock);
                for (bp = session->bugs; bp; bp = bp->next) {
@@ -11677,41 +13283,46 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
                                continue;
                        }
 
-                       if (bp->ready && switch_test_flag(bp, SMBF_READ_VIDEO_STREAM)) {
-                               if ((*frame) && (*frame)->img) {
-                                       switch_image_t *img = NULL;
-                                       switch_img_copy((*frame)->img, &img);
-                                       switch_queue_push(bp->read_video_queue, img);
-                                       if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM)) {
-                                               switch_core_media_bug_patch_spy_frame(bp, (*frame)->img, SWITCH_RW_READ);
-                                               patched = 1;
-                                       }
-                               }
-                       }
+                       if (bp->ready && switch_test_flag(bp, SMBF_READ_TEXT_STREAM)) {
+                               int bytes = 0;
 
-                       if (bp->ready && (*frame) && (*frame)->img && 
-                               (switch_test_flag(bp, SMBF_READ_VIDEO_PING) || (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched))) {
-                               
+                               if ((*frame)) {
+                                       switch_size_t inuse = 0;                                
 
-                               if (bp->callback && switch_test_flag(bp, SMBF_READ_VIDEO_PING)) {
-                                       if (switch_test_flag(bp, SMBF_READ_VIDEO_PATCH)) {
-                                               patchers++;
+                                       if ((*frame)->data && (*frame)->datalen && !((*frame)->flags & SFF_CNG)) {
+                                               switch_mutex_lock(session->text_mutex);
+                                               switch_buffer_write(bp->text_buffer, (char *)(*frame)->data, (*frame)->datalen);
+                                               switch_mutex_unlock(session->text_mutex);
                                        }
 
-                                       bp->video_ping_frame = *frame;
+                                       inuse = switch_buffer_inuse(bp->text_buffer);
 
-                                       if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ_VIDEO_PING) == SWITCH_FALSE
-                                               || (bp->stop_time && bp->stop_time <= switch_epoch_time_now(NULL))) {
-                                               ok = SWITCH_FALSE;
-                                       }
-                                       bp->video_ping_frame = NULL;
-                               }
+                                       if (zstr(bp->text_framedata) && inuse && 
+                                               (switch_channel_test_flag(session->channel, CF_TEXT_IDLE) || switch_test_flag((*frame), SFF_TEXT_LINE_BREAK))) {
 
-                               if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched) {
-                                       switch_core_media_bug_patch_spy_frame(bp, (*frame)->img, SWITCH_RW_READ);
+                                               if (inuse + 1 > bp->text_framesize) {
+                                                       void *tmp = malloc(inuse + 1024);
+                                                       memcpy(tmp, bp->text_framedata, bp->text_framesize);
+                                                       
+                                                       switch_assert(tmp);
+                                                       
+                                                       bp->text_framesize = inuse + 1024;
+                                                       
+                                                       free(bp->text_framedata);
+                                                       bp->text_framedata = tmp;
+                                                       
+                                               }
+                                       
+
+                                               bytes = switch_buffer_read(bp->text_buffer, bp->text_framedata, inuse);
+                                               *(bp->text_framedata + bytes) = '\0'; 
+
+                                               ok = bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ_TEXT);
+                                               bp->text_framedata[0] = '\0';
+                                       } else ok = SWITCH_TRUE;
                                }
                        }
-
+                       
                        if (ok == SWITCH_FALSE) {
                                switch_set_flag(bp, SMBF_PRUNE);
                                prune++;
@@ -11725,69 +13336,287 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
                }
        }
 
-       if ((*frame) && (*frame)->codec) {
-               if (patchers) {
-                       switch_set_flag((*frame)->codec, SWITCH_CODEC_FLAG_VIDEO_PATCHING);
-               } else {
-                       switch_clear_flag((*frame)->codec, SWITCH_CODEC_FLAG_VIDEO_PATCHING);
+       if (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) {
+               switch_core_session_text_read_callback(session, *frame);
+       }
+
+ done:
+
+       return status;
+}
+
+
+static void build_red_packet(switch_rtp_engine_t *t_engine)
+{
+       int pos;
+       switch_frame_t *frame = &t_engine->tf->text_write_frame;
+       switch_byte_t *buf = (switch_byte_t *) frame->data;
+       uint32_t plen = 0, loops = 0;
+       uint16_t *u16;
+
+       pos = t_engine->tf->red_pos + 1;
+       
+       if (pos == t_engine->tf->red_max) pos = 0;
+
+       for (;;) {
+               uint16_t ts = frame->timestamp - t_engine->tf->red_ts[pos];
+               uint16_t len = t_engine->tf->red_buflen[pos];
+
+               loops++;
+
+               //1
+               *buf = t_engine->t140_pt & 0x7f;
+
+               if (pos != t_engine->tf->red_pos) {
+                       *buf |= 0x80;
+
+                       buf++; //2
+                       u16 = (uint16_t *) buf;
+                       *u16 = htons(ts << 2);
+                       buf++;//3
+                       *buf += (len & 0x300) >> 8;
+                       buf++;//4
+                       *buf = len & 0xff;                      
                }
+
+               buf++;
+
+               if (pos == t_engine->tf->red_pos) break;
+               
+
+               pos++;
+
+               if (pos == t_engine->tf->red_max) pos = 0;
        }
 
-       if (status == SWITCH_STATUS_SUCCESS) {
-               switch_core_session_video_read_callback(session, *frame);
+
+       plen = ((loops - 1) * 4) + 1;
+
+
+       pos = t_engine->tf->red_pos + 1;
+       
+       if (pos == t_engine->tf->red_max) pos = 0;
+
+       for (;;) {
+               if (t_engine->tf->red_buflen[pos]) {
+                       memcpy(buf, t_engine->tf->red_buf[pos], t_engine->tf->red_buflen[pos]);
+                       plen += t_engine->tf->red_buflen[pos];
+                       buf += t_engine->tf->red_buflen[pos];
+               }
+
+               if (pos == t_engine->tf->red_pos) break;
+
+               pos++;
+
+               if (pos == t_engine->tf->red_max) pos = 0;
        }
 
-       return status;
+
+       buf = frame->data;
+       *(buf+plen) = '\0';
+
+       frame->datalen = plen;
+       frame->payload = t_engine->red_pt;
 }
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_set_video_read_callback(switch_core_session_t *session, 
-                                                                                                                                                       switch_core_video_thread_callback_func_t func, void *user_data)
+SWITCH_DECLARE(switch_status_t) switch_core_session_write_text_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags,
+                                                                                                                                         int stream_id)
 {
-       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       switch_status_t status = SWITCH_STATUS_FALSE;
        switch_media_handle_t *smh;
+       switch_io_event_hook_text_write_frame_t *ptr;
+       switch_rtp_engine_t *t_engine;
+       switch_io_write_text_frame_t write_text_frame = NULL;
+
+       switch_assert(session);
 
        if (!(smh = session->media_handle)) {
                return SWITCH_STATUS_FALSE;
        }
 
-       switch_mutex_lock(smh->control_mutex);
-       if (!func) {
-               session->video_read_callback = NULL;
-               session->video_read_user_data = NULL;
-       } else if (session->video_read_callback) {
-               status = SWITCH_STATUS_FALSE;
-       } else {
-               session->video_read_callback = func;
-               session->video_read_user_data = user_data;
+       if (switch_channel_down(session->channel)) {
+               return SWITCH_STATUS_FALSE;
+       }
+       
+       if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_TEXT) == SWITCH_MEDIA_FLOW_RECVONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_TEXT) == SWITCH_MEDIA_FLOW_INACTIVE) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG3, "Writing text to RECVONLY/INACTIVE session\n");
+               return SWITCH_STATUS_SUCCESS;
        }
 
-       switch_core_session_start_video_thread(session);
-       switch_mutex_unlock(smh->control_mutex);
+       //if (switch_channel_test_flag(session->channel, CF_TEXT_PAUSE_WRITE)) {
+       //      return SWITCH_STATUS_SUCCESS;
+       //}
+
+       if (smh->write_mutex[SWITCH_MEDIA_TYPE_TEXT] && switch_mutex_trylock(smh->write_mutex[SWITCH_MEDIA_TYPE_TEXT]) != SWITCH_STATUS_SUCCESS) {
+               /* return CNG, another thread is already writing  */
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s is already being written to for %s\n", 
+                                                 switch_channel_get_name(session->channel), type2str(SWITCH_MEDIA_TYPE_TEXT));
+               goto done;
+       }
+
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
+
+       if (switch_channel_test_cap(session->channel, CC_RTP_RTT)) {
+
+               if (frame) {
+                       char *str = (char *) frame->data;
+                       switch_buffer_write(t_engine->tf->write_buffer, str, frame->datalen);
+               }
+
+               if (!switch_buffer_inuse(t_engine->tf->write_buffer)) {
+                       t_engine->tf->write_empty++;
+                       return SWITCH_STATUS_BREAK;
+               }
+
+               frame = &t_engine->tf->text_write_frame;
+               switch_core_timer_sync(&t_engine->tf->timer);
+               frame->timestamp = t_engine->tf->timer.samplecount;
+
+               if (t_engine->red_pt) {
+                       t_engine->tf->red_ts[t_engine->tf->red_pos] = frame->timestamp;
+
+                       if (t_engine->tf->write_empty > TEXT_PERIOD_TIMEOUT / TEXT_TIMER_MS) {
+                               int pos;
+                       
+                               for(pos = 0; pos < t_engine->tf->red_max; pos++) {
+                                       t_engine->tf->red_ts[pos] = 0;
+                                       t_engine->tf->red_buf[pos][0] = '\0';
+                                       t_engine->tf->red_buflen[pos] = 0;
+                               }
+
+                               frame->m = 1;
+                               t_engine->tf->write_empty = 0;
+                       
+                       } else {
+                               frame->m = 0;
+                       }
+
+                       t_engine->tf->red_buflen[t_engine->tf->red_pos] = 
+                               switch_buffer_read(t_engine->tf->write_buffer, t_engine->tf->red_buf[t_engine->tf->red_pos], RED_PACKET_SIZE);
+
+                       *(t_engine->tf->red_buf[t_engine->tf->red_pos] + t_engine->tf->red_buflen[t_engine->tf->red_pos]) = '\0';
+
+                       build_red_packet(t_engine);
+               
+               
+               } else {
+                       frame->datalen = switch_buffer_read(t_engine->tf->write_buffer, t_engine->tf->text_write_frame.data, RED_PACKET_SIZE);
+                       frame->payload = t_engine->t140_pt;
+               }
+       }
+
+       if (!(write_text_frame = session->endpoint_interface->io_routines->write_text_frame)) {
+               if (session->io_override) {
+                       write_text_frame = session->io_override->write_text_frame;
+               }
+       }
+
+       if (write_text_frame) {
+               if ((status = write_text_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
+                       for (ptr = session->event_hooks.text_write_frame; ptr; ptr = ptr->next) {
+                               if ((status = ptr->text_write_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
+                                       break;
+                               }
+                       }
+               }
+       }
+
+
+       if (switch_channel_test_cap(session->channel, CC_RTP_RTT)) {
+               if (t_engine->red_pt) {
+                       t_engine->tf->red_pos++;
+                       if (t_engine->tf->red_pos == t_engine->tf->red_max) {
+                               t_engine->tf->red_pos = 0;
+                       }
+               }
+       }
+
+ done:
+
+       if (smh->write_mutex[SWITCH_MEDIA_TYPE_TEXT]) {
+               switch_mutex_unlock(smh->write_mutex[SWITCH_MEDIA_TYPE_TEXT]);
+       }
 
        return status;
-}
+               }
 
-SWITCH_DECLARE(switch_status_t) switch_core_session_video_read_callback(switch_core_session_t *session, switch_frame_t *frame)
+SWITCH_DECLARE(switch_status_t) switch_core_session_printf(switch_core_session_t *session, const char *fmt, ...)
 {
+       char *data = NULL;
+       int ret = 0;
+       va_list ap;
+       switch_frame_t frame = { 0 };
+       switch_rtp_engine_t *t_engine;
        switch_media_handle_t *smh;
-       switch_status_t status = SWITCH_STATUS_CONTINUE;
+       unsigned char CR[] = TEXT_UNICODE_LINEFEED;
 
        if (!(smh = session->media_handle)) {
                return SWITCH_STATUS_FALSE;
+       }               
+
+       t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
+       
+       if (!switch_rtp_ready(t_engine->rtp_session)) {
+               return SWITCH_STATUS_NOTIMPL;
        }
 
-       switch_mutex_lock(smh->control_mutex);
 
-       if (session->video_read_callback) {
-               status = session->video_read_callback(session, frame, session->video_read_user_data);
+       va_start(ap, fmt);
+       ret = switch_vasprintf(&data, fmt, ap);
+       va_end(ap);
+
+       if (ret == -1) {
+               abort();
        }
 
-       switch_mutex_unlock(smh->control_mutex);
 
-       return status;
+       frame.data = data;
+       frame.datalen = strlen(data);
+       
+       switch_core_session_write_text_frame(session, &frame, 0, 0);
+
+       frame.data = CR;
+       frame.datalen = 3;
+       
+       switch_core_session_write_text_frame(session, &frame, 0, 0);
+
+       switch_safe_free(data);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_session_print(switch_core_session_t *session, const char *data)
+{
+       switch_frame_t frame = { 0 };
+       //switch_rtp_engine_t *t_engine;
+       //switch_media_handle_t *smh;
+
+       //if (!(smh = session->media_handle)) {
+       //      return SWITCH_STATUS_FALSE;
+       //}               
+
+       //t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
+       
+       //if (!switch_rtp_ready(t_engine->rtp_session)) {
+       //      return SWITCH_STATUS_NOTIMPL;
+       //}
+
+
+       if (!switch_channel_test_flag(session->channel, CF_TEXT)) {
+               return SWITCH_STATUS_NOTIMPL;
+       }
+
+       frame.data = (char *) data;
+       frame.datalen = strlen(data);
+       
+       switch_core_session_write_text_frame(session, &frame, 0, 0);
+
+       return SWITCH_STATUS_SUCCESS;
 }
 
 
+
 /* For Emacs:
  * Local Variables:
  * mode:c
index e50eb3df5765c5f4872c3eb27936b5f6e214de20..b5f6a865496912f3f29aef0ec4db7f4bbe0c856a 100644 (file)
@@ -88,6 +88,11 @@ SWITCH_DECLARE(switch_core_session_t *) switch_core_media_bug_get_session(switch
        return bug->session;
 }
 
+SWITCH_DECLARE(const char *) switch_core_media_bug_get_text(switch_media_bug_t *bug)
+{
+       return bug->text_framedata;
+}
+
 SWITCH_DECLARE(switch_frame_t *) switch_core_media_bug_get_video_ping_frame(switch_media_bug_t *bug)
 {
        return bug->video_ping_frame;
@@ -791,6 +796,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t
                switch_queue_create(&bug->spy_video_queue[1], SWITCH_CORE_QUEUE_LEN, switch_core_session_get_pool(session));
        }
 
+       if ((switch_test_flag(bug, SMBF_READ_TEXT_STREAM))) {
+
+               switch_buffer_create_dynamic(&bug->text_buffer, 512, 1024, 0);
+               switch_zmalloc(bug->text_framedata, 1024);
+               bug->text_framesize = 1024;
+               
+       }
+
        if ((switch_test_flag(bug, SMBF_READ_VIDEO_STREAM) || switch_test_flag(bug, SMBF_WRITE_VIDEO_STREAM))) {
                switch_memory_pool_t *pool = switch_core_session_get_pool(session);
 
@@ -1150,6 +1163,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_close(switch_media_bug_t *
                        bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE);
                }
 
+               if (bp->text_buffer) {
+                       switch_buffer_destroy(&bp->text_buffer);
+                       switch_safe_free(bp->text_framedata);
+               }
+
                if (switch_test_flag(bp, SMBF_READ_VIDEO_STREAM) || switch_test_flag(bp, SMBF_WRITE_VIDEO_STREAM) || switch_test_flag(bp, SMBF_READ_VIDEO_PING) || switch_test_flag(bp, SMBF_WRITE_VIDEO_PING)) {
                        switch_channel_clear_flag_recursive(bp->session->channel, CF_VIDEO_DECODED_READ);
                }
index aa0f92f222c7fd30f77b63d21e183b5ed5bb2f53..84333086099039fe9b28c7d7f58071dd8aa4201a 100644 (file)
@@ -1469,6 +1469,10 @@ SWITCH_DECLARE(void) switch_core_session_perform_destroy(switch_core_session_t *
                                          switch_channel_get_name((*session)->channel), switch_channel_state_name(switch_channel_get_state((*session)->channel)));
 
 
+       if ((*session)->text_buffer) {
+               switch_buffer_destroy(&(*session)->text_buffer);
+       }
+
        switch_core_session_reset(*session, SWITCH_TRUE, SWITCH_TRUE);
 
        switch_core_media_bug_remove_all(*session);
@@ -1880,6 +1884,19 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_thread_launch(switch_core_se
        return status;
 }
 
+SWITCH_DECLARE(const char *) switch_core_session_get_text_buffer(switch_core_session_t *session)
+{
+       const char *buf = NULL;
+       
+       if (session->text_buffer) {
+               switch_mutex_lock(session->text_mutex);
+               buf = (const char *)switch_core_session_strdup(session, (const char *) switch_buffer_get_head_pointer(session->text_buffer));
+               switch_mutex_unlock(session->text_mutex);
+       }
+
+       return buf;
+}
+
 SWITCH_DECLARE(void) switch_core_session_launch_thread(switch_core_session_t *session, switch_thread_start_t func, void *obj)
 {
        switch_thread_t *thread;
@@ -2945,6 +2962,17 @@ SWITCH_DECLARE(void) switch_core_session_raw_read(switch_core_session_t *session
        switch_core_session_set_codec_slin(session, session->sdata);
 }
 
+SWITCH_DECLARE(switch_status_t) switch_core_session_override_io_routines(switch_core_session_t *session, switch_io_routines_t *ior)
+{
+       if (session->endpoint_interface && switch_channel_test_cap(session->channel, CC_IO_OVERRIDE)) {
+               session->io_override = ior;
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
+
 /* For Emacs:
  * Local Variables:
  * mode:c
index 4ebe0f37d52e5834841d44e33107d3e64c62ab12..138621a758d9527e13a4b29bc76114d7e261bc17 100644 (file)
@@ -219,6 +219,7 @@ static char *EVENT_NAMES[] = {
        "CALL_SETUP_RESULT",
        "CALL_DETAIL",
        "DEVICE_STATE",
+       "REAL_TIME_TEXT",
        "ALL"
 };
 
index 54ec0ff6a3d15e999d07d7989eecac1c8d90dd83..bed71c97c6e81d0a17cf7007f94ecf279cfe77f3 100644 (file)
@@ -2728,7 +2728,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_generate_xml_cdr(switch_core_session_
        char tmp[512], *f;
        int cdr_off = 0, v_off = 0, cd_off = 0;
        switch_hold_record_t *hold_record = switch_channel_get_hold_record(channel), *hr;
-       
+       const char *text_buffer = NULL;
+
        if (*xml_cdr) {
                cdr = *xml_cdr;
        } else {
@@ -2750,6 +2751,12 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_generate_xml_cdr(switch_core_session_
        x_field = switch_xml_add_child_d(x_channel_data, "direction", cd_off++);
        switch_xml_set_txt_d(x_field, switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
 
+
+       if ((text_buffer = switch_core_session_get_text_buffer(session))) {
+               x_field = switch_xml_add_child_d(x_channel_data, "textlog", cd_off++);
+               switch_xml_set_txt_d(x_field, text_buffer);
+       }
+
        x_field = switch_xml_add_child_d(x_channel_data, "state_number", cd_off++);
        switch_snprintf(tmp, sizeof(tmp), "%d", switch_channel_get_state(channel));
        switch_xml_set_txt_d(x_field, tmp);
index e52884fd99898967a688cd73fcab9a0dfc91bf34..8df35b5a0bbfbcc9ed5d059d4b6d6ce075b8282c 100644 (file)
@@ -663,6 +663,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_echo(switch_core_session_t *s
        }
 
        switch_channel_set_flag(channel, CF_VIDEO_ECHO);
+       switch_channel_set_flag(channel, CF_TEXT_ECHO);
 
        while (switch_channel_ready(channel)) {
                status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
@@ -717,6 +718,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_echo(switch_core_session_t *s
 
        switch_core_session_video_reset(session);
        switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
+       switch_channel_clear_flag(channel, CF_TEXT_ECHO);
 
        return SWITCH_STATUS_SUCCESS;
 }
@@ -1525,6 +1527,84 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s
        return SWITCH_TRUE;
 }
 
+
+static switch_bool_t text_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+
+       switch (type) {
+       case SWITCH_ABC_TYPE_READ_TEXT:
+               {
+                       const char *text = switch_core_media_bug_get_text(bug);
+                       
+
+                       if (!zstr(text)) {
+                               switch_event_t *event = NULL;
+                               switch_core_session_t *session = switch_core_media_bug_get_session(bug);
+                               //switch_channel_t *channel = switch_core_session_get_channel(session);
+
+                               if (switch_event_create(&event, SWITCH_EVENT_REAL_TIME_TEXT) == SWITCH_STATUS_SUCCESS) {
+                                       switch_event_add_body(event, text, SWITCH_VA_NONE);
+                                       
+                                       if (switch_true(switch_core_get_variable("fire_text_events"))) {
+                                               switch_event_t *clone = NULL;
+
+                                               switch_event_dup(&clone, event);
+                                               switch_event_fire(&clone);
+                                       }
+                                       
+                                       switch_core_session_queue_event(session, &event);
+                               }
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       return SWITCH_TRUE;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_ivr_capture_text(switch_core_session_t *session, switch_bool_t on)
+{
+       switch_media_bug_t *bug;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+
+       bug = (switch_media_bug_t *) switch_channel_get_private(channel, "capture_text");
+
+       if (on) {
+
+               if (bug) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "text bug already attached\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+
+
+               if (switch_core_media_bug_add(session, "capture_text", switch_core_session_get_uuid(session),
+                                                                         text_callback, NULL, 0,
+                                                                         SMBF_READ_TEXT_STREAM,
+                                                                         &bug) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot attach bug\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+
+               switch_channel_set_private(channel, "capture_text", bug);
+               return SWITCH_STATUS_SUCCESS;
+
+       } else {
+
+               if (bug) {
+                       switch_channel_set_private(channel, "capture_text", NULL);
+                       switch_core_media_bug_remove(session, &bug);
+                       return SWITCH_STATUS_SUCCESS;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "text bug not attached\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+
+       }
+}
+
+
 SWITCH_DECLARE(switch_status_t) switch_ivr_record_session_mask(switch_core_session_t *session, const char *file, switch_bool_t on)
 {
        switch_media_bug_t *bug;
index 62d5daf60017335d175c42d97935410a6e8dd11d..735e0efc91e04b5c63a2c713a34253cd1d7ad4a9 100644 (file)
@@ -39,13 +39,39 @@ static void cleanup_proxy_mode_b(switch_core_session_t *session);
 /* Bridge Related Stuff*/
 /*********************************************************************************/
 
-#ifdef SWITCH_VIDEO_IN_THREADS
 struct vid_helper {
        switch_core_session_t *session_a;
        switch_core_session_t *session_b;
        int up;
 };
 
+
+static void text_bridge_thread(switch_core_session_t *session, void *obj)
+{
+       struct vid_helper *vh = obj;
+       switch_status_t status;
+       switch_frame_t *read_frame = 0;
+       switch_channel_t *channel = switch_core_session_get_channel(vh->session_a);
+       switch_channel_t *b_channel = switch_core_session_get_channel(vh->session_b);
+
+       vh->up = 1;
+
+       while (switch_channel_up_nosig(channel) && switch_channel_up_nosig(b_channel) && vh->up == 1) {
+               status = switch_core_session_read_text_frame(vh->session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+               if (SWITCH_READ_ACCEPTABLE(status) && !switch_test_flag(read_frame, SFF_CNG)) {
+                       switch_core_session_write_text_frame(vh->session_b, read_frame, 0, 0);
+               }
+
+               switch_core_session_write_text_frame(vh->session_a, NULL, 0, 0);
+       }
+
+       vh->up = 0;
+}
+
+
+#ifdef SWITCH_VIDEO_IN_THREADS
+
 static void video_bridge_thread(switch_core_session_t *session, void *obj)
 {
        struct vid_helper *vh = obj;
@@ -223,7 +249,7 @@ static void video_bridge_thread(switch_core_session_t *session, void *obj)
 
 static void launch_video(struct vid_helper *vh)
 {
-       switch_core_media_start_video_function(vh->session_a, video_bridge_thread, vh);
+       switch_core_media_start_engine_function(vh->session_a, SWITCH_MEDIA_TYPE_VIDEO, video_bridge_thread, vh);
 }
 #endif
 
@@ -334,6 +360,8 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
        const char *exec_app = NULL;
        const char *exec_data = NULL;
        switch_codec_implementation_t read_impl = { 0 };
+       uint32_t txt_launch = 0;
+       struct vid_helper th = { 0 };
 
 #ifdef SWITCH_VIDEO_IN_THREADS
        struct vid_helper vh = { 0 };
@@ -449,6 +477,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
 
        bridge_filter_dtmf = switch_true(switch_channel_get_variable(chan_a, "bridge_filter_dtmf"));
 
+
        for (;;) {
                switch_channel_state_t b_state;
                switch_status_t status;
@@ -512,6 +541,16 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
                        }
                        continue;
                }
+
+               if (switch_channel_test_flag(chan_a, CF_TEXT) && switch_channel_test_flag(chan_b, CF_TEXT) && !txt_launch) {
+                       txt_launch++;
+                       
+                       th.session_a = session_a;
+                       th.session_b = session_b;
+                       switch_core_media_start_engine_function(th.session_a, SWITCH_MEDIA_TYPE_TEXT, text_bridge_thread, &th);
+
+               }
+
 #ifdef SWITCH_VIDEO_IN_THREADS
                if (switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO) && !vid_launch) {
                        vid_launch++;
@@ -716,6 +755,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
 
   end_of_bridge_loop:
 
+
 #ifdef SWITCH_VIDEO_IN_THREADS
        if (vh.up > 0) {
                vh.up = -1;
@@ -760,8 +800,18 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
 
   end:
 
+
+       if (switch_core_media_check_engine_function(session_a, SWITCH_MEDIA_TYPE_TEXT)) {
+               if (th.up == 1) {
+                       th.up = -1;
+               }
+
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending text thread.\n");
+               switch_core_media_end_engine_function(session_a, SWITCH_MEDIA_TYPE_TEXT);
+       }
+
 #ifdef SWITCH_VIDEO_IN_THREADS
-       if (switch_core_media_check_video_function(session_a)) {
+       if (switch_core_media_check_engine_function(session_a, SWITCH_MEDIA_TYPE_VIDEO)) {
                if (vh.up == 1) {
                        vh.up = -1;
                }
@@ -772,7 +822,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
                switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
 
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending video thread.\n");
-               switch_core_media_end_video_function(session_a);
+               switch_core_media_end_engine_function(session_a, SWITCH_MEDIA_TYPE_VIDEO);
                switch_channel_clear_flag(chan_a, CF_NOT_READY);
                switch_channel_clear_flag(chan_b, CF_NOT_READY);
        }
index b396dd4611b8c9383ac92684d7ad5d02f1446391..2d74888ce4e472cc00bfd7d59e6ba0646de1e7c4 100644 (file)
@@ -37,7 +37,7 @@
 #define PERIOD_LEN 250
 #define MAX_FRAME_PADDING 2
 #define MAX_MISSING_SEQ 20
-#define jb_debug(_jb, _level, _format, ...) if (_jb->debug_level >= _level) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(_jb->session), SWITCH_LOG_ALERT, "JB:%p:%s lv:%d ln:%.4d sz:%.3u/%.3u/%.3u/%.3u c:%.3u %.3u/%.3u/%.3u/%.3u %.2f%% ->" _format, (void *) _jb, (jb->type == SJB_AUDIO ? "aud" : "vid"), _level, __LINE__,  _jb->min_frame_len, _jb->max_frame_len, _jb->frame_len, _jb->complete_frames, _jb->period_count, _jb->consec_good_count, _jb->period_good_count, _jb->consec_miss_count, _jb->period_miss_count, _jb->period_miss_pct, __VA_ARGS__)
+#define jb_debug(_jb, _level, _format, ...) if (_jb->debug_level >= _level) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG_CLEAN(_jb->session), SWITCH_LOG_ALERT, "JB:%p:%s lv:%d ln:%.4d sz:%.3u/%.3u/%.3u/%.3u c:%.3u %.3u/%.3u/%.3u/%.3u %.2f%% ->" _format, (void *) _jb, (jb->type == SJB_TEXT ? "txt" : (jb->type == SJB_AUDIO ? "aud" : "vid")), _level, __LINE__,  _jb->min_frame_len, _jb->max_frame_len, _jb->frame_len, _jb->complete_frames, _jb->period_count, _jb->consec_good_count, _jb->period_good_count, _jb->consec_miss_count, _jb->period_miss_count, _jb->period_miss_pct, __VA_ARGS__)
 
 //const char *TOKEN_1 = "ONE";
 //const char *TOKEN_2 = "TWO";
@@ -101,6 +101,8 @@ struct switch_jb_s {
        switch_jb_type_t type;
        switch_core_session_t *session;
        switch_channel_t *channel;
+       uint32_t buffer_lag;
+       uint32_t flush;
 };
 
 
@@ -1117,7 +1119,7 @@ SWITCH_DECLARE(switch_status_t) switch_jb_put_packet(switch_jb_t *jb, switch_rtp
 
        if (!want) want = got;
 
-       if (switch_test_flag(jb, SJB_QUEUE_ONLY) || jb->type == SJB_AUDIO) {
+       if (switch_test_flag(jb, SJB_QUEUE_ONLY) || jb->type == SJB_AUDIO || jb->type == SJB_TEXT) {
                jb->next_seq = htons(got + 1);
        } else {
 
@@ -1203,12 +1205,26 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp
        switch_mutex_lock(jb->mutex);
 
        if (jb->complete_frames == 0) {
+               jb->flush = 0;
                switch_goto_status(SWITCH_STATUS_BREAK, end);
        }
 
        if (jb->complete_frames < jb->frame_len) {
-               jb_debug(jb, 2, "BUFFERING %u/%u\n", jb->complete_frames , jb->frame_len);
-               switch_goto_status(SWITCH_STATUS_MORE_DATA, end);
+               
+               if (jb->type == SJB_TEXT) {
+                       if (jb->complete_frames && !jb->buffer_lag) {
+                               jb->buffer_lag = 10;
+                       }
+
+                       if (jb->buffer_lag && --jb->buffer_lag == 0) {
+                               jb->flush = 1;
+                       }
+               }
+
+               if (!jb->flush) {
+                       jb_debug(jb, 2, "BUFFERING %u/%u\n", jb->complete_frames , jb->frame_len);
+                       switch_goto_status(SWITCH_STATUS_MORE_DATA, end);
+               }
        }
 
        jb_debug(jb, 2, "GET PACKET %u/%u n:%d\n", jb->complete_frames , jb->frame_len, jb->visible_nodes);
@@ -1254,8 +1270,8 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp
                                }
                        }
                }
-
        }
+       
 
        jb->period_miss_pct = ((double)jb->period_miss_count / jb->period_count) * 100;
 
@@ -1263,7 +1279,7 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp
                jb_debug(jb, 2, "Miss percent %02f too high, resetting buffer.\n", jb->period_miss_pct);
                switch_jb_reset(jb);
        }
-
+       
        if ((status = jb_next_packet(jb, &node)) == SWITCH_STATUS_SUCCESS) {
                jb_debug(jb, 2, "Found next frame cur ts: %u seq: %u\n", htonl(node->packet.header.ts), htons(node->packet.header.seq));
 
@@ -1272,7 +1288,7 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp
                        jb->highest_read_seq = node->packet.header.seq;
                }
                
-               if (jb->read_init && htons(node->packet.header.seq) >= htons(jb->highest_read_seq) && (ntohl(node->packet.header.ts) > ntohl(jb->highest_read_ts))) {
+               if (jb->type == SJB_TEXT || (jb->read_init && htons(node->packet.header.seq) >= htons(jb->highest_read_seq) && (ntohl(node->packet.header.ts) > ntohl(jb->highest_read_ts)))) {
                        jb->complete_frames--;
                        jb_debug(jb, 2, "READ frame ts: %u complete=%u/%u n:%u\n", ntohl(node->packet.header.ts), jb->complete_frames , jb->frame_len, jb->visible_nodes);
                        jb->highest_read_ts = node->packet.header.ts;
index 9e61003009c2cee21eb140af0b67461653563f5a..05f9b63be55104186cfa8d66c88b63b626b40ea1 100644 (file)
@@ -511,7 +511,7 @@ typedef enum {
 static void do_2833(switch_rtp_t *rtp_session);
 
 
-#define rtp_type(rtp_session) rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "video" : "audio"
+#define rtp_type(rtp_session) rtp_session->flags[SWITCH_RTP_FLAG_TEXT] ?  "text" : (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "video" : "audio")
 
 
 static void switch_rtp_change_ice_dest(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, const char *host, switch_port_t port)
@@ -1278,7 +1278,7 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d
                        msg.message_id = SWITCH_MESSAGE_INDICATE_STUN_ERROR;
                        switch_core_session_receive_message(rtp_session->session, &msg);                        
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG, 
-                                                         "STUN/ICE binding error received on %s channel\n", rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "video" : "audio");
+                                                         "STUN/ICE binding error received on %s channel\n", rtp_type(rtp_session));
                }
 
        }
@@ -3626,7 +3626,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
 
                        if (status == SWITCH_STATUS_SUCCESS) {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Activating %s Secure %s RECV\n", 
-                                                                 rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "Video" : "Audio", idx ? "RTCP" : "RTP");
+                                                                 rtp_type(rtp_session), idx ? "RTCP" : "RTP");
                                rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] = 1;
                        } else {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error allocating srtp [%d]\n", stat);
@@ -3648,7 +3648,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
 
                        if (status == SWITCH_STATUS_SUCCESS) {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Activating %s Secure %s SEND\n",
-                                                                 rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "Video" : "Audio", idx ? "RTCP" : "RTP");
+                                                                 rtp_type(rtp_session), idx ? "RTCP" : "RTP");
                                rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND] = 1;
                        } else {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error allocating SRTP [%d]\n", stat);
@@ -4057,11 +4057,11 @@ SWITCH_DECLARE(switch_timer_t *) switch_rtp_get_media_timer(switch_rtp_t *rtp_se
 
 SWITCH_DECLARE(switch_jb_t *) switch_rtp_get_jitter_buffer(switch_rtp_t *rtp_session)
 {
-       if (!switch_rtp_ready(rtp_session) || !rtp_session->jb) {
+       if (!switch_rtp_ready(rtp_session)) {
                return NULL;
        }
 
-       return rtp_session->jb;
+       return rtp_session->jb ? rtp_session->jb : rtp_session->vb;
 }
 
 SWITCH_DECLARE(switch_status_t) switch_rtp_pause_jitter_buffer(switch_rtp_t *rtp_session, switch_bool_t pause)
@@ -4123,7 +4123,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_video_buffer_size(switch_rtp_t *r
        rtp_session->last_max_vb_frames = max_frames;
        
        if (!rtp_session->vb) {
-               switch_jb_create(&rtp_session->vb, SJB_VIDEO, frames, max_frames, rtp_session->pool);
+               switch_jb_create(&rtp_session->vb, rtp_session->flags[SWITCH_RTP_FLAG_TEXT] ? SJB_TEXT : SJB_VIDEO, frames, max_frames, rtp_session->pool);
                switch_jb_set_session(rtp_session->vb, rtp_session->session);
        } else {
                switch_jb_set_frames(rtp_session->vb, frames, max_frames);
@@ -5659,7 +5659,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
                        switch_jb_destroy(&rtp_session->vb);
                }
        }
-
+       
        if (rtp_session->has_rtp && *bytes) {
                uint32_t read_ssrc = ntohl(rtp_session->last_rtp_hdr.ssrc);
 
@@ -5718,7 +5718,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
        }
 
        if (!*bytes || rtp_session->has_rtp) {
-               
+
                if (rtp_session->jb && !rtp_session->pause_jb && jb_valid(rtp_session)) {
                        switch_status_t jstatus = switch_jb_get_packet(rtp_session->jb, (switch_rtp_packet_t *) &rtp_session->recv_msg, bytes);
 
@@ -5778,9 +5778,21 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
                        default:
                                break;
                        }
-               
+
+                       if (vstatus == SWITCH_STATUS_NOTFOUND && rtp_session->flags[SWITCH_RTP_FLAG_TEXT]) {
+                               int pt = get_recv_payload(rtp_session);
+                               (*flags) |= SFF_PLC;
+                               status = SWITCH_STATUS_SUCCESS;
+                               *bytes = switch_jb_get_last_read_len(rtp_session->vb);
+                               rtp_session->last_rtp_hdr = rtp_session->recv_msg.header;
+                               if (pt > -1) {
+                                       rtp_session->last_rtp_hdr.pt = pt;
+                               }
+                       }
+                               
                        if (vstatus == SWITCH_STATUS_SUCCESS) {
                                rtp_session->last_rtp_hdr = rtp_session->recv_msg.header;
+
                                if (!xcheck_jitter) {
                                        check_jitter(rtp_session);
                                        xcheck_jitter = *bytes;
@@ -5854,6 +5866,7 @@ static void handle_nack(switch_rtp_t *rtp_session, uint32_t nack)
                                        
                                }
                                //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "RE----SEND %u\n", ntohs(send_msg->header.seq));
+
                                switch_rtp_write_raw(rtp_session, (void *) &send_msg, &bytes, SWITCH_FALSE);
                        } else {
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Cannot send NACK for seq %u\n", ntohs(seq) + i);
@@ -6281,7 +6294,9 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
                                }
                        }
 
-                       if (hot_socket && (rtp_session->hot_hits % 10) != 0) { 
+                       if (rtp_session->flags[SWITCH_RTP_FLAG_TEXT]) {
+                               ///NOOP
+                       } else if (hot_socket && (rtp_session->hot_hits % 10) != 0) { 
                                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG10, "%s timer while HOT\n", rtp_session_name(rtp_session));
                                switch_core_timer_next(&rtp_session->timer);
                        } else if (hot_socket) {
@@ -6368,10 +6383,6 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
                        poll_status = switch_poll(rtp_session->read_pollfd, 1, &fdr, pt);
 
 
-                       //if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) {
-                       //      switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "WTF Poll %d\n", poll_status);
-                       //}
-                       
                        if (!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->dtmf_data.out_digit_dur > 0) {
                                return_cng_frame();
                        }
@@ -6393,7 +6404,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
                        if (read_pretriggered) {
                                read_pretriggered = 0;
                        } else {
-
+                               
                                status = read_rtp_packet(rtp_session, &bytes, flags, poll_status, SWITCH_TRUE);
 
                                if (status == SWITCH_STATUS_GENERR) {
@@ -6665,11 +6676,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
 
                if (bytes && rtp_session->last_rtp_hdr.m && rtp_session->last_rtp_hdr.pt != rtp_session->recv_te && 
                        !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] &&
+                       !rtp_session->flags[SWITCH_RTP_FLAG_TEXT] &&
                        !(rtp_session->rtp_bugs & RTP_BUG_IGNORE_MARK_BIT)) {
                        rtp_flush_read_buffer(rtp_session, SWITCH_RTP_FLUSH_ONCE);
                }
 
-
                if (rtp_session->last_rtp_hdr.pt == rtp_session->cng_pt || rtp_session->last_rtp_hdr.pt == 13) {
                        *flags |= SFF_NOT_AUDIO;
                } else {
@@ -6752,6 +6763,19 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
                        }
                }
 
+               if (rtp_session->flags[SWITCH_RTP_FLAG_TEXT]) {
+                       if (!bytes) {
+                               if (rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER]) {
+                                       switch_core_timer_next(&rtp_session->timer);
+                               }
+                               return_cng_frame();
+                       } else {
+                               *payload_type = rtp_session->last_rtp_hdr.pt;
+                               ret = (int) bytes;
+                               goto end;
+                       }
+               }
+
                if (bytes && (rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] || rtp_session->flags[SWITCH_RTP_FLAG_UDPTL])) {
                        /* Fast PASS! */
                        *flags |= SFF_PROXY_PACKET;
@@ -7923,14 +7947,19 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra
 #endif
        }
 
-       if (switch_test_flag(frame, SFF_RTP_HEADER)) {
-               switch_size_t wrote = switch_rtp_write_manual(rtp_session, frame->data, frame->datalen,
-                                                                                                         frame->m, frame->payload, (uint32_t) (frame->timestamp), &frame->flags);
+
+       if (switch_test_flag(frame, SFF_RTP_HEADER) || rtp_session->flags[SWITCH_RTP_FLAG_TEXT]) {
+               switch_size_t wrote;
+
+               wrote = switch_rtp_write_manual(rtp_session, frame->data, frame->datalen,
+                                                                               frame->m, frame->payload, (uint32_t) (frame->timestamp), &frame->flags);
                
                rtp_session->stats.outbound.raw_bytes += wrote;
                rtp_session->stats.outbound.media_bytes += wrote;
                rtp_session->stats.outbound.media_packet_count++;
                rtp_session->stats.outbound.packet_count++;
+
+               return wrote;
        }
 
        if (frame->pmap && rtp_session->pmaps && *rtp_session->pmaps) {