Source: lib/util/player_configuration.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.util.PlayerConfiguration');
  7. goog.require('goog.asserts');
  8. goog.require('shaka.abr.SimpleAbrManager');
  9. goog.require('shaka.config.AutoShowText');
  10. goog.require('shaka.config.CodecSwitchingStrategy');
  11. goog.require('shaka.log');
  12. goog.require('shaka.media.Capabilities');
  13. goog.require('shaka.net.NetworkingEngine');
  14. goog.require('shaka.util.ConfigUtils');
  15. goog.require('shaka.util.FairPlayUtils');
  16. goog.require('shaka.util.LanguageUtils');
  17. goog.require('shaka.util.ManifestParserUtils');
  18. goog.require('shaka.util.Platform');
  19. /**
  20. * @final
  21. * @export
  22. */
  23. shaka.util.PlayerConfiguration = class {
  24. /**
  25. * @return {shaka.extern.PlayerConfiguration}
  26. * @export
  27. */
  28. static createDefault() {
  29. // This is a relatively safe default in the absence of clues from the
  30. // browser. For slower connections, the default estimate may be too high.
  31. const bandwidthEstimate = 1e6; // 1Mbps
  32. let abrMaxHeight = Infinity;
  33. // Some browsers implement the Network Information API, which allows
  34. // retrieving information about a user's network connection.
  35. if (navigator.connection) {
  36. // If the user has checked a box in the browser to ask it to use less
  37. // data, the browser will expose this intent via connection.saveData.
  38. // When that is true, we will default the max ABR height to 360p. Apps
  39. // can override this if they wish.
  40. //
  41. // The decision to use 360p was somewhat arbitrary. We needed a default
  42. // limit, and rather than restrict to a certain bandwidth, we decided to
  43. // restrict resolution. This will implicitly restrict bandwidth and
  44. // therefore save data. We (Shaka+Chrome) judged that:
  45. // - HD would be inappropriate
  46. // - If a user is asking their browser to save data, 360p it reasonable
  47. // - 360p would not look terrible on small mobile device screen
  48. // We also found that:
  49. // - YouTube's website on mobile defaults to 360p (as of 2018)
  50. // - iPhone 6, in portrait mode, has a physical resolution big enough
  51. // for 360p widescreen, but a little smaller than 480p widescreen
  52. // (https://apple.co/2yze4es)
  53. // If the content's lowest resolution is above 360p, AbrManager will use
  54. // the lowest resolution.
  55. if (navigator.connection.saveData) {
  56. abrMaxHeight = 360;
  57. }
  58. }
  59. const drm = {
  60. retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
  61. // These will all be verified by special cases in mergeConfigObjects_():
  62. servers: {}, // key is arbitrary key system ID, value must be string
  63. clearKeys: {}, // key is arbitrary key system ID, value must be string
  64. advanced: {}, // key is arbitrary key system ID, value is a record type
  65. delayLicenseRequestUntilPlayed: false,
  66. persistentSessionOnlinePlayback: false,
  67. persistentSessionsMetadata: [],
  68. initDataTransform: (initData, initDataType, drmInfo) => {
  69. if (shaka.util.Platform.isMediaKeysPolyfilled() &&
  70. initDataType == 'skd') {
  71. const cert = drmInfo.serverCertificate;
  72. const contentId =
  73. shaka.util.FairPlayUtils.defaultGetContentId(initData);
  74. initData = shaka.util.FairPlayUtils.initDataTransform(
  75. initData, contentId, cert);
  76. }
  77. return initData;
  78. },
  79. logLicenseExchange: false,
  80. updateExpirationTime: 1,
  81. preferredKeySystems: [],
  82. keySystemsMapping: {},
  83. // The Xbox One browser does not detect DRM key changes signalled by a
  84. // change in the PSSH in media segments. We need to parse PSSH from media
  85. // segments to detect key changes.
  86. parseInbandPsshEnabled: shaka.util.Platform.isXboxOne(),
  87. minHdcpVersion: '',
  88. ignoreDuplicateInitData: !shaka.util.Platform.isTizen2(),
  89. };
  90. // The Xbox One and PS4 only support the Playready DRM, so they should
  91. // prefer that key system by default to improve startup performance.
  92. if (shaka.util.Platform.isXboxOne() ||
  93. shaka.util.Platform.isPS4()) {
  94. drm.preferredKeySystems.push('com.microsoft.playready');
  95. }
  96. let codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD;
  97. let multiTypeVariantsAllowed = false;
  98. if (shaka.media.Capabilities.isChangeTypeSupported() &&
  99. shaka.util.Platform.supportsSmoothCodecSwitching()) {
  100. codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.SMOOTH;
  101. multiTypeVariantsAllowed = true;
  102. }
  103. const manifest = {
  104. retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
  105. availabilityWindowOverride: NaN,
  106. disableAudio: false,
  107. disableVideo: false,
  108. disableText: false,
  109. disableThumbnails: false,
  110. defaultPresentationDelay: 0,
  111. segmentRelativeVttTiming: false,
  112. raiseFatalErrorOnManifestUpdateRequestFailure: false,
  113. dash: {
  114. clockSyncUri: '',
  115. ignoreDrmInfo: false,
  116. disableXlinkProcessing: false,
  117. xlinkFailGracefully: false,
  118. ignoreMinBufferTime: false,
  119. autoCorrectDrift: true,
  120. initialSegmentLimit: 1000,
  121. ignoreSuggestedPresentationDelay: false,
  122. ignoreEmptyAdaptationSet: false,
  123. ignoreMaxSegmentDuration: false,
  124. keySystemsByURI: {
  125. 'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b':
  126. 'org.w3.clearkey',
  127. 'urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e':
  128. 'org.w3.clearkey',
  129. 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed':
  130. 'com.widevine.alpha',
  131. 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95':
  132. 'com.microsoft.playready',
  133. 'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95':
  134. 'com.microsoft.playready',
  135. },
  136. manifestPreprocessor:
  137. shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
  138. manifestPreprocessorTXml:
  139. shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
  140. sequenceMode: false,
  141. enableAudioGroups: false,
  142. multiTypeVariantsAllowed,
  143. useStreamOnceInPeriodFlattening: false,
  144. updatePeriod: -1,
  145. enableFastSwitching: true,
  146. },
  147. hls: {
  148. ignoreTextStreamFailures: false,
  149. ignoreImageStreamFailures: false,
  150. defaultAudioCodec: 'mp4a.40.2',
  151. defaultVideoCodec: 'avc1.42E01E',
  152. ignoreManifestProgramDateTime: false,
  153. ignoreManifestProgramDateTimeForTypes: [],
  154. mediaPlaylistFullMimeType:
  155. 'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
  156. useSafariBehaviorForLive: true,
  157. liveSegmentsDelay: 3,
  158. sequenceMode: shaka.util.Platform.supportsSequenceMode(),
  159. ignoreManifestTimestampsInSegmentsMode: false,
  160. disableCodecGuessing: false,
  161. disableClosedCaptionsDetection: false,
  162. allowLowLatencyByteRangeOptimization: true,
  163. },
  164. mss: {
  165. manifestPreprocessor:
  166. shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
  167. manifestPreprocessorTXml:
  168. shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
  169. sequenceMode: false,
  170. keySystemsBySystemId: {
  171. '9a04f079-9840-4286-ab92-e65be0885f95':
  172. 'com.microsoft.playready',
  173. '79f0049a-4098-8642-ab92-e65be0885f95':
  174. 'com.microsoft.playready',
  175. },
  176. },
  177. };
  178. const streaming = {
  179. retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
  180. // Need some operation in the callback or else closure may remove calls
  181. // to the function as it would be a no-op. The operation can't just be a
  182. // log message, because those are stripped in the compiled build.
  183. failureCallback: (error) => {
  184. shaka.log.error('Unhandled streaming error', error);
  185. return shaka.util.ConfigUtils.referenceParametersAndReturn(
  186. [error],
  187. undefined);
  188. },
  189. // When low latency streaming is enabled, rebufferingGoal will default to
  190. // 0.01 if not specified.
  191. rebufferingGoal: 2,
  192. bufferingGoal: 10,
  193. bufferBehind: 30,
  194. evictionGoal: 1,
  195. ignoreTextStreamFailures: false,
  196. alwaysStreamText: false,
  197. startAtSegmentBoundary: false,
  198. gapDetectionThreshold: 0.5,
  199. gapPadding: 0.01,
  200. gapJumpTimerTime: 0.25 /* seconds */,
  201. durationBackoff: 1,
  202. // Offset by 5 seconds since Chromecast takes a few seconds to start
  203. // playing after a seek, even when buffered.
  204. safeSeekOffset: 5,
  205. stallEnabled: true,
  206. stallThreshold: 1 /* seconds */,
  207. stallSkip: 0.1 /* seconds */,
  208. useNativeHlsOnSafari: true,
  209. useNativeHlsForFairPlay: true,
  210. // If we are within 2 seconds of the start of a live segment, fetch the
  211. // previous one. This allows for segment drift, but won't download an
  212. // extra segment if we aren't close to the start.
  213. // When low latency streaming is enabled, inaccurateManifestTolerance
  214. // will default to 0 if not specified.
  215. inaccurateManifestTolerance: 2,
  216. lowLatencyMode: false,
  217. autoLowLatencyMode: false,
  218. forceHTTP: false,
  219. forceHTTPS: false,
  220. preferNativeHls: false,
  221. updateIntervalSeconds: 1,
  222. dispatchAllEmsgBoxes: false,
  223. observeQualityChanges: false,
  224. maxDisabledTime: 30,
  225. parsePrftBox: false,
  226. // When low latency streaming is enabled, segmentPrefetchLimit will
  227. // default to 2 if not specified.
  228. segmentPrefetchLimit: 0,
  229. prefetchAudioLanguages: [],
  230. disableAudioPrefetch: false,
  231. disableTextPrefetch: false,
  232. disableVideoPrefetch: false,
  233. liveSync: false,
  234. liveSyncTargetLatencyTolerance: 0.5,
  235. liveSyncMaxLatency: 1,
  236. liveSyncPlaybackRate: 1.1,
  237. liveSyncMinLatency: 0,
  238. liveSyncMinPlaybackRate: 0.95,
  239. liveSyncPanicMode: false,
  240. liveSyncPanicThreshold: 60,
  241. allowMediaSourceRecoveries: true,
  242. minTimeBetweenRecoveries: 5,
  243. vodDynamicPlaybackRate: false,
  244. vodDynamicPlaybackRateLowBufferRate: 0.95,
  245. vodDynamicPlaybackRateBufferRatio: 0.5,
  246. infiniteLiveStreamDuration: false,
  247. preloadNextUrlWindow: 30,
  248. loadTimeout: 30,
  249. clearDecodingCache: shaka.util.Platform.isPS4() ||
  250. shaka.util.Platform.isPS5(),
  251. };
  252. // WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
  253. // that respond slowly to seeking.
  254. // Therefore we should not seek when we detect a stall
  255. // on one of these platforms. Instead, default stallSkip to 0 to force the
  256. // stall detector to pause and play instead.
  257. if (shaka.util.Platform.isWebOS() ||
  258. shaka.util.Platform.isTizen() ||
  259. shaka.util.Platform.isChromecast() ||
  260. shaka.util.Platform.isHisense()) {
  261. streaming.stallSkip = 0;
  262. }
  263. if (shaka.util.Platform.isTizen()) {
  264. streaming.gapPadding = 2;
  265. }
  266. const offline = {
  267. // We need to set this to a throw-away implementation for now as our
  268. // default implementation will need to reference other fields in the
  269. // config. We will set it to our intended implementation after we have
  270. // the top-level object created.
  271. // eslint-disable-next-line require-await
  272. trackSelectionCallback: async (tracks) => tracks,
  273. downloadSizeCallback: async (sizeEstimate) => {
  274. if (navigator.storage && navigator.storage.estimate) {
  275. const estimate = await navigator.storage.estimate();
  276. // Limit to 95% of quota.
  277. return estimate.usage + sizeEstimate < estimate.quota * 0.95;
  278. } else {
  279. return true;
  280. }
  281. },
  282. // Need some operation in the callback or else closure may remove calls
  283. // to the function as it would be a no-op. The operation can't just be a
  284. // log message, because those are stripped in the compiled build.
  285. progressCallback: (content, progress) => {
  286. return shaka.util.ConfigUtils.referenceParametersAndReturn(
  287. [content, progress],
  288. undefined);
  289. },
  290. // By default we use persistent licenses as forces errors to surface if
  291. // a platform does not support offline licenses rather than causing
  292. // unexpected behaviours when someone tries to plays downloaded content
  293. // without a persistent license.
  294. usePersistentLicense: true,
  295. numberOfParallelDownloads: 5,
  296. };
  297. const abr = {
  298. enabled: true,
  299. useNetworkInformation: true,
  300. defaultBandwidthEstimate: bandwidthEstimate,
  301. switchInterval: 8,
  302. bandwidthUpgradeTarget: 0.85,
  303. bandwidthDowngradeTarget: 0.95,
  304. restrictions: {
  305. minWidth: 0,
  306. maxWidth: Infinity,
  307. minHeight: 0,
  308. maxHeight: abrMaxHeight,
  309. minPixels: 0,
  310. maxPixels: Infinity,
  311. minFrameRate: 0,
  312. maxFrameRate: Infinity,
  313. minBandwidth: 0,
  314. maxBandwidth: Infinity,
  315. minChannelsCount: 0,
  316. maxChannelsCount: Infinity,
  317. },
  318. advanced: {
  319. minTotalBytes: 128e3,
  320. minBytes: 16e3,
  321. fastHalfLife: 2,
  322. slowHalfLife: 5,
  323. },
  324. restrictToElementSize: false,
  325. restrictToScreenSize: false,
  326. ignoreDevicePixelRatio: false,
  327. clearBufferSwitch: false,
  328. safeMarginSwitch: 0,
  329. cacheLoadThreshold: 20,
  330. minTimeToSwitch: shaka.util.Platform.isApple() ? 0.5 : 0,
  331. };
  332. const cmcd = {
  333. enabled: false,
  334. sessionId: '',
  335. contentId: '',
  336. rtpSafetyFactor: 5,
  337. useHeaders: false,
  338. includeKeys: [],
  339. };
  340. const cmsd = {
  341. enabled: true,
  342. applyMaximumSuggestedBitrate: true,
  343. estimatedThroughputWeightRatio: 0.5,
  344. };
  345. const lcevc = {
  346. enabled: false,
  347. dynamicPerformanceScaling: true,
  348. logLevel: 0,
  349. drawLogo: false,
  350. };
  351. const mediaSource = {
  352. codecSwitchingStrategy: codecSwitchingStrategy,
  353. addExtraFeaturesToSourceBuffer: (mimeType) => {
  354. return shaka.util.ConfigUtils.referenceParametersAndReturn(
  355. [mimeType],
  356. '');
  357. },
  358. forceTransmux: false,
  359. insertFakeEncryptionInInit: true,
  360. modifyCueCallback: (cue, uri) => {
  361. return shaka.util.ConfigUtils.referenceParametersAndReturn(
  362. [cue, uri],
  363. undefined);
  364. },
  365. };
  366. let customPlayheadTracker = false;
  367. let skipPlayDetection = false;
  368. let supportsMultipleMediaElements = true;
  369. if (shaka.util.Platform.isSmartTV()) {
  370. customPlayheadTracker = true;
  371. skipPlayDetection = true;
  372. supportsMultipleMediaElements = false;
  373. }
  374. const ads = {
  375. customPlayheadTracker,
  376. skipPlayDetection,
  377. supportsMultipleMediaElements,
  378. };
  379. const textDisplayer = {
  380. captionsUpdatePeriod: 0.25,
  381. };
  382. const AutoShowText = shaka.config.AutoShowText;
  383. /** @type {shaka.extern.PlayerConfiguration} */
  384. const config = {
  385. drm: drm,
  386. manifest: manifest,
  387. streaming: streaming,
  388. mediaSource: mediaSource,
  389. offline: offline,
  390. abrFactory: () => new shaka.abr.SimpleAbrManager(),
  391. abr: abr,
  392. autoShowText: AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED,
  393. preferredAudioLanguage: '',
  394. preferredAudioLabel: '',
  395. preferredTextLanguage: '',
  396. preferredVariantRole: '',
  397. preferredTextRole: '',
  398. preferredAudioChannelCount: 2,
  399. preferredVideoHdrLevel: 'AUTO',
  400. preferredVideoLayout: '',
  401. preferredVideoLabel: '',
  402. preferredVideoCodecs: [],
  403. preferredAudioCodecs: [],
  404. preferForcedSubs: false,
  405. preferSpatialAudio: false,
  406. preferredDecodingAttributes: [],
  407. restrictions: {
  408. minWidth: 0,
  409. maxWidth: Infinity,
  410. minHeight: 0,
  411. maxHeight: Infinity,
  412. minPixels: 0,
  413. maxPixels: Infinity,
  414. minFrameRate: 0,
  415. maxFrameRate: Infinity,
  416. minBandwidth: 0,
  417. maxBandwidth: Infinity,
  418. minChannelsCount: 0,
  419. maxChannelsCount: Infinity,
  420. },
  421. playRangeStart: 0,
  422. playRangeEnd: Infinity,
  423. textDisplayer: textDisplayer,
  424. textDisplayFactory: () => null,
  425. cmcd: cmcd,
  426. cmsd: cmsd,
  427. lcevc: lcevc,
  428. ads: ads,
  429. };
  430. // Add this callback so that we can reference the preferred audio language
  431. // through the config object so that if it gets updated, we have the
  432. // updated value.
  433. // eslint-disable-next-line require-await
  434. offline.trackSelectionCallback = async (tracks) => {
  435. return shaka.util.PlayerConfiguration.defaultTrackSelect(
  436. tracks, config.preferredAudioLanguage,
  437. config.preferredVideoHdrLevel);
  438. };
  439. return config;
  440. }
  441. /**
  442. * Merges the given configuration changes into the given destination. This
  443. * uses the default Player configurations as the template.
  444. *
  445. * @param {shaka.extern.PlayerConfiguration} destination
  446. * @param {!Object} updates
  447. * @param {shaka.extern.PlayerConfiguration=} template
  448. * @return {boolean}
  449. * @export
  450. */
  451. static mergeConfigObjects(destination, updates, template) {
  452. const overrides = {
  453. '.drm.keySystemsMapping': '',
  454. '.drm.servers': '',
  455. '.drm.clearKeys': '',
  456. '.drm.advanced': {
  457. distinctiveIdentifierRequired: false,
  458. persistentStateRequired: false,
  459. videoRobustness: '',
  460. audioRobustness: '',
  461. sessionType: '',
  462. serverCertificate: new Uint8Array(0),
  463. serverCertificateUri: '',
  464. individualizationServer: '',
  465. headers: {},
  466. },
  467. };
  468. return shaka.util.ConfigUtils.mergeConfigObjects(
  469. destination, updates,
  470. template || shaka.util.PlayerConfiguration.createDefault(), overrides,
  471. '');
  472. }
  473. /**
  474. * @param {!Array.<shaka.extern.Track>} tracks
  475. * @param {string} preferredAudioLanguage
  476. * @param {string} preferredVideoHdrLevel
  477. * @return {!Array.<shaka.extern.Track>}
  478. */
  479. static defaultTrackSelect(
  480. tracks, preferredAudioLanguage, preferredVideoHdrLevel) {
  481. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  482. const LanguageUtils = shaka.util.LanguageUtils;
  483. let hdrLevel = preferredVideoHdrLevel;
  484. if (hdrLevel == 'AUTO') {
  485. // Auto detect the ideal HDR level.
  486. if (window.matchMedia('(color-gamut: p3)').matches) {
  487. hdrLevel = 'PQ';
  488. } else {
  489. hdrLevel = 'SDR';
  490. }
  491. }
  492. /** @type {!Array.<shaka.extern.Track>} */
  493. const allVariants = tracks.filter((track) => {
  494. if (track.type != 'variant') {
  495. return false;
  496. }
  497. if (track.hdr && track.hdr != hdrLevel) {
  498. return false;
  499. }
  500. return true;
  501. });
  502. /** @type {!Array.<shaka.extern.Track>} */
  503. let selectedVariants = [];
  504. // Find the locale that best matches our preferred audio locale.
  505. const closestLocale = LanguageUtils.findClosestLocale(
  506. preferredAudioLanguage,
  507. allVariants.map((variant) => variant.language));
  508. // If we found a locale that was close to our preference, then only use
  509. // variants that use that locale.
  510. if (closestLocale) {
  511. selectedVariants = allVariants.filter((variant) => {
  512. const locale = LanguageUtils.normalize(variant.language);
  513. return locale == closestLocale;
  514. });
  515. }
  516. // If we failed to get a language match, go with primary.
  517. if (selectedVariants.length == 0) {
  518. selectedVariants = allVariants.filter((variant) => {
  519. return variant.primary;
  520. });
  521. }
  522. // Otherwise, there is no good way to choose the language, so we don't
  523. // choose a language at all.
  524. if (selectedVariants.length == 0) {
  525. // Issue a warning, but only if the content has multiple languages.
  526. // Otherwise, this warning would just be noise.
  527. const languages = new Set(allVariants.map((track) => {
  528. return track.language;
  529. }));
  530. if (languages.size > 1) {
  531. shaka.log.warning('Could not choose a good audio track based on ' +
  532. 'language preferences or primary tracks. An ' +
  533. 'arbitrary language will be stored!');
  534. }
  535. // Default back to all variants.
  536. selectedVariants = allVariants;
  537. }
  538. // From previously selected variants, choose the SD ones (height <= 480).
  539. const tracksByHeight = selectedVariants.filter((track) => {
  540. return track.height && track.height <= 480;
  541. });
  542. // If variants don't have video or no video with height <= 480 was
  543. // found, proceed with the previously selected tracks.
  544. if (tracksByHeight.length) {
  545. // Sort by resolution, then select all variants which match the height
  546. // of the highest SD res. There may be multiple audio bitrates for the
  547. // same video resolution.
  548. tracksByHeight.sort((a, b) => {
  549. // The items in this list have already been screened for height, but the
  550. // compiler doesn't know that.
  551. goog.asserts.assert(a.height != null, 'Null height');
  552. goog.asserts.assert(b.height != null, 'Null height');
  553. return b.height - a.height;
  554. });
  555. selectedVariants = tracksByHeight.filter((track) => {
  556. return track.height == tracksByHeight[0].height;
  557. });
  558. }
  559. /** @type {!Array.<shaka.extern.Track>} */
  560. const selectedTracks = [];
  561. // If there are multiple matches at different audio bitrates, select the
  562. // middle bandwidth one.
  563. if (selectedVariants.length) {
  564. const middleIndex = Math.floor(selectedVariants.length / 2);
  565. selectedVariants.sort((a, b) => a.bandwidth - b.bandwidth);
  566. selectedTracks.push(selectedVariants[middleIndex]);
  567. }
  568. // Since this default callback is used primarily by our own demo app and by
  569. // app developers who haven't thought about which tracks they want, we
  570. // should select all image/text tracks, regardless of language. This makes
  571. // for a better demo for us, and does not rely on user preferences for the
  572. // unconfigured app.
  573. for (const track of tracks) {
  574. if (track.type == ContentType.TEXT || track.type == ContentType.IMAGE) {
  575. selectedTracks.push(track);
  576. }
  577. }
  578. return selectedTracks;
  579. }
  580. /**
  581. * @param {!Element} element
  582. * @return {!Element}
  583. */
  584. static defaultManifestPreprocessor(element) {
  585. return shaka.util.ConfigUtils.referenceParametersAndReturn(
  586. [element],
  587. element);
  588. }
  589. /**
  590. * @param {!shaka.extern.xml.Node} element
  591. * @return {!shaka.extern.xml.Node}
  592. */
  593. static defaultManifestPreprocessorTXml(element) {
  594. return shaka.util.ConfigUtils.referenceParametersAndReturn(
  595. [element],
  596. element);
  597. }
  598. };