Source: lib/device/apple_browser.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2025 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.device.AppleBrowser');
  7. goog.require('shaka.device.AbstractDevice');
  8. goog.require('shaka.device.DeviceFactory');
  9. goog.require('shaka.device.IDevice');
  10. goog.require('shaka.util.Lazy');
  11. /**
  12. * @final
  13. */
  14. shaka.device.AppleBrowser = class extends shaka.device.AbstractDevice {
  15. constructor() {
  16. super();
  17. /** @private {!shaka.util.Lazy<?number>} */
  18. this.version_ = new shaka.util.Lazy(() => {
  19. // This works for iOS Safari and desktop Safari, which contain something
  20. // like "Version/13.0" indicating the major Safari or iOS version.
  21. let match = navigator.userAgent.match(/Version\/(\d+)/);
  22. if (match) {
  23. return parseInt(match[1], /* base= */ 10);
  24. }
  25. // This works for all other browsers on iOS, which contain something like
  26. // "OS 13_3" indicating the major & minor iOS version.
  27. match = navigator.userAgent.match(/OS (\d+)(?:_\d+)?/);
  28. if (match) {
  29. return parseInt(match[1], /* base= */ 10);
  30. }
  31. return null;
  32. });
  33. /** @private {!shaka.util.Lazy<!shaka.device.IDevice.DeviceType>} */
  34. this.deviceType_ = new shaka.util.Lazy(() => {
  35. if (/(?:iPhone|iPad|iPod)/.test(navigator.userAgent) ||
  36. navigator.maxTouchPoints > 1) {
  37. if ('xr' in navigator) {
  38. return shaka.device.IDevice.DeviceType.VR;
  39. }
  40. return shaka.device.IDevice.DeviceType.MOBILE;
  41. }
  42. if ('xr' in navigator) {
  43. return shaka.device.IDevice.DeviceType.VR;
  44. }
  45. return shaka.device.IDevice.DeviceType.DESKTOP;
  46. });
  47. }
  48. /**
  49. * @override
  50. */
  51. getVersion() {
  52. return this.version_.value();
  53. }
  54. /**
  55. * @override
  56. */
  57. getDeviceName() {
  58. return 'Apple Browser';
  59. }
  60. /**
  61. * @override
  62. */
  63. getDeviceType() {
  64. return this.deviceType_.value();
  65. }
  66. /**
  67. * @override
  68. */
  69. getBrowserEngine() {
  70. return shaka.device.IDevice.BrowserEngine.WEBKIT;
  71. }
  72. /**
  73. * @override
  74. */
  75. supportsMediaCapabilities() {
  76. return false;
  77. }
  78. /**
  79. * @override
  80. */
  81. requiresEncryptionInfoInAllInitSegments(keySystem, contentType) {
  82. return contentType === 'audio';
  83. }
  84. /**
  85. * @override
  86. */
  87. insertEncryptionDataBeforeClear() {
  88. return true;
  89. }
  90. /**
  91. * @override
  92. */
  93. requiresTfhdFix(contentType) {
  94. return contentType === 'audio';
  95. }
  96. /**
  97. * @override
  98. */
  99. adjustConfig(config) {
  100. super.adjustConfig(config);
  101. config.abr.minTimeToSwitch = 0.5;
  102. return config;
  103. }
  104. /**
  105. * @override
  106. */
  107. supportsAirPlay() {
  108. return true;
  109. }
  110. /**
  111. * @return {boolean}
  112. * @private
  113. */
  114. static isAppleBrowser_() {
  115. if (!(navigator.vendor || '').includes('Apple')) {
  116. return false;
  117. }
  118. if (/(?:iPhone|iPad|iPod)/.test(navigator.userAgent) ||
  119. navigator.maxTouchPoints > 1) {
  120. return true;
  121. }
  122. if (navigator.userAgentData && navigator.userAgentData.platform &&
  123. navigator.userAgentData.platform.toLowerCase() == 'macos') {
  124. return true;
  125. } else if (navigator.platform &&
  126. navigator.platform.toLowerCase().includes('mac')) {
  127. return true;
  128. }
  129. return false;
  130. }
  131. };
  132. if (shaka.device.AppleBrowser.isAppleBrowser_()) {
  133. shaka.device.DeviceFactory.registerDeviceFactory(
  134. () => new shaka.device.AppleBrowser());
  135. }