From c409499cd9fc9b95c052766bfa71860eeb76d5a4 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sun, 10 Jul 2016 22:25:14 -0500 Subject: [PATCH] FS-9576 #resolve [Add Realtime Text] --- configure.ac | 4 +- html5/verto/js/src/jquery.verto.js | 35 +- html5/verto/video_demo/index.html | 8 + html5/verto/video_demo/js/verto-min.js | 8 +- html5/verto/video_demo/verto.js | 92 +- libs/esl/src/esl_event.c | 1 + libs/esl/src/include/esl_event.h | 1 + libs/sofia-sip/libsofia-sip-ua/sdp/sdp.bnf | 2 +- .../sofia-sip/libsofia-sip-ua/sdp/sdp_parse.c | 4 +- .../sofia-sip/libsofia-sip-ua/sdp/sdp_print.c | 1 + .../libsofia-sip-ua/sdp/sofia-sip/sdp.h | 3 +- src/include/private/switch_core_pvt.h | 11 + src/include/switch_buffer.h | 2 + src/include/switch_channel.h | 5 + src/include/switch_core.h | 9 + src/include/switch_core_event_hook.h | 28 +- src/include/switch_core_media.h | 26 +- src/include/switch_ivr.h | 1 + src/include/switch_jitterbuffer.h | 3 +- src/include/switch_module_interfaces.h | 8 + src/include/switch_types.h | 36 +- src/include/switch_utils.h | 27 + src/mod/applications/mod_av/avformat.c | 2 +- .../applications/mod_commands/mod_commands.c | 59 + .../mod_conference/conference_member.c | 7 + .../mod_conference/mod_conference.c | 98 + .../mod_conference/mod_conference.h | 8 + .../applications/mod_dptools/mod_dptools.c | 6 + src/mod/applications/mod_fsv/mod_fsv.c | 8 +- src/mod/endpoints/mod_rtc/mod_rtc.c | 3 + src/mod/endpoints/mod_sofia/mod_sofia.c | 14 + src/mod/endpoints/mod_sofia/rtp.c | 2 + src/mod/endpoints/mod_sofia/sofia_glue.c | 1 + src/mod/endpoints/mod_verto/mcast/mcast.c | 2 +- src/mod/endpoints/mod_verto/mod_verto.c | 168 +- src/mod/endpoints/mod_verto/mod_verto.h | 12 + src/switch_buffer.c | 6 + src/switch_core_media.c | 2003 ++++++++++++++++- src/switch_core_media_bug.c | 18 + src/switch_core_session.c | 28 + src/switch_event.c | 1 + src/switch_ivr.c | 9 +- src/switch_ivr_async.c | 80 + src/switch_ivr_bridge.c | 58 +- src/switch_jitterbuffer.c | 30 +- src/switch_rtp.c | 69 +- 46 files changed, 2834 insertions(+), 173 deletions(-) diff --git a/configure.ac b/configure.ac index 7932b835ca..bc3cafa0bd 100644 --- a/configure.ac +++ b/configure.ac @@ -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, []) diff --git a/html5/verto/js/src/jquery.verto.js b/html5/verto/js/src/jquery.verto.js index d0c3563c12..0e0e38bf38 100644 --- a/html5/verto/js/src/jquery.verto.js +++ b/html5/verto/js/src/jquery.verto.js @@ -2062,12 +2062,18 @@ 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, @@ -2371,6 +2377,26 @@ } }; + $.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) { @@ -2517,7 +2543,8 @@ $.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) { diff --git a/html5/verto/video_demo/index.html b/html5/verto/video_demo/index.html index 229275e9cf..edd80e27f2 100644 --- a/html5/verto/video_demo/index.html +++ b/html5/verto/video_demo/index.html @@ -244,6 +244,14 @@ div#preload { display: none; } +

+ + +
+
+
+ +
diff --git a/html5/verto/video_demo/js/verto-min.js b/html5/verto/video_demo/js/verto-min.js index dbe0017137..95d5cf1111 100644 --- a/html5/verto/video_demo/js/verto-min.js +++ b/html5/verto/video_demo/js/verto-min.js @@ -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?" <":" <")+dialog.params.remote_caller_id_number+(enc?">":">");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?" <":" <")+dialog.params.remote_caller_id_number+(enc?">":">");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]();} diff --git a/html5/verto/video_demo/verto.js b/html5/verto/video_demo/verto.js index e0ee4aec19..3b06234439 100644 --- a/html5/verto/video_demo/verto.js +++ b/html5/verto/video_demo/verto.js @@ -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, "$1
<\/a>"); - } else { + } else { body = body.replace(/(http[s]{0,1}:\/\/\S+)/g, "
$1<\/a>"); - } + } - if (body.slice(-1) !== "\n") { + if (body.slice(-1) !== "\n") { body += "\n"; - } - body = body.replace(/(?:\r\n|\r|\n)/g, '
'); - - var from = data.from_msg_name || data.from; + } + body = body.replace(/(?:\r\n|\r|\n)/g, '
'); + + var from = data.from_msg_name || data.from; - $("#chatwin").append("" + from + ":
" + body); - $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast"); + $("#chatwin").append("" + from + ":
" + body); + $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast"); */ - var from = data.from_msg_name || data.from; - - $('#chatwin') - .append($('').text(from + ':')) - .append($('
')) - .append(messageTextToJQ(body)) - .append($('
')); - $('#chatwin').animate({"scrollTop": $('#chatwin')[0].scrollHeight}, "fast"); + var from = data.from_msg_name || data.from; + $('#chatwin') + .append($('').text(from + ':')) + .append($('
')) + .append(messageTextToJQ(body)) + .append($('
')); + $('#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" ); diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index 24f7163976..d097f74569 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -147,6 +147,7 @@ static const char *EVENT_NAMES[] = { "CALL_SETUP_RESULT", "CALL_DETAIL", "DEVICE_STATE", + "REAL_TIME_TEXT", "ALL" }; diff --git a/libs/esl/src/include/esl_event.h b/libs/esl/src/include/esl_event.h index 47c38b6cf7..1380f68092 100644 --- a/libs/esl/src/include/esl_event.h +++ b/libs/esl/src/include/esl_event.h @@ -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; diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.bnf b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.bnf index 8a2b08ab7c..42281c1ec2 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.bnf +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.bnf @@ -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 diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_parse.c b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_parse.c index 887e4e818a..3b1190ee5f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_parse.c +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_parse.c @@ -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; } diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c index 0f8e390ebf..767556d7ac 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c @@ -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; } diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sofia-sip/sdp.h b/libs/sofia-sip/libsofia-sip-ua/sdp/sofia-sip/sdp.h index 6034a7a37a..bf0741e044 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sofia-sip/sdp.h +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sofia-sip/sdp.h @@ -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. */ diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h index 4204fcd0d5..bf4dd351e1 100644 --- a/src/include/private/switch_core_pvt.h +++ b/src/include/private/switch_core_pvt.h @@ -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; }; diff --git a/src/include/switch_buffer.h b/src/include/switch_buffer.h index a3c5032343..e5af0baf0a 100644 --- a/src/include/switch_buffer.h +++ b/src/include/switch_buffer.h @@ -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 diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index d3267ae122..b1f4b2d219 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -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 diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 86763f9713..3d8b2e8988 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -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: diff --git a/src/include/switch_core_event_hook.h b/src/include/switch_core_event_hook.h index 26dfb0c87d..f991955bd4 100644 --- a/src/include/switch_core_event_hook.h +++ b/src/include/switch_core_event_hook.h @@ -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); diff --git a/src/include/switch_core_media.h b/src/include/switch_core_media.h index 096bc815fc..5bca68d6c0 100644 --- a/src/include/switch_core_media.h +++ b/src/include/switch_core_media.h @@ -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: diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 06fa8c137a..712179d783 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -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); /** @} */ diff --git a/src/include/switch_jitterbuffer.h b/src/include/switch_jitterbuffer.h index de423fd7fd..e55bcbec28 100644 --- a/src/include/switch_jitterbuffer.h +++ b/src/include/switch_jitterbuffer.h @@ -39,7 +39,8 @@ typedef enum { typedef enum { SJB_VIDEO = 0, - SJB_AUDIO + SJB_AUDIO, + SJB_TEXT } switch_jb_type_t; diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 1802f872e3..0997bbeb05 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -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 */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index cc8539085e..7b81a07e8f 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -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: diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 691395f362..1c423b202b 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -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) { diff --git a/src/mod/applications/mod_av/avformat.c b/src/mod/applications/mod_av/avformat.c index 883a5be51f..04d35096e1 100644 --- a/src/mod/applications/mod_av/avformat.c +++ b/src/mod/applications/mod_av/avformat.c @@ -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); diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 39bffd58ab..f44b4ab55b 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -3066,6 +3066,61 @@ SWITCH_STANDARD_API(uuid_chat) return SWITCH_STATUS_SUCCESS; } +#define UUID_CAPTURE_TEXT_SYNTAX " " +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 " " +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 " [on | off ] [ mask_digits | mask_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"); diff --git a/src/mod/applications/mod_conference/conference_member.c b/src/mod/applications/mod_conference/conference_member.c index b90ab477c5..064d12edac 100644 --- a/src/mod/applications/mod_conference/conference_member.c +++ b/src/mod/applications/mod_conference/conference_member.c @@ -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--; } diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index dbcb80299a..75b2e3b27e 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -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); diff --git a/src/mod/applications/mod_conference/mod_conference.h b/src/mod/applications/mod_conference/mod_conference.h index 02503f6110..a4aca869fc 100644 --- a/src/mod/applications/mod_conference/mod_conference.h +++ b/src/mod/applications/mod_conference/mod_conference.h @@ -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); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 0545ac07c4..d3f26e33d2 100644 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -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, ""); SWITCH_ADD_API(api_interface, "page", "Send a file as a page", page_api_function, "(var1=val1,var2=val2)[:_:]"); SWITCH_ADD_API(api_interface, "strmicroepoch", "Convert a date string into micoepoch time", strmicroepoch_api_function, ""); @@ -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, "[^^] ", 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, "[]", SAF_NONE); SWITCH_ADD_APP(app_interface, "break", "Break", "Set the break flag.", break_function, "", SAF_SUPPORT_NOMEDIA); diff --git a/src/mod/applications/mod_fsv/mod_fsv.c b/src/mod/applications/mod_fsv/mod_fsv.c index e2c6f5af4d..e99022cee8 100644 --- a/src/mod/applications/mod_fsv/mod_fsv.c +++ b/src/mod/applications/mod_fsv/mod_fsv.c @@ -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); } diff --git a/src/mod/endpoints/mod_rtc/mod_rtc.c b/src/mod/endpoints/mod_rtc/mod_rtc.c index 4f964827c8..2ef2bac87f 100644 --- a/src/mod/endpoints/mod_rtc/mod_rtc.c +++ b/src/mod/endpoints/mod_rtc/mod_rtc.c @@ -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); diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 36ef8b06b9..017150d6ed 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -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 }; diff --git a/src/mod/endpoints/mod_sofia/rtp.c b/src/mod/endpoints/mod_sofia/rtp.c index ce42dc1875..eafde46716 100644 --- a/src/mod/endpoints/mod_sofia/rtp.c +++ b/src/mod/endpoints/mod_sofia/rtp.c @@ -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 diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 10fe226649..9c493bf9a9 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -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); diff --git a/src/mod/endpoints/mod_verto/mcast/mcast.c b/src/mod/endpoints/mod_verto/mcast/mcast.c index f15ff6efc5..984d1d4f22 100644 --- a/src/mod/endpoints/mod_verto/mcast/mcast.c +++ b/src/mod/endpoints/mod_verto/mcast/mcast.c @@ -53,7 +53,7 @@ #include #define closesocket(x) close(x) #endif -#include +#include #include "mcast.h" diff --git a/src/mod/endpoints/mod_verto/mod_verto.c b/src/mod/endpoints/mod_verto/mod_verto.c index b839660082..15a5d6825d 100644 --- a/src/mod/endpoints/mod_verto/mod_verto.c +++ b/src/mod/endpoints/mod_verto/mod_verto.c @@ -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, ¶ms); + 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); diff --git a/src/mod/endpoints/mod_verto/mod_verto.h b/src/mod/endpoints/mod_verto/mod_verto.h index c39c74d681..404529e0bf 100644 --- a/src/mod/endpoints/mod_verto/mod_verto.h +++ b/src/mod/endpoints/mod_verto/mod_verto.h @@ -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 { diff --git a/src/switch_buffer.c b/src/switch_buffer.c index b2fd1482fe..01745cc6b1 100644 --- a/src/switch_buffer.c +++ b/src/switch_buffer.c @@ -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)) { diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 121c12bef0..06b4d312f8 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -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 + +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 { + 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,6 +5869,138 @@ SWITCH_DECLARE(void) switch_core_autobind_cpu(void) } + + +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; + 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; + 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; + } + + cr_frame.data = CR; + cr_frame.datalen = 3; + + t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT]; + t_engine->thread_id = switch_thread_self(); + + 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; @@ -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,9 +7506,313 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi + text: + + + + 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 (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(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, 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)); + t_engine->cur_payload_map->negotiated = 1; + goto text_up; + } else { + 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, 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(t_engine->rtp_session)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + "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(t_engine->rtp_session, t_engine->cur_payload_map->agreed_pt); + } + } + + 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 (t_engine->rtp_session && switch_channel_test_flag(session->channel, CF_REINVITE)) { + const char *rport = NULL; + 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_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_AVPF) && + !((val = switch_channel_get_variable(session->channel, "disable_rtp_auto_adjust")) && switch_true(val))) { + /* Reactivate the NAT buster flag. */ + switch_rtp_set_flag(t_engine->rtp_session, SWITCH_RTP_FLAG_AUTOADJ); + } + + } + goto text_up; + } + + if (switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) { + switch_core_media_proxy_remote_addr(session, NULL); + + 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; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + "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, + 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(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; + + if ((var = switch_channel_get_variable(session->channel, "rtp_timer_name"))) { + timer_name = (char *) var; + } + } + + /******************************************************************************************/ + + if (t_engine->rtp_session) { + goto text_up; + } + + + if (!t_engine->local_sdp_port) { + switch_core_media_choose_port(session, SWITCH_MEDIA_TYPE_TEXT, 1); + } + + memset(flags, 0, sizeof(flags)); + flags[SWITCH_RTP_FLAG_DATAWAIT]++; + flags[SWITCH_RTP_FLAG_RAW_WRITE]++; + + 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 (switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) { + flags[SWITCH_RTP_FLAG_PROXY_MEDIA]++; + } + //TEXT switch_core_media_set_text_codec(session, 0); + + flags[SWITCH_RTP_FLAG_USE_TIMER] = 1; + flags[SWITCH_RTP_FLAG_NOBLOCK] = 0; + flags[SWITCH_RTP_FLAG_TEXT]++; + //flags[SWITCH_RTP_FLAG_VIDEO]++; + + 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, "%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, + 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(t_engine->rtp_session)) { + const char *ssrc; + + + if (!t_engine->tf) { + switch_rtp_text_factory_create(&t_engine->tf, switch_core_session_get_pool(session)); + } + + 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); + } + + if (t_engine->remote_ssrc) { + switch_rtp_set_remote_ssrc(t_engine->rtp_session, t_engine->remote_ssrc); + } + + if (t_engine->ice_in.cands[t_engine->ice_in.chosen[0]][0].ready) { + + gen_ice(session, SWITCH_MEDIA_TYPE_TEXT, NULL, 0); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating Text 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_RTP, +#ifdef GOOGLE_ICE + ICE_GOOGLE_JINGLE, + NULL +#else + switch_ice_direction(session) == + SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED), + &t_engine->ice_in +#endif + ); + + + } + + 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 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) { + 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 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 (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 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, + NULL +#else + switch_ice_direction(session) == + SWITCH_CALL_DIRECTION_OUTBOUND ? ICE_VANILLA : (ICE_VANILLA | ICE_CONTROLLED), + &t_engine->ice_in +#endif + ); + + + + } + + } + } + + if (!zstr(t_engine->local_dtls_fingerprint.str) && switch_rtp_has_dtls() && dtls_ok(smh->session)) { + dtls_type_t xtype, + dtype = t_engine->dtls_controller ? DTLS_TYPE_CLIENT : DTLS_TYPE_SERVER; + xtype = DTLS_TYPE_RTP; + if (t_engine->rtcp_mux > 0 && smh->mparams->rtcp_text_interval_msec) xtype |= DTLS_TYPE_RTCP; + + switch_rtp_add_dtls(t_engine->rtp_session, &t_engine->local_dtls_fingerprint, &t_engine->remote_dtls_fingerprint, dtype | xtype); + + if (t_engine->rtcp_mux < 1 && smh->mparams->rtcp_text_interval_msec) { + xtype = DTLS_TYPE_RTCP; + 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_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(t_engine->rtp_session, t_engine->rtp_bugs | smh->mparams->manual_text_rtp_bugs); + + //XX + + + 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_TEXT); + + + if (switch_channel_test_flag(session->channel, CF_ZRTP_PASSTHRU)) { + 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, "TEXT RTP REPORTS ERROR: [%s]\n", switch_str_nil(err)); + switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + goto end; + } + } - + text_up: video: if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { @@ -7076,10 +8060,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi 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) { + 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 { @@ -7126,9 +8108,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi 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; - //} + 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); @@ -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; @@ -9222,8 +10475,72 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session) } 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_TEXT, + "PROXY-VID", + NULL, + NULL, + SDP_TYPE_RESPONSE, + 0, + 90000, + 90000, + 1, + SWITCH_TRUE); + t_engine->cur_payload_map = pmap; + + switch_snprintf(tport_buf, sizeof(tport_buf), "%u", t_engine->adv_sdp_port); + + 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); + } + + t_engine->codec_negotiated = 1; + //TEXT switch_core_media_set_text_codec(session, SWITCH_FALSE); + } + + strncpy(q, p, 8); + p += 8; + + if (p >= pe) { + bad = 8; + goto end; + } + + q += 8; + + if (q >= qe) { + bad = 9; + goto end; + } + + strncpy(q, tport_buf, strlen(tport_buf)); + q += strlen(tport_buf); + + if (q >= qe) { + bad = 10; + goto end; + } + + while (p && *p && (*p >= '0' && *p <= '9')) { + + if (p >= pe) { + bad = 11; + goto end; + } + + p++; + } + + 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); @@ -11725,7 +13059,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core } } - if ((*frame) && (*frame)->codec) { + if ((*frame)->codec) { if (patchers) { switch_set_flag((*frame)->codec, SWITCH_CODEC_FLAG_VIDEO_PATCHING); } else { @@ -11740,6 +13074,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core return status; } + 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) { @@ -11788,6 +13123,500 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_video_read_callback(switch_c } + +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; + + if (!(smh = session->media_handle)) { + return SWITCH_STATUS_FALSE; + } + + 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; + } + + switch_core_session_start_text_thread(session); + switch_mutex_unlock(smh->control_mutex); + + return status; +} + + + +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; + + if (!(smh = session->media_handle)) { + return SWITCH_STATUS_FALSE; + } + + 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_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_text_read_frame_t *ptr; + switch_media_handle_t *smh; + 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; + } + + if (switch_channel_down_nosig(session->channel)) { + return SWITCH_STATUS_FALSE; + } + + 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 (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) { + *frame = &runtime.dummy_cng_frame; + switch_cond_next(); + return SWITCH_STATUS_SUCCESS; + } + + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + goto done; + } + + if (!(*frame)) { + goto done; + } + + now = switch_micro_time_now(); + + 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; + + smh->last_text_frame = now; + switch_channel_set_flag(session->channel, CF_TEXT_ACTIVE); + switch_channel_clear_flag(session->channel, CF_TEXT_IDLE); + + while(p && *p) { + if (*p == '\r' || *p == '\n') { + switch_set_flag((*frame), SFF_TEXT_LINE_BREAK); + break; + } + + if (*p == 0xE2 && *(p+1) == 0x80 && *(p+2) == 0xA8) { + switch_set_flag((*frame), SFF_TEXT_LINE_BREAK); + break; + } + + p++; + } + } + + 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); + } + + if (session->bugs) { + switch_media_bug_t *bp; + int prune = 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_READ_TEXT_STREAM)) { + int bytes = 0; + + if ((*frame)) { + switch_size_t inuse = 0; + + 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); + } + + inuse = switch_buffer_inuse(bp->text_buffer); + + if (zstr(bp->text_framedata) && inuse && + (switch_channel_test_flag(session->channel, CF_TEXT_IDLE) || switch_test_flag((*frame), SFF_TEXT_LINE_BREAK))) { + + 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++; + } + } + + switch_thread_rwlock_unlock(session->bug_rwlock); + + if (prune) { + switch_core_media_bug_prune(session); + } + } + + 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; + } + + + 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; + } + + + buf = frame->data; + *(buf+plen) = '\0'; + + frame->datalen = plen; + frame->payload = t_engine->red_pt; +} + +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_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; + } + + 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; + } + + //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_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; + 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; + } + + + va_start(ap, fmt); + ret = switch_vasprintf(&data, fmt, ap); + va_end(ap); + + if (ret == -1) { + abort(); + } + + + 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 diff --git a/src/switch_core_media_bug.c b/src/switch_core_media_bug.c index e50eb3df57..b5f6a86549 100644 --- a/src/switch_core_media_bug.c +++ b/src/switch_core_media_bug.c @@ -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); } diff --git a/src/switch_core_session.c b/src/switch_core_session.c index aa0f92f222..8433308609 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -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 diff --git a/src/switch_event.c b/src/switch_event.c index 4ebe0f37d5..138621a758 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -219,6 +219,7 @@ static char *EVENT_NAMES[] = { "CALL_SETUP_RESULT", "CALL_DETAIL", "DEVICE_STATE", + "REAL_TIME_TEXT", "ALL" }; diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 54ec0ff6a3..bed71c97c6 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -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); diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index e52884fd99..8df35b5a0b 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -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; diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index 62d5daf600..735e0efc91 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -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); } diff --git a/src/switch_jitterbuffer.c b/src/switch_jitterbuffer.c index b396dd4611..2d74888ce4 100644 --- a/src/switch_jitterbuffer.c +++ b/src/switch_jitterbuffer.c @@ -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; diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 9e61003009..05f9b63be5 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -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) {