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