rtcmulticonnection
Version:
RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.
539 lines (453 loc) • 16.5 kB
HTML
<html itemscope itemtype="http://schema.org/WebPage">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0, user-scalable=no">
<title>Dashboard + Video Conferencing + Chat + File Sharing | RTCMultiConnection</title>
<meta name="description" content="WebRTC Dashboard including support for canvas drawing, canvas data syncing, video conferencing, screen sharing and video conferencing. Including chat and file sharing.">
<meta name="author" content="Muaz Khan">
<link rel="author" type="text/html" href="https://plus.google.com/+MuazKhan">
</head>
<body>
<script src="https://cdn.webrtc-experiment.com/Canvas-Designer/canvas-designer-widget.js"></script>
<style>
* {
-webkit-user-select: none;
-moz-user-select: none;
-o-user-select: none;
-ms-user-select: none;
user-select: none;
}
input[type=text] {
-webkit-user-select: initial;
-moz-user-select: initial;
-o-user-select: initial;
-ms-user-select: initial;
user-select: initial;
}
button, input {
font-family: Myriad, Arial, Verdana;
font-weight: normal;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
padding: 4px 12px;
text-decoration: none;
color: rgb(27, 26, 26);
display: inline-block;
box-shadow: rgb(255, 255, 255) 1px 1px 0px 0px inset;
text-shadow: none;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0.05, rgb(241, 241, 241)), to(rgb(230, 230, 230)));
font-size: 20px;
border: 1px solid red;
outline:none;
}
button:hover, button:hover, input:focus {
background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(5%, rgb(221, 221, 221)), to(rgb(250, 250, 250)));
border: 1px solid rgb(142, 142, 142);
}
button[disabled], input[disabled], textarea[disabled] {
background: rgb(249, 249, 249);
border: 1px solid rgb(218, 207, 207);
color: rgb(197, 189, 189);
}
input {
background: white;
}
.extra-controls {
position: absolute;
right: 21%;
}
#btn-comments {
color: red;
margin-top: 5px;
font-size: 24px;
text-shadow: 1px 1px white;
}
#other-videos {
margin-top: 5px;
}
#other-videos video {
width: 45%;
margin: 5px;
border: 1px solid black;
padding: 1px;
border-radius: 3px;
}
#txt-chat-message {
width: 100%;
resize: vertical;
margin: 5px;
margin-right: 0;
min-height: 30px;
}
#btn-chat-message {
margin: 5px;
}
#conversation-panel {
margin-bottom: 20px;
text-align: left;
max-height: 200px;
overflow: auto;
border-top: 1px solid #E5E5E5;
width: 106%;
}
#conversation-panel .message {
border-bottom: 1px solid #E5E5E5;
padding: 5px 10px;
}
#conversation-panel .message img, #conversation-panel .message video, #conversation-panel .message iframe {
max-width: 100%;
}
#main-video {
width: 100%;
margin-top: -9px;
border-bottom: 1px solid #121010;
display: none;
padding-bottom: 1px;
}
hr {
height: 1px;
border: 0;
background: #E5E5E5;
}
#btn-attach-file {
width: 25px;
vertical-align: middle;
cursor: pointer;
display: none;
}
.checkmark {
display:none;
width: 15px;
vertical-align: middle;
}
</style>
<div id="widget-container" style="position: fixed;bottom: 0;right: 0;left: 20%;height: 100%;border: 1px solid black; border-top:0; border-bottom: 0;"></div>
<div style="width: 20%; height: 100%; position: absolute;left:0;">
<video id="main-video" controls></video>
<div id="other-videos"></div>
<hr>
<div style="padding: 5px 10px;">
<div id="onUserStatusChanged">
<label for="user-full-name">Name:</label>
<input type="text" id="user-full-name" placeholder="Name" disabled style="margin: 5px; margin-right: 0;width: 55%;margin-right: 2px;">
<br>
<label for="room-name">Room:</label>
<input type="text" id="room-name" placeholder="room-name" disabled style="margin: 5px; margin-right: 0;width: 55%;margin-right: 2px;">
<br>
<button id="open-room" disabled style="margin: 5px; margin-left: 52px;">Join</button>
</div>
</div>
<div style="margin-top: 20px;position: absolute;bottom: 0;background: white; padding-bottom: 20px; width: 94%">
<div id="conversation-panel"></div>
<textarea id="txt-chat-message" disabled></textarea>
<button id="btn-chat-message" disabled>Send</button>
<img id="btn-attach-file" src="https://webrtcweb.com/attach-file.png" title="Attach a File">
</div>
</div>
<script>
document.getElementById('open-room').onclick = function() {
var roomid = roomName.value;
if (!roomid.length) return alert('Please enter roomid.');
this.disabled = true;
connection.openOrJoin(roomid, function(isRoomAlreadyCreatedBySomeoneElse) {
// Remove "true" from below line, if you want only presenter/moderator to share dashboard screen
if(true || isRoomAlreadyCreatedBySomeoneElse === false) {
onOpenRoom();
}
else {
document.getElementById('open-room').parentNode.innerHTML = '';
}
});
};
var designer = new CanvasDesigner();
// you can place widget.html anywhere
designer.widgetHtmlURL = 'https://cdn.webrtc-experiment.com/Canvas-Designer/widget.html';
designer.widgetJsURL = 'https://cdn.webrtc-experiment.com/Canvas-Designer/widget.min.js'
designer.addSyncListener(function(data) {
connection.send(data);
});
designer.setSelected('pencil');
designer.setTools({
pencil: true,
text: true,
image: true,
pdf: true,
eraser: true,
line: true,
arrow: true,
dragSingle: true,
dragMultiple: true,
arc: true,
rectangle: true,
quadratic: false,
bezier: true,
marker: true,
zoom: false,
lineWidth: false,
colorsPicker: false,
extraOptions: false,
code: false,
undo: true
});
designer.appendTo(document.getElementById('widget-container'));
</script>
<script src="/dev/adapter.js"></script>
<script src="/dist/RTCMultiConnection.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/dev/FileBufferReader.js"></script>
<script>
var connection = new RTCMultiConnection();
connection.socketURL = '/';
// connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'canvas-designer';
connection.chunkSize = 16000;
connection.enableFileSharing = true;
connection.session = {
audio: true,
video: true,
data: true
};
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
connection.onUserStatusChanged = function(event) {
var infoBar = document.getElementById('onUserStatusChanged');
var names = [];
connection.getAllParticipants().forEach(function(pid) {
names.push(getFullName(pid));
});
if(!names.length) {
names = ['Only You'];
}
else {
names = [connection.extra.userFullName || 'You'].concat(names);
}
infoBar.innerHTML = '<b>Active users:</b> ' + names.join(', ');
};
connection.onopen = function(event) {
connection.onUserStatusChanged(event);
if (designer.pointsLength <= 0) {
// make sure that remote user gets all drawings synced.
setTimeout(function() {
connection.send('plz-sync-points');
}, 1000);
}
document.getElementById('btn-chat-message').disabled = false;
document.getElementById('txt-chat-message').disabled = false;
document.getElementById('btn-attach-file').style.display = 'inline-block';
};
connection.onclose = connection.onerror = connection.onleave = function(event) {
connection.onUserStatusChanged(event);
};
connection.onmessage = function(event) {
if(event.data.chatMessage) {
appendChatMessage(event);
return;
}
if(event.data.checkmark === 'received') {
var checkmarkElement = document.getElementById(event.data.checkmark_id);
if(checkmarkElement) {
checkmarkElement.style.display = 'inline';
}
return;
}
if (event.data === 'plz-sync-points') {
designer.sync();
return;
}
designer.syncData(event.data);
};
</script>
<script src="https://cdn.webrtc-experiment.com/Canvas-Designer/dev/webrtc-handler.js"></script>
<script>
function onOpenRoom() {
// capture canvas-2d stream
// and share in realtime using RTCPeerConnection.addStream
// requires: dev/webrtc-handler.js
designer.captureStream(function(stream) {
stream.isScreen = true;
stream.streamid = stream.id;
stream.type = 'local';
/*
var video = document.createElement('video');
video.muted = true;
video.srcObject = stream;
video.play();
*/
connection.attachStreams.push(stream);
connection.onstream({
stream: stream,
type: 'local',
streamid: stream.id,
// mediaElement: video
});
});
}
connection.onstream = function(event) {
if(event.stream.isScreen) {
var video = document.getElementById('main-video');
video.setAttribute('data-streamid', event.streamid);
video.style.display = 'block';
video.srcObject = event.stream;
video.play();
}
else {
event.mediaElement.controls = false;
var otherVideos = document.querySelector('#other-videos');
otherVideos.appendChild(event.mediaElement);
}
};
connection.onstreamended = function(event) {
var video = document.querySelector('video[data-streamid="' + event.streamid + '"]');
if(!video) {
video = document.getElementById(event.streamid);
if(video) {
video.parentNode.removeChild(video);
return;
}
}
if(video) {
video.srcObject = null;
video.style.display = 'none';
}
};
var conversationPanel = document.getElementById('conversation-panel');
function appendChatMessage(event, checkmark_id) {
var div = document.createElement('div');
div.className = 'message';
if(event.data) {
div.innerHTML = '<b>' + (event.extra.userFullName || event.userid) + ':</b><br>' + event.data.chatMessage;
if(event.data.checkmark_id) {
connection.send({
checkmark: 'received',
checkmark_id: event.data.checkmark_id
});
}
}
else {
div.innerHTML = '<b>You:</b> <img class="checkmark" id="' + checkmark_id + '" title="Received" src="https://webrtcweb.com/checkmark.png"><br>' + event;
div.style.background = '#cbffcb';
}
conversationPanel.appendChild(div);
conversationPanel.scrollTop = conversationPanel.clientHeight;
conversationPanel.scrollTop = conversationPanel.scrollHeight - conversationPanel.scrollTop;
}
document.getElementById('btn-chat-message').onclick = function() {
var chatMessage = document.getElementById('txt-chat-message').value;
document.getElementById('txt-chat-message').value = '';
if(!chatMessage || !chatMessage.replace(/ /g, '').length) return;
var checkmark_id = connection.userid + connection.token();
appendChatMessage(chatMessage, checkmark_id);
connection.send({
chatMessage: chatMessage,
checkmark_id: checkmark_id
});
};
document.getElementById('txt-chat-message').onkeyup = function(e) {
var code = e.keyCode || e.which;
if (code == 13) {
document.getElementById('btn-chat-message').click(); // you can even call "onclick"
}
};
document.getElementById('btn-attach-file').onclick = function() {
var file = new FileSelector();
file.selectSingleFile(function(file) {
connection.send(file);
});
};
function getFileHTML(file) {
var url = file.url || URL.createObjectURL(file);
var attachment = '<a href="' + url + '" target="_blank" download="' + file.name + '">Download: <b>' + file.name + '</b></a>';
if (file.name.match(/\.jpg|\.png|\.jpeg|\.gif/gi)) {
attachment += '<br><img crossOrigin="anonymous" src="' + url + '">';
} else if (file.name.match(/\.wav|\.mp3/gi)) {
attachment += '<br><audio src="' + url + '" controls></audio>';
} else if (file.name.match(/\.pdf|\.js|\.txt|\.sh/gi)) {
attachment += '<iframe class="inline-iframe" src="' + url + '"></iframe></a>';
}
return attachment;
}
function getFullName(userid) {
var _userFullName = userid;
if(connection.peers[userid] && connection.peers[userid].extra.userFullName) {
_userFullName = connection.peers[userid].extra.userFullName;
}
return _userFullName;
}
connection.onFileEnd = function (file) {
var html = getFileHTML(file);
var div = progressHelper[file.uuid].div;
if(file.userid === connection.userid) {
div.innerHTML = '<b>You:</b><br>' + html;
div.style.background = '#cbffcb';
}
else {
div.innerHTML = '<b>' + getFullName(file.userid) + ':</b><br>' + html;
}
};
// to make sure file-saver dialog is not invoked.
connection.autoSaveToDisk = false;
var progressHelper = {};
connection.onFileProgress = function (chunk, uuid) {
var helper = progressHelper[chunk.uuid];
helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max;
updateLabel(helper.progress, helper.label);
};
connection.onFileStart = function (file) {
var div = document.createElement('div');
div.className = 'message';
if(file.userid === connection.userid) {
div.innerHTML = '<b>You:</b><br><label>0%</label> <progress></progress>';
div.style.background = '#cbffcb';
}
else {
div.innerHTML = '<b>' + getFullName(file.userid) + ':</b><br><label>0%</label> <progress></progress>';
}
div.title = file.name;
conversationPanel.appendChild(div);
progressHelper[file.uuid] = {
div: div,
progress: div.querySelector('progress'),
label: div.querySelector('label')
};
progressHelper[file.uuid].progress.max = file.maxChunks;
conversationPanel.scrollTop = conversationPanel.clientHeight;
conversationPanel.scrollTop = conversationPanel.scrollHeight - conversationPanel.scrollTop;
};
function updateLabel(progress, label) {
if (progress.position == -1) return;
var position = +progress.position.toFixed(2).split('.')[1] || 100;
label.innerHTML = position + '%';
}
var roomName = document.getElementById('room-name');
var userFullName = document.getElementById('user-full-name');
document.getElementById('open-room').disabled = false;
roomName.disabled = false;
userFullName.disabled = false;
roomName.onkeyup = roomName.onblur = roomName.oninput = roomName.onpaste = function() {
localStorage.setItem('canvas-designer-roomid', this.value);
};
if (localStorage.getItem('canvas-designer-roomid')) {
roomName.value = localStorage.getItem('canvas-designer-roomid');
}
userFullName.onkeyup = userFullName.onblur = userFullName.oninput = userFullName.onpaste = function() {
localStorage.setItem('canvas-designer-user-full-name', userFullName.value);
connection.extra.userFullName = userFullName.value;
if(connection.getAllParticipants().length) {
connection.updateExtraData();
}
};
if (localStorage.getItem('canvas-designer-user-full-name')) {
userFullName.value = localStorage.getItem('canvas-designer-user-full-name');
}
if(userFullName.value.length && userFullName.value.replace(/ /g, '').length) {
connection.extra.userFullName = userFullName.value;
}
</script>
</body>
</html>