Skip to content

Instantly share code, notes, and snippets.

@thehunmonkgroup
Last active March 6, 2024 08:53
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save thehunmonkgroup/446370910266f006cdcf25df5e28df7b to your computer and use it in GitHub Desktop.
Save thehunmonkgroup/446370910266f006cdcf25df5e28df7b to your computer and use it in GitHub Desktop.
ClueCon talk 2016 - Videoconferencing with Verto, example code/config/links/slides
<configuration name="conference.conf" description="Audio Conference">
<!-- Other conference config... -->
<profiles>
<profile name="video-mcu-stereo">
<!-- Other profile config... -->
<!-- Mux the inbound video streams into one outbound stream. -->
<param name="video-mode" value="mux"/>
<!-- Enable the live array, minimize outbound video encoding. -->
<param name="conference-flags" value="livearray-sync|livearray-json-status|minimize-video-encoding"/>
<!-- Use this video layout by default if no other is specified. -->
<param name="video-layout-name" value="group:grid"/>
<!-- The size of the video canvas on the client (in pixels). -->
<param name="video-canvas-size" value="1920x1080"/>
<!-- HTML color code for the color of the video canvas element. -->
<param name="video-canvas-bgcolor" value="#333333"/>
<!-- HTML color code for the background color of each video feed
if it is cropped to fit. -->
<param name="video-letterbox-bgcolor" value="#000000"/>
<!-- Custom video mute banner.
Font, size, color, background color, and text can all be
customized. -->
<param name="video-mute-banner" value="={font_face=FreeSans.ttf,font_scale=5,bg=#800000,fg=#FFFFFF}VIDEO DISABLED"/>
<!-- Bandwidth used for each outbound video feed. -->
<param name="video-codec-bandwidth" value="1mb"/>
<!-- Milliseconds of speaking before a speaker gets the floor. -->
<param name="video-auto-floor-msec" value="800"/>
<!-- Maximum frames per second, users can request less. -->
<param name="video-fps" value="15"/>
<!-- Motion factor of the video, used to adjust the video bitrate.
From 1 to 4, low motion to high motion video. -->
<param name="video-quality" value="1"/>
</profile>
</profiles>
</configuration>
<include>
<extension name="conference_1_admin">
<condition field="destination_number" expression="^conference_1_admin$">
<!-- Custom user video banner.
Font, size, color, background color, and text can all be
customized. -->
<action application="set" data="video_banner_text={font_face=FreeSans.ttf,font_scale=5,bg=#800000,fg=#FFFFFF}${caller_id_name}"/>
<!-- Custom video mute banner.
Font, size, color, background color, and text can all be
customized. -->
<action application="set" data="video_mute_banner={font_face=FreeSans.ttf,font_scale=5,bg=#800000,fg=#FFFFFF}VIDEO DISABLED"/>
<!-- Customize image displayed when a user is video muted. -->
<action application="set" data="video_mute_png=/path/to/some/image"/>
<!-- Customize image displayed when FreeSWITCH is not receiving video
from the user. -->
<action application="set" data="video_no_video_avatar_png=/path/to/some/image"/>
<action application="answer"/>
<action application="conference" data="conference_1@video-mcu-stereo++flags{moderator}"/>
</condition>
</extension>
</include>
<include>
<domain name="$${domain}">
<params>
<!-- Required for Verto to function properly -->
<param name="jsonrpc-allowed-methods" value="verto"/>
</params>
<groups>
<group name="default">
<users>
<!-- Users in this domain can now log in via Verto. -->
</users>
</group>
</groups>
</domain>
</include>
#!/usr/bin/env sh
# This script documents in code how to install the necessary SSL certs for
# a default Verto configuration. Adjust variables in the CONFIG section as
# necessary.
#
# Tip: Use https://letsencrypt.org to generate free, valid SSL certificates
# for your server.
#
# CAVEATS:
# Won't work on Windows.
##### BEGIN CONFIG #####
# Location of FreeSWITCH installation.
freeswitch_base_dir="/usr/local/freeswitch"
# Location of the server's SSL certificate file.
server_ssl_cert="~/server.crt"
# Location of the server's SSL key file.
server_ssl_key="~/server.key"
# Location of the certificate authority chain file.
ca_chain="~/ca-chain.crt"
##### END CONFIG #####
certs_dir="${freeswitch_base_dir}/certs"
wss_filepath="${certs_dir}/wss.pem"
mkdir -p ${certs_dir} && \
cat ${server_ssl_cert} ${server_ssl_key} ${ca_chain} > ${wss_filepath}
ret=$?
if [ ${ret} -eq 0 ]; then
echo "
Combined SSL file written to ${wss_filepath} -- make sure this location
matches the 'secure-combined' parameter in verto.conf.xml.
"
else
echo "Oh no! Error setting up the SSL file.
fi
return ${ret}
/**
* This example code represents a typical workflow for Verto in a client
* browser.
*
* It can be used as a bare-bones starting point to hang your application
* specific code around.
*
* There's a lot of inline documentation below, so read on. :)
*/
/*
* Globally instantiated variables.
*/
// Verto object.
var vertoObj;
// Verto configuration object.
var vertoConf;
// Actively placed call object.
var currentCall;
// Live array object.
var liveArray;
/*
* Initialize Verto.
*/
// This includes:
// - Detecting audio/video/sound devices
// - Getting user audio/video permissions
// NOTE: These steps can be done manually instead.
$.verto.init({}, function() {
// Create a new verto instance:
// This step performs a user login to FreeSWITCH via secure websocket.
// The user must be properly configured in the FreeSWITCH user directory.
vertoObj = new $.verto({
login: 'someuser@freeswitch.example.com',
passwd: 'supersekret',
// As configured in verto.conf.xml on the server.
socketUrl: 'wss://freeswitch.example.com:8082',
// ID of HTML video tag where the video feed is placed.
tag: "video-feed",
// STUN/TURN server config, more than one is allowed.
iceServers: [
{
url: 'stun:stun.example.com',
},
],
// Internal session ID used by Verto to track the call, eg. for call
// recovery. A random one will be generated if none is provided, and,
// it can be useful to provide a custom ID to store and reference for
// other purposes.
sessid: sessid,
// Google Chrome specific adjustments/filters for audio.
// Official documentation is scant, best to try them out and see!
audioParams: {
googEchoCancellation: true,
googAutoGainControl: true,
googNoiseSuppression: true,
googHighpassFilter: true,
googTypingNoiseDetection: true,
googEchoCancellation2: false,
googAutoGainControl2: false,
},
// These can be set per-call as well as per-login.
deviceParams: {
useCamera: 'any',
useMic: 'any',
useSpeak: 'any',
},
}, callbacks);
});
/*
* Handle websocket messages from the freeswitch server.
*/
var callbacks = {
// Receives websocket login status from FreeSWITCH.
onWSLogin: function (verto, success) {
if (success) {
// At this point you're connected to the FreeSWITCH server and logged
// in, ready to place a call to the conference, or run a test for the
// user's bandwidth.
testBandwidth(function(bandwidthTestData) {
// Do something with the bandwidth test results...
});
}
},
// Websocket connection to FreeSWITCH closed.
onWSClose: function (verto, success) {
// Perhaps try to reconnect?
},
// Receives call state messages from FreeSWITCH.
onDialogState: function (d) {
switch (d.state.name) {
case "trying":
break;
case "answering":
break;
case "active":
break;
case "hangup":
log("Call ended with cause: " + d.cause);
break;
case "destroy":
// Some kind of client side cleanup...
break;
}
},
// Receives conference-related messages from FreeSWITCH.
// Note that it's possible to write server-side modules to send customized
// messages via this callback.
onMessage: function (verto, dialog, message, data) {
switch (message) {
case $.verto.enum.message.pvtEvent:
if (data.pvtData) {
switch (data.pvtData.action) {
// This client has joined the live array for the conference.
case "conference-liveArray-join":
// With the initial live array data from the server, you can
// configure/subscribe to the live array.
initLiveArray(verto, dialog, data);
break;
// This client has left the live array for the conference.
case "conference-liveArray-part":
// Some kind of client-side wrapup...
break;
}
}
break;
}
},
};
/*
* Place a call to the videoconference.
*/
var call = function(bandwidthTestData) {
// Sets the parameters for the video stream that will be sent to the
// videoconference.
// Hint: Use the upKPS result in bandwidthTestData to determine the video
// resolution to send!
vertoObj.videoParams({
// Dimensions of the video feed to send.
minWidth: 640,
minHeight: 480,
maxWidth: 640,
maxHeight: 480,
// The minimum frame rate of the client camera, Verto will fail if it's
// less than this.
minFrameRate: 15,
// The maximum frame rate to send from the camera.
vertoBestFrameRate: 30,
});
currentCall = vertoObj.newCall({
// Extension to dial.
destination_number: 'conference_1',
caller_id_name: 'Bob Smith',
caller_id_number: 'some-caller-or-user-id',
// Enable video support.
useVideo: true,
// User devices to use.
useCamera: 'any',
useMic: 'any',
useSpeak: 'any',
// Data returned from the bandwidth test can be used to set these params,
// which will be used to calculate the best strategy for sending/receiving
// video within these bandwidth limits.
outgoingBandwidth: bandwidthTestData.upKPS,
incomingBandwidth: bandwidthTestData.downKPS,
// Use a dedicated outbound encoder for this user's video.
// NOTE: This is generally only needed if the user has some kind of
// non-standard video setup, and is not recommended to use, as it
// dramatically increases the CPU usage for the conference.
dedEnc: false,
// You can pass any application/call specific variables here, and they will
// be available as a dialplan variable, prefixed with 'verto_dvar_'.
userVariables: {
// Shows up as a 'verto_dvar_foo' dialplan variable.
foo: 'bar',
},
// Enable stereo audio.
useStereo: true,
// Mirror local user's webcam.
mirrorInput: true,
});
}
/*
* Setting up and subscribing to the live array.
*/
var initLiveArray = function(verto, dialog, data) {
// Set up addtional configuration specific to the call.
vertoConf = new $.verto.conf(verto, {
dialog: dialog,
hasVid: true,
laData: data.pvtData,
// For subscribing to published chat messages.
chatCallback: function(verto, eventObj) {
var from = eventObj.data.fromDisplay || eventObj.data.from || 'Unknown';
var message = eventObj.data.message || '';
},
});
var config = {
subParams: {
callID: dialog ? dialog.callID : null
},
};
// Set up the live array, using the live array data received from FreeSWITCH.
liveArray = new $.verto.liveArray(vertoObj, data.pvtData.laChannel, data.pvtData.laName, config);
// Subscribe to live array changes.
liveArray.onChange = function(liveArrayObj, args) {
console.log("Call UUID is: " + args.key);
console.log("Call data is: ", args.data);
try {
switch (args.action) {
// Initial list of existing conference users.
case "bootObj":
break;
// New user joined conference.
case "add":
break;
// User left conference.
case "del":
break;
// Existing user's state changed (mute/unmute, talking, floor, etc)
case "modify":
break;
}
} catch (err) {
console.error("ERROR: " + err);
}
};
// Called if the live array throws an error.
liveArray.onErr = function (obj, args) {
console.error("Error: ", obj, args);
};
}
/*
* Send a command to the conference.
*/
// This translates to the following conference API command:
// conference [conference id] [command] [id] [value]
var sendCommand = function (command, id, value) {
vertoObj.rpcClient.call("verto.broadcast", {
"eventChannel": vertoConf.params.laData.modChannel,
"data": {
"application": "conf-control",
"command": command,
"id": id,
"value": value
}
});
}
// Some examples of using the above sendCommand() function.
var toggleAudioMute = function(conferenceUserId, arg) {
sendCommand('tmute', conferenceUserId, arg);
}
var toggleVideoMute = function(conferenceUserId, arg) {
sendCommand('tvmute', conferenceUserId, arg);
}
var videoLayout = function(layoutId) {
sendCommand("vid-layout", null, layoutId);
}
/*
* Send a key press to the conference.
*/
// currentCall is the object returned by vertoObj.newCall()
// The sent key digits map to the <caller-controls> section of
// conference.conf.xml
var memberMute = function() {
// Normally audio mute/unmute.
currentCall.dtmf("0");
// Normally video mute/unmute.
currentCall.dtmf("*0");
}
/*
* Send a chat message.
*/
var sendConferenceChat = function(message) {
vertoConf.sendChat(message, "message");
}
/*
* Perform bandwidth test.
*/
// This can be performed any time after a successful Verto login to the
// FreeSWITCH server.
var testBandwidth = function(someCallback) {
vertoObj.rpcClient.speedTest(1024 * 256, function(event, data) {
// These values are in kilobits/sec.
var upBand = Math.ceil(data.upKPS);
var downBand = Math.ceil(data.downKPS);
console.log('[BANDWIDTH TEST] Up: ' + upBand + ', Down: ' + downBand);
if(someCallback) {
someCallback(data);
}
});
}
Display the source blob
Display the rendered blob
Raw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment