1
0
mirror of https://github.com/signalwire/freeswitch.git synced 2025-04-15 00:22:35 +00:00

Merge branch 'master' into v1.6

This commit is contained in:
Ken Rice 2015-11-19 13:55:18 -06:00
commit 684235116d
102 changed files with 3376 additions and 1133 deletions
build
conf
testing
autoload_configs
dialplan/default
vars.xml
vanilla
autoload_configs
directory
configure.ac
debian
htdocs/portal/assets/bootstrap/css
html5/verto
libs
esl
freetdm
conf
mod_freetdm
src
ftdm_io.c
ftmod
ftmod_gsm
ftmod_wanpipe
include/private
libks/src
src

@ -1,5 +1,6 @@
MK=`echo $(MAKE) | $(AWK) '{printf "%5s\n", $$0}' `
all:
@echo " +---------- FreeSWITCH Build Complete ----------+"
@echo " + FreeSWITCH has been successfully built. +"
@ -54,6 +55,13 @@ install:
@echo " + Install/Re-install default config: +"
@echo " + ---------------------------------- +"
@echo " + $(MK) samples +"
if SYSTEMD_INIT
@echo " + +"
@echo " + Install systemd startup scripts: +"
@echo " + -------------------------------- +"
@echo " + +"
@echo " + build/startup/install_systemd.sh +"
endif
@echo " + +"
@echo " + +"
@echo " + Additional resources: +"

@ -1 +0,0 @@
d /run/freeswitch 0750 freeswitch daemon -

@ -1,17 +0,0 @@
[Unit]
Description=FreeSWITCH
After=syslog.target network.target
After=postgresql.service postgresql-9.3.service postgresql-9.4.service mysqld.service httpd.service
[Service]
User=freeswitch
EnvironmentFile=-/etc/sysconfig/freeswitch
# RuntimeDirectory is not yet supported in CentOS 7. A workaround is to use /etc/tmpfiles.d/freeswitch.conf
#RuntimeDirectory=/run/freeswitch
#RuntimeDirectoryMode=0750
WorkingDirectory=/run/freeswitch
ExecStart=/usr/bin/freeswitch -nc -nf $FREESWITCH_PARAMS
ExecReload=/usr/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

@ -23,6 +23,7 @@ applications/mod_fifo
applications/mod_fsk
applications/mod_fsv
applications/mod_hash
applications/mod_hiredis
applications/mod_httapi
applications/mod_http_cache
#applications/mod_ladspa
@ -101,7 +102,6 @@ event_handlers/mod_erlang_event
event_handlers/mod_event_multicast
event_handlers/mod_event_socket
event_handlers/mod_format_cdr
event_handlers/mod_hiredis
event_handlers/mod_json_cdr
#event_handlers/mod_radius_cdr
event_handlers/mod_odbc_cdr

@ -0,0 +1,2 @@
# /etc/default/freeswitch
DAEMON_OPTS="-nonat"

@ -0,0 +1,32 @@
[Unit]
Description=freeswitch
After=syslog.target network.target local-fs.target
[Service]
; service
Type=forking
PIDFile=@runtimedir@/freeswitch.pid
PermissionsStartOnly=true
Environment="DAEMON_OPTS=-nonat"
EnvironmentFile=-/etc/sysconfig/freeswitch
EnvironmentFile=-/etc/default/freeswitch
ExecStart=@bindir_expanded@/freeswitch -u freeswitch -g freeswitch -ncwait -rp ${DAEMON_OPTS}
TimeoutSec=20s
Restart=on-failure
; exec
User=root
Group=daemon
LimitCORE=infinity
LimitNOFILE=100000
LimitNPROC=60000
;LimitSTACK=240
LimitRTPRIO=infinity
LimitRTTIME=7000000
IOSchedulingClass=realtime
IOSchedulingPriority=2
CPUSchedulingPolicy=rr
CPUSchedulingPriority=89
UMask=0007
[Install]
WantedBy=multi-user.target

@ -0,0 +1 @@
d /var/run/freeswitch 0755 freeswitch freeswitch - -

@ -0,0 +1,59 @@
#!/bin/bash
# Niek Vlessert
USER=`whoami`
DISTRO=$(source /etc/os-release && echo $PRETTY_NAME)
if [ $USER != "root" ] ; then
SUDO=`which sudo | awk -F"/" '{print $NF}'`
if [ -z $SUDO ] ; then
echo "No root and no sudo... please run this as root or install sudo and make sure your user has permissions to use it."
exit
else
read -p "The currently active user is not root but sudo is available... do you want to install using sudo? (y/n) " -n 1 -r
if ! [[ $REPLY =~ ^[yY]$ ]]
then
echo
exit
fi
fi
fi
echo
echo "This will do several things on your $DISTRO installation:"
echo "- Create user freeswitch and add it to group freeswitch"
FSPATH=@prefix@
if [[ $FSPATH == *"freeswitch"* ]]
then
echo "- Set permissions on @prefix@ and files in @bindir_expanded@"
fi
echo "- Install systemd unit file and other required files"
echo
read -p "Do you want to continue? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[yY]$ ]]
then
echo
echo "Installing..."
$SUDO useradd -d @confdir@ -r -U -s /bin/false -c "FreeSWITCH open source softswitch" freeswitch
if [[ $FSPATH == *"freeswitch"* ]]
then
$SUDO chown -R freeswitch:freeswitch @prefix@
$SUDO chmod -R ug=rwX,o= @prefix@
$SUDO chmod -R u=rwx,g=rx @bindir_expanded@/*
fi
$SUDO cp build/startup/freeswitch.service /etc/systemd/system/
$SUDO cp build/startup/freeswitch.tmpfile /etc/tmpfiles.d/freeswitch.conf
if [ -d /etc/sysconfig ]; then
$SUDO cp build/startup/freeswitch.default /etc/sysconfig/freeswitch
else
$SUDO cp build/startup/freeswitch.default /etc/default/freeswitch
fi
$SUDO systemd-tmpfiles --clean --create
$SUDO systemctl daemon-reload
echo
if [ -f @confdir@/vars.xml ] ; then
echo "You may now start Freeswitch using 'systemctl start freeswitch'"
else
echo "Make sure your config files are in place in @confdir@, if they are you can start Freeswitch using 'systemctl start freeswitch'"
fi
echo "Then start fs_cli by running @bindir_expanded@/fs_cli"
fi

@ -281,6 +281,41 @@
<param name="video-fps" value="15"/>
</profile>
<profile name="video-mcu-stereo-personal">
<param name="domain" value="$${domain}"/>
<param name="rate" value="48000"/>
<param name="channels" value="2"/>
<param name="interval" value="20"/>
<param name="energy-level" value="200"/>
<!-- <param name="tts-engine" value="flite"/> -->
<!-- <param name="tts-voice" value="kal16"/> -->
<param name="caller-controls" value="defaultvideo"/>
<param name="muted-sound" value="conference/conf-muted.wav"/>
<param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
<param name="alone-sound" value="conference/conf-alone.wav"/>
<param name="moh-sound" value="local_stream://video"/>
<param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>
<param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>
<param name="kicked-sound" value="conference/conf-kicked.wav"/>
<param name="locked-sound" value="conference/conf-locked.wav"/>
<param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
<param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
<param name="pin-sound" value="conference/conf-pin.wav"/>
<param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>
<param name="caller-id-name" value="$${outbound_caller_name}"/>
<param name="caller-id-number" value="$${outbound_caller_id}"/>
<param name="comfort-noise" value="false"/>
<param name="conference-flags" value="video-muxing-personal-canvas|livearray-json-status|json-events|video-floor-only|livearray-sync|minimize-video-encoding|video-required-for-canvas"/>
<param name="video-mode" value="mux"/>
<param name="video-layout-name" value="3x3"/>
<param name="video-layout-name" value="group:grid"/>
<param name="video-canvas-size" value="1920x1080"/>
<param name="video-canvas-bgcolor" value="#333333"/>
<param name="video-layout-bgcolor" value="#000000"/>
<param name="video-codec-bandwidth" value="3mb"/>
<param name="video-fps" value="15"/>
</profile>
<profile name="test_res_id">
<param name="domain" value="$${domain}"/>
<param name="rate" value="48000"/>

@ -7,6 +7,15 @@
<action application="conference" data="6070@video-mcu-stereo"/>
</condition>
</extension>
<extension name="conf">
<condition field="destination_number" expression="^6070-personal$">
<!--<action application="answer" data="is_conference"/> -->
<action application="push" data="conference_member_flags=moderator"/>
<action application="conference" data="6070@video-mcu-stereo-personal"/>
</condition>
</extension>
<extension name="cdquality_stereo_conferences">
<condition field="destination_number" expression="^(6070).*?-screen$">
<action application="answer"/>
@ -16,6 +25,13 @@
</condition>
</extension>
<extension name="conference-canvases" continue="true">
<condition field="destination_number" expression="(.*?)-canvas-(\d+)">
<action application="push" data="conference_member_flags=second-screen"/>
<action application="set" data="video_initial_watching_canvas=$2"/>
<action application="transfer" data="$1"/>
</condition>
</extension>
<extension name="conf">
<condition field="destination_number" expression="^6070-moderator$">

@ -78,5 +78,6 @@
<!-- Stock Video Avatars -->
<X-PRE-PROCESS cmd="set" data="video_mute_png=$${images_dir}/default-mute.png"/>
<X-PRE-PROCESS cmd="set" data="video_no_avatar_png=$${images_dir}/default-avatar.png"/>
<X-PRE-PROCESS cmd="set" data="api_on_startup=fsctl recover"/>
</include>

@ -152,6 +152,12 @@
<param name="rtp-enable-zrtp" value="false"/>
<!--
Store encryption keys for secure media in channel variables and call CDRs. Default: false.
WARNING: If true, anyone with CDR access can decrypt secure media!
-->
<!-- <param name="rtp-retain-crypto-keys" value="true"/> -->
<!-- <param name="core-db-dsn" value="pgsql://hostaddr=127.0.0.1 dbname=freeswitch user=freeswitch password='' options='-c client_min_messages=NOTICE'" /> -->
<!-- <param name="core-db-dsn" value="dsn:username:password" /> -->
<!--

@ -21,7 +21,7 @@
<!--the domain or ip (the right hand side of the @ in the addr-->
<domain name="$${domain}">
<params>
<param name="dial-string" value="{^^:sip_invite_domain=${dialed_domain}:presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(*/${dialed_user}@${dialed_domain})}"/>
<param name="dial-string" value="{^^:sip_invite_domain=${dialed_domain}:presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(*/${dialed_user}@${dialed_domain})},${verto_contact(${dialed_user}@${dialed_domain})}"/>
<!-- These are required for Verto to function properly -->
<param name="jsonrpc-allowed-methods" value="verto"/>
<!-- <param name="jsonrpc-allowed-event-channels" value="demo,conference,presence"/> -->

@ -83,6 +83,8 @@ default_certsdir="$prefix/certs"
default_fontsdir="$prefix/fonts"
default_imagesdir="$prefix/images"
eval bindir_expanded="${bindir}"
if test "${enable_fhs}" = "yes"; then
eval full_datadir="${datadir}/freeswitch"
eval datadir=$full_datadir
@ -771,6 +773,14 @@ case "$host" in
;;
*linux*)
APR_ADDTO([PLATFORM_CORE_LIBS], [-ldl -lcrypt -lrt])
systemdinit=false
if test -d /run/systemd/system; then
systemdinit=true
AC_SUBST(bindir_expanded)
AC_CONFIG_FILES([build/startup/install_systemd.sh], [chmod +x build/startup/install_systemd.sh])
AC_CONFIG_FILES([build/startup/freeswitch.service])
fi
AM_CONDITIONAL([SYSTEMD_INIT], [test x$systemdinit = xtrue])
;;
esac

3
debian/bootstrap.sh vendored

@ -70,13 +70,14 @@ avoid_mods_wheezy=(
languages/mod_managed
applications/mod_av
applications/mod_cv
applications/mod_hiredis
applications/mod_hiredis
formats/mod_shout
applications/mod_sonar
applications/mod_soundtouch
formats/mod_vlc
)
avoid_mods_trusty=(
event_handlers/mod_amqp
)
avoid_mods_utopic=(
directories/mod_ldap

@ -1,2 +1,2 @@
debian/tmp/usr/perl/freeswitch.pm /usr/lib/perl5
debian/tmp/usr/perl/freeswitch.so /usr/lib/perl5/auto/freeswitch
debian/tmp/usr/perl/freeswitch.pm /usr/share/perl5
debian/tmp/usr/perl/freeswitch.so /usr/share/perl5/auto/freeswitch

@ -223,7 +223,7 @@ textarea {
body {
margin: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: verdana, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
color: #333333;
@ -1065,7 +1065,7 @@ input,
button,
select,
textarea {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: verdana, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
label {
@ -4510,7 +4510,7 @@ input[type="submit"].btn.btn-mini {
.navbar-search .search-query {
padding: 4px 14px;
margin-bottom: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: verdana, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
font-weight: normal;
line-height: 1;
@ -5624,7 +5624,6 @@ a.thumbnail:focus {
display: inline-block;
padding: 2px 4px;
font-size: 11.844px;
font-weight: bold;
line-height: 14px;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);

@ -90,7 +90,8 @@
},
}, options);
this.enabled = true;
this.audioEnabled = true;
this.videoEnabled = true;
this.mediaData = {
@ -360,7 +361,7 @@
$.FSRTC.prototype.getMute = function() {
var self = this;
return self.enabled;
return self.audioEnabled;
}
$.FSRTC.prototype.setMute = function(what) {
@ -381,10 +382,39 @@
break;
}
self.enabled = audioTracks[i].enabled;
self.audioEnabled = audioTracks[i].enabled;
}
return !self.enabled;
return !self.audioEnabled;
}
$.FSRTC.prototype.getVideoMute = function() {
var self = this;
return self.videoEnabled;
}
$.FSRTC.prototype.setVideoMute = function(what) {
var self = this;
var videoTracks = self.localStream.getVideoTracks();
for (var i = 0, len = videoTracks.length; i < len; i++ ) {
switch(what) {
case "on":
videoTracks[i].enabled = true;
break;
case "off":
videoTracks[i].enabled = false;
break;
case "toggle":
videoTracks[i].enabled = !videoTracks[i].enabled;
default:
break;
}
self.videoEnabled = videoTracks[i].enabled;
}
return !self.videoEnabled;
}
$.FSRTC.prototype.createAnswer = function(params) {

@ -91,6 +91,32 @@
/// The next JSON-RPC request id.
$.JsonRpcClient.prototype._current_id = 1;
$.JsonRpcClient.prototype.speedTest = function (bytes, cb) {
var socket = this.options.getSocket(this.wsOnMessage);
if (socket !== null) {
this.speedCB = cb;
this.speedBytes = bytes;
socket.send("#SPU " + bytes);
var loops = bytes / 1024;
var rem = bytes % 1024;
var i;
var data = new Array(1024).join(".");
for (i = 0; i < loops; i++) {
socket.send("#SPB " + data);
}
if (rem) {
socket.send("#SPB " + data);
}
socket.send("#SPE");
}
};
/**
* @fn call
* @memberof $.JsonRpcClient
@ -382,6 +408,26 @@
$.JsonRpcClient.prototype._wsOnMessage = function(event) {
// Check if this could be a JSON RPC message.
var response;
// Special sub proto
if (event.data[0] == "#" && event.data[1] == "S" && event.data[2] == "P") {
if (event.data[3] == "U") {
this.up_dur = parseInt(event.data.substring(4));
} else if (this.speedCB && event.data[3] == "D") {
this.down_dur = parseInt(event.data.substring(4));
var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0);
var down_kps = (((this.speedBytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0);
console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps);
this.speedCB(event, { upDur: this.up_dur, downDur: this.down_dur, upKPS: up_kps, downKPS: down_kps });
this.speedCB = null;
}
return;
}
try {
response = $.parseJSON(event.data);

@ -1075,6 +1075,27 @@
console.error("Error: ", obj, args);
};
/* back compat so jsonstatus can always be enabled */
function genRow(data) {
if (typeof(data[4]) === "string" && data[4].indexOf("{") > -1) {
var tmp = $.parseJSON(data[4]);
data[4] = tmp.oldStatus;
data[5] = null;
}
return data;
}
function genArray(obj) {
var data = obj.asArray();
for (var i in data) {
data[i] = genRow(data[i]);
}
return data;
}
la.onChange = function(obj, args) {
var index = 0;
var iserr = 0;
@ -1122,7 +1143,7 @@
return;
}
dt.fnClearTable();
dt.fnAddData(obj.asArray());
dt.fnAddData(genArray(obj));
dt.fnAdjustColumnSizing();
break;
case "add":
@ -1133,9 +1154,9 @@
if (args.redraw > -1) {
// specific position, more costly
dt.fnClearTable();
dt.fnAddData(obj.asArray());
dt.fnAddData(genArray(obj));
} else {
dt.fnAddData(args.data);
dt.fnAddData(genRow(args.data));
}
dt.fnAdjustColumnSizing();
break;
@ -1144,7 +1165,7 @@
return;
}
//console.debug(args, index);
dt.fnUpdate(args.data, index);
dt.fnUpdate(genRow(args.data), index);
dt.fnAdjustColumnSizing();
break;
case "del":
@ -1157,7 +1178,7 @@
case "reorder":
// specific position, more costly
dt.fnClearTable();
dt.fnAddData(obj.asArray());
dt.fnAddData(genArray(obj));
break;
case "hide":
jq.hide();
@ -2022,6 +2043,53 @@
return false;
}
// Attach audio output device to video element using device/sink ID.
function find_name(id) {
for (var i in $.verto.audioOutDevices) {
var source = $.verto.audioOutDevices[i];
if (source.id === id) {
return(source.label);
}
}
return id;
}
$.verto.dialog.prototype.setAudioPlaybackDevice = function(sinkId, callback, arg) {
var dialog = this;
var element = dialog.audioStream;
if (typeof element.sinkId !== 'undefined') {
var devname = find_name(sinkId);
console.info("Dialog: " + dialog.callID + " Setting speaker:", element, devname);
element.setSinkId(sinkId)
.then(function() {
console.log("Dialog: " + dialog.callID + ' Success, audio output device attached: ' + sinkId);
if (callback) {
callback(true, devname, arg);
}
})
.catch(function(error) {
var errorMessage = error;
if (error.name === 'SecurityError') {
errorMessage = "Dialog: " + dialog.callID + ' You need to use HTTPS for selecting audio output ' +
'device: ' + error;
}
if (callback) {
callback(false, null, arg);
}
console.error(errorMessage);
});
} else {
console.warn("Dialog: " + dialog.callID + ' Browser does not support output device selection.');
if (callback) {
callback(false, null, arg);
}
}
}
$.verto.dialog.prototype.setState = function(state) {
var dialog = this;
@ -2061,11 +2129,9 @@
console.info("Using Speaker: ", speaker);
if (speaker && speaker !== "any") {
var videoElement = dialog.audioStream;
setTimeout(function() {
console.info("Setting speaker:", videoElement, speaker);
attachSinkId(videoElement, speaker);}, 500);
dialog.setAudioPlaybackDevice(speaker);
}, 500);
}
break;
@ -2226,6 +2292,16 @@
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;

@ -42,7 +42,7 @@
"jquery-cookie": "~1.4.1",
"jquery-json": "~2.5.1",
"datatables": "~1.10.8",
"angular-bootstrap": "~0.13.3",
"angular-bootstrap": "~0.14.3",
"bootstrap-material-design": "~0.3.0"
},
"resolutions": {

@ -1,10 +1,10 @@
// Last time updated at Sep 07, 2014, 08:32:23
// Last time updated at Oct 24, 2015, 08:32:23
// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/getScreenId.js
// Documentation - https://github.com/muaz-khan/getScreenId.
// ______________
// getScreenId.js
@ -13,7 +13,7 @@
getScreenId(function (error, sourceId, screen_constraints) {
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
// sourceId == null || 'string' || 'firefox'
if(sourceId == 'firefox') {
navigator.mozGetUserMedia(screen_constraints, onSuccess, onFailure);
}
@ -79,6 +79,11 @@ getScreenId(function (error, sourceId, screen_constraints) {
}
function postMessage() {
if (!iframe) {
loadIFrame(postMessage);
return;
}
if (!iframe.isLoaded) {
setTimeout(postMessage, 100);
return;
@ -89,11 +94,137 @@ getScreenId(function (error, sourceId, screen_constraints) {
}, '*');
}
var iframe = document.createElement('iframe');
iframe.onload = function() {
iframe.isLoaded = true;
function loadIFrame(loadCallback) {
if (iframe) {
loadCallback();
return;
}
iframe = document.createElement('iframe');
iframe.onload = function() {
iframe.isLoaded = true;
loadCallback();
};
iframe.src = 'https://www.webrtc-experiment.com/getSourceId/'; // https://wwww.yourdomain.com/getScreenId.html
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
var iframe;
// this function is used in v3.0
window.getScreenConstraints = function(callback) {
loadIFrame(function() {
getScreenId(function(error, sourceId, screen_constraints) {
callback(error, screen_constraints.video);
});
});
};
})();
(function() {
if(document.domain.indexOf('webrtc-experiment.com') === -1) {
return;
}
window.getScreenId = function(callback) {
// for Firefox:
// sourceId == 'firefox'
// screen_constraints = {...}
if (!!navigator.mozGetUserMedia) {
callback(null, 'firefox', {
video: {
mozMediaSource: 'window',
mediaSource: 'window'
}
});
return;
}
postMessage();
window.addEventListener('message', onIFrameCallback);
function onIFrameCallback(event) {
if (!event.data) return;
if (event.data.chromeMediaSourceId) {
if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
callback('permission-denied');
} else callback(null, event.data.chromeMediaSourceId, getScreenConstraints(null, event.data.chromeMediaSourceId));
}
if (event.data.chromeExtensionStatus) {
callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus));
}
// this event listener is no more needed
window.removeEventListener('message', onIFrameCallback);
}
};
function getScreenConstraints(error, sourceId) {
var screen_constraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: error ? 'screen' : 'desktop',
maxWidth: window.screen.width > 1920 ? window.screen.width : 1920,
maxHeight: window.screen.height > 1080 ? window.screen.height : 1080
},
optional: []
}
};
if (sourceId) {
screen_constraints.video.mandatory.chromeMediaSourceId = sourceId;
}
return screen_constraints;
}
function postMessage() {
if (!iframe) {
loadIFrame(postMessage);
return;
}
if (!iframe.isLoaded) {
setTimeout(postMessage, 100);
return;
}
iframe.contentWindow.postMessage({
captureSourceId: true
}, '*');
}
function loadIFrame(loadCallback) {
if (iframe) {
loadCallback();
return;
}
iframe = document.createElement('iframe');
iframe.onload = function() {
iframe.isLoaded = true;
loadCallback();
};
iframe.src = 'https://www.webrtc-experiment.com/getSourceId/'; // https://wwww.yourdomain.com/getScreenId.html
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
var iframe;
// this function is used in v3.0
window.getScreenConstraints = function(callback) {
loadIFrame(function() {
getScreenId(function(error, sourceId, screen_constraints) {
callback(error, screen_constraints.video);
});
});
};
iframe.src = 'https://www.webrtc-experiment.com/getSourceId/';
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
})();

@ -0,0 +1,96 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Chris Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
Usage:
audioNode = createAudioMeter(audioContext,clipLevel,averaging,clipLag);
audioContext: the AudioContext you're using.
clipLevel: the level (0 to 1) that you would consider "clipping".
Defaults to 0.98.
averaging: how "smoothed" you would like the meter to be over time.
Should be between 0 and less than 1. Defaults to 0.95.
clipLag: how long you would like the "clipping" indicator to show
after clipping has occured, in milliseconds. Defaults to 750ms.
Access the clipping through node.checkClipping(); use node.shutdown to get rid of it.
*/
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) {
var processor = audioContext.createScriptProcessor(512);
processor.onaudioprocess = volumeAudioProcess;
processor.clipping = false;
processor.lastClip = 0;
processor.volume = 0;
processor.clipLevel = clipLevel || 0.98;
processor.averaging = averaging || 0.95;
processor.clipLag = clipLag || 750;
// this will have no effect, since we don't copy the input to the output,
// but works around a current Chrome bug.
processor.connect(audioContext.destination);
processor.checkClipping =
function(){
if (!this.clipping)
return false;
if ((this.lastClip + this.clipLag) < window.performance.now())
this.clipping = false;
return this.clipping;
};
processor.shutdown =
function(){
this.disconnect();
this.onaudioprocess = null;
};
return processor;
}
function volumeAudioProcess( event ) {
var buf = event.inputBuffer.getChannelData(0);
var bufLength = buf.length;
var sum = 0;
var x;
// Do a root-mean-square on the samples: sum up the squares...
for (var i=0; i<bufLength; i++) {
x = buf[i];
if (Math.abs(x)>=this.clipLevel) {
this.clipping = true;
this.lastClip = window.performance.now();
}
sum += x * x;
}
// ... then take the square root of the sum.
var rms = Math.sqrt(sum / bufLength);
// Now smooth this out with the averaging factor applied
// to the previous sample - take the max here because we
// want "fast attack, slow release."
this.volume = Math.max(rms, this.volume*this.averaging);
}

@ -15,7 +15,7 @@
"grunt-contrib-copy": "^0.7.0",
"grunt-contrib-cssmin": "^0.12.0",
"grunt-contrib-htmlmin": "^0.4.0",
"grunt-contrib-imagemin": "^0.9.2",
"grunt-contrib-imagemin": "^1.0.0",
"grunt-contrib-jshint": "^0.11.0",
"grunt-contrib-uglify": "^0.7.0",
"grunt-contrib-watch": "latest",

@ -8,6 +8,15 @@ body {
padding-top: 60px;
}
.panel.panel-material-blue-900 .panel-heading {
background-color: #0d47a1;
}
.install {
color: white;
text-decoration: underline;
}
.ellipsis {
text-overflow: ellipsis;
overflow: hidden;
@ -595,12 +604,8 @@ body .modal-body .btn-group .btn.active {
transition-delay:0s;
}
#incall .video-hover-buttons .btn-group {
margin: 0;
}
#incall .video-hover-buttons .btn-group .dropdown-menu {
height: 200px;
max-height: 200px;
overflow: auto;
}
@ -766,6 +771,10 @@ body .modal-body .btn-group .btn.active {
transition: all 0.5s ease;
}
.tooltip-inner {
padding: 8px 8px;
background-color: #000;
}
#sidebar-wrapper {
right: 360px;
@ -909,7 +918,6 @@ body .modal-body .btn-group .btn.active {
.members-badges {
font-size: 10px;
text-transform: uppercase;
margin-top: -2px;
}
@ -1464,3 +1472,76 @@ body:-webkit-full-screen #incall .video-footer {
}
}
.preview-wrapper {
position: relative;
}
.preview-wrapper video {
transform: scaleX(-1);
}
.drop-net-info {
padding-top: 0px;
cursor: default;
}
.drop-net-info .title {
text-align: center;
font-size: 16px;
font-weight: bold;
padding: 8px 14px;
margin: 0;
font-size: 14px;
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
}
.drop-net-info .title:hover {
background-color: #f7f7f7;
}
.drop-net-info a:hover {
color: #333 !important;
}
.net-info .yellow {
color: #e3d95b;
}
.net-info .green {
color: #00ae00;
}
.net-info .red {
color: #ae0000;
}
#mic-meter {
position: absolute;
bottom: 5px;
left: 10px;
}
#mic-meter .icon {
margin-left: 3px;
color: #CCC;
}
#mic-meter .volumes {
width: 30px;
}
#mic-meter .volumes .volume-segment {
height: 10px;
width: 100%;
border-radius: 5px;
border: 2px solid #CCC;
display: block;
margin-top: 1.5px;
}
#mic-meter .volumes .volume-segment.active {
background-color: #CCC;
}
#preview .refresh {
margin: 15px 0px 0px 0px;
}

@ -95,6 +95,7 @@
<script type="text/javascript" src="js/3rd-party/getScreenId.js"></script>
<script type="text/javascript" src="js/3rd-party/md5.min.js"></script>
<script type="text/javascript" src="js/3rd-party/volume-meter.js"></script>
<script type="text/javascript" src="src/vertoApp/vertoApp.module.js"></script>
@ -113,6 +114,7 @@
<script type="text/javascript" src="src/vertoControllers/controllers/ModalWsReconnectController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/ModalLoginInformationController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/ModalSettingsController.js"></script>
<script type="text/javascript" src="src/vertoControllers/controllers/PreviewController.js"></script>
<script type="text/javascript" src="src/vertoDirectives/vertoDirectives.module.js"></script>
<script type="text/javascript" src="src/vertoDirectives/directives/autofocus.js"></script>

@ -28,7 +28,8 @@
<div class="members-badges">
<div ng-if="member.status.video.floor" class="label badge-floor" ng-class="{'label-danger': member.status.video.floorLocked, 'label-info': !member.status.video.floorLocked}"><i class="mdi mdi-action-https lock-floor" ng-if="member.status.video.floorLocked"></i> <span>Floor</span></div>
<div ng-if="member.status.video.reservationID == 'presenter'" class="label label-warning">Presenter</div>
<div ng-if="member.status.video.reservationID == 'presenter'" class="label label-info">Presenter</div>
<div ng-if="member.status.video.screenShare" class="label label-info">Screen Share</div>
</div>
</h4>

@ -1,4 +1,10 @@
<div class="centered-block-frame" id="dialpad">
<div ng-show="loading">
<h3 style="margin-top: 4%;" class="text-center">Calling to {{ dialpadNumber }}...</h3>
<svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
</svg>
</div>
<div class="centered-block-frame" id="dialpad" ng-show="!loading">
<div id="call-history-wrapper">
<div id="call_history" class="shadow-z-2 panel">
<div class="panel-heading">

@ -19,9 +19,21 @@
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="navbar-item-icon net-info" ng-show="(bandUp || bandDown) && storage.data.autoBand">
<a href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<i ng-class="iconClass"></i>
<span class="caret"></span>
</a>
<ul class="dropdown-menu drop-net-info" role="menu" ng-click="$event.stopPropagation()">
<li><a class="title">Bandwidth Info</a></li>
<li><a>Outgoing: {{bandUp}} Kbps</a></li>
<li><a>Incoming: {{bandDown}} Kbps</a></li>
<li><a>Video Resolution: {{vidRes}}</a></li>
</ul>
</li>
<li>
<a href="">
<i class="mdi-toggle-radio-button-on" user-status condition="storage.data.userStatus"></i>
<i class="mdi-toggle-radio-button-on" user-status condition="storage.data.userStatus"></i>
</a>
</li>
<li>
@ -29,7 +41,6 @@
{{ storage.data.called_number && storage.data.userStatus == 'connecting' ? 'Last Call: ' : 'In Call: ' }} {{ storage.data.called_number }}
</a>
</li>
<li class="navbar-item-icon" ng-show="verto.data.connected">
<a href="" ng-click="openModal('partials/modal_settings.html', 'ModalSettingsController')">
<i class="mdi-action-settings"></i>

@ -22,10 +22,17 @@
<select name="microphone" id="settings-microphone" class="form-control"
ng-model="mydata.selectedAudio" ng-options="item.id as item.label for item in verto.data.audioDevices">
</select>
<a class="btn btn-primary" href="" ng-click="refreshDeviceList()">Refresh device list</a>
</div>
<div class="form-group">
<label for="settings-microphone">Speaker:</label>
<select name="microphone" id="settings-microphone" class="form-control"
ng-model="mydata.selectedSpeaker" ng-options="item.id as item.label for item in verto.data.speakerDevices">
</select>
</div>
<a class="btn btn-primary" href="" ng-click="refreshDeviceList()">Refresh device list</a>
<div class="form-group">
<label for="settings-microphone">General settings:</label>
<div class="checkbox">
@ -84,35 +91,52 @@
</div>
<div class="form-group">
<label for="video-quality">Video quality:</label>
<select name="video_quality" id="video-quality" class="form-control"
ng-model="mydata.vidQual"
ng-options="item.id as item.label for item in verto.videoQuality"></select>
</div>
<label>Video settings:</label> <br>
<input type="hidden" name="use_dedenc" ng-value="mydata.useDedenc" ng-model="mydata.useDedenc">
<input type="hidden" name="use_dedenc" ng-value="mydata.useDedenc" ng-model="mydata.useDedenc">
<h4>Dedicated Remote Encoder</h4>
<h5>Select a non default bandwidth to use a dedicated remote encoder.</h5>
<div ng-show="mydata.useDedenc" class="dedicated_encoder">
<p>Dedicated Remote Encoder enabled.</b>
</div>
<div ng-show="mydata.useDedenc" class="dedicated_encoder">
<p>Dedicated Remote Encoder enabled.</b>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="mydata.autoBand" ng-change="checkAutoBand(mydata.autoBand)">
Automatically determine speed and resolution settings
</label>
</div>
<div class="form-group">
<label for="outgoing-bandwidth">Max outgoing bandwidth:</label>
<select name="outgoing_bandwidth" id="outgoing-bandwidth" class="form-control"
ng-model="mydata.outgoingBandwidth"
ng-change="checkUseDedRemoteEncoder(mydata.outgoingBandwidth)"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
<div class="checkbox" ng-show="mydata.autoBand">
<label>
<input type="checkbox" ng-model="mydata.testSpeedJoin">
Recheck bandwidth before each outgoing call
</label>
</div>
<div class="form-group">
<label for="incoming-bandwidth">Max incoming bandwidth:</label>
<select name="incoming_bandwidth" id="incoming-bandwidth" class="form-control"
ng-model="mydata.incomingBandwidth"
ng-change="checkUseDedRemoteEncoder(mydata.incomingBandwidth)"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
<a ng-show="mydata.autoBand" class="btn btn-primary" href="" ng-click="testSpeed()">Check Network Speed</a> <span ng-bind="speedMsg"></span>
<div ng-show="!mydata.autoBand">
<label for="video-quality">Video quality:</label>
<select name="video_quality" id="video-quality" class="form-control"
ng-disabled="mydata.autoBand"
ng-model="mydata.vidQual"
ng-options="item.id as item.label for item in verto.videoQuality"></select>
</div>
<div ng-show="!mydata.autoBand">
<label for="incoming-bandwidth">Max incoming bandwidth:</label>
<select name="incoming_bandwidth" id="incoming-bandwidth" class="form-control"
ng-model="mydata.incomingBandwidth"
ng-change="checkUseDedRemoteEncoder(mydata.incomingBandwidth)"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
<div ng-show="!mydata.autoBand">
<label for="outgoing-bandwidth">Max outgoing bandwidth:</label>
<select name="outgoing_bandwidth" id="outgoing-bandwidth" class="form-control"
ng-model="mydata.outgoingBandwidth"
ng-options="item.id as item.label for item in verto.bandwidth"></select>
</div>
</div>
</div>

@ -0,0 +1,47 @@
<div class="centered-block-frame" id="preview">
<div class="col-md-4 col-sm-12 col-xs-12 centered-block">
<div class="panel panel-material-blue-900 shadow-z-2 ">
<div class="panel-heading">
<h3 class="panel-title text-center">Setup your camera and microphone settings</h3>
</div>
<div class="panel-body">
<div class="preview-wrapper">
<video id="videopreview" muted autoplay style="width: 100%;"></video>
<div id="mic-meter">
<div class="volumes">
<div class="volume-segment"></div>
<div class="volume-segment"></div>
<div class="volume-segment"></div>
<div class="volume-segment"></div>
<div class="volume-segment"></div>
</div>
<i class="icon mdi-hardware-keyboard-voice"></i>
</div>
</div>
<form name="form">
<div class="form-group col-md-5 col-sm-12 col-xs-12" ng-show="true">
<label for="settings-camera">Camera:</label>
<select name="camera" id="settings-camera" class="form-control" ng-model="storage.data.selectedVideo"
ng-options="item.id as item.label for item in verto.data.videoDevices" ng-change="localVideo()" >
</select>
</div>
<div class="form-group col-md-5 col-sm-12 col-xs-12" ng-show="true">
<label for="settings-microphone">Microphone:</label>
<select name="microphone" id="settings-microphone" class="form-control" ng-model="storage.data.selectedAudio"
ng-options="item.id as item.label for item in verto.data.audioDevices" ng-change="localVideo()">
</select>
</div>
<a class="btn btn-primary btn-sm col-md-2 refresh" ng-click="refreshDeviceList()">
<i class="icon mdi-action-autorenew"></i>
</a>
<div class="form-group text-center">
<button type="submit" class="btn btn-success" ng-click="endPreview()" title="Save">
Save
</button>
</div>
</form>
</div>
</div>
</div>
</div>

@ -2,23 +2,30 @@
<div class="video-wrapper">
<div class="video-hover-buttons" ng-show="verto.data.callState == 'active'">
<div id="moderator-tools" ng-show="verto.data.confRole == 'moderator'">
<button tooltips="" tooltip-title="Play" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="play()">
<button tooltip-placement="bottom" tooltip-title="Play" uib-tooltip="Play"
class="btn btn-material-blue-900" ng-click="play()">
<i class="mdi-av-play-circle-outline"></i>
</button>
<button tooltips="" tooltip-title="Stop" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="stop()">
<button tooltip-placement="bottom" tooltip-title="Stop" uib-tooltip="Stop"
class="btn btn-material-blue-900" ng-click="stop()">
<i class="mdi-av-stop"></i>
</button>
<button tooltips="" tooltip-title="Record" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="record()">
<button tooltip-placement="bottom" tooltip-title="Record" uib-tooltip="Record"
class="btn btn-material-blue-900" ng-click="record()">
<i class="mdi-toggle-radio-button-on"></i>
</button>
<button tooltips="" tooltip-title="Stop Record" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="stopRecord()">
<button tooltip-placement="bottom" tooltip-title="Stop Record" uib-tooltip="Stop Record"
class="btn btn-material-blue-900" ng-click="stopRecord()">
<i class="mdi-image-switch-camera"></i>
</button>
<button tooltips="" tooltip-title="Snapshot" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="snapshot()">
<button tooltip-placement="bottom" tooltip-title="Snapshot" uib-tooltip="Snapshot"
tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="snapshot()">
<i class="mdi-image-photo-camera"></i>
</button>
<div class="btn-group">
<button tooltips="" tooltip-title="Video Mode" tooltip-side="bottom" tooltip-lazy="false" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<button tooltip-placement="bottom" tooltip-title="Video Mode" uib-tooltip="Video Mode"
type="button" class="btn btn-material-blue-900 dropdown-toggle"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="mdi-action-view-module"></i>
<span class="caret"></span>
</button>
@ -29,22 +36,39 @@
</ul>
</div>
</div>
<br/>
<button tooltips="" tooltip-title="(un)Mute Mic" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="muteMic(cbMuteMic)">
<i class="" ng-class="{'mdi-av-mic': !verto.data.mutedMic, 'mdi-av-mic-off': verto.data.mutedMic}"></i>
</button>
<button tooltips="" tooltip-title="(un)Mute Video" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="muteVideo(cbMuteVideo)" ng-if="verto.data.canVideo">
<i class="" ng-class="{'mdi-av-videocam': !verto.data.mutedVideo, 'mdi-av-videocam-off': verto.data.mutedVideo}"></i>
</button>
<button tooltips="" tooltip-title="Toggle Fullscreen Mode" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="goFullscreen()">
<i class="" ng-class="{'mdi-navigation-fullscreen': !fullscreenEnabled, 'mdi-navigation-fullscreen-exit': fullscreenEnabled}"></i>
</button>
<button tooltips="" tooltip-title="Screenshare" tooltip-side="bottom" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="screenshare()">
<i class="mdi-hardware-desktop-windows"></i>
</button>
<button tooltips="" tooltip-title="Open/Close Chat" tooltip-side="right" tooltip-lazy="false" class="btn btn-material-blue-900" ng-click="toggleChat()" ng-show="fullscreenEnabled">
<i class="mdi-communication-chat"></i>
</button>
<div class="user-tools">
<button tooltip-placement="bottom" tooltip-title="(un)Mute Mic" uib-tooltip="(un)Mute Mic"
class="btn btn-material-blue-900" ng-click="muteMic(cbMuteMic)">
<i class="" ng-class="{'mdi-av-mic': !verto.data.mutedMic, 'mdi-av-mic-off': verto.data.mutedMic}"></i>
</button>
<button tooltip-placement="bottom" tooltip-title="(un)Mute Video" uib-tooltip="(un)Mute Video"
class="btn btn-material-blue-900" ng-click="muteVideo(cbMuteVideo)" ng-if="verto.data.canVideo">
<i class="" ng-class="{'mdi-av-videocam': !verto.data.mutedVideo, 'mdi-av-videocam-off': verto.data.mutedVideo}"></i>
</button>
<button tooltip-placement="bottom" tooltip-title="Toggle Fullscreen Mode" uib-tooltip="Toggle Fullscreen Mode"
class="btn btn-material-blue-900" ng-click="goFullscreen()">
<i class="" ng-class="{'mdi-navigation-fullscreen': !fullscreenEnabled, 'mdi-navigation-fullscreen-exit': fullscreenEnabled}"></i>
</button>
<button tooltip-placement="bottom" tooltip-title="Screenshare" uib-tooltip="Screenshare"
class="btn btn-material-blue-900" ng-click="screenshare()">
<i class="mdi-hardware-desktop-windows"></i>
</button>
<button tooltip-placement="bottom" tooltip-title="Open/Close Chat" uib-tooltip="Open/Close Chat"
class="btn btn-material-blue-900" ng-click="toggleChat()" ng-show="fullscreenEnabled">
<i class="mdi-communication-chat"></i>
</button>
<div class="btn-group">
<button tooltip-placement="bottom" tooltip-title="Speaker" uib-tooltips="Speaker" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="mdi-hardware-headset"></i>
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li ng-repeat="speaker in verto.data.speakerDevices">
<a ng-click="confChangeSpeaker(speaker.id)">{{ speaker.label }}</a>
</li>
</ul>
</div>
</div>
</div>
<div class="video-tag-wrapper" id="video-tag-wrapper" ng-dblclick="goFullscreen()" show-controls>
<video-tag ng-class="{'invisible': (verto.data.callState != 'active')}"></video-tag>
@ -69,4 +93,3 @@
</div>
</div>
</div>

@ -4,7 +4,7 @@
.module('storageService')
.service('splashscreen', ['$rootScope', '$q', 'storage', 'config', 'verto',
function($rootScope, $q, storage, config, verto) {
var checkBrowser = function() {
return $q(function(resolve, reject) {
var activity = 'browser-upgrade';
@ -22,10 +22,10 @@
if (!navigator.getUserMedia) {
result['status'] = 'error';
result['message'] = 'Error: browser doesn\'t support WebRTC.';
reject(result);
reject(result);
}
resolve(result);
resolve(result);
});
};
@ -48,7 +48,7 @@
reject(result);
}
verto.data.mediaPerm = true;
resolve(result);
resolve(result);
});
});
};
@ -62,7 +62,7 @@
'activity': activity,
'message': 'Refresh Media Devices.'
};
verto.refreshDevices(function(status) {
verto.refreshDevicesCallback(function() {
resolve(result);
@ -72,6 +72,28 @@
});
};
var checkConnectionSpeed = function() {
return $q(function(resolve, reject) {
var activity = 'check-connection-speed';
var result = {
'status': 'success',
'soft': true,
'activity': activity,
'message': 'Check Connection Speed.'
};
if (storage.data.autoBand && verto.data.instance) {
verto.testSpeed(cb);
} else {
resolve(result);
}
function cb(data) {
resolve(result);
}
});
};
var provisionConfig = function() {
return $q(function(resolve, reject) {
var activity = 'provision-config';
@ -100,7 +122,7 @@
});
result['promise'] = configPromise;
resolve(result);
});
};
@ -136,13 +158,13 @@
verto.data.connecting = false;
resolve(result);
});
};
};
};
if(storage.data.ui_connected && storage.data.ws_connected) {
checkUserStored();
checkUserStored();
} else {
resolve(result);
resolve(result);
};
});
};
@ -152,7 +174,8 @@
checkMediaPerm,
refreshMediaDevices,
provisionConfig,
checkLogin
checkLogin,
checkConnectionSpeed
];
var progress_message = [
@ -160,12 +183,13 @@
'Checking media permissions',
'Refresh Media Devices.',
'Provisioning configuration.',
'Checking login.'
'Checking login.',
'Check Connection Speed.'
];
var getProgressMessage = function(current_progress) {
if(progress_message[current_progress] != undefined) {
return progress_message[current_progress];
return progress_message[current_progress];
} else {
return 'Please wait...';
}
@ -176,7 +200,7 @@
var calculateProgress = function(index) {
var _progress;
_progress = index + 1;
progress_percentage = (_progress / progress.length) * 100;
return progress_percentage;
@ -186,12 +210,12 @@
var fn, fn_return, status, interrupt, activity, soft, message, promise;
interrupt = false;
current_progress++;
if(current_progress >= progress.length) {
$rootScope.$emit('progress.complete', current_progress);
return;
}
fn = progress[current_progress];
fn_return = fn();
@ -221,7 +245,7 @@
emitNextProgress(fn_return);
}
);
};
return {
@ -232,4 +256,3 @@
};
}]);

@ -21,9 +21,11 @@
userStatus: 'disconnected',
mutedVideo: false,
mutedMic: false,
preview: true,
selectedVideo: null,
selectedAudio: null,
selectedShare: null,
selectedSpeaker: null,
useStereo: true,
useSTUN: true,
useDedenc: false,
@ -34,7 +36,9 @@
askRecoverCall: false,
googNoiseSuppression: true,
googHighpassFilter: true,
googEchoCancellation: true
googEchoCancellation: true,
autoBand: true,
testSpeedJoin: true
};
data.$default(defaultSettings);

@ -15,6 +15,7 @@
'cgPrompt',
'720kb.tooltips',
'ui.gravatar',
'ui.bootstrap',
'directive.g+signin',
]);
@ -41,6 +42,11 @@
templateUrl: 'partials/incall.html',
controller: 'InCallController'
}).
when('/preview', {
title: 'Preview Video',
templateUrl: 'partials/preview.html',
controller: 'PreviewController'
}).
when('/browser-upgrade', {
title: '',
templateUrl: 'partials/browser_upgrade.html',

@ -120,6 +120,8 @@
if (parseInt(member.id) == parseInt(verto.data.conferenceMemberID)) {
verto.data.mutedMic = member.status.audio.muted;
verto.data.mutedVideo = member.status.video.muted;
verto.data.call.setMute(member.status.audio.muted ? "off" : "on");
verto.data.call.setVideoMute(member.status.video.muted ? "off" : "on");
}
angular.extend($scope.members[memberIdx], member);
});

@ -7,9 +7,9 @@
'$http', '$location', 'toastr', 'verto', 'storage', 'CallHistory', 'eventQueue',
function($rootScope, $scope, $http, $location, toastr, verto, storage, CallHistory, eventQueue) {
console.debug('Executing DialPadController.');
eventQueue.process();
$scope.call_history = CallHistory.all();
$scope.history_control = CallHistory.all_control();
$scope.has_history = Object.keys($scope.call_history).length;
@ -55,6 +55,10 @@
$rootScope.dialpadNumber = number;
};
$scope.preview = function() {
$location.path('/preview');
};
$rootScope.transfer = function() {
if (!$rootScope.dialpadNumber) {
return false;
@ -93,8 +97,17 @@
/**
* Call to the number in the $rootScope.dialpadNumber.
*/
$scope.loading = false;
$rootScope.call = function(extension) {
return call(extension);
if (!storage.data.testSpeedJoin || !$rootScope.dialpadNumber) {
return call(extension);
}
$scope.loading = true;
verto.testSpeed(function() {
$scope.loading = false;
call(extension);
});
}
}
]);

@ -5,7 +5,7 @@
.module('vertoControllers')
.controller('InCallController', ['$rootScope', '$scope',
'$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen',
function($rootScope, $scope, $http, $location, $modal, $timeout, toatr,
function($rootScope, $scope, $http, $location, $modal, $timeout, toastr,
verto, storage, prompt, Fullscreen) {
console.debug('Executing InCallController.');
@ -19,7 +19,7 @@
if (storage.data.videoCall) {
$scope.callTemplate = 'partials/video_call.html';
}
$rootScope.$on('call.conference', function(event, data) {
$timeout(function() {
if($scope.chatStatus) {
@ -75,9 +75,36 @@
verto.data.conf.setVideoLayout(layout);
};
$scope.confChangeSpeaker = function(speakerId) {
storage.data.selectedSpeaker = speakerId;
$rootScope.$emit('changedSpeaker', speakerId);
};
$scope.screenshare = function() {
if(verto.data.shareCall) {
verto.screenshareHangup();
return false;
}
verto.screenshare(storage.data.called_number);
};
$scope.muteMic = verto.muteMic;
$scope.muteVideo = verto.muteVideo;
$rootScope.$on('ScreenShareExtensionStatus', function(event, error) {
var pluginUrl = 'https://chrome.google.com/webstore/detail/screen-capturing/ajhifddimkapgcifgcodmmfdlknahffk';
switch(error) {
case 'permission-denied':
toastr.info('Please allow the plugin in order to use Screen Share', 'Error'); break;
case 'not-installed':
toastr.warning('Please <a target="_blank" class="install" href="'+ pluginUrl +'">install</a> the plugin in order to use Screen Share', 'Warning', { allowHtml: true }); break;
case 'installed-disabled':
toastr.info('Please enable the plugin in order to use Screen Share', 'Error'); break;
// case 'not-chrome'
// toastr.info('Chrome', 'Error');
}
});
$timeout(function() {
console.log('broadcast time-start incall');
$scope.$broadcast('timer-start');

@ -24,16 +24,24 @@
* @type {string}
*/
$rootScope.dialpadNumber = '';
// If verto is not connected, redirects to login page.
if (!verto.data.connected) {
console.debug('MainController: WebSocket not connected. Redirecting to login.');
$location.path('/');
}
$rootScope.$on('config.http.success', function(ev) {
$scope.login(false);
});
$rootScope.$on('changedSpeaker', function(event, speakerId) {
// This should provide feedback
//setAudioPlaybackDevice(<id>[,<callback>[,<callback arg>]]);
// if callback is set it will be called as callback(<bool success/fail>, <device name>, <arg if you supplied it>)
verto.data.call.setAudioPlaybackDevice(speakerId);
});
/**
* Login the user to verto server and
* redirects him to dialpad page.
@ -52,13 +60,19 @@
storage.data.email = verto.data.email;
storage.data.login = verto.data.login;
storage.data.password = verto.data.password;
if (redirect) {
if (storage.data.autoBand) {
verto.testSpeed();
}
if (redirect && storage.data.preview) {
$location.path('/preview');
} else if (redirect) {
$location.path('/dialpad');
}
}
});
};
verto.data.connecting = true;
verto.connect(connectCallback);
};
@ -135,7 +149,7 @@
templateUrl: templateUrl,
controller: controller,
};
angular.extend(options, _options);
var modalInstance = $modal.open(options);
@ -154,7 +168,7 @@
jQuery.material.init();
}
);
return modalInstance;
};
@ -177,6 +191,9 @@
};
function onWSLogin(ev, data) {
if(storage.data.autoBand) {
verto.testSpeed();
}
if(!ws_modalInstance) {
return;
};
@ -454,14 +471,6 @@
$scope.incomingCall = false;
};
$scope.screenshare = function() {
if (verto.data.shareCall) {
verto.screenshareHangup();
return false;
}
verto.screenshare(storage.data.called_number);
};
$scope.play = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File',
function(file) {

@ -4,10 +4,43 @@
angular
.module('vertoControllers')
.controller('MenuController', ['$scope', '$http', '$location',
'verto', 'storage',
function($scope, $http, $location, verto, storage) {
'verto', 'storage', '$rootScope',
function($scope, $http, $location, verto, storage, $rootScope) {
console.debug('Executing MenuController.');
}
$scope.storage = storage;
$rootScope.$on('testSpeed', function(e, data) {
var vidQual = storage.data.vidQual;
var bwp = 4;
$scope.bandDown = data.downKPS;
$scope.bandUp = data.upKPS;
if (data.downKPS < 2000) {
bwp--;
}
if (data.upKPS < 2000) {
bwp--;
}
$scope.iconClass = 'mdi-device-signal-wifi-4-bar green';
if (bwp < 4) {
$scope.iconClass = 'mdi-device-signal-wifi-3-bar yellow';
} else if (bwp < 2) {
$scope.iconClass = 'mdi-device-signal-wifi-1-bar red';
}
verto.videoQuality.forEach(function(vid) {
if (vid.id == vidQual){
$scope.vidRes = vid.label;
}
});
$scope.$apply();
});
}
]);
})();
})();

@ -4,8 +4,8 @@
angular
.module('vertoControllers')
.controller('ModalSettingsController', ['$scope', '$http',
'$location', '$modalInstance', 'storage', 'verto',
function($scope, $http, $location, $modalInstance, storage, verto) {
'$location', '$modalInstance', '$rootScope', 'storage', 'verto',
function($scope, $http, $location, $modalInstance, $rootScope, storage, verto) {
console.debug('Executing ModalSettingsController.');
$scope.storage = storage;
@ -13,8 +13,15 @@
$scope.mydata = angular.copy(storage.data);
$scope.ok = function() {
if ($scope.mydata.selectedSpeaker != storage.data.selectedSpeaker) {
$rootScope.$emit('changedSpeaker', $scope.mydata.selectedSpeaker);
}
storage.changeData($scope.mydata);
verto.data.instance.iceServers(storage.data.useSTUN);
if (storage.data.autoBand) {
$scope.testSpeed();
}
$modalInstance.close('Ok.');
};
@ -26,6 +33,16 @@
return verto.refreshDevices();
};
$scope.testSpeed = function() {
return verto.testSpeed(cb);
function cb(data) {
$scope.mydata.vidQual = storage.data.vidQual;
$scope.speedMsg = 'Up: ' + data.upKPS + ' Down: ' + data.downKPS;
$scope.$apply();
}
};
$scope.resetSettings = function() {
if (confirm('Factory Reset Settings?')) {
storage.factoryReset();
@ -35,11 +52,22 @@
};
};
$scope.checkUseDedRemoteEncoder = function(option) {
if ($scope.mydata.incomingBandwidth != 'default' || $scope.mydata.outgoingBandwidth != 'default') {
$scope.mydata.useDedenc = true;
$scope.checkAutoBand = function(option) {
$scope.mydata.useDedenc = false;
if (!option) {
$scope.mydata.outgoingBandwidth = 'default';
$scope.mydata.incomingBandwidth = 'default';
$scope.mydata.vidQual = 'hd';
} else {
$scope.mydata.testSpeedJoin = true;
}
};
$scope.checkUseDedRemoteEncoder = function(option) {
if (['0', 'default', '5120'].indexOf(option) != -1) {
$scope.mydata.useDedenc = false;
} else {
$scope.mydata.useDedenc = true;
}
};
}

@ -0,0 +1,122 @@
(function() {
'use strict';
angular
.module('vertoControllers')
.controller('PreviewController', ['$rootScope', '$scope',
'$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen',
function($rootScope, $scope, $http, $location, $modal, $timeout, toastr,
verto, storage, prompt, Fullscreen) {
$scope.storage = storage;
console.debug('Executing PreviewController.');
var localVideo = document.getElementById('videopreview');
var volumes = document.querySelector('#mic-meter .volumes').children;
$scope.localVideo = function() {
var constraints = {
mirrored: true,
audio: {
optional: [{ sourceId: storage.data.selectedAudio }]
}
};
if (storage.data.selectedVideo !== 'none') {
constraints.video = {
optional: [{ sourceId: storage.data.selectedVideo }]
};
}
navigator.getUserMedia(constraints, handleMedia, function(err, data) {
});
};
var audioContext = new AudioContext();
var mediaStreamSource = null;
var meter;
var streamObj = {};
function stopMedia(stream) {
if (typeof stream == 'function') {
stream.stop();
} else {
if (stream.active) {
var tracks = stream.getTracks();
tracks.forEach(function(track, index) {
track.stop();
})
}
}
}
function handleMedia(stream) {
if (streamObj) {
stopMedia(streamObj);
}
streamObj = stream;
localVideo.src = window.URL.createObjectURL(stream);
mediaStreamSource = audioContext.createMediaStreamSource(stream);
meter = createAudioMeter(audioContext);
mediaStreamSource.connect(meter);
renderMic();
}
function renderMic() {
// meter.volume;
var n = Math.round(meter.volume * 25);
for(var i = volumes.length -1, j = 0; i >= 0; i--, j++) {
var el = angular.element(volumes[j]);
if (i >= n) el.removeClass('active');
else el.addClass('active');
}
if(!verto.data.call) {
window.requestAnimationFrame(renderMic);
}
}
/**
* TODO: useless?
*/
$scope.refreshDeviceList = function() {
return verto.refreshDevices();
};
$scope.videoCall = function() {
prompt({
title: 'Would you like to activate video for this call?',
message: 'Video will be active during the next calls.'
}).then(function() {
storage.data.videoCall = true;
$scope.callTemplate = 'partials/video_call.html';
});
};
$scope.cbMuteVideo = function(event, data) {
storage.data.mutedVideo = !storage.data.mutedVideo;
}
$scope.cbMuteMic = function(event, data) {
storage.data.mutedMic = !storage.data.mutedMic;
}
$scope.confChangeVideoLayout = function(layout) {
verto.data.conf.setVideoLayout(layout);
};
$scope.endPreview = function() {
localVideo.src = null;
meter.shutdown();
meter.onaudioprocess = null;
stopMedia(streamObj);
$location.path('/dialpad');
storage.data.preview = false;
};
$scope.localVideo();
}
]);
})();

@ -3,10 +3,10 @@
angular
.module('vertoControllers')
.controller('SplashScreenController', ['$scope', '$rootScope', '$location', '$timeout', 'splashscreen', 'prompt', 'verto',
function($scope, $rootScope, $location, $timeout, splashscreen, prompt, verto) {
.controller('SplashScreenController', ['$scope', '$rootScope', '$location', '$timeout', 'storage', 'splashscreen', 'prompt', 'verto',
function($scope, $rootScope, $location, $timeout, storage, splashscreen, prompt, verto) {
console.debug('Executing SplashScreenController.');
$scope.progress_percentage = splashscreen.progress_percentage;
$scope.message = '';
$scope.interrupt_next = false;
@ -18,26 +18,26 @@
link = activity;
}
}
$location.path(link);
}
var checkProgressState = function(current_progress, status, promise, activity, soft, interrupt, message) {
$scope.progress_percentage = splashscreen.calculate(current_progress);
$scope.progress_percentage = splashscreen.calculate(current_progress);
$scope.message = message;
if(interrupt && status == 'error') {
$scope.errors.push(message);
if(!soft) {
redirectTo('', activity);
redirectTo('', activity);
return;
} else {
message = message + '. Continue?';
message = message + '. Continue?';
};
if(!confirm(message)) {
$scope.interrupt_next = true;
};
$scope.interrupt_next = true;
};
};
if($scope.interrupt_next) {
@ -48,7 +48,7 @@
return true;
};
$rootScope.$on('progress.next', function(ev, current_progress, status, promise, activity, soft, interrupt, message) {
$timeout(function() {
if(promise) {
@ -62,11 +62,11 @@
return;
}
if(!checkProgressState(current_progress, status, promise, activity, soft, interrupt, message)) {
return;
}
splashscreen.next();
}, 400);
});
@ -74,7 +74,12 @@
$rootScope.$on('progress.complete', function(ev, current_progress) {
$scope.message = 'Complete';
if(verto.data.connected) {
redirectTo('/dialpad');
if (storage.data.preview) {
$location.path('/preview');
}
else {
$location.path('/dialpad');
}
} else {
redirectTo('/login');
$location.path('/login');

@ -40,7 +40,7 @@ vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', '
verto.data.googlelogin = data.googlelogin;
verto.data.googleclientid = data.googleclientid;
}
angular.extend(verto.data, data);
/**
@ -62,13 +62,13 @@ vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', '
console.debug("auto login per config.json");
verto.data.autologin_done = true;
}
if(verto.data.autologin && storage.data.name.length && storage.data.email.length && storage.data.login.length && storage.data.password.length) {
$rootScope.$emit('config.http.success', data);
$rootScope.$emit('config.http.success', data);
};
return response;
}, function(response) {
$rootScope.$emit('config.http.error', response);
$rootScope.$emit('config.http.error', response);
return response;
});
@ -79,4 +79,3 @@ vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', '
'configure': configure
};
}]);

@ -76,6 +76,12 @@ var bandwidth = [{
}, {
id: '2048',
label: '2mb'
}, {
id: '3196',
label: '3mb'
}, {
id: '4192',
label: '4mb'
}, {
id: '5120',
label: '5mb'
@ -169,7 +175,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
var height = res[1];
if(resolution.width == width && resolution.height == height) {
videoQuality.push(resolution);
videoQuality.push(resolution);
}
});
});
@ -200,14 +206,15 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
refreshDevicesCallback : function refreshDevicesCallback(callback) {
data.videoDevices = [{
id: 'none',
label: 'No Camera'
}];
id: 'none',
label: 'No Camera'
}];
data.shareDevices = [{
id: 'screen',
label: 'Screen'
}];
data.audioDevices = [];
data.speakerDevices = [];
if(!storage.data.selectedShare) {
storage.data.selectedShare = data.shareDevices[0]['id'];
@ -265,6 +272,26 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
label: device.label || device.id
});
}
for (var i in jQuery.verto.audioOutDevices) {
var device = jQuery.verto.audioOutDevices[i];
// Selecting the first source.
if (i == 0 && !storage.data.selectedSpeaker) {
storage.data.selectedSpeaker = device.id;
}
if (!device.label) {
data.speakerDevices.push({
id: 'Speaker ' + i,
label: 'Speaker ' + i
});
continue;
}
data.speakerDevices.push({
id: device.id,
label: device.label || device.id
});
}
console.debug('Devices were refreshed, checking that we have cameras.');
// This means that we cannot use video!
@ -465,13 +492,17 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
if (params.pvtData) {
switch (params.pvtData.action) {
case "conference-liveArray-join":
console.log("conference-liveArray-join");
stopConference();
startConference(v, dialog, params.pvtData);
if (!params.pvtData.screenShare && !params.pvtData.videoOnly) {
console.log("conference-liveArray-join");
stopConference();
startConference(v, dialog, params.pvtData);
}
break;
case "conference-liveArray-part":
console.log("conference-liveArray-part");
stopConference();
if (!params.pvtData.screenShare && !params.pvtData.videoOnly) {
console.log("conference-liveArray-part");
stopConference();
}
break;
}
}
@ -556,10 +587,10 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
// Checking if we have a failed connection attempt before
// connecting again.
if (data.instance && !data.instance.rpcClient.socketReady()) {
clearTimeout(data.instance.rpcClient.to);
data.instance.logout();
data.instance.login();
return;
clearTimeout(data.instance.rpcClient.to);
data.instance.logout();
data.instance.login();
return;
};
data.instance = new jQuery.verto({
login: data.login + '@' + data.hostname,
@ -583,22 +614,23 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
jQuery.verto.unloadJobs.push(function() {
that.reloaded = true;
});
data.instance.deviceParams({
useCamera: storage.data.selectedVideo,
useMic: storage.data.selectedAudio,
onResCheck: that.refreshVideoResolution
});
data.instance.deviceParams({
useCamera: storage.data.selectedVideo,
useSpeak: storage.data.selectedSpeaker,
useMic: storage.data.selectedAudio,
onResCheck: that.refreshVideoResolution
});
}
if (data.mediaPerm) {
ourBootstrap();
} else {
$.FSRTC.checkPerms(ourBootstrap, true, true);
$.FSRTC.checkPerms(ourBootstrap, true, true);
}
},
mediaPerm: function(callback) {
$.FSRTC.checkPerms(callback, true, true);
$.FSRTC.checkPerms(callback, true, true);
},
/**
@ -654,6 +686,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
useVideo: storage.data.useVideo,
useStereo: storage.data.useStereo,
useCamera: storage.data.selectedVideo,
useSpeak: storage.data.selectedSpeaker,
useMic: storage.data.selectedAudio,
dedEnc: storage.data.useDedenc,
mirrorInput: storage.data.mirrorInput,
@ -681,6 +714,12 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
var that = this;
getScreenId(function(error, sourceId, screen_constraints) {
if(error) {
$rootScope.$emit('ScreenShareExtensionStatus', error);
return;
}
var call = data.instance.newCall({
destination_number: destination + '-screen',
caller_id_name: data.name + ' (Screen)',
@ -698,6 +737,24 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
}
});
// Override onStream callback in $.FSRTC instance
call.rtc.options.callbacks.onStream = function(rtc, stream) {
if(stream) {
var StreamTrack = stream.getVideoTracks()[0];
StreamTrack.addEventListener('ended', stopSharing);
// (stream.getVideoTracks()[0]).onended = stopSharing;
}
console.log("screenshare started");
function stopSharing() {
if(that.data.shareCall) {
that.screenshareHangup();
console.log("screenshare ended");
}
}
};
data.shareCall = call;
console.log('shareCall', data);
@ -773,6 +830,39 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora
}
},
/**
* Do speed test.
*
* @param callback
*/
testSpeed: function(cb) {
data.instance.rpcClient.speedTest(1024 * 256, function(e, data) {
var upBand = Math.ceil(data.upKPS * .75),
downBand = Math.ceil(data.downKPS * .75);
if (storage.data.autoBand) {
storage.data.incomingBandwidth = downBand;
storage.data.outgoingBandwidth = upBand;
storage.data.useDedenc = false;
storage.data.vidQual = 'hd';
if (upBand < 512) {
storage.data.vidQual = 'qvga';
}
else if (upBand < 1024) {
storage.data.vidQual = 'vga';
}
}
if(cb) {
cb(data);
}
$rootScope.$emit('testSpeed', data);
});
},
/**
* Mute the microphone for the current call.
*

@ -91,7 +91,7 @@ div#preload { display: none; }
<body>
<div data-role="page" id="page-login" align="center">
<div data-role="header" class="page-header">
FreeSWITCH Verto&trade; Video Transcoding Demo
FreeSWITCH Verto&trade; Video Transcoding Demo
</div>
</div>
@ -108,6 +108,18 @@ div#preload { display: none; }
</div>
<div data-role="page" id="page-bwtest" align="center">
<div data-role="header" class="page-header">
FreeSWITCH Verto&trade; Testing Network Connection
</div>
<h1>Testing Network Connection</h1>
<img src="images/speed.gif"/>
</div>
<div data-role="page" id="page-incall" align="center">
<div data-role="header" id="calltitle" class="pageheader">
Verto&trade; IN CALL
@ -135,6 +147,7 @@ div#preload { display: none; }
<button data-inline="true" id="hupbtn">End Call</button>
<button data-inline="true" id="mutebtn">Toggle Audio Mute</button>
<button data-inline="true" id="localmutebtn">Toggle Local Audio Mute</button>
<button data-inline="true" id="localvidmutebtn">Toggle Local Video Mute</button>
<!-- <button data-inline="true" class="startxferbtn">Transfer</button>-->
<span class="sharediv">
<button data-inline="true" id="sharebtn">Share</button>
@ -248,7 +261,7 @@ div#preload { display: none; }
<div data-role="page" id="page-main" align="center">
<div data-role="header" class="pageheader">
FreeSWITCH Verto&trade; Video Transcoding Demo
FreeSWITCH Verto&trade; Video Transcoding Demo (<span id="bwinfo">*checking*</span>)
</div>
<br>
<center> <table width="1024" border="0">
@ -347,9 +360,9 @@ if ($('#devices').is(':visible')) {
}
</script>
<!--<button data-inline="true" id="showdemo" onclick="toggle_demo();">View Demo Extensions</button>-->
<button data-inline="true" id="showdevices" onclick="toggle_device();">View Device Settings</button>
<button data-inline="true"id="speedbtn">Check Speed</button>
<button data-inline="true"id="logoutbtn">Log Out</button>
<div id="devices" style="border-style:outset;border-width:2px">

@ -6,7 +6,7 @@ function getCodecPayloadType(sdpLine){var pattern=new RegExp('a=rtpmap:(\\d+) \\
function setDefaultCodec(mLine,payload){var elements=mLine.split(' ');var newLine=[];var index=0;for(var i=0;i<elements.length;i++){if(index===3){newLine[index++]=payload;}
if(elements[i]!==payload)newLine[index++]=elements[i];}
return newLine.join(' ');}
$.FSRTC=function(options){this.options=$.extend({useVideo:null,useStereo:false,userData:null,localVideo:null,screenShare:false,useCamera:"any",iceServers:false,videoParams:{},audioParams:{},callbacks:{onICEComplete:function(){},onICE:function(){},onOfferSDP:function(){}},},options);this.enabled=true;this.mediaData={SDP:null,profile:{},candidateList:[]};if(moz){this.constraints={offerToReceiveAudio:true,offerToReceiveVideo:this.options.useVideo?true:false,};}else{this.constraints={optional:[{'DtlsSrtpKeyAgreement':'true'}],mandatory:{OfferToReceiveAudio:true,OfferToReceiveVideo:this.options.useVideo?true:false,}};}
$.FSRTC=function(options){this.options=$.extend({useVideo:null,useStereo:false,userData:null,localVideo:null,screenShare:false,useCamera:"any",iceServers:false,videoParams:{},audioParams:{},callbacks:{onICEComplete:function(){},onICE:function(){},onOfferSDP:function(){}},},options);this.audioEnabled=true;this.videoEnabled=true;this.mediaData={SDP:null,profile:{},candidateList:[]};if(moz){this.constraints={offerToReceiveAudio:true,offerToReceiveVideo:this.options.useVideo?true:false,};}else{this.constraints={optional:[{'DtlsSrtpKeyAgreement':'true'}],mandatory:{OfferToReceiveAudio:true,OfferToReceiveVideo:this.options.useVideo?true:false,}};}
if(self.options.useVideo){self.options.useVideo.style.display='none';}
setCompat();checkCompat();};$.FSRTC.validRes=[];$.FSRTC.prototype.useVideo=function(obj,local){var self=this;if(obj){self.options.useVideo=obj;self.options.localVideo=local;if(moz){self.constraints.offerToReceiveVideo=true;}else{self.constraints.mandatory.OfferToReceiveVideo=true;}}else{self.options.useVideo=null;self.options.localVideo=null;if(moz){self.constraints.offerToReceiveVideo=false;}else{self.constraints.mandatory.OfferToReceiveVideo=false;}}
if(self.options.useVideo){self.options.useVideo.style.display='none';}};$.FSRTC.prototype.useStereo=function(on){var self=this;self.options.useStereo=on;};$.FSRTC.prototype.stereoHack=function(sdp){var self=this;if(!self.options.useStereo){return sdp;}
@ -34,10 +34,14 @@ if(self.localStream){if(typeof self.localStream.stop=='function'){self.localStre
self.localStream=null;}
if(self.options.localVideo){self.options.localVideo.style.display='none';if(moz){self.options.localVideo['mozSrcObject']=null;}else{self.options.localVideo['src']='';}}
if(self.options.localVideoStream){if(typeof self.options.localVideoStream.stop=='function'){self.options.localVideoStream.stop();}else{if(self.localVideoStream.active){var tracks=self.localVideoStream.getTracks();console.error(tracks);tracks.forEach(function(track,index){console.log(track);track.stop();})}}}
if(self.peer){console.log("stopping peer");self.peer.stop();}};$.FSRTC.prototype.getMute=function(){var self=this;return self.enabled;}
if(self.peer){console.log("stopping peer");self.peer.stop();}};$.FSRTC.prototype.getMute=function(){var self=this;return self.audioEnabled;}
$.FSRTC.prototype.setMute=function(what){var self=this;var audioTracks=self.localStream.getAudioTracks();for(var i=0,len=audioTracks.length;i<len;i++){switch(what){case"on":audioTracks[i].enabled=true;break;case"off":audioTracks[i].enabled=false;break;case"toggle":audioTracks[i].enabled=!audioTracks[i].enabled;default:break;}
self.enabled=audioTracks[i].enabled;}
return!self.enabled;}
self.audioEnabled=audioTracks[i].enabled;}
return!self.audioEnabled;}
$.FSRTC.prototype.getVideoMute=function(){var self=this;return self.videoEnabled;}
$.FSRTC.prototype.setVideoMute=function(what){var self=this;var videoTracks=self.localStream.getVideoTracks();for(var i=0,len=videoTracks.length;i<len;i++){switch(what){case"on":videoTracks[i].enabled=true;break;case"off":videoTracks[i].enabled=false;break;case"toggle":videoTracks[i].enabled=!videoTracks[i].enabled;default:break;}
self.videoEnabled=videoTracks[i].enabled;}
return!self.videoEnabled;}
$.FSRTC.prototype.createAnswer=function(params){var self=this;self.type="answer";self.remoteSDP=params.sdp;console.debug("inbound sdp: ",params.sdp);function onSuccess(stream){self.localStream=stream;self.peer=RTCPeerConnection({type:self.type,attachStream:self.localStream,onICE:function(candidate){return onICE(self,candidate);},onICEComplete:function(){return onICEComplete(self);},onRemoteStream:function(stream){return onRemoteStream(self,stream);},onICESDP:function(sdp){return onICESDP(self,sdp);},onChannelError:function(e){return onChannelError(self,e);},constraints:self.constraints,iceServers:self.options.iceServers,offerSDP:{type:"offer",sdp:self.remoteSDP}});onStreamSuccess(self);}
function onError(e){onStreamError(self,e);}
var mediaParams=getMediaParams(self);console.log("Audio constraints",mediaParams.audio);console.log("Video constraints",mediaParams.video);if(self.options.useVideo&&self.options.localVideo){getUserMedia({constraints:{audio:false,video:{mandatory:self.options.videoParams,optional:[]},},localVideo:self.options.localVideo,onsuccess:function(e){self.options.localVideoStream=e;console.log("local video ready");},onerror:function(e){console.error("local video error!");}});}
@ -95,7 +99,9 @@ $.FSRTC.getValidRes=function(cam,func){var used=[];var cached=localStorage.getIt
return func?func(cache):null;}
$.FSRTC.validRes=[];resI=0;checkRes(cam,func);}
$.FSRTC.checkPerms=function(runtime,check_audio,check_video){getUserMedia({constraints:{audio:check_audio,video:check_video,},onsuccess:function(e){e.getTracks().forEach(function(track){track.stop();});console.info("media perm init complete");if(runtime){setTimeout(runtime,100,true);}},onerror:function(e){if(check_video&&check_audio){console.error("error, retesting with audio params only");return $.FSRTC.checkPerms(runtime,check_audio,false);}
console.error("media perm init error");if(runtime){runtime(false)}}});}})(jQuery);(function($){$.JsonRpcClient=function(options){var self=this;this.options=$.extend({ajaxUrl:null,socketUrl:null,onmessage:null,login:null,passwd:null,sessid:null,loginParams:null,userVariables:null,getSocket:function(onmessage_cb){return self._getSocket(onmessage_cb);}},options);self.ws_cnt=0;this.wsOnMessage=function(event){self._wsOnMessage(event);};};$.JsonRpcClient.prototype._ws_socket=null;$.JsonRpcClient.prototype._ws_callbacks={};$.JsonRpcClient.prototype._current_id=1;$.JsonRpcClient.prototype.call=function(method,params,success_cb,error_cb){if(!params){params={};}
console.error("media perm init error");if(runtime){runtime(false)}}});}})(jQuery);(function($){$.JsonRpcClient=function(options){var self=this;this.options=$.extend({ajaxUrl:null,socketUrl:null,onmessage:null,login:null,passwd:null,sessid:null,loginParams:null,userVariables:null,getSocket:function(onmessage_cb){return self._getSocket(onmessage_cb);}},options);self.ws_cnt=0;this.wsOnMessage=function(event){self._wsOnMessage(event);};};$.JsonRpcClient.prototype._ws_socket=null;$.JsonRpcClient.prototype._ws_callbacks={};$.JsonRpcClient.prototype._current_id=1;$.JsonRpcClient.prototype.speedTest=function(bytes,cb){var socket=this.options.getSocket(this.wsOnMessage);if(socket!==null){this.speedCB=cb;this.speedBytes=bytes;socket.send("#SPU "+bytes);var loops=bytes/1024;var rem=bytes%1024;var i;var data=new Array(1024).join(".");for(i=0;i<loops;i++){socket.send("#SPB "+data);}
if(rem){socket.send("#SPB "+data);}
socket.send("#SPE");}};$.JsonRpcClient.prototype.call=function(method,params,success_cb,error_cb){if(!params){params={};}
if(this.options.sessid){params.sessid=this.options.sessid;}
var request={jsonrpc:'2.0',method:method,params:params,id:this._current_id++};if(!success_cb){success_cb=function(e){console.log("Success: ",e);};}
if(!error_cb){error_cb=function(e){console.log("Error: ",e);};}
@ -113,7 +119,9 @@ console.error("Websocket Lost "+self.ws_cnt+" sleep: "+self.ws_sleep+"msec");sel
self.ws_sleep=1000;self.ws_cnt=0;if(self.options.onWSConnect){self.options.onWSConnect(self);}
var req;while((req=$.JsonRpcClient.q.pop())){self._ws_socket.send(req);}};}}
return self._ws_socket?true:false;};$.JsonRpcClient.prototype._getSocket=function(onmessage_cb){if(this.options.socketUrl===null||!("WebSocket"in window))return null;this.connectSocket(onmessage_cb);return this._ws_socket;};$.JsonRpcClient.q=[];$.JsonRpcClient.prototype._wsCall=function(socket,request,success_cb,error_cb){var request_json=$.toJSON(request);if(socket.readyState<1){self=this;$.JsonRpcClient.q.push(request_json);}else{socket.send(request_json);}
if('id'in request&&typeof success_cb!=='undefined'){this._ws_callbacks[request.id]={request:request_json,request_obj:request,success_cb:success_cb,error_cb:error_cb};}};$.JsonRpcClient.prototype._wsOnMessage=function(event){var response;try{response=$.parseJSON(event.data);if(typeof response==='object'&&'jsonrpc'in response&&response.jsonrpc==='2.0'){if('result'in response&&this._ws_callbacks[response.id]){var success_cb=this._ws_callbacks[response.id].success_cb;delete this._ws_callbacks[response.id];success_cb(response.result,this);return;}else if('error'in response&&this._ws_callbacks[response.id]){var error_cb=this._ws_callbacks[response.id].error_cb;var orig_req=this._ws_callbacks[response.id].request;if(!self.authing&&response.error.code==-32000&&self.options.login&&self.options.passwd){self.authing=true;this.call("login",{login:self.options.login,passwd:self.options.passwd,loginParams:self.options.loginParams,userVariables:self.options.userVariables},this._ws_callbacks[response.id].request_obj.method=="login"?function(e){self.authing=false;console.log("logged in");delete self._ws_callbacks[response.id];if(self.options.onWSLogin){self.options.onWSLogin(true,self);}}:function(e){self.authing=false;console.log("logged in, resending request id: "+response.id);var socket=self.options.getSocket(self.wsOnMessage);if(socket!==null){socket.send(orig_req);}
if('id'in request&&typeof success_cb!=='undefined'){this._ws_callbacks[request.id]={request:request_json,request_obj:request,success_cb:success_cb,error_cb:error_cb};}};$.JsonRpcClient.prototype._wsOnMessage=function(event){var response;if(event.data[0]=="#"&&event.data[1]=="S"&&event.data[2]=="P"){if(event.data[3]=="U"){this.up_dur=parseInt(event.data.substring(4));}else if(this.speedCB&&event.data[3]=="D"){this.down_dur=parseInt(event.data.substring(4));var up_kps=(((this.speedBytes*8)/(this.up_dur/1000))/1024).toFixed(0);var down_kps=(((this.speedBytes*8)/(this.down_dur/1000))/1024).toFixed(0);console.info("Speed Test: Up: "+up_kps+" Down: "+down_kps);this.speedCB(event,{upDur:this.up_dur,downDur:this.down_dur,upKPS:up_kps,downKPS:down_kps});this.speedCB=null;}
return;}
try{response=$.parseJSON(event.data);if(typeof response==='object'&&'jsonrpc'in response&&response.jsonrpc==='2.0'){if('result'in response&&this._ws_callbacks[response.id]){var success_cb=this._ws_callbacks[response.id].success_cb;delete this._ws_callbacks[response.id];success_cb(response.result,this);return;}else if('error'in response&&this._ws_callbacks[response.id]){var error_cb=this._ws_callbacks[response.id].error_cb;var orig_req=this._ws_callbacks[response.id].request;if(!self.authing&&response.error.code==-32000&&self.options.login&&self.options.passwd){self.authing=true;this.call("login",{login:self.options.login,passwd:self.options.passwd,loginParams:self.options.loginParams,userVariables:self.options.userVariables},this._ws_callbacks[response.id].request_obj.method=="login"?function(e){self.authing=false;console.log("logged in");delete self._ws_callbacks[response.id];if(self.options.onWSLogin){self.options.onWSLogin(true,self);}}:function(e){self.authing=false;console.log("logged in, resending request id: "+response.id);var socket=self.options.getSocket(self.wsOnMessage);if(socket!==null){socket.send(orig_req);}
if(self.options.onWSLogin){self.options.onWSLogin(true,self);}},function(e){console.log("error logging in, request id:",response.id);delete self._ws_callbacks[response.id];error_cb(response.error,this);if(self.options.onWSLogin){self.options.onWSLogin(false,self);}});return;}
delete this._ws_callbacks[response.id];error_cb(response.error,this);return;}}}catch(err){console.log("ERROR: "+err);return;}
if(typeof this.options.onmessage==='function'){event.eventData=response;if(!event.eventData){event.eventData={};}
@ -193,17 +201,21 @@ break;case"clear":la.clear();break;case"reorder":la.reorder(packet.wireSerno,pac
break;}};if(la.context){binding=la.verto.subscribe(la.context,{handler:eventHandler,userData:la,subParams:config.subParams});}
la.destroy=function(){la._clear();la.verto.unsubscribe(binding);};la.sendCommand=function(cmd,obj){var self=la;self.broadcast(self.context,{liveArray:{command:cmd,context:self.context,name:self.name,obj:obj}});};la.bootstrap=function(obj){var self=la;la.sendCommand("bootstrap",obj);};la.changepage=function(obj){var self=la;self.clear();self.broadcast(self.context,{liveArray:{command:"changepage",context:la.context,name:la.name,obj:obj}});};la.heartbeat=function(obj){var self=la;var callback=function(){self.heartbeat.call(self,obj);};self.broadcast(self.context,{liveArray:{command:"heartbeat",context:self.context,name:self.name,obj:obj}});self.hb_pid=setTimeout(callback,30000);};la.bootstrap(la.user_obj);};$.verto.liveTable=function(verto,context,name,jq,config){var dt;var la=new $.verto.liveArray(verto,context,name,{subParams:config.subParams});var lt=this;lt.liveArray=la;lt.dataTable=dt;lt.verto=verto;lt.destroy=function(){if(dt){dt.fnDestroy();}
if(la){la.destroy();}
dt=null;la=null;};la.onErr=function(obj,args){console.error("Error: ",obj,args);};la.onChange=function(obj,args){var index=0;var iserr=0;if(!dt){if(!config.aoColumns){if(args.action!="init"){return;}
dt=null;la=null;};la.onErr=function(obj,args){console.error("Error: ",obj,args);};function genRow(data){if(typeof(data[4])==="string"&&data[4].indexOf("{")>-1){var tmp=$.parseJSON(data[4]);data[4]=tmp.oldStatus;data[5]=null;}
return data;}
function genArray(obj){var data=obj.asArray();for(var i in data){data[i]=genRow(data[i]);}
return data;}
la.onChange=function(obj,args){var index=0;var iserr=0;if(!dt){if(!config.aoColumns){if(args.action!="init"){return;}
config.aoColumns=[];for(var i in args.data){config.aoColumns.push({"sTitle":args.data[i]});}}
dt=jq.dataTable(config);}
if(dt&&(args.action=="del"||args.action=="modify")){index=args.index;if(index===undefined&&args.key){index=la.indexOf(args.key);}
if(index===undefined){console.error("INVALID PACKET Missing INDEX\n",args);return;}}
if(config.onChange){config.onChange(obj,args);}
try{switch(args.action){case"bootObj":if(!args.data){console.error("missing data");return;}
dt.fnClearTable();dt.fnAddData(obj.asArray());dt.fnAdjustColumnSizing();break;case"add":if(!args.data){console.error("missing data");return;}
if(args.redraw>-1){dt.fnClearTable();dt.fnAddData(obj.asArray());}else{dt.fnAddData(args.data);}
dt.fnClearTable();dt.fnAddData(genArray(obj));dt.fnAdjustColumnSizing();break;case"add":if(!args.data){console.error("missing data");return;}
if(args.redraw>-1){dt.fnClearTable();dt.fnAddData(genArray(obj));}else{dt.fnAddData(genRow(args.data));}
dt.fnAdjustColumnSizing();break;case"modify":if(!args.data){return;}
dt.fnUpdate(args.data,index);dt.fnAdjustColumnSizing();break;case"del":dt.fnDeleteRow(index);dt.fnAdjustColumnSizing();break;case"clear":dt.fnClearTable();break;case"reorder":dt.fnClearTable();dt.fnAddData(obj.asArray());break;case"hide":jq.hide();break;case"show":jq.show();break;}}catch(err){console.error("ERROR: "+err);iserr++;}
dt.fnUpdate(genRow(args.data),index);dt.fnAdjustColumnSizing();break;case"del":dt.fnDeleteRow(index);dt.fnAdjustColumnSizing();break;case"clear":dt.fnClearTable();break;case"reorder":dt.fnClearTable();dt.fnAddData(genArray(obj));break;case"hide":jq.hide();break;case"show":jq.show();break;}}catch(err){console.error("ERROR: "+err);iserr++;}
if(iserr){obj.errs++;if(obj.errs<3){obj.bootstrap(obj.user_obj);}}else{obj.errs=0;}};la.onChange(la,{action:"init"});};var CONFMAN_SERNO=1;$.verto.conf=function(verto,params){var conf=this;conf.params=$.extend({dialog:null,hasVid:false,laData:null,onBroadcast:null,onLaChange:null,onLaRow:null},params);conf.verto=verto;conf.serno=CONFMAN_SERNO++;createMainModeratorMethods();verto.subscribe(conf.params.laData.modChannel,{handler:function(v,e){if(conf.params.onBroadcast){conf.params.onBroadcast(verto,conf,e.data);}}});verto.subscribe(conf.params.laData.chatChannel,{handler:function(v,e){if(typeof(conf.params.chatCallback)==="function"){conf.params.chatCallback(v,e);}}});};$.verto.conf.prototype.modCommand=function(cmd,id,value){var conf=this;conf.verto.rpcClient.call("verto.broadcast",{"eventChannel":conf.params.laData.modChannel,"data":{"application":"conf-control","command":cmd,"id":id,"value":value}});};$.verto.conf.prototype.destroy=function(){var conf=this;conf.destroyed=true;conf.params.onBroadcast(conf.verto,conf,'destroy');if(conf.params.laData.modChannel){conf.verto.unsubscribe(conf.params.laData.modChannel);}
if(conf.params.laData.chatChannel){conf.verto.unsubscribe(conf.params.laData.chatChannel);}};function createMainModeratorMethods(){$.verto.conf.prototype.listVideoLayouts=function(){this.modCommand("list-videoLayouts",null,null);};$.verto.conf.prototype.play=function(file){this.modCommand("play",null,file);};$.verto.conf.prototype.stop=function(){this.modCommand("stop",null,"all");};$.verto.conf.prototype.record=function(file){this.modCommand("recording",null,["start",file]);};$.verto.conf.prototype.stopRecord=function(){this.modCommand("recording",null,["stop","all"]);};$.verto.conf.prototype.snapshot=function(file){if(!this.params.hasVid){throw'Conference has no video';}
this.modCommand("vid-write-png",null,file);};$.verto.conf.prototype.setVideoLayout=function(layout){if(!this.params.hasVid){throw'Conference has no video';}
@ -245,12 +257,17 @@ if(rtc.type=="offer"){if(dialog.state==$.verto.enum.state.active){dialog.setStat
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;}
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;}
$.verto.dialog.prototype.setAudioDevice=function(sinkId,callback,arg){var dialog=this;var element=dialog.audioStream;if(typeof element.sinkId!=='undefined'){console.info("Dialog: "+dialog.callID+" Setting speaker:",element,find_name(sinkId));element.setSinkId(sinkId).then(function(){console.log("Dialog: "+dialog.callID+' Success, audio output device attached: '+sinkId);if(callback){callback(true,arg);}}).catch(function(error){var errorMessage=error;if(error.name==='SecurityError'){errorMessage="Dialog: "+dialog.callID+' You need to use HTTPS for selecting audio output '+'device: '+error;}
if(callback){callback(false,arg);}
console.error(errorMessage);});}else{console.warn("Dialog: "+dialog.callID+' Browser does not support output device selection.');if(callback){callback(false,arg);}}}
$.verto.dialog.prototype.setState=function(state){var dialog=this;if(dialog.state==$.verto.enum.state.ringing){dialog.stopRinging();}
if(dialog.state==state||!checkStateChange(dialog.state,state)){console.error("Dialog "+dialog.callID+": INVALID state change from "+dialog.state.name+" to "+state.name);dialog.hangup();return false;}
console.log("Dialog "+dialog.callID+": state change from "+dialog.state.name+" to "+state.name);dialog.lastState=dialog.state;dialog.state=state;if(!dialog.causeCode){dialog.causeCode=16;}
if(!dialog.cause){dialog.cause="NORMAL CLEARING";}
if(dialog.callbacks.onDialogState){dialog.callbacks.onDialogState(this);}
switch(dialog.state){case $.verto.enum.state.early:case $.verto.enum.state.active:var speaker=dialog.useSpeak;console.info("Using Speaker: ",speaker);if(speaker&&speaker!=="any"){var videoElement=dialog.audioStream;setTimeout(function(){console.info("Setting speaker:",videoElement,speaker);attachSinkId(videoElement,speaker);},500);}
switch(dialog.state){case $.verto.enum.state.early:case $.verto.enum.state.active:var speaker=dialog.useSpeak;console.info("Using Speaker: ",speaker);if(speaker&&speaker!=="any"){setTimeout(function(){dialog.setAudioDevice(speaker);},500);}
break;case $.verto.enum.state.trying:setTimeout(function(){if(dialog.state==$.verto.enum.state.trying){dialog.setState($.verto.enum.state.hangup);}},30000);break;case $.verto.enum.state.purge:dialog.setState($.verto.enum.state.destroy);break;case $.verto.enum.state.hangup:if(dialog.lastState.val>$.verto.enum.state.requesting.val&&dialog.lastState.val<$.verto.enum.state.hangup.val){dialog.sendMethod("verto.bye",{});}
dialog.setState($.verto.enum.state.destroy);break;case $.verto.enum.state.destroy:delete dialog.verto.dialogs[dialog.callID];if(dialog.params.screenShare){dialog.rtc.stopPeer();}else{dialog.rtc.stop();}
break;}
@ -261,7 +278,7 @@ 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.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.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={};}

@ -197,31 +197,43 @@ function check_vid() {
return use_vid;
}
// Attach audio output device to video element using device/sink ID.
function attachSinkId(element, sinkId) {
if (typeof element.sinkId !== 'undefined') {
element.setSinkId(sinkId)
.then(function() {
console.log('Success, audio output device attached: ' + sinkId);
})
.catch(function(error) {
var errorMessage = error;
if (error.name === 'SecurityError') {
errorMessage = 'You need to use HTTPS for selecting audio output ' +
'device: ' + error;
}
console.error(errorMessage);
// Jump back to first output device in the list as it's the default.
//audioOutputSelect.selectedIndex = 0;
});
} else {
console.warn('Browser does not support output device selection.');
}
function do_speed_test(fn)
{
goto_page("bwtest");
vertoHandle.rpcClient.speedTest(1024 * 256, function(e, obj) {
//console.error("Up: " + obj.upKPS, "Down: ", obj.downKPS);
var vid = "default";
//if (outgoingBandwidth === "default") {
outgoingBandwidth = Math.ceil(obj.upKPS * .75).toString();
$("#vqual_hd").prop("checked", true);
vid = "1280x720";
if (outgoingBandwidth < 1024) {
$("#vqual_vga").prop("checked", true);
vid = "640x480";
}
if (outgoingBandwidth < 512) {
$("#vqual_qvga").prop("checked", true);
vid = "320x240";
}
//}
if (incomingBandwidth === "default") {
incomingBandwidth = Math.ceil(obj.downKPS * .75).toString();
}
console.info(outgoingBandwidth, incomingBandwidth);
$("#bwinfo").html("<b>Bandwidth: " + "Up: " + obj.upKPS + " Down: " + obj.downKPS + " Vid: " + vid + "</b>");
if (fn) {
fn();
}
});
}
function messageTextToJQ(body) {
// Builds a jQuery collection from body text, linkifies http/https links, imageifies http/https links to images, and doesn't allow script injection
@ -559,24 +571,34 @@ var callbacks = {
ringing = false;
if (success) {
online(true);
/*
verto.subscribe("presence", {
handler: function(v, e) {
console.error("PRESENCE:", e);
}
});
*/
do_speed_test(function() {
online(true);
goto_page("main");
if (!window.location.hash) {
goto_page("main");
}
$("input[type='radio']").checkboxradio("refresh");
$("input[type='checkbox']").checkboxradio("refresh");
/*
verto.subscribe("presence", {
handler: function(v, e) {
console.error("PRESENCE:", e);
}
});
*/
if (!window.location.hash) {
goto_page("main");
}
if (autocall) {
autocall = false;
docall();
}
});
if (autocall) {
autocall = false;
docall();
}
} else {
goto_page("main");
goto_dialog("login-error");
@ -667,6 +689,17 @@ $("#localmutebtn").click(function() {
});
$("#localvidmutebtn").click(function() {
var muted = cur_call.setVideoMute("toggle");
if (muted) {
display("Talking to: " + cur_call.cidString() + " [VIDEO LOCALLY MUTED]");
} else {
display("Talking to: " + cur_call.cidString());
}
});
$("#vmutebtn").click(function() {
cur_call.dtmf("*0");
});
@ -765,7 +798,7 @@ function docall() {
$("#main_info").html("Trying");
check_vid_res();
console.error(outgoingBandwidth, incomingBandwidth);
cur_call = vertoHandle.newCall({
destination_number: $("#ext").val(),
caller_id_name: $("#cidname").val(),
@ -993,8 +1026,10 @@ function refresh_devices()
$("#useshare").selectmenu('refresh', true);
//$("input[type='radio']).checkboxradio({});
$("input[type='radio']").checkboxradio("refresh");
$("input[type='checkbox']").checkboxradio("refresh");
//$("input[type='radio']").checkboxradio("refresh");
//$("input[type='checkbox']").checkboxradio("refresh");
//console.error($("#usecamera").find(":selected").val());
//$.FSRTC.getValidRes($("#usecamera").find(":selected").val(), undefined);
@ -1021,7 +1056,7 @@ function refresh_devices()
function init() {
cur_call = null;
goto_page("main");
goto_page("bwtest");
$("#usecamera").selectmenu({});
$("#usemic").selectmenu({});
@ -1480,6 +1515,13 @@ function init() {
$("#errordisplay").html("");
});
$("#speedbtn").click(function() {
do_speed_test(function() {
goto_page("main");
});
$("#errordisplay").html("");
});
$("#loginbtn").click(function() {
online(false);
vertoHandle.loginData({

@ -1,6 +1,6 @@
LOCAL_CFLAGS=`python ./python-config --includes`
LOCAL_LDFLAGS=`python ./python-config --ldflags`
SITE_DIR=$(DESTDIR)/`python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"`
SITE_DIR=$(DESTDIR)/`python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)"`
all: _ESL.so
@ -14,6 +14,7 @@ _ESL.so: esl_wrap.o
$(CXX) $(SOLINK) esl_wrap.o $(MYLIB) $(LOCAL_LDFLAGS) -o _ESL.so -L. $(LIBS)
install: _ESL.so
mkdir -p $(SITE_DIR)
install -m 755 _ESL.so $(SITE_DIR)
install -m 755 ESL.py $(SITE_DIR)

@ -150,6 +150,13 @@ static char *print_number(cJSON *item)
return str;
}
#define is_hexdigit(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
static int scan_unicode(const char *ptr, unsigned int *uc)
{
if (!is_hexdigit(*(ptr)) || !is_hexdigit(*(ptr+1)) || !is_hexdigit(*(ptr+2)) || !is_hexdigit(*(ptr+3))) return -1;
return sscanf(ptr, "%4x", uc);
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
@ -177,8 +184,7 @@ static const char *parse_string(cJSON *item,const char *str)
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
if (sscanf(ptr+1,"%4x",&uc) < 1) break;
if (scan_unicode(ptr+1, &uc) < 1) break;
ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
@ -186,7 +192,7 @@ static const char *parse_string(cJSON *item,const char *str)
if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
{
if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
if (sscanf(ptr+3,"%4x",&uc2) < 1) break;
if (scan_unicode(ptr+3,&uc2) < 1) break;
ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);

@ -571,5 +571,63 @@ with the signaling protocols that you can run on top of your I/O interfaces.
<param name="context" value="default"/>
</span>
</pritap_spans>
<!--
GSM spans (libwat must be installed when configuring the freetdm build)
-->
<gsm_spans>
<span name="gsm01">
<!-- where to send inbound calls to -->
<param name="dialplan" value="XML" />
<param name="context" value="module1" />
<!--
GSM module type
Accepted values: "telit", "telit-gc864", "telit-he910", "telit-cc864", "telit-de910", "motorola"
-->
<param name="moduletype" value="telit-gc864" />
<!--
Debug mask (accepted values: all, uart_raw, uart_raw, call_state, span_state, at_parse, at_handle, sms_encode, sms_decode, none)
comma-separated values are accepted to combine those levels
-->
<param name="debug" value="all" />
<!--
Whether to enable HW DTMF in the hardware module
Accepted values: true, generate, detect, false
-->
<param name="hwdtmf" value="true" />
<!--
This enables conditional forwarding on module startup
-->
<!-- <param name="conditional-forwarding-prefix" value="*71" /> -->
<!-- <param name="conditional-forwarding-number" value="123456789" /> -->
<!--
This enables immediate forwarding logic to simulate hunt groups
The syntax for immediate-forwarding-numbers ia a comma-separated
list of elements in the form [<span-name>:]<number>
if span-name is specified, the span will be checked for availability
before enabling forwarding to that span number
The span-name must be a defined freetdm span name
If the span-name is not specified then only first number specified is used for
forwarding whenever this span is busy
-->
<!-- <param name="immediate-forwarding-prefix" value="*72" /> -->
<!-- <param name="immediate-forwarding-numbers" value="gsm02:123456789" /> -->
<!-- Number to dial to disable forwarding when the call ends (if immediate forwarding was enabled) -->
<!-- <param name="disable-forwarding-number" value="*73" /> -->
<!-- Startup raw AT commands to run, you can repeat this parameter as many times as needed -->
<!-- <param name="startup-command" value="AT$GPSP=1" /> -->
<!-- <param name="startup-command" value="AT$GPSAT=1" /> -->
</span>
</gsm_spans>
</configuration>

@ -4936,6 +4936,7 @@ FTDM_CLI_DECLARE(ftdm_cmd_trace)
uint32_t chan_id = 0;
uint32_t span_id = 0;
uint32_t chan_count = 0;
ftdm_status_t status;
ftdm_span_t *span = NULL;
ftdm_channel_t *chan = NULL;
@ -4964,17 +4965,39 @@ FTDM_CLI_DECLARE(ftdm_cmd_trace)
if (chan_id) {
chan = ftdm_span_get_channel(span, chan_id);
snprintf(tracepath, sizeof(tracepath), "%s-in-s%dc%d", argv[1], span_id, chan_id);
ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath);
status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath);
if (status != FTDM_SUCCESS) {
stream->write_function(stream, "-ERR failed to enable input trace at path %s\n", tracepath);
goto end;
}
snprintf(tracepath, sizeof(tracepath), "%s-out-s%dc%d", argv[1], span_id, chan_id);
ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath);
status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath);
if (status != FTDM_SUCCESS) {
stream->write_function(stream, "-ERR failed to enable output trace at path %s\n", tracepath);
ftdm_channel_command(chan, FTDM_COMMAND_TRACE_END_ALL, NULL);
goto end;
}
} else {
for (i = 1; i <= chan_count; i++) {
chan = ftdm_span_get_channel(span, i);
snprintf(tracepath, sizeof(tracepath), "%s-in-s%dc%d", argv[1], span_id, i);
ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath);
status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_INPUT, tracepath);
if (status != FTDM_SUCCESS) {
stream->write_function(stream, "-ERR failed to enable input trace at path %s\n", tracepath);
goto end;
}
snprintf(tracepath, sizeof(tracepath), "%s-out-s%dc%d", argv[1], span_id, i);
ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath);
status = ftdm_channel_command(chan, FTDM_COMMAND_TRACE_OUTPUT, tracepath);
if (status != FTDM_SUCCESS) {
stream->write_function(stream, "-ERR failed to enable output trace at path %s\n", tracepath);
ftdm_channel_command(chan, FTDM_COMMAND_TRACE_END_ALL, NULL);
goto end;
}
}
}
stream->write_function(stream, "+OK trace enabled with prefix path %s\n", argv[1]);

@ -724,11 +724,22 @@ static ftdm_status_t ftdm_span_destroy(ftdm_span_t *span)
ftdm_queue_destroy(&span->pendingchans);
}
if (span->pendingsignals) {
ftdm_sigmsg_t *sigmsg = NULL;
while ((sigmsg = ftdm_queue_dequeue(span->pendingsignals))) {
ftdm_sigmsg_free(&sigmsg);
}
ftdm_queue_destroy(&span->pendingsignals);
}
ftdm_mutex_unlock(span->mutex);
ftdm_mutex_destroy(&span->mutex);
ftdm_safe_free(span->signal_data);
/* Give the span a chance to destroy its own signaling data */
if (span->destroy) {
span->destroy(span);
} else if (span->signal_data) {
/* We take care of their dirty business ... */
ftdm_free(span->signal_data);
}
return status;
}
@ -5520,7 +5531,8 @@ FT_DECLARE(ftdm_io_interface_t *) ftdm_global_get_io_interface(const char *iotyp
FT_DECLARE(int) ftdm_load_module(const char *name)
{
ftdm_dso_lib_t lib;
int count = 0, x = 0;
int count = 0;
ftdm_bool_t load_proceed = FTDM_TRUE;
char path[512] = "";
char *err;
ftdm_module_t *mod;
@ -5544,11 +5556,11 @@ FT_DECLARE(int) ftdm_load_module(const char *name)
if (mod->io_load(&interface1) != FTDM_SUCCESS || !interface1 || !interface1->name) {
ftdm_log(FTDM_LOG_ERROR, "Error loading %s\n", path);
load_proceed = FTDM_FALSE;
} else {
ftdm_log(FTDM_LOG_INFO, "Loading IO from %s [%s]\n", path, interface1->name);
if (ftdm_global_add_io_interface(interface1) == FTDM_SUCCESS) {
process_module_config(interface1);
x++;
}
}
}
@ -5556,13 +5568,13 @@ FT_DECLARE(int) ftdm_load_module(const char *name)
if (mod->sig_load) {
if (mod->sig_load() != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Error loading %s\n", path);
load_proceed = FTDM_FALSE;
} else {
ftdm_log(FTDM_LOG_INFO, "Loading SIG from %s\n", path);
x++;
}
}
if (x) {
if (load_proceed) {
char *p;
mod->lib = lib;
ftdm_set_string(mod->path, path);
@ -5583,7 +5595,7 @@ FT_DECLARE(int) ftdm_load_module(const char *name)
}
ftdm_mutex_unlock(globals.mutex);
} else {
ftdm_log(FTDM_LOG_ERROR, "Unloading %s\n", path);
ftdm_log(FTDM_LOG_ERROR, "Errors during module load. Unloading %s\n", path);
ftdm_dso_destroy(&lib);
}
@ -6114,7 +6126,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_trigger_signals(const ftdm_span_t *span)
return FTDM_SUCCESS;
}
static void execute_safety_hangup(void *data)
{
ftdm_channel_t *fchan = data;

File diff suppressed because it is too large Load Diff

@ -1394,13 +1394,11 @@ static FIO_GET_ALARMS_FUNCTION(wanpipe_get_alarms)
}
if (!ftdmchan->alarm_flags) {
if (FTDM_IS_DIGITAL_CHANNEL(ftdmchan)) {
ftdm_channel_hw_link_status_t sangoma_status = 0;
/* there is a bug in wanpipe where alarms were not properly set when they should be
* on at application startup, until that is fixed we check the link status here too */
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_LINK_STATUS, &sangoma_status);
ftdmchan->alarm_flags = sangoma_status == FTDM_HW_LINK_DISCONNECTED ? FTDM_ALARM_RED : FTDM_ALARM_NONE;
}
/* there is a bug in wanpipe where alarms were not properly set when they should be
* on at application startup, until that is fixed we check the link status here too */
ftdm_channel_hw_link_status_t sangoma_status = 0;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_LINK_STATUS, &sangoma_status);
ftdmchan->alarm_flags = sangoma_status == FTDM_HW_LINK_DISCONNECTED ? FTDM_ALARM_RED : FTDM_ALARM_NONE;
}
if (alarms) {
@ -1425,33 +1423,33 @@ static __inline__ ftdm_status_t wanpipe_channel_process_event(ftdm_channel_t *fc
case WP_API_EVENT_LINK_STATUS:
{
if (FTDM_IS_DIGITAL_CHANNEL(fchan)) {
switch(tdm_api->wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
/* *event_id = FTDM_OOB_ALARM_CLEAR; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Ignoring wanpipe link connected event\n");
break;
default:
/* *event_id = FTDM_OOB_ALARM_TRAP; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Ignoring wanpipe link disconnected event\n");
break;
};
/* The WP_API_EVENT_ALARM event should be used to clear alarms */
*event_id = FTDM_OOB_NOOP;
} else {
switch(tdm_api->wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
/* *event_id = FTDM_OOB_ALARM_CLEAR; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Using analog link connected event as alarm clear\n");
*event_id = FTDM_OOB_ALARM_CLEAR;
fchan->alarm_flags = FTDM_ALARM_NONE;
break;
default:
/* *event_id = FTDM_OOB_ALARM_TRAP; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Using analog link disconnected event as alarm trap\n");
*event_id = FTDM_OOB_ALARM_TRAP;
fchan->alarm_flags = FTDM_ALARM_RED;
break;
};
switch(tdm_api->wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
/* *event_id = FTDM_OOB_ALARM_CLEAR; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Ignoring wanpipe link connected event\n");
break;
default:
/* *event_id = FTDM_OOB_ALARM_TRAP; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Ignoring wanpipe link disconnected event\n");
break;
}
/* The WP_API_EVENT_ALARM event should be used to clear alarms */
*event_id = FTDM_OOB_NOOP;
} else {
switch(tdm_api->wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
/* *event_id = FTDM_OOB_ALARM_CLEAR; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Using analog link connected event as alarm clear\n");
*event_id = FTDM_OOB_ALARM_CLEAR;
fchan->alarm_flags = FTDM_ALARM_NONE;
break;
default:
/* *event_id = FTDM_OOB_ALARM_TRAP; */
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "Using analog link disconnected event as alarm trap\n");
*event_id = FTDM_OOB_ALARM_TRAP;
fchan->alarm_flags = FTDM_ALARM_RED;
break;
}
}
}
break;

@ -512,6 +512,7 @@ struct ftdm_span {
fio_channel_request_t channel_request;
ftdm_span_start_t start;
ftdm_span_stop_t stop;
ftdm_span_destroy_t destroy;
ftdm_channel_sig_read_t sig_read;
ftdm_channel_sig_write_t sig_write;
ftdm_channel_sig_dtmf_t sig_queue_dtmf;

@ -348,6 +348,7 @@ typedef struct ftdm_bitstream ftdm_bitstream_t;
typedef struct ftdm_fsk_modulator ftdm_fsk_modulator_t;
typedef ftdm_status_t (*ftdm_span_start_t)(ftdm_span_t *span);
typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span);
typedef ftdm_status_t (*ftdm_span_destroy_t)(ftdm_span_t *span);
typedef ftdm_status_t (*ftdm_channel_sig_read_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
typedef ftdm_status_t (*ftdm_channel_sig_write_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
typedef ftdm_status_t (*ftdm_channel_sig_dtmf_t)(ftdm_channel_t *ftdmchan, const char *dtmf);

@ -150,6 +150,13 @@ static char *print_number(cJSON *item)
return str;
}
#define is_hexdigit(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
static int scan_unicode(const char *ptr, unsigned int *uc)
{
if (!is_hexdigit(*(ptr)) || !is_hexdigit(*(ptr+1)) || !is_hexdigit(*(ptr+2)) || !is_hexdigit(*(ptr+3))) return -1;
return sscanf(ptr, "%4x", uc);
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
@ -177,8 +184,7 @@ static const char *parse_string(cJSON *item,const char *str)
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
if (sscanf(ptr+1,"%4x",&uc) < 1) break;
if (scan_unicode(ptr+1, &uc) < 1) break;
ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
@ -186,7 +192,7 @@ static const char *parse_string(cJSON *item,const char *str)
if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
{
if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
if (sscanf(ptr+3,"%4x",&uc2) < 1) break;
if (scan_unicode(ptr+3,&uc2) < 1) break;
ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);

@ -220,6 +220,7 @@ struct switch_media_bug {
uint32_t record_pre_buffer_count;
uint32_t record_pre_buffer_max;
switch_frame_t *ping_frame;
switch_frame_t *video_ping_frame;
switch_frame_t *read_demux_frame;
switch_queue_t *read_video_queue;
switch_queue_t *write_video_queue;

@ -341,6 +341,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_set_video_file(switch_core_ses
SWITCH_DECLARE(switch_file_handle_t *) switch_core_media_get_video_file(switch_core_session_t *session, switch_rw_t rw);
SWITCH_DECLARE(switch_bool_t) switch_core_session_in_video_thread(switch_core_session_t *session);
SWITCH_DECLARE(switch_bool_t) switch_core_media_check_dtls(switch_core_session_t *session, switch_media_type_t type);
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_END_EXTERN_C
#endif

@ -51,6 +51,7 @@ struct switch_rtcp_report_block_frame {
uint32_t lsr; /* The middle 32 bits out of 64 in the NTP timestamp */
uint32_t dlsr; /* The delay, expressed in units of 1/65536 seconds, between receiving the last SR packet from source SSRC_n and sending this reception report block */
uint32_t loss_avg;
double rtt_avg;
};
/*! \brief An abstraction of a rtcp frame */

@ -613,6 +613,14 @@ typedef enum {
} switch_vad_flag_enum_t;
typedef uint32_t switch_vad_flag_t;
typedef struct error_period {
int64_t start;
int64_t stop;
struct error_period *next;
} switch_error_period_t;
typedef struct {
switch_size_t raw_bytes;
switch_size_t media_bytes;
@ -648,6 +656,7 @@ typedef struct {
switch_size_t last_flaw;
double R;
double mos;
struct error_period *error_log;
} switch_rtp_numbers_t;
typedef struct {

@ -859,7 +859,7 @@ static inline switch_bool_t switch_strstr(char *s, char *q)
S = strdup(s);
assert(S != NULL);
switch_assert(S != NULL);
for (p = S; p && *p; p++) {
*p = (char) switch_toupper(*p);
@ -871,7 +871,7 @@ static inline switch_bool_t switch_strstr(char *s, char *q)
}
Q = strdup(q);
assert(Q != NULL);
switch_assert(Q != NULL);
for (p = Q; p && *p; p++) {
*p = (char) switch_toupper(*p);

@ -25,6 +25,7 @@
*
* Seven Du <dujinfang@gmail.com>
* Anthony Minessale <anthm@freeswitch.org>
* Emmanuel Schmidbauer <eschmidbauer@gmail.com>
*
* mod_avcodec -- Codec with libav.org
*
@ -538,7 +539,7 @@ static void fs_rtp_parse_h263_rfc2190(h264_codec_context_t *context, AVPacket *p
mb_info_pos++;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Unable to split H263 packet! mb_info_pos=%d mb_info_count=%d pos=%d max=%ld\n", mb_info_pos, mb_info_count, pos, end - buf_base);
"Unable to split H263 packet! mb_info_pos=%d mb_info_count=%d pos=%d max=%"SWITCH_SIZE_T_FMT"\n", mb_info_pos, mb_info_count, pos, (switch_size_t)(end - buf_base));
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Should Not Happen!!! mb_info_pos=%d mb_info_count=%d mb_info_size=%d\n", mb_info_pos, mb_info_count, mb_info_size);
@ -799,7 +800,8 @@ static switch_status_t consume_nalu(h264_codec_context_t *context, switch_frame_
static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t width, uint32_t height)
{
int sane = 0;
if (!context->encoder) context->encoder = avcodec_find_encoder(context->av_codec_id);
if (!context->encoder) {
@ -846,6 +848,11 @@ static switch_status_t open_encoder(h264_codec_context_t *context, uint32_t widt
context->bandwidth = switch_calc_bitrate(context->codec_settings.video.width, context->codec_settings.video.height, 0, 0) * 8;
}
if (context->bandwidth > sane) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "BITRATE TRUNCATED TO %d\n", sane);
context->bandwidth = sane * 8;
}
//context->encoder_ctx->bit_rate = context->bandwidth * 1024;
context->encoder_ctx->width = context->codec_settings.video.width;
context->encoder_ctx->height = context->codec_settings.video.height;
@ -1493,7 +1500,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avcodec_load)
switch_h264_init, switch_h264_encode, switch_h264_decode, switch_h264_control, switch_h264_destroy);
SWITCH_ADD_CODEC(codec_interface, "H263+ Video");
switch_core_codec_add_video_implementation(pool, codec_interface, 34, "H263-1998", NULL,
switch_core_codec_add_video_implementation(pool, codec_interface, 115, "H263-1998", NULL,
switch_h264_init, switch_h264_encode, switch_h264_decode, switch_h264_control, switch_h264_destroy);
SWITCH_ADD_API(api_interface, "av_codec", "av_codec information", av_codec_api_function, "");

@ -330,7 +330,6 @@ static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec
break;
case SWITCH_VIDEO_ENCODE_SPEED_FAST:
av_opt_set(c->priv_data, "preset", "veryfast", 0);
av_opt_set(c->priv_data, "tune", "zerolatency", 0);
break;
default:
break;
@ -513,7 +512,7 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
record_helper_t *eh = (record_helper_t *) obj;
void *pop = NULL;
switch_image_t *img = NULL, *tmp_img = NULL;
int d_w = 0, d_h = 0;
int d_w = eh->video_st->width, d_h = eh->video_st->height;
int size = 0, skip = 0, skip_freq = 0, skip_count = 0, skip_total = 0, skip_total_count = 0;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "video thread start\n");

@ -1213,15 +1213,18 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit
if (idx < 0 || idx > (int)(conference->canvas_count - 1)) idx = 0;
switch_mutex_lock(conference->canvas_mutex);
if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
stream->write_function(stream, "Change personal canvas set to layout [%s]\n", vlayout->name);
switch_mutex_lock(conference->member_mutex);
conference->new_personal_vlayout = vlayout;
switch_mutex_unlock(conference->member_mutex);
} else {
stream->write_function(stream, "Change canvas %d to layout [%s]\n", idx + 1, vlayout->name);
switch_mutex_lock(conference->canvases[idx]->mutex);
conference->canvases[idx]->new_vlayout = vlayout;
switch_mutex_unlock(conference->canvases[idx]->mutex);
}
switch_mutex_unlock(conference->canvas_mutex);
return SWITCH_STATUS_SUCCESS;
}
@ -1579,8 +1582,6 @@ switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switc
return SWITCH_STATUS_SUCCESS;
}
switch_mutex_lock(member->conference->canvas_mutex);
if (!strcasecmp(text, "clear") || (member->video_reservation_id && !strcasecmp(text, member->video_reservation_id))) {
member->video_reservation_id = NULL;
stream->write_function(stream, "+OK reservation_id cleared\n");
@ -1591,8 +1592,6 @@ switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switc
conference_video_detach_video_layer(member);
switch_mutex_unlock(member->conference->canvas_mutex);
return SWITCH_STATUS_SUCCESS;
}

@ -378,13 +378,10 @@ void conference_event_la_command_handler(switch_live_array_t *la, const char *cm
void conference_event_adv_la(conference_obj_t *conference, conference_member_t *member, switch_bool_t join)
{
//if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
switch_channel_set_flag(member->channel, CF_VIDEO_REFRESH_REQ);
switch_core_media_gen_key_frame(member->session);
//}
if (conference && conference->la && member->session &&
!switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)) {
if (conference && conference->la && member->session) {
cJSON *msg, *data;
const char *uuid = switch_core_session_get_uuid(member->session);
const char *cookie = switch_channel_get_variable(member->channel, "event_channel_cookie");
@ -412,6 +409,14 @@ void conference_event_adv_la(conference_obj_t *conference, conference_member_t *
cJSON_AddItemToObject(data, "secondScreen", cJSON_CreateTrue());
}
if (switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)) {
cJSON_AddItemToObject(data, "videoOnly", cJSON_CreateTrue());
}
if (switch_true(switch_channel_get_variable_dup(member->channel, "video_screen_share", SWITCH_FALSE, -1))) {
cJSON_AddItemToObject(data, "screenShare", cJSON_CreateTrue());
}
if (conference_utils_member_test_flag(member, MFLAG_MOD)) {
cJSON_AddItemToObject(data, "modChannel", cJSON_CreateString(conference->mod_event_channel));
}

@ -410,6 +410,45 @@ switch_status_t conference_file_local_play(conference_obj_t *conference, switch_
return status;
}
switch_status_t conference_close_open_files(conference_obj_t *conference)
{
int x = 0;
switch_mutex_lock(conference->mutex);
/* Close Unused Handles */
if (conference->fnode) {
conference_file_node_t *fnode, *cur;
switch_memory_pool_t *pool;
fnode = conference->fnode;
while (fnode) {
cur = fnode;
fnode = fnode->next;
if (cur->type != NODE_TYPE_SPEECH) {
conference_file_close(conference, cur);
}
pool = cur->pool;
switch_core_destroy_memory_pool(&pool);
x++;
}
conference->fnode = NULL;
}
if (conference->async_fnode) {
switch_memory_pool_t *pool;
conference_file_close(conference, conference->async_fnode);
pool = conference->async_fnode->pool;
conference->async_fnode = NULL;
switch_core_destroy_memory_pool(&pool);
x++;
}
switch_mutex_unlock(conference->mutex);
return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
/* For Emacs:
* Local Variables:
* mode:c

@ -150,13 +150,41 @@ void conference_member_update_status_field(conference_member_t *member)
char *str, *vstr = "", display[128] = "", *json_display = NULL;
cJSON *json, *audio, *video;
if (!member->conference->la || !member->json ||
!member->status_field || switch_channel_test_flag(member->channel, CF_VIDEO_ONLY) || conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
if (!member->conference->la || !member->json || !member->status_field || conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
return;
}
switch_live_array_lock(member->conference->la);
if (!conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
str = "MUTE";
} else if (switch_channel_test_flag(member->channel, CF_HOLD)) {
str = "HOLD";
} else if (member == member->conference->floor_holder) {
if (conference_utils_member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING (FLOOR)";
} else {
str = "FLOOR";
}
} else if (conference_utils_member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING";
} else {
str = "ACTIVE";
}
if (switch_channel_test_flag(member->channel, CF_VIDEO)) {
if (!conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)) {
vstr = " VIDEO (BLIND)";
} else {
vstr = " VIDEO";
if (member && member->id == member->conference->video_floor_holder) {
vstr = " VIDEO (FLOOR)";
}
}
}
switch_snprintf(display, sizeof(display), "%s%s", str, vstr);
if (conference_utils_test_flag(member->conference, CFLAG_JSON_STATUS)) {
json = cJSON_CreateObject();
audio = cJSON_CreateObject();
@ -169,6 +197,19 @@ void conference_member_update_status_field(conference_member_t *member)
if (switch_channel_test_flag(member->channel, CF_VIDEO) || member->avatar_png_img) {
video = cJSON_CreateObject();
if (conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN) &&
member->video_layer_id > -1 && switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) != SWITCH_MEDIA_FLOW_SENDONLY) {
cJSON_AddItemToObject(video, "visible", cJSON_CreateTrue());
} else {
cJSON_AddItemToObject(video, "visible", cJSON_CreateFalse());
}
cJSON_AddItemToObject(video, "videoOnly", cJSON_CreateBool(switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)));
if (switch_true(switch_channel_get_variable_dup(member->channel, "video_screen_share", SWITCH_FALSE, -1))) {
cJSON_AddItemToObject(video, "screenShare", cJSON_CreateTrue());
}
cJSON_AddItemToObject(video, "avatarPresented", cJSON_CreateBool(!!member->avatar_png_img));
cJSON_AddItemToObject(video, "mediaFlow", cJSON_CreateString(switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY ? "sendOnly" : "sendRecv"));
cJSON_AddItemToObject(video, "muted", cJSON_CreateBool(!conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)));
@ -186,43 +227,19 @@ void conference_member_update_status_field(conference_member_t *member)
cJSON_AddItemToObject(json, "video", cJSON_CreateFalse());
}
if (conference_utils_test_flag(member->conference, CFLAG_JSON_STATUS)) {
cJSON_AddItemToObject(json, "oldStatus", cJSON_CreateString(display));
}
json_display = cJSON_PrintUnformatted(json);
cJSON_Delete(json);
} else {
if (!conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
str = "MUTE";
} else if (switch_channel_test_flag(member->channel, CF_HOLD)) {
str = "HOLD";
} else if (member == member->conference->floor_holder) {
if (conference_utils_member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING (FLOOR)";
} else {
str = "FLOOR";
}
} else if (conference_utils_member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING";
} else {
str = "ACTIVE";
}
if (switch_channel_test_flag(member->channel, CF_VIDEO)) {
if (!conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)) {
vstr = " VIDEO (BLIND)";
} else {
vstr = " VIDEO";
if (member && member->id == member->conference->video_floor_holder) {
vstr = " VIDEO (FLOOR)";
}
}
}
switch_snprintf(display, sizeof(display), "%s%s", str, vstr);
}
switch_safe_free(member->status_field->valuestring);
if (json_display) {
member->status_field->valuestring = json_display;
} else {
free(member->status_field->valuestring);
member->status_field->valuestring = strdup(display);
}
@ -694,12 +711,10 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
video_layout_t *vlayout = NULL;
switch_mutex_lock(conference->canvas_mutex);
if ((vlayout = conference_video_get_layout(conference, conference->video_layout_name, conference->video_layout_group))) {
conference_video_init_canvas(conference, vlayout, &member->canvas);
conference_video_init_canvas_layers(conference, member->canvas, vlayout);
}
switch_mutex_unlock(conference->canvas_mutex);
}
conference->members = member;
@ -767,6 +782,19 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
conference_utils_member_set_flag_locked(member, MFLAG_NO_MINIMIZE_ENCODING);
}
if ((var = switch_channel_get_variable(member->channel, "rtp_video_max_bandwidth_in"))) {
member->max_bw_in = switch_parse_bandwidth_string(var);
}
if ((var = switch_channel_get_variable(member->channel, "rtp_video_max_bandwidth_out"))) {
member->max_bw_out = switch_parse_bandwidth_string(var);;
if (member->max_bw_out < conference->video_codec_settings.video.bandwidth) {
conference_utils_member_set_flag_locked(member, MFLAG_NO_MINIMIZE_ENCODING);
switch_core_media_set_outgoing_bitrate(member->session, SWITCH_MEDIA_TYPE_VIDEO, member->max_bw_out);
}
}
switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id);
switch_channel_set_variable_printf(channel, "conference_moderator", "%s", conference_utils_member_test_flag(member, MFLAG_MOD) ? "true" : "false");
switch_channel_set_variable_printf(channel, "conference_ghost", "%s", conference_utils_member_test_flag(member, MFLAG_GHOST) ? "true" : "false");
@ -903,7 +931,7 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
switch_mutex_unlock(member->audio_out_mutex);
switch_mutex_unlock(member->audio_in_mutex);
if (conference->la && member->channel && !switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)) {
if (conference->la && member->channel) {
if (!conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
cJSON *dvars;
switch_event_t *var_event;
@ -918,7 +946,6 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
switch_channel_get_variable(member->channel, "original_read_codec"),
switch_channel_get_variable(member->channel, "original_read_rate")
));
member->status_field = cJSON_CreateString("");
cJSON_AddItemToArray(member->json, member->status_field);
@ -953,6 +980,7 @@ switch_status_t conference_member_add(conference_obj_t *conference, conference_m
if (!conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
switch_live_array_add(conference->la, switch_core_session_get_uuid(member->session), -1, &member->json, SWITCH_FALSE);
}
}
@ -1213,7 +1241,7 @@ switch_status_t conference_member_del(conference_obj_t *conference, conference_m
switch_mutex_unlock(member->audio_in_mutex);
if (conference->la && member->session && !switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)) {
if (conference->la && member->session) {
switch_live_array_del(conference->la, switch_core_session_get_uuid(member->session));
//switch_live_array_clear_alias(conference->la, switch_core_session_get_uuid(member->session), "conference");
conference_event_adv_la(conference, member, SWITCH_FALSE);

@ -337,8 +337,6 @@ void conference_video_clear_layer(mcu_layer_t *layer)
void conference_video_reset_layer(mcu_layer_t *layer)
{
layer->tagged = 0;
switch_img_free(&layer->banner_img);
switch_img_free(&layer->logo_img);
switch_img_free(&layer->logo_text_img);
@ -553,28 +551,16 @@ mcu_layer_t *conference_video_get_layer_locked(conference_member_t *member)
mcu_canvas_t *canvas = NULL;
if (!member || member->canvas_id < 0 || member->video_layer_id < 0) return NULL;
switch_mutex_lock(member->conference->canvas_mutex);
canvas = member->conference->canvases[member->canvas_id];
if ((canvas = conference_video_get_canvas_locked(member))) {
switch_mutex_lock(canvas->mutex);
layer = &canvas->layers[member->video_layer_id];
if (!canvas) {
goto end;
}
switch_mutex_lock(canvas->mutex);
layer = &canvas->layers[member->video_layer_id];
if (!layer) {
switch_mutex_unlock(canvas->mutex);
if (!layer) {
switch_mutex_unlock(canvas->mutex);
}
}
end:
if (!layer) {
switch_mutex_unlock(member->conference->canvas_mutex);
}
return layer;
}
@ -589,28 +575,50 @@ void conference_video_release_layer(mcu_layer_t **layer)
if (!canvas) return;
switch_mutex_unlock(canvas->mutex);
switch_assert(canvas->conference);
switch_mutex_unlock(canvas->conference->canvas_mutex);
conference_video_release_canvas(&canvas);
*layer = NULL;
}
mcu_canvas_t *conference_video_get_canvas_locked(conference_member_t *member)
{
mcu_canvas_t *canvas = NULL;
if (!member || member->canvas_id < 0 || member->video_layer_id < 0) return NULL;
switch_mutex_lock(member->conference->canvas_mutex);
canvas = member->conference->canvases[member->canvas_id];
if (!canvas) {
switch_mutex_unlock(member->conference->canvas_mutex);
}
return canvas;
}
void conference_video_release_canvas(mcu_canvas_t **canvasP)
{
mcu_canvas_t *canvas = NULL;
switch_assert(canvasP);
canvas = *canvasP;
if (!canvas) return;
switch_mutex_unlock(canvas->conference->canvas_mutex);
*canvasP = NULL;
}
void conference_video_detach_video_layer(conference_member_t *member)
{
mcu_layer_t *layer = NULL;
mcu_canvas_t *canvas = NULL;
switch_mutex_lock(member->conference->canvas_mutex);
if (member->canvas_id < 0 || member->video_layer_id < 0) return;
if (member->canvas_id < 0) goto end;
canvas = member->conference->canvases[member->canvas_id];
if (!canvas || member->video_layer_id < 0) {
goto end;
if (!(canvas = conference_video_get_canvas_locked(member))) {
return;
}
switch_mutex_lock(canvas->mutex);
@ -646,11 +654,8 @@ void conference_video_detach_video_layer(conference_member_t *member)
}
switch_mutex_unlock(canvas->mutex);
end:
switch_mutex_unlock(member->conference->canvas_mutex);
conference_video_release_canvas(&canvas);
}
@ -892,8 +897,6 @@ switch_status_t conference_video_attach_video_layer(conference_member_t *member,
return SWITCH_STATUS_FALSE;
}
switch_mutex_lock(member->conference->canvas_mutex);
switch_mutex_lock(canvas->mutex);
layer = &canvas->layers[idx];
@ -973,8 +976,6 @@ switch_status_t conference_video_attach_video_layer(conference_member_t *member,
switch_mutex_unlock(canvas->mutex);
switch_mutex_unlock(member->conference->canvas_mutex);
return status;
}
@ -985,7 +986,6 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva
if (!canvas) return;
switch_thread_rwlock_wrlock(canvas->video_rwlock);
switch_mutex_lock(conference->canvas_mutex);
switch_mutex_lock(canvas->mutex);
canvas->layout_floor_id = -1;
@ -996,7 +996,6 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva
if (!vlayout) {
switch_mutex_unlock(canvas->mutex);
switch_mutex_lock(conference->canvas_mutex);
switch_thread_rwlock_unlock(canvas->video_rwlock);
return;
}
@ -1072,7 +1071,6 @@ void conference_video_init_canvas_layers(conference_obj_t *conference, mcu_canva
}
switch_mutex_unlock(canvas->mutex);
switch_mutex_unlock(conference->canvas_mutex);
switch_thread_rwlock_unlock(canvas->video_rwlock);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Canvas position %d applied layout %s\n", canvas->canvas_id + 1, vlayout->name);
@ -1265,7 +1263,6 @@ void conference_video_write_canvas_image_to_codec_group(conference_obj_t *confer
}
if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) ||
switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY ||
switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS) {
continue;
}
@ -1274,6 +1271,11 @@ void conference_video_write_canvas_image_to_codec_group(conference_obj_t *confer
switch_core_session_request_video_refresh(imember->session);
}
if (switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY) {
switch_core_session_rwunlock(imember->session);
continue;
}
//switch_core_session_write_encoded_video_frame(imember->session, frame, 0, 0);
switch_set_flag(frame, SFF_ENCODED);
@ -1322,7 +1324,7 @@ video_layout_t *conference_video_get_layout(conference_obj_t *conference, const
} else {
vlayout = switch_core_hash_find(conference->layout_hash, video_layout_name);
}
return vlayout;
}
@ -1334,8 +1336,8 @@ void conference_video_vmute_snap(conference_member_t *member, switch_bool_t clea
mcu_layer_t *layer = NULL;
mcu_canvas_t *canvas = NULL;
canvas = member->conference->canvases[member->canvas_id];
canvas = conference_video_get_canvas_locked(member);
switch_mutex_lock(canvas->mutex);
layer = &canvas->layers[member->video_layer_id];
switch_img_free(&layer->mute_img);
@ -1347,6 +1349,7 @@ void conference_video_vmute_snap(conference_member_t *member, switch_bool_t clea
}
switch_mutex_unlock(canvas->mutex);
conference_video_release_canvas(&canvas);
}
}
@ -1491,7 +1494,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_write_thread_run(switch_thread_
switch_time_t now = switch_time_now();
if (last) {
int delta = now - last;
int delta = (int)(now - last);
if (delta > member->conference->video_fps.ms * 5000) {
switch_core_session_request_video_refresh(member->session);
}
@ -1505,26 +1508,25 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_write_thread_run(switch_thread_
switch_frame_buffer_free(member->fb, &frame);
}
canvas = NULL;
layer = NULL;
switch_mutex_lock(member->conference->canvas_mutex);
if (member->video_layer_id > -1 && member->canvas_id > -1) {
canvas = member->conference->canvases[member->canvas_id];
layer = &canvas->layers[member->video_layer_id];
if (layer->need_patch) {
switch_mutex_lock(canvas->mutex);
} else {
if (!layer->need_patch || switch_thread_rwlock_tryrdlock(canvas->video_rwlock) != SWITCH_STATUS_SUCCESS) {
canvas = NULL;
layer = NULL;
}
}
switch_mutex_unlock(member->conference->canvas_mutex);
if (canvas) {
switch_thread_rwlock_rdlock(canvas->video_rwlock);
if (canvas && layer && layer->need_patch) {
conference_video_scale_and_patch(layer, NULL, SWITCH_FALSE);
layer->need_patch = 0;
switch_thread_rwlock_unlock(canvas->video_rwlock);
switch_mutex_unlock(canvas->mutex);
}
}
}
@ -1580,10 +1582,13 @@ void conference_video_check_avatar(conference_member_t *member, switch_bool_t fo
return;
}
canvas = member->conference->canvases[member->canvas_id];
canvas = conference_video_get_canvas_locked(member);
if (conference_utils_test_flag(member->conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS) &&
(!switch_channel_test_flag(member->channel, CF_VIDEO) || switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY)) {
if (canvas) {
conference_video_release_canvas(&canvas);
}
return;
}
@ -1621,6 +1626,7 @@ void conference_video_check_avatar(conference_member_t *member, switch_bool_t fo
if (canvas) {
switch_mutex_unlock(canvas->mutex);
conference_video_release_canvas(&canvas);
}
}
@ -1713,7 +1719,7 @@ switch_status_t conference_video_find_layer(conference_obj_t *conference, mcu_ca
mcu_layer_t *layer = NULL;
int i;
switch_mutex_lock(conference->canvas_mutex);
switch_mutex_lock(canvas->mutex);
for (i = 0; i < canvas->total_layers; i++) {
mcu_layer_t *xlayer = &canvas->layers[i];
@ -1759,7 +1765,7 @@ switch_status_t conference_video_find_layer(conference_obj_t *conference, mcu_ca
}
}
switch_mutex_unlock(conference->canvas_mutex);
switch_mutex_unlock(canvas->mutex);
if (layer) {
*layerP = layer;
@ -1836,14 +1842,67 @@ void conference_video_pop_next_image(conference_member_t *member, switch_image_t
*imgP = img;
}
void conference_video_set_incoming_bitrate(conference_member_t *member, int kps)
{
switch_core_session_message_t msg = { 0 };
msg.message_id = SWITCH_MESSAGE_INDICATE_BITRATE_REQ;
msg.numeric_arg = kps * 1024;
msg.from = __FILE__;
switch_core_session_receive_message(member->session, &msg);
member->managed_kps = kps;
}
void conference_video_set_max_incoming_bitrate_member(conference_member_t *member, int kps)
{
member->max_bw_in = kps;
member->managed_kps = 0;
}
void conference_video_set_absolute_incoming_bitrate_member(conference_member_t *member, int kps)
{
member->max_bw_in = 0;
member->force_bw_in = kps;
member->managed_kps = 0;
if (!conference_utils_test_flag(member->conference, CFLAG_MANAGE_INBOUND_VIDEO_BITRATE) && switch_channel_test_flag(member->channel, CF_VIDEO)) {
conference_video_set_incoming_bitrate(member, kps);
}
}
void conference_video_set_max_incoming_bitrate(conference_obj_t *conference, int kps)
{
conference_member_t *imember;
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
if (imember->channel && switch_channel_ready(imember->channel) && conference_utils_member_test_flag(imember, MFLAG_RUNNING)) {
conference_video_set_max_incoming_bitrate_member(imember, kps);
}
}
switch_mutex_unlock(conference->member_mutex);
}
void conference_video_set_absolute_incoming_bitrate(conference_obj_t *conference, int kps)
{
conference_member_t *imember;
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
if (imember->channel && switch_channel_ready(imember->channel) && conference_utils_member_test_flag(imember, MFLAG_RUNNING)) {
conference_video_set_absolute_incoming_bitrate_member(imember, kps);
}
}
switch_mutex_unlock(conference->member_mutex);
}
void conference_video_check_auto_bitrate(conference_member_t *member, mcu_layer_t *layer)
{
if (switch_channel_test_flag(member->channel, CF_VIDEO_BITRATE_UNMANAGABLE)) {
member->managed_kps = 0;
} else if (conference_utils_test_flag(member->conference, CFLAG_MANAGE_INBOUND_VIDEO_BITRATE) && !member->managed_kps) {
switch_core_session_message_t msg = { 0 };
int kps;
int kps = 256;
int w = 320;
int h = 240;
@ -1853,23 +1912,35 @@ void conference_video_check_auto_bitrate(conference_member_t *member, mcu_layer_
h = layer->screen_h;
}
}
if (!layer || !conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN) || member->avatar_png_img) {
kps = 200;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s auto-setting bitrate to %dkps because user's image is not visible\n",
if (member->conference->force_bw_in || member->force_bw_in) {
if (!(kps = member->conference->force_bw_in)) {
kps = member->force_bw_in;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s setting bitrate to %dkps because it was forced.\n",
switch_channel_get_name(member->channel), kps);
} else {
kps = switch_calc_bitrate(w, h, 2, (int)(member->conference->video_fps.fps));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s auto-setting bitrate to %dkps to accomodate %dx%d resolution\n",
switch_channel_get_name(member->channel), kps, layer->screen_w, layer->screen_h);
int max = 0;
if (layer) {
kps = switch_calc_bitrate(w, h, 1, (int)(member->conference->video_fps.fps));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s auto-setting bitrate to %dkps to accomodate %dx%d resolution\n",
switch_channel_get_name(member->channel), kps, layer->screen_w, layer->screen_h);
}
if (member->conference->max_bw_in) {
max = member->conference->max_bw_in;
} else {
max = member->max_bw_in;
}
if (max && kps > max) {
kps = max;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s overriding bitrate setting to %dkps because it was the max allowed.\n",
switch_channel_get_name(member->channel), kps);
}
}
msg.message_id = SWITCH_MESSAGE_INDICATE_BITRATE_REQ;
msg.numeric_arg = kps * 1024;
msg.from = __FILE__;
switch_core_session_receive_message(member->session, &msg);
member->managed_kps = kps;
conference_video_set_incoming_bitrate(member, kps);
}
}
@ -1899,6 +1970,39 @@ static void wait_for_canvas(mcu_canvas_t *canvas)
}
}
static void personal_attach(mcu_layer_t *layer, conference_member_t *member)
{
layer->tagged = 1;
if (layer->member_id != (int)member->id) {
const char *var = NULL;
layer->mute_patched = 0;
layer->avatar_patched = 0;
switch_img_free(&layer->banner_img);
switch_img_free(&layer->logo_img);
if (layer->geometry.audio_position) {
conference_api_sub_position(member, NULL, layer->geometry.audio_position);
}
var = NULL;
if (member->video_banner_text ||
(var = switch_channel_get_variable_dup(member->channel, "video_banner_text", SWITCH_FALSE, -1))) {
conference_video_layer_set_banner(member, layer, var);
}
var = NULL;
if (member->video_logo ||
(var = switch_channel_get_variable_dup(member->channel, "video_logo_path", SWITCH_FALSE, -1))) {
conference_video_layer_set_logo(member, layer, var);
}
}
layer->member_id = member->id;
}
void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thread, void *obj)
{
mcu_canvas_t *canvas = (mcu_canvas_t *) obj;
@ -2113,7 +2217,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
}
//VIDFLOOR
if (conference->canvas_count == 1 && canvas->layout_floor_id > -1 && imember->id == conference->video_floor_holder &&
if (canvas->layout_floor_id > -1 && imember->id == conference->video_floor_holder &&
imember->video_layer_id != canvas->layout_floor_id) {
conference_video_attach_video_layer(imember, canvas, canvas->layout_floor_id);
}
@ -2128,7 +2232,6 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
//printf("MEMBER %d layer_id %d canvas: %d/%d\n", imember->id, imember->video_layer_id,
// canvas->layers_used, canvas->total_layers);
switch_mutex_lock(conference->canvas_mutex);
if (imember->video_layer_id > -1) {
layer = &canvas->layers[imember->video_layer_id];
if (layer->member_id != (int)imember->id) {
@ -2137,8 +2240,6 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
imember->layer_timeout = DEFAULT_LAYER_TIMEOUT;
}
}
switch_mutex_unlock(conference->canvas_mutex);
if (imember->avatar_png_img) {
if (layer) {
@ -2285,6 +2386,10 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
if (total < 1) total = 1;
if (conference->members_with_video == 1 && file_count) {
total = 0;
}
if (conference->video_layout_group && (lg = switch_core_hash_find(conference->layout_group_hash, conference->video_layout_group))) {
if ((vlayout = conference_video_find_best_layout(conference, lg, total + file_count))) {
conference_video_init_canvas_layers(conference, imember->canvas, vlayout);
@ -2322,52 +2427,69 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
for (imember = conference->members; imember; imember = imember->next) {
int i = 0;
mcu_layer_t *floor_layer = NULL;
if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO || switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) ||
switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS) {
continue;
}
i = 0;
while (i < imember->canvas->total_layers) {
layer = &imember->canvas->layers[i++];
switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, &layer->canvas->bgcolor);
}
i = 0;
if (!file_count && imember->canvas->layout_floor_id > -1 && imember->conference->video_floor_holder &&
imember->id != imember->conference->video_floor_holder) {
if ((omember = conference_member_get(imember->conference, imember->conference->video_floor_holder))) {
if (conference->members_with_video + conference->members_with_avatar == 1 || imember != omember) {
layer = &imember->canvas->layers[imember->canvas->layout_floor_id];
floor_layer = layer;
layer = NULL;
}
switch_thread_rwlock_unlock(omember->rwlock);
}
}
for (omember = conference->members; omember; omember = omember->next) {
mcu_layer_t *layer = NULL;
switch_image_t *use_img = NULL;
if (!omember->session || !switch_channel_test_flag(omember->channel, CF_VIDEO) || switch_core_session_media_flow(omember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
if (!omember->session || !switch_channel_test_flag(omember->channel, CF_VIDEO) ||
switch_core_session_media_flow(omember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
continue;
}
if (conference->members_with_video + conference->members_with_avatar != 1 && imember == omember) {
continue;
}
if (i < imember->canvas->total_layers) {
layer = &imember->canvas->layers[i++];
if (layer->member_id != (int)omember->id) {
const char *var = NULL;
layer->mute_patched = 0;
layer->avatar_patched = 0;
switch_img_free(&layer->banner_img);
switch_img_free(&layer->logo_img);
if (layer->geometry.audio_position) {
conference_api_sub_position(omember, NULL, layer->geometry.audio_position);
}
var = NULL;
if (omember->video_banner_text ||
(var = switch_channel_get_variable_dup(omember->channel, "video_banner_text", SWITCH_FALSE, -1))) {
conference_video_layer_set_banner(omember, layer, var);
}
var = NULL;
if (omember->video_logo ||
(var = switch_channel_get_variable_dup(omember->channel, "video_logo_path", SWITCH_FALSE, -1))) {
conference_video_layer_set_logo(omember, layer, var);
if (file_count && (conference->members_with_video + conference->members_with_avatar == 1)) {
floor_layer = NULL;
continue;
}
if (!file_count && floor_layer && omember->id == conference->video_floor_holder) {
layer = floor_layer;
} else {
if (floor_layer || (file_count && i == imember->canvas->layout_floor_id)) {
if ((i+1) < imember->canvas->total_layers) {
i++;
}
}
if (i < imember->canvas->total_layers) {
layer = &imember->canvas->layers[i++];
}
}
layer->member_id = omember->id;
if (layer) {
personal_attach(layer, omember);
}
if (!layer && omember->al) {
@ -2375,7 +2497,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
}
use_img = omember->pcanvas_img;
if (layer) {
if (use_img && !omember->avatar_png_img) {
@ -2418,12 +2540,15 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
for (j = 0; j < file_count; j++) {
switch_image_t *img = file_imgs[j];
if (i < imember->canvas->total_layers) {
if (j == 0 && imember->canvas->layout_floor_id > -1) {
layer = &imember->canvas->layers[imember->canvas->layout_floor_id];
conference_video_scale_and_patch(layer, img, SWITCH_FALSE);
} else if (i < imember->canvas->total_layers) {
layer = &imember->canvas->layers[i++];
conference_video_scale_and_patch(layer, img, SWITCH_FALSE);
}
}
switch_core_session_rwunlock(imember->session);
}
@ -2601,7 +2726,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
}
}
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
switch_frame_t *dupframe;
@ -2613,7 +2738,6 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
}
if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) ||
switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY ||
switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS) {
continue;
}
@ -2622,6 +2746,12 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
switch_core_session_request_video_refresh(imember->session);
}
if (switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY) {
switch_core_session_rwunlock(imember->session);
continue;
}
if (send_keyframe) {
switch_core_media_gen_key_frame(imember->session);
}
@ -2678,6 +2808,8 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr
}
}
conference_close_open_files(conference);
switch_core_timer_destroy(&canvas->timer);
conference_video_destroy_canvas(&canvas);
@ -2954,10 +3086,7 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_
continue;
}
if (switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY) {
continue;
}
if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) ||
switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS) {
continue;
@ -2967,6 +3096,11 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_
switch_core_session_request_video_refresh(imember->session);
}
if (switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY) {
switch_core_session_rwunlock(imember->session);
continue;
}
if (send_keyframe) {
switch_core_media_gen_key_frame(imember->session);
}
@ -3099,6 +3233,11 @@ void conference_video_set_floor_holder(conference_obj_t *conference, conference_
conference_utils_clear_flag(conference, CFLAG_VID_FLOOR_LOCK);
}
if (member && member->video_reservation_id) {
/* no video floor when a reservation id is set */
return;
}
if ((!force && conference_utils_test_flag(conference, CFLAG_VID_FLOOR_LOCK))) {
return;
}
@ -3143,11 +3282,16 @@ void conference_video_set_floor_holder(conference_obj_t *conference, conference_
}
//VIDFLOOR
if (conference->canvas_count == 1 && member && conference->canvases[0] && conference->canvases[0]->layout_floor_id > -1) {
conference_video_attach_video_layer(member, conference->canvases[0], conference->canvases[0]->layout_floor_id);
}
if (member) {
mcu_canvas_t *canvas = NULL;
if ((canvas = conference_video_get_canvas_locked(member))) {
if (canvas->layout_floor_id > -1) {
conference_video_attach_video_layer(member, canvas, canvas->layout_floor_id);
}
conference_video_release_canvas(&canvas);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Adding video floor %s\n",
switch_channel_get_name(member->channel));
@ -3345,10 +3489,14 @@ switch_status_t conference_video_thread_callback(switch_core_session_t *session,
if (conference_utils_test_flag(member->conference, CFLAG_VIDEO_MUXING)) {
switch_image_t *img_copy = NULL;
if (frame->img && (member->video_layer_id > -1 || member->canvas) && conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN) &&
if (frame->img && (member->video_layer_id > -1 || member->canvas) &&
conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN) &&
switch_queue_size(member->video_queue) < member->conference->video_fps.fps * 2 &&
!member->conference->playing_video_file) {
switch_img_copy(frame->img, &img_copy);
switch_queue_push(member->video_queue, img_copy);
if (switch_queue_trypush(member->video_queue, img_copy) != SWITCH_STATUS_SUCCESS) {
switch_img_free(&img_copy);
}
}
switch_thread_rwlock_unlock(member->conference->rwlock);
@ -3389,7 +3537,8 @@ switch_status_t conference_video_thread_callback(switch_core_session_t *session,
conference_member_t *fmember;
if ((fmember = conference_member_get(member->conference, member->conference->video_floor_holder))) {
switch_core_session_write_video_frame(fmember->session, frame, SWITCH_IO_FLAG_NONE, 0);
if (!conference_utils_member_test_flag(fmember, MFLAG_RECEIVING_VIDEO))
switch_core_session_write_video_frame(fmember->session, frame, SWITCH_IO_FLAG_NONE, 0);
switch_thread_rwlock_unlock(fmember->rwlock);
}
}

@ -1800,6 +1800,7 @@ SWITCH_STANDARD_APP(conference_function)
goto done;
}
conference->flags[CFLAG_JSON_STATUS] = 1;
conference_utils_set_cflags(cflags_str, conference->flags);
if (locked) {
@ -1884,6 +1885,7 @@ SWITCH_STANDARD_APP(conference_function)
goto done;
}
conference->flags[CFLAG_JSON_STATUS] = 1;
conference_utils_set_cflags(cflags_str, conference->flags);
if (locked) {
@ -2862,7 +2864,7 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co
if (video_codec_bandwidth) {
if (!strcasecmp(video_codec_bandwidth, "auto")) {
conference->video_codec_settings.video.bandwidth = switch_calc_bitrate(canvas_w, canvas_h, 2, (int)conference->video_fps.fps);
conference->video_codec_settings.video.bandwidth = switch_calc_bitrate(canvas_w, canvas_h, 1, (int)conference->video_fps.fps);
} else {
conference->video_codec_settings.video.bandwidth = switch_parse_bandwidth_string(video_codec_bandwidth);
}

@ -657,6 +657,8 @@ typedef struct conference_obj {
int recording_members;
uint32_t video_floor_packets;
video_layout_t *new_personal_vlayout;
int max_bw_in;
int force_bw_in;
} conference_obj_t;
/* Relationship with another member */
@ -757,6 +759,9 @@ struct conference_member {
switch_media_flow_t video_media_flow;
mcu_canvas_t *canvas;
switch_image_t *pcanvas_img;
int max_bw_in;
int force_bw_in;
int max_bw_out;
};
typedef enum {
@ -860,7 +865,7 @@ void conference_member_add_file_data(conference_member_t *member, int16_t *data,
void conference_send_notify(conference_obj_t *conference, const char *status, const char *call_id, switch_bool_t final);
switch_status_t conference_file_close(conference_obj_t *conference, conference_file_node_t *node);
void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, void *obj);
switch_status_t conference_close_open_files(conference_obj_t *conference);
void conference_al_gen_arc(conference_obj_t *conference, switch_stream_handle_t *stream);
void conference_al_process(al_handle_t *al, void *data, switch_size_t datalen, int rate);
@ -882,7 +887,8 @@ switch_status_t conference_loop_dmachine_dispatcher(switch_ivr_dmachine_match_t
mcu_layer_t *conference_video_get_layer_locked(conference_member_t *member);
void conference_video_release_layer(mcu_layer_t **layer);
mcu_canvas_t *conference_video_get_canvas_locked(conference_member_t *member);
void conference_video_release_canvas(mcu_canvas_t **canvasP);
int conference_member_setup_media(conference_member_t *member, conference_obj_t *conference);
al_handle_t *conference_al_create(switch_memory_pool_t *pool);

@ -142,7 +142,7 @@ SWITCH_LIMIT_INCR(hiredis_limit_incr)
}
if ( interval ) {
limit_key = switch_mprintf("%s_%d", resource, now % interval);
limit_key = switch_mprintf("%s_%d", resource, now / interval);
} else {
limit_key = switch_mprintf("%s", resource);
}
@ -163,6 +163,7 @@ SWITCH_LIMIT_INCR(hiredis_limit_incr)
limit_pvt->resource = switch_core_strdup(session_pool, resource);
limit_pvt->limit_key = switch_core_strdup(session_pool, limit_key);
limit_pvt->inc = 1;
limit_pvt->interval = interval;
switch_channel_set_private(channel, "hiredis_limit_pvt", limit_pvt);
count = atoll(response);
@ -184,30 +185,21 @@ SWITCH_LIMIT_INCR(hiredis_limit_incr)
SWITCH_LIMIT_RELEASE(hiredis_limit_release)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
hiredis_profile_t *profile = switch_core_hash_find(mod_hiredis_globals.profiles, realm);
hiredis_profile_t *profile = NULL;
char *hashkey = NULL, *response = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
hiredis_limit_pvt_t *limit_pvt = switch_channel_get_private(channel, "hiredis_limit_pvt");
if ( !zstr(realm) ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "hiredis: realm must be defined\n");
switch_goto_status(SWITCH_STATUS_GENERR, done);
}
if ( !profile ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "hiredis: Unable to locate profile[%s]\n", realm);
switch_goto_status(SWITCH_STATUS_GENERR, done);
}
/* If realm and resource are NULL, then clear all of the limits */
if ( !realm && !resource ) {
hiredis_limit_pvt_t *tmp = limit_pvt;
while (tmp) {
profile = switch_core_hash_find(mod_hiredis_globals.profiles, limit_pvt->realm);
hashkey = switch_mprintf("decr %s", tmp->limit_key);
limit_pvt = tmp->next;
if ( hiredis_profile_execute_sync(profile, hashkey, &response) != SWITCH_STATUS_SUCCESS) {
if ( limit_pvt && (limit_pvt->interval > 0) && (hiredis_profile_execute_sync(profile, hashkey, &response) != SWITCH_STATUS_SUCCESS)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "hiredis: profile[%s] error executing [%s] because [%s]\n",
tmp->realm, hashkey, response);
}
@ -217,6 +209,7 @@ SWITCH_LIMIT_RELEASE(hiredis_limit_release)
switch_safe_free(hashkey);
}
} else {
profile = switch_core_hash_find(mod_hiredis_globals.profiles, limit_pvt->realm);
hashkey = switch_mprintf("decr %s", limit_pvt->limit_key);

@ -37,6 +37,7 @@ typedef struct hiredis_limit_pvt_s {
char *resource;
char *limit_key;
int inc;
int interval;
struct hiredis_limit_pvt_s *next;
} hiredis_limit_pvt_t;

@ -14,9 +14,9 @@
<profile name="s3">
<!-- Credentials for AWS account. -->
<aws-s3>
<!-- 20 character key identifier -->
<!-- 20 character key identifier, can override with AWS_ACCESS_KEY_ID environment variable -->
<access-key-id><![CDATA[AKIAIOSFODNN7EXAMPLE]]></access-key-id>
<!-- 40 character secret -->
<!-- 40 character secret, can override with AWS_SECRET_ACCESS_KEY environment variable -->
<secret-access-key><![CDATA[wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY]]></secret-access-key>
<!--base-url><![CDATA[s3.example.com]]></base-url-->
</aws-s3>
@ -29,3 +29,4 @@
</profiles>
</configuration>

@ -36,6 +36,8 @@
#include <switch_curl.h>
#include "aws.h"
#include <stdlib.h>
/* 253 max domain size + '/' + NUL byte */
#define DOMAIN_BUF_SIZE 255
@ -1532,21 +1534,35 @@ static switch_status_t do_config(url_cache_t *cache)
char *base_domain = NULL;
if (s3) {
switch_xml_t base_domain_xml = switch_xml_child(s3, "base-domain");
switch_xml_t id = switch_xml_child(s3, "access-key-id");
switch_xml_t secret = switch_xml_child(s3, "secret-access-key");
if (id && secret) {
access_key_id = switch_strip_whitespace(switch_xml_txt(id));
secret_access_key = switch_strip_whitespace(switch_xml_txt(secret));
if (zstr(access_key_id) || zstr(secret_access_key)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing aws s3 credentials for profile \"%s\"\n", name);
switch_safe_free(access_key_id);
access_key_id = NULL;
switch_safe_free(secret_access_key);
secret_access_key = NULL;
}
/* check if environment variables set the keys */
access_key_id = getenv("AWS_ACCESS_KEY_ID");
secret_access_key = getenv("AWS_SECRET_ACCESS_KEY");
if (!zstr(access_key_id) && !zstr(secret_access_key)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Using AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables for s3 access on profile \"%s\"\n", name);
access_key_id = strdup(access_key_id);
secret_access_key = strdup(secret_access_key);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key id or secret\n");
continue;
/* use configuration for keys */
switch_xml_t id = switch_xml_child(s3, "access-key-id");
switch_xml_t secret = switch_xml_child(s3, "secret-access-key");
access_key_id = NULL;
secret_access_key = NULL;
if (id && secret) {
access_key_id = switch_strip_whitespace(switch_xml_txt(id));
secret_access_key = switch_strip_whitespace(switch_xml_txt(secret));
if (zstr(access_key_id) || zstr(secret_access_key)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing aws s3 credentials for profile \"%s\"\n", name);
switch_safe_free(access_key_id);
access_key_id = NULL;
switch_safe_free(secret_access_key);
secret_access_key = NULL;
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key id or secret\n");
continue;
}
}
if (base_domain_xml) {
base_domain = switch_strip_whitespace(switch_xml_txt(base_domain_xml));

@ -84,6 +84,13 @@ static opus_codec_settings_t default_codec_settings_8k = {
/*.samplerate*/ 0
};
struct dec_stats {
uint32_t fec_counter;
uint32_t plc_counter;
uint32_t frame_counter;
};
typedef struct dec_stats dec_stats_t;
struct opus_context {
OpusEncoder *encoder_object;
OpusDecoder *decoder_object;
@ -95,6 +102,7 @@ struct opus_context {
opus_codec_settings_t codec_settings;
int look_check;
int look_ts;
dec_stats_t decoder_stats;
};
struct {
@ -328,10 +336,17 @@ static char *gen_fmtp(opus_codec_settings_t *settings, switch_memory_pool_t *poo
static switch_bool_t switch_opus_has_fec(const uint8_t* payload,int payload_length_bytes)
{
/* nb_silk_frames: number of silk-frames (10 or 20 ms) in an opus frame: 0, 1, 2 or 3 */
/* computed from the 5 MSB (configuration) of the TOC byte (payload[0]) */
/* nb_opus_frames: number of opus frames in the packet */
/* computed from the 2 LSB (p0p1) of the TOC byte */
/* p0p1 = 0 => nb_opus_frames = 1 */
/* p0p1 = 1 or 2 => nb_opus_frames = 2 */
/* p0p1 = 3 => given by the 6 LSB of payload[1] */
int nb_silk_frames, nb_opus_frames, n, i;
opus_int16 frame_sizes[48];
const unsigned char *frame_data[48];
int frames;
if (payload == NULL || payload_length_bytes <= 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted packet (invalid size)\n");
@ -343,7 +358,7 @@ static switch_bool_t switch_opus_has_fec(const uint8_t* payload,int payload_leng
return SWITCH_FALSE;
}
if ((nb_opus_frames = opus_packet_parse(payload, payload_length_bytes, NULL, frame_data, frame_sizes, NULL)) < 0 ) {
if ((nb_opus_frames = opus_packet_parse(payload, payload_length_bytes, NULL, frame_data, frame_sizes, NULL)) <= 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OPUS_INVALID_PACKET ! nb_opus_frames: %d\n", nb_opus_frames);
return SWITCH_FALSE;
}
@ -355,38 +370,24 @@ static switch_bool_t switch_opus_has_fec(const uint8_t* payload,int payload_leng
if(nb_silk_frames == 0) {
nb_silk_frames = 1;
}
if (nb_silk_frames == 1) {
for (n = 0; n <= (payload[0]&0x4) ; n++) { /* mono or stereo */
if ((nb_silk_frames == 1) && (nb_opus_frames == 1)) {
for (n = 0; n <= (payload[0]&0x4) ; n++) { /* mono or stereo: 10,20 ms */
if (frame_data[0][0] & (0x80 >> ((n + 1) * (nb_silk_frames + 1) - 1))) {
return SWITCH_TRUE; /* 1st 20ms (or 10 ms) frame has FEC */
return SWITCH_TRUE; /* frame has FEC */
}
}
} else {
opus_int16 LBRR_flag = 0 ;
for (i=0 ; i < nb_opus_frames; i++ ) { /* only mono */
for (i=0 ; i < nb_opus_frames; i++ ) { /* only mono Opus frames */
LBRR_flag = (frame_data[i][0] >> (7 - nb_silk_frames)) & 0x1;
if (LBRR_flag) {
return SWITCH_TRUE; /* one of the silk frames has FEC */
}
}
}
}
frames = opus_packet_parse(payload, payload_length_bytes, NULL, frame_data, frame_sizes, NULL);
if (frames < 0) {
return SWITCH_FALSE;
}
if (frame_sizes[0] <= 1) {
return SWITCH_FALSE;
}
for (n = 0; n <= (payload[0]&0x4);n++) {
if (frame_data[0][0] & (0x80 >> ((n + 1) * (frames + 1) - 1))) {
return SWITCH_TRUE; /*this works only for 20 ms frames now, it will return VAD for 40 ms or 60 ms*/
}
}
return SWITCH_FALSE;
}
@ -420,21 +421,13 @@ static int switch_opus_get_fec_bitrate(int fs, int loss)
static switch_status_t switch_opus_info(void * encoded_data, uint32_t len, uint32_t samples_per_second, char *print_text)
{
/* nb_silk_frames: number of silk-frames (10 or 20 ms) in an opus frame: 0, 1, 2 or 3 */
/* computed from the 5 MSB (configuration) of the TOC byte (encoded_data[0]) */
/* nb_opus_frames: number of opus frames in the packet */
/* computed from the 2 LSB (p0p1) of the TOC byte */
/* p0p1 = 0 => nb_opus_frames= 1 */
/* p0p1 = 1 or 2 => nb_opus_frames= 2 */
/* p0p1 = 3 => given by the 6 LSB of encoded_data[1] */
int nb_samples, nb_silk_frames, nb_opus_frames, n, i;
int nb_samples, nb_opus_frames, nb_channels;
int audiobandwidth;
char audiobandwidth_str[32] = {0};
opus_int16 frame_sizes[48];
const unsigned char *frame_data[48];
char has_fec = 0;
uint8_t * payload = encoded_data ;
uint8_t * payload = encoded_data;
if (!encoded_data) {
return SWITCH_STATUS_FALSE;
@ -446,38 +439,19 @@ static switch_status_t switch_opus_info(void * encoded_data, uint32_t len, uint3
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: OPUS_INVALID_PACKET !\n", print_text);
}
if ((nb_opus_frames = opus_packet_parse(encoded_data, len, NULL, frame_data, frame_sizes, NULL)) < 0 ) {
if ((nb_opus_frames = opus_packet_parse(encoded_data, len, NULL, frame_data, frame_sizes, NULL)) <= 0 ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: OPUS_INVALID_PACKET ! frames: %d\n", print_text, nb_opus_frames);
return SWITCH_STATUS_FALSE;
}
nb_samples = opus_packet_get_samples_per_frame(encoded_data, samples_per_second) * nb_opus_frames;
nb_silk_frames = 0;
if ((payload[0] >> 3 ) < 12) { /* config in silk-only : NB,MB,WB */
nb_silk_frames = (payload[0] >> 3) & 0x3;
if(nb_silk_frames == 0) {
nb_silk_frames = 1;
}
if (nb_silk_frames == 1) {
for (n = 0; n <= (payload[0]&0x4) ; n++) { /* mono or stereo */
if (frame_data[0][0] & (0x80 >> ((n + 1) * (nb_silk_frames + 1) - 1))){
has_fec = 1 ; /* 1st 20ms (or 10 ms) frame has fec */
}
}
} else {
opus_int16 LBRR_flag = 0 ;
for (i=0 ; i < nb_opus_frames; i++ ) { /* only mono */
LBRR_flag = (frame_data[i][0] >> (7 - nb_silk_frames)) & 0x1;
if (LBRR_flag) {
has_fec = 1;
}
}
}
}
has_fec = switch_opus_has_fec(payload, len);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: nb_opus_frames [%d] nb_silk_frames [%d] samples [%d] audio bandwidth [%s] bytes [%d] FEC[%s]\n",
print_text, nb_opus_frames, nb_silk_frames, nb_samples, audiobandwidth_str, len, has_fec ? "yes" : "no" );
nb_channels = opus_packet_get_nb_channels(payload);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: opus_frames [%d] samples [%d] audio bandwidth [%s] bytes [%d] FEC[%s] channels[%d]\n",
print_text, nb_opus_frames, nb_samples, audiobandwidth_str, len, has_fec ? "yes" : "no", nb_channels);
return SWITCH_STATUS_SUCCESS;
}
@ -682,6 +656,11 @@ static switch_status_t switch_opus_destroy(switch_codec_t *codec)
if (context) {
if (context->decoder_object) {
switch_core_session_t *session = codec->session;
if (session) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,"Opus decoder stats: Frames[%d] PLC[%d] FEC[%d]\n",
context->decoder_stats.frame_counter, context->decoder_stats.plc_counter-context->decoder_stats.fec_counter, context->decoder_stats.fec_counter);
}
opus_decoder_destroy(context->decoder_object);
context->decoder_object = NULL;
}
@ -832,6 +811,15 @@ static switch_status_t switch_opus_decode(switch_codec_t *codec,
!encoded_data ? "PLC correction" : fec ? "FEC correction" : "decode");
}
if (plc) {
context->decoder_stats.plc_counter++;
}
if (fec) {
context->decoder_stats.fec_counter++;
}
/* a frame for which we decode FEC will be counted twice */
context->decoder_stats.frame_counter++;
samples = opus_decode(context->decoder_object, encoded_data, encoded_data_len, decoded_data, frame_size, fec);
if (samples < 0) {
@ -858,8 +846,9 @@ static switch_status_t switch_opus_encode_repacketize(switch_codec_t *codec,
int16_t *dec_ptr_buf = decoded_data;
/* work inside the available buffer to avoid other buffer allocations. *encoded_data_len will be SWITCH_RECOMMENDED_BUFFER_SIZE */
unsigned char *enc_ptr_buf = (unsigned char *)encoded_data + (len / 2);
int nb_frames = codec->implementation->microseconds_per_packet / 20000 ; /* requested ptime: 20 ms * nb_frames */
int frame_size, i, bytes = 0, want_fec = 0, toggle_fec = 0;
uint32_t nb_frames = codec->implementation->microseconds_per_packet / 20000 ; /* requested ptime: 20 ms * nb_frames */
uint32_t frame_size, i;
int bytes = 0, want_fec = 0, toggle_fec = 0;
opus_int32 ret = 0;
opus_int32 total_len = 0;
switch_status_t status = SWITCH_STATUS_SUCCESS;
@ -875,22 +864,28 @@ static switch_status_t switch_opus_encode_repacketize(switch_codec_t *codec,
if (codec->implementation->microseconds_per_packet / 1000 == 100) { /* 100 ms = 20 ms * 5 . because there is no 50 ms frame in Opus */
nb_frames = 5;
}
toggle_fec = 1;
}
frame_size = (decoded_data_len / 2) / nb_frames;
if((frame_size * nb_frames) != context->enc_frame_size) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,"Encoder Error: Decoded Datalen %u Number of frames: %d Encoder frame size: %d\n",decoded_data_len,nb_frames,context->enc_frame_size);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,"Encoder Error: Decoded Datalen %u Number of frames: %u Encoder frame size: %u\n",decoded_data_len,nb_frames,context->enc_frame_size);
switch_goto_status(SWITCH_STATUS_GENERR, end);
}
opus_repacketizer_init(rp);
dec_ptr_buf = (int16_t *)decoded_data;
for (i = 0; i < nb_frames; i++) {
/* set inband FEC ON or OFF for the next Opus frame */
if (i == (nb_frames - 1) && want_fec) {
/* When FEC is enabled for Opus frame N, LBRR is stored during regular encoding of */
/* this Opus frame N, and this LBRR data will be packed with the regular encoding */
/* data of Opus frame N+1. We enable FEC on our last Opus frame which is to be packed, just */
/* to actually have it stored in the first Opus frame, that is when switch_opus_encode_repacketize() */
/* is called again to pack the next big 80,100 or 120 ms frame. */
toggle_fec = 1; /* FEC ON for the last frame */
}
opus_encoder_ctl(context->encoder_object, OPUS_SET_INBAND_FEC(toggle_fec));
bytes = opus_encode(context->encoder_object, (opus_int16 *) dec_ptr_buf, frame_size, enc_ptr_buf, len);
/* set inband FEC off for the next frame to be packed, the current frame may contain FEC */
if (toggle_fec == 1) {
toggle_fec = 0;
opus_encoder_ctl(context->encoder_object, OPUS_SET_INBAND_FEC(toggle_fec));
}
if (bytes < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder Error: %s Decoded Datalen %u Codec NumberChans %u" \
"Len %u DecodedDate %p EncodedData %p ContextEncoderObject %p enc_frame_size: %d\n",
@ -975,7 +970,7 @@ static switch_status_t opus_load_config(switch_bool_t reload)
opus_prefs.use_jb_lookahead = switch_true(val);
} else if (!strcasecmp(key, "keep-fec-enabled")) { /* encoder */
opus_prefs.keep_fec = atoi(val);
} else if (!strcasecmp(key, "advertise_useinbandfec")) { /*decoder, has meaning only for FMTP: useinbandfec=1 by default */
} else if (!strcasecmp(key, "advertise-useinbandfec")) { /*decoder, has meaning only for FMTP: useinbandfec=1 by default */
opus_prefs.fec_decode = atoi(val);
} else if (!strcasecmp(key, "maxaveragebitrate")) {
opus_prefs.maxaveragebitrate = atoi(val);
@ -1080,13 +1075,13 @@ static switch_status_t switch_opus_control(switch_codec_t *codec,
switch(cmd) {
case SCC_CODEC_SPECIFIC:
{
const char *cmd = (const char *)cmd_data;
const char *command = (const char *)cmd_data;
const char *arg = (const char *)cmd_arg;
switch_codec_control_type_t reply_type = SCCT_STRING;
const char *reply = "ERROR INVALID COMMAND";
if (!zstr(cmd)) {
if (!strcasecmp(cmd, "jb_lookahead")) {
if (!zstr(command)) {
if (!strcasecmp(command, "jb_lookahead")) {
if (!zstr(arg)) {
context->use_jb_lookahead = switch_true(arg);
}

@ -227,6 +227,7 @@ struct vpx_context {
uint8_t decoder_init;
switch_buffer_t *vpx_packet_buffer;
int got_key_frame;
int no_key_frame;
int got_start_frame;
uint32_t last_received_timestamp;
switch_bool_t last_received_complete_picture;
@ -273,6 +274,7 @@ static switch_status_t init_decoder(switch_codec_t *codec)
context->last_received_complete_picture = 0;
context->decoder_init = 1;
context->got_key_frame = 0;
context->no_key_frame = 0;
context->got_start_frame = 0;
// the types of post processing to be done, should be combination of "vp8_postproc_level"
ppcfg.post_proc_flag = VP8_DEBLOCK;//VP8_DEMACROBLOCK | VP8_DEBLOCK;
@ -298,7 +300,8 @@ static switch_status_t init_encoder(switch_codec_t *codec)
vpx_codec_enc_cfg_t *config = &context->config;
int token_parts = 1;
int cpus = switch_core_cpu_count();
int sane;
if (!context->codec_settings.video.width) {
context->codec_settings.video.width = 1280;
}
@ -310,15 +313,19 @@ static switch_status_t init_encoder(switch_codec_t *codec)
if (context->codec_settings.video.bandwidth == -1) {
context->codec_settings.video.bandwidth = 0;
}
if (context->codec_settings.video.bandwidth) {
context->bandwidth = context->codec_settings.video.bandwidth;
} else {
context->bandwidth = switch_calc_bitrate(context->codec_settings.video.width, context->codec_settings.video.height, 0, 0);
}
if (context->bandwidth > 40960) {
context->bandwidth = 40960;
sane = switch_calc_bitrate(context->codec_settings.video.width, context->codec_settings.video.height, 4, 30);
if (context->bandwidth > sane) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_WARNING, "BITRATE TRUNCATED TO %d\n", sane);
context->bandwidth = sane;
}
context->pkt = NULL;
@ -812,7 +819,18 @@ static switch_status_t switch_vpx_decode(switch_codec_t *codec, switch_frame_t *
is_start = (*(unsigned char *)frame->data & 0x10);
is_keyframe = IS_VP8_KEY_FRAME((uint8_t *)frame->data);
}
if (context->got_key_frame <= 0) {
context->no_key_frame++;
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "no keyframe, %d\n", context->no_key_frame);
if (context->no_key_frame > 50) {
if ((is_keyframe = is_start)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "no keyframe, treating start as key.\n");
}
}
}
// if (is_keyframe) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got key %d\n", is_keyframe);
if (context->need_decoder_reset != 0) {
@ -845,9 +863,7 @@ static switch_status_t switch_vpx_decode(switch_codec_t *codec, switch_frame_t *
if (is_keyframe) {
if (context->got_key_frame <= 0) {
context->got_key_frame = 1;
if (!is_keyframe) {
get_refresh = 1;
}
context->no_key_frame = 0;
} else {
context->got_key_frame++;
}
@ -855,6 +871,9 @@ static switch_status_t switch_vpx_decode(switch_codec_t *codec, switch_frame_t *
if ((--context->got_key_frame % 200) == 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Waiting for key frame %d\n", context->got_key_frame);
}
get_refresh = 1;
if (!context->got_start_frame) {
switch_goto_status(SWITCH_STATUS_MORE_DATA, end);
}

@ -136,7 +136,7 @@ again:
break;
}
if (*len != orig_len) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "sent %ld of %ld\n", *len, orig_len);
if (*len != orig_len) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "sent %"SWITCH_SIZE_T_FMT" of %"SWITCH_SIZE_T_FMT"\n", *len, orig_len);
buf += *len;
remaining -= *len;
*len = remaining;

@ -130,9 +130,12 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="mod_skypopen.c">
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/analyze:stacksize65535</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">/analyze:stacksize65535</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/analyze:stacksize65535</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</AdditionalOptions>
</ClCompile>
@ -141,9 +144,12 @@
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4305;4306;28193;4244;4267;4324;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4305;4306;28193;4244;4267;4324;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4305;4306;28193;4244;4267;4324;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/analyze:stacksize32768</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">/analyze:stacksize32768</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/analyze:stacksize32768</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</AdditionalOptions>
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</AdditionalOptions>
</ClCompile>

@ -2162,11 +2162,19 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
const char *call_info = switch_channel_get_variable(channel, "presence_call_info_full");
const char *b_sdp = NULL;
int is_proxy = 0, is_3pcc = 0;
int send_sip_code = 183;
const char * p_send_sip_msg = sip_183_Session_progress;
b_sdp = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE);
is_proxy = (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA));
is_3pcc = (sofia_test_pflag(tech_pvt->profile, PFLAG_3PCC_PROXY) && sofia_test_flag(tech_pvt, TFLAG_3PCC));
// send 180 instead of 183 if variable "early_use_180" is "true"
if (switch_true(switch_channel_get_variable(channel, "early_use_180"))) {
send_sip_code = 180;
p_send_sip_msg = sip_180_Ringing;
}
if (b_sdp && is_proxy && !is_3pcc) {
switch_core_media_set_local_sdp(session, b_sdp, SWITCH_TRUE);
@ -2205,7 +2213,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_PROGRESS_HEADER_PREFIX);
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_183_SESSION_PROGRESS,
nua_respond(tech_pvt->nh, send_sip_code, p_send_sip_msg,
TAG_IF(is_proxy, NUTAG_AUTOANSWER(0)),
SIPTAG_CONTACT_STR(tech_pvt->profile->url),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
@ -2217,7 +2225,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
TAG_IF(switch_stristr("update_display", tech_pvt->x_freeswitch_support_remote),
SIPTAG_HEADER_STR("X-FS-Support: " FREESWITCH_SUPPORT)), TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_183_SESSION_PROGRESS,
nua_respond(tech_pvt->nh, send_sip_code, p_send_sip_msg,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->profile->url),
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
@ -2341,7 +2349,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh,
SIP_183_SESSION_PROGRESS,
send_sip_code, p_send_sip_msg,
NUTAG_AUTOANSWER(0),
TAG_IF(sticky, NUTAG_PROXY(tech_pvt->record_route)),
TAG_IF(cid, SIPTAG_HEADER_STR(cid)),
@ -2356,7 +2364,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
SIPTAG_HEADER_STR("X-FS-Support: " FREESWITCH_SUPPORT)), TAG_END());
} else {
nua_respond(tech_pvt->nh,
SIP_183_SESSION_PROGRESS,
send_sip_code, p_send_sip_msg,
NUTAG_AUTOANSWER(0),
NUTAG_MEDIA_ENABLE(0),
TAG_IF(sticky, NUTAG_PROXY(tech_pvt->record_route)),

@ -5850,7 +5850,7 @@ static void sofia_handle_sip_r_options(switch_core_session_t *session, int statu
int ping_time = 0;
if (sofia_private && sofia_private->ping_sent) {
ping_time = switch_time_now() - sofia_private->ping_sent;
ping_time = (int)(switch_time_now() - sofia_private->ping_sent);
}
sip_user_status.status = ping_status;
@ -8894,6 +8894,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t
switch_core_session_t *other_session;
if (switch_channel_test_flag(channel, CF_VIDEO)) {
switch_core_media_gen_key_frame(session);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
sofia_glue_build_vid_refresh_message(other_session, sip->sip_payload->pl_data);
switch_core_session_rwunlock(other_session);

@ -1234,6 +1234,12 @@ static void detach_calls(jsock_t *jsock)
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
continue;
}
if (switch_channel_test_flag(tech_pvt->channel, CF_VIDEO_ONLY)) {
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_NORMAL_CLEARING);
continue;
}
switch_core_session_stop_media(tech_pvt->session);
tech_pvt->detach_time = switch_epoch_time_now(NULL);
globals.detached++;
@ -1844,15 +1850,79 @@ static void client_run(jsock_t *jsock)
switch_ssize_t bytes;
ws_opcode_t oc;
uint8_t *data;
bytes = ws_read_frame(&jsock->ws, &oc, &data);
if (bytes < 0) {
die("BAD READ %" SWITCH_SSIZE_T_FMT "\n", bytes);
break;
}
if (bytes) {
char *s = (char *) data;
if (*s == '#') {
char repl[2048] = "";
switch_time_t a, b;
if (s[1] == 'S' && s[2] == 'P') {
if (s[3] == 'U') {
int i, size = 0;
char *p = s+4;
int loops = 0;
int rem = 0;
int dur = 0, j = 0;
if (!(size = atoi(p))) {
continue;
}
a = switch_time_now();
do {
bytes = ws_read_frame(&jsock->ws, &oc, &data);
s = (char *) data;
} while (bytes && data && s[0] == '#' && s[3] == 'B');
b = switch_time_now();
if (!bytes || !data) continue;
if (s[0] != '#') goto nm;
switch_snprintf(repl, sizeof(repl), "#SPU %ld", (b - a) / 1000);
ws_write_frame(&jsock->ws, WSOC_TEXT, repl, strlen(repl));
loops = size / 1024;
rem = size % 1024;
switch_snprintf(repl, sizeof(repl), "#SPB ");
memset(repl+4, '.', 1024);
for (j = 0; j < 10 ; j++) {
int ddur = 0;
a = switch_time_now();
for (i = 0; i < loops; i++) {
ws_write_frame(&jsock->ws, WSOC_TEXT, repl, 1024);
}
if (rem) {
ws_write_frame(&jsock->ws, WSOC_TEXT, repl, rem);
}
b = switch_time_now();
ddur += ((b - a) / 1000);
dur += ddur;
}
dur /= j+1;
switch_snprintf(repl, sizeof(repl), "#SPD %d", dur);
ws_write_frame(&jsock->ws, WSOC_TEXT, repl, strlen(repl));
}
}
continue;
}
nm:
if (process_input(jsock, data, bytes) != SWITCH_STATUS_SUCCESS) {
die("Input Error\n");
}
@ -2485,6 +2555,10 @@ static int verto_recover_callback(switch_core_session_t *session)
const char *profile_name = NULL, *jsock_uuid_str = NULL;
switch_channel_t *channel = switch_core_session_get_channel(session);
if (switch_channel_test_flag(channel, CF_VIDEO_ONLY)) {
return 0;
}
PROTECT_INTERFACE(verto_endpoint_interface);
profile_name = switch_channel_get_variable(channel, "verto_profile_name");
@ -3271,7 +3345,7 @@ static switch_bool_t verto__info_func(const char *method, cJSON *params, jsock_t
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, *json_ptr = NULL;
cJSON *obj = cJSON_CreateObject(), *screenShare = NULL, *dedEnc = NULL, *mirrorInput, *json_ptr = NULL, *bandwidth = NULL;
switch_core_session_t *session = NULL;
switch_channel_t *channel;
switch_event_t *var_event;
@ -3281,7 +3355,7 @@ static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock
cJSON *dialog;
verto_pvt_t *tech_pvt;
char name[512];
const char *var, *destination_number, *call_id = NULL, *sdp = NULL, *bandwidth = NULL,
const char *var, *destination_number, *call_id = NULL, *sdp = NULL,
*caller_id_name = NULL, *caller_id_number = NULL, *remote_caller_id_name = NULL, *remote_caller_id_number = NULL,*context = NULL;
switch_event_header_t *hp;
@ -3341,6 +3415,7 @@ static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock
}
if ((screenShare = cJSON_GetObjectItem(dialog, "screenShare")) && screenShare->type == cJSON_True) {
switch_channel_set_variable(channel, "video_screen_share", "true");
switch_channel_set_flag(channel, CF_VIDEO_ONLY);
}
@ -3353,15 +3428,41 @@ static switch_bool_t verto__invite_func(const char *method, cJSON *params, jsock
switch_channel_set_flag(channel, CF_VIDEO_MIRROR_INPUT);
}
if ((bandwidth = cJSON_GetObjectCstr(dialog, "outgoingBandwidth"))) {
if (strcasecmp(bandwidth, "default")) {
switch_channel_set_variable(channel, "rtp_video_max_bandwidth_in", bandwidth);
if ((bandwidth = cJSON_GetObjectItem(dialog, "outgoingBandwidth"))) {
int core_bw = 0, bwval = 0;
const char *val;
if ((val = switch_channel_get_variable_dup(channel, "rtp_video_max_bandwidth_in", SWITCH_FALSE, -1))) {
core_bw = switch_parse_bandwidth_string(val);
}
if (!zstr(bandwidth->valuestring) && strcasecmp(bandwidth->valuestring, "default")) {
bwval = atoi(bandwidth->valuestring);
} else if (bandwidth->valueint) {
bwval = bandwidth->valueint;
}
if (bwval <= 0 || (core_bw && bwval < core_bw)) {
switch_channel_set_variable_printf(channel, "rtp_video_max_bandwidth_in", "%d", bwval);
}
}
if ((bandwidth = cJSON_GetObjectCstr(dialog, "incomingBandwidth"))) {
if (strcasecmp(bandwidth, "default")) {
switch_channel_set_variable(channel, "rtp_video_max_bandwidth_out", bandwidth);
if ((bandwidth = cJSON_GetObjectItem(dialog, "incomingBandwidth"))) {
int core_bw = 0, bwval = 0;
const char *val;
if ((val = switch_channel_get_variable_dup(channel, "rtp_video_max_bandwidth_out", SWITCH_FALSE, -1))) {
core_bw = switch_parse_bandwidth_string(val);
}
if (!zstr(bandwidth->valuestring) && strcasecmp(bandwidth->valuestring, "default")) {
bwval = atoi(bandwidth->valuestring);
} else if (bandwidth->valueint) {
bwval = bandwidth->valueint;
}
if (bwval <= 0 || (core_bw && bwval < core_bw)) {
switch_channel_set_variable_printf(channel, "rtp_video_max_bandwidth_out", "%d", bwval);
}
}

@ -264,7 +264,7 @@ int ws_handshake(wsh_t *wsh)
}
}
if (bytes > sizeof(wsh->buffer) -1) {
if (bytes > wsh->buflen -1) {
goto err;
}
@ -362,7 +362,7 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
}
}
} while (r == -1 && err == SSL_ERROR_WANT_READ && wsh->x < 100);
} while (r == -1 && err == SSL_ERROR_WANT_READ && wsh->x < 1000);
goto end;
}
@ -382,7 +382,7 @@ ssize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes, int block)
ms_sleep(10);
}
}
} while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 100);
} while (r == -1 && xp_is_blocking(xp_errno()) && wsh->x < 1000);
if (wsh->x >= 1000 || (block && wsh->x >= 100)) {
r = -1;
@ -596,7 +596,15 @@ int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock, int
wsh->close_sock = 1;
}
wsh->buflen = sizeof(wsh->buffer);
wsh->buflen = 1024 * 64;
wsh->bbuflen = wsh->buflen;
wsh->buffer = malloc(wsh->buflen);
wsh->bbuffer = malloc(wsh->bbuflen);
//printf("init %p %ld\n", (void *) wsh->bbuffer, wsh->bbuflen);
//memset(wsh->buffer, 0, wsh->buflen);
//memset(wsh->bbuffer, 0, wsh->bbuflen);
wsh->secure = ssl_ctx ? 1 : 0;
setup_socket(sock);
@ -644,6 +652,12 @@ void ws_destroy(wsh_t *wsh)
SSL_free(wsh->ssl);
wsh->ssl = NULL;
}
if (wsh->buffer) free(wsh->buffer);
if (wsh->bbuffer) free(wsh->bbuffer);
wsh->buffer = wsh->bbuffer = NULL;
}
ssize_t ws_close(wsh_t *wsh, int16_t reason)
@ -685,6 +699,20 @@ ssize_t ws_close(wsh_t *wsh, int16_t reason)
}
uint64_t hton64(uint64_t val)
{
if (__BYTE_ORDER == __BIG_ENDIAN) return (val);
else return __bswap_64(val);
}
uint64_t ntoh64(uint64_t val)
{
if (__BYTE_ORDER == __BIG_ENDIAN) return (val);
else return __bswap_64(val);
}
ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
{
@ -692,6 +720,10 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
char *maskp;
int ll = 0;
int frag = 0;
int blen;
wsh->body = wsh->bbuffer;
wsh->packetlen = 0;
again:
need = 2;
@ -745,12 +777,11 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
int fin = (wsh->buffer[0] >> 7) & 1;
int mask = (wsh->buffer[1] >> 7) & 1;
if (fin) {
if (*oc == WSOC_CONTINUATION) {
frag = 1;
} else {
frag = 0;
}
if (!fin && *oc != WSOC_CONTINUATION) {
frag = 1;
} else if (fin && *oc == WSOC_CONTINUATION) {
frag = 0;
}
if (mask) {
@ -765,23 +796,33 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
wsh->plen = wsh->buffer[1] & 0x7f;
wsh->payload = &wsh->buffer[2];
if (wsh->plen == 127) {
uint64_t *u64;
int more = 0;
need += 8;
if (need > wsh->datalen) {
/* too small - protocol err */
*oc = WSOC_CLOSE;
return ws_close(wsh, WS_PROTO_ERR);
}
//*oc = WSOC_CLOSE;
//return ws_close(wsh, WS_PROTO_ERR);
more = ws_raw_read(wsh, wsh->buffer + wsh->datalen, need - wsh->datalen, WS_BLOCK);
if (more < need - wsh->datalen) {
*oc = WSOC_CLOSE;
return ws_close(wsh, WS_PROTO_ERR);
} else {
wsh->datalen += more;
}
}
u64 = (uint64_t *) wsh->payload;
wsh->payload += 8;
wsh->plen = ntohl((u_long)*u64);
wsh->plen = ntoh64(*u64);
} else if (wsh->plen == 126) {
uint16_t *u16;
@ -811,16 +852,30 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
return ws_close(wsh, WS_PROTO_ERR);
}
if ((need + wsh->datalen) > (ssize_t)wsh->buflen) {
/* too big - Ain't nobody got time fo' dat */
*oc = WSOC_CLOSE;
return ws_close(wsh, WS_DATA_TOO_BIG);
blen = wsh->body - wsh->bbuffer;
if (need + blen > (ssize_t)wsh->bbuflen) {
void *tmp;
wsh->bbuflen = need + blen + wsh->rplen;
if ((tmp = realloc(wsh->bbuffer, wsh->bbuflen))) {
wsh->bbuffer = tmp;
} else {
abort();
}
wsh->body = wsh->bbuffer + blen;
}
wsh->rplen = wsh->plen - need;
if (wsh->rplen) {
memcpy(wsh->body, wsh->payload, wsh->rplen);
}
while(need) {
ssize_t r = ws_raw_read(wsh, wsh->payload + wsh->rplen, need, WS_BLOCK);
ssize_t r = ws_raw_read(wsh, wsh->body + wsh->rplen, need, WS_BLOCK);
if (r < 1) {
/* invalid read - protocol err .. */
@ -837,28 +892,30 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
ssize_t i;
for (i = 0; i < wsh->datalen; i++) {
wsh->payload[i] ^= maskp[i % 4];
wsh->body[i] ^= maskp[i % 4];
}
}
if (*oc == WSOC_PING) {
ws_write_frame(wsh, WSOC_PONG, wsh->payload, wsh->rplen);
ws_write_frame(wsh, WSOC_PONG, wsh->body, wsh->rplen);
goto again;
}
*(wsh->body+wsh->rplen) = '\0';
wsh->packetlen += wsh->rplen;
wsh->body += wsh->rplen;
if (frag) {
goto again;
}
*data = (uint8_t *)wsh->bbuffer;
*(wsh->payload+wsh->rplen) = '\0';
*data = (uint8_t *)wsh->payload;
//printf("READ[%ld][%d]-----------------------------:\n[%s]\n-------------------------------\n", wsh->rplen, *oc, (char *)*data);
//printf("READ[%ld][%d]-----------------------------:\n[%s]\n-------------------------------\n", wsh->packetlen, *oc, (char *)*data);
return wsh->rplen;
return wsh->packetlen;
}
break;
default:
@ -871,6 +928,7 @@ ssize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
}
}
#if 0
ssize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes)
{
@ -885,6 +943,7 @@ ssize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes)
return bytes;
}
ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc)
{
ssize_t r = 0;
@ -899,7 +958,7 @@ ssize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc)
return r;
}
#endif
ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes)
{
@ -934,7 +993,7 @@ ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes)
hlen += 8;
u64 = (uint64_t *) &hdr[2];
*u64 = htonl(bytes);
*u64 = hton64(bytes);
}
if (wsh->write_buffer_len < (hlen + bytes + 1)) {

@ -78,15 +78,18 @@ typedef enum {
typedef struct wsh_s {
ws_socket_t sock;
char buffer[65536];
char wbuffer[65536];
char *buffer;
char *bbuffer;
char *body;
char *uri;
size_t buflen;
size_t bbuflen;
ssize_t datalen;
ssize_t wdatalen;
char *payload;
ssize_t plen;
ssize_t rplen;
ssize_t packetlen;
SSL *ssl;
int handshake;
uint8_t down;

@ -322,7 +322,7 @@ static switch_status_t my_on_reporting_cb(switch_core_session_t *session, cdr_pr
memset(cdr_text_escaped, 0, need_bytes);
if (profile->encode == ENCODING_DEFAULT) {
headers = switch_curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
switch_url_encode(cdr_text, cdr_text_escaped, need_bytes);
switch_url_encode_opt(cdr_text, cdr_text_escaped, need_bytes, SWITCH_TRUE);
} else {
headers = switch_curl_slist_append(headers, "Content-Type: application/x-www-form-base64-encoded");
switch_b64_encode((unsigned char *) cdr_text, need_bytes / 3, (unsigned char *) cdr_text_escaped, need_bytes);

@ -493,7 +493,7 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void
break;
}
source->prebuf = source->samples * 2 * source->channels;
source->prebuf = (uint32_t)(source->samples * 2 * source->channels);
if (!source->total) {
flush_video_queue(source->video_q);
@ -806,8 +806,8 @@ static switch_status_t local_stream_file_read_video(switch_file_handle_t *handle
local_stream_context_t *context = handle->private_info;
switch_status_t status;
switch_time_t now;
int fps = (int)ceil(handle->mm.fps);
int min_qsize = fps;
unsigned int fps = (unsigned int)ceil(handle->mm.fps);
unsigned int min_qsize = fps;
if (!(context->ready && context->source->ready)) {
return SWITCH_STATUS_FALSE;

@ -1707,6 +1707,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_shout_load)
supported_formats[0] = "shout";
supported_formats[1] = "mp3";
supported_formats[2] = "mpga";
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);

@ -88,6 +88,11 @@ void Session::setLUA(lua_State * state)
int Session::originate(CoreSession *a_leg_session, char *dest, int timeout)
{
if (zstr(dest)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing destination.\n");
return 0;
}
int x = CoreSession::originate(a_leg_session, dest, timeout);
if (x) {
@ -356,7 +361,7 @@ Dbh::Dbh(char *dsn, char *user, char *pass)
dsn = tmp;
}
if (switch_cache_db_get_db_handle_dsn(&dbh, dsn) == SWITCH_STATUS_SUCCESS) {
if (!zstr(dsn) && switch_cache_db_get_db_handle_dsn(&dbh, dsn) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DBH handle %p Connected.\n", (void *) dbh);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Connection failed. DBH NOT Connected.\n");
@ -391,8 +396,12 @@ bool Dbh::connected()
bool Dbh::test_reactive(char *test_sql, char *drop_sql, char *reactive_sql)
{
if (dbh) {
if (switch_cache_db_test_reactive(dbh, test_sql, drop_sql, reactive_sql) == SWITCH_TRUE) {
return true;
if (!zstr(test_sql) && !zstr(reactive_sql)) {
if (switch_cache_db_test_reactive(dbh, test_sql, drop_sql, reactive_sql) == SWITCH_TRUE) {
return true;
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing parameters.\n");
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n");
@ -431,6 +440,11 @@ int Dbh::query_callback(void *pArg, int argc, char **argv, char **cargv)
bool Dbh::query(char *sql, SWIGLUA_FN lua_fun)
{
if (zstr(sql)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing SQL query.\n");
return false;
}
if (dbh) {
if (lua_fun.L) {
if (switch_cache_db_execute_sql_callback(dbh, sql, query_callback, &lua_fun, NULL) == SWITCH_STATUS_SUCCESS) {
@ -459,6 +473,11 @@ int Dbh::affected_rows()
int Dbh::load_extension(const char *extension)
{
if (zstr(extension)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing extension name.\n");
return 0;
}
if (dbh) {
return switch_cache_db_load_extension(dbh, extension);
}

@ -365,6 +365,7 @@ switch_status_t mod_xml_radius_add_params(switch_core_session_t *session, switch
char *skip_if_set = (char *) switch_xml_attr(param, "skip_if_set");
char *format = (char *) switch_xml_attr(param, "format");
char *other_leg = (char *) switch_xml_attr(param, "other_leg");
char *regex = (char *) switch_xml_attr(param, "regex");
attribute = rc_dict_findattr(handle, var);
@ -528,6 +529,24 @@ switch_status_t mod_xml_radius_add_params(switch_core_session_t *session, switch
}
}
if ( regex && val ) {
switch_regex_t *re = NULL;
int ovector[30];
int proceed;
char replace[1024] = "";
proceed = 0;
proceed = switch_regex_perform(val, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));
if ( proceed > 0 ) {
switch_regex_copy_substring(val, ovector, proceed, proceed - 1, replace, sizeof(replace));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "original value: %s, regex: %s, result: %s\n", val, regex, replace);
val = replace;
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "original value: %s, regex: %s, result: nomatch, value left intact\n", val, regex);
}
switch_regex_safe_free(re);
}
if ( val == NULL && val_default != NULL) {
av_value = switch_mprintf(format, val_default);
} else {

@ -944,32 +944,34 @@ SWITCH_DECLARE(const char *) switch_channel_get_variable_dup(switch_channel_t *c
switch_mutex_lock(channel->profile_mutex);
if (channel->scope_variables) {
switch_event_t *ep;
if (!zstr(varname)) {
if (channel->scope_variables) {
switch_event_t *ep;
for (ep = channel->scope_variables; ep; ep = ep->next) {
if ((v = switch_event_get_header_idx(ep, varname, idx))) {
break;
}
}
}
if (!v && (!channel->variables || !(v = switch_event_get_header_idx(channel->variables, varname, idx)))) {
switch_caller_profile_t *cp = switch_channel_get_caller_profile(channel);
if (cp) {
if (!strncmp(varname, "aleg_", 5)) {
cp = cp->originator_caller_profile;
varname += 5;
} else if (!strncmp(varname, "bleg_", 5)) {
cp = cp->originatee_caller_profile;
varname += 5;
for (ep = channel->scope_variables; ep; ep = ep->next) {
if ((v = switch_event_get_header_idx(ep, varname, idx))) {
break;
}
}
}
if (!cp || !(v = switch_caller_get_field_by_name(cp, varname))) {
if ((vdup = switch_core_get_variable_pdup(varname, switch_core_session_get_pool(channel->session)))) {
v = vdup;
if (!v && (!channel->variables || !(v = switch_event_get_header_idx(channel->variables, varname, idx)))) {
switch_caller_profile_t *cp = switch_channel_get_caller_profile(channel);
if (cp) {
if (!strncmp(varname, "aleg_", 5)) {
cp = cp->originator_caller_profile;
varname += 5;
} else if (!strncmp(varname, "bleg_", 5)) {
cp = cp->originatee_caller_profile;
varname += 5;
}
}
if (!cp || !(v = switch_caller_get_field_by_name(cp, varname))) {
if ((vdup = switch_core_get_variable_pdup(varname, switch_core_session_get_pool(channel->session)))) {
v = vdup;
}
}
}
}

@ -393,11 +393,13 @@ SWITCH_DECLARE(char *) switch_core_get_variable_dup(const char *varname)
{
char *val = NULL, *v;
switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) {
val = strdup(v);
if (varname) {
switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) {
val = strdup(v);
}
switch_thread_rwlock_unlock(runtime.global_var_rwlock);
}
switch_thread_rwlock_unlock(runtime.global_var_rwlock);
return val;
}
@ -406,11 +408,13 @@ SWITCH_DECLARE(char *) switch_core_get_variable_pdup(const char *varname, switch
{
char *val = NULL, *v;
switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) {
val = switch_core_strdup(pool, v);
if (varname) {
switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) {
val = switch_core_strdup(pool, v);
}
switch_thread_rwlock_unlock(runtime.global_var_rwlock);
}
switch_thread_rwlock_unlock(runtime.global_var_rwlock);
return val;
}
@ -2300,9 +2304,15 @@ static void switch_load_core_config(const char *file)
} else if (!strcasecmp(var, "rtp-enable-zrtp")) {
switch_core_set_variable("zrtp_enabled", val);
#endif
} else if (!strcasecmp(var, "switchname") && !zstr(val)) {
} else if (!strcasecmp(var, "switchname") && !zstr(val)) {
runtime.switchname = switch_core_strdup(runtime.memory_pool, val);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Set switchname to %s\n", runtime.switchname);
} else if (!strcasecmp(var, "rtp-retain-crypto-keys")) {
if (switch_true(val)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"rtp-retain-crypto-keys enabled. Could be used to decrypt secure media.\n");
}
switch_core_set_variable("rtp_retain_crypto_keys", val);
}
}
}

@ -1750,7 +1750,8 @@ SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *ses
const char *abs, *codec_string = NULL;
const char *ocodec = NULL, *val;
switch_media_handle_t *smh;
char *tmp_codec_string;
switch_assert(session);
if (!(smh = session->media_handle)) {
@ -1806,16 +1807,16 @@ SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *ses
}
ready:
if (codec_string) {
char *tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
switch_channel_set_variable(session->channel, "rtp_use_codec_string", codec_string);
smh->codec_order_last = switch_separate_string(tmp_codec_string, ',', smh->codec_order, SWITCH_MAX_CODECS);
smh->mparams->num_codecs = switch_loadable_module_get_codecs_sorted(smh->codecs, SWITCH_MAX_CODECS, smh->codec_order, smh->codec_order_last);
} else {
smh->mparams->num_codecs = switch_loadable_module_get_codecs(smh->codecs, sizeof(smh->codecs) / sizeof(smh->codecs[0]));
if (!codec_string) {
codec_string = "PCMU@20i,PCMA@20i,speex@20i";
}
tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
switch_channel_set_variable(session->channel, "rtp_use_codec_string", codec_string);
smh->codec_order_last = switch_separate_string(tmp_codec_string, ',', smh->codec_order, SWITCH_MAX_CODECS);
smh->mparams->num_codecs = switch_loadable_module_get_codecs_sorted(smh->codecs, SWITCH_MAX_CODECS, smh->codec_order, smh->codec_order_last);
}
@ -3169,6 +3170,7 @@ static switch_status_t check_ice(switch_media_handle_t *smh, switch_media_type_t
engine->ice_in.is_chosen[1] = 0;
engine->ice_in.cand_idx[0] = 0;
engine->ice_in.cand_idx[1] = 0;
engine->remote_ssrc = 0;
if (m) {
attr = m->m_attributes;
@ -3428,7 +3430,6 @@ static switch_status_t check_ice(switch_media_handle_t *smh, switch_media_type_t
}
if (switch_channel_test_flag(smh->session->channel, CF_REINVITE)) {
if (switch_rtp_ready(engine->rtp_session) && engine->ice_in.cands[engine->ice_in.chosen[0]][0].ready && engine->new_ice) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_INFO, "RE-Activating %s ICE\n", type2str(type));
@ -3931,10 +3932,12 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s
switch_channel_set_variable(session->channel, "media_audio_mode", NULL);
}
if (sendonly) {
a_engine->smode = sdp_media_flow(sdp_sendonly);
} else if (recvonly) {
a_engine->smode = sdp_media_flow(sdp_recvonly);
if (sdp_type == SDP_TYPE_RESPONSE) {
if (sendonly) {
a_engine->smode = sdp_media_flow(sdp_sendonly);
} else if (recvonly) {
a_engine->smode = sdp_media_flow(sdp_recvonly);
}
}
@ -4379,8 +4382,13 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s
switch_snprintf(tmp, sizeof(tmp), "%d", a_engine->cur_payload_map->recv_pt);
switch_channel_set_variable(session->channel, "rtp_audio_recv_pt", tmp);
if (switch_core_codec_ready(&a_engine->read_codec) && strcasecmp(matches[0].imp->iananame, a_engine->read_codec.implementation->iananame)) {
if (switch_core_codec_ready(&a_engine->read_codec) &&
(strcasecmp(matches[0].imp->iananame, a_engine->read_codec.implementation->iananame) ||
matches[0].imp->microseconds_per_packet != a_engine->read_codec.implementation->microseconds_per_packet ||
matches[0].imp->samples_per_second != a_engine->read_codec.implementation->samples_per_second
)) {
a_engine->reset_codec = 1;
}
@ -7173,24 +7181,12 @@ SWITCH_DECLARE(void)switch_core_media_set_local_sdp(switch_core_session_t *sessi
static void add_fb(char *buf, uint32_t buflen, int pt, int fir, int nack, int pli, int tmmbr)
{
const char *zfir = "";
const char *ztmmbr = "";
char *sp = "";
if (fir) {
zfir = "fir";
switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "a=rtcp-fb:%d ccm fir\n", pt);
}
if (tmmbr) {
ztmmbr = "tmmbr";
}
if (fir && tmmbr) {
sp = " ";
}
if (!zstr(zfir) || !zstr(ztmmbr)) {
switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "a=rtcp-fb:%d ccm %s%s%s\n", pt, zfir, sp, ztmmbr);
switch_snprintf(buf + strlen(buf), buflen - strlen(buf), "a=rtcp-fb:%d ccm tmmbr\n", pt);
}
if (nack) {
@ -8894,6 +8890,30 @@ 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_media_handle_t *smh;
switch_rtp_engine_t *engine;
switch_status_t status = SWITCH_STATUS_FALSE;
if (!(smh = session->media_handle)) {
return SWITCH_STATUS_FALSE;
}
if (switch_channel_down(session->channel)) {
return SWITCH_STATUS_FALSE;
}
engine = &smh->engines[type];
if (switch_core_codec_ready(&engine->write_codec)) {
status = switch_core_codec_control(&engine->write_codec, SCC_VIDEO_BANDWIDTH,
SCCT_INT, &bitrate, SCCT_NONE, NULL, NULL, NULL);
}
return status;
}
//?
SWITCH_DECLARE(switch_status_t) switch_core_media_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
{
@ -10769,12 +10789,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_cor
bug_frame.img = img;
if (bp->callback && switch_test_flag(bp, SMBF_WRITE_VIDEO_PING)) {
bp->ping_frame = &bug_frame;
bp->video_ping_frame = &bug_frame;
if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE_VIDEO_PING) == SWITCH_FALSE
|| (bp->stop_time && bp->stop_time <= switch_epoch_time_now(NULL))) {
ok = SWITCH_FALSE;
}
bp->ping_frame = NULL;
bp->video_ping_frame = NULL;
}
if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM_BLEG) && !patched) {
@ -10874,12 +10894,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
return SWITCH_STATUS_FALSE;
}
if (switch_channel_test_flag(session->channel, CF_VIDEO_PAUSE_READ)) {
*frame = &runtime.dummy_cng_frame;
switch_yield(20000);
return SWITCH_STATUS_SUCCESS;
}
if (session->endpoint_interface->io_routines->read_video_frame) {
if ((status = session->endpoint_interface->io_routines->read_video_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
for (ptr = session->event_hooks.video_read_frame; ptr; ptr = ptr->next) {
@ -10890,6 +10904,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
}
}
if (switch_channel_test_flag(session->channel, CF_VIDEO_PAUSE_READ)) {
*frame = &runtime.dummy_cng_frame;
switch_cond_next();
return SWITCH_STATUS_SUCCESS;
}
if (status == SWITCH_STATUS_INUSE) {
*frame = &runtime.dummy_cng_frame;
switch_cond_next();
@ -11002,12 +11022,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core
if (bp->callback && switch_test_flag(bp, SMBF_READ_VIDEO_PING)) {
bp->ping_frame = *frame;
bp->video_ping_frame = *frame;
if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ_VIDEO_PING) == SWITCH_FALSE
|| (bp->stop_time && bp->stop_time <= switch_epoch_time_now(NULL))) {
ok = SWITCH_FALSE;
}
bp->ping_frame = NULL;
bp->video_ping_frame = NULL;
}
if (switch_core_media_bug_test_flag(bp, SMBF_SPY_VIDEO_STREAM) && !patched) {

@ -90,7 +90,7 @@ SWITCH_DECLARE(switch_core_session_t *) switch_core_media_bug_get_session(switch
SWITCH_DECLARE(switch_frame_t *) switch_core_media_bug_get_video_ping_frame(switch_media_bug_t *bug)
{
return bug->ping_frame;
return bug->video_ping_frame;
}
SWITCH_DECLARE(switch_frame_t *) switch_core_media_bug_get_write_replace_frame(switch_media_bug_t *bug)
@ -603,17 +603,15 @@ static void *SWITCH_THREAD_FUNC video_bug_thread(switch_thread_t *thread, void *
}
switch_thread_rwlock_rdlock(bug->session->bug_rwlock);
//switch_mutex_lock(bug->read_mutex);
frame.img = other_q ? IMG : img;
bug->ping_frame = &frame;
bug->video_ping_frame = &frame;
if (bug->callback) {
if (bug->callback(bug, bug->user_data, SWITCH_ABC_TYPE_STREAM_VIDEO_PING) == SWITCH_FALSE
|| (bug->stop_time && bug->stop_time <= switch_epoch_time_now(NULL))) {
ok = SWITCH_FALSE;
}
}
bug->ping_frame = NULL;
//switch_mutex_unlock(bug->read_mutex);
bug->video_ping_frame = NULL;
switch_thread_rwlock_unlock(bug->session->bug_rwlock);
if (!ok) {
@ -1021,14 +1019,14 @@ SWITCH_DECLARE(uint32_t) switch_core_media_bug_patch_video(switch_core_session_t
for (bp = orig_session->bugs; bp; bp = bp->next) {
if (!switch_test_flag(bp, SMBF_PRUNE) && !switch_test_flag(bp, SMBF_LOCK) && !strcmp(bp->function, "patch:video")) {
if (bp->ready && frame->img && switch_test_flag(bp, SMBF_VIDEO_PATCH)) {
bp->ping_frame = frame;
bp->video_ping_frame = frame;
if (bp->callback) {
if (bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_VIDEO_PATCH) == SWITCH_FALSE
|| (bp->stop_time && bp->stop_time <= switch_epoch_time_now(NULL))) {
ok = SWITCH_FALSE;
}
}
bp->ping_frame = NULL;
bp->video_ping_frame = NULL;
}
if (ok == SWITCH_FALSE) {

@ -266,17 +266,24 @@ SWITCH_DECLARE(const char *) API::executeString(const char *cmd)
this_check("");
mycmd = strdup(cmd);
SWITCH_STANDARD_STREAM(stream);
switch_assert(mycmd);
if (zstr(cmd)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No application specified\n");
stream.write_function(&stream, "-ERR No application specified");
} else {
mycmd = strdup(cmd);
if ((arg = strchr(mycmd, ' '))) {
*arg++ = '\0';
switch_assert(mycmd);
if ((arg = strchr(mycmd, ' '))) {
*arg++ = '\0';
}
switch_api_execute(mycmd, arg, session, &stream);
switch_safe_free(mycmd);
}
SWITCH_STANDARD_STREAM(stream);
switch_api_execute(mycmd, arg, session, &stream);
switch_safe_free(mycmd);
return (char *) stream.data;
}
@ -425,6 +432,11 @@ SWITCH_DECLARE(const char *)Event::getHeader(const char *header_name)
{
this_check("");
if (zstr(header_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Trying to getHeader an invalid header!\n");
return NULL;
}
if (event) {
return switch_event_get_header(event, header_name);
} else {
@ -450,6 +462,11 @@ SWITCH_DECLARE(bool) Event::delHeader(const char *header_name)
{
this_check(false);
if (zstr(header_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Trying to delHeader an invalid header!\n");
return false;
}
if (event) {
return switch_event_del_header(event, header_name) == SWITCH_STATUS_SUCCESS ? true : false;
} else {

@ -1932,9 +1932,10 @@ SWITCH_DECLARE(void) switch_event_prep_for_delivery_detailed(const char *file, c
char date[80] = "";
switch_size_t retsize;
switch_time_t ts = switch_micro_time_now();
uint64_t seq;
switch_mutex_lock(EVENT_QUEUE_MUTEX);
EVENT_SEQUENCE_NR++;
seq = ++EVENT_SEQUENCE_NR;
switch_mutex_unlock(EVENT_QUEUE_MUTEX);
@ -1954,7 +1955,7 @@ SWITCH_DECLARE(void) switch_event_prep_for_delivery_detailed(const char *file, c
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Event-Calling-File", switch_cut_path(file));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Event-Calling-Function", func);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Calling-Line-Number", "%d", line);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Sequence", "%" SWITCH_UINT64_T_FMT, (uint64_t) EVENT_SEQUENCE_NR);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Sequence", "%" SWITCH_UINT64_T_FMT, seq);
}

@ -2490,6 +2490,105 @@ SWITCH_DECLARE(int) switch_ivr_set_xml_profile_data(switch_xml_t xml, switch_cal
return off;
}
#define add_stat(_x, _i, _s) \
switch_snprintf(var_val, sizeof(var_val), "%" SWITCH_SIZE_T_FMT, _i); \
x_tmp = switch_xml_add_child_d(_x, _s, loff++); \
switch_xml_set_txt_d(x_tmp, var_val)
#define add_stat_double(_x, _i, _s) \
switch_snprintf(var_val, sizeof(var_val), "%0.2f", _i); \
x_tmp = switch_xml_add_child_d(_x, _s, loff++); \
switch_xml_set_txt_d(x_tmp, var_val)
SWITCH_DECLARE(int) switch_ivr_set_xml_call_stats(switch_xml_t xml, switch_core_session_t *session, int off, switch_media_type_t type)
{
const char *name = (type == SWITCH_MEDIA_TYPE_VIDEO) ? "video" : "audio";
switch_xml_t x_stat, x_in, x_out, x_tmp = NULL;
int loff = 0;
switch_rtp_stats_t *stats = switch_core_media_get_stats(session, type, NULL);
char var_val[35] = "";
if (!stats) return off;
if (!(x_stat = switch_xml_add_child_d(xml, name, off++))) {
abort();
}
if (!(x_in = switch_xml_add_child_d(x_stat, "inbound", off++))) {
abort();
}
if (!(x_out = switch_xml_add_child_d(x_stat, "outbound", off++))) {
abort();
}
stats->inbound.std_deviation = sqrt(stats->inbound.variance);
add_stat(x_in, stats->inbound.raw_bytes, "raw_bytes");
add_stat(x_in, stats->inbound.media_bytes, "media_bytes");
add_stat(x_in, stats->inbound.packet_count, "packet_count");
add_stat(x_in, stats->inbound.media_packet_count, "media_packet_count");
add_stat(x_in, stats->inbound.skip_packet_count, "skip_packet_count");
add_stat(x_in, stats->inbound.jb_packet_count, "jitter_packet_count");
add_stat(x_in, stats->inbound.dtmf_packet_count, "dtmf_packet_count");
add_stat(x_in, stats->inbound.cng_packet_count, "cng_packet_count");
add_stat(x_in, stats->inbound.flush_packet_count, "flush_packet_count");
add_stat(x_in, stats->inbound.largest_jb_size, "largest_jb_size");
add_stat_double(x_in, stats->inbound.min_variance, "jitter_min_variance");
add_stat_double(x_in, stats->inbound.max_variance, "jitter_max_variance");
add_stat_double(x_in, stats->inbound.lossrate, "jitter_loss_rate");
add_stat_double(x_in, stats->inbound.burstrate, "jitter_burst_rate");
add_stat_double(x_in, stats->inbound.mean_interval, "mean_interval");
add_stat(x_in, stats->inbound.flaws, "flaw_total");
add_stat_double(x_in, stats->inbound.R, "quality_percentage");
add_stat_double(x_in, stats->inbound.mos, "mos");
if (stats->inbound.error_log) {
switch_xml_t x_err_log, x_err;
switch_error_period_t *ep;
int eoff = 0;
if (!(x_err_log = switch_xml_add_child_d(x_stat, "error-log", off++))) {
abort();
}
for(ep = stats->inbound.error_log; ep; ep = ep->next) {
if (!(ep->start && ep->stop)) continue;
if (!(x_err = switch_xml_add_child_d(x_err_log, "error-period", eoff++))) {
abort();
}
switch_snprintf(var_val, sizeof(var_val), "%" SWITCH_TIME_T_FMT, ep->start);
x_tmp = switch_xml_add_child_d(x_err, "start", 0);
switch_xml_set_txt_d(x_tmp, var_val);
switch_snprintf(var_val, sizeof(var_val), "%" SWITCH_TIME_T_FMT, ep->stop);
x_tmp = switch_xml_add_child_d(x_err, "stop", 1);
switch_xml_set_txt_d(x_tmp, var_val);
switch_snprintf(var_val, sizeof(var_val), "%" SWITCH_TIME_T_FMT, (ep->stop - ep->start) / 1000);
x_tmp = switch_xml_add_child_d(x_err, "duration-msec", 2);
switch_xml_set_txt_d(x_tmp, var_val);
}
}
add_stat(x_out, stats->outbound.raw_bytes, "raw_bytes");
add_stat(x_out, stats->outbound.media_bytes, "media_bytes");
add_stat(x_out, stats->outbound.packet_count, "packet_count");
add_stat(x_out, stats->outbound.media_packet_count, "media_packet_count");
add_stat(x_out, stats->outbound.skip_packet_count, "skip_packet_count");
add_stat(x_out, stats->outbound.dtmf_packet_count, "dtmf_packet_count");
add_stat(x_out, stats->outbound.cng_packet_count, "cng_packet_count");
add_stat(x_out, stats->rtcp.packet_count, "rtcp_packet_count");
add_stat(x_out, stats->rtcp.octet_count, "rtcp_octet_count");
return off;
}
static int switch_ivr_set_xml_chan_var(switch_xml_t xml, const char *var, const char *val, int off)
{
char *data;
@ -2584,6 +2683,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_generate_xml_cdr(switch_core_session_
free(f);
}
if (!(variables = switch_xml_add_child_d(cdr, "call-stats", cdr_off++))) {
goto error;
}
switch_ivr_set_xml_call_stats(variables, session, v_off, SWITCH_MEDIA_TYPE_AUDIO);
switch_ivr_set_xml_call_stats(variables, session, v_off, SWITCH_MEDIA_TYPE_VIDEO);
if (!(variables = switch_xml_add_child_d(cdr, "variables", cdr_off++))) {
goto error;
@ -2888,6 +2994,80 @@ static void switch_ivr_set_json_profile_data(cJSON *json, switch_caller_profile_
cJSON_AddItemToObject(json, "chan_name", cJSON_CreateString(caller_profile->chan_name));
}
#define add_jstat(_j, _i, _s) \
switch_snprintf(var_val, sizeof(var_val), "%" SWITCH_SIZE_T_FMT, _i); \
cJSON_AddItemToObject(_j, _s, cJSON_CreateNumber(_i))
SWITCH_DECLARE(void) switch_ivr_set_json_call_stats(cJSON *json, switch_core_session_t *session, switch_media_type_t type)
{
const char *name = (type == SWITCH_MEDIA_TYPE_VIDEO) ? "video" : "audio";
cJSON *j_stat, *j_in, *j_out;
switch_rtp_stats_t *stats = switch_core_media_get_stats(session, type, NULL);
char var_val[35] = "";
if (!stats) return;
j_stat = cJSON_CreateObject();
j_in = cJSON_CreateObject();
j_out = cJSON_CreateObject();
cJSON_AddItemToObject(json, name, j_stat);
cJSON_AddItemToObject(j_stat, "inbound", j_in);
cJSON_AddItemToObject(j_stat, "outbound", j_out);
stats->inbound.std_deviation = sqrt(stats->inbound.variance);
add_jstat(j_in, stats->inbound.raw_bytes, "raw_bytes");
add_jstat(j_in, stats->inbound.media_bytes, "media_bytes");
add_jstat(j_in, stats->inbound.packet_count, "packet_count");
add_jstat(j_in, stats->inbound.media_packet_count, "media_packet_count");
add_jstat(j_in, stats->inbound.skip_packet_count, "skip_packet_count");
add_jstat(j_in, stats->inbound.jb_packet_count, "jitter_packet_count");
add_jstat(j_in, stats->inbound.dtmf_packet_count, "dtmf_packet_count");
add_jstat(j_in, stats->inbound.cng_packet_count, "cng_packet_count");
add_jstat(j_in, stats->inbound.flush_packet_count, "flush_packet_count");
add_jstat(j_in, stats->inbound.largest_jb_size, "largest_jb_size");
add_jstat(j_in, stats->inbound.min_variance, "jitter_min_variance");
add_jstat(j_in, stats->inbound.max_variance, "jitter_max_variance");
add_jstat(j_in, stats->inbound.lossrate, "jitter_loss_rate");
add_jstat(j_in, stats->inbound.burstrate, "jitter_burst_rate");
add_jstat(j_in, stats->inbound.mean_interval, "mean_interval");
add_jstat(j_in, stats->inbound.flaws, "flaw_total");
add_jstat(j_in, stats->inbound.R, "quality_percentage");
add_jstat(j_in, stats->inbound.mos, "mos");
if (stats->inbound.error_log) {
cJSON *j_err_log, *j_err;
switch_error_period_t *ep;
j_err_log = cJSON_CreateArray();
cJSON_AddItemToObject(j_in, "errorLog", j_err_log);
for(ep = stats->inbound.error_log; ep; ep = ep->next) {
if (!(ep->start && ep->stop)) continue;
j_err = cJSON_CreateObject();
cJSON_AddItemToObject(j_err, "start", cJSON_CreateNumber(ep->start));
cJSON_AddItemToObject(j_err, "stop", cJSON_CreateNumber(ep->stop));
cJSON_AddItemToObject(j_err, "durationMS", cJSON_CreateNumber((ep->stop - ep->start) / 1000));
cJSON_AddItemToArray(j_err_log, j_err);
}
}
add_jstat(j_out, stats->outbound.raw_bytes, "raw_bytes");
add_jstat(j_out, stats->outbound.media_bytes, "media_bytes");
add_jstat(j_out, stats->outbound.packet_count, "packet_count");
add_jstat(j_out, stats->outbound.media_packet_count, "media_packet_count");
add_jstat(j_out, stats->outbound.skip_packet_count, "skip_packet_count");
add_jstat(j_out, stats->outbound.dtmf_packet_count, "dtmf_packet_count");
add_jstat(j_out, stats->outbound.cng_packet_count, "cng_packet_count");
add_jstat(j_out, stats->rtcp.packet_count, "rtcp_packet_count");
add_jstat(j_out, stats->rtcp.octet_count, "rtcp_octet_count");
}
static void switch_ivr_set_json_chan_vars(cJSON *json, switch_channel_t *channel, switch_bool_t urlencode)
{
switch_event_header_t *hi = switch_channel_variable_first(channel);
@ -2925,7 +3105,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_generate_json_cdr(switch_core_session
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_caller_profile_t *caller_profile;
cJSON *variables, *j_main_cp, *j_caller_profile, *j_caller_extension, *j_caller_extension_apps, *j_times, *j_application,
*j_callflow, *j_profile, *j_inner_extension, *j_app_log, *j_apps, *j_o, *j_o_profiles, *j_channel_data;
*j_callflow, *j_profile, *j_inner_extension, *j_app_log, *j_apps, *j_o, *j_o_profiles, *j_channel_data, *callStats;
switch_app_log_t *app_log;
char tmp[512], *f;
@ -2951,9 +3131,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_generate_json_cdr(switch_core_session
free(f);
}
callStats = cJSON_CreateObject();
cJSON_AddItemToObject(cdr, "callStats", callStats);
switch_ivr_set_json_call_stats(callStats, session, SWITCH_MEDIA_TYPE_AUDIO);
switch_ivr_set_json_call_stats(callStats, session, SWITCH_MEDIA_TYPE_VIDEO);
variables = cJSON_CreateObject();
cJSON_AddItemToObject(cdr, "variables", variables);
switch_ivr_set_json_chan_vars(variables, channel, urlencode);

@ -1481,9 +1481,10 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s
case SWITCH_ABC_TYPE_READ_VIDEO_PING:
case SWITCH_ABC_TYPE_STREAM_VIDEO_PING:
if (rh->fh) {
if (!bug->ping_frame) break;
if (!bug->video_ping_frame) break;
if ((len || bug->ping_frame->img) && switch_core_file_write_video(rh->fh, bug->ping_frame) != SWITCH_STATUS_SUCCESS && rh->hangup_on_error) {
if ((len || bug->video_ping_frame->img) && switch_core_file_write_video(rh->fh, bug->video_ping_frame) != SWITCH_STATUS_SUCCESS &&
rh->hangup_on_error) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error writing video to %s\n", rh->file);
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
@ -1716,12 +1717,12 @@ static switch_bool_t eavesdrop_callback(switch_media_bug_t *bug, void *user_data
case SWITCH_ABC_TYPE_STREAM_VIDEO_PING:
{
if (!bug->ping_frame || !bug->ping_frame->img) {
if (!bug->video_ping_frame || !bug->video_ping_frame->img) {
break;
}
if (ep->eavesdropper && switch_core_session_read_lock(ep->eavesdropper) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_write_video_frame(ep->eavesdropper, bug->ping_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
if (switch_core_session_write_video_frame(ep->eavesdropper, bug->video_ping_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error writing video to %s\n", switch_core_session_get_name(ep->eavesdropper));
ep->errs++;

@ -1234,9 +1234,9 @@ 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;
if (jb->period_miss_pct > 40.0f) {
if (jb->period_miss_pct > 60.0f) {
jb_debug(jb, 2, "Miss percent %02f too high, resetting buffer.\n", jb->period_miss_pct);
//switch_jb_reset(jb);
switch_jb_reset(jb);
}
if ((status = jb_next_packet(jb, &node)) == SWITCH_STATUS_SUCCESS) {
@ -1324,15 +1324,12 @@ SWITCH_DECLARE(switch_status_t) switch_jb_get_packet(switch_jb_t *jb, switch_rtp
}
switch_mutex_unlock(jb->mutex);
if (status == SWITCH_STATUS_SUCCESS) {
if (jb->complete_frames > jb->max_frame_len) {
thin_frames(jb, 8, 25);
}
if (jb->complete_frames > jb->max_frame_len) {
thin_frames(jb, 8, 25);
}
return status;
}

@ -150,6 +150,13 @@ static char *print_number(cJSON *item)
return str;
}
#define is_hexdigit(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
static int scan_unicode(const char *ptr, unsigned int *uc)
{
if (!is_hexdigit(*(ptr)) || !is_hexdigit(*(ptr+1)) || !is_hexdigit(*(ptr+2)) || !is_hexdigit(*(ptr+3))) return -1;
return sscanf(ptr, "%4x", uc);
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
@ -177,8 +184,7 @@ static const char *parse_string(cJSON *item,const char *str)
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
if (sscanf(ptr+1,"%4x",&uc) < 1) break;
if (scan_unicode(ptr+1, &uc) < 1) break;
ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
@ -186,7 +192,7 @@ static const char *parse_string(cJSON *item,const char *str)
if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
{
if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
if (sscanf(ptr+3,"%4x",&uc2) < 1) break;
if (scan_unicode(ptr+3,&uc2) < 1) break;
ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);

Some files were not shown because too many files have changed in this diff Show More