diff --git a/openvidu-browser/src/OpenVidu/LocalRecorder.ts b/openvidu-browser/src/OpenVidu/LocalRecorder.ts index 9a9af25f..d23085b1 100644 --- a/openvidu-browser/src/OpenVidu/LocalRecorder.ts +++ b/openvidu-browser/src/OpenVidu/LocalRecorder.ts @@ -17,8 +17,8 @@ import { Stream } from './Stream'; import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState'; -import platform = require('platform'); import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; +import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; /** @@ -30,6 +30,11 @@ declare var MediaRecorder: any; */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); + /** * Easy recording of [[Stream]] objects straightaway from the browser. Initialized with [[OpenVidu.initLocalRecorder]] method @@ -211,7 +216,7 @@ export class LocalRecorder { this.videoPreview.id = this.id; this.videoPreview.autoplay = true; - if (platform.name === 'Safari') { + if (platform.isSafariBrowser()) { this.videoPreview.setAttribute('playsinline', 'true'); } diff --git a/openvidu-browser/src/OpenVidu/OpenVidu.ts b/openvidu-browser/src/OpenVidu/OpenVidu.ts index 1e9213f0..3b5d80c1 100644 --- a/openvidu-browser/src/OpenVidu/OpenVidu.ts +++ b/openvidu-browser/src/OpenVidu/OpenVidu.ts @@ -28,6 +28,7 @@ import { CustomMediaStreamConstraints } from '../OpenViduInternal/Interfaces/Pri import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; +import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; import * as screenSharingAuto from '../OpenViduInternal/ScreenSharing/Screen-Capturing-Auto'; import * as screenSharing from '../OpenViduInternal/ScreenSharing/Screen-Capturing'; @@ -39,13 +40,6 @@ import EventEmitter = require('wolfy87-eventemitter'); * @hidden */ import RpcBuilder = require('../OpenViduInternal/KurentoUtils/kurento-jsonrpc'); -/** - * @hidden - */ -import platform = require('platform'); - -platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1; -platform['isIonicAndroid'] = platform.os!!.family === 'Android' && platform.name == "Android Browser"; /** * @hidden @@ -60,6 +54,11 @@ declare var cordova: any; */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); + /** * Entrypoint of OpenVidu Browser library. * Use it to initialize objects of type [[Session]], [[Publisher]] and [[LocalRecorder]] @@ -122,7 +121,7 @@ export class OpenVidu { logger.info("'OpenVidu' initialized"); logger.info("openvidu-browser version: " + this.libraryVersion); - if (platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') { + if (platform.isMobileDevice()) { // Listen to orientationchange only on mobile devices (window).addEventListener('orientationchange', () => { this.publishers.forEach(publisher => { @@ -135,7 +134,7 @@ export class OpenVidu { const getNewVideoDimensions = (): Promise<{ newWidth: number, newHeight: number }> => { return new Promise((resolve, reject) => { - if (platform['isIonicIos']) { + if (platform.isIonicIos()) { // iOS Ionic. Limitation: must get new dimensions from an existing video element already inserted into DOM resolve({ newWidth: publisher.stream.streamManager.videos[0].video.videoWidth, @@ -146,8 +145,8 @@ export class OpenVidu { // New resolution got from different places for Chrome and Firefox. Chrome needs a videoWidth and videoHeight of a videoElement. // Firefox needs getSettings from the videoTrack const firefoxSettings = publisher.stream.getMediaStream().getVideoTracks()[0].getSettings(); - const newWidth = ((platform.name!!.toLowerCase().indexOf('firefox') !== -1) ? firefoxSettings.width : publisher.videoReference.videoWidth); - const newHeight = ((platform.name!!.toLowerCase().indexOf('firefox') !== -1) ? firefoxSettings.height : publisher.videoReference.videoHeight); + const newWidth = ((platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) ? firefoxSettings.width : publisher.videoReference.videoWidth); + const newHeight = ((platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) ? firefoxSettings.height : publisher.videoReference.videoHeight); resolve({ newWidth, newHeight }); } }); @@ -336,8 +335,8 @@ export class OpenVidu { */ checkSystemRequirements(): number { - if (this.isIPhoneOrIPad()) { - if (this.isIOSWithSafari() || platform['isIonicIos']) { + if (platform.isIPhoneOrIPad()) { + if (platform.isIOSWithSafari() || platform.isIonicIos()) { return 1; } return 0; @@ -345,10 +344,10 @@ export class OpenVidu { // Accept: Chrome (desktop and Android), Firefox (desktop and Android), Opera (desktop and Android), // Safari (OSX and iOS), Ionic (Android and iOS), Samsung Internet Browser (Android) - if (this.isSafariBrowser() || this.isChromeBrowser() || this.isChromeMobileBrowser() || - this.isFirefoxBrowser() || this.isFirefoxMobileBrowser() || this.isOperaBrowser() || - this.isOperaMobileBrowser() || this.isAndroidBrowser() || this.isElectron() || - this.isSamsungBrowser() + if (platform.isSafariBrowser() || platform.isChromeBrowser() || platform.isChromeMobileBrowser() || + platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser() || platform.isOperaBrowser() || + platform.isOperaMobileBrowser() || platform.isAndroidBrowser() || platform.isElectron() || + platform.isSamsungBrowser() ) { return 1; } @@ -362,22 +361,8 @@ export class OpenVidu { * Checks if the browser supports screen-sharing. Desktop Chrome, Firefox and Opera support screen-sharing * @returns 1 if the browser supports screen-sharing, 0 otherwise */ - checkScreenSharingCapabilities(): number { - const browser = platform.name; - const version = platform?.version ? parseFloat(platform.version) : -1; - const family = platform.os!!.family; - - // Reject mobile devices - if (family === 'iOS' || family === 'Android') { - return 0; - } - - if ((browser !== 'Chrome') && (browser !== 'Firefox') && (browser !== 'Opera') && (browser !== 'Electron') && - (browser === 'Safari' && version < 13)) { - return 0; - } else { - return 1; - } + checkScreenSharingCapabilities(): boolean { + return platform.canScreenShare(); } @@ -390,7 +375,7 @@ export class OpenVidu { const devices: Device[] = []; // Ionic Android devices - if (platform['isIonicAndroid'] && typeof cordova != "undefined" && cordova?.plugins?.EnumerateDevicesPlugin) { + if (platform.isIonicAndroid() && typeof cordova != "undefined" && cordova?.plugins?.EnumerateDevicesPlugin) { cordova.plugins.EnumerateDevicesPlugin.getEnumerateDevices().then((pluginDevices: Device[]) => { let pluginAudioDevices: Device[] = []; let videoDevices: Device[] = []; @@ -582,10 +567,10 @@ export class OpenVidu { // Video is deviceId or screen sharing if (options.videoSource === 'screen' || options.videoSource === 'window' || - (platform.name === 'Electron' && options.videoSource.startsWith('screen:'))) { + (platform.isElectron() && options.videoSource.startsWith('screen:'))) { // Video is screen sharing mustAskForAudioTrackLater = !myConstraints.audioTrack && (options.audioSource !== null && options.audioSource !== false); - if (navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') { + if (navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) { // getDisplayMedia supported navigator.mediaDevices['getDisplayMedia']({ video: true }) .then(mediaStream => { @@ -872,98 +857,6 @@ export class OpenVidu { return mediaStream; } - /** - * @hidden - */ - public isChromeBrowser(): boolean { - return platform.name === 'Chrome'; - } - - /** - * @hidden - */ - public isSafariBrowser(): boolean { - return platform.name === 'Safari'; - } - - /** - * @hidden - */ - public isChromeMobileBrowser(): boolean { - return platform.name === 'Chrome Mobile'; - } - - /** - * @hidden - */ - public isFirefoxBrowser(): boolean { - return platform.name === 'Firefox'; - } - - /** - * @hidden - */ - public isFirefoxMobileBrowser(): boolean { - return platform.name === 'Firefox Mobile'; - } - - /** - * @hidden - */ - public isOperaBrowser(): boolean { - return platform.name === 'Opera'; - } - - /** - * @hidden - */ - public isOperaMobileBrowser(): boolean { - return platform.name === 'Opera Mobile'; - } - - /** - * @hidden - */ - public isAndroidBrowser(): boolean { - return platform.name === 'Android Browser'; - } - - /** - * @hidden - */ - public isElectron(): boolean { - return platform.name === 'Electron'; - } - - /** - * @hidden - */ - public isSamsungBrowser(): boolean { - return platform.name === 'Samsung Internet Mobile' || platform.name === 'Samsung Internet'; - } - - /** - * @hidden - */ - public isIPhoneOrIPad(): boolean { - const userAgent = !!platform.ua ? platform.ua : navigator.userAgent; - - const isTouchable = 'ontouchend' in document; - const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable; - const isIPhone = /\b(\w*iPhone\w*)\b/.test(userAgent) && /\b(\w*Mobile\w*)\b/.test(userAgent) && isTouchable; - - return isIPad || isIPhone; - } - - /** - * @hidden - */ - public isIOSWithSafari(): boolean { - const userAgent = !!platform.ua ? platform.ua : navigator.userAgent; - return /\b(\w*Apple\w*)\b/.test(navigator.vendor) && /\b(\w*Safari\w*)\b/.test(userAgent) - && !/\b(\w*CriOS\w*)\b/.test(userAgent) && !/\b(\w*FxiOS\w*)\b/.test(userAgent); - } - /** * @hidden */ @@ -984,12 +877,12 @@ export class OpenVidu { // Screen sharing if (!this.checkScreenSharingCapabilities()) { - const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0) or Electron. Detected client: ' + platform.name); + const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0) or Electron. Detected client: ' + platform.getName()); logger.error(error); reject(error); } else { - if (platform.name === 'Electron') { + if (platform.isElectron()) { const prefix = "screen:"; const videoSourceString: string = videoSource; const electronScreenId = videoSourceString.substr(videoSourceString.indexOf(prefix) + prefix.length); @@ -1003,7 +896,7 @@ export class OpenVidu { } else { - if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) { + if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) && !navigator.mediaDevices['getDisplayMedia']) { // Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia() @@ -1042,7 +935,7 @@ export class OpenVidu { resolve(myConstraints); } else { // Default screen sharing extension for Chrome/Opera, or is Firefox < 66 - const firefoxString = platform.name!.indexOf('Firefox') !== -1 ? publisherProperties.videoSource : undefined; + const firefoxString = (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) ? publisherProperties.videoSource : undefined; screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => { if (!!error) { @@ -1150,7 +1043,7 @@ export class OpenVidu { private isScreenShare(videoSource: string) { return videoSource === 'screen' || videoSource === 'window' || - (platform.name === 'Electron' && videoSource.startsWith('screen:')) + (platform.isElectron() && videoSource.startsWith('screen:')) } } \ No newline at end of file diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts index 15aa155a..b6fc847e 100644 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ b/openvidu-browser/src/OpenVidu/Publisher.ts @@ -27,15 +27,19 @@ import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPro import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent'; import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; - -import platform = require('platform'); import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; +import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; /** * @hidden */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); + /** * Packs local media streams. Participants can publish it to a session. Initialized with [[OpenVidu.initPublisher]] method * @@ -400,7 +404,7 @@ export class Publisher extends StreamManager { if (this.stream.isSendVideo()) { if (!this.stream.isSendScreen()) { - if (platform['isIonicIos'] || platform.name === 'Safari') { + if (platform.isIonicIos() || platform.isSafariBrowser()) { // iOS Ionic or Safari. Limitation: cannot set videoDimensions directly, as the videoReference is not loaded // if not added to DOM. Must add it to DOM and wait for videoWidth and videoHeight properties to be defined @@ -436,7 +440,7 @@ export class Publisher extends StreamManager { // Orientation must be checked for mobile devices (width and height are reversed) const { width, height } = this.getVideoDimensions(mediaStream); - if ((platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') && (window.innerHeight > window.innerWidth)) { + if (platform.isMobileDevice() && (window.innerHeight > window.innerWidth)) { // Mobile portrait mode this.stream.videoDimensions = { width: height || 0, @@ -460,8 +464,8 @@ export class Publisher extends StreamManager { }; this.screenShareResizeInterval = setInterval(() => { const firefoxSettings = mediaStream.getVideoTracks()[0].getSettings(); - const newWidth = (platform.name === 'Chrome' || platform.name === 'Opera') ? this.videoReference.videoWidth : firefoxSettings.width; - const newHeight = (platform.name === 'Chrome' || platform.name === 'Opera') ? this.videoReference.videoHeight : firefoxSettings.height; + const newWidth = (platform.isChromeBrowser() || platform.isOperaBrowser()) ? this.videoReference.videoWidth : firefoxSettings.width; + const newHeight = (platform.isChromeBrowser() || platform.isOperaBrowser()) ? this.videoReference.videoHeight : firefoxSettings.height; if (this.stream.isLocalStreamPublished && (newWidth !== this.stream.videoDimensions.width || newHeight !== this.stream.videoDimensions.height)) { @@ -631,7 +635,7 @@ export class Publisher extends StreamManager { startTime = Date.now(); this.setPermissionDialogTimer(timeForDialogEvent); - if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') { + if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) { navigator.mediaDevices['getDisplayMedia']({ video: true }) .then(mediaStream => { this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream); @@ -680,7 +684,7 @@ export class Publisher extends StreamManager { initializeVideoReference(mediaStream: MediaStream) { this.videoReference = document.createElement('video'); - if (platform.name === 'Safari') { + if (platform.isSafariBrowser()) { this.videoReference.setAttribute('playsinline', 'true'); } diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index e424ea2d..31376bc2 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -40,15 +40,19 @@ import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPro import { NetworkQualityLevelChangedEvent } from '../OpenViduInternal/Events/NetworkQualityLevelChangedEvent'; import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; - -import platform = require('platform'); import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; +import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; /** * @hidden */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); + /** * Represents a video call. It can also be seen as a videoconference room where multiple users can connect. * Participants who publish their videos to a session can be seen by the rest of users connected to that specific session. @@ -193,7 +197,7 @@ export class Session extends EventDispatcher { reject(error); }); } else { - reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.name + ' (version ' + platform.version + ') for ' + platform.os!!.family + ' is not supported in OpenVidu')); + reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.getName() + ' (version ' + platform.getVersion() + ') for ' + platform.getFamily() + ' is not supported in OpenVidu')); } }); } @@ -1122,7 +1126,7 @@ export class Session extends EventDispatcher { const joinParams = { token: (!!token) ? token : '', session: this.sessionId, - platform: !!platform.description ? platform.description : 'unknown', + platform: !!platform.getDescription() ? platform.getDescription() : 'unknown', metadata: !!this.options.metadata ? this.options.metadata : '', secret: this.openvidu.getSecret(), recorder: this.openvidu.getRecorder() @@ -1132,10 +1136,10 @@ export class Session extends EventDispatcher { sendVideoData(streamManager: StreamManager, intervalSeconds: number = 1) { if( - this.openvidu.isChromeBrowser() || this.openvidu.isChromeMobileBrowser() || this.openvidu.isOperaBrowser() || - this.openvidu.isOperaMobileBrowser() || this.openvidu.isElectron() || this.openvidu.isSafariBrowser() || - this.openvidu.isAndroidBrowser() || this.openvidu.isSamsungBrowser() || - (this.openvidu.isIPhoneOrIPad() && this.openvidu.isIOSWithSafari()) + platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || + platform.isOperaMobileBrowser() || platform.isElectron() || platform.isSafariBrowser() || + platform.isAndroidBrowser() || platform.isSamsungBrowser() || + (platform.isIPhoneOrIPad() && platform.isIOSWithSafari()) ) { setTimeout(async () => { const statsMap = await streamManager.stream.getWebRtcPeer().pc.getStats(); @@ -1154,7 +1158,7 @@ export class Session extends EventDispatcher { } }); }, intervalSeconds * 1000); - } else if (this.openvidu.isFirefoxBrowser() || this.openvidu.isFirefoxMobileBrowser()) { + } else if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) { // Basic version for Firefox. It does not support stats this.openvidu.sendRequest('videoData', { height: streamManager.stream.videoDimensions.height, @@ -1167,7 +1171,7 @@ export class Session extends EventDispatcher { } }); } else { - console.error('Browser ' + platform.name + ' (version ' + platform.version + ') for ' + platform.os!!.family + ' is not supported in OpenVidu for Network Quality'); + logger.error('Browser ' + platform.getName() + ' (version ' + platform.getVersion() + ') for ' + platform.getFamily() + ' is not supported in OpenVidu for Network Quality'); } } diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index e4f3cfe5..45702ba8 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -30,19 +30,22 @@ import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpea import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent'; import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent'; import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; +import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; +import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; /** * @hidden */ import hark = require('hark'); -import platform = require('platform'); -import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; /** * @hidden */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); - +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); /** * Represents each one of the media streams available in OpenVidu Server for certain session. @@ -108,7 +111,7 @@ export class Stream extends EventDispatcher { * - `"SCREEN"`: when the video source comes from screen-sharing. * - `"CUSTOM"`: when [[PublisherProperties.videoSource]] has been initialized in the Publisher side with a custom MediaStreamTrack when calling [[OpenVidu.initPublisher]]). * - `"IPCAM"`: when the video source comes from an IP camera participant instead of a regular participant (see [IP cameras](/en/stable/advanced-features/ip-cameras/)). - * + * * If [[hasVideo]] is false, this property is undefined */ typeOfVideo?: string; @@ -537,7 +540,7 @@ export class Stream extends EventDispatcher { */ isSendScreen(): boolean { let screen = this.outboundStreamOpts.publisherProperties.videoSource === 'screen'; - if (platform.name === 'Electron') { + if (platform.isElectron()) { screen = typeof this.outboundStreamOpts.publisherProperties.videoSource === 'string' && this.outboundStreamOpts.publisherProperties.videoSource.startsWith('screen:'); } diff --git a/openvidu-browser/src/OpenVidu/StreamManager.ts b/openvidu-browser/src/OpenVidu/StreamManager.ts index 2025d69b..ae4dbc26 100644 --- a/openvidu-browser/src/OpenVidu/StreamManager.ts +++ b/openvidu-browser/src/OpenVidu/StreamManager.ts @@ -22,14 +22,19 @@ import { Event } from '../OpenViduInternal/Events/Event'; import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent'; import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent'; import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; - -import platform = require('platform'); import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; +import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; + /** * @hidden */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); + /** * Interface in charge of displaying the media streams in the HTML DOM. This wraps any [[Publisher]] and [[Subscriber]] object. * You can insert as many video players fo the same Stream as you want by calling [[StreamManager.addVideoElement]] or @@ -121,7 +126,7 @@ export class StreamManager extends EventDispatcher { id: '', canplayListenerAdded: false }; - if (platform.name === 'Safari') { + if (platform.isSafariBrowser()) { this.firstVideoElement.video.setAttribute('playsinline', 'true'); } this.targetElement = targEl; @@ -379,7 +384,7 @@ export class StreamManager extends EventDispatcher { video.autoplay = true; video.controls = false; - if (platform.name === 'Safari') { + if (platform.isSafariBrowser()) { video.setAttribute('playsinline', 'true'); } @@ -463,7 +468,7 @@ export class StreamManager extends EventDispatcher { updateMediaStream(mediaStream: MediaStream) { this.videos.forEach(streamManagerVideo => { streamManagerVideo.video.srcObject = mediaStream; - if (platform['isIonicIos']) { + if (platform.isIonicIos()) { // iOS Ionic. LIMITATION: must reinsert the video in the DOM for // the media stream to be updated const vParent = streamManagerVideo.video.parentElement; @@ -506,7 +511,7 @@ export class StreamManager extends EventDispatcher { } private mirrorVideo(video): void { - if (!platform['isIonicIos']) { + if (!platform.isIonicIos()) { video.style.transform = 'rotateY(180deg)'; video.style.webkitTransform = 'rotateY(180deg)'; } diff --git a/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts b/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts new file mode 100644 index 00000000..96f429f4 --- /dev/null +++ b/openvidu-browser/src/OpenViduInternal/Utils/Platform.ts @@ -0,0 +1,183 @@ +import platform = require("platform"); + +export class PlatformUtils { + private static instance: PlatformUtils; + private constructor() {} + + static getInstance(): PlatformUtils { + if (!PlatformUtils.instance) { + PlatformUtils.instance = new PlatformUtils(); + } + return PlatformUtils.instance; + } + + public isChromeBrowser(): boolean { + return platform.name === "Chrome"; + } + + /** + * @hidden + */ + public isSafariBrowser(): boolean { + return platform.name === "Safari"; + } + + /** + * @hidden + */ + public isChromeMobileBrowser(): boolean { + return platform.name === "Chrome Mobile"; + } + + /** + * @hidden + */ + public isFirefoxBrowser(): boolean { + return platform.name === "Firefox"; + } + + /** + * @hidden + */ + public isFirefoxMobileBrowser(): boolean { + return platform.name === "Firefox Mobile"; + } + + /** + * @hidden + */ + public isOperaBrowser(): boolean { + return platform.name === "Opera"; + } + + /** + * @hidden + */ + public isOperaMobileBrowser(): boolean { + return platform.name === "Opera Mobile"; + } + + /** + * @hidden + */ + public isAndroidBrowser(): boolean { + return platform.name === "Android Browser"; + } + + /** + * @hidden + */ + public isElectron(): boolean { + return platform.name === "Electron"; + } + + /** + * @hidden + */ + public isSamsungBrowser(): boolean { + return ( + platform.name === "Samsung Internet Mobile" || + platform.name === "Samsung Internet" + ); + } + + /** + * @hidden + */ + public isIPhoneOrIPad(): boolean { + const userAgent = !!platform.ua ? platform.ua : navigator.userAgent; + + const isTouchable = "ontouchend" in document; + const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable; + const isIPhone = + /\b(\w*iPhone\w*)\b/.test(userAgent) && + /\b(\w*Mobile\w*)\b/.test(userAgent) && + isTouchable; + + return isIPad || isIPhone; + } + + /** + * @hidden + */ + public isIOSWithSafari(): boolean { + const userAgent = !!platform.ua ? platform.ua : navigator.userAgent; + return ( + /\b(\w*Apple\w*)\b/.test(navigator.vendor) && + /\b(\w*Safari\w*)\b/.test(userAgent) && + !/\b(\w*CriOS\w*)\b/.test(userAgent) && + !/\b(\w*FxiOS\w*)\b/.test(userAgent) + ); + } + + /** + * @hidden + */ + public isIonicIos(): boolean { + return this.isIPhoneOrIPad() && platform.ua!!.indexOf("Safari") === -1; + } + + /** + * @hidden + */ + public isIonicAndroid(): boolean { + return ( + platform.os!!.family === "Android" && platform.name == "Android Browser" + ); + } + + /** + * @hidden + */ + public isMobileDevice(): boolean { + return platform.os!!.family === "iOS" || platform.os!!.family === "Android"; + } + + /** + * @hidden + */ + public canScreenShare(): boolean { + const version = platform?.version ? parseFloat(platform.version) : -1; + + // Reject mobile devices + if (this.isMobileDevice()) { + return false; + } + + return ( + this.isChromeBrowser() || + this.isFirefoxBrowser() || + this.isOperaBrowser() || + this.isElectron() || + (this.isSafariBrowser() && version >= 13) + ); + } + + /** + * @hidden + */ + public getName(): string { + return platform.name || ""; + } + + /** + * @hidden + */ + public getVersion(): string { + return platform.version || ""; + } + + /** + * @hidden + */ + public getFamily(): string { + return platform.os!!.family || ""; + } + + /** + * @hidden + */ + public getDescription(): string { + return platform.description || ""; + } +} diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts index 42e161ae..cefdb392 100644 --- a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts +++ b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts @@ -17,12 +17,17 @@ import freeice = require('freeice'); import uuid = require('uuid'); -import platform = require('platform'); import { OpenViduLogger } from '../Logger/OpenViduLogger'; +import { PlatformUtils } from '../Utils/Platform'; + /** * @hidden */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); export interface WebRtcPeerConfiguration { @@ -139,7 +144,7 @@ export class WebRtcPeer { logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints)); - if (platform.name === 'Safari' && platform.ua!!.indexOf('Safari') !== -1) { + if (platform.isSafariBrowser() && !platform.isIonicIos()) { // Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental if (offerAudio) { this.pc.addTransceiver('audio', { @@ -217,7 +222,7 @@ export class WebRtcPeer { * @hidden */ setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike | undefined) => void, reject: (reason?: any) => void) { - if (platform['isIonicIos']) { + if (platform.isIonicIos()) { // Ionic iOS platform if (needsTimeoutOnProcessAnswer) { // 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts b/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts index 502adc0e..5007192e 100644 --- a/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts +++ b/openvidu-browser/src/OpenViduInternal/WebRtcStats/WebRtcStats.ts @@ -18,13 +18,16 @@ // tslint:disable:no-string-literal import { Stream } from '../../OpenVidu/Stream'; -import platform = require('platform'); import { OpenViduLogger } from '../Logger/OpenViduLogger'; +import { PlatformUtils } from '../Utils/Platform'; /** * @hidden */ const logger: OpenViduLogger = OpenViduLogger.getInstance(); - +/** + * @hidden + */ +const platform: PlatformUtils = PlatformUtils.getInstance(); export class WebRtcStats { @@ -103,7 +106,7 @@ export class WebRtcStats { return new Promise((resolve, reject) => { this.getStatsAgnostic(this.stream.getRTCPeerConnection(), (stats) => { - if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) { + if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) { let localCandidateId, remoteCandidateId, googCandidatePair; const localCandidates = {}; const remoteCandidates = {}; @@ -181,7 +184,7 @@ export class WebRtcStats { const f = (stats) => { - if (platform.name!.indexOf('Firefox') !== -1) { + if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) { stats.forEach((stat) => { let json = {}; @@ -278,7 +281,7 @@ export class WebRtcStats { sendPost(JSON.stringify(json)); } }); - } else if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) { + } else if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) { for (const key of Object.keys(stats)) { const stat = stats[key]; if (stat.type === 'ssrc') { @@ -377,7 +380,7 @@ export class WebRtcStats { logger.log(response); const standardReport = {}; - if (platform.name!.indexOf('Firefox') !== -1) { + if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) { Object.keys(response).forEach(key => { logger.log(response[key]); }); @@ -400,13 +403,13 @@ export class WebRtcStats { } private getStatsAgnostic(pc, successCb, failureCb) { - if (platform.name!.indexOf('Firefox') !== -1) { + if (platform.isFirefoxBrowser() || platform.isFirefoxMobileBrowser()) { // getStats takes args in different order in Chrome and Firefox return pc.getStats(null).then(response => { const report = this.standardizeReport(response); successCb(report); }).catch(failureCb); - } else if ((platform.name!.indexOf('Chrome') !== -1) || (platform.name!.indexOf('Opera') !== -1)) { + } else if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) { // In Chrome, the first two arguments are reversed return pc.getStats((response) => { const report = this.standardizeReport(response);