UNPKG

shaka-player

Version:
316 lines (278 loc) 8.59 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.util.Platform'); goog.require('shaka.util.Timer'); /** * A wrapper for platform-specific functions. * * @final */ shaka.util.Platform = class { /** * Check if the current platform supports media source. We assume that if * the current platform supports media source, then we can use media source * as per its design. * * @return {boolean} */ static supportsMediaSource() { // Browsers that lack a media source implementation will have no reference // to |window.MediaSource|. Platforms that we see having problematic media // source implementations will have this reference removed via a polyfill. if (!window.MediaSource) { return false; } // Some very old MediaSource implementations didn't have isTypeSupported. if (!MediaSource.isTypeSupported) { return false; } return true; } /** * Returns true if the media type is supported natively by the platform. * * @param {string} mimeType * @return {boolean} */ static supportsMediaType(mimeType) { const video = shaka.util.Platform.anyMediaElement(); return video.canPlayType(mimeType) != ''; } /** * Check if the current platform is MS Edge. * * @return {boolean} */ static isEdge() { // Legacy Edge contains "Edge/version". // Chromium-based Edge contains "Edg/version" (no "e"). if (navigator.userAgent.match(/Edge?\//)) { return true; } return false; } /** * Check if the current platform is Legacy Edge. * * @return {boolean} */ static isLegacyEdge() { // Legacy Edge contains "Edge/version". // Chromium-based Edge contains "Edg/version" (no "e"). if (navigator.userAgent.match(/Edge\//)) { return true; } return false; } /** * Check if the current platform is MS IE. * * @return {boolean} */ static isIE() { return shaka.util.Platform.userAgentContains_('Trident/'); } /** * Check if the current platform is an Xbox One. * * @return {boolean} */ static isXboxOne() { return shaka.util.Platform.userAgentContains_('Xbox One'); } /** * Check if the current platform is a Tizen TV. * * @return {boolean} */ static isTizen() { return shaka.util.Platform.userAgentContains_('Tizen'); } /** * Check if the current platform is a Tizen 4 TV. * * @return {boolean} */ static isTizen4() { return shaka.util.Platform.userAgentContains_('Tizen 4'); } /** * Check if the current platform is a Tizen 3 TV. * * @return {boolean} */ static isTizen3() { return shaka.util.Platform.userAgentContains_('Tizen 3'); } /** * Check if the current platform is a Tizen 2 TV. * * @return {boolean} */ static isTizen2() { return shaka.util.Platform.userAgentContains_('Tizen 2'); } /** * Check if the current platform is a WebOS. * * @return {boolean} */ static isWebOS() { return shaka.util.Platform.userAgentContains_('Web0S'); } /** * Check if the current platform is a Google Chromecast. * * @return {boolean} */ static isChromecast() { return shaka.util.Platform.userAgentContains_('CrKey'); } /** * Check if the current platform is Google Chrome. * * @return {boolean} */ static isChrome() { // The Edge user agent will also contain the "Chrome" keyword, so we need // to make sure this is not Edge. return shaka.util.Platform.userAgentContains_('Chrome') && !shaka.util.Platform.isEdge(); } /** * Check if the current platform is from Apple. * * Returns true on all iOS browsers and on desktop Safari. * * Returns false for non-Safari browsers on macOS, which are independent of * Apple. * * @return {boolean} */ static isApple() { return !!navigator.vendor && navigator.vendor.includes('Apple') && !shaka.util.Platform.isTizen(); } /** * Returns a major version number for Safari, or Safari-based iOS browsers. * * For example: * - Safari 13.0.4 on macOS returns 13. * - Safari on iOS 13.3.1 returns 13. * - Chrome on iOS 13.3.1 returns 13 (since this is based on Safari/WebKit). * - Chrome on macOS returns null (since this is independent of Apple). * * Returns null on Firefox on iOS, where this version information is not * available. * * @return {?number} A major version number or null if not iOS. */ static safariVersion() { // All iOS browsers and desktop Safari will return true for isApple(). if (!shaka.util.Platform.isApple()) { return null; } // This works for iOS Safari and desktop Safari, which contain something // like "Version/13.0" indicating the major Safari or iOS version. let match = navigator.userAgent.match(/Version\/(\d+)/); if (match) { return parseInt(match[1], /* base= */ 10); } // This works for all other browsers on iOS, which contain something like // "OS 13_3" indicating the major & minor iOS version. match = navigator.userAgent.match(/OS (\d+)(?:_\d+)?/); if (match) { return parseInt(match[1], /* base= */ 10); } return null; } /** * Guesses if the platform is a mobile one (iOS or Android). * * @return {boolean} */ static isMobile() { if (/(?:iPhone|iPad|iPod|Android)/.test(navigator.userAgent)) { // This is Android, iOS, or iPad < 13. return true; } // Starting with iOS 13 on iPad, the user agent string no longer has the // word "iPad" in it. It looks very similar to desktop Safari. This seems // to be intentional on Apple's part. // See: https://forums.developer.apple.com/thread/119186 // // So if it's an Apple device with multi-touch support, assume it's a mobile // device. If some future iOS version starts masking their user agent on // both iPhone & iPad, this clause should still work. If a future // multi-touch desktop Mac is released, this will need some adjustment. // // As of January 2020, this is mainly used to adjust the default UI config // for mobile devices, so it's low risk if something changes to break this // detection. return shaka.util.Platform.isApple() && navigator.maxTouchPoints > 1; } /** * Check if the user agent contains a key. This is the best way we know of * right now to detect platforms. If there is a better way, please send a * PR. * * @param {string} key * @return {boolean} * @private */ static userAgentContains_(key) { const userAgent = navigator.userAgent || ''; return userAgent.includes(key); } /** * For canPlayType queries, we just need any instance. * * First, use a cached element from a previous query. * Second, search the page for one. * Third, create a temporary one. * * Cached elements expire in one second so that they can be GC'd or removed. * * @return {!HTMLMediaElement} */ static anyMediaElement() { const Platform = shaka.util.Platform; if (Platform.cachedMediaElement_) { return Platform.cachedMediaElement_; } if (!Platform.cacheExpirationTimer_) { Platform.cacheExpirationTimer_ = new shaka.util.Timer(() => { Platform.cachedMediaElement_ = null; }); } Platform.cachedMediaElement_ = /** @type {HTMLMediaElement} */( document.getElementsByTagName('video')[0] || document.getElementsByTagName('audio')[0]); if (!Platform.cachedMediaElement_) { Platform.cachedMediaElement_ = /** @type {!HTMLMediaElement} */( document.createElement('video')); } Platform.cacheExpirationTimer_.tickAfter(/* seconds= */ 1); return Platform.cachedMediaElement_; } /** * Returns true if the platform requires encryption information in all init * segments. For such platforms, MediaSourceEngine will attempt to work * around a lack of such info by inserting fake encryption information into * initialization segments. * * @return {boolean} * @see https://github.com/google/shaka-player/issues/2759 */ static requiresEncryptionInfoInAllInitSegments() { const Platform = shaka.util.Platform; return Platform.isTizen() || Platform.isXboxOne(); } }; /** @private {shaka.util.Timer} */ shaka.util.Platform.cacheExpirationTimer_ = null; /** @private {HTMLMediaElement} */ shaka.util.Platform.cachedMediaElement_ = null;