Source: lib/device/xbox.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2025 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.device.Xbox');
  7. goog.require('shaka.device.AbstractDevice');
  8. goog.require('shaka.device.DeviceFactory');
  9. goog.require('shaka.device.IDevice');
  10. goog.require('shaka.log');
  11. /**
  12. * @final
  13. */
  14. shaka.device.Xbox = class extends shaka.device.AbstractDevice {
  15. constructor() {
  16. super();
  17. /** @private {boolean} */
  18. this.isLegacyEdge_ = navigator.userAgent.includes('Edge/');
  19. // Looking for something like "Edg/106.0.0.0".
  20. const match = navigator.userAgent.match(/Edge?\/(\d+)/);
  21. /** @private {?number} */
  22. this.version_ = match ? parseInt(match[1], /* base= */ 10) : null;
  23. }
  24. /**
  25. * @override
  26. */
  27. getVersion() {
  28. return this.version_;
  29. }
  30. /**
  31. * @override
  32. */
  33. getDeviceName() {
  34. return 'Xbox';
  35. }
  36. /**
  37. * @override
  38. */
  39. getDeviceType() {
  40. return shaka.device.IDevice.DeviceType.CONSOLE;
  41. }
  42. /**
  43. * @override
  44. */
  45. getBrowserEngine() {
  46. return this.isLegacyEdge_ ? shaka.device.IDevice.BrowserEngine.EDGE :
  47. shaka.device.IDevice.BrowserEngine.CHROMIUM;
  48. }
  49. /**
  50. * @override
  51. */
  52. requiresEncryptionInfoInAllInitSegments(keySystem) {
  53. return true;
  54. }
  55. /**
  56. * @override
  57. */
  58. insertEncryptionDataBeforeClear() {
  59. return true;
  60. }
  61. /**
  62. * @override
  63. */
  64. shouldOverrideDolbyVisionCodecs() {
  65. return this.isLegacyEdge_;
  66. }
  67. /**
  68. * @override
  69. */
  70. detectMaxHardwareResolution() {
  71. const maxResolution = {width: 1920, height: 1080};
  72. const winRT = shaka.device.Xbox.getWinRT_();
  73. if (winRT) {
  74. try {
  75. const protectionCapabilities =
  76. new winRT.Media.Protection.ProtectionCapabilities();
  77. const protectionResult =
  78. winRT.Media.Protection.ProtectionCapabilityResult;
  79. // isTypeSupported may return "maybe", which means the operation
  80. // is not completed. This means we need to retry
  81. // https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
  82. let result = null;
  83. const type =
  84. 'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
  85. 'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
  86. 'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
  87. 'display-bpc=8"';
  88. const keySystem = 'com.microsoft.playready.recommendation';
  89. do {
  90. result = protectionCapabilities.isTypeSupported(type, keySystem);
  91. } while (result === protectionResult.maybe);
  92. if (result === protectionResult.probably) {
  93. maxResolution.width = 3840;
  94. maxResolution.height = 2160;
  95. }
  96. } catch (e) {
  97. shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
  98. 'screen size 1920x1080.');
  99. }
  100. }
  101. return Promise.resolve(maxResolution);
  102. }
  103. /**
  104. * @override
  105. */
  106. adjustConfig(config) {
  107. super.adjustConfig(config);
  108. // The Xbox One browser does not detect DRM key changes signalled by a
  109. // change in the PSSH in media segments. We need to parse PSSH from media
  110. // segments to detect key changes.
  111. config.drm.parseInbandPsshEnabled = this.isLegacyEdge_;
  112. // The Xbox only supports the Playready DRM, so it should
  113. // prefer that key system by default to improve startup performance.
  114. config.drm.preferredKeySystems.push('com.microsoft.playready');
  115. if (this.isLegacyEdge_) {
  116. config.streaming.gapPadding = 0.01;
  117. }
  118. return config;
  119. }
  120. /**
  121. * @override
  122. */
  123. supportsOfflineStorage() {
  124. return false;
  125. }
  126. /**
  127. * @return {?WinRT}
  128. * @private
  129. */
  130. static getWinRT_() {
  131. let winRT = null;
  132. try {
  133. // Try to access to WinRT for WebView, if it's not defined,
  134. // try to access to WinRT for WebView2, if it's not defined either,
  135. // let it throw.
  136. if (typeof Windows !== 'undefined') {
  137. winRT = Windows;
  138. } else {
  139. winRT = chrome.webview.hostObjects.sync.Windows;
  140. }
  141. } catch (e) {}
  142. return winRT;
  143. }
  144. /**
  145. * Check if the current platform is an Xbox One.
  146. *
  147. * @return {boolean}
  148. * @private
  149. */
  150. static isXbox_() {
  151. return navigator.userAgent.includes('Xbox One') ||
  152. shaka.device.Xbox.getWinRT_() !== null;
  153. }
  154. };
  155. if (shaka.device.Xbox.isXbox_()) {
  156. shaka.device.DeviceFactory.registerDeviceFactory(
  157. () => new shaka.device.Xbox());
  158. }