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) {