diff --git a/DetectRTC.js b/DetectRTC.js
new file mode 100644
index 0000000..58dc4c7
--- /dev/null
+++ b/DetectRTC.js
@@ -0,0 +1,1192 @@
+'use strict';
+
+// Last Updated On: 2020-08-12 11:18:41 AM UTC
+
+// ________________
+// DetectRTC v1.4.1
+
+// Open-Sourced: https://github.com/muaz-khan/DetectRTC
+
+// --------------------------------------------------
+// Muaz Khan - www.MuazKhan.com
+// MIT License - www.WebRTC-Experiment.com/licence
+// --------------------------------------------------
+
+(function() {
+
+ var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
+
+ var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node && /*node-process*/ !process.browser;
+ if (isNodejs) {
+ var version = process.versions.node.toString().replace('v', '');
+ browserFakeUserAgent = 'Nodejs/' + version + ' (NodeOS) AppleWebKit/' + version + ' (KHTML, like Gecko) Nodejs/' + version + ' Nodejs/' + version
+ }
+
+ (function(that) {
+ if (typeof window !== 'undefined') {
+ return;
+ }
+
+ if (typeof window === 'undefined' && typeof global !== 'undefined') {
+ global.navigator = {
+ userAgent: browserFakeUserAgent,
+ getUserMedia: function() {}
+ };
+
+ /*global window:true */
+ that.window = global;
+ } else if (typeof window === 'undefined') {
+ // window = this;
+ }
+
+ if (typeof location === 'undefined') {
+ /*global location:true */
+ that.location = {
+ protocol: 'file:',
+ href: '',
+ hash: ''
+ };
+ }
+
+ if (typeof screen === 'undefined') {
+ /*global screen:true */
+ that.screen = {
+ width: 0,
+ height: 0
+ };
+ }
+ })(typeof global !== 'undefined' ? global : window);
+
+ /*global navigator:true */
+ var navigator = window.navigator;
+
+ if (typeof navigator !== 'undefined') {
+ if (typeof navigator.webkitGetUserMedia !== 'undefined') {
+ navigator.getUserMedia = navigator.webkitGetUserMedia;
+ }
+
+ if (typeof navigator.mozGetUserMedia !== 'undefined') {
+ navigator.getUserMedia = navigator.mozGetUserMedia;
+ }
+ } else {
+ navigator = {
+ getUserMedia: function() {},
+ userAgent: browserFakeUserAgent
+ };
+ }
+
+ var isMobileDevice = !!(/Android|webOS|iPhone|iPad|iPod|BB10|BlackBerry|IEMobile|Opera Mini|Mobile|mobile/i.test(navigator.userAgent || ''));
+
+ var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob);
+
+ var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
+ var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && ('netscape' in window) && / rv:/.test(navigator.userAgent);
+ var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
+ var isChrome = !!window.chrome && !isOpera;
+ var isIE = typeof document !== 'undefined' && !!document.documentMode && !isEdge;
+
+ // this one can also be used:
+ // https://www.websocket.org/js/stuff.js (DetectBrowser.js)
+
+ function getBrowserInfo() {
+ var nVer = navigator.appVersion;
+ var nAgt = navigator.userAgent;
+ var browserName = navigator.appName;
+ var fullVersion = '' + parseFloat(navigator.appVersion);
+ var majorVersion = parseInt(navigator.appVersion, 10);
+ var nameOffset, verOffset, ix;
+
+ // In Opera, the true version is after 'Opera' or after 'Version'
+ if (isOpera) {
+ browserName = 'Opera';
+ try {
+ fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0];
+ majorVersion = fullVersion.split('.')[0];
+ } catch (e) {
+ fullVersion = '0.0.0.0';
+ majorVersion = 0;
+ }
+ }
+ // In MSIE version <=10, the true version is after 'MSIE' in userAgent
+ // In IE 11, look for the string after 'rv:'
+ else if (isIE) {
+ verOffset = nAgt.indexOf('rv:');
+ if (verOffset > 0) { //IE 11
+ fullVersion = nAgt.substring(verOffset + 3);
+ } else { //IE 10 or earlier
+ verOffset = nAgt.indexOf('MSIE');
+ fullVersion = nAgt.substring(verOffset + 5);
+ }
+ browserName = 'IE';
+ }
+ // In Chrome, the true version is after 'Chrome'
+ else if (isChrome) {
+ verOffset = nAgt.indexOf('Chrome');
+ browserName = 'Chrome';
+ fullVersion = nAgt.substring(verOffset + 7);
+ }
+ // In Safari, the true version is after 'Safari' or after 'Version'
+ else if (isSafari) {
+ // both and safri and chrome has same userAgent
+ if (nAgt.indexOf('CriOS') !== -1) {
+ verOffset = nAgt.indexOf('CriOS');
+ browserName = 'Chrome';
+ fullVersion = nAgt.substring(verOffset + 6);
+ } else if (nAgt.indexOf('FxiOS') !== -1) {
+ verOffset = nAgt.indexOf('FxiOS');
+ browserName = 'Firefox';
+ fullVersion = nAgt.substring(verOffset + 6);
+ } else {
+ verOffset = nAgt.indexOf('Safari');
+
+ browserName = 'Safari';
+ fullVersion = nAgt.substring(verOffset + 7);
+
+ if ((verOffset = nAgt.indexOf('Version')) !== -1) {
+ fullVersion = nAgt.substring(verOffset + 8);
+ }
+
+ if (navigator.userAgent.indexOf('Version/') !== -1) {
+ fullVersion = navigator.userAgent.split('Version/')[1].split(' ')[0];
+ }
+ }
+ }
+ // In Firefox, the true version is after 'Firefox'
+ else if (isFirefox) {
+ verOffset = nAgt.indexOf('Firefox');
+ browserName = 'Firefox';
+ fullVersion = nAgt.substring(verOffset + 8);
+ }
+
+ // In most other browsers, 'name/version' is at the end of userAgent
+ else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
+ browserName = nAgt.substring(nameOffset, verOffset);
+ fullVersion = nAgt.substring(verOffset + 1);
+
+ if (browserName.toLowerCase() === browserName.toUpperCase()) {
+ browserName = navigator.appName;
+ }
+ }
+
+ if (isEdge) {
+ browserName = 'Edge';
+ fullVersion = navigator.userAgent.split('Edge/')[1];
+ // fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString();
+ }
+
+ // trim the fullVersion string at semicolon/space/bracket if present
+ if ((ix = fullVersion.search(/[; \)]/)) !== -1) {
+ fullVersion = fullVersion.substring(0, ix);
+ }
+
+ majorVersion = parseInt('' + fullVersion, 10);
+
+ if (isNaN(majorVersion)) {
+ fullVersion = '' + parseFloat(navigator.appVersion);
+ majorVersion = parseInt(navigator.appVersion, 10);
+ }
+
+ return {
+ fullVersion: fullVersion,
+ version: majorVersion,
+ name: browserName,
+ isPrivateBrowsing: false
+ };
+ }
+
+ // via: https://gist.github.com/cou929/7973956
+
+ function retry(isDone, next) {
+ var currentTrial = 0,
+ maxRetry = 50,
+ interval = 10,
+ isTimeout = false;
+ var id = window.setInterval(
+ function() {
+ if (isDone()) {
+ window.clearInterval(id);
+ next(isTimeout);
+ }
+ if (currentTrial++ > maxRetry) {
+ window.clearInterval(id);
+ isTimeout = true;
+ next(isTimeout);
+ }
+ },
+ 10
+ );
+ }
+
+ function isIE10OrLater(userAgent) {
+ var ua = userAgent.toLowerCase();
+ if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
+ return false;
+ }
+ var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
+ if (match && parseInt(match[1], 10) >= 10) {
+ return true;
+ }
+ return false;
+ }
+
+ function detectPrivateMode(callback) {
+ var isPrivate;
+
+ try {
+
+ if (window.webkitRequestFileSystem) {
+ window.webkitRequestFileSystem(
+ window.TEMPORARY, 1,
+ function() {
+ isPrivate = false;
+ },
+ function(e) {
+ isPrivate = true;
+ }
+ );
+ } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
+ var db;
+ try {
+ db = window.indexedDB.open('test');
+ db.onerror = function() {
+ return true;
+ };
+ } catch (e) {
+ isPrivate = true;
+ }
+
+ if (typeof isPrivate === 'undefined') {
+ retry(
+ function isDone() {
+ return db.readyState === 'done' ? true : false;
+ },
+ function next(isTimeout) {
+ if (!isTimeout) {
+ isPrivate = db.result ? false : true;
+ }
+ }
+ );
+ }
+ } else if (isIE10OrLater(window.navigator.userAgent)) {
+ isPrivate = false;
+ try {
+ if (!window.indexedDB) {
+ isPrivate = true;
+ }
+ } catch (e) {
+ isPrivate = true;
+ }
+ } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
+ try {
+ window.localStorage.setItem('test', 1);
+ } catch (e) {
+ isPrivate = true;
+ }
+
+ if (typeof isPrivate === 'undefined') {
+ isPrivate = false;
+ window.localStorage.removeItem('test');
+ }
+ }
+
+ } catch (e) {
+ isPrivate = false;
+ }
+
+ retry(
+ function isDone() {
+ return typeof isPrivate !== 'undefined' ? true : false;
+ },
+ function next(isTimeout) {
+ callback(isPrivate);
+ }
+ );
+ }
+
+ var isMobile = {
+ Android: function() {
+ return navigator.userAgent.match(/Android/i);
+ },
+ BlackBerry: function() {
+ return navigator.userAgent.match(/BlackBerry|BB10/i);
+ },
+ iOS: function() {
+ return navigator.userAgent.match(/iPhone|iPad|iPod/i);
+ },
+ Opera: function() {
+ return navigator.userAgent.match(/Opera Mini/i);
+ },
+ Windows: function() {
+ return navigator.userAgent.match(/IEMobile/i);
+ },
+ any: function() {
+ return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
+ },
+ getOsName: function() {
+ var osName = 'Unknown OS';
+ if (isMobile.Android()) {
+ osName = 'Android';
+ }
+
+ if (isMobile.BlackBerry()) {
+ osName = 'BlackBerry';
+ }
+
+ if (isMobile.iOS()) {
+ osName = 'iOS';
+ }
+
+ if (isMobile.Opera()) {
+ osName = 'Opera Mini';
+ }
+
+ if (isMobile.Windows()) {
+ osName = 'Windows';
+ }
+
+ return osName;
+ }
+ };
+
+ // via: http://jsfiddle.net/ChristianL/AVyND/
+ function detectDesktopOS() {
+ var unknown = '-';
+
+ var nVer = navigator.appVersion;
+ var nAgt = navigator.userAgent;
+
+ var os = unknown;
+ var clientStrings = [{
+ s: 'Chrome OS',
+ r: /CrOS/
+ }, {
+ s: 'Windows 10',
+ r: /(Windows 10.0|Windows NT 10.0)/
+ }, {
+ s: 'Windows 8.1',
+ r: /(Windows 8.1|Windows NT 6.3)/
+ }, {
+ s: 'Windows 8',
+ r: /(Windows 8|Windows NT 6.2)/
+ }, {
+ s: 'Windows 7',
+ r: /(Windows 7|Windows NT 6.1)/
+ }, {
+ s: 'Windows Vista',
+ r: /Windows NT 6.0/
+ }, {
+ s: 'Windows Server 2003',
+ r: /Windows NT 5.2/
+ }, {
+ s: 'Windows XP',
+ r: /(Windows NT 5.1|Windows XP)/
+ }, {
+ s: 'Windows 2000',
+ r: /(Windows NT 5.0|Windows 2000)/
+ }, {
+ s: 'Windows ME',
+ r: /(Win 9x 4.90|Windows ME)/
+ }, {
+ s: 'Windows 98',
+ r: /(Windows 98|Win98)/
+ }, {
+ s: 'Windows 95',
+ r: /(Windows 95|Win95|Windows_95)/
+ }, {
+ s: 'Windows NT 4.0',
+ r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/
+ }, {
+ s: 'Windows CE',
+ r: /Windows CE/
+ }, {
+ s: 'Windows 3.11',
+ r: /Win16/
+ }, {
+ s: 'Android',
+ r: /Android/
+ }, {
+ s: 'Open BSD',
+ r: /OpenBSD/
+ }, {
+ s: 'Sun OS',
+ r: /SunOS/
+ }, {
+ s: 'Linux',
+ r: /(Linux|X11)/
+ }, {
+ s: 'iOS',
+ r: /(iPhone|iPad|iPod)/
+ }, {
+ s: 'Mac OS X',
+ r: /Mac OS X/
+ }, {
+ s: 'Mac OS',
+ r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/
+ }, {
+ s: 'QNX',
+ r: /QNX/
+ }, {
+ s: 'UNIX',
+ r: /UNIX/
+ }, {
+ s: 'BeOS',
+ r: /BeOS/
+ }, {
+ s: 'OS/2',
+ r: /OS\/2/
+ }, {
+ s: 'Search Bot',
+ r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/
+ }];
+ for (var i = 0, cs; cs = clientStrings[i]; i++) {
+ if (cs.r.test(nAgt)) {
+ os = cs.s;
+ break;
+ }
+ }
+
+ var osVersion = unknown;
+
+ if (/Windows/.test(os)) {
+ if (/Windows (.*)/.test(os)) {
+ osVersion = /Windows (.*)/.exec(os)[1];
+ }
+ os = 'Windows';
+ }
+
+ switch (os) {
+ case 'Mac OS X':
+ if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) {
+ osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1];
+ }
+ break;
+ case 'Android':
+ if (/Android ([\.\_\d]+)/.test(nAgt)) {
+ osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1];
+ }
+ break;
+ case 'iOS':
+ if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) {
+ osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer);
+ if (osVersion && osVersion.length > 3) {
+ osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0);
+ }
+ }
+ break;
+ }
+
+ return {
+ osName: os,
+ osVersion: osVersion
+ };
+ }
+
+ var osName = 'Unknown OS';
+ var osVersion = 'Unknown OS Version';
+
+ function getAndroidVersion(ua) {
+ ua = (ua || navigator.userAgent).toLowerCase();
+ var match = ua.match(/android\s([0-9\.]*)/);
+ return match ? match[1] : false;
+ }
+
+ var osInfo = detectDesktopOS();
+
+ if (osInfo && osInfo.osName && osInfo.osName != '-') {
+ osName = osInfo.osName;
+ osVersion = osInfo.osVersion;
+ } else if (isMobile.any()) {
+ osName = isMobile.getOsName();
+
+ if (osName == 'Android') {
+ osVersion = getAndroidVersion();
+ }
+ }
+
+ var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node;
+
+ if (osName === 'Unknown OS' && isNodejs) {
+ osName = 'Nodejs';
+ osVersion = process.versions.node.toString().replace('v', '');
+ }
+
+ var isCanvasSupportsStreamCapturing = false;
+ var isVideoSupportsStreamCapturing = false;
+ ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) {
+ if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
+ return;
+ }
+
+ if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) {
+ isCanvasSupportsStreamCapturing = true;
+ }
+
+ if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) {
+ isVideoSupportsStreamCapturing = true;
+ }
+ });
+
+ var regexIpv4Local = /^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/,
+ regexIpv4 = /([0-9]{1,3}(\.[0-9]{1,3}){3})/,
+ regexIpv6 = /[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}/;
+
+ // via: https://github.com/diafygi/webrtc-ips
+ function DetectLocalIPAddress(callback, stream) {
+ if (!DetectRTC.isWebRTCSupported) {
+ return;
+ }
+
+ var isPublic = true,
+ isIpv4 = true;
+ getIPs(function(ip) {
+ if (!ip) {
+ callback(); // Pass nothing to tell that ICE-gathering-ended
+ } else if (ip.match(regexIpv4Local)) {
+ isPublic = false;
+ callback('Local: ' + ip, isPublic, isIpv4);
+ } else if (ip.match(regexIpv6)) { //via https://ourcodeworld.com/articles/read/257/how-to-get-the-client-ip-address-with-javascript-only
+ isIpv4 = false;
+ callback('Public: ' + ip, isPublic, isIpv4);
+ } else {
+ callback('Public: ' + ip, isPublic, isIpv4);
+ }
+ }, stream);
+ }
+
+ function getIPs(callback, stream) {
+ if (typeof document === 'undefined' || typeof document.getElementById !== 'function') {
+ return;
+ }
+
+ var ipDuplicates = {};
+
+ var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
+
+ if (!RTCPeerConnection) {
+ var iframe = document.getElementById('iframe');
+ if (!iframe) {
+ return;
+ }
+ var win = iframe.contentWindow;
+ RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection;
+ }
+
+ if (!RTCPeerConnection) {
+ return;
+ }
+
+ var peerConfig = null;
+
+ if (DetectRTC.browser === 'Chrome' && DetectRTC.browser.version < 58) {
+ // todo: add support for older Opera
+ peerConfig = {
+ optional: [{
+ RtpDataChannels: true
+ }]
+ };
+ }
+
+ var servers = {
+ iceServers: [{
+ urls: 'stun:stun.l.google.com:19302'
+ }]
+ };
+
+ var pc = new RTCPeerConnection(servers, peerConfig);
+
+ if (stream) {
+ if (pc.addStream) {
+ pc.addStream(stream);
+ } else if (pc.addTrack && stream.getTracks()[0]) {
+ pc.addTrack(stream.getTracks()[0], stream);
+ }
+ }
+
+ function handleCandidate(candidate) {
+ if (!candidate) {
+ callback(); // Pass nothing to tell that ICE-gathering-ended
+ return;
+ }
+
+ var match = regexIpv4.exec(candidate);
+ if (!match) {
+ return;
+ }
+ var ipAddress = match[1];
+ var isPublic = (candidate.match(regexIpv4Local)),
+ isIpv4 = true;
+
+ if (ipDuplicates[ipAddress] === undefined) {
+ callback(ipAddress, isPublic, isIpv4);
+ }
+
+ ipDuplicates[ipAddress] = true;
+ }
+
+ // listen for candidate events
+ pc.onicecandidate = function(event) {
+ if (event.candidate && event.candidate.candidate) {
+ handleCandidate(event.candidate.candidate);
+ } else {
+ handleCandidate(); // Pass nothing to tell that ICE-gathering-ended
+ }
+ };
+
+ // create data channel
+ if (!stream) {
+ try {
+ pc.createDataChannel('sctp', {});
+ } catch (e) {}
+ }
+
+ // create an offer sdp
+ if (DetectRTC.isPromisesSupported) {
+ pc.createOffer().then(function(result) {
+ pc.setLocalDescription(result).then(afterCreateOffer);
+ });
+ } else {
+ pc.createOffer(function(result) {
+ pc.setLocalDescription(result, afterCreateOffer, function() {});
+ }, function() {});
+ }
+
+ function afterCreateOffer() {
+ var lines = pc.localDescription.sdp.split('\n');
+
+ lines.forEach(function(line) {
+ if (line && line.indexOf('a=candidate:') === 0) {
+ handleCandidate(line);
+ }
+ });
+ }
+ }
+
+ var MediaDevices = [];
+
+ var audioInputDevices = [];
+ var audioOutputDevices = [];
+ var videoInputDevices = [];
+
+ if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
+ // Firefox 38+ seems having support of enumerateDevices
+ // Thanks @xdumaine/enumerateDevices
+ navigator.enumerateDevices = function(callback) {
+ var enumerateDevices = navigator.mediaDevices.enumerateDevices();
+ if (enumerateDevices && enumerateDevices.then) {
+ navigator.mediaDevices.enumerateDevices().then(callback).catch(function() {
+ callback([]);
+ });
+ } else {
+ callback([]);
+ }
+ };
+ }
+
+ // Media Devices detection
+ var canEnumerate = false;
+
+ /*global MediaStreamTrack:true */
+ if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) {
+ canEnumerate = true;
+ } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) {
+ canEnumerate = true;
+ }
+
+ var hasMicrophone = false;
+ var hasSpeakers = false;
+ var hasWebcam = false;
+
+ var isWebsiteHasMicrophonePermissions = false;
+ var isWebsiteHasWebcamPermissions = false;
+
+ // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices
+ function checkDeviceSupport(callback) {
+ if (!canEnumerate) {
+ if (callback) {
+ callback();
+ }
+ return;
+ }
+
+ if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) {
+ navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack);
+ }
+
+ if (!navigator.enumerateDevices && navigator.enumerateDevices) {
+ navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator);
+ }
+
+ if (!navigator.enumerateDevices) {
+ if (callback) {
+ callback();
+ }
+ return;
+ }
+
+ MediaDevices = [];
+
+ audioInputDevices = [];
+ audioOutputDevices = [];
+ videoInputDevices = [];
+
+ hasMicrophone = false;
+ hasSpeakers = false;
+ hasWebcam = false;
+
+ isWebsiteHasMicrophonePermissions = false;
+ isWebsiteHasWebcamPermissions = false;
+
+ // to prevent duplication
+ var alreadyUsedDevices = {};
+
+ navigator.enumerateDevices(function(devices) {
+ MediaDevices = [];
+
+ audioInputDevices = [];
+ audioOutputDevices = [];
+ videoInputDevices = [];
+
+ devices.forEach(function(_device) {
+ var device = {};
+ for (var d in _device) {
+ try {
+ if (typeof _device[d] !== 'function') {
+ device[d] = _device[d];
+ }
+ } catch (e) {}
+ }
+
+ if (alreadyUsedDevices[device.deviceId + device.label + device.kind]) {
+ return;
+ }
+
+ // if it is MediaStreamTrack.getSources
+ if (device.kind === 'audio') {
+ device.kind = 'audioinput';
+ }
+
+ if (device.kind === 'video') {
+ device.kind = 'videoinput';
+ }
+
+ if (!device.deviceId) {
+ device.deviceId = device.id;
+ }
+
+ if (!device.id) {
+ device.id = device.deviceId;
+ }
+
+ if (!device.label) {
+ device.isCustomLabel = true;
+
+ if (device.kind === 'videoinput') {
+ device.label = 'Camera ' + (videoInputDevices.length + 1);
+ } else if (device.kind === 'audioinput') {
+ device.label = 'Microphone ' + (audioInputDevices.length + 1);
+ } else if (device.kind === 'audiooutput') {
+ device.label = 'Speaker ' + (audioOutputDevices.length + 1);
+ } else {
+ device.label = 'Please invoke getUserMedia once.';
+ }
+
+ if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
+ if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1) {
+ device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.';
+ }
+ }
+ }
+
+ if (device.kind === 'audioinput') {
+ hasMicrophone = true;
+ isWebsiteHasMicrophonePermissions = true;
+
+ if (audioInputDevices.indexOf(device) === -1) {
+ audioInputDevices.push(device);
+ }
+ }
+
+ if (device.kind === 'audiooutput') {
+ hasSpeakers = true;
+
+ if (audioOutputDevices.indexOf(device) === -1) {
+ audioOutputDevices.push(device);
+ }
+ }
+
+ if (device.kind === 'videoinput') {
+ hasWebcam = true;
+ isWebsiteHasWebcamPermissions = true;
+
+ if (videoInputDevices.indexOf(device) === -1) {
+ videoInputDevices.push(device);
+ }
+ }
+
+ // there is no 'videoouput' in the spec.
+ MediaDevices.push(device);
+
+ alreadyUsedDevices[device.deviceId + device.label + device.kind] = device;
+ });
+
+ if (typeof DetectRTC !== 'undefined') {
+ // to sync latest outputs
+ DetectRTC.MediaDevices = MediaDevices;
+ DetectRTC.hasMicrophone = hasMicrophone;
+ DetectRTC.hasSpeakers = hasSpeakers;
+ DetectRTC.hasWebcam = hasWebcam;
+
+ DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions;
+ DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions;
+
+ DetectRTC.audioInputDevices = audioInputDevices;
+ DetectRTC.audioOutputDevices = audioOutputDevices;
+ DetectRTC.videoInputDevices = videoInputDevices;
+ }
+
+ if (callback) {
+ callback();
+ }
+ });
+ }
+
+
+ /**
+ * 获取RTC发送器支持的视频编解码列表
+ *
+ * @returns RTP Sender video codecs, json Array [{clockRate:"", mimeType:"", sdpFmtpLine:""}]
+ */
+ function videoSenderCodecs() {
+ if (RTCRtpSender && RTCRtpSender.getCapabilities("video")) {
+ return RTCRtpSender.getCapabilities("video").codecs;
+ } else {
+ return [];
+ }
+ }
+
+ /**
+ * 获取RTC接收器支持的视频编解码列表
+ *
+ * @returns RTP Receiver video codecs, json Array [{clockRate:"", mimeType:"", sdpFmtpLine:""}]
+ */
+ function videoReceiverCodecs() {
+ if (RTCRtpReceiver && RTCRtpReceiver.getCapabilities("video")) {
+ return RTCRtpReceiver.getCapabilities("video").codecs;
+ } else {
+ return [];
+ }
+ }
+
+ var DetectRTC = window.DetectRTC || {};
+
+ // 绑定支持的视频编解码列表属性
+ DetectRTC.videoSenderCodecs = videoSenderCodecs();
+ DetectRTC.videoReceiverCodecs = videoReceiverCodecs();
+
+ // ----------
+ // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion
+ DetectRTC.browser = getBrowserInfo();
+
+ detectPrivateMode(function(isPrivateBrowsing) {
+ DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing;
+ });
+
+ // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge
+ DetectRTC.browser['is' + DetectRTC.browser.name] = true;
+
+ // -----------
+ DetectRTC.osName = osName;
+ DetectRTC.osVersion = osVersion;
+
+ var isNodeWebkit = typeof process === 'object' && typeof process.versions === 'object' && process.versions['node-webkit'];
+
+ // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1.
+ var isWebRTCSupported = false;
+ ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) {
+ if (isWebRTCSupported) {
+ return;
+ }
+
+ if (item in window) {
+ isWebRTCSupported = true;
+ }
+ });
+ DetectRTC.isWebRTCSupported = isWebRTCSupported;
+
+ //-------
+ DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined';
+
+ // --------- Detect if system supports screen capturing API
+ var isScreenCapturingSupported = false;
+ if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) {
+ isScreenCapturingSupported = true;
+ } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) {
+ isScreenCapturingSupported = true;
+ } else if (DetectRTC.browser.isEdge && DetectRTC.browser.version >= 17) {
+ isScreenCapturingSupported = true;
+ } else if (DetectRTC.osName === 'Android' && DetectRTC.browser.isChrome) {
+ isScreenCapturingSupported = true;
+ }
+
+ if (!!navigator.getDisplayMedia || (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia)) {
+ isScreenCapturingSupported = true;
+ }
+
+ if (!/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
+ var isNonLocalHost = typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1;
+ if (isNonLocalHost && (DetectRTC.browser.isChrome || DetectRTC.browser.isEdge || DetectRTC.browser.isOpera)) {
+ isScreenCapturingSupported = false;
+ } else if (DetectRTC.browser.isFirefox) {
+ isScreenCapturingSupported = false;
+ }
+ }
+ DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported;
+
+ // --------- Detect if WebAudio API are supported
+ var webAudio = {
+ isSupported: false,
+ isCreateMediaStreamSourceSupported: false
+ };
+
+ ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) {
+ if (webAudio.isSupported) {
+ return;
+ }
+
+ if (item in window) {
+ webAudio.isSupported = true;
+
+ if (window[item] && 'createMediaStreamSource' in window[item].prototype) {
+ webAudio.isCreateMediaStreamSourceSupported = true;
+ }
+ }
+ });
+ DetectRTC.isAudioContextSupported = webAudio.isSupported;
+ DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported;
+
+ // ---------- Detect if SCTP/RTP channels are supported.
+
+ var isRtpDataChannelsSupported = false;
+ if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) {
+ isRtpDataChannelsSupported = true;
+ }
+ DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported;
+
+ var isSCTPSupportd = false;
+ if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) {
+ isSCTPSupportd = true;
+ } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) {
+ isSCTPSupportd = true;
+ } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) {
+ isSCTPSupportd = true;
+ }
+ DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd;
+
+ // ---------
+
+ DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js"
+
+ // ------
+ var isGetUserMediaSupported = false;
+ if (navigator.getUserMedia) {
+ isGetUserMediaSupported = true;
+ } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
+ isGetUserMediaSupported = true;
+ }
+
+ if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
+ if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1) {
+ isGetUserMediaSupported = 'Requires HTTPs';
+ }
+ }
+
+ if (DetectRTC.osName === 'Nodejs') {
+ isGetUserMediaSupported = false;
+ }
+ DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported;
+
+ var displayResolution = '';
+ if (screen.width) {
+ var width = (screen.width) ? screen.width : '';
+ var height = (screen.height) ? screen.height : '';
+ displayResolution += '' + width + ' x ' + height;
+ }
+ DetectRTC.displayResolution = displayResolution;
+
+ 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);
+ }
+
+ DetectRTC.displayAspectRatio = getAspectRatio(screen.width, screen.height).toFixed(2);
+
+ // ----------
+ DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing;
+ DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing;
+
+ if (DetectRTC.browser.name == 'Chrome' && DetectRTC.browser.version >= 53) {
+ if (!DetectRTC.isCanvasSupportsStreamCapturing) {
+ DetectRTC.isCanvasSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features';
+ }
+
+ if (!DetectRTC.isVideoSupportsStreamCapturing) {
+ DetectRTC.isVideoSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features';
+ }
+ }
+
+ // ------
+ DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress;
+
+ DetectRTC.isWebSocketsSupported = 'WebSocket' in window && 2 === window.WebSocket.CLOSING;
+ DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported;
+
+ if (DetectRTC.osName === 'Nodejs') {
+ DetectRTC.isWebSocketsSupported = true;
+ DetectRTC.isWebSocketsBlocked = false;
+ }
+
+ DetectRTC.checkWebSocketsSupport = function(callback) {
+ callback = callback || function() {};
+ try {
+ var starttime;
+ var websocket = new WebSocket('wss://echo.websocket.org:443/');
+ websocket.onopen = function() {
+ DetectRTC.isWebSocketsBlocked = false;
+ starttime = (new Date).getTime();
+ websocket.send('ping');
+ };
+ websocket.onmessage = function() {
+ DetectRTC.WebsocketLatency = (new Date).getTime() - starttime + 'ms';
+ callback();
+ websocket.close();
+ websocket = null;
+ };
+ websocket.onerror = function() {
+ DetectRTC.isWebSocketsBlocked = true;
+ callback();
+ };
+ } catch (e) {
+ DetectRTC.isWebSocketsBlocked = true;
+ callback();
+ }
+ };
+
+ // -------
+ DetectRTC.load = function(callback) {
+ callback = callback || function() {};
+ checkDeviceSupport(callback);
+ };
+
+ // check for microphone/camera support!
+ if (typeof checkDeviceSupport === 'function') {
+ // checkDeviceSupport();
+ }
+
+ if (typeof MediaDevices !== 'undefined') {
+ DetectRTC.MediaDevices = MediaDevices;
+ } else {
+ DetectRTC.MediaDevices = [];
+ }
+
+ DetectRTC.hasMicrophone = hasMicrophone;
+ DetectRTC.hasSpeakers = hasSpeakers;
+ DetectRTC.hasWebcam = hasWebcam;
+
+ DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions;
+ DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions;
+
+ DetectRTC.audioInputDevices = audioInputDevices;
+ DetectRTC.audioOutputDevices = audioOutputDevices;
+ DetectRTC.videoInputDevices = videoInputDevices;
+
+ // ------
+ var isSetSinkIdSupported = false;
+ if (typeof document !== 'undefined' && typeof document.createElement === 'function' && 'setSinkId' in document.createElement('video')) {
+ isSetSinkIdSupported = true;
+ }
+ DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported;
+
+ // -----
+ var isRTPSenderReplaceTracksSupported = false;
+ if (DetectRTC.browser.isFirefox && typeof mozRTCPeerConnection !== 'undefined' /*&& DetectRTC.browser.version > 39*/ ) {
+ /*global mozRTCPeerConnection:true */
+ if ('getSenders' in mozRTCPeerConnection.prototype) {
+ isRTPSenderReplaceTracksSupported = true;
+ }
+ } else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') {
+ /*global webkitRTCPeerConnection:true */
+ if ('getSenders' in webkitRTCPeerConnection.prototype) {
+ isRTPSenderReplaceTracksSupported = true;
+ }
+ }
+ DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported;
+
+ //------
+ var isRemoteStreamProcessingSupported = false;
+ if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) {
+ isRemoteStreamProcessingSupported = true;
+ }
+ DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported;
+
+ //-------
+ var isApplyConstraintsSupported = false;
+
+ /*global MediaStreamTrack:true */
+ if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) {
+ isApplyConstraintsSupported = true;
+ }
+ DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported;
+
+ //-------
+ var isMultiMonitorScreenCapturingSupported = false;
+ if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) {
+ // version 43 merely supports platforms for multi-monitors
+ // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing.
+ isMultiMonitorScreenCapturingSupported = true;
+ }
+ DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported;
+
+ DetectRTC.isPromisesSupported = !!('Promise' in window);
+
+ // version is generated by "grunt"
+ DetectRTC.version = '1.4.1';
+
+ if (typeof DetectRTC === 'undefined') {
+ window.DetectRTC = {};
+ }
+
+ var MediaStream = window.MediaStream;
+
+ if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
+ MediaStream = webkitMediaStream;
+ }
+
+ if (typeof MediaStream !== 'undefined' && typeof MediaStream === 'function') {
+ DetectRTC.MediaStream = Object.keys(MediaStream.prototype);
+ } else DetectRTC.MediaStream = false;
+
+ if (typeof MediaStreamTrack !== 'undefined') {
+ DetectRTC.MediaStreamTrack = Object.keys(MediaStreamTrack.prototype);
+ } else DetectRTC.MediaStreamTrack = false;
+
+ var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
+
+ if (typeof RTCPeerConnection !== 'undefined') {
+ DetectRTC.RTCPeerConnection = Object.keys(RTCPeerConnection.prototype);
+ } else DetectRTC.RTCPeerConnection = false;
+
+ window.DetectRTC = DetectRTC;
+
+ if (typeof module !== 'undefined' /* && !!module.exports*/ ) {
+ module.exports = DetectRTC;
+ }
+
+ if (typeof define === 'function' && define.amd) {
+ define('DetectRTC', [], function() {
+ return DetectRTC;
+ });
+ }
+})();
diff --git a/index.html b/index.html
old mode 100644
new mode 100755
index 08efdee..0334d70
--- a/index.html
+++ b/index.html
@@ -1 +1,591 @@
-
Github pages index
+
+
+
+
+ DetectRTC | TTT Is WebRTC Supported In Your Browser?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DetectRTC!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<script src="https://www.webrtc-experiment.com/DetectRTC.js"></script>
+<script>
+// OR otherwise use NPM
+var DetectRTC = require('detectrtc');
+</script>
+
+
+
+
+DetectRTC.load(function() {
+ DetectRTC.hasWebcam (has webcam device!)
+ DetectRTC.hasMicrophone (has microphone device!)
+ DetectRTC.hasSpeakers (has speakers!)
+ DetectRTC.isScreenCapturingSupported
+ DetectRTC.isSctpDataChannelsSupported
+ DetectRTC.isRtpDataChannelsSupported
+ DetectRTC.isAudioContextSupported
+ DetectRTC.isWebRTCSupported
+ DetectRTC.isDesktopCapturingSupported
+ DetectRTC.isMobileDevice
+
+ DetectRTC.isWebSocketsSupported
+ DetectRTC.isWebSocketsBlocked
+ DetectRTC.checkWebSocketsSupport(callback)
+
+ DetectRTC.isWebsiteHasWebcamPermissions // getUserMedia allowed for HTTPs domain in Chrome?
+ DetectRTC.isWebsiteHasMicrophonePermissions // getUserMedia allowed for HTTPs domain in Chrome?
+
+ DetectRTC.audioInputDevices // microphones
+ DetectRTC.audioOutputDevices // speakers
+ DetectRTC.videoInputDevices // cameras
+
+ DetectRTC.osName
+ DetectRTC.osVersion
+
+ DetectRTC.browser.name === 'Edge' || 'Chrome' || 'Firefox'
+ DetectRTC.browser.version
+ DetectRTC.browser.isChrome
+ DetectRTC.browser.isFirefox
+ DetectRTC.browser.isOpera
+ DetectRTC.browser.isIE
+ DetectRTC.browser.isSafari
+ DetectRTC.browser.isEdge
+
+ DetectRTC.browser.isPrivateBrowsing // incognito or private modes
+
+ DetectRTC.isCanvasSupportsStreamCapturing
+ DetectRTC.isVideoSupportsStreamCapturing
+
+ DetectRTC.DetectLocalIPAddress(callback)
+});
+
+
+
+
+
+
+
+
+
+
+
+