init project
This commit is contained in:
commit
077a7d127e
361
CodecsHandler.js
Executable file
361
CodecsHandler.js
Executable file
@ -0,0 +1,361 @@
|
||||
// CodecsHandler.js
|
||||
|
||||
var CodecsHandler = (function() {
|
||||
function preferCodec(sdp, codecName) {
|
||||
var info = splitLines(sdp);
|
||||
|
||||
if (!info.videoCodecNumbers) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
sdp = preferCodecHelper(sdp, codecName, info);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function preferCodecHelper(sdp, codec, info, ignore) {
|
||||
var preferCodecNumber = '';
|
||||
|
||||
if (codec === 'vp8') {
|
||||
if (!info.vp8LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.vp8LineNumber;
|
||||
}
|
||||
|
||||
if (codec === 'vp9') {
|
||||
if (!info.vp9LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.vp9LineNumber;
|
||||
}
|
||||
|
||||
if (codec === 'h264') {
|
||||
if (!info.h264LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
preferCodecNumber = info.h264LineNumber;
|
||||
}
|
||||
|
||||
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
|
||||
|
||||
var newOrder = [preferCodecNumber];
|
||||
|
||||
if (ignore) {
|
||||
newOrder = [];
|
||||
}
|
||||
|
||||
info.videoCodecNumbers.forEach(function(codecNumber) {
|
||||
if (codecNumber === preferCodecNumber) return;
|
||||
newOrder.push(codecNumber);
|
||||
});
|
||||
|
||||
newLine += newOrder.join(' ');
|
||||
|
||||
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function splitLines(sdp) {
|
||||
var info = {};
|
||||
sdp.split('\n').forEach(function(line) {
|
||||
if (line.indexOf('m=video') === 0) {
|
||||
info.videoCodecNumbers = [];
|
||||
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
|
||||
codecNumber = codecNumber.trim();
|
||||
if (!codecNumber || !codecNumber.length) return;
|
||||
info.videoCodecNumbers.push(codecNumber);
|
||||
info.videoCodecNumbersOriginal = line;
|
||||
});
|
||||
}
|
||||
|
||||
if (line.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
|
||||
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (line.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
|
||||
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (line.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
|
||||
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
});
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
function removeVPX(sdp) {
|
||||
var info = splitLines(sdp);
|
||||
|
||||
// last parameter below means: ignore these codecs
|
||||
sdp = preferCodecHelper(sdp, 'vp9', info, true);
|
||||
sdp = preferCodecHelper(sdp, 'vp8', info, true);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function disableNACK(sdp) {
|
||||
if (!sdp || typeof sdp !== 'string') {
|
||||
throw 'Invalid arguments.';
|
||||
}
|
||||
|
||||
sdp = sdp.replace('a=rtcp-fb:126 nack\r\n', '');
|
||||
sdp = sdp.replace('a=rtcp-fb:126 nack pli\r\n', 'a=rtcp-fb:126 pli\r\n');
|
||||
sdp = sdp.replace('a=rtcp-fb:97 nack\r\n', '');
|
||||
sdp = sdp.replace('a=rtcp-fb:97 nack pli\r\n', 'a=rtcp-fb:97 pli\r\n');
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function prioritize(codecMimeType, peer) {
|
||||
if (!peer || !peer.getSenders || !peer.getSenders().length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!codecMimeType || typeof codecMimeType !== 'string') {
|
||||
throw 'Invalid arguments.';
|
||||
}
|
||||
|
||||
peer.getSenders().forEach(function(sender) {
|
||||
var params = sender.getParameters();
|
||||
for (var i = 0; i < params.codecs.length; i++) {
|
||||
if (params.codecs[i].mimeType == codecMimeType) {
|
||||
params.codecs.unshift(params.codecs.splice(i, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
sender.setParameters(params);
|
||||
});
|
||||
}
|
||||
|
||||
function removeNonG722(sdp) {
|
||||
return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, 'm=audio $1 RTP\/SAVPF 9');
|
||||
}
|
||||
|
||||
function setBAS(sdp, bandwidth, isScreen) {
|
||||
if (!bandwidth) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
if (typeof isFirefox !== 'undefined' && isFirefox) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
if (isScreen) {
|
||||
if (!bandwidth.screen) {
|
||||
console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.');
|
||||
} else if (bandwidth.screen < 300) {
|
||||
console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.');
|
||||
}
|
||||
}
|
||||
|
||||
// if screen; must use at least 300kbs
|
||||
if (bandwidth.screen && isScreen) {
|
||||
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:' + bandwidth.screen + '\r\n');
|
||||
}
|
||||
|
||||
// remove existing bandwidth lines
|
||||
if (bandwidth.audio || bandwidth.video) {
|
||||
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
|
||||
}
|
||||
|
||||
if (bandwidth.audio) {
|
||||
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n');
|
||||
}
|
||||
|
||||
if (bandwidth.screen) {
|
||||
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n');
|
||||
} else if (bandwidth.video) {
|
||||
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.video + '\r\n');
|
||||
}
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// Find the line in sdpLines that starts with |prefix|, and, if specified,
|
||||
// contains |substr| (case-insensitive search).
|
||||
function findLine(sdpLines, prefix, substr) {
|
||||
return findLineInRange(sdpLines, 0, -1, prefix, substr);
|
||||
}
|
||||
|
||||
// Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
|
||||
// and, if specified, contains |substr| (case-insensitive search).
|
||||
function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
|
||||
var realEndLine = endLine !== -1 ? endLine : sdpLines.length;
|
||||
for (var i = startLine; i < realEndLine; ++i) {
|
||||
if (sdpLines[i].indexOf(prefix) === 0) {
|
||||
if (!substr ||
|
||||
sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Gets the codec payload type from an a=rtpmap:X line.
|
||||
function getCodecPayloadType(sdpLine) {
|
||||
var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');
|
||||
var result = sdpLine.match(pattern);
|
||||
return (result && result.length === 2) ? result[1] : null;
|
||||
}
|
||||
|
||||
function setVideoBitrates(sdp, params) {
|
||||
params = params || {};
|
||||
var xgoogle_min_bitrate = params.min;
|
||||
var xgoogle_max_bitrate = params.max;
|
||||
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
|
||||
// VP8
|
||||
var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000');
|
||||
var vp8Payload;
|
||||
if (vp8Index) {
|
||||
vp8Payload = getCodecPayloadType(sdpLines[vp8Index]);
|
||||
}
|
||||
|
||||
if (!vp8Payload) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000');
|
||||
var rtxPayload;
|
||||
if (rtxIndex) {
|
||||
rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]);
|
||||
}
|
||||
|
||||
if (!rtxIndex) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString());
|
||||
if (rtxFmtpLineIndex !== null) {
|
||||
var appendrtxNext = '\r\n';
|
||||
appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228');
|
||||
sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext);
|
||||
sdp = sdpLines.join('\r\n');
|
||||
}
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function setOpusAttributes(sdp, params) {
|
||||
params = params || {};
|
||||
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
|
||||
// Opus
|
||||
var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000');
|
||||
var opusPayload;
|
||||
if (opusIndex) {
|
||||
opusPayload = getCodecPayloadType(sdpLines[opusIndex]);
|
||||
}
|
||||
|
||||
if (!opusPayload) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString());
|
||||
if (opusFmtpLineIndex === null) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
var appendOpusNext = '';
|
||||
appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1');
|
||||
appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1');
|
||||
|
||||
if (typeof params.maxaveragebitrate != 'undefined') {
|
||||
appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8);
|
||||
}
|
||||
|
||||
if (typeof params.maxplaybackrate != 'undefined') {
|
||||
appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8);
|
||||
}
|
||||
|
||||
if (typeof params.cbr != 'undefined') {
|
||||
appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1');
|
||||
}
|
||||
|
||||
if (typeof params.useinbandfec != 'undefined') {
|
||||
appendOpusNext += '; useinbandfec=' + params.useinbandfec;
|
||||
}
|
||||
|
||||
if (typeof params.usedtx != 'undefined') {
|
||||
appendOpusNext += '; usedtx=' + params.usedtx;
|
||||
}
|
||||
|
||||
if (typeof params.maxptime != 'undefined') {
|
||||
appendOpusNext += '\r\na=maxptime:' + params.maxptime;
|
||||
}
|
||||
|
||||
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext);
|
||||
|
||||
sdp = sdpLines.join('\r\n');
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// forceStereoAudio => via webrtcexample.com
|
||||
// requires getUserMedia => echoCancellation:false
|
||||
function forceStereoAudio(sdp) {
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
var fmtpLineIndex = null;
|
||||
for (var i = 0; i < sdpLines.length; i++) {
|
||||
if (sdpLines[i].search('opus/48000') !== -1) {
|
||||
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < sdpLines.length; i++) {
|
||||
if (sdpLines[i].search('a=fmtp') !== -1) {
|
||||
var payload = extractSdp(sdpLines[i], /a=fmtp:(\d+)/);
|
||||
if (payload === opusPayload) {
|
||||
fmtpLineIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fmtpLineIndex === null) return sdp;
|
||||
sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat('; stereo=1; sprop-stereo=1');
|
||||
sdp = sdpLines.join('\r\n');
|
||||
return sdp;
|
||||
}
|
||||
|
||||
return {
|
||||
removeVPX: removeVPX,
|
||||
disableNACK: disableNACK,
|
||||
prioritize: prioritize,
|
||||
removeNonG722: removeNonG722,
|
||||
setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) {
|
||||
return setBAS(sdp, bandwidth, isScreen);
|
||||
},
|
||||
setVideoBitrates: function(sdp, params) {
|
||||
return setVideoBitrates(sdp, params);
|
||||
},
|
||||
setOpusAttributes: function(sdp, params) {
|
||||
return setOpusAttributes(sdp, params);
|
||||
},
|
||||
preferVP9: function(sdp) {
|
||||
return preferCodec(sdp, 'vp9');
|
||||
},
|
||||
preferCodec: preferCodec,
|
||||
forceStereoAudio: forceStereoAudio
|
||||
};
|
||||
})();
|
||||
|
||||
// backward compatibility
|
||||
window.BandwidthHandler = CodecsHandler;
|
41
IceServersHandler.js
Executable file
41
IceServersHandler.js
Executable file
@ -0,0 +1,41 @@
|
||||
// IceServersHandler.js
|
||||
|
||||
var IceServersHandler = (function() {
|
||||
function getIceServers(connection) {
|
||||
// resiprocate: 3344+4433
|
||||
var iceServers = [{
|
||||
'urls': [
|
||||
'turn:webrtcweb.com:7788', // coTURN 7788+8877
|
||||
'turn:webrtcweb.com:4455', // restund udp
|
||||
|
||||
'turn:webrtcweb.com:7788?transport=udp', // coTURN udp
|
||||
'turn:webrtcweb.com:7788?transport=tcp', // coTURN tcp
|
||||
|
||||
'turn:webrtcweb.com:4455?transport=udp', // restund udp
|
||||
'turn:webrtcweb.com:5544?transport=tcp', // restund tcp
|
||||
|
||||
'turn:webrtcweb.com:7575?transport=udp', // pions/turn
|
||||
],
|
||||
'username': 'muazkh',
|
||||
'credential': 'muazkh'
|
||||
},
|
||||
{
|
||||
'urls': [
|
||||
'stun:stun.l.google.com:19302',
|
||||
'stun:stun.l.google.com:19302?transport=udp'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
if (typeof window.InstallTrigger !== 'undefined') {
|
||||
iceServers[0].urls = iceServers[0].urls.pop();
|
||||
iceServers[1].urls = iceServers[1].urls.pop();
|
||||
}
|
||||
|
||||
return iceServers;
|
||||
}
|
||||
|
||||
return {
|
||||
getIceServers: getIceServers
|
||||
};
|
||||
})();
|
468
MultiStreamsMixer.js
Executable file
468
MultiStreamsMixer.js
Executable file
@ -0,0 +1,468 @@
|
||||
// Last time updated: 2018-03-02 2:56:28 AM UTC
|
||||
|
||||
// ________________________
|
||||
// MultiStreamsMixer v1.0.5
|
||||
|
||||
// Open-Sourced: https://github.com/muaz-khan/MultiStreamsMixer
|
||||
|
||||
// --------------------------------------------------
|
||||
// Muaz Khan - www.MuazKhan.com
|
||||
// MIT License - www.WebRTC-Experiment.com/licence
|
||||
// --------------------------------------------------
|
||||
|
||||
function MultiStreamsMixer(arrayOfMediaStreams) {
|
||||
|
||||
// requires: chrome://flags/#enable-experimental-web-platform-features
|
||||
|
||||
var videos = [];
|
||||
var isStopDrawingFrames = false;
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
canvas.style = 'opacity:0;position:absolute;z-index:-1;top: -100000000;left:-1000000000; margin-top:-1000000000;margin-left:-1000000000;';
|
||||
(document.body || document.documentElement).appendChild(canvas);
|
||||
|
||||
this.disableLogs = false;
|
||||
this.frameInterval = 10;
|
||||
|
||||
this.width = 360;
|
||||
this.height = 240;
|
||||
|
||||
// use gain node to prevent echo
|
||||
this.useGainNode = true;
|
||||
|
||||
var self = this;
|
||||
|
||||
// _____________________________
|
||||
// Cross-Browser-Declarations.js
|
||||
|
||||
// WebAudio API representer
|
||||
var AudioContext = window.AudioContext;
|
||||
|
||||
if (typeof AudioContext === 'undefined') {
|
||||
if (typeof webkitAudioContext !== 'undefined') {
|
||||
/*global AudioContext:true */
|
||||
AudioContext = webkitAudioContext;
|
||||
}
|
||||
|
||||
if (typeof mozAudioContext !== 'undefined') {
|
||||
/*global AudioContext:true */
|
||||
AudioContext = mozAudioContext;
|
||||
}
|
||||
}
|
||||
|
||||
/*jshint -W079 */
|
||||
var URL = window.URL;
|
||||
|
||||
if (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') {
|
||||
/*global URL:true */
|
||||
URL = webkitURL;
|
||||
}
|
||||
|
||||
if (typeof navigator !== 'undefined' && typeof navigator.getUserMedia === 'undefined') { // maybe window.navigator?
|
||||
if (typeof navigator.webkitGetUserMedia !== 'undefined') {
|
||||
navigator.getUserMedia = navigator.webkitGetUserMedia;
|
||||
}
|
||||
|
||||
if (typeof navigator.mozGetUserMedia !== 'undefined') {
|
||||
navigator.getUserMedia = navigator.mozGetUserMedia;
|
||||
}
|
||||
}
|
||||
|
||||
var MediaStream = window.MediaStream;
|
||||
|
||||
if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
|
||||
MediaStream = webkitMediaStream;
|
||||
}
|
||||
|
||||
/*global MediaStream:true */
|
||||
if (typeof MediaStream !== 'undefined') {
|
||||
if (!('getVideoTracks' in MediaStream.prototype)) {
|
||||
MediaStream.prototype.getVideoTracks = function() {
|
||||
if (!this.getTracks) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var tracks = [];
|
||||
this.getTracks.forEach(function(track) {
|
||||
if (track.kind.toString().indexOf('video') !== -1) {
|
||||
tracks.push(track);
|
||||
}
|
||||
});
|
||||
return tracks;
|
||||
};
|
||||
|
||||
MediaStream.prototype.getAudioTracks = function() {
|
||||
if (!this.getTracks) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var tracks = [];
|
||||
this.getTracks.forEach(function(track) {
|
||||
if (track.kind.toString().indexOf('audio') !== -1) {
|
||||
tracks.push(track);
|
||||
}
|
||||
});
|
||||
return tracks;
|
||||
};
|
||||
}
|
||||
|
||||
// override "stop" method for all browsers
|
||||
if (typeof MediaStream.prototype.stop === 'undefined') {
|
||||
MediaStream.prototype.stop = function() {
|
||||
this.getTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var Storage = {};
|
||||
|
||||
if (typeof AudioContext !== 'undefined') {
|
||||
Storage.AudioContext = AudioContext;
|
||||
} else if (typeof webkitAudioContext !== 'undefined') {
|
||||
Storage.AudioContext = webkitAudioContext;
|
||||
}
|
||||
|
||||
function setSrcObject(stream, element, ignoreCreateObjectURL) {
|
||||
if ('createObjectURL' in URL && !ignoreCreateObjectURL) {
|
||||
try {
|
||||
element.src = URL.createObjectURL(stream);
|
||||
} catch (e) {
|
||||
setSrcObject(stream, element, true);
|
||||
return;
|
||||
}
|
||||
} else if ('srcObject' in element) {
|
||||
element.srcObject = stream;
|
||||
} else if ('mozSrcObject' in element) {
|
||||
element.mozSrcObject = stream;
|
||||
} else {
|
||||
alert('createObjectURL/srcObject both are not supported.');
|
||||
}
|
||||
}
|
||||
|
||||
this.startDrawingFrames = function() {
|
||||
drawVideosToCanvas();
|
||||
};
|
||||
|
||||
function drawVideosToCanvas() {
|
||||
if (isStopDrawingFrames) {
|
||||
return;
|
||||
}
|
||||
|
||||
var videosLength = videos.length;
|
||||
|
||||
var fullcanvas = false;
|
||||
var remaining = [];
|
||||
videos.forEach(function(video) {
|
||||
if (!video.stream) {
|
||||
video.stream = {};
|
||||
}
|
||||
|
||||
if (video.stream.fullcanvas) {
|
||||
fullcanvas = video;
|
||||
} else {
|
||||
remaining.push(video);
|
||||
}
|
||||
});
|
||||
|
||||
if (fullcanvas) {
|
||||
canvas.width = fullcanvas.stream.width;
|
||||
canvas.height = fullcanvas.stream.height;
|
||||
} else if (remaining.length) {
|
||||
canvas.width = videosLength > 1 ? remaining[0].width * 2 : remaining[0].width;
|
||||
|
||||
var height = 1;
|
||||
if (videosLength === 3 || videosLength === 4) {
|
||||
height = 2;
|
||||
}
|
||||
if (videosLength === 5 || videosLength === 6) {
|
||||
height = 3;
|
||||
}
|
||||
if (videosLength === 7 || videosLength === 8) {
|
||||
height = 4;
|
||||
}
|
||||
if (videosLength === 9 || videosLength === 10) {
|
||||
height = 5;
|
||||
}
|
||||
canvas.height = remaining[0].height * height;
|
||||
} else {
|
||||
canvas.width = self.width || 360;
|
||||
canvas.height = self.height || 240;
|
||||
}
|
||||
|
||||
if (fullcanvas && fullcanvas instanceof HTMLVideoElement) {
|
||||
drawImage(fullcanvas);
|
||||
}
|
||||
|
||||
remaining.forEach(function(video, idx) {
|
||||
drawImage(video, idx);
|
||||
});
|
||||
|
||||
setTimeout(drawVideosToCanvas, self.frameInterval);
|
||||
}
|
||||
|
||||
function drawImage(video, idx) {
|
||||
if (isStopDrawingFrames) {
|
||||
return;
|
||||
}
|
||||
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var width = video.width;
|
||||
var height = video.height;
|
||||
|
||||
if (idx === 1) {
|
||||
x = video.width;
|
||||
}
|
||||
|
||||
if (idx === 2) {
|
||||
y = video.height;
|
||||
}
|
||||
|
||||
if (idx === 3) {
|
||||
x = video.width;
|
||||
y = video.height;
|
||||
}
|
||||
|
||||
if (idx === 4) {
|
||||
y = video.height * 2;
|
||||
}
|
||||
|
||||
if (idx === 5) {
|
||||
x = video.width;
|
||||
y = video.height * 2;
|
||||
}
|
||||
|
||||
if (idx === 6) {
|
||||
y = video.height * 3;
|
||||
}
|
||||
|
||||
if (idx === 7) {
|
||||
x = video.width;
|
||||
y = video.height * 3;
|
||||
}
|
||||
|
||||
if (typeof video.stream.left !== 'undefined') {
|
||||
x = video.stream.left;
|
||||
}
|
||||
|
||||
if (typeof video.stream.top !== 'undefined') {
|
||||
y = video.stream.top;
|
||||
}
|
||||
|
||||
if (typeof video.stream.width !== 'undefined') {
|
||||
width = video.stream.width;
|
||||
}
|
||||
|
||||
if (typeof video.stream.height !== 'undefined') {
|
||||
height = video.stream.height;
|
||||
}
|
||||
|
||||
context.drawImage(video, x, y, width, height);
|
||||
|
||||
if (typeof video.stream.onRender === 'function') {
|
||||
video.stream.onRender(context, x, y, width, height, idx);
|
||||
}
|
||||
}
|
||||
|
||||
function getMixedStream() {
|
||||
isStopDrawingFrames = false;
|
||||
var mixedVideoStream = getMixedVideoStream();
|
||||
|
||||
var mixedAudioStream = getMixedAudioStream();
|
||||
if (mixedAudioStream) {
|
||||
mixedAudioStream.getAudioTracks().forEach(function(track) {
|
||||
mixedVideoStream.addTrack(track);
|
||||
});
|
||||
}
|
||||
|
||||
var fullcanvas;
|
||||
arrayOfMediaStreams.forEach(function(stream) {
|
||||
if (stream.fullcanvas) {
|
||||
fullcanvas = true;
|
||||
}
|
||||
});
|
||||
|
||||
return mixedVideoStream;
|
||||
}
|
||||
|
||||
function getMixedVideoStream() {
|
||||
resetVideoStreams();
|
||||
|
||||
var capturedStream;
|
||||
|
||||
if ('captureStream' in canvas) {
|
||||
capturedStream = canvas.captureStream();
|
||||
} else if ('mozCaptureStream' in canvas) {
|
||||
capturedStream = canvas.mozCaptureStream();
|
||||
} else if (!self.disableLogs) {
|
||||
console.error('Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features');
|
||||
}
|
||||
|
||||
var videoStream = new MediaStream();
|
||||
|
||||
capturedStream.getVideoTracks().forEach(function(track) {
|
||||
videoStream.addTrack(track);
|
||||
});
|
||||
|
||||
canvas.stream = videoStream;
|
||||
|
||||
return videoStream;
|
||||
}
|
||||
|
||||
function getMixedAudioStream() {
|
||||
// via: @pehrsons
|
||||
if (!Storage.AudioContextConstructor) {
|
||||
Storage.AudioContextConstructor = new Storage.AudioContext();
|
||||
}
|
||||
|
||||
self.audioContext = Storage.AudioContextConstructor;
|
||||
|
||||
self.audioSources = [];
|
||||
|
||||
if (self.useGainNode === true) {
|
||||
self.gainNode = self.audioContext.createGain();
|
||||
self.gainNode.connect(self.audioContext.destination);
|
||||
self.gainNode.gain.value = 0; // don't hear self
|
||||
}
|
||||
|
||||
var audioTracksLength = 0;
|
||||
arrayOfMediaStreams.forEach(function(stream) {
|
||||
if (!stream.getAudioTracks().length) {
|
||||
return;
|
||||
}
|
||||
|
||||
audioTracksLength++;
|
||||
|
||||
var audioSource = self.audioContext.createMediaStreamSource(stream);
|
||||
|
||||
if (self.useGainNode === true) {
|
||||
audioSource.connect(self.gainNode);
|
||||
}
|
||||
|
||||
self.audioSources.push(audioSource);
|
||||
});
|
||||
|
||||
if (!audioTracksLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.audioDestination = self.audioContext.createMediaStreamDestination();
|
||||
self.audioSources.forEach(function(audioSource) {
|
||||
audioSource.connect(self.audioDestination);
|
||||
});
|
||||
return self.audioDestination.stream;
|
||||
}
|
||||
|
||||
function getVideo(stream) {
|
||||
var video = document.createElement('video');
|
||||
|
||||
setSrcObject(stream, video);
|
||||
|
||||
video.muted = true;
|
||||
video.volume = 0;
|
||||
|
||||
video.width = stream.width || self.width || 360;
|
||||
video.height = stream.height || self.height || 240;
|
||||
|
||||
video.play();
|
||||
|
||||
return video;
|
||||
}
|
||||
|
||||
this.appendStreams = function(streams) {
|
||||
if (!streams) {
|
||||
throw 'First parameter is required.';
|
||||
}
|
||||
|
||||
if (!(streams instanceof Array)) {
|
||||
streams = [streams];
|
||||
}
|
||||
|
||||
arrayOfMediaStreams.concat(streams);
|
||||
|
||||
streams.forEach(function(stream) {
|
||||
if (stream.getVideoTracks().length) {
|
||||
var video = getVideo(stream);
|
||||
video.stream = stream;
|
||||
videos.push(video);
|
||||
}
|
||||
|
||||
if (stream.getAudioTracks().length && self.audioContext) {
|
||||
var audioSource = self.audioContext.createMediaStreamSource(stream);
|
||||
audioSource.connect(self.audioDestination);
|
||||
self.audioSources.push(audioSource);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.releaseStreams = function() {
|
||||
videos = [];
|
||||
isStopDrawingFrames = true;
|
||||
|
||||
if (self.gainNode) {
|
||||
self.gainNode.disconnect();
|
||||
self.gainNode = null;
|
||||
}
|
||||
|
||||
if (self.audioSources.length) {
|
||||
self.audioSources.forEach(function(source) {
|
||||
source.disconnect();
|
||||
});
|
||||
self.audioSources = [];
|
||||
}
|
||||
|
||||
if (self.audioDestination) {
|
||||
self.audioDestination.disconnect();
|
||||
self.audioDestination = null;
|
||||
}
|
||||
|
||||
if (self.audioContext) {
|
||||
self.audioContext.close();
|
||||
}
|
||||
|
||||
self.audioContext = null;
|
||||
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
if (canvas.stream) {
|
||||
canvas.stream.stop();
|
||||
canvas.stream = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.resetVideoStreams = function(streams) {
|
||||
if (streams && !(streams instanceof Array)) {
|
||||
streams = [streams];
|
||||
}
|
||||
|
||||
resetVideoStreams(streams);
|
||||
};
|
||||
|
||||
function resetVideoStreams(streams) {
|
||||
videos = [];
|
||||
streams = streams || arrayOfMediaStreams;
|
||||
|
||||
// via: @adrian-ber
|
||||
streams.forEach(function(stream) {
|
||||
if (!stream.getVideoTracks().length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var video = getVideo(stream);
|
||||
video.stream = stream;
|
||||
videos.push(video);
|
||||
});
|
||||
}
|
||||
|
||||
// for debugging
|
||||
this.name = 'MultiStreamsMixer';
|
||||
this.toString = function() {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
this.getMixedStream = getMixedStream;
|
||||
|
||||
}
|
68
README.md
Executable file
68
README.md
Executable file
@ -0,0 +1,68 @@
|
||||
# Chrome extension for WebRTC Screen Sharing
|
||||
|
||||
<a target="_blank" href="https://chrome.google.com/webstore/detail/webrtc-desktop-sharing/nkemblooioekjnpfekmjhpgkackcajhg"><img alt="WebRTC Screen Sharing" src="https://lh3.googleusercontent.com/Jpi56T9fBfBXJGsDJchpAvW-PvZrysL99GLibfUKMVon8mk0KnBZtZU3W08IbkeYIAgyRvz9Lg=w640-h400-e365" title="WebRTC Screen Sharing"></img></a>
|
||||
|
||||
## How to install?
|
||||
|
||||
<a target="_blank" href="https://chrome.google.com/webstore/detail/webrtc-desktop-sharing/nkemblooioekjnpfekmjhpgkackcajhg"><img alt="Install Dessktop Sharing Extension" src="https://raw.github.com/GoogleChrome/chrome-app-samples/master/tryitnowbutton_small.png" title="Click here to install this sample from the Chrome Web Store"></img></a>
|
||||
|
||||
* https://chrome.google.com/webstore/detail/webrtc-desktop-sharing/nkemblooioekjnpfekmjhpgkackcajhg
|
||||
|
||||
## How to view screen?
|
||||
|
||||
Try any of the below URL. Replace `your_room_id` with real room-id:
|
||||
|
||||
```
|
||||
https://webrtcweb.com/screen?s=your_room_id
|
||||
https://cdn.rawgit.com/muaz-khan/Chrome-Extensions/master/desktopCapture-p2p/index.html
|
||||
```
|
||||
|
||||
## Developer Notes
|
||||
|
||||
1. Chrome extension can share your screen, tab, any application's window, camera, microphone and speakers.
|
||||
2. Clicking extension icon will generate a unique random room URL. You can share that URL with multiple users and all of them can view your screen.
|
||||
3. [RTCMultiConnection](https://github.com/muaz-khan/RTCMultiConnection) is a WebRTC library that is used for peer-to-peer WebRTC streaming.
|
||||
4. PubNub is used as a signaling method for handshake. However you can use [any WebRTC signaing option](https://github.com/muaz-khan/WebRTC-Experiment/blob/master/Signaling.md).
|
||||
5. You can replace or include your own STUN+TURN servers in the [IceServersHandler.js](https://github.com/muaz-khan/Chrome-Extensions/blob/master/desktopCapture-p2p/IceServersHandler.js) file.
|
||||
6. VP8 is currently default video codecs. However VP9 is recommended. You can always change codecs using options page.
|
||||
7. [getStats](https://github.com/muaz-khan/getStats) is a WebRTC library that is used for bandwidth & codecs detection. This library is optional. You can always remove it.
|
||||
|
||||
## Before publishing it for your own business
|
||||
|
||||
> This step is optional. You can keep using `webrtcweb.com` URL as a screen viewer.
|
||||
|
||||
Open [desktop-capturing.js](https://github.com/muaz-khan/Chrome-Extensions/blob/master/desktopCapture-p2p/desktop-capturing.js) and find following line:
|
||||
|
||||
```javascript
|
||||
var resultingURL = 'https://webrtcweb.com/screen?s=' + connection.sessionid;
|
||||
```
|
||||
|
||||
Replace above line with your own server/website:
|
||||
|
||||
```javascript
|
||||
var resultingURL = 'https://yourWebSite.com/index.html?s=' + connection.sessionid;
|
||||
```
|
||||
|
||||
You can find `index.html` here:
|
||||
|
||||
* [desktopCapture-p2p/index.html](https://github.com/muaz-khan/Chrome-Extensions/blob/master/desktopCapture-p2p/index.html)
|
||||
|
||||
## How to publish it for your own business?
|
||||
|
||||
Make ZIP of the directory. Then navigate to [Chrome WebStore Developer Dashboard](https://chrome.google.com/webstore/developer/dashboard) and click **Add New Item** blue button.
|
||||
|
||||
To learn more about how to publish a chrome extension in Google App Store:
|
||||
|
||||
* https://developer.chrome.com/webstore/publish
|
||||
|
||||
## For more information
|
||||
|
||||
For additional information, click [this link](https://github.com/muaz-khan/WebRTC-Experiment/blob/7cd04a81b30cdca2db159eb746e2714307640767/Chrome-Extensions/desktopCapture/README.md).
|
||||
|
||||
## It is Open-Sourced!
|
||||
|
||||
* https://github.com/muaz-khan/Chrome-Extensions/tree/master/desktopCapture-p2p
|
||||
|
||||
## License
|
||||
|
||||
[Chrome-Extensions](https://github.com/muaz-khan/Chrome-Extensions) are released under [MIT licence](https://www.webrtc-experiment.com/licence/) . Copyright (c) [Muaz Khan](https://plus.google.com/+MuazKhan).
|
6557
RTCMultiConnection.js
Executable file
6557
RTCMultiConnection.js
Executable file
File diff suppressed because it is too large
Load Diff
1
camera-mic.html
Executable file
1
camera-mic.html
Executable file
@ -0,0 +1 @@
|
||||
<script src="camera-mic.js"></script>
|
15
camera-mic.js
Executable file
15
camera-mic.js
Executable file
@ -0,0 +1,15 @@
|
||||
document.write('<h1 style="font-family: Courier New; font-size: 30px; color:green;margin-top:200px;">The purpose of this page is to access your camera and microphone.</h1>');
|
||||
document.write('<h1 style="font-family: Courier New; font-size: 25px; color:red;margin-top:20px;">You can REMOVE i.e. DELETE camera permissions anytime on this page:</h1>');
|
||||
document.write('<pre style="font-family: Courier New; font-size: 25px; color:blue;margin-top:20px;">chrome://settings/content/camera?search=camera</pr>');
|
||||
|
||||
var constraints = {
|
||||
audio: true,
|
||||
video: true
|
||||
};
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
|
||||
document.write('<h1 style="font-family: Courier New; font-size: 35px; color: green;"></h1><video autoplay controls src="' + URL.createObjectURL(stream) + '"></video>');
|
||||
document.querySelector('h1').innerHTML = 'Now you can close this page and click extension icon again.'
|
||||
}).catch(function() {
|
||||
document.querySelector('h1').innerHTML = 'Unable to capture your camera and microphone.';
|
||||
});
|
925
desktop-capturing.js
Executable file
925
desktop-capturing.js
Executable file
@ -0,0 +1,925 @@
|
||||
// Muaz Khan - https://github.com/muaz-khan
|
||||
// MIT License - https://www.WebRTC-Experiment.com/licence/
|
||||
// Source Code - https://github.com/muaz-khan/Chrome-Extensions
|
||||
|
||||
// this page is using desktopCapture API to capture and share desktop
|
||||
// http://developer.chrome.com/extensions/desktopCapture.html
|
||||
|
||||
// chrome.browserAction.onClicked.addListener(captureDesktop);
|
||||
|
||||
var runtimePort;
|
||||
var websocket;
|
||||
chrome.runtime.onConnect.addListener(function(port) {
|
||||
runtimePort = port;
|
||||
|
||||
runtimePort.onMessage.addListener(function(message) {
|
||||
if (!message || !message.messageFromContentScript1234) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.startSharing || message.stopSharing) {
|
||||
captureDesktop();
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
window.addEventListener('offline', function() {
|
||||
if (!connection || !connection.attachStreams.length) return;
|
||||
|
||||
setDefaults();
|
||||
chrome.runtime.reload();
|
||||
}, false);
|
||||
|
||||
window.addEventListener('online', function() {
|
||||
if (!connection) return;
|
||||
|
||||
setDefaults();
|
||||
chrome.runtime.reload();
|
||||
}, false);
|
||||
|
||||
function captureDesktop() {
|
||||
if (connection && connection.attachStreams[0]) {
|
||||
setDefaults();
|
||||
|
||||
connection.attachStreams.forEach(function(stream) {
|
||||
stream.getTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
});
|
||||
});
|
||||
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'false',
|
||||
enableCamera: 'false',
|
||||
enableScreen: 'false',
|
||||
isSharingOn: 'false',
|
||||
enableSpeakers: 'false'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Capturing Desktop'
|
||||
});
|
||||
|
||||
chrome.storage.sync.get(null, function(items) {
|
||||
var resolutions = {};
|
||||
|
||||
if (items['room_password']) {
|
||||
room_password = items['room_password'];
|
||||
}
|
||||
|
||||
if (items['room_id']) {
|
||||
room_id = items['room_id'];
|
||||
}
|
||||
|
||||
if (items['codecs']) {
|
||||
codecs = items['codecs'];
|
||||
}
|
||||
|
||||
if (items['bandwidth']) {
|
||||
bandwidth = items['bandwidth'];
|
||||
}
|
||||
|
||||
if (items['enableTabCaptureAPI'] == 'true') {
|
||||
enableTabCaptureAPI = items['enableTabCaptureAPI'];
|
||||
}
|
||||
|
||||
if (items['enableMicrophone'] == 'true') {
|
||||
enableMicrophone = items['enableMicrophone'];
|
||||
}
|
||||
|
||||
if (items['enableSpeakers'] == 'true') {
|
||||
enableSpeakers = items['enableSpeakers'];
|
||||
}
|
||||
|
||||
if (items['enableCamera'] == 'true') {
|
||||
enableCamera = items['enableCamera'];
|
||||
}
|
||||
|
||||
if (items['enableScreen'] == 'true') {
|
||||
enableScreen = items['enableScreen'];
|
||||
}
|
||||
|
||||
if (items['enableTabCaptureAPI'] == 'true') {
|
||||
enableTabCaptureAPI = items['enableTabCaptureAPI'];
|
||||
}
|
||||
|
||||
if (items['isSharingOn'] == 'true') {
|
||||
isSharingOn = items['isSharingOn'];
|
||||
}
|
||||
|
||||
var _resolutions = items['resolutions'];
|
||||
if (!_resolutions) {
|
||||
_resolutions = 'fit-screen';
|
||||
chrome.storage.sync.set({
|
||||
resolutions: 'fit-screen'
|
||||
}, function() {});
|
||||
}
|
||||
|
||||
if (_resolutions === 'fit-screen') {
|
||||
// resolutions.maxWidth = screen.availWidth;
|
||||
// resolutions.maxHeight = screen.availHeight;
|
||||
|
||||
resolutions.maxWidth = screen.width;
|
||||
resolutions.maxHeight = screen.height;
|
||||
}
|
||||
|
||||
if (_resolutions === '4K') {
|
||||
resolutions.maxWidth = 3840;
|
||||
resolutions.maxHeight = 2160;
|
||||
}
|
||||
|
||||
if (_resolutions === '1080p') {
|
||||
resolutions.maxWidth = 1920;
|
||||
resolutions.maxHeight = 1080;
|
||||
}
|
||||
|
||||
if (_resolutions === '720p') {
|
||||
resolutions.maxWidth = 1280;
|
||||
resolutions.maxHeight = 720;
|
||||
}
|
||||
|
||||
if (_resolutions === '360p') {
|
||||
resolutions.maxWidth = 640;
|
||||
resolutions.maxHeight = 360;
|
||||
}
|
||||
|
||||
if (_resolutions === '4K') {
|
||||
alert('"4K" resolutions is not stable in Chrome. Please try "fit-screen" instead.');
|
||||
}
|
||||
|
||||
var sources = ['screen', 'window', 'tab'];
|
||||
|
||||
if (enableSpeakers) {
|
||||
sources.push('audio');
|
||||
}
|
||||
|
||||
if (enableTabCaptureAPI) {
|
||||
captureTabUsingTabCapture(resolutions);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enableCamera || enableMicrophone) {
|
||||
captureCamera(function(stream) {
|
||||
if (!enableScreen) {
|
||||
gotCustomStream(stream);
|
||||
//CHANGES
|
||||
//gotStream(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
desktop_id = chrome.desktopCapture.chooseDesktopMedia(sources, function(chromeMediaSourceId, opts) {
|
||||
opts = opts || {};
|
||||
opts.resolutions = resolutions;
|
||||
opts.stream = stream;
|
||||
onAccessApproved(chromeMediaSourceId, opts);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
desktop_id = chrome.desktopCapture.chooseDesktopMedia(sources, function(chromeMediaSourceId, opts) {
|
||||
opts = opts || {};
|
||||
opts.resolutions = resolutions;
|
||||
onAccessApproved(chromeMediaSourceId, opts);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function captureTabUsingTabCapture(resolutions) {
|
||||
chrome.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
}, function(arrayOfTabs) {
|
||||
var activeTab = arrayOfTabs[0];
|
||||
var activeTabId = activeTab.id; // or do whatever you need
|
||||
|
||||
var constraints = {
|
||||
video: true,
|
||||
videoConstraints: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'tab',
|
||||
maxWidth: resolutions.maxWidth,
|
||||
maxHeight: resolutions.maxHeight,
|
||||
minWidth: resolutions.minWidth,
|
||||
minHeight: resolutions.minHeight,
|
||||
minAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight),
|
||||
maxAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight),
|
||||
minFrameRate: 64,
|
||||
maxFrameRate: 128
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!!enableSpeakers) {
|
||||
constraints.audio = true;
|
||||
constraints.audioConstraints = {
|
||||
mandatory: {
|
||||
echoCancellation: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// chrome.tabCapture.onStatusChanged.addListener(function(event) { /* event.status */ });
|
||||
|
||||
chrome.tabCapture.capture(constraints, function(stream) {
|
||||
gotTabCaptureStream(stream, constraints);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function gotTabCaptureStream(stream, constraints) {
|
||||
if (!stream) {
|
||||
if (constraints.audio === true) {
|
||||
enableSpeakers = false;
|
||||
captureTabUsingTabCapture(resolutions);
|
||||
return;
|
||||
}
|
||||
return alert('still no tabCapture stream');
|
||||
chrome.runtime.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
var newStream = new MediaStream();
|
||||
|
||||
stream.getTracks().forEach(function(track) {
|
||||
newStream.addTrack(track);
|
||||
});
|
||||
|
||||
initVideoPlayer(newStream);
|
||||
|
||||
gotCustomStream(newStream);
|
||||
// CHANGES
|
||||
//gotStream(newStream);
|
||||
}
|
||||
|
||||
var desktop_id;
|
||||
var constraints;
|
||||
var room_password = '';
|
||||
var room_id = '';
|
||||
var codecs = 'default';
|
||||
var bandwidth;
|
||||
|
||||
var enableTabCaptureAPI;
|
||||
var enableMicrophone;
|
||||
var enableSpeakers;
|
||||
var enableCamera;
|
||||
var enableScreen;
|
||||
var isSharingOn;
|
||||
|
||||
// Array of blobs
|
||||
var recordedBlobs;
|
||||
// Socket for sending data
|
||||
var socket;
|
||||
|
||||
|
||||
function getAspectRatio(w, h) {
|
||||
function gcd(a, b) {
|
||||
return (b == 0) ? a : gcd(b, a % b);
|
||||
}
|
||||
var r = gcd(w, h);
|
||||
return (w / r) / (h / r);
|
||||
}
|
||||
|
||||
function onAccessApproved(chromeMediaSourceId, opts) {
|
||||
if (!chromeMediaSourceId) {
|
||||
setDefaults();
|
||||
return;
|
||||
}
|
||||
|
||||
var resolutions = opts.resolutions;
|
||||
|
||||
// CHANGES
|
||||
chrome.storage.sync.get(null, function(items) {
|
||||
constraints = {
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: chromeMediaSourceId,
|
||||
maxWidth: 1280, //resolutions.maxWidth,
|
||||
maxHeight: 720,//resolutions.maxHeight,
|
||||
minWidth: resolutions.minWidth,
|
||||
minHeight: resolutions.minHeight,
|
||||
minAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight),
|
||||
maxAspectRatio: getAspectRatio(resolutions.maxWidth, resolutions.maxHeight),
|
||||
minFrameRate: 25, //64,
|
||||
maxFrameRate: 60 //128
|
||||
},
|
||||
optional: []
|
||||
}
|
||||
};
|
||||
|
||||
if (opts.canRequestAudioTrack === true) {
|
||||
constraints.audio = {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: chromeMediaSourceId,
|
||||
echoCancellation: true
|
||||
},
|
||||
optional: []
|
||||
};
|
||||
}
|
||||
|
||||
navigator.webkitGetUserMedia(constraints, function(screenStream) {
|
||||
var win;
|
||||
addStreamStopListener(screenStream, function() {
|
||||
if (win && !win.closed) {
|
||||
win.close();
|
||||
} else {
|
||||
captureDesktop();
|
||||
}
|
||||
});
|
||||
|
||||
if (opts.stream) {
|
||||
if (enableCamera && opts.stream.getVideoTracks().length) {
|
||||
var cameraStream = opts.stream;
|
||||
|
||||
screenStream.fullcanvas = true;
|
||||
screenStream.width = screen.width; // or 3840
|
||||
screenStream.height = screen.height; // or 2160
|
||||
|
||||
cameraStream.width = parseInt((15 / 100) * screenStream.width);
|
||||
cameraStream.height = parseInt((15 / 100) * screenStream.height);
|
||||
cameraStream.top = screenStream.height - cameraStream.height - 20;
|
||||
cameraStream.left = screenStream.width - cameraStream.width - 20;
|
||||
|
||||
var mixer = new MultiStreamsMixer([screenStream, cameraStream]);
|
||||
|
||||
mixer.frameInterval = 1;
|
||||
mixer.startDrawingFrames();
|
||||
|
||||
screenStream = mixer.getMixedStream();
|
||||
// win = openVideoPreview(screenStream);
|
||||
} else if (enableMicrophone && opts.stream.getAudioTracks().length) {
|
||||
var speakers = new MediaStream();
|
||||
screenStream.getAudioTracks().forEach(function(track) {
|
||||
speakers.addTrack(track);
|
||||
screenStream.removeTrack(track);
|
||||
});
|
||||
|
||||
var mixer = new MultiStreamsMixer([speakers, opts.stream]);
|
||||
mixer.getMixedStream().getAudioTracks().forEach(function(track) {
|
||||
screenStream.addTrack(track);
|
||||
});
|
||||
|
||||
screenStream.getVideoTracks().forEach(function(track) {
|
||||
track.onended = function() {
|
||||
if (win && !win.closed) {
|
||||
win.close();
|
||||
} else {
|
||||
captureDesktop();
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
gotCustomStream(screenStream);
|
||||
// CHANGES
|
||||
//gotStream(screenStream);
|
||||
}, getUserMediaError);
|
||||
});
|
||||
}
|
||||
|
||||
function openVideoPreview(stream) {
|
||||
var win = window.open("video.html?src=" + URL.createObjectURL(stream), "_blank", "top=0,left=0");
|
||||
var timer = setInterval(function() {
|
||||
if (win.closed) {
|
||||
clearInterval(timer);
|
||||
captureDesktop();
|
||||
}
|
||||
}, 1000);
|
||||
return win;
|
||||
}
|
||||
|
||||
function addStreamStopListener(stream, callback) {
|
||||
var streamEndedEvent = 'ended';
|
||||
if ('oninactive' in stream) {
|
||||
streamEndedEvent = 'inactive';
|
||||
}
|
||||
stream.addEventListener(streamEndedEvent, function() {
|
||||
callback();
|
||||
callback = function() {};
|
||||
}, false);
|
||||
stream.getAudioTracks().forEach(function(track) {
|
||||
track.addEventListener(streamEndedEvent, function() {
|
||||
callback();
|
||||
callback = function() {};
|
||||
}, false);
|
||||
});
|
||||
stream.getVideoTracks().forEach(function(track) {
|
||||
track.addEventListener(streamEndedEvent, function() {
|
||||
callback();
|
||||
callback = function() {};
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function gotCustomStream(stream) {
|
||||
if (!stream) {
|
||||
setDefaults();
|
||||
|
||||
chrome.windows.create({
|
||||
url: "data:text/html,<h1>Internal error occurred while capturing the screen.</h1>",
|
||||
type: 'popup',
|
||||
width: screen.width / 2,
|
||||
height: 170
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Connecting to WebSockets server.'
|
||||
});
|
||||
|
||||
|
||||
|
||||
chrome.browserAction.disable();
|
||||
|
||||
addStreamStopListener(stream, function() {
|
||||
setDefaults();
|
||||
chrome.runtime.reload();
|
||||
});
|
||||
|
||||
chrome.windows.create({
|
||||
url: chrome.extension.getURL('_generated_background_page.html'),
|
||||
type: 'popup',
|
||||
focused: false,
|
||||
width: 1,
|
||||
height: 1,
|
||||
top: parseInt(screen.height),
|
||||
left: parseInt(screen.width)
|
||||
}, function(win) {
|
||||
var background_page_id = win.id;
|
||||
|
||||
setTimeout(function() {
|
||||
chrome.windows.remove(background_page_id);
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
chrome.browserAction.setIcon({
|
||||
path: 'images/pause22.png'
|
||||
});
|
||||
|
||||
|
||||
window.stream = stream;
|
||||
|
||||
startSharing();
|
||||
}
|
||||
|
||||
|
||||
function startSharing(){
|
||||
if(!window.stream){
|
||||
console.log('windows.steam not found');
|
||||
return;
|
||||
}
|
||||
|
||||
recordedBlobs = [];
|
||||
websocket = io('https://kurento.fishrungames.com/');
|
||||
|
||||
|
||||
websocket.on('connect', function(data){
|
||||
|
||||
console.log('Connected to socket.')
|
||||
|
||||
});
|
||||
|
||||
var options = {mimeType: 'video/webm;codecs=vp9'};
|
||||
|
||||
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
|
||||
console.log(options.mimeType + ' is not Supported');
|
||||
options = {mimeType: 'video/webm;codecs=vp8'};
|
||||
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
|
||||
console.log(options.mimeType + ' is not Supported');
|
||||
options = {mimeType: 'video/webm'};
|
||||
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
|
||||
console.log(options.mimeType + ' is not Supported');
|
||||
options = {mimeType: ''};
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
mediaRecorder = new MediaRecorder(window.stream, options);
|
||||
} catch (e) {
|
||||
console.error('Exception while creating MediaRecorder: ' + e);
|
||||
alert('Exception while creating MediaRecorder: '
|
||||
+ e + '. mimeType: ' + options.mimeType);
|
||||
return;
|
||||
}
|
||||
|
||||
//here
|
||||
|
||||
mediaRecorder.ondataavailable = handleDataAvailable;
|
||||
mediaRecorder.start(60); // collect data
|
||||
|
||||
}
|
||||
|
||||
function handleDataAvailable(event) {
|
||||
if (event.data && event.data.size > 0) {
|
||||
recordedBlobs.push(event.data);
|
||||
websocket.emit('blob', event.data);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function gotStream(stream) {
|
||||
if (!stream) {
|
||||
setDefaults();
|
||||
|
||||
chrome.windows.create({
|
||||
url: "data:text/html,<h1>Internal error occurred while capturing the screen.</h1>",
|
||||
type: 'popup',
|
||||
width: screen.width / 2,
|
||||
height: 170
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Connecting to WebSockets server.'
|
||||
});
|
||||
|
||||
chrome.browserAction.disable();
|
||||
|
||||
addStreamStopListener(stream, function() {
|
||||
setDefaults();
|
||||
chrome.runtime.reload();
|
||||
});
|
||||
|
||||
// as it is reported that if you drag chrome screen's status-bar
|
||||
// and scroll up/down the screen-viewer page.
|
||||
// chrome auto-stops the screen without firing any 'onended' event.
|
||||
// chrome also hides screen status bar.
|
||||
chrome.windows.create({
|
||||
url: chrome.extension.getURL('_generated_background_page.html'),
|
||||
type: 'popup',
|
||||
focused: false,
|
||||
width: 1,
|
||||
height: 1,
|
||||
top: parseInt(screen.height),
|
||||
left: parseInt(screen.width)
|
||||
}, function(win) {
|
||||
var background_page_id = win.id;
|
||||
|
||||
setTimeout(function() {
|
||||
chrome.windows.remove(background_page_id);
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
setupRTCMultiConnection(stream);
|
||||
|
||||
chrome.browserAction.setIcon({
|
||||
path: 'images/pause22.png'
|
||||
});
|
||||
}
|
||||
|
||||
function getUserMediaError(e) {
|
||||
setDefaults();
|
||||
|
||||
chrome.windows.create({
|
||||
url: "data:text/html,<h1>getUserMediaError: " + JSON.stringify(e, null, '<br>') + "</h1><br>Constraints used:<br><pre>" + JSON.stringify(constraints, null, '<br>') + '</pre>',
|
||||
type: 'popup',
|
||||
width: screen.width / 2,
|
||||
height: 170
|
||||
});
|
||||
}
|
||||
|
||||
// RTCMultiConnection - www.RTCMultiConnection.org
|
||||
var connection;
|
||||
var popup_id;
|
||||
|
||||
function setBadgeText(text) {
|
||||
chrome.browserAction.setBadgeBackgroundColor({
|
||||
color: [255, 0, 0, 255]
|
||||
});
|
||||
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: text + ''
|
||||
});
|
||||
|
||||
chrome.browserAction.setTitle({
|
||||
title: text + ' users are viewing your screen!'
|
||||
});
|
||||
}
|
||||
|
||||
function setupRTCMultiConnection(stream) {
|
||||
// www.RTCMultiConnection.org/docs/
|
||||
connection = new RTCMultiConnection();
|
||||
|
||||
connection.optionalArgument = {
|
||||
optional: [],
|
||||
mandatory: {}
|
||||
};
|
||||
|
||||
connection.channel = connection.sessionid = connection.userid;
|
||||
|
||||
if (room_id && room_id.length) {
|
||||
connection.channel = connection.sessionid = connection.userid = room_id;
|
||||
}
|
||||
|
||||
connection.autoReDialOnFailure = true;
|
||||
connection.getExternalIceServers = false;
|
||||
|
||||
connection.iceServers = IceServersHandler.getIceServers();
|
||||
|
||||
function setBandwidth(sdp, value) {
|
||||
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:' + value + '\r\n');
|
||||
return sdp;
|
||||
}
|
||||
|
||||
connection.processSdp = function(sdp) {
|
||||
if (bandwidth) {
|
||||
try {
|
||||
bandwidth = parseInt(bandwidth);
|
||||
} catch (e) {
|
||||
bandwidth = null;
|
||||
}
|
||||
|
||||
if (bandwidth && bandwidth != NaN && bandwidth != 'NaN' && typeof bandwidth == 'number') {
|
||||
sdp = setBandwidth(sdp, bandwidth);
|
||||
sdp = BandwidthHandler.setVideoBitrates(sdp, {
|
||||
min: bandwidth,
|
||||
max: bandwidth
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!!codecs && codecs !== 'default') {
|
||||
sdp = CodecsHandler.preferCodec(sdp, codecs);
|
||||
}
|
||||
return sdp;
|
||||
};
|
||||
|
||||
// www.RTCMultiConnection.org/docs/session/
|
||||
connection.session = {
|
||||
video: true,
|
||||
oneway: true
|
||||
};
|
||||
|
||||
// www.rtcmulticonnection.org/docs/sdpConstraints/
|
||||
connection.sdpConstraints.mandatory = {
|
||||
OfferToReceiveAudio: false,
|
||||
OfferToReceiveVideo: false
|
||||
};
|
||||
|
||||
connection.onstream = connection.onstreamended = function(event) {
|
||||
try {
|
||||
event.mediaElement.pause();
|
||||
delete event.mediaElement;
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
// www.RTCMultiConnection.org/docs/dontCaptureUserMedia/
|
||||
connection.dontCaptureUserMedia = true;
|
||||
|
||||
// www.RTCMultiConnection.org/docs/attachStreams/
|
||||
connection.attachStreams.push(stream);
|
||||
|
||||
if (room_password && room_password.length) {
|
||||
connection.onRequest = function(request) {
|
||||
if (request.extra.password !== room_password) {
|
||||
connection.reject(request);
|
||||
chrome.windows.create({
|
||||
url: "data:text/html,<h1>A user tried to join your room with invalid password. His request is rejected. He tried password: " + request.extra.password + " </h2>",
|
||||
type: 'popup',
|
||||
width: screen.width / 2,
|
||||
height: 170
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
connection.accept(request);
|
||||
};
|
||||
}
|
||||
|
||||
// www.RTCMultiConnection.org/docs/openSignalingChannel/
|
||||
var onMessageCallbacks = {};
|
||||
|
||||
var websocket = io('https://kurento.fishrungames.com/');
|
||||
|
||||
|
||||
|
||||
var text = '-';
|
||||
(function looper() {
|
||||
if (!connection) {
|
||||
setBadgeText('');
|
||||
return;
|
||||
}
|
||||
|
||||
if (connection.isInitiator) {
|
||||
setBadgeText('0');
|
||||
return;
|
||||
}
|
||||
|
||||
text += ' -';
|
||||
if (text.length > 6) {
|
||||
text = '-';
|
||||
}
|
||||
|
||||
setBadgeText(text);
|
||||
setTimeout(looper, 500);
|
||||
})();
|
||||
|
||||
var connectedUsers = 0;
|
||||
connection.ondisconnected = function() {
|
||||
connectedUsers--;
|
||||
setBadgeText(connectedUsers);
|
||||
};
|
||||
|
||||
websocket.onmessage = function(e) {
|
||||
data = JSON.parse(e.data);
|
||||
|
||||
if (data === 'received-your-screen') {
|
||||
connectedUsers++;
|
||||
setBadgeText(connectedUsers);
|
||||
}
|
||||
|
||||
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 && connection.attachStreams.length) {
|
||||
chrome.windows.create({
|
||||
url: "data:text/html,<h1>Failed connecting the WebSockets server. Please click screen icon to try again.</h1>",
|
||||
type: 'popup',
|
||||
width: screen.width / 2,
|
||||
height: 170
|
||||
});
|
||||
}
|
||||
|
||||
setDefaults();
|
||||
chrome.runtime.reload();
|
||||
};
|
||||
|
||||
|
||||
websocket.onopen = function() {
|
||||
chrome.browserAction.enable();
|
||||
|
||||
setBadgeText(0);
|
||||
|
||||
console.info('WebSockets connection is opened.');
|
||||
|
||||
// www.RTCMultiConnection.org/docs/open/
|
||||
var sessionDescription = connection.open({
|
||||
dontTransmit: true
|
||||
});
|
||||
|
||||
var resultingURL = 'https://webrtcweb.com/screen?s=' + connection.sessionid;
|
||||
|
||||
// resultingURL = 'http://localhost:9001/?s=' + connection.sessionid;
|
||||
|
||||
if (room_password && room_password.length) {
|
||||
resultingURL += '&p=' + room_password;
|
||||
}
|
||||
|
||||
var popup_width = 600;
|
||||
var popup_height = 170;
|
||||
|
||||
chrome.windows.create({
|
||||
url: "data:text/html,<title>Unique Room URL</title><h1 style='text-align:center'>Copy following private URL:</h1><input type='text' value='" + resultingURL + "' style='text-align:center;width:100%;font-size:1.2em;'><p style='text-align:center'>You can share this private-session URI with fellows using email or social networks.</p>",
|
||||
type: 'popup',
|
||||
width: popup_width,
|
||||
height: popup_height,
|
||||
top: parseInt((screen.height / 2) - (popup_height / 2)),
|
||||
left: parseInt((screen.width / 2) - (popup_width / 2)),
|
||||
focused: true
|
||||
}, function(win) {
|
||||
popup_id = win.id;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function setDefaults() {
|
||||
if (connection) {
|
||||
connection.close();
|
||||
connection.attachStreams = [];
|
||||
}
|
||||
|
||||
chrome.browserAction.setIcon({
|
||||
path: 'images/desktopCapture22.png'
|
||||
});
|
||||
|
||||
if (popup_id) {
|
||||
try {
|
||||
chrome.windows.remove(popup_id);
|
||||
} catch (e) {}
|
||||
|
||||
popup_id = null;
|
||||
}
|
||||
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Share Desktop'
|
||||
});
|
||||
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: ''
|
||||
});
|
||||
}
|
||||
|
||||
var videoPlayers = [];
|
||||
|
||||
function initVideoPlayer(stream) {
|
||||
var videoPlayer = document.createElement('video');
|
||||
videoPlayer.muted = !enableTabCaptureAPI;
|
||||
videoPlayer.volume = !!enableTabCaptureAPI;
|
||||
videoPlayer.autoplay = true;
|
||||
videoPlayer.srcObject = stream;
|
||||
videoPlayers.push(videoPlayer);
|
||||
}
|
||||
|
||||
var microphoneDevice = false;
|
||||
var cameraDevice = false;
|
||||
|
||||
function captureCamera(callback) {
|
||||
var supported = navigator.mediaDevices.getSupportedConstraints();
|
||||
var constraints = {};
|
||||
|
||||
if (enableCamera) {
|
||||
constraints.video = {
|
||||
width: {
|
||||
min: 640,
|
||||
ideal: 1920,
|
||||
max: 1920
|
||||
},
|
||||
height: {
|
||||
min: 400,
|
||||
ideal: 1080
|
||||
}
|
||||
};
|
||||
|
||||
if (supported.aspectRatio) {
|
||||
constraints.video.aspectRatio = 1.777777778;
|
||||
}
|
||||
|
||||
if (supported.frameRate) {
|
||||
constraints.video.frameRate = {
|
||||
ideal: 30
|
||||
};
|
||||
}
|
||||
|
||||
if (cameraDevice && cameraDevice.length) {
|
||||
constraints.video.deviceId = cameraDevice;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableMicrophone) {
|
||||
constraints.audio = {};
|
||||
|
||||
if (microphoneDevice && microphoneDevice.length) {
|
||||
constraints.audio.deviceId = microphoneDevice;
|
||||
}
|
||||
|
||||
if (supported.echoCancellation) {
|
||||
constraints.audio.echoCancellation = true;
|
||||
}
|
||||
}
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
|
||||
initVideoPlayer(stream);
|
||||
callback(stream);
|
||||
|
||||
if (enableCamera && !enableScreen) {
|
||||
openVideoPreview(stream);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
setDefaults();
|
||||
|
||||
chrome.tabs.create({
|
||||
url: 'camera-mic.html'
|
||||
});
|
||||
});
|
||||
}
|
134
dropdown.html
Executable file
134
dropdown.html
Executable file
@ -0,0 +1,134 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
-user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
-user-drag: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Custom_Font';
|
||||
src: local('.SFNSText-Light'),
|
||||
local('.HelveticaNeueDeskInterface-Light'),
|
||||
local('.LucidaGrandeUI'),
|
||||
local('Ubuntu Light'),
|
||||
local('Segoe UI Light'),
|
||||
local('Roboto-Light'),
|
||||
local('DroidSans'),
|
||||
local('Tahoma');
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 400px;
|
||||
background-color: #F3F3F3;
|
||||
font-family: Custom_Font!important;
|
||||
font-size: 1em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: solid lightgray 1px;
|
||||
}
|
||||
|
||||
div {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
div.btn {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.btn:hover {
|
||||
background: rgba(234,234,234,1);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a {
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
color: #0C97BB;
|
||||
}
|
||||
|
||||
div.btn img {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-right: 15px;
|
||||
text-align: right;
|
||||
width: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<article id="default-section">
|
||||
<div class="btn" id="full-screen">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Screen Without Audio
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="microphone-screen">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Screen + Microphone
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="full-screen-audio">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Screen + Speakers
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="full-screen-audio-microphone">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Screen + Microphone + Speakers
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="full-screen-audio-microphone-camera">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Screen + Microphone + Speakers + Camera
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="selected-tab">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Chrome Tab + Speakers
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="microphone-screen-camera">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Screen + Camera
|
||||
</div>
|
||||
<hr>
|
||||
<div class="btn" id="microphone-webcam">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Camera Only
|
||||
</div>
|
||||
<hr>
|
||||
<div style="text-align: right;">
|
||||
<a id="btn-options" href="options.html" target="_blank">Options</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="stop-section">
|
||||
<div class="btn" id="stop-sharing">
|
||||
<section><img src="images/desktopCapture22.png"></section>
|
||||
Stop Sharing
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<script src="dropdown.js"></script>
|
||||
</body>
|
||||
</html>
|
175
dropdown.js
Executable file
175
dropdown.js
Executable file
@ -0,0 +1,175 @@
|
||||
var runtimePort = chrome.runtime.connect({
|
||||
name: location.href.replace(/\/|:|#|\?|\$|\^|%|\.|`|~|!|\+|@|\[|\||]|\|*. /g, '').split('\n').join('').split('\r').join('')
|
||||
});
|
||||
|
||||
runtimePort.onMessage.addListener(function(message) {
|
||||
if (!message || !message.messageFromContentScript1234) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('stop-sharing').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
isSharingOn: 'false' // FALSE
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
stopSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('full-screen').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'false',
|
||||
enableCamera: 'false',
|
||||
enableScreen: 'true', // TRUE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'false' // FALSE
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('full-screen-audio').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'false',
|
||||
enableCamera: 'false',
|
||||
enableScreen: 'true', // TRUE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'true' // TRUE
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('full-screen-audio-microphone').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'true', // TRUE
|
||||
enableCamera: 'false',
|
||||
enableScreen: 'true', // TRUE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'true' // TRUE
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('full-screen-audio-microphone-camera').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'true', // TRUE
|
||||
enableCamera: 'true',
|
||||
enableScreen: 'true', // TRUE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'true' // TRUE
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('selected-tab').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'true', // TRUE
|
||||
enableMicrophone: 'false',
|
||||
enableCamera: 'false',
|
||||
enableScreen: 'false',
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'true'
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('microphone-screen').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'true', // TRUE
|
||||
enableCamera: 'false',
|
||||
enableScreen: 'true', // TRUE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'false'
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('microphone-screen-camera').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'true', // TRUE
|
||||
enableCamera: 'true', // TRUE
|
||||
enableScreen: 'true', // TRUE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'false'
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('microphone-webcam').onclick = function() {
|
||||
chrome.storage.sync.set({
|
||||
enableTabCaptureAPI: 'false',
|
||||
enableMicrophone: 'true', // TRUE
|
||||
enableCamera: 'true', // TRUE
|
||||
enableScreen: 'false', // FALSE
|
||||
isSharingOn: 'true', // TRUE
|
||||
enableSpeakers: 'false'
|
||||
}, function() {
|
||||
runtimePort.postMessage({
|
||||
messageFromContentScript1234: true,
|
||||
startSharing: true
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('btn-options').onclick = function(e) {
|
||||
e.preventDefault();
|
||||
location.href = this.href;
|
||||
};
|
||||
|
||||
var isSharingOn = false;
|
||||
chrome.storage.sync.get('isSharingOn', function(obj) {
|
||||
document.getElementById('default-section').style.display = obj.isSharingOn === 'true' ? 'none' : 'block';
|
||||
document.getElementById('stop-section').style.display = obj.isSharingOn === 'true' ? 'block' : 'none';
|
||||
|
||||
isSharingOn = obj.isSharingOn === 'true';
|
||||
|
||||
// auto-stop-sharing
|
||||
if (isSharingOn === true) {
|
||||
document.getElementById('stop-sharing').click();
|
||||
}
|
||||
});
|
567
getStats.js
Executable file
567
getStats.js
Executable file
@ -0,0 +1,567 @@
|
||||
'use strict';
|
||||
|
||||
// Last time updated: 2017-11-19 4:49:44 AM UTC
|
||||
|
||||
// _______________
|
||||
// getStats v1.0.6
|
||||
|
||||
// Open-Sourced: https://github.com/muaz-khan/getStats
|
||||
|
||||
// --------------------------------------------------
|
||||
// Muaz Khan - www.MuazKhan.com
|
||||
// MIT License - www.WebRTC-Experiment.com/licence
|
||||
// --------------------------------------------------
|
||||
|
||||
window.getStats = function(mediaStreamTrack, callback, interval) {
|
||||
|
||||
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
|
||||
|
||||
if (typeof MediaStreamTrack === 'undefined') {
|
||||
MediaStreamTrack = {}; // todo?
|
||||
}
|
||||
|
||||
var systemNetworkType = ((navigator.connection || {}).type || 'unknown').toString().toLowerCase();
|
||||
|
||||
var getStatsResult = {
|
||||
encryption: 'sha-256',
|
||||
audio: {
|
||||
send: {
|
||||
tracks: [],
|
||||
codecs: [],
|
||||
availableBandwidth: 0,
|
||||
streams: 0
|
||||
},
|
||||
recv: {
|
||||
tracks: [],
|
||||
codecs: [],
|
||||
availableBandwidth: 0,
|
||||
streams: 0
|
||||
},
|
||||
bytesSent: 0,
|
||||
bytesReceived: 0
|
||||
},
|
||||
video: {
|
||||
send: {
|
||||
tracks: [],
|
||||
codecs: [],
|
||||
availableBandwidth: 0,
|
||||
streams: 0
|
||||
},
|
||||
recv: {
|
||||
tracks: [],
|
||||
codecs: [],
|
||||
availableBandwidth: 0,
|
||||
streams: 0
|
||||
},
|
||||
bytesSent: 0,
|
||||
bytesReceived: 0
|
||||
},
|
||||
bandwidth: {
|
||||
systemBandwidth: 0,
|
||||
sentPerSecond: 0,
|
||||
encodedPerSecond: 0,
|
||||
helper: {
|
||||
audioBytesSent: 0,
|
||||
videoBytestSent: 0
|
||||
},
|
||||
speed: 0
|
||||
},
|
||||
results: {},
|
||||
connectionType: {
|
||||
systemNetworkType: systemNetworkType,
|
||||
systemIpAddress: '192.168.1.2',
|
||||
local: {
|
||||
candidateType: [],
|
||||
transport: [],
|
||||
ipAddress: [],
|
||||
networkType: []
|
||||
},
|
||||
remote: {
|
||||
candidateType: [],
|
||||
transport: [],
|
||||
ipAddress: [],
|
||||
networkType: []
|
||||
}
|
||||
},
|
||||
resolutions: {
|
||||
send: {
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
recv: {
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
},
|
||||
internal: {
|
||||
audio: {
|
||||
send: {},
|
||||
recv: {}
|
||||
},
|
||||
video: {
|
||||
send: {},
|
||||
recv: {}
|
||||
},
|
||||
candidates: {}
|
||||
},
|
||||
nomore: function() {
|
||||
nomore = true;
|
||||
}
|
||||
};
|
||||
|
||||
var getStatsParser = {
|
||||
checkIfOfferer: function(result) {
|
||||
if (result.type === 'googLibjingleSession') {
|
||||
getStatsResult.isOfferer = result.googInitiator;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var peer = this;
|
||||
|
||||
if (arguments[0] instanceof RTCPeerConnection) {
|
||||
peer = arguments[0];
|
||||
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
mediaStreamTrack = arguments[1];
|
||||
callback = arguments[2];
|
||||
interval = arguments[3];
|
||||
}
|
||||
|
||||
if (!(mediaStreamTrack instanceof MediaStreamTrack) && !!navigator.mozGetUserMedia) {
|
||||
throw '2nd argument is not instance of MediaStreamTrack.';
|
||||
}
|
||||
} else if (!(mediaStreamTrack instanceof MediaStreamTrack) && !!navigator.mozGetUserMedia) {
|
||||
throw '1st argument is not instance of MediaStreamTrack.';
|
||||
}
|
||||
|
||||
var nomore = false;
|
||||
|
||||
function getStatsLooper() {
|
||||
getStatsWrapper(function(results) {
|
||||
results.forEach(function(result) {
|
||||
Object.keys(getStatsParser).forEach(function(key) {
|
||||
if (typeof getStatsParser[key] === 'function') {
|
||||
getStatsParser[key](result);
|
||||
}
|
||||
});
|
||||
|
||||
if (result.type !== 'local-candidate' && result.type !== 'remote-candidate' && result.type !== 'candidate-pair') {
|
||||
// console.error('result', result);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// failed|closed
|
||||
if (peer.iceConnectionState.search(/failed/gi) !== -1) {
|
||||
nomore = true;
|
||||
}
|
||||
} catch (e) {
|
||||
nomore = true;
|
||||
}
|
||||
|
||||
if (nomore === true) {
|
||||
if (getStatsResult.datachannel) {
|
||||
getStatsResult.datachannel.state = 'close';
|
||||
}
|
||||
getStatsResult.ended = true;
|
||||
}
|
||||
|
||||
// allow users to access native results
|
||||
getStatsResult.results = results;
|
||||
|
||||
if (getStatsResult.audio && getStatsResult.video) {
|
||||
getStatsResult.bandwidth.speed = (getStatsResult.audio.bytesSent - getStatsResult.bandwidth.helper.audioBytesSent) + (getStatsResult.video.bytesSent - getStatsResult.bandwidth.helper.videoBytesSent);
|
||||
getStatsResult.bandwidth.helper.audioBytesSent = getStatsResult.audio.bytesSent;
|
||||
getStatsResult.bandwidth.helper.videoBytesSent = getStatsResult.video.bytesSent;
|
||||
}
|
||||
|
||||
callback(getStatsResult);
|
||||
|
||||
// second argument checks to see, if target-user is still connected.
|
||||
if (!nomore) {
|
||||
typeof interval != undefined && interval && setTimeout(getStatsLooper, interval || 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// a wrapper around getStats which hides the differences (where possible)
|
||||
// following code-snippet is taken from somewhere on the github
|
||||
function getStatsWrapper(cb) {
|
||||
// if !peer or peer.signalingState == 'closed' then return;
|
||||
|
||||
if (typeof window.InstallTrigger !== 'undefined') {
|
||||
peer.getStats(
|
||||
mediaStreamTrack,
|
||||
function(res) {
|
||||
var items = [];
|
||||
res.forEach(function(r) {
|
||||
items.push(r);
|
||||
});
|
||||
cb(items);
|
||||
},
|
||||
cb
|
||||
);
|
||||
} else {
|
||||
peer.getStats(function(res) {
|
||||
var items = [];
|
||||
res.result().forEach(function(res) {
|
||||
var item = {};
|
||||
res.names().forEach(function(name) {
|
||||
item[name] = res.stat(name);
|
||||
});
|
||||
item.id = res.id;
|
||||
item.type = res.type;
|
||||
item.timestamp = res.timestamp;
|
||||
items.push(item);
|
||||
});
|
||||
cb(items);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getStatsParser.datachannel = function(result) {
|
||||
if (result.type !== 'datachannel') return;
|
||||
|
||||
getStatsResult.datachannel = {
|
||||
state: result.state // open or connecting
|
||||
}
|
||||
};
|
||||
|
||||
getStatsParser.googCertificate = function(result) {
|
||||
if (result.type == 'googCertificate') {
|
||||
getStatsResult.encryption = result.googFingerprintAlgorithm;
|
||||
}
|
||||
};
|
||||
|
||||
var AUDIO_codecs = ['opus', 'isac', 'ilbc'];
|
||||
|
||||
getStatsParser.checkAudioTracks = function(result) {
|
||||
if (!result.googCodecName || result.mediaType !== 'audio') return;
|
||||
|
||||
if (AUDIO_codecs.indexOf(result.googCodecName.toLowerCase()) === -1) return;
|
||||
|
||||
var sendrecvType = result.id.split('_').pop();
|
||||
|
||||
if (getStatsResult.audio[sendrecvType].codecs.indexOf(result.googCodecName) === -1) {
|
||||
getStatsResult.audio[sendrecvType].codecs.push(result.googCodecName);
|
||||
}
|
||||
|
||||
if (result.bytesSent) {
|
||||
var kilobytes = 0;
|
||||
if (!!result.bytesSent) {
|
||||
if (!getStatsResult.internal.audio[sendrecvType].prevBytesSent) {
|
||||
getStatsResult.internal.audio[sendrecvType].prevBytesSent = result.bytesSent;
|
||||
}
|
||||
|
||||
var bytes = result.bytesSent - getStatsResult.internal.audio[sendrecvType].prevBytesSent;
|
||||
getStatsResult.internal.audio[sendrecvType].prevBytesSent = result.bytesSent;
|
||||
|
||||
kilobytes = bytes / 1024;
|
||||
}
|
||||
|
||||
getStatsResult.audio[sendrecvType].availableBandwidth = kilobytes.toFixed(1);
|
||||
}
|
||||
|
||||
if (result.bytesReceived) {
|
||||
var kilobytes = 0;
|
||||
if (!!result.bytesReceived) {
|
||||
if (!getStatsResult.internal.audio[sendrecvType].prevBytesReceived) {
|
||||
getStatsResult.internal.audio[sendrecvType].prevBytesReceived = result.bytesReceived;
|
||||
}
|
||||
|
||||
var bytes = result.bytesReceived - getStatsResult.internal.audio[sendrecvType].prevBytesReceived;
|
||||
getStatsResult.internal.audio[sendrecvType].prevBytesReceived = result.bytesReceived;
|
||||
|
||||
kilobytes = bytes / 1024;
|
||||
}
|
||||
|
||||
getStatsResult.audio[sendrecvType].availableBandwidth = kilobytes.toFixed(1);
|
||||
}
|
||||
|
||||
if (getStatsResult.audio[sendrecvType].tracks.indexOf(result.googTrackId) === -1) {
|
||||
getStatsResult.audio[sendrecvType].tracks.push(result.googTrackId);
|
||||
}
|
||||
};
|
||||
|
||||
var VIDEO_codecs = ['vp9', 'vp8', 'h264'];
|
||||
|
||||
getStatsParser.checkVideoTracks = function(result) {
|
||||
if (!result.googCodecName || result.mediaType !== 'video') return;
|
||||
|
||||
if (VIDEO_codecs.indexOf(result.googCodecName.toLowerCase()) === -1) return;
|
||||
|
||||
// googCurrentDelayMs, googRenderDelayMs, googTargetDelayMs
|
||||
// transportId === 'Channel-audio-1'
|
||||
var sendrecvType = result.id.split('_').pop();
|
||||
|
||||
if (getStatsResult.video[sendrecvType].codecs.indexOf(result.googCodecName) === -1) {
|
||||
getStatsResult.video[sendrecvType].codecs.push(result.googCodecName);
|
||||
}
|
||||
|
||||
if (!!result.bytesSent) {
|
||||
var kilobytes = 0;
|
||||
if (!getStatsResult.internal.video[sendrecvType].prevBytesSent) {
|
||||
getStatsResult.internal.video[sendrecvType].prevBytesSent = result.bytesSent;
|
||||
}
|
||||
|
||||
var bytes = result.bytesSent - getStatsResult.internal.video[sendrecvType].prevBytesSent;
|
||||
getStatsResult.internal.video[sendrecvType].prevBytesSent = result.bytesSent;
|
||||
|
||||
kilobytes = bytes / 1024;
|
||||
}
|
||||
|
||||
if (!!result.bytesReceived) {
|
||||
var kilobytes = 0;
|
||||
if (!getStatsResult.internal.video[sendrecvType].prevBytesReceived) {
|
||||
getStatsResult.internal.video[sendrecvType].prevBytesReceived = result.bytesReceived;
|
||||
}
|
||||
|
||||
var bytes = result.bytesReceived - getStatsResult.internal.video[sendrecvType].prevBytesReceived;
|
||||
getStatsResult.internal.video[sendrecvType].prevBytesReceived = result.bytesReceived;
|
||||
|
||||
kilobytes = bytes / 1024;
|
||||
}
|
||||
|
||||
getStatsResult.video[sendrecvType].availableBandwidth = kilobytes.toFixed(1);
|
||||
|
||||
if (result.googFrameHeightReceived && result.googFrameWidthReceived) {
|
||||
getStatsResult.resolutions[sendrecvType].width = result.googFrameWidthReceived;
|
||||
getStatsResult.resolutions[sendrecvType].height = result.googFrameHeightReceived;
|
||||
}
|
||||
|
||||
if (result.googFrameHeightSent && result.googFrameWidthSent) {
|
||||
getStatsResult.resolutions[sendrecvType].width = result.googFrameWidthSent;
|
||||
getStatsResult.resolutions[sendrecvType].height = result.googFrameHeightSent;
|
||||
}
|
||||
|
||||
if (getStatsResult.video[sendrecvType].tracks.indexOf(result.googTrackId) === -1) {
|
||||
getStatsResult.video[sendrecvType].tracks.push(result.googTrackId);
|
||||
}
|
||||
};
|
||||
|
||||
getStatsParser.bweforvideo = function(result) {
|
||||
if (result.type !== 'VideoBwe') return;
|
||||
|
||||
getStatsResult.bandwidth.availableSendBandwidth = result.googAvailableSendBandwidth;
|
||||
|
||||
getStatsResult.bandwidth.googActualEncBitrate = result.googActualEncBitrate;
|
||||
getStatsResult.bandwidth.googAvailableSendBandwidth = result.googAvailableSendBandwidth;
|
||||
getStatsResult.bandwidth.googAvailableReceiveBandwidth = result.googAvailableReceiveBandwidth;
|
||||
getStatsResult.bandwidth.googRetransmitBitrate = result.googRetransmitBitrate;
|
||||
getStatsResult.bandwidth.googTargetEncBitrate = result.googTargetEncBitrate;
|
||||
getStatsResult.bandwidth.googBucketDelay = result.googBucketDelay;
|
||||
getStatsResult.bandwidth.googTransmitBitrate = result.googTransmitBitrate;
|
||||
};
|
||||
|
||||
getStatsParser.candidatePair = function(result) {
|
||||
if (result.type !== 'googCandidatePair' && result.type !== 'candidate-pair') return;
|
||||
|
||||
// result.googActiveConnection means either STUN or TURN is used.
|
||||
|
||||
if (result.googActiveConnection == 'true') {
|
||||
// id === 'Conn-audio-1-0'
|
||||
// localCandidateId, remoteCandidateId
|
||||
|
||||
// bytesSent, bytesReceived
|
||||
|
||||
Object.keys(getStatsResult.internal.candidates).forEach(function(cid) {
|
||||
var candidate = getStatsResult.internal.candidates[cid];
|
||||
if (candidate.ipAddress.indexOf(result.googLocalAddress) !== -1) {
|
||||
getStatsResult.connectionType.local.candidateType = candidate.candidateType;
|
||||
getStatsResult.connectionType.local.ipAddress = candidate.ipAddress;
|
||||
getStatsResult.connectionType.local.networkType = candidate.networkType;
|
||||
getStatsResult.connectionType.local.transport = candidate.transport;
|
||||
}
|
||||
if (candidate.ipAddress.indexOf(result.googRemoteAddress) !== -1) {
|
||||
getStatsResult.connectionType.remote.candidateType = candidate.candidateType;
|
||||
getStatsResult.connectionType.remote.ipAddress = candidate.ipAddress;
|
||||
getStatsResult.connectionType.remote.networkType = candidate.networkType;
|
||||
getStatsResult.connectionType.remote.transport = candidate.transport;
|
||||
}
|
||||
});
|
||||
|
||||
getStatsResult.connectionType.transport = result.googTransportType;
|
||||
|
||||
var localCandidate = getStatsResult.internal.candidates[result.localCandidateId];
|
||||
if (localCandidate) {
|
||||
if (localCandidate.ipAddress) {
|
||||
getStatsResult.connectionType.systemIpAddress = localCandidate.ipAddress;
|
||||
}
|
||||
}
|
||||
|
||||
var remoteCandidate = getStatsResult.internal.candidates[result.remoteCandidateId];
|
||||
if (remoteCandidate) {
|
||||
if (remoteCandidate.ipAddress) {
|
||||
getStatsResult.connectionType.systemIpAddress = remoteCandidate.ipAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.type === 'candidate-pair') {
|
||||
if (result.selected === true && result.nominated === true && result.state === 'succeeded') {
|
||||
// remoteCandidateId, localCandidateId, componentId
|
||||
var localCandidate = getStatsResult.internal.candidates[result.remoteCandidateId];
|
||||
var remoteCandidate = getStatsResult.internal.candidates[result.remoteCandidateId];
|
||||
|
||||
// Firefox used above two pairs for connection
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var LOCAL_candidateType = {};
|
||||
var LOCAL_transport = {};
|
||||
var LOCAL_ipAddress = {};
|
||||
var LOCAL_networkType = {};
|
||||
|
||||
getStatsParser.localcandidate = function(result) {
|
||||
if (result.type !== 'localcandidate' && result.type !== 'local-candidate') return;
|
||||
if (!result.id) return;
|
||||
|
||||
if (!LOCAL_candidateType[result.id]) {
|
||||
LOCAL_candidateType[result.id] = [];
|
||||
}
|
||||
|
||||
if (!LOCAL_transport[result.id]) {
|
||||
LOCAL_transport[result.id] = [];
|
||||
}
|
||||
|
||||
if (!LOCAL_ipAddress[result.id]) {
|
||||
LOCAL_ipAddress[result.id] = [];
|
||||
}
|
||||
|
||||
if (!LOCAL_networkType[result.id]) {
|
||||
LOCAL_networkType[result.id] = [];
|
||||
}
|
||||
|
||||
if (result.candidateType && LOCAL_candidateType[result.id].indexOf(result.candidateType) === -1) {
|
||||
LOCAL_candidateType[result.id].push(result.candidateType);
|
||||
}
|
||||
|
||||
if (result.transport && LOCAL_transport[result.id].indexOf(result.transport) === -1) {
|
||||
LOCAL_transport[result.id].push(result.transport);
|
||||
}
|
||||
|
||||
if (result.ipAddress && LOCAL_ipAddress[result.id].indexOf(result.ipAddress + ':' + result.portNumber) === -1) {
|
||||
LOCAL_ipAddress[result.id].push(result.ipAddress + ':' + result.portNumber);
|
||||
}
|
||||
|
||||
if (result.networkType && LOCAL_networkType[result.id].indexOf(result.networkType) === -1) {
|
||||
LOCAL_networkType[result.id].push(result.networkType);
|
||||
}
|
||||
|
||||
getStatsResult.internal.candidates[result.id] = {
|
||||
candidateType: LOCAL_candidateType[result.id],
|
||||
ipAddress: LOCAL_ipAddress[result.id],
|
||||
portNumber: result.portNumber,
|
||||
networkType: LOCAL_networkType[result.id],
|
||||
priority: result.priority,
|
||||
transport: LOCAL_transport[result.id],
|
||||
timestamp: result.timestamp,
|
||||
id: result.id,
|
||||
type: result.type
|
||||
};
|
||||
|
||||
getStatsResult.connectionType.local.candidateType = LOCAL_candidateType[result.id];
|
||||
getStatsResult.connectionType.local.ipAddress = LOCAL_ipAddress[result.id];
|
||||
getStatsResult.connectionType.local.networkType = LOCAL_networkType[result.id];
|
||||
getStatsResult.connectionType.local.transport = LOCAL_transport[result.id];
|
||||
};
|
||||
|
||||
var REMOTE_candidateType = {};
|
||||
var REMOTE_transport = {};
|
||||
var REMOTE_ipAddress = {};
|
||||
var REMOTE_networkType = {};
|
||||
|
||||
getStatsParser.remotecandidate = function(result) {
|
||||
if (result.type !== 'remotecandidate' && result.type !== 'remote-candidate') return;
|
||||
if (!result.id) return;
|
||||
|
||||
if (!REMOTE_candidateType[result.id]) {
|
||||
REMOTE_candidateType[result.id] = [];
|
||||
}
|
||||
|
||||
if (!REMOTE_transport[result.id]) {
|
||||
REMOTE_transport[result.id] = [];
|
||||
}
|
||||
|
||||
if (!REMOTE_ipAddress[result.id]) {
|
||||
REMOTE_ipAddress[result.id] = [];
|
||||
}
|
||||
|
||||
if (!REMOTE_networkType[result.id]) {
|
||||
REMOTE_networkType[result.id] = [];
|
||||
}
|
||||
|
||||
if (result.candidateType && REMOTE_candidateType[result.id].indexOf(result.candidateType) === -1) {
|
||||
REMOTE_candidateType[result.id].push(result.candidateType);
|
||||
}
|
||||
|
||||
if (result.transport && REMOTE_transport[result.id].indexOf(result.transport) === -1) {
|
||||
REMOTE_transport[result.id].push(result.transport);
|
||||
}
|
||||
|
||||
if (result.ipAddress && REMOTE_ipAddress[result.id].indexOf(result.ipAddress + ':' + result.portNumber) === -1) {
|
||||
REMOTE_ipAddress[result.id].push(result.ipAddress + ':' + result.portNumber);
|
||||
}
|
||||
|
||||
if (result.networkType && REMOTE_networkType[result.id].indexOf(result.networkType) === -1) {
|
||||
REMOTE_networkType[result.id].push(result.networkType);
|
||||
}
|
||||
|
||||
getStatsResult.internal.candidates[result.id] = {
|
||||
candidateType: REMOTE_candidateType[result.id],
|
||||
ipAddress: REMOTE_ipAddress[result.id],
|
||||
portNumber: result.portNumber,
|
||||
networkType: REMOTE_networkType[result.id],
|
||||
priority: result.priority,
|
||||
transport: REMOTE_transport[result.id],
|
||||
timestamp: result.timestamp,
|
||||
id: result.id,
|
||||
type: result.type
|
||||
};
|
||||
|
||||
getStatsResult.connectionType.remote.candidateType = REMOTE_candidateType[result.id];
|
||||
getStatsResult.connectionType.remote.ipAddress = REMOTE_ipAddress[result.id];
|
||||
getStatsResult.connectionType.remote.networkType = REMOTE_networkType[result.id];
|
||||
getStatsResult.connectionType.remote.transport = REMOTE_transport[result.id];
|
||||
};
|
||||
|
||||
getStatsParser.dataSentReceived = function(result) {
|
||||
if (!result.googCodecName || (result.mediaType !== 'video' && result.mediaType !== 'audio')) return;
|
||||
|
||||
if (!!result.bytesSent) {
|
||||
getStatsResult[result.mediaType].bytesSent = parseInt(result.bytesSent);
|
||||
}
|
||||
|
||||
if (!!result.bytesReceived) {
|
||||
getStatsResult[result.mediaType].bytesReceived = parseInt(result.bytesReceived);
|
||||
}
|
||||
};
|
||||
|
||||
var SSRC = {
|
||||
audio: {
|
||||
send: [],
|
||||
recv: []
|
||||
},
|
||||
video: {
|
||||
send: [],
|
||||
recv: []
|
||||
}
|
||||
};
|
||||
|
||||
getStatsParser.ssrc = function(result) {
|
||||
if (!result.googCodecName || (result.mediaType !== 'video' && result.mediaType !== 'audio')) return;
|
||||
if (result.type !== 'ssrc') return;
|
||||
var sendrecvType = result.id.split('_').pop();
|
||||
|
||||
if (SSRC[result.mediaType][sendrecvType].indexOf(result.ssrc) === -1) {
|
||||
SSRC[result.mediaType][sendrecvType].push(result.ssrc)
|
||||
}
|
||||
|
||||
getStatsResult[result.mediaType][sendrecvType].streams = SSRC[result.mediaType][sendrecvType].length;
|
||||
};
|
||||
|
||||
getStatsLooper();
|
||||
|
||||
};
|
BIN
images/desktopCapture128.png
Executable file
BIN
images/desktopCapture128.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
images/desktopCapture16.png
Executable file
BIN
images/desktopCapture16.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 659 B |
BIN
images/desktopCapture22.png
Executable file
BIN
images/desktopCapture22.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 584 B |
BIN
images/desktopCapture32.png
Executable file
BIN
images/desktopCapture32.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 642 B |
BIN
images/desktopCapture48.png
Executable file
BIN
images/desktopCapture48.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 827 B |
BIN
images/pause22.png
Executable file
BIN
images/pause22.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 269 B |
511
index.html
Executable file
511
index.html
Executable file
@ -0,0 +1,511 @@
|
||||
|
||||
<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>
|
47
manifest.json
Executable file
47
manifest.json
Executable file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name":"WebRTC Desktop Sharing",
|
||||
"author":"Muaz Khan",
|
||||
"version":"3.9",
|
||||
"manifest_version":2,
|
||||
"minimum_chrome_version":"34",
|
||||
"description":"WebRTC based P2P HQ/HD screen sharing. Share audio+tab or any application's screen. Even share full/entire screen.",
|
||||
"homepage_url":"https://github.com/muaz-khan/Chrome-Extensions/tree/master/desktopCapture-p2p",
|
||||
"background":{
|
||||
"scripts":[
|
||||
"socket.io.js",
|
||||
"RTCMultiConnection.js",
|
||||
"CodecsHandler.js",
|
||||
"IceServersHandler.js",
|
||||
"MultiStreamsMixer.js",
|
||||
"desktop-capturing.js"
|
||||
],
|
||||
"persistent":false
|
||||
},
|
||||
"browser_action":{
|
||||
"default_icon":"images/desktopCapture22.png",
|
||||
"default_title":"Share Your Screen",
|
||||
"default_popup": "dropdown.html"
|
||||
},
|
||||
"icons":{
|
||||
"16":"images/desktopCapture16.png",
|
||||
"22":"images/desktopCapture22.png",
|
||||
"32":"images/desktopCapture32.png",
|
||||
"48":"images/desktopCapture48.png",
|
||||
"128":"images/desktopCapture128.png"
|
||||
},
|
||||
"permissions":[
|
||||
"desktopCapture",
|
||||
"storage",
|
||||
"tabs",
|
||||
"<all_urls>",
|
||||
"activeTab",
|
||||
"tabCapture"
|
||||
],
|
||||
"web_accessible_resources":[
|
||||
"images/desktopCapture48.png"
|
||||
],
|
||||
"options_ui":{
|
||||
"page":"options.html",
|
||||
"chrome_style":true
|
||||
}
|
||||
}
|
73
options.html
Executable file
73
options.html
Executable file
@ -0,0 +1,73 @@
|
||||
<style>
|
||||
body {
|
||||
min-width: 450px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
select {
|
||||
font-size: 1.2em;
|
||||
border: 1px solid;
|
||||
padding: 0;
|
||||
outline: none!important;
|
||||
border: 1px solid black !important;
|
||||
}
|
||||
|
||||
select option {
|
||||
padding: 2px 5px;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
select option:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
label, input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
select[disabled] {
|
||||
background-color: rgba(232, 229, 229, 0.17);
|
||||
color: rgb(84, 82, 82);
|
||||
}
|
||||
</style>
|
||||
|
||||
<h2>Select Screen Resolutions:</h2>
|
||||
<select id="resolutions" size="5">
|
||||
<option value="fit-screen" selected>Fit Screen</option>
|
||||
<option value="4K">4K (2160p)</option>
|
||||
<option value="1080p">Full-HD (1080p)</option>
|
||||
<option value="720p">HD (720p)</option>
|
||||
<option value="360p">SD (360p)</option>
|
||||
</select>
|
||||
<hr>
|
||||
<h2>Select Codecs:</h2>
|
||||
<select id="codecs" size="4">
|
||||
<option value="default" selected>Default</option>
|
||||
<option value="vp8">VP8</option>
|
||||
<option value="vp9">VP9</option>
|
||||
<option value="h264">H264</option>
|
||||
</select>
|
||||
<hr>
|
||||
<label for="bandwidth">Set Bandwidth (e.g. 8192)</label>
|
||||
<input type="text" id="bandwidth" value="">
|
||||
<hr>
|
||||
<label for="room_password">Set Room Password</label>
|
||||
<input type="password" id="room_password" value="">
|
||||
<br>
|
||||
<small>Keep empty for No password.</small>
|
||||
<hr>
|
||||
<label for="room_id">Set ReUsable Room ID</label>
|
||||
<input type="text" id="room_id" value="">
|
||||
<br>
|
||||
<small>Set Your Own Room-ID. Keep empty for random room-id.</small>
|
||||
|
||||
<script src="options.js"></script>
|
83
options.js
Executable file
83
options.js
Executable file
@ -0,0 +1,83 @@
|
||||
chrome.storage.sync.get(null, function(items) {
|
||||
if (items['resolutions']) {
|
||||
document.getElementById('resolutions').value = items['resolutions'];
|
||||
} else {
|
||||
chrome.storage.sync.set({
|
||||
resolutions: 'fit-screen'
|
||||
}, function() {
|
||||
document.getElementById('resolutions').value = 'fit-screen'
|
||||
});
|
||||
}
|
||||
|
||||
if (items['codecs']) {
|
||||
document.getElementById('codecs').value = items['codecs'];
|
||||
} else {
|
||||
chrome.storage.sync.set({
|
||||
codecs: 'default'
|
||||
}, function() {
|
||||
document.getElementById('codecs').value = 'default'
|
||||
});
|
||||
}
|
||||
|
||||
if (items['room_password']) {
|
||||
document.getElementById('room_password').value = items['room_password'];
|
||||
}
|
||||
|
||||
if (items['bandwidth']) {
|
||||
document.getElementById('bandwidth').value = items['bandwidth'];
|
||||
}
|
||||
|
||||
if (items['room_id']) {
|
||||
document.getElementById('room_id').value = items['room_id'];
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('resolutions').onchange = function() {
|
||||
this.disabled = true;
|
||||
|
||||
chrome.storage.sync.set({
|
||||
resolutions: this.value
|
||||
}, function() {
|
||||
document.getElementById('resolutions').disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('codecs').onchange = function() {
|
||||
this.disabled = true;
|
||||
|
||||
chrome.storage.sync.set({
|
||||
codecs: this.value
|
||||
}, function() {
|
||||
document.getElementById('codecs').disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('bandwidth').onblur = function() {
|
||||
this.disabled = true;
|
||||
|
||||
chrome.storage.sync.set({
|
||||
bandwidth: this.value
|
||||
}, function() {
|
||||
document.getElementById('bandwidth').disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('room_password').onblur = function() {
|
||||
this.disabled = true;
|
||||
|
||||
chrome.storage.sync.set({
|
||||
room_password: this.value
|
||||
}, function() {
|
||||
document.getElementById('room_password').disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById('room_id').onblur = function() {
|
||||
this.disabled = true;
|
||||
|
||||
chrome.storage.sync.set({
|
||||
room_id: this.value
|
||||
}, function() {
|
||||
document.getElementById('room_id').disabled = false;
|
||||
});
|
||||
};
|
74
server.js
Executable file
74
server.js
Executable file
@ -0,0 +1,74 @@
|
||||
// http://127.0.0.1:9001
|
||||
// http://localhost:9001
|
||||
|
||||
var server = require('http'),
|
||||
url = require('url'),
|
||||
path = require('path'),
|
||||
fs = require('fs');
|
||||
|
||||
function serverHandler(request, response) {
|
||||
var uri = url.parse(request.url).pathname,
|
||||
filename = path.join(process.cwd(), uri);
|
||||
|
||||
fs.exists(filename, function(exists) {
|
||||
if (!exists) {
|
||||
response.writeHead(404, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
response.write('404 Not Found: ' + filename + '\n');
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (filename.indexOf('favicon.ico') !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isWin = !!process.platform.match(/^win/);
|
||||
|
||||
if (fs.statSync(filename).isDirectory() && !isWin) {
|
||||
filename += '/index.html';
|
||||
} else if (fs.statSync(filename).isDirectory() && !!isWin) {
|
||||
filename += '\\index.html';
|
||||
}
|
||||
|
||||
fs.readFile(filename, 'binary', function(err, file) {
|
||||
if (err) {
|
||||
response.writeHead(500, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
response.write(err + '\n');
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
var contentType;
|
||||
|
||||
if (filename.indexOf('.html') !== -1) {
|
||||
contentType = 'text/html';
|
||||
}
|
||||
|
||||
if (filename.indexOf('.js') !== -1) {
|
||||
contentType = 'application/javascript';
|
||||
}
|
||||
|
||||
if (contentType) {
|
||||
response.writeHead(200, {
|
||||
'Content-Type': contentType
|
||||
});
|
||||
} else response.writeHead(200);
|
||||
|
||||
response.write(file, 'binary');
|
||||
response.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var app;
|
||||
|
||||
app = server.createServer(serverHandler);
|
||||
|
||||
app = app.listen(process.env.PORT || 9001, process.env.IP || "0.0.0.0", function() {
|
||||
var addr = app.address();
|
||||
console.log("Server listening at", addr.address + ":" + addr.port);
|
||||
});
|
8
socket.io.js
Executable file
8
socket.io.js
Executable file
File diff suppressed because one or more lines are too long
24
video.html
Executable file
24
video.html
Executable file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr" lang="en" style="overflow: hidden;padding: 0;margin: 0;">
|
||||
<head>
|
||||
<title>RecordRTC</title>
|
||||
<style type="text/css">
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
h1 {
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
video {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="padding: 0;margin: 0;overflow: hidden;">
|
||||
<h1>Close this window to stop the broadcast!</h1>
|
||||
<video autoplay playsinline muted></video>
|
||||
<script src="video.js"></script>
|
||||
</body>
|
||||
</html>
|
2
video.js
Executable file
2
video.js
Executable file
@ -0,0 +1,2 @@
|
||||
var src = location.href.split('?src=')[1];
|
||||
document.querySelector('video').src = src;
|
96
websocket.js
Executable file
96
websocket.js
Executable file
@ -0,0 +1,96 @@
|
||||
// Version: 3.6.7
|
||||
(function(){
|
||||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
|
||||
r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
|
||||
32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push(4294967296*h.random()|0);return new r.init(c,a)}}),l=f.enc={},k=l.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b,
|
||||
2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}},
|
||||
u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);c.sigBytes-=b}return new r.init(g,b)},clone:function(){var a=m.clone.call(this);
|
||||
a._data=this._data.clone();return a},_minBufferSize:0});g.Hasher=u.extend({cfg:m.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){u.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new t.HMAC.init(a,
|
||||
d)).finalize(c)}}});var t=f.algo={};return f}(Math);
|
||||
(function(h){for(var s=CryptoJS,f=s.lib,g=f.WordArray,q=f.Hasher,f=s.algo,m=[],r=[],l=function(a){return 4294967296*(a-(a|0))|0},k=2,n=0;64>n;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]=
|
||||
c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
|
||||
d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math);
|
||||
(function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j<h;j++)k[j]^=1549556828,n[j]^=909522486;r.sigBytes=l.sigBytes=m;this.reset()},reset:function(){var f=this._hasher;f.reset();f.update(this._iKey)},update:function(f){this._hasher.update(f);return this},finalize:function(f){var g=
|
||||
this._hasher;f=g.finalize(f);g.reset();return g.finalize(this._oKey.clone().concat(f))}})})();
|
||||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
(function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a<f;a+=3)for(var d=(e[a>>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g<f;g++)b.push(c.charAt(d>>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d<
|
||||
e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
|
||||
var v=void 0,y=!0,z=null,A=!1;function C(){return function(){}}
|
||||
window.JSON&&window.JSON.stringify||function(){function a(){try{return this.valueOf()}catch(a){return z}}function d(a){c.lastIndex=0;return c.test(a)?'"'+a.replace(c,function(a){var b=q[a];return"string"===typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function b(c,q){var t,r,g,j,h,l=f,e=q[c];e&&"object"===typeof e&&(e=a.call(e));"function"===typeof m&&(e=m.call(q,c,e));switch(typeof e){case "string":return d(e);case "number":return isFinite(e)?String(e):"null";case "boolean":case "null":return String(e);
|
||||
case "object":if(!e)return"null";f+=p;h=[];if("[object Array]"===Object.prototype.toString.apply(e)){j=e.length;for(t=0;t<j;t+=1)h[t]=b(t,e)||"null";g=0===h.length?"[]":f?"[\n"+f+h.join(",\n"+f)+"\n"+l+"]":"["+h.join(",")+"]";f=l;return g}if(m&&"object"===typeof m){j=m.length;for(t=0;t<j;t+=1)r=m[t],"string"===typeof r&&(g=b(r,e))&&h.push(d(r)+(f?": ":":")+g)}else for(r in e)Object.hasOwnProperty.call(e,r)&&(g=b(r,e))&&h.push(d(r)+(f?": ":":")+g);g=0===h.length?"{}":f?"{\n"+f+h.join(",\n"+f)+"\n"+
|
||||
l+"}":"{"+h.join(",")+"}";f=l;return g}}window.JSON||(window.JSON={});var c=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,f,p,q={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},m;"function"!==typeof JSON.stringify&&(JSON.stringify=function(a,c,d){var q;p=f="";if("number"===typeof d)for(q=0;q<d;q+=1)p+=" ";else"string"===typeof d&&(p=d);if((m=c)&&"function"!==typeof c&&("object"!==typeof c||"number"!==
|
||||
typeof c.length))throw Error("JSON.stringify");return b("",{"":a})});"function"!==typeof JSON.parse&&(JSON.parse=function(a){return eval("("+a+")")})}();var ca=1,fa=A,ga=[],ha="-pnpres",D=1E3,ia="/",ja="&",ka=/{([\w\-]+)}/g;function la(){return"x"+ ++ca+""+ +new Date}function G(){return+new Date}var ma,na=Math.floor(20*Math.random());ma=function(a,d){return 0<a.indexOf("pubsub.")&&a.replace("pubsub","ps"+(d?pa().split("-")[0]:20>++na?na:na=1))||a};
|
||||
function qa(a,d){var b=a.join(ia),c=[];if(!d)return b;N(d,function(a,b){var d="object"==typeof b?JSON.stringify(b):b;"undefined"!=typeof b&&(b!=z&&0<encodeURIComponent(d).length)&&c.push(a+"="+encodeURIComponent(d))});return b+="?"+c.join(ja)}function ra(a,d){function b(){f+d>G()?(clearTimeout(c),c=setTimeout(b,d)):(f=G(),a())}var c,f=0;return b}function sa(a,d){var b=[];N(a||[],function(a){d(a)&&b.push(a)});return b}function ta(a,d){return a.replace(ka,function(a,c){return d[c]||a})}
|
||||
function pa(a){var d="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var c=16*Math.random()|0;return("x"==a?c:c&3|8).toString(16)});a&&a(d);return d}function N(a,d){if(a&&d)if(a&&(Array.isArray&&Array.isArray(a)||"number"===typeof a.length))for(var b=0,c=a.length;b<c;)d.call(a[b],a[b],b++);else for(b in a)a.hasOwnProperty&&a.hasOwnProperty(b)&&d.call(a[b],b,a[b])}function ua(a,d){var b=[];N(a||[],function(a,f){b.push(d(a,f))});return b}
|
||||
function va(a,d){var b=[];N(a,function(a,f){d?0>a.search("-pnpres")&&f.f&&b.push(a):f.f&&b.push(a)});return b.sort()}function wa(){setTimeout(function(){fa||(fa=1,N(ga,function(a){a()}))},D)}var O,T=14,U=8,xa=A;function ya(a,d){var b="",c,f;if(d){c=a[15];if(16<c)throw"Decryption error: Maybe bad key";if(16==c)return"";for(f=0;f<16-c;f++)b+=String.fromCharCode(a[f])}else for(f=0;16>f;f++)b+=String.fromCharCode(a[f]);return b}
|
||||
function za(a,d){var b=[],c;if(!d)try{a=unescape(encodeURIComponent(a))}catch(f){throw"Error on UTF-8 encode";}for(c=0;c<a.length;c++)b[c]=a.charCodeAt(c);return b}function Aa(a,d){var b=12<=T?3:2,c=[],f=[],c=[],f=[],p=a.concat(d),q;c[0]=GibberishAES.l.m(p);f=c[0];for(q=1;q<b;q++)c[q]=GibberishAES.l.m(c[q-1].concat(p)),f=f.concat(c[q]);c=f.slice(0,4*U);f=f.slice(4*U,4*U+16);return{key:c,i:f}}
|
||||
function Ba(a,d,b){var d=Ca(d),c=Math.ceil(a.length/16),f=[],p,q=[];for(p=0;p<c;p++){var m=f,u=p,x=a.slice(16*p,16*p+16),t=[],r=v,r=v;16>x.length&&(r=16-x.length,t=[r,r,r,r,r,r,r,r,r,r,r,r,r,r,r,r]);for(r=0;r<x.length;r++)t[r]=x[r];m[u]=t}0===a.length%16&&f.push([16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16]);for(p=0;p<f.length;p++)f[p]=0===p?Da(f[p],b):Da(f[p],q[p-1]),q[p]=Ea(f[p],d);return q}
|
||||
function Fa(a,d,b,c){var d=Ca(d),f=a.length/16,p=[],q,m=[],u="";for(q=0;q<f;q++)p.push(a.slice(16*q,16*(q+1)));for(q=p.length-1;0<=q;q--)m[q]=Ga(p[q],d),m[q]=0===q?Da(m[q],b):Da(m[q],p[q-1]);for(q=0;q<f-1;q++)u+=ya(m[q]);var u=u+ya(m[q],y),x;if(c)x=u;else try{x=decodeURIComponent(escape(u))}catch(t){throw"Bad Key";}return x}function Ea(a,d){xa=A;var b=La(a,d,0),c;for(c=1;c<T+1;c++)b=Ma(b),b=Na(b),c<T&&(b=Oa(b)),b=La(b,d,c);return b}
|
||||
function Ga(a,d){xa=y;var b=La(a,d,T),c;for(c=T-1;-1<c;c--)b=Na(b),b=Ma(b),b=La(b,d,c),0<c&&(b=Oa(b));return b}function Ma(a){var d=xa?Pa:Qa,b=[],c;for(c=0;16>c;c++)b[c]=d[a[c]];return b}function Na(a){var d=[],b=xa?[0,13,10,7,4,1,14,11,8,5,2,15,12,9,6,3]:[0,5,10,15,4,9,14,3,8,13,2,7,12,1,6,11],c;for(c=0;16>c;c++)d[c]=a[b[c]];return d}
|
||||
function Oa(a){var d=[],b;if(xa)for(b=0;4>b;b++)d[4*b]=Ta[a[4*b]]^Ua[a[1+4*b]]^Za[a[2+4*b]]^$a[a[3+4*b]],d[1+4*b]=$a[a[4*b]]^Ta[a[1+4*b]]^Ua[a[2+4*b]]^Za[a[3+4*b]],d[2+4*b]=Za[a[4*b]]^$a[a[1+4*b]]^Ta[a[2+4*b]]^Ua[a[3+4*b]],d[3+4*b]=Ua[a[4*b]]^Za[a[1+4*b]]^$a[a[2+4*b]]^Ta[a[3+4*b]];else for(b=0;4>b;b++)d[4*b]=ab[a[4*b]]^bb[a[1+4*b]]^a[2+4*b]^a[3+4*b],d[1+4*b]=a[4*b]^ab[a[1+4*b]]^bb[a[2+4*b]]^a[3+4*b],d[2+4*b]=a[4*b]^a[1+4*b]^ab[a[2+4*b]]^bb[a[3+4*b]],d[3+4*b]=bb[a[4*b]]^a[1+4*b]^a[2+4*b]^ab[a[3+4*
|
||||
b]];return d}function La(a,d,b){var c=[],f;for(f=0;16>f;f++)c[f]=a[f]^d[b][f];return c}function Da(a,d){var b=[],c;for(c=0;16>c;c++)b[c]=a[c]^d[c];return b}
|
||||
function Ca(a){var d=[],b=[],c,f,p=[];for(c=0;c<U;c++)f=[a[4*c],a[4*c+1],a[4*c+2],a[4*c+3]],d[c]=f;for(c=U;c<4*(T+1);c++){d[c]=[];for(a=0;4>a;a++)b[a]=d[c-1][a];if(0===c%U){a=b[0];f=v;for(f=0;4>f;f++)b[f]=b[f+1];b[3]=a;b=cb(b);b[0]^=db[c/U-1]}else 6<U&&4==c%U&&(b=cb(b));for(a=0;4>a;a++)d[c][a]=d[c-U][a]^b[a]}for(c=0;c<T+1;c++){p[c]=[];for(b=0;4>b;b++)p[c].push(d[4*c+b][0],d[4*c+b][1],d[4*c+b][2],d[4*c+b][3])}return p}function cb(a){for(var d=0;4>d;d++)a[d]=Qa[a[d]];return a}
|
||||
function eb(a,d){var b=[];for(i=0;i<a.length;i+=d)b[i/d]=parseInt(a.substr(i,d),16);return b}function fb(a){for(var d=[],b=0;256>b;b++){for(var c=a,f=b,p=v,q=v,p=q=0;8>p;p++)q=1==(f&1)?q^c:q,c=127<c?283^c<<1:c<<1,f>>>=1;d[b]=q}return d}
|
||||
var Qa=eb("637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cfd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdbe0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9ee1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16",2),
|
||||
Pa,gb=Qa,hb=[];for(i=0;i<gb.length;i++)hb[gb[i]]=i;Pa=hb;var db=eb("01020408102040801b366cd8ab4d9a2f5ebc63c697356ad4b37dfaefc591",2),ab=fb(2),bb=fb(3),$a=fb(9),Ua=fb(11),Za=fb(13),Ta=fb(14),ib,jb="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",kb=jb.split("");"function"===typeof Array.indexOf&&(jb=kb);
|
||||
ib={encode:function(a){var d=[],b="",c;for(c=0;c<16*a.length;c++)d.push(a[Math.floor(c/16)][c%16]);for(c=0;c<d.length;c+=3)b+=kb[d[c]>>2],b+=kb[(d[c]&3)<<4|d[c+1]>>4],b=d[c+1]!==v?b+kb[(d[c+1]&15)<<2|d[c+2]>>6]:b+"=",b=d[c+2]!==v?b+kb[d[c+2]&63]:b+"=";a=b.slice(0,64);for(c=1;c<Math.ceil(b.length/64);c++)a+=b.slice(64*c,64*c+64)+(Math.ceil(b.length/64)==c+1?"":"\n");return a},decode:function(a){var a=a.replace(/\n/g,""),d=[],b=[],c=[],f;for(f=0;f<a.length;f+=4)b[0]=jb.indexOf(a.charAt(f)),b[1]=jb.indexOf(a.charAt(f+
|
||||
1)),b[2]=jb.indexOf(a.charAt(f+2)),b[3]=jb.indexOf(a.charAt(f+3)),c[0]=b[0]<<2|b[1]>>4,c[1]=(b[1]&15)<<4|b[2]>>2,c[2]=(b[2]&3)<<6|b[3],d.push(c[0],c[1],c[2]);return d=d.slice(0,d.length-d.length%16)}};
|
||||
O={size:function(a){switch(a){case 128:T=10;U=4;break;case 192:T=12;U=6;break;case 256:T=14;U=8;break;default:throw"Invalid Key Size Specified:"+a;}},h2a:function(a){var d=[];a.replace(/(..)/g,function(a){d.push(parseInt(a,16))});return d},expandKey:Ca,encryptBlock:Ea,decryptBlock:Ga,Decrypt:xa,s2a:za,rawEncrypt:Ba,rawDecrypt:Fa,dec:function(a,d,b){var a=ib.q(a),c=a.slice(8,16),c=Aa(za(d,b),c),d=c.key,c=c.i,a=a.slice(16,a.length);return a=Fa(a,d,c,b)},openSSLKey:Aa,a2h:function(a){var d="",b;for(b=
|
||||
0;b<a.length;b++)d+=(16>a[b]?"0":"")+a[b].toString(16);return d},enc:function(a,d,b){var c;c=[];var f;for(f=0;8>f;f++)c=c.concat(Math.floor(256*Math.random()));f=Aa(za(d,b),c);d=f.key;f=f.i;c=[[83,97,108,116,101,100,95,95].concat(c)];a=za(a,b);a=Ba(a,d,f);a=c.concat(a);return ib.s(a)},Hash:{MD5:function(a){function d(a,b){var c,d,f,e,g;f=a&2147483648;e=b&2147483648;c=a&1073741824;d=b&1073741824;g=(a&1073741823)+(b&1073741823);return c&d?g^2147483648^f^e:c|d?g&1073741824?g^3221225472^f^e:g^1073741824^
|
||||
f^e:g^f^e}function b(a,b,c,f,e,g,l){a=d(a,d(d(b&c|~b&f,e),l));return d(a<<g|a>>>32-g,b)}function c(a,b,c,f,e,g,l){a=d(a,d(d(b&f|c&~f,e),l));return d(a<<g|a>>>32-g,b)}function f(a,b,c,f,e,g,l){a=d(a,d(d(b^c^f,e),l));return d(a<<g|a>>>32-g,b)}function p(a,b,c,f,g,e,l){a=d(a,d(d(c^(b|~f),g),l));return d(a<<e|a>>>32-e,b)}function q(a){var b,c,d=[];for(c=0;3>=c;c++)b=a>>>8*c&255,d=d.concat(b);return d}var m=[],u,x,t,r,g,j,h,l,e=eb("67452301efcdab8998badcfe10325476d76aa478e8c7b756242070dbc1bdceeef57c0faf4787c62aa8304613fd469501698098d88b44f7afffff5bb1895cd7be6b901122fd987193a679438e49b40821f61e2562c040b340265e5a51e9b6c7aad62f105d02441453d8a1e681e7d3fbc821e1cde6c33707d6f4d50d87455a14eda9e3e905fcefa3f8676f02d98d2a4c8afffa39428771f6816d9d6122fde5380ca4beea444bdecfa9f6bb4b60bebfbc70289b7ec6eaa127fad4ef308504881d05d9d4d039e6db99e51fa27cf8c4ac5665f4292244432aff97ab9423a7fc93a039655b59c38f0ccc92ffeff47d85845dd16fa87e4ffe2ce6e0a30143144e0811a1f7537e82bd3af2352ad7d2bbeb86d391",
|
||||
8),m=a.length;u=m+8;x=16*((u-u%64)/64+1);t=[];for(g=r=0;g<m;)u=(g-g%4)/4,r=8*(g%4),t[u]|=a[g]<<r,g++;u=(g-g%4)/4;t[u]|=128<<8*(g%4);t[x-2]=m<<3;t[x-1]=m>>>29;m=t;g=e[0];j=e[1];h=e[2];l=e[3];for(a=0;a<m.length;a+=16)u=g,x=j,t=h,r=l,g=b(g,j,h,l,m[a+0],7,e[4]),l=b(l,g,j,h,m[a+1],12,e[5]),h=b(h,l,g,j,m[a+2],17,e[6]),j=b(j,h,l,g,m[a+3],22,e[7]),g=b(g,j,h,l,m[a+4],7,e[8]),l=b(l,g,j,h,m[a+5],12,e[9]),h=b(h,l,g,j,m[a+6],17,e[10]),j=b(j,h,l,g,m[a+7],22,e[11]),g=b(g,j,h,l,m[a+8],7,e[12]),l=b(l,g,j,h,m[a+9],
|
||||
12,e[13]),h=b(h,l,g,j,m[a+10],17,e[14]),j=b(j,h,l,g,m[a+11],22,e[15]),g=b(g,j,h,l,m[a+12],7,e[16]),l=b(l,g,j,h,m[a+13],12,e[17]),h=b(h,l,g,j,m[a+14],17,e[18]),j=b(j,h,l,g,m[a+15],22,e[19]),g=c(g,j,h,l,m[a+1],5,e[20]),l=c(l,g,j,h,m[a+6],9,e[21]),h=c(h,l,g,j,m[a+11],14,e[22]),j=c(j,h,l,g,m[a+0],20,e[23]),g=c(g,j,h,l,m[a+5],5,e[24]),l=c(l,g,j,h,m[a+10],9,e[25]),h=c(h,l,g,j,m[a+15],14,e[26]),j=c(j,h,l,g,m[a+4],20,e[27]),g=c(g,j,h,l,m[a+9],5,e[28]),l=c(l,g,j,h,m[a+14],9,e[29]),h=c(h,l,g,j,m[a+3],14,e[30]),
|
||||
j=c(j,h,l,g,m[a+8],20,e[31]),g=c(g,j,h,l,m[a+13],5,e[32]),l=c(l,g,j,h,m[a+2],9,e[33]),h=c(h,l,g,j,m[a+7],14,e[34]),j=c(j,h,l,g,m[a+12],20,e[35]),g=f(g,j,h,l,m[a+5],4,e[36]),l=f(l,g,j,h,m[a+8],11,e[37]),h=f(h,l,g,j,m[a+11],16,e[38]),j=f(j,h,l,g,m[a+14],23,e[39]),g=f(g,j,h,l,m[a+1],4,e[40]),l=f(l,g,j,h,m[a+4],11,e[41]),h=f(h,l,g,j,m[a+7],16,e[42]),j=f(j,h,l,g,m[a+10],23,e[43]),g=f(g,j,h,l,m[a+13],4,e[44]),l=f(l,g,j,h,m[a+0],11,e[45]),h=f(h,l,g,j,m[a+3],16,e[46]),j=f(j,h,l,g,m[a+6],23,e[47]),g=f(g,j,
|
||||
h,l,m[a+9],4,e[48]),l=f(l,g,j,h,m[a+12],11,e[49]),h=f(h,l,g,j,m[a+15],16,e[50]),j=f(j,h,l,g,m[a+2],23,e[51]),g=p(g,j,h,l,m[a+0],6,e[52]),l=p(l,g,j,h,m[a+7],10,e[53]),h=p(h,l,g,j,m[a+14],15,e[54]),j=p(j,h,l,g,m[a+5],21,e[55]),g=p(g,j,h,l,m[a+12],6,e[56]),l=p(l,g,j,h,m[a+3],10,e[57]),h=p(h,l,g,j,m[a+10],15,e[58]),j=p(j,h,l,g,m[a+1],21,e[59]),g=p(g,j,h,l,m[a+8],6,e[60]),l=p(l,g,j,h,m[a+15],10,e[61]),h=p(h,l,g,j,m[a+6],15,e[62]),j=p(j,h,l,g,m[a+13],21,e[63]),g=p(g,j,h,l,m[a+4],6,e[64]),l=p(l,g,j,h,m[a+
|
||||
11],10,e[65]),h=p(h,l,g,j,m[a+2],15,e[66]),j=p(j,h,l,g,m[a+9],21,e[67]),g=d(g,u),j=d(j,x),h=d(h,t),l=d(l,r);return q(g).concat(q(j),q(h),q(l))}},Base64:ib};
|
||||
if(!window.PUBNUB){var lb=function(a,d){return CryptoJS.HmacSHA256(a,d).toString(CryptoJS.enc.Base64)},mb=function(a){return document.getElementById(a)},nb=function(a){console.error(a)},pb=function(a,d){var b=[];N(a.split(/\s+/),function(a){N((d||document).getElementsByTagName(a),function(a){b.push(a)})});return b},qb=function(a,d,b){N(a.split(","),function(a){function f(a){a||(a=window.event);b(a)||(a.cancelBubble=y,a.preventDefault&&a.preventDefault(),a.stopPropagation&&a.stopPropagation())}d.addEventListener?
|
||||
d.addEventListener(a,f,A):d.attachEvent?d.attachEvent("on"+a,f):d["on"+a]=f})},rb=function(){return pb("head")[0]},V=function(a,d,b){if(b)a.setAttribute(d,b);else return a&&a.getAttribute&&a.getAttribute(d)},sb=function(a,d){for(var b in d)if(d.hasOwnProperty(b))try{a.style[b]=d[b]+(0<"|width|height|top|left|".indexOf(b)&&"number"==typeof d[b]?"px":"")}catch(c){}},tb=function(a){return document.createElement(a)},zb=function(){return ub||$()?0:la()},Ab=function(a){function d(a,b){H||(H=1,e.onerror=
|
||||
z,clearTimeout(Ra),a||!b||Sa(b),setTimeout(function(){a&&S();var b=mb(aa),c=b&&b.parentNode;c&&c.removeChild(b)},D))}if(ub||$()){a:{var b,c,f=function(){if(!q){q=1;clearTimeout(u);try{c=JSON.parse(b.responseText)}catch(a){return j(1)}p=1;r(c)}},p=0,q=0,m=a.timeout||1E4,u=setTimeout(function(){j(1,{message:"timeout"})},m),x=a.b||C(),t=a.data||{},r=a.c||C(),g=!a.h,j=function(a,c){p||(p=1,clearTimeout(u),b&&(b.onerror=b.onload=z,b.abort&&b.abort(),b=z),a&&x(c))};try{b=$()||window.XDomainRequest&&new XDomainRequest||
|
||||
new XMLHttpRequest;b.onerror=b.onabort=function(){j(1,b.responseText||{error:"Network Connection Error"})};b.onload=b.onloadend=f;b.onreadystatechange=function(){if(b&&4==b.readyState)switch(b.status){case 401:case 402:case 403:try{c=JSON.parse(b.responseText),j(1,c)}catch(a){return j(1,b.responseText)}}};var h=qa(a.url,t);b.open("GET",h,g);g&&(b.timeout=m);b.send()}catch(l){j(0);ub=0;a=Ab(a);break a}a=j}return a}var e=tb("script"),f=a.a,aa=la(),H=0,Ra=setTimeout(function(){d(1,{message:"timeout"})},
|
||||
a.timeout||1E4),S=a.b||C(),m=a.data||{},Sa=a.c||C();window[f]=function(a){d(0,a)};a.h||(e[Bb]=Bb);e.onerror=function(){d(1)};e.src=qa(a.url,m);V(e,"id",aa);rb().appendChild(e);return d},Cb=function(){return!("onLine"in navigator)?1:navigator.onLine},$=function(){if(!Db||!Db.get)return 0;var a={id:$.id++,send:C(),abort:function(){a.id={}},open:function(d,b){$[a.id]=a;Db.get(a.id,b)}};return a},Bb="async",ub=-1==navigator.userAgent.indexOf("MSIE 6");window.console||(window.console=window.console||{});
|
||||
console.log||(console.log=console.error=(window.opera||{}).postError||C());var Eb,Fb=window.localStorage;Eb={get:function(a){try{return Fb?Fb.getItem(a):-1==document.cookie.indexOf(a)?z:((document.cookie||"").match(RegExp(a+"=([^;]+)"))||[])[1]||z}catch(d){}},set:function(a,d){try{if(Fb)return Fb.setItem(a,d)&&0;document.cookie=a+"="+d+"; expires=Thu, 1 Aug 2030 20:00:00 UTC; path=/"}catch(b){}}};var Gb={list:{},unbind:function(a){Gb.list[a]=[]},bind:function(a,d){(Gb.list[a]=Gb.list[a]||[]).push(d)},
|
||||
fire:function(a,d){N(Gb.list[a]||[],function(a){a(d)})}},Ib=mb("pubnub")||0,Jb=function(a){function d(){}function b(a,b){function c(b){b&&(Va=G()-(b/1E4+(G()-d)/2),a&&a(Va))}var d=G();b&&c(b)||I.time(c)}function c(a,b){Ha&&Ha(a,b);Ha=z}function f(){I.time(function(a){b(C(),a);a||c(1,{error:"Heartbeat failed to connect to Pubnub Servers.Please check your network settings."});setTimeout(f,ob)})}function p(){Nb()||c(1,{error:"Offline. Please check your network settings. "});setTimeout(p,D)}function q(a,
|
||||
b){"object"==typeof a&&a.error&&a.message&&a.payload?b({message:a.message,payload:a.payload}):b(a)}function m(a,b,c){if("object"==typeof a){if(a.error&&a.message&&a.payload){c({message:a.message,payload:a.payload});return}if(a.payload){b(a.payload);return}}b(a)}function u(a){var b=0;N(va(B),function(c){if(c=B[c])b++,(a||C())(c)});return b}function x(a){if(Ob){if(!Q.length)return}else{a&&(Q.j=0);if(Q.j||!Q.length)return;Q.j=1}E(Q.shift())}function t(){!Wa&&r()}function r(){clearTimeout(W);!J||500<=
|
||||
J||1>J||!va(B,y).length?Wa=A:(Wa=y,I.presence_heartbeat({callback:function(){W=setTimeout(r,J*D)},error:function(a){s&&s("Presence Heartbeat unable to reach Pubnub servers."+JSON.stringify(a));W=setTimeout(r,J*D)}}))}function g(a,b){return ba.decrypt(a,b||X)||ba.decrypt(a,X)||a}function j(a,b,c){var d=A;if("number"===typeof a)d=5<a||0==a?A:y;else{if("boolean"===typeof a)return a?30:0;d=y}return d?(c&&c("Presence Heartbeat value invalid. Valid range ( x > 5 or x = 0). Current Value : "+(b||5)),b||
|
||||
5):a}function h(a){var b="",c=[];N(a,function(a){c.push(a)});var d=c.sort(),f;for(f in d){var e=d[f],b=b+(e+"="+encodeURIComponent(a[e]));f!=d.length-1&&(b+="&")}return b}function l(a){a||(a={});N(Y,function(b,c){b in a||(a[b]=c)});return a}function e(a){return Jb(a)}function aa(a){function b(a,c){var d=(a&65535)+(c&65535);return(a>>16)+(c>>16)+(d>>16)<<16|d&65535}function c(a,b){return a>>>b|a<<32-b}var d;d=a.replace(/\r\n/g,"\n");for(var a="",f=0;f<d.length;f++){var e=d.charCodeAt(f);128>e?a+=String.fromCharCode(e):
|
||||
(127<e&&2048>e?a+=String.fromCharCode(e>>6|192):(a+=String.fromCharCode(e>>12|224),a+=String.fromCharCode(e>>6&63|128)),a+=String.fromCharCode(e&63|128))}f=a;d=[];for(e=0;e<8*f.length;e+=8)d[e>>5]|=(f.charCodeAt(e/8)&255)<<24-e%32;var g=8*a.length,f=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,
|
||||
2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],a=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,
|
||||
1541459225],e=Array(64),l,m,j,h,q,p,r,t,s,w,u;d[g>>5]|=128<<24-g%32;d[(g+64>>9<<4)+15]=g;for(t=0;t<d.length;t+=16){g=a[0];l=a[1];m=a[2];j=a[3];h=a[4];q=a[5];p=a[6];r=a[7];for(s=0;64>s;s++)e[s]=16>s?d[s+t]:b(b(b(c(e[s-2],17)^c(e[s-2],19)^e[s-2]>>>10,e[s-7]),c(e[s-15],7)^c(e[s-15],18)^e[s-15]>>>3),e[s-16]),w=b(b(b(b(r,c(h,6)^c(h,11)^c(h,25)),h&q^~h&p),f[s]),e[s]),u=b(c(g,2)^c(g,13)^c(g,22),g&l^g&m^l&m),r=p,p=q,q=h,h=b(j,w),j=m,m=l,l=g,g=b(w,u);a[0]=b(g,a[0]);a[1]=b(l,a[1]);a[2]=b(m,a[2]);a[3]=b(j,a[3]);
|
||||
a[4]=b(h,a[4]);a[5]=b(q,a[5]);a[6]=b(p,a[6]);a[7]=b(r,a[7])}d="";for(f=0;f<4*a.length;f++)d+="0123456789abcdef".charAt(a[f>>2]>>8*(3-f%4)+4&15)+"0123456789abcdef".charAt(a[f>>2]>>8*(3-f%4)&15);return d}a.jsonp&&(ub=0);var H=a.subscribe_key||"";a.uuid||Eb.get(H+"uuid");var Ra=a.leave_on_unload||0;a.xdr=Ab;a.db=Eb;a.error=a.error||nb;a._is_online=Cb;a.jsonp_cb=zb;a.hmac_SHA256=lb;O.size(256);var S=O.s2a("0123456789012345");a.crypto_obj={encrypt:function(a,b){if(!b)return a;var c=O.s2a(aa(b).slice(0,
|
||||
32)),d=O.s2a(JSON.stringify(a)),c=O.rawEncrypt(d,c,S);return O.Base64.encode(c)||a},decrypt:function(a,b){if(!b)return a;var c=O.s2a(aa(b).slice(0,32));try{var d=O.Base64.decode(a),e=O.rawDecrypt(d,c,S,A);return JSON.parse(e)}catch(f){}}};a.params={pnsdk:"PubNub-JS-Web/3.6.7"};var Sa=+a.windowing||10,Hb=(+a.timeout||310)*D,ob=(+a.keepalive||60)*D,Lb=a.noleave||0,P=a.publish_key||"demo",w=a.subscribe_key||"demo",M=a.auth_key||"",Ia=a.secret_key||"",vb=a.hmac_SHA256,Xa=a.ssl?"s":"",oa="http"+Xa+"://"+
|
||||
(a.origin||"pubsub.pubnub.com"),K=ma(oa),wb=ma(oa),Q=[],Va=0,xb=0,yb=0,Ha=0,Ja=a.restore||0,da=0,Ya=A,B={},Z={},W=z,R=j(a.heartbeat||a.pnexpires||0,a.error),J=a.heartbeat_interval||R-3,Wa=A,Ob=a.no_wait_for_pending,Pb=a["compatible_3.5"]||A,E=a.xdr,Y=a.params||{},s=a.error||C(),Nb=a._is_online||function(){return 1},L=a.jsonp_cb||function(){return 0},ea=a.db||{get:C(),set:C()},X=a.cipher_key,F=a.uuid||ea&&ea.get(w+"uuid")||"",ba=a.crypto_obj||{encrypt:function(a){return a},decrypt:function(a){return a}},
|
||||
I={LEAVE:function(a,b,c,d){var e={uuid:F,auth:M},f=ma(oa),c=c||C(),g=d||C(),d=L();if(0<a.indexOf(ha))return y;if(Pb&&(!Xa||"0"==d)||Lb)return A;"0"!=d&&(e.callback=d);E({h:b||Xa,timeout:2E3,a:d,data:l(e),c:function(a){m(a,c,g)},b:function(a){q(a,g)},url:[f,"v2","presence","sub_key",w,"channel",encodeURIComponent(a),"leave"]});return y},set_resumed:function(a){Ya=a},get_cipher_key:function(){return X},set_cipher_key:function(a){X=a},raw_encrypt:function(a,b){return ba.encrypt(a,b||X)||a},raw_decrypt:function(a,
|
||||
b){return g(a,b)},get_heartbeat:function(){return R},set_heartbeat:function(a){R=j(a,J,s);J=1<=R-3?R-3:1;d();r()},get_heartbeat_interval:function(){return J},set_heartbeat_interval:function(a){J=a;r()},get_version:function(){return"3.6.7"},getGcmMessageObject:function(a){return{data:a}},getApnsMessageObject:function(a){var b={aps:{badge:1,alert:""}};for(k in a)k[b]=a[k];return b},newPnMessage:function(){var a={};gcm&&(a.pn_gcm=gcm);apns&&(a.pn_apns=apns);for(k in n)a[k]=n[k];return a},_add_param:function(a,
|
||||
b){Y[a]=b},history:function(a,b){var b=a.callback||b,c=a.count||a.limit||100,d=a.reverse||"false",e=a.error||C(),f=a.auth_key||M,h=a.cipher_key,m=a.channel,j=a.start,p=a.end,r=a.include_token,t={},u=L();if(!m)return s("Missing Channel");if(!b)return s("Missing Callback");if(!w)return s("Missing Subscribe Key");t.stringtoken="true";t.count=c;t.reverse=d;t.auth=f;u&&(t.callback=u);j&&(t.start=j);p&&(t.end=p);r&&(t.include_token="true");E({a:u,data:l(t),c:function(a){if("object"==typeof a&&a.error)e({message:a.message,
|
||||
payload:a.payload});else{for(var c=a[0],d=[],f=0;f<c.length;f++){var l=g(c[f],h);try{d.push(JSON.parse(l))}catch(m){d.push(l)}}b([d,a[1],a[2]])}},b:function(a){q(a,e)},url:[K,"v2","history","sub-key",w,"channel",encodeURIComponent(m)]})},replay:function(a,b){var b=b||a.callback||C(),c=a.auth_key||M,d=a.source,e=a.destination,f=a.stop,g=a.start,h=a.end,j=a.reverse,q=a.limit,p=L(),r={};if(!d)return s("Missing Source Channel");if(!e)return s("Missing Destination Channel");if(!P)return s("Missing Publish Key");
|
||||
if(!w)return s("Missing Subscribe Key");"0"!=p&&(r.callback=p);f&&(r.stop="all");j&&(r.reverse="true");g&&(r.start=g);h&&(r.end=h);q&&(r.count=q);r.auth=c;E({a:p,c:function(a){m(a,b,err)},b:function(){b([0,"Disconnected"])},url:[K,"v1","replay",P,w,d,e],data:l(r)})},auth:function(a){M=a;d()},time:function(a){var b=L();E({a:b,data:l({uuid:F,auth:M}),timeout:5*D,url:[K,"time",b],c:function(b){a(b[0])},b:function(){a(0)}})},publish:function(a,b){var c=a.message;if(!c)return s("Missing Message");var b=
|
||||
b||a.callback||c.callback||C(),d=a.channel||c.channel,e=a.auth_key||M,f=a.cipher_key,g=a.error||c.error||C(),h=a.post||A,j="store_in_history"in a?a.store_in_history:y,p=L(),r="push";a.prepend&&(r="unshift");if(!d)return s("Missing Channel");if(!P)return s("Missing Publish Key");if(!w)return s("Missing Subscribe Key");c.getPubnubMessage&&(c=c.getPubnubMessage());c=JSON.stringify(ba.encrypt(c,f||X)||c);c=[K,"publish",P,w,0,encodeURIComponent(d),p,encodeURIComponent(c)];Y={uuid:F,auth:e};j||(Y.store=
|
||||
"0");Q[r]({a:p,timeout:5*D,url:c,data:l(Y),b:function(a){q(a,g);x(1)},c:function(a){m(a,b,g);x(1)},mode:h?"POST":"GET"});x()},unsubscribe:function(a,b){var c=a.channel,b=b||a.callback||C(),e=a.error||C();da=0;c=ua((c.join?c.join(","):""+c).split(","),function(a){if(B[a])return a+","+a+ha}).join(",");N(c.split(","),function(a){var c=y;a&&(fa&&(c=I.LEAVE(a,0,b,e)),c||b({action:"leave"}),B[a]=0,a in Z&&delete Z[a])});d()},subscribe:function(a,b){function e(a){a?setTimeout(d,D):(K=ma(oa,1),wb=ma(oa,1),
|
||||
setTimeout(function(){I.time(e)},D));u(function(b){if(a&&b.d)return b.d=0,b.p(b.name);!a&&!b.d&&(b.d=1,b.o(b.name))})}function f(){var a=L(),b=va(B).join(",");if(b){c();var h=l({uuid:F,auth:m});2<JSON.stringify(Z).length&&(h.state=JSON.stringify(Z));R&&(h.heartbeat=R);t();Ha=E({timeout:aa,a:a,b:function(a){q(a,x);I.time(e)},data:l(h),url:[wb,"subscribe",w,encodeURIComponent(b),a,da],c:function(a){if(!a||"object"==typeof a&&"error"in a&&a.error)return x(a.error),setTimeout(d,D);J(a[1]);da=!da&&Ja&&
|
||||
ea.get(w)||a[1];u(function(a){a.g||(a.g=1,a.n(a.name))});if(Ya&&!Ja)da=0,Ya=A,ea.set(w,0);else{Q&&(da=1E4,Q=0);ea.set(w,a[1]);var b,c=(2<a.length?a[2]:ua(va(B),function(b){return ua(Array(a[0].length).join(",").split(","),function(){return b})}).join(",")).split(",");b=function(){var a=c.shift()||yb;return[(B[a]||{}).a||xb,a.split(ha)[0]]};var e=G()-Va-+a[1]/1E4;N(a[0],function(c){var d=b(),c=g(c,B[d[1]]?B[d[1]].cipher_key:z);d[0](c,a,d[1],e)})}setTimeout(f,Y)}})}}var h=a.channel,b=(b=b||a.callback)||
|
||||
a.message,m=a.auth_key||M,j=a.connect||C(),p=a.reconnect||C(),r=a.disconnect||C(),x=a.error||C(),J=a.idle||C(),H=a.presence||0,P=a.noheresync||0,Q=a.backfill||0,X=a.timetoken||0,aa=a.timeout||Hb,Y=a.windowing||Sa,S=a.state,W=a.heartbeat||a.pnexpires,ba=a.restore||Ja;Ja=ba;da=X;if(!h)return s("Missing Channel");if(!b)return s("Missing Callback");if(!w)return s("Missing Subscribe Key");(W||0===W)&&I.set_heartbeat(W);N((h.join?h.join(","):""+h).split(","),function(c){var d=B[c]||{};B[yb=c]={name:c,g:d.g,
|
||||
d:d.d,f:1,a:xb=b,cipher_key:a.cipher_key,n:j,o:r,p:p};S&&(Z[c]=c in S?S[c]:S);H&&(I.subscribe({channel:c+ha,callback:H,restore:ba}),!d.f&&!P&&I.here_now({channel:c,callback:function(a){N("uuids"in a?a.uuids:[],function(b){H({action:"join",uuid:b,timestamp:Math.floor(G()/1E3),occupancy:a.occupancy||1},a,c)})}}))});d=function(){c();setTimeout(f,Y)};if(!fa)return ga.push(d);d()},here_now:function(a,b){var b=a.callback||b,c=a.error||C(),d=a.auth_key||M,e=a.channel,f=L(),g=a.state,d={uuid:F,auth:d};if(!("uuids"in
|
||||
a?a.uuids:1))d.disable_uuids=1;g&&(d.state=1);if(!b)return s("Missing Callback");if(!w)return s("Missing Subscribe Key");g=[K,"v2","presence","sub_key",w];e&&g.push("channel")&&g.push(encodeURIComponent(e));"0"!=f&&(d.callback=f);E({a:f,data:l(d),c:function(a){m(a,b,c)},b:function(a){q(a,c)},url:g})},where_now:function(a,b){var b=a.callback||b,c=a.error||C(),d=a.auth_key||M,e=L(),f=a.uuid||F,d={auth:d};if(!b)return s("Missing Callback");if(!w)return s("Missing Subscribe Key");"0"!=e&&(d.callback=
|
||||
e);E({a:e,data:l(d),c:function(a){m(a,b,c)},b:function(a){q(a,c)},url:[K,"v2","presence","sub_key",w,"uuid",encodeURIComponent(f)]})},state:function(a,b){var b=a.callback||b||C(),c=a.error||C(),d=a.auth_key||M,e=L(),f=a.state,g=a.uuid||F,h=a.channel,d=l({auth:d});if(!w)return s("Missing Subscribe Key");if(!g)return s("Missing UUID");if(!h)return s("Missing Channel");"0"!=e&&(d.callback=e);B[h]&&(B[h].f&&f)&&(Z[h]=f);d.state=JSON.stringify(f);f=f?[K,"v2","presence","sub-key",w,"channel",encodeURIComponent(h),
|
||||
"uuid",g,"data"]:[K,"v2","presence","sub-key",w,"channel",encodeURIComponent(h),"uuid",encodeURIComponent(g)];E({a:e,data:l(d),c:function(a){m(a,b,c)},b:function(a){q(a,c)},url:f})},grant:function(a,b){var b=a.callback||b,c=a.error||C(),d=a.channel,e=L(),f=a.ttl,g=a.read?"1":"0",j=a.write?"1":"0",p=a.auth_key;if(!b)return s("Missing Callback");if(!w)return s("Missing Subscribe Key");if(!P)return s("Missing Publish Key");if(!Ia)return s("Missing Secret Key");var r=w+"\n"+P+"\ngrant\n",g={w:j,r:g,timestamp:Math.floor((new Date).getTime()/
|
||||
1E3)};"undefined"!=d&&(d!=z&&0<d.length)&&(g.channel=d);"0"!=e&&(g.callback=e);if(f||0===f)g.ttl=f;p&&(g.auth=p);g=l(g);p||delete g.auth;r+=h(g);d=vb(r,Ia);d=d.replace(/\+/g,"-");d=d.replace(/\//g,"_");g.signature=d;E({a:e,data:g,c:function(a){m(a,b,c)},b:function(a){q(a,c)},url:[K,"v1","auth","grant","sub-key",w]})},audit:function(a,b){var b=a.callback||b,c=a.error||C(),d=a.channel,e=a.auth_key,f=L();if(!b)return s("Missing Callback");if(!w)return s("Missing Subscribe Key");if(!P)return s("Missing Publish Key");
|
||||
if(!Ia)return s("Missing Secret Key");var g=w+"\n"+P+"\naudit\n",j={timestamp:Math.floor((new Date).getTime()/1E3)};"0"!=f&&(j.callback=f);"undefined"!=d&&(d!=z&&0<d.length)&&(j.channel=d);e&&(j.auth=e);j=l(j);e||delete j.auth;g+=h(j);d=vb(g,Ia);d=d.replace(/\+/g,"-");d=d.replace(/\//g,"_");j.signature=d;E({a:f,data:j,c:function(a){m(a,b,c)},b:function(a){q(a,c)},url:[K,"v1","auth","audit","sub-key",w]})},revoke:function(a,b){a.read=A;a.write=A;I.grant(a,b)},set_uuid:function(a){F=a;d()},get_uuid:function(){return F},
|
||||
presence_heartbeat:function(a){var b=a.callback||C(),c=a.error||C(),a=L(),d={uuid:F,auth:M};2<JSON.stringify(Z).length&&(d.state=JSON.stringify(Z));0<R&&320>R&&(d.heartbeat=R);"0"!=a&&(d.callback=a);var e=E,d=l(d),f=5*D,g=K,h=w,j=va(B,y).join(",");e({a:a,data:d,timeout:f,url:[g,"v2","presence","sub-key",h,"channel",encodeURIComponent(j),"heartbeat"],c:function(a){m(a,b,c)},b:function(a){q(a,c)}})},xdr:E,ready:wa,db:ea,uuid:pa,map:ua,each:N,"each-channel":u,grep:sa,offline:function(){c(1,{message:"Offline. Please check your network settings."})},
|
||||
supplant:ta,now:G,unique:la,updater:ra};F||(F=I.uuid());ea.set(w+"uuid",F);setTimeout(p,D);setTimeout(f,ob);W=setTimeout(t,(J-3)*D);b();var H=I,Ka;for(Ka in H)H.hasOwnProperty(Ka)&&(e[Ka]=H[Ka]);e.css=sb;e.$=mb;e.create=tb;e.bind=qb;e.head=rb;e.search=pb;e.attr=V;e.events=Gb;e.init=e;e.secure=e;qb("beforeunload",window,function(){if(Ra)e["each-channel"](function(a){e.LEAVE(a.name,0)});return y});if(a.notest)return e;qb("offline",window,e.offline);qb("offline",document,e.offline);return e};Jb.init=
|
||||
Jb;Jb.secure=Jb;"complete"===document.readyState?setTimeout(wa,0):qb("load",window,function(){setTimeout(wa,0)});var Kb=Ib||{};PUBNUB=Jb({notest:1,publish_key:V(Kb,"pub-key"),subscribe_key:V(Kb,"sub-key"),ssl:!document.location.href.indexOf("https")||"on"==V(Kb,"ssl"),origin:V(Kb,"origin"),uuid:V(Kb,"uuid")});window.jQuery&&(window.jQuery.PUBNUB=Jb);"undefined"!==typeof module&&(module.exports=PUBNUB)&&wa();var Db=mb("pubnubs")||0;if(Ib){sb(Ib,{position:"absolute",top:-D});if("opera"in window||V(Ib,
|
||||
"flash"))Ib.innerHTML="<object id=pubnubs data=https://pubnub.a.ssl.fastly.net/pubnub.swf><param name=movie value=https://pubnub.a.ssl.fastly.net/pubnub.swf><param name=allowscriptaccess value=always></object>";PUBNUB.rdx=function(a,d){if(!d)return $[a].onerror();$[a].responseText=unescape(d);$[a].onload()};$.id=D}}
|
||||
var Mb=PUBNUB.ws=function(a,d){if(!(this instanceof Mb))return new Mb(a,d);var b=this,a=b.url=a||"";b.protocol=d||"Sec-WebSocket-Protocol";var c=a.split("/"),c={ssl:"wss:"===c[0],origin:c[2],publish_key:c[3],subscribe_key:c[4],channel:c[5]};b.CONNECTING=0;b.OPEN=1;b.CLOSING=2;b.CLOSED=3;b.CLOSE_NORMAL=1E3;b.CLOSE_GOING_AWAY=1001;b.CLOSE_PROTOCOL_ERROR=1002;b.CLOSE_UNSUPPORTED=1003;b.CLOSE_TOO_LARGE=1004;b.CLOSE_NO_STATUS=1005;b.CLOSE_ABNORMAL=1006;b.onclose=b.onerror=b.onmessage=b.onopen=b.onsend=
|
||||
C();b.binaryType="";b.extensions="";b.bufferedAmount=0;b.trasnmitting=A;b.buffer=[];b.readyState=b.CONNECTING;if(!a)return b.readyState=b.CLOSED,b.onclose({code:b.CLOSE_ABNORMAL,reason:"Missing URL",wasClean:y}),b;b.e=PUBNUB.init(c);b.e.k=c;b.k=c;b.e.subscribe({restore:A,channel:c.channel,disconnect:b.onerror,reconnect:b.onopen,error:function(){b.onclose({code:b.CLOSE_ABNORMAL,reason:"Missing URL",wasClean:A})},callback:function(a){b.onmessage({data:a})},connect:function(){b.readyState=b.OPEN;b.onopen()}})};
|
||||
Mb.prototype.send=function(a){var d=this;d.e.publish({channel:d.e.k.channel,message:a,callback:function(a){d.onsend({data:a})}})};
|
||||
})();
|
Loading…
Reference in New Issue
Block a user