web real time communication platform
This is a ready to deploy webrtc SDK and SaaS for a customized and flexible communication and collaboration solution .
The Solution primarily contains nodejs frameworks for hosting the project and webbsockets over socket.io to perform offer - answer handshake and share SDP (Session description protocol ).
-
WebRTC Web based real time communication framework. read more on webrtc
-
Node ( v10.0.0) Asynchronous event driven JavaScript runtime
-
socket.io ( v0.9) Communication and signalling
Note : while its possible to use any prtotocol like SIP , XMPP , AJAX , JSON etc for this purpose , modifying thsi libabry will require a lot of rework . It would be advisble to start from apprtc directly in that case .
- Grunt It is a task Runner and its used to automate running of command in gruntfile
grunt -verbose
Project is divided into 4 parts
- Core RTC Conn Lib
- Wrappers for the Core Lib containing feature sets and widgets like screensharing , recording , pointer share , machine learning , face detection etc
- Demo Applicatins like two party full-features , multi-party full features etc which implement and use the SDK by invoking the constructirs , emitters and listeners .
- SIgnaller over socket.io for SDP excahnge on offer answer model
Download the dev dependencies by setting the NODE_ENV to dev . This will install all grunt and gulp dependencies used for building the SDK
NODE_ENV=development npm install
To build the RtcConn , outputs RTCMultiConn
grunt rtcconn
To build the webrtcdev lib . It encapsulates the rtcconn core along with external libs for building various custom features . Outputs webrtcdevelopment.js , webrtcdevelopment_header.js , webrtcdevelopment.css , webrtcdevelopment_header.css and webrtcdevelopmentserver.js
gulp production
To run this project following steps need to be followed in that order :
1. Get the project from github
git clone https://github.com/altanai/webrtc.git webrtc
2. install nvm ( node version manager )
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash
. ~/.nvm/nvm.sh
nvm install v5.0.0
nvm use v5.0.0
3. install npm and update the dependencies It will read the package.json and update the dependencies in node_modules folder on project location
sudo apt-get install npm
npm install
4. Change ENV variables and Test
To change the ports for running the https server and rest server, goto env.json
{
"hostname" : "host",
"enviornment" : "local",
"host" : "localhost",
"jsdebug" : true,
"httpsPort" : 8086,
"restPort" : 8087,
"extensionID" : "elfbfompfpakbefoaicaeoabnnoihoac"
}
To run the tests
npm test
5. Start up the Server
To start the Server in dev mode and stop the server as soon as Ctrl+ C is hit or the terminal widnow is closed .
node webrtcserver.js
read more about node
To start the Server using npm start ( using package.json) , behaves same as earlier run using node. We use supervisor to restart the server incase of exceptions or new code .
npm start
6. JS and CSS Libs
Make a webpage and give holders for video and button elements that SDK will use .
Inside the head tag of html
build/webrtcdevelopment_header.css
build/webrtcdevelopment_header.js
After the body tag of html build/webrtcdevelopment.css build/webrtcdevelopment.js
7. Configure
Create the webrtc dom object with local and remote objects
local object :
var local={
video : "myAloneVideo", // name of the local video element
videoClass : "", // class of the localvideo
videoContainer : "singleVideoContainer", // outer container of the video element
userDisplay : false, // do you want to display user details
userMetaDisplay : false,
userdetails:{ // users details include name , email , color
username : username,
usercolor : "#DDECEF",
useremail : useremail,
role : "participant" // role of user in the session , can be participant , admin , inspector
}
};
remote object :
var remote={
videoarr : ["myConferenceVideo", "otherConferenceVideo"], // conatiners for the video after session is made
// first one is usually the local video holder followed by remote video holders
videoClass : "",
maxAllowed : "6",
videoContainer : "confVideoContainer",
userDisplay : false,
userMetaDisplay : false,
dynamicVideos : false
};
Incoming and outgoing media configiration ( self exlanatory ) :
var incoming={
audio : true,
video : true,
data : true,
screen: true
};
var outgoing={
audio : true,
video : true,
data : true,
screen: true
};
webrtcdomobj= new WebRTCdom(
local, remote, incoming, outgoing
);
7. Adding Widgets
set widgets (expained in section below)
var widgets={
}
Set widgets and their properties .
8. Creating session
Get session id automaically
sessionid = init(true);
or get session name from user
sessionid = init(false);
Create a session json object with turn credentials and the session created from above step
set preference for the incoming and outgoing media connectection. Bydefault all are set to true .
var incoming={
audio: true,
video: true,
data: true,
screen: true
};
var outgoing={
audio: true,
video: true,
data: true,
screen: true
};
finally initiate the webrtcdev contructor
var webrtcdevobj = new WebRTCdev (
session, incoming, outgoing , widgets
);
Start the session
startcall();
Currently available widgets are * Chat * Fileshare * Timer * Screen Record * Screen Share * Video Record * Snapshot * Minimising/ maximising Video * Mute (audio and/or video) * Draw on Canvas and Sync * Text Editor and Sync * Reconnect
Description of Widgets with SDK invocation
User RTCDataConnection api from webRTC to send peer to peer nessages within a session. If camera is present the SDK captures a screenshot with user's cemars feed at the isnatnt of typing the message and send along with the message.
When the chat widget is active , if the dom specified by the container id is present then webSDK uses as it is, else it creates one default box
{
active: true,
container: {
id: "chatContainer" // dom id of the chat conatiner
},
inputBox:{
text_id:"chatInputText", // dom id of the chta's input box
sendbutton_id:"chatSendButton", // dom id for the chat's send button
minbutton_id:"minimizeChatButton" // dom id for minimizing the Chat conaginer
},
chatBox:{
id: "chatBoard" // dom id for the chat board where all messages are dispalyed
},
button:{ // on and off button states for the chat widget button
class_on:"btn btn-warning glyphicon glyphicon-font topPanelButton",
html_on:"Chat",
class_off:"btn btn-success glyphicon glyphicon-font topPanelButton",
html_off:"Chat"
}
}
Upcoming : Adding emoticons to Chat
Uses the RTCDataConnection API from WebRTC to excahnge files peer to peer. Progress bar is displayed for the chunks of file transferrred out of total number of chunks. Many different kindes of file transfer have been tested such as media files ( mp3 , mp4 ) , text or pdf files , microsoft pr libra office dicuments , images ( jpg , png etc ) etc .
File share widgets creates uses 2 conatiners - File Share and File List . If the dom ids of the container are not present on the page , the SDK crestes default conainers and appends them to page
The list of files with buttons to view , hide or remove them from file viewers are in file Viewer container . Displaying or playing the text or media files happens in file share conainer , which also has button to maximize , minimize the viewer window or in case of images to rotate them.
{
active : true,
fileShareContainer : "fileSharingRow", // File sharing container
fileshare:{ // components of file sharing container
rotateicon:"assets/images/refresh-icon.png", // rotate icon
minicon:"assets/images/arrow-icon-down.png", // min icon
maxicon:"assets/images/arrow-icon.png", // max icon
closeicon:"assets/images/cross-icon.png" // close icon
},
fileListContainer : "fileListingRow", // File List container container
filelist:{ // components of file list conainer
downloadicon:"", // icon donwload
trashicon:"", // icon trash
saveicon:"", // icon save
showicon:"", // icon show
hideicon:"", // icon hide
},
button:{
id: "fileshareBtn", // dom for widget button to call file share
class_on: "col-xs-8 text-center fileshareclass",
html:"File"
},
props:{
fileShare:"divided", // Can be divided for two particiapnts , chatpreview , single for many participants , hidden
fileList:"divided" // same as aboev Can be divided , single , hidden
}
}
Creates or assigns a timer for teh ongoing sesssion . Also displays the geolocation and timezone of the peers if perssion if provided . Timer can start upwards or downwards. Can be used for billing and policy control .
{
active: true,
type: "forward", // Forwards timer starts from 0:0:00 goes thereafter, backward timer ticks backword from prespecified time limit
counter:{
hours: "countdownHours", // dom id for hours
minutes:"countdownMinutes", // dom id for mins
seconds :"countdownSeconds" // dom if for seconds
},
upperlimit: { // upperlimit of time for the session
hour:0 ,
min: 3 ,
sec: 60
},
span:{ // dom ids for local and remote time labels
currentTime_id:"currentTimeArea",
currentTimeZone_id:"currentTimeZoneArea",
remoteTime_id :"remoteTimeArea",
remoteTimeZone_id:"remoteTimeZoneArea",
class_on:""
},
container:{
id:'collapseThree',
minbutton_id:'timerBtn'
},
button :{
id: 'timerBtn' // dom for widget timer button to call
}
}
Records everything pesent on the tab selected along with audio and displays recording as mp4 file. Use an extension and pre-declared safe-site to facilitate captuing the tab.
{
active : true,
videoRecordContainer: true, // container for storing or displaying recorded video
button:{ // button to control screen control wisget and its on / off states
id: "scrRecordBtn",
class_on:"btn btn-lg screenRecordBtnClass On",
html_on:'<img title="Session Record" src="assets/images/icon_5.png"/>',
class_off:"btn btn-lg screenRecordBtnClass Off",
html_off: '<img title="Session Record" src="assets/images/icon_5.png"/>'
}
},
One of the most powerful features of the SDK is to capture the current screen and share it with peer over RTC Peer connection channel. Simmilar to csreen record , uses an extension and pre-declared site ownership to capture the screen and share as peer to peer stream . Button for screen share has 3 states -
- install button for inline isnatllation of extension from page ,
- share screen button and
- view button for incoming screen by peer .
{
active : true,
screenshareContainer: "screenShareRow", // container to display screen being shared
extensionID: props.extensionID, // extension id
button:{
installButton:{ // widget button to start inline installation of extension
id:"scrInstallButton",
class_on:"screeninstall-btn on",
html_on:"Stop Install",
class_off:"screeninstall-btn off",
html_off:"ScreenShare"
},
shareButton:{ // widget button to start sharing screen , deactivated once already active or when peer is sharig
id:"scrShareButton",
class_on:"btn btn-lg on",
html_on:'<img title="Stop Screen Share" src=assets/images/icon_2.png />',
class_off:"btn btn-lg off",
html_off:'<img title="Start Screen Share" src=assets/images/icon_2.png />',
class_busy:"btn btn-lg busy",
html_busy:'<img title="Peer is Sharing Screen" src=assets/images/icon_2.png />'
},
viewButton:{ // button to view the icnoming screen share
id:"scrViewButton",
class_on:"screeninstall-btn on",
html_on:"Stop Viewing",
class_off:"screeninstall-btn off",
html_off:"View Screen"
}
}
}
Records video stream . Created for each peer video .
{
active : true,
videoRecordContainer : true,
button:{
class_on:"pull-right btn btn-modify-video2_on videoButtonClass on",
html_on:"<i class='fa fa-circle' title='Stop recording this Video'></i>",
class_off:"pull-right btn btn-modify-video2 videoButtonClass off",
html_off:"<i class='fa fa-circle' title='Record this Video'></i>"
}
}
Takes a snapshot from video stream . Will be created for each inidvidual peer video .
{
active : true,
snapshotContainer: true,
button:{
class_on:"pull-right btn btn-modify-video2 videoButtonClass",
html_on:"<i class='fa fa-th-large' title='Take a snapshot'></i>"
}
}
To enable the user to watch video in full screen mode or to inimize the video to hide it from screen. Will be seprately created for each individual peer video .
{
active : true,
max:{
button:{ // button to maximise the video to full screen mode
id: 'maxVideoButton',
class_on:"pull-right btn btn-modify-video2 videoButtonClass On",
html_on:"<i class='fa fa-laptop' title='full Screen'></i>",
class_off:"pull-right btn btn-modify-video2 videoButtonClass Off",
html_off:"<i class=' fa fa-laptop' title='full Screen'></i>"
}
} ,
min :{
button:{ // button to minimize or hide the video
id: 'minVideoButton',
class_on:"pull-right btn btn-modify-video2 videoButtonClass On",
html_on:"<i class='fa fa-minus' title='minimize Video'></i>",
class_off:"pull-right btn btn-modify-video2 videoButtonClass Off",
html_off:"<i class='fa fa-minus' title='minimize Video'></i>"
}
}
}
Mutes the audio or video of the peer video . Created for each peer video.
{
active: true,
drawCanvasContainer: "drawBoardRow",
container:{
id:'drawContainer',
minbutton_id:'minimizeDrawButton'
},
button:{
id: "draw-webrtc" ,
class_on:"btn btn-lg draw-webrtc On",
html_on:'<img title="Draw" src=assets/images/icon_3.png />',
class_off:"btn btn-lg draw-webrtc Off",
html_off:'<img title="Draw" src=assets/images/icon_3.png />'
}
}
Allows a user to recoonect a session without refreshing a page . Will enable him to drop the session and create a new one.
{
active: false,
button:{
id: "reconnectBtn",
class:"btn btn-success glyphicon glyphicon-refresh topPanelButton",
html:"Reconnect",
resyncfiles:false
}
}
{
active : true,
pointer:{
class_on: "fa fa-hand-o-up fa-3x"
},
button:{
id: 'shareCursorButton',
class_on:"pull-right btn btn-modify-video2 videoButtonClass On",
html_on:"<i class='fa fa-hand-o-up' title='Cursor'></i>",
class_off:"pull-right btn btn-modify-video2 videoButtonClass Off",
html_off:"<i class='fa fa-hand-o-up' title='Cursor'></i>"
}
},
{
active: true,
button:{
id:"ListenInButton",
textbox : "listenInLink"
}
}
debug : false,
{
active : true ,
helpContainer : "help-view-body",
screenshotContainer: "help-screenshot-body",
descriptionContainer: "help-description-body"
}
{
active : true ,
statsConainer : "network-stats-body"
}
var widgets={
chat : < add chat widget json >,
fileshare : < fieshare widget>,
debug : false,
reconnect :{
active: false,
button:{
id: "reconnectBtn",
class:"btn btn-success glyphicon glyphicon-refresh topPanelButton",
html:"Reconnect",
resyncfiles:false
}
},
timer :{
active: true,
type: "forward",
counter:{
hours: "countdownHours",
minutes:"countdownMinutes",
seconds :"countdownSeconds"
},
upperlimit: {
hour:0 ,
min: 3 ,
sec: 60
},
span:{
currentTime_id:"currentTimeArea",
currentTimeZone_id:"currentTimeZoneArea",
remoteTime_id :"remoteTimeArea",
remoteTimeZone_id:"remoteTimeZoneArea",
class_on:""
},
container:{
id:'collapseThree',
minbutton_id:'timerBtn'
},
button :{
id: 'timerBtn'
}
},
chat : < chat widget >
fileShare :< file share widget >
mute : < mute unmute widget >
videoRecord : < video record widget >
snapshot : < snapshot widget >
cursor : < widget for cursor sharing >
minmax : < widget to maximize or minimize the video >
drawCanvas : < draw widget >
screenrecord : < screen record widget >
screenshare : < screen share >
listenin : < listen in widget >
help : {
active : true ,
helpContainer : "help-view-body",
screenshotContainer: "help-screenshot-body",
descriptionContainer: "help-description-body"
},
statistics:{
active : true ,
statsConainer : "network-stats-body"
}
}
Implemented event listners
-
onLocalConnect
-
onSessionConnect
-
onScreenShareStarted
-
onScreenShareSEnded
-
onNoCameraCard
Add the Key and certs openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout ssl_certs/server.key -out ssl_certs/server.crt -subj "/CN=webrtc.altanai.com" -days 3650
open tab on chrome or mozilla browser and add a link to the https server using nodejs script https://127.0.0.1:8086/multiparty_fullfeatures.html
Other implementation of the SDK are
webrtc_quickstart - https://github.com/altanai/webrtc_quickstart
webrtc_usecases - https://github.com/altanai/webrtc_usercases
Following are the additioanl libraries packed with the project
Gulp Minify and concat the js and css files into minscripts
Task Runner you can run gulp alone to minify and concat the js and css files into minscripts
gulp
or can run grunt to concat , minifify , ugligy , push to git and npm all together
grunt production
forever Keeps running even when window is not active
cd WebCall
forever start webrtcserver.js
Notification / Alerting
//tbd
creating doc
./node_modules/.bin/esdoc
open ./docs/index.html
PM2 To start the Server using PM2 ( a process manager for nodejs)
Naviagte on browser https://localhost:8084/#2435937115056035 which creates websocket over socket.io wss://localhost:8084/socket.io/?EIO=3&transport=websocket
[ "presence", { channel: "2435937115056035" } ]
Presence Check index of 2435937115056035 is false
[ "open-channel", { channel: "2435937115056035", sender: "gxh0oi2jrs", maxAllowed: 6 } ]
------------open channel------------- 2435937115056035 by gxh0oi2jrs registered new in channels [ '2435937115056035' ] information added to channel { '2435937115056035': { channel: '2435937115056035', timestamp: '12/18/2018, 10:18:01 PM', maxAllowed: 6, users: [ 'gxh0oi2jrs' ], status: 'waiting', endtimestamp: 0, log: [ '12/18/2018, 10:18:01 PM:-channel created . User gxh0oi2jrs waiting ' ] } }
[ "open-channel-resp", { status: true, channel: "2435937115056035" } ]
navigate another browser client to same session url such as
check presence ["presence", {channel: "2435937115056035"}]
["presence", true]
Presence Check index of 2435937115056035 is true
["join-channel", {channel: "2435937115056035", sender: "2ilwvn9qq39",…}]
------------join channel------------- 2435937115056035 by 2ilwvn9qq39 isallowed true
[ "join-channel-resp" { status: true, channel: "2435937115056035", users: ["gxh0oi2jrs", "2ilwvn9qq39"] }]
Cases when user deosnt have ir isnt able to acces his audio/video devices due of any of reasons such as
- user has no webcam or microphone
- intentioanlly/accidentally denied access to the webcam
- plugs in the webcam/microphone after getUserMedia() code has initialized
- device is already used by another app on Windows
- user dismisses the privacy dialog
Rejections of the returned promise are made by passing a DOMException error object to the promise's failure handler. The DOMException interface represents an abnormal event
Possible errors are:
openrmc.webrtc.Errors = {
NOT_SUPPORTED : 'NOT_SUPPORTED',
CONSTRAINTS_REQUIRED : 'CONSTRAINTS_REQUIRED',
AUDIO_NOT_AVAILABLE : 'AUDIO_NOT_AVAILABLE',
VIDEO_NOT_AVAILABLE : 'VIDEO_NOT_AVAILABLE',
AV_NOT_AVAILABLE : 'AV_NOT_AVAILABLE'
} ;
-
AbortError - Although the user and operating system both granted access to the hardware device, and no hardware issues occurred that would cause a NotReadableError, some problem occurred which prevented the device from being used.
-
NotAllowedError - One or more of the requested source devices cannot be used at this time. This will happen if the browsing context is insecure (that is, the page was loaded using HTTP rather than HTTPS). It also happens if the user has specified that the current browsing instance is not permitted access to the device, the user has denied access for the current session, or the user has denied all access to user media devices globally. On browsers that support managing media permissions with Feature Policy, this error is returned if Feature Policy is not configured to allow access to the input source(s). Older versions of the specification used SecurityError for this instead; SecurityError has taken on a new meaning.
-
NotFoundError - No media tracks of the type specified were found that satisfy the given constraints. NotReadableError Although the user granted permission to use the matching devices, a hardware error occurred at the operating system, browser, or Web page level which prevented access to the device.
-
OverconstrainedError - The specified constraints resulted in no candidate devices which met the criteria requested. The error is an object of type OverconstrainedError, and has a constraint property whose string value is the name of a constraint which was impossible to meet, and a message property containing a human-readable string explaining the problem. Because this error can occur even when the user has not yet granted permission to use the underlying device, it can potentially be used as a fingerprinting surface.
-
SecurityError - User media support is disabled on the Document on which getUserMedia() was called. The mechanism by which user media support is enabled and disabled is left up to the individual user agent.
-
TypeError - The list of constraints specified is empty, or has all constraints set to false. This can also happen if you try to call getUserMedia() in an insecure context, since navigator.mediaDevices is undefined in an insecure context.
ref : https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
MIT