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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment