Source: lib/polyfill/media_capabilities.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.polyfill.MediaCapabilities');
  7. goog.require('shaka.log');
  8. goog.require('shaka.polyfill');
  9. goog.require('shaka.util.Platform');
  10. /**
  11. * @summary A polyfill to provide navigator.mediaCapabilities on all browsers.
  12. * This is necessary for Tizen 3, Xbox One and possibly others we have yet to
  13. * discover.
  14. * @export
  15. */
  16. shaka.polyfill.MediaCapabilities = class {
  17. /**
  18. * Install the polyfill if needed.
  19. * @suppress {const}
  20. * @export
  21. */
  22. static install() {
  23. // Since MediaCapabilities is not fully supported on Chromecast yet, we
  24. // should always install polyfill for Chromecast.
  25. // TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
  26. // Since MediaCapabilities implementation is buggy in Apple browsers, we
  27. // should always install polyfill for Apple browsers.
  28. // See: https://github.com/shaka-project/shaka-player/issues/3530
  29. // TODO: re-evaluate MediaCapabilities in the future versions of Apple
  30. // Browsers.
  31. // Since MediaCapabilities implementation is buggy in PS5 browsers, we
  32. // should always install polyfill for PS5 browsers.
  33. // See: https://github.com/shaka-project/shaka-player/issues/3582
  34. // TODO: re-evaluate MediaCapabilities in the future versions of PS5
  35. // Browsers.
  36. if (!shaka.util.Platform.isChromecast() &&
  37. !shaka.util.Platform.isApple() &&
  38. !shaka.util.Platform.isPS5() &&
  39. navigator.mediaCapabilities) {
  40. shaka.log.info(
  41. 'MediaCapabilities: Native mediaCapabilities support found.');
  42. return;
  43. }
  44. shaka.log.info('MediaCapabilities: install');
  45. if (!navigator.mediaCapabilities) {
  46. navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
  47. }
  48. // Keep the patched MediaCapabilities object from being garbage-collected in
  49. // Safari.
  50. // See https://github.com/shaka-project/shaka-player/issues/3696#issuecomment-1009472718
  51. shaka.polyfill.MediaCapabilities.originalMcap =
  52. navigator.mediaCapabilities;
  53. navigator.mediaCapabilities.decodingInfo =
  54. shaka.polyfill.MediaCapabilities.decodingInfo_;
  55. }
  56. /**
  57. * @param {!MediaDecodingConfiguration} mediaDecodingConfig
  58. * @return {!Promise.<!MediaCapabilitiesDecodingInfo>}
  59. * @private
  60. */
  61. static async decodingInfo_(mediaDecodingConfig) {
  62. const res = {
  63. supported: false,
  64. powerEfficient: true,
  65. smooth: true,
  66. keySystemAccess: null,
  67. configuration: mediaDecodingConfig,
  68. };
  69. if (!mediaDecodingConfig) {
  70. return res;
  71. }
  72. if (mediaDecodingConfig.type == 'media-source') {
  73. if (!shaka.util.Platform.supportsMediaSource()) {
  74. return res;
  75. }
  76. // Use 'MediaSource.isTypeSupported' to check if the stream is supported.
  77. if (mediaDecodingConfig['video']) {
  78. const contentType = mediaDecodingConfig['video'].contentType;
  79. const isSupported = MediaSource.isTypeSupported(contentType);
  80. if (!isSupported) {
  81. return res;
  82. }
  83. }
  84. if (mediaDecodingConfig['audio']) {
  85. const contentType = mediaDecodingConfig['audio'].contentType;
  86. const isSupported = MediaSource.isTypeSupported(contentType);
  87. if (!isSupported) {
  88. return res;
  89. }
  90. }
  91. } else if (mediaDecodingConfig.type == 'file') {
  92. if (mediaDecodingConfig['video']) {
  93. const contentType = mediaDecodingConfig['video'].contentType;
  94. const isSupported = shaka.util.Platform.supportsMediaType(contentType);
  95. if (!isSupported) {
  96. return res;
  97. }
  98. }
  99. if (mediaDecodingConfig['audio']) {
  100. const contentType = mediaDecodingConfig['audio'].contentType;
  101. const isSupported = shaka.util.Platform.supportsMediaType(contentType);
  102. if (!isSupported) {
  103. return res;
  104. }
  105. }
  106. } else {
  107. // Otherwise not supported.
  108. return res;
  109. }
  110. if (!mediaDecodingConfig.keySystemConfiguration) {
  111. // The variant is supported if it's unencrypted.
  112. res.supported = true;
  113. return Promise.resolve(res);
  114. } else {
  115. // Get the MediaKeySystemAccess for the key system.
  116. // Convert the MediaDecodingConfiguration object to a
  117. // MediaKeySystemConfiguration object.
  118. /** @type {MediaCapabilitiesKeySystemConfiguration} */
  119. const mediaCapkeySystemConfig =
  120. mediaDecodingConfig.keySystemConfiguration;
  121. const audioCapabilities = [];
  122. const videoCapabilities = [];
  123. if (mediaCapkeySystemConfig.audio) {
  124. const capability = {
  125. robustness: mediaCapkeySystemConfig.audio.robustness || '',
  126. contentType: mediaDecodingConfig.audio.contentType,
  127. };
  128. audioCapabilities.push(capability);
  129. }
  130. if (mediaCapkeySystemConfig.video) {
  131. const capability = {
  132. robustness: mediaCapkeySystemConfig.video.robustness || '',
  133. contentType: mediaDecodingConfig.video.contentType,
  134. };
  135. videoCapabilities.push(capability);
  136. }
  137. /** @type {MediaKeySystemConfiguration} */
  138. const mediaKeySystemConfig = {
  139. initDataTypes: [mediaCapkeySystemConfig.initDataType],
  140. distinctiveIdentifier: mediaCapkeySystemConfig.distinctiveIdentifier,
  141. persistentState: mediaCapkeySystemConfig.persistentState,
  142. sessionTypes: mediaCapkeySystemConfig.sessionTypes,
  143. };
  144. // Only add the audio video capabilities if they have valid data.
  145. // Otherwise the query will fail.
  146. if (audioCapabilities.length) {
  147. mediaKeySystemConfig.audioCapabilities = audioCapabilities;
  148. }
  149. if (videoCapabilities.length) {
  150. mediaKeySystemConfig.videoCapabilities = videoCapabilities;
  151. }
  152. let keySystemAccess;
  153. try {
  154. keySystemAccess = await navigator.requestMediaKeySystemAccess(
  155. mediaCapkeySystemConfig.keySystem, [mediaKeySystemConfig]);
  156. } catch (e) {
  157. shaka.log.info('navigator.requestMediaKeySystemAccess failed.');
  158. }
  159. if (keySystemAccess) {
  160. res.supported = true;
  161. res.keySystemAccess = keySystemAccess;
  162. }
  163. }
  164. return res;
  165. }
  166. };
  167. /**
  168. * A copy of the MediaCapabilities instance, to prevent Safari from
  169. * garbage-collecting the polyfilled method on it. We make it public and export
  170. * it to ensure that it is not stripped out by the compiler.
  171. *
  172. * @type {MediaCapabilities}
  173. * @export
  174. */
  175. shaka.polyfill.MediaCapabilities.originalMcap = null;
  176. // Install at a lower priority than MediaSource polyfill, so that we have
  177. // MediaSource available first.
  178. shaka.polyfill.register(shaka.polyfill.MediaCapabilities.install, -1);