512 lines
14 KiB
HTML
Executable File
512 lines
14 KiB
HTML
Executable File
|
|
<title>WebRTC Desktop Viewer</title>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
<meta charset="utf-8">
|
|
|
|
<meta name="description" content="This WebRTC Experiment page shows privately shared screens, desktops, and parts of the screens." />
|
|
<meta name="keywords" content="WebRTC,Desktop-Sharing,Screen-Sharing,RTCWeb,WebRTC-Experiment,WebRTC-Demo" />
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
<link rel="author" type="text/html" href="https://plus.google.com/+MuazKhan">
|
|
<meta name="author" content="Muaz Khan">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
|
|
<script src="RTCMultiConnection.js"> </script>
|
|
<script src="CodecsHandler.js"></script>
|
|
<script src="IceServersHandler.js"></script>
|
|
<script src="getStats.js"></script>
|
|
<script src="websocket.js"> </script>
|
|
|
|
<style>
|
|
body,
|
|
html {
|
|
background: black;
|
|
text-align: center;
|
|
color: white;
|
|
overflow: hidden;
|
|
}
|
|
.local-media,
|
|
.remote-media {
|
|
max-width: 100%;
|
|
max-height: 70%;
|
|
}
|
|
.local-media-small,
|
|
.remote-media-small {
|
|
width: 20%;
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
}
|
|
button {
|
|
display: inline-block;
|
|
outline: 0;
|
|
color: white;
|
|
background: #4472b9;
|
|
white-space: nowrap;
|
|
border: 5px solid #4472b9 !important;
|
|
font-family: 'Gotham Rounded A', 'Gotham Rounded B', sans-serif;
|
|
font-weight: 500;
|
|
font-style: normal;
|
|
padding: 9px 16px !important;
|
|
line-height: 1.4;
|
|
position: relative;
|
|
border-radius: 10px;
|
|
-webkit-box-shadow: 5px 5px 0 0 rgba(0, 0, 0, 0.15);
|
|
box-shadow: 5px 5px 0 0 rgba(0, 0, 0, 0.15);
|
|
-webkit-transition: 0.1s;
|
|
transition: 0.1s;
|
|
}
|
|
button:hover,
|
|
button:active,
|
|
button:focus {
|
|
background: #04C;
|
|
}
|
|
button[disabled] {
|
|
background: transparent;
|
|
border-color: rgb(83, 81, 81);
|
|
color: rgb(139, 133, 133);
|
|
}
|
|
#container {
|
|
-webkit-perspective: 1000;
|
|
background-color: #000000;
|
|
height: 100%;
|
|
margin: 0px auto;
|
|
position: absolute;
|
|
width: 100%;
|
|
}
|
|
#card {
|
|
-webkit-transform-style: preserve-3d;
|
|
-webkit-transition-duration: 2s;
|
|
-webkit-transition-property: rotation;
|
|
}
|
|
#local {
|
|
-webkit-backface-visibility: hidden;
|
|
-webkit-transform: scale(-1, 1);
|
|
position: absolute;
|
|
width: 100%;
|
|
}
|
|
#remote {
|
|
-webkit-backface-visibility: hidden;
|
|
-webkit-transform: rotateY(180deg);
|
|
position: absolute;
|
|
width: 100%;
|
|
}
|
|
#mini {
|
|
/* -webkit-transform: scale(-1, 1); */
|
|
|
|
bottom: 0;
|
|
height: 30%;
|
|
opacity: 1.0;
|
|
position: absolute;
|
|
right: 4px;
|
|
width: 30%;
|
|
}
|
|
#remoteVideo {
|
|
-webkit-transition-duration: 2s;
|
|
-webkit-transition-property: opacity;
|
|
height: 100%;
|
|
opacity: 0;
|
|
width: 100%;
|
|
}
|
|
#info-bar {
|
|
background-color: #15DBFF;
|
|
bottom: 55%;
|
|
color: rgb(255, 255, 255);
|
|
font-size: 25px;
|
|
font-weight: bold;
|
|
height: 38px;
|
|
line-height: 38px;
|
|
position: absolute;
|
|
text-align: center;
|
|
width: 100%;
|
|
text-shadow: 1px 1px rgb(14, 105, 137);
|
|
border: 2px solid rgb(47, 102, 118);
|
|
box-shadow: 0 0 6px white;
|
|
}
|
|
#stats-bar {
|
|
background-color: rgba(255, 255, 255, 0.92);
|
|
top: 20px;
|
|
left: 20px;
|
|
color: black;
|
|
font-size: 17px;
|
|
line-height: 1.5em;
|
|
position: absolute;
|
|
border: 2px solid rgba(0, 0, 0, 0.82);
|
|
border-radius: 7px;
|
|
font-family: Arial;
|
|
|
|
text-align: left;
|
|
display: none;
|
|
}
|
|
|
|
#stats-bar-html {
|
|
padding: 5px 10px;
|
|
}
|
|
|
|
#hide-stats-bar {
|
|
float: right;
|
|
cursor: pointer;
|
|
color: red;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
#hide-stats-bar:hover, #hide-stats-bar:active {
|
|
color: #6c1414;
|
|
}
|
|
</style>
|
|
|
|
<div id="container" ondblclick="enterFullScreen()">
|
|
<div id="card">
|
|
<div id="remote">
|
|
<video id="remoteVideo" autoplay playsinline></video>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="info-bar"></div>
|
|
<div id="stats-bar">
|
|
<div id="hide-stats-bar">x</div>
|
|
<div id="stats-bar-html"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
var params = {},
|
|
r = /([^&=]+)=?([^&]*)/g;
|
|
|
|
function d(s) {
|
|
return decodeURIComponent(s.replace(/\+/g, ' '));
|
|
}
|
|
|
|
var match, search = window.location.search;
|
|
while (match = r.exec(search.substring(1)))
|
|
params[d(match[1])] = d(match[2]);
|
|
|
|
window.params = params;
|
|
})();
|
|
|
|
// http://www.rtcmulticonnection.org/docs/constructor/
|
|
var connection = new RTCMultiConnection(params.s);
|
|
|
|
// www.rtcmulticonnection.org/docs/sdpConstraints/
|
|
connection.sdpConstraints.mandatory = {
|
|
OfferToReceiveAudio: true,
|
|
OfferToReceiveVideo: true
|
|
};
|
|
|
|
connection.getExternalIceServers = false;
|
|
connection.iceServers = IceServersHandler.getIceServers();
|
|
|
|
function setBandwidth(sdp) {
|
|
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
|
|
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:10000\r\n');
|
|
return sdp;
|
|
}
|
|
|
|
connection.processSdp = function(sdp) {
|
|
return sdp;
|
|
sdp = setBandwidth(sdp);
|
|
sdp = BandwidthHandler.setVideoBitrates(sdp, {
|
|
min: 300,
|
|
max: 10000
|
|
});
|
|
// sdp = CodecsHandler.preferVP9(sdp);
|
|
return sdp;
|
|
};
|
|
|
|
connection.optionalArgument = {
|
|
optional: [],
|
|
mandatory: {}
|
|
};
|
|
</script>
|
|
|
|
<script>
|
|
// DOM objects
|
|
var remoteVideo = document.getElementById('remoteVideo');
|
|
var card = document.getElementById('card');
|
|
var containerDiv;
|
|
|
|
if (navigator.mozGetUserMedia) {
|
|
attachMediaStream = function(element, stream) {
|
|
console.log("Attaching media stream");
|
|
element.mozSrcObject = stream;
|
|
element.play();
|
|
};
|
|
reattachMediaStream = function(to, from) {
|
|
console.log("Reattaching media stream");
|
|
to.mozSrcObject = from.mozSrcObject;
|
|
to.play();
|
|
};
|
|
} else if (navigator.webkitGetUserMedia) {
|
|
attachMediaStream = function(element, stream) {
|
|
if (typeof element.srcObject !== 'undefined') {
|
|
element.srcObject = stream;
|
|
} else if (typeof element.mozSrcObject !== 'undefined') {
|
|
element.mozSrcObject = stream;
|
|
} else if (typeof element.src !== 'undefined') {
|
|
element.src = URL.createObjectURL(stream);
|
|
} else {
|
|
console.log('Error attaching stream to element.');
|
|
}
|
|
};
|
|
reattachMediaStream = function(to, from) {
|
|
to.src = from.src;
|
|
};
|
|
} else {
|
|
console.log("Browser does not appear to be WebRTC-capable");
|
|
}
|
|
// onstream event; fired both for local and remote videos
|
|
|
|
var infoBar = document.getElementById('info-bar');
|
|
|
|
connection.onstatechange = function(state) {
|
|
infoBar.innerHTML = state.name + ': ' + state.reason;
|
|
|
|
if(state.name == 'request-rejected' && params.p) {
|
|
infoBar.innerHTML = 'Password (' + params.p + ') did not match with broadcaster, that is why your participation request has been rejected.<br>Please contact him and ask for valid password.';
|
|
}
|
|
|
|
if(state.name === 'room-not-available') {
|
|
infoBar.innerHTML = 'Screen share session is closed or paused. You will join automatically when share session is resumed.';
|
|
}
|
|
};
|
|
|
|
connection.onstreamid = function(event) {
|
|
infoBar.innerHTML = 'Remote peer is about to send his screen.';
|
|
};
|
|
|
|
connection.onstream = function(e) {
|
|
if (e.type == 'remote') {
|
|
connection.remoteStream = e.stream;
|
|
|
|
infoBar.style.display = 'none';
|
|
remoteStream = e.stream;
|
|
attachMediaStream(remoteVideo, e.stream);
|
|
waitForRemoteVideo();
|
|
remoteVideo.setAttribute('data-id', e.userid);
|
|
|
|
websocket.send('received-your-screen');
|
|
}
|
|
};
|
|
// if user left
|
|
connection.onleave = function(e) {
|
|
transitionToWaiting();
|
|
connection.onSessionClosed();
|
|
};
|
|
|
|
connection.onSessionClosed = function() {
|
|
infoBar.innerHTML = 'Screen sharing has been closed.';
|
|
infoBar.style.display = 'block';
|
|
statsBar.style.display = 'none';
|
|
connection.close();
|
|
websocket.onopen();
|
|
|
|
remoteVideo.pause();
|
|
remoteVideo.src = 'https://cdn.webrtc-experiment.com/images/muted.png';
|
|
};
|
|
|
|
connection.ondisconnected = connection.onSessionClosed;
|
|
connection.onstreamended = connection.onSessionClosed;
|
|
|
|
function waitForRemoteVideo() {
|
|
// Call the getVideoTracks method via adapter.js.
|
|
var videoTracks = remoteStream.getVideoTracks();
|
|
if (videoTracks.length === 0 || remoteVideo.currentTime > 0) {
|
|
transitionToActive();
|
|
} else {
|
|
setTimeout(waitForRemoteVideo, 100);
|
|
}
|
|
}
|
|
|
|
function transitionToActive() {
|
|
remoteVideo.style.opacity = 1;
|
|
card.style.webkitTransform = 'rotateY(180deg)';
|
|
window.onresize();
|
|
}
|
|
|
|
function transitionToWaiting() {
|
|
card.style.webkitTransform = 'rotateY(0deg)';
|
|
remoteVideo.style.opacity = 0;
|
|
}
|
|
// Set the video displaying in the center of window.
|
|
window.onresize = function() {
|
|
var aspectRatio;
|
|
if (remoteVideo.style.opacity === '1') {
|
|
aspectRatio = remoteVideo.videoWidth / remoteVideo.videoHeight;
|
|
} else {
|
|
return;
|
|
}
|
|
var innerHeight = this.innerHeight;
|
|
var innerWidth = this.innerWidth;
|
|
var videoWidth = innerWidth < aspectRatio * window.innerHeight ?
|
|
innerWidth : aspectRatio * window.innerHeight;
|
|
var videoHeight = innerHeight < window.innerWidth / aspectRatio ?
|
|
innerHeight : window.innerWidth / aspectRatio;
|
|
containerDiv = document.getElementById('container');
|
|
containerDiv.style.width = videoWidth + 'px';
|
|
containerDiv.style.height = videoHeight + 'px';
|
|
containerDiv.style.left = (innerWidth - videoWidth) / 2 + 'px';
|
|
containerDiv.style.top = (innerHeight - videoHeight) / 2 + 'px';
|
|
};
|
|
|
|
function enterFullScreen() {
|
|
container.webkitRequestFullScreen();
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
// using websockets as signaling medium
|
|
// http://www.rtcmulticonnection.org/docs/openSignalingChannel/
|
|
// using websockets for signaling
|
|
// www.RTCMultiConnection.org/docs/openSignalingChannel/
|
|
var onMessageCallbacks = {};
|
|
var pub = 'pub-c-3c0fc243-9892-4858-aa38-1445e58b4ecb';
|
|
var sub = 'sub-c-d0c386c6-7263-11e2-8b02-12313f022c90';
|
|
|
|
WebSocket = PUBNUB.ws;
|
|
var websocket = new WebSocket('wss://pubsub.pubnub.com/' + pub + '/' + sub + '/' + connection.channel);
|
|
|
|
websocket.onmessage = function(e) {
|
|
data = JSON.parse(e.data);
|
|
|
|
if (data.sender == connection.userid) return;
|
|
|
|
if (onMessageCallbacks[data.channel]) {
|
|
onMessageCallbacks[data.channel](data.message);
|
|
};
|
|
};
|
|
|
|
websocket.push = websocket.send;
|
|
websocket.send = function(data) {
|
|
data.sender = connection.userid;
|
|
websocket.push(JSON.stringify(data));
|
|
};
|
|
|
|
// overriding "openSignalingChannel" method
|
|
connection.openSignalingChannel = function(config) {
|
|
var channel = config.channel || this.channel;
|
|
onMessageCallbacks[channel] = config.onmessage;
|
|
|
|
if (config.onopen) setTimeout(config.onopen, 1000);
|
|
|
|
// directly returning socket object using "return" statement
|
|
return {
|
|
send: function(message) {
|
|
websocket.send({
|
|
sender: connection.userid,
|
|
channel: channel,
|
|
message: message
|
|
});
|
|
},
|
|
channel: channel
|
|
};
|
|
};
|
|
|
|
websocket.onerror = function() {
|
|
if(connection.numberOfConnectedUsers <= 0) {
|
|
location.reload();
|
|
}
|
|
};
|
|
|
|
websocket.onclose = function() {
|
|
if(connection.numberOfConnectedUsers <= 0) {
|
|
location.reload();
|
|
}
|
|
};
|
|
|
|
infoBar.innerHTML = 'Connecting WebSockets server.';
|
|
|
|
websocket.onopen = function() {
|
|
infoBar.innerHTML = 'WebSockets connection is opened.';
|
|
|
|
var sessionDescription = {
|
|
userid: params.s,
|
|
extra: {},
|
|
session: {
|
|
video: true,
|
|
oneway: true
|
|
},
|
|
sessionid: params.s
|
|
};
|
|
|
|
if (params.s) {
|
|
infoBar.innerHTML = 'Joining session: ' + params.s;
|
|
|
|
if(params.p) {
|
|
// it seems a password protected room.
|
|
connection.extra.password = params.p;
|
|
}
|
|
|
|
// http://www.rtcmulticonnection.org/docs/join/
|
|
connection.join(sessionDescription);
|
|
}
|
|
};
|
|
|
|
var dontDuplicate = {};
|
|
connection.onconnected = function(event) {
|
|
if(dontDuplicate[event.userid]) return;
|
|
dontDuplicate[event.userid] = true;
|
|
|
|
var peer = connection.peers[event.userid].peer.connection;
|
|
|
|
if(DetectRTC.browser.name === 'Firefox') {
|
|
getStats(peer, (connection.remoteStream || peer.getRemoteStreams()[0]).getTracks()[0], function(stats) {
|
|
onGettingWebRCStats(stats, event.userid);
|
|
}, 1000);
|
|
return;
|
|
}
|
|
|
|
getStats(peer, function(stats) {
|
|
onGettingWebRCStats(stats, event.userid);
|
|
}, 1000);
|
|
|
|
statsBar.style.display = 'block';
|
|
};
|
|
|
|
var statsBar = document.getElementById('stats-bar');
|
|
var statsBarHTML = document.getElementById('stats-bar-html');
|
|
var NO_MORE = false;
|
|
|
|
document.getElementById('hide-stats-bar').onclick = function() {
|
|
statsBar.style.display = 'none';
|
|
NO_MORE = true;
|
|
};
|
|
|
|
function onGettingWebRCStats(stats, userid) {
|
|
if(!connection.peers[userid] || NO_MORE) {
|
|
stats.nomore();
|
|
return;
|
|
}
|
|
|
|
var html = 'Codecs: ' + stats.audio.recv.codecs.concat(stats.video.recv.codecs).join(', ');
|
|
html += '<br>';
|
|
html += 'Resolutions: ' + stats.resolutions.recv.width + 'x' + stats.resolutions.recv.height;
|
|
html += '<br>';
|
|
html += 'Data: ' + bytesToSize(stats.audio.bytesReceived + stats.video.bytesReceived);
|
|
// html += '<br>';
|
|
// html += 'Speed: ' + bytesToSize(stats.bandwidth.speed || 0);
|
|
statsBarHTML.innerHTML = html;
|
|
}
|
|
|
|
function bytesToSize(bytes) {
|
|
var k = 1000;
|
|
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
if (bytes === 0) {
|
|
return '0 Bytes';
|
|
}
|
|
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);
|
|
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
|
|
}
|
|
|
|
window.addEventListener('offline', function() {
|
|
infoBar.innerHTML = 'You seems offLine.';
|
|
}, false);
|
|
|
|
window.addEventListener('online', function() {
|
|
infoBar.innerHTML = 'You seems onLine. Reloading the page..';
|
|
location.reload();
|
|
}, false);
|
|
</script>
|