shaka-player 4.12.8 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +4 -0
  3. package/README.md +22 -21
  4. package/cspell.config.yaml +18 -0
  5. package/dist/controls.css +1 -1
  6. package/dist/controls.css.map +1 -1
  7. package/dist/deps.js +29 -24
  8. package/dist/locales.js +33 -33
  9. package/dist/shaka-player.compiled.d.ts +138 -36
  10. package/dist/shaka-player.compiled.debug.d.ts +138 -36
  11. package/dist/shaka-player.compiled.debug.externs.js +367 -107
  12. package/dist/shaka-player.compiled.debug.js +828 -765
  13. package/dist/shaka-player.compiled.debug.map +4 -4
  14. package/dist/shaka-player.compiled.externs.js +367 -107
  15. package/dist/shaka-player.compiled.js +1451 -1416
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +172 -38
  18. package/dist/shaka-player.ui.debug.d.ts +172 -38
  19. package/dist/shaka-player.ui.debug.externs.js +406 -113
  20. package/dist/shaka-player.ui.debug.js +842 -766
  21. package/dist/shaka-player.ui.debug.map +4 -4
  22. package/dist/shaka-player.ui.externs.js +406 -113
  23. package/dist/shaka-player.ui.js +1670 -1628
  24. package/dist/shaka-player.ui.map +4 -4
  25. package/eslint.config.mjs +343 -0
  26. package/externs/airplay.js +1 -1
  27. package/externs/audiotrack.js +1 -1
  28. package/externs/awesomplete.js +1 -1
  29. package/externs/cast-sdk-v2.js +2 -2
  30. package/externs/cmcd.js +16 -1
  31. package/externs/device_sensor_event.js +1 -1
  32. package/externs/ima.js +8 -8
  33. package/externs/isoboxer.js +31 -31
  34. package/externs/jwk_set.js +1 -1
  35. package/externs/lcevc.js +8 -8
  36. package/externs/mediakeys.js +1 -1
  37. package/externs/mediasession.js +3 -2
  38. package/externs/mediatailor.js +18 -18
  39. package/externs/pictureinpicture.js +41 -1
  40. package/externs/playstation.js +2 -2
  41. package/externs/prefixed_eme.js +1 -1
  42. package/externs/shaka/abortable.js +4 -4
  43. package/externs/shaka/abr_manager.js +1 -1
  44. package/externs/shaka/ads.js +45 -6
  45. package/externs/shaka/cea.js +2 -2
  46. package/externs/shaka/codecs.js +4 -4
  47. package/externs/shaka/drm_info.js +6 -6
  48. package/externs/shaka/error.js +5 -5
  49. package/externs/shaka/manifest.js +51 -56
  50. package/externs/shaka/manifest_parser.js +3 -9
  51. package/externs/shaka/net.js +23 -20
  52. package/externs/shaka/offline.js +37 -37
  53. package/externs/shaka/offline_compat_v1.js +10 -10
  54. package/externs/shaka/offline_compat_v2.js +10 -10
  55. package/externs/shaka/player.js +139 -109
  56. package/externs/shaka/text.js +10 -3
  57. package/externs/shaka/transmuxer.js +1 -1
  58. package/externs/videotrack.js +1 -1
  59. package/lib/abr/ewma.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +70 -22
  61. package/lib/ads/ad_manager.js +43 -18
  62. package/lib/ads/ad_utils.js +160 -58
  63. package/lib/ads/ads_stats.js +1 -1
  64. package/lib/ads/client_side_ad_manager.js +2 -2
  65. package/lib/ads/interstitial_ad.js +16 -11
  66. package/lib/ads/interstitial_ad_manager.js +419 -72
  67. package/lib/ads/interstitial_static_ad.js +299 -0
  68. package/lib/ads/media_tailor_ad.js +1 -0
  69. package/lib/ads/media_tailor_ad_manager.js +37 -36
  70. package/lib/ads/server_side_ad_manager.js +7 -6
  71. package/lib/cast/cast_receiver.js +5 -3
  72. package/lib/cast/cast_sender.js +2 -2
  73. package/lib/cast/cast_utils.js +20 -13
  74. package/lib/cea/cea608_data_channel.js +53 -53
  75. package/lib/cea/cea708_service.js +42 -40
  76. package/lib/cea/cea708_window.js +5 -2
  77. package/lib/cea/cea_decoder.js +5 -7
  78. package/lib/cea/mp4_cea_parser.js +1 -1
  79. package/lib/cea/sei_processor.js +1 -1
  80. package/lib/dash/content_protection.js +62 -192
  81. package/lib/dash/dash_parser.js +119 -119
  82. package/lib/dash/mpd_utils.js +14 -13
  83. package/lib/dash/segment_base.js +5 -5
  84. package/lib/dash/segment_list.js +10 -10
  85. package/lib/dash/segment_template.js +55 -53
  86. package/lib/debug/log.js +2 -2
  87. package/lib/dependencies/all.js +2 -1
  88. package/lib/{media → drm}/drm_engine.js +274 -143
  89. package/lib/{util → drm}/drm_utils.js +20 -11
  90. package/lib/drm/playready.js +233 -0
  91. package/lib/hls/hls_classes.js +14 -14
  92. package/lib/hls/hls_parser.js +257 -196
  93. package/lib/hls/hls_utils.js +14 -14
  94. package/lib/hls/manifest_text_parser.js +10 -10
  95. package/lib/media/adaptation_set.js +6 -5
  96. package/lib/media/adaptation_set_criteria.js +51 -379
  97. package/lib/media/buffering_observer.js +5 -4
  98. package/lib/media/closed_caption_parser.js +2 -2
  99. package/lib/media/content_workarounds.js +2 -2
  100. package/lib/media/example_based_criteria.js +69 -0
  101. package/lib/media/gap_jumping_controller.js +14 -1
  102. package/lib/media/manifest_filterer.js +8 -8
  103. package/lib/media/manifest_parser.js +2 -2
  104. package/lib/media/media_source_capabilities.js +20 -1
  105. package/lib/media/media_source_engine.js +84 -35
  106. package/lib/media/mp4_segment_index_parser.js +5 -5
  107. package/lib/media/playhead.js +2 -8
  108. package/lib/media/playhead_observer.js +1 -1
  109. package/lib/media/preference_based_criteria.js +333 -0
  110. package/lib/media/preload_manager.js +53 -37
  111. package/lib/media/presentation_timeline.js +2 -2
  112. package/lib/media/quality_observer.js +35 -35
  113. package/lib/media/region_observer.js +2 -2
  114. package/lib/media/region_timeline.js +3 -3
  115. package/lib/media/segment_index.js +14 -14
  116. package/lib/media/segment_prefetch.js +17 -13
  117. package/lib/media/segment_reference.js +7 -7
  118. package/lib/media/segment_utils.js +9 -9
  119. package/lib/media/streaming_engine.js +77 -38
  120. package/lib/media/time_ranges_utils.js +1 -1
  121. package/lib/media/video_wrapper.js +1 -1
  122. package/lib/media/webm_segment_index_parser.js +4 -4
  123. package/lib/mss/content_protection.js +17 -222
  124. package/lib/mss/mss_parser.js +23 -24
  125. package/lib/net/data_uri_plugin.js +1 -0
  126. package/lib/net/http_fetch_plugin.js +8 -7
  127. package/lib/net/http_plugin_utils.js +5 -2
  128. package/lib/net/http_xhr_plugin.js +4 -3
  129. package/lib/net/networking_engine.js +72 -17
  130. package/lib/net/networking_utils.js +5 -2
  131. package/lib/offline/download_info.js +5 -1
  132. package/lib/offline/download_manager.js +15 -4
  133. package/lib/offline/download_progress_estimator.js +1 -1
  134. package/lib/offline/indexeddb/base_storage_cell.js +9 -9
  135. package/lib/offline/indexeddb/db_connection.js +1 -1
  136. package/lib/offline/indexeddb/db_operation.js +2 -2
  137. package/lib/offline/indexeddb/eme_session_storage_cell.js +1 -1
  138. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  139. package/lib/offline/indexeddb/v1_storage_cell.js +3 -3
  140. package/lib/offline/indexeddb/v2_storage_cell.js +2 -2
  141. package/lib/offline/manifest_converter.js +11 -12
  142. package/lib/offline/offline_scheme.js +8 -4
  143. package/lib/offline/session_deleter.js +11 -11
  144. package/lib/offline/storage.js +137 -88
  145. package/lib/offline/storage_muxer.js +8 -8
  146. package/lib/offline/stored_content_utils.js +3 -3
  147. package/lib/offline/stream_bandwidth_estimator.js +1 -1
  148. package/lib/player.js +291 -207
  149. package/lib/polyfill/all.js +1 -1
  150. package/lib/polyfill/media_capabilities.js +5 -5
  151. package/lib/polyfill/patchedmediakeys_apple.js +6 -6
  152. package/lib/polyfill/patchedmediakeys_webkit.js +8 -8
  153. package/lib/polyfill/video_play_promise.js +0 -1
  154. package/lib/text/cue.js +3 -2
  155. package/lib/text/lrc_text_parser.js +1 -1
  156. package/lib/text/mp4_ttml_parser.js +1 -1
  157. package/lib/text/mp4_vtt_parser.js +3 -3
  158. package/lib/text/sbv_text_parser.js +1 -1
  159. package/lib/text/simple_text_displayer.js +37 -10
  160. package/lib/text/ssa_text_parser.js +3 -1
  161. package/lib/text/stub_text_displayer.js +12 -5
  162. package/lib/text/text_engine.js +5 -4
  163. package/lib/text/text_utils.js +4 -4
  164. package/lib/text/ttml_text_parser.js +21 -20
  165. package/lib/text/ui_text_displayer.js +20 -13
  166. package/lib/text/vtt_text_parser.js +10 -10
  167. package/lib/text/web_vtt_generator.js +2 -2
  168. package/lib/transmuxer/aac_transmuxer.js +2 -2
  169. package/lib/transmuxer/ac3_transmuxer.js +2 -2
  170. package/lib/transmuxer/adts.js +2 -0
  171. package/lib/transmuxer/ec3.js +3 -3
  172. package/lib/transmuxer/ec3_transmuxer.js +2 -2
  173. package/lib/transmuxer/h264.js +5 -5
  174. package/lib/transmuxer/h265.js +3 -3
  175. package/lib/transmuxer/mp3_transmuxer.js +2 -2
  176. package/lib/transmuxer/mpeg_audio.js +6 -6
  177. package/lib/transmuxer/mss_transmuxer.js +2 -0
  178. package/lib/transmuxer/transmuxer_engine.js +1 -1
  179. package/lib/transmuxer/ts_transmuxer.js +10 -10
  180. package/lib/util/abortable_operation.js +10 -10
  181. package/lib/util/array_utils.js +6 -6
  182. package/lib/util/cmcd_manager.js +149 -18
  183. package/lib/util/cmsd_manager.js +11 -11
  184. package/lib/util/config_utils.js +3 -3
  185. package/lib/util/content_steering_manager.js +17 -17
  186. package/lib/util/data_view_reader.js +3 -3
  187. package/lib/util/ebml_parser.js +1 -1
  188. package/lib/util/error.js +11 -6
  189. package/lib/util/event_manager.js +1 -1
  190. package/lib/util/exp_golomb.js +1 -1
  191. package/lib/util/fairplay_utils.js +6 -1
  192. package/lib/util/fake_event.js +3 -2
  193. package/lib/util/fake_event_target.js +1 -1
  194. package/lib/util/functional.js +8 -8
  195. package/lib/util/id3_utils.js +1 -1
  196. package/lib/util/iterables.js +6 -6
  197. package/lib/util/language_utils.js +17 -17
  198. package/lib/util/manifest_parser_utils.js +13 -13
  199. package/lib/util/map_utils.js +6 -6
  200. package/lib/util/media_ready_state_utils.js +1 -1
  201. package/lib/util/mime_utils.js +15 -4
  202. package/lib/util/mp4_box_parsers.js +5 -5
  203. package/lib/util/mp4_generator.js +31 -30
  204. package/lib/util/mp4_parser.js +2 -2
  205. package/lib/util/multi_map.js +5 -5
  206. package/lib/util/mutex.js +1 -1
  207. package/lib/util/networking.js +1 -1
  208. package/lib/util/object_utils.js +5 -1
  209. package/lib/util/operation_manager.js +1 -1
  210. package/lib/util/periods.js +24 -24
  211. package/lib/util/platform.js +8 -4
  212. package/lib/util/player_configuration.js +50 -17
  213. package/lib/util/pssh.js +34 -6
  214. package/lib/util/public_promise.js +2 -2
  215. package/lib/util/state_history.js +2 -2
  216. package/lib/util/stream_utils.js +138 -120
  217. package/lib/util/string_utils.js +4 -3
  218. package/lib/util/switch_history.js +2 -2
  219. package/lib/util/tXml.js +22 -17
  220. package/lib/util/text_parser.js +2 -2
  221. package/lib/util/timer.js +1 -1
  222. package/lib/util/ts_parser.js +22 -22
  223. package/lib/util/xml_utils.js +1 -0
  224. package/package.json +7 -4
  225. package/project-words.txt +485 -0
  226. package/shaka-player.uncompiled.js +1 -1
  227. package/third_party/closure-uri/uri.js +2 -2
  228. package/third_party/closure-uri/utils.js +2 -2
  229. package/third_party/language-mapping-list/language-mapping-list.js +1 -1
  230. package/ui/ad_statistics_button.js +9 -5
  231. package/ui/airplay_button.js +1 -0
  232. package/ui/audio_language_selection.js +2 -1
  233. package/ui/context_menu.js +2 -2
  234. package/ui/controls.js +7 -7
  235. package/ui/externs/ui.js +29 -21
  236. package/ui/externs/watermark.js +64 -0
  237. package/ui/fast_forward_button.js +1 -1
  238. package/ui/gl_matrix/matrix_4x4.js +11 -5
  239. package/ui/hidden_fast_forward_button.js +1 -1
  240. package/ui/hidden_seek_button.js +1 -1
  241. package/ui/language_utils.js +27 -26
  242. package/ui/less/buttons.less +2 -2
  243. package/ui/localization.js +21 -21
  244. package/ui/loop_button.js +2 -2
  245. package/ui/overflow_menu.js +2 -2
  246. package/ui/playback_rate_selection.js +1 -1
  247. package/ui/presentation_time.js +4 -1
  248. package/ui/range_element.js +2 -2
  249. package/ui/resolution_selection.js +42 -6
  250. package/ui/rewind_button.js +1 -1
  251. package/ui/seek_bar.js +5 -5
  252. package/ui/statistics_button.js +8 -4
  253. package/ui/text_selection.js +3 -2
  254. package/ui/ui.js +32 -2
  255. package/ui/vr_manager.js +3 -3
  256. package/ui/vr_utils.js +14 -14
  257. package/ui/vr_webgl.js +5 -5
  258. package/ui/watermark.js +313 -0
  259. package/.eslintignore +0 -4
  260. package/.eslintrc.js +0 -310
  261. package/lib/polyfill/patchedmediakeys_nop.js +0 -148
@@ -17,7 +17,7 @@ goog.require('shaka.util.MimeUtils');
17
17
  goog.require('shaka.util.MultiMap');
18
18
  goog.require('shaka.util.ObjectUtils');
19
19
  goog.require('shaka.util.Platform');
20
- goog.requireType('shaka.media.DrmEngine');
20
+ goog.requireType('shaka.drm.DrmEngine');
21
21
 
22
22
 
23
23
  /**
@@ -30,10 +30,10 @@ shaka.util.StreamUtils = class {
30
30
  * bandwidth and filter out the rest.
31
31
  * Also filters out variants that have too many audio channels.
32
32
  * @param {!shaka.extern.Manifest} manifest
33
- * @param {!Array.<string>} preferredVideoCodecs
34
- * @param {!Array.<string>} preferredAudioCodecs
35
- * @param {!Array.<string>} preferredDecodingAttributes
36
- * @param {!Array.<string>} preferredTextFormats
33
+ * @param {!Array<string>} preferredVideoCodecs
34
+ * @param {!Array<string>} preferredAudioCodecs
35
+ * @param {!Array<string>} preferredDecodingAttributes
36
+ * @param {!Array<string>} preferredTextFormats
37
37
  */
38
38
  static chooseCodecsAndFilterManifest(manifest, preferredVideoCodecs,
39
39
  preferredAudioCodecs, preferredDecodingAttributes, preferredTextFormats) {
@@ -67,7 +67,7 @@ shaka.util.StreamUtils = class {
67
67
 
68
68
  if (preferredDecodingAttributes.length) {
69
69
  // group variants by resolution and choose preferred variants only
70
- /** @type {!shaka.util.MultiMap.<shaka.extern.Variant>} */
70
+ /** @type {!shaka.util.MultiMap<shaka.extern.Variant>} */
71
71
  const variantsByResolutionMap = new shaka.util.MultiMap();
72
72
  for (const variant of variants) {
73
73
  variantsByResolutionMap
@@ -243,8 +243,8 @@ shaka.util.StreamUtils = class {
243
243
  * Choose the codecs by configured preferred audio and video codecs.
244
244
  *
245
245
  * @param {!Array<shaka.extern.Variant>} variants
246
- * @param {!Array.<string>} preferredVideoCodecs
247
- * @param {!Array.<string>} preferredAudioCodecs
246
+ * @param {!Array<string>} preferredVideoCodecs
247
+ * @param {!Array<string>} preferredAudioCodecs
248
248
  * @return {!Array<shaka.extern.Variant>}
249
249
  */
250
250
  static choosePreferredCodecs(variants, preferredVideoCodecs,
@@ -366,7 +366,7 @@ shaka.util.StreamUtils = class {
366
366
 
367
367
 
368
368
  /**
369
- * @param {!Array.<shaka.extern.Variant>} variants
369
+ * @param {!Array<shaka.extern.Variant>} variants
370
370
  * @param {shaka.extern.Restrictions} restrictions
371
371
  * @param {shaka.extern.Resolution} maxHwRes
372
372
  * @return {boolean} Whether the tracks changed.
@@ -391,10 +391,10 @@ shaka.util.StreamUtils = class {
391
391
  /**
392
392
  * Alters the given Manifest to filter out any unplayable streams.
393
393
  *
394
- * @param {shaka.media.DrmEngine} drmEngine
394
+ * @param {shaka.drm.DrmEngine} drmEngine
395
395
  * @param {shaka.extern.Manifest} manifest
396
396
  * @param {!Array<string>=} preferredKeySystems
397
- * @param {!Object.<string, string>=} keySystemsMapping
397
+ * @param {!Object<string, string>=} keySystemsMapping
398
398
  */
399
399
  static async filterManifest(drmEngine, manifest, preferredKeySystems = [],
400
400
  keySystemsMapping = {}) {
@@ -410,11 +410,11 @@ shaka.util.StreamUtils = class {
410
410
  * Alters the given Manifest to filter out any streams unsupported by the
411
411
  * platform via MediaCapabilities.decodingInfo() API.
412
412
  *
413
- * @param {shaka.media.DrmEngine} drmEngine
413
+ * @param {shaka.drm.DrmEngine} drmEngine
414
414
  * @param {shaka.extern.Manifest} manifest
415
415
  * @param {boolean} usePersistentLicenses
416
416
  * @param {!Array<string>} preferredKeySystems
417
- * @param {!Object.<string, string>} keySystemsMapping
417
+ * @param {!Object<string, string>} keySystemsMapping
418
418
  */
419
419
  static async filterManifestByMediaCapabilities(
420
420
  drmEngine, manifest, usePersistentLicenses, preferredKeySystems,
@@ -491,7 +491,7 @@ shaka.util.StreamUtils = class {
491
491
  /**
492
492
  * @param {!shaka.extern.Variant} variant
493
493
  * @param {?string} keySystem
494
- * @param {!Object.<string, string>} keySystemsMapping
494
+ * @param {!Object<string, string>} keySystemsMapping
495
495
  * @return {boolean}
496
496
  * @private
497
497
  */
@@ -553,7 +553,7 @@ shaka.util.StreamUtils = class {
553
553
  * and assigns it to the given variant.
554
554
  * If that query has been done before, instead return a cached result.
555
555
  * @param {!shaka.extern.Variant} variant
556
- * @param {!Array.<!MediaDecodingConfiguration>} decodingConfigs
556
+ * @param {!Array<!MediaDecodingConfiguration>} decodingConfigs
557
557
  * @private
558
558
  */
559
559
  static async getDecodingInfosForVariant_(variant, decodingConfigs) {
@@ -620,7 +620,7 @@ shaka.util.StreamUtils = class {
620
620
 
621
621
  /**
622
622
  * @param {!MediaDecodingConfiguration} decodingConfig
623
- * @return {!Promise.<?Array.<!MediaCapabilitiesDecodingInfo>>}
623
+ * @return {!Promise<?Array<!MediaCapabilitiesDecodingInfo>>}
624
624
  * @private
625
625
  */
626
626
  static checkEachDecodingConfigCombination_(decodingConfig) {
@@ -677,7 +677,7 @@ shaka.util.StreamUtils = class {
677
677
  * This should be called after the DrmEngine is created and configured, and
678
678
  * before DrmEngine sets the mediaKeys.
679
679
  *
680
- * @param {!Array.<shaka.extern.Variant>} variants
680
+ * @param {!Array<shaka.extern.Variant>} variants
681
681
  * @param {boolean} usePersistentLicenses
682
682
  * @param {boolean} srcEquals
683
683
  * @param {!Array<string>} preferredKeySystems
@@ -696,7 +696,7 @@ shaka.util.StreamUtils = class {
696
696
  for (const preferredKeySystem of preferredKeySystems) {
697
697
  let keySystemSatisfied = false;
698
698
  for (const variant of variants) {
699
- /** @type {!Array.<!Array.<!MediaDecodingConfiguration>>} */
699
+ /** @type {!Array<!Array<!MediaDecodingConfiguration>>} */
700
700
  const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
701
701
  variant, usePersistentLicenses, srcEquals)
702
702
  .filter((configs) => {
@@ -726,7 +726,7 @@ shaka.util.StreamUtils = class {
726
726
  } // for (const preferredKeySystem of preferredKeySystems)
727
727
 
728
728
  for (const variant of variants) {
729
- /** @type {!Array.<!Array.<!MediaDecodingConfiguration>>} */
729
+ /** @type {!Array<!Array<!MediaDecodingConfiguration>>} */
730
730
  const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
731
731
  variant, usePersistentLicenses, srcEquals)
732
732
  .filter((configs) => {
@@ -758,7 +758,7 @@ shaka.util.StreamUtils = class {
758
758
  * @param {!shaka.extern.Variant} variant
759
759
  * @param {boolean} usePersistentLicenses
760
760
  * @param {boolean} srcEquals
761
- * @return {!Array.<!Array.<!MediaDecodingConfiguration>>}
761
+ * @return {!Array<!Array<!MediaDecodingConfiguration>>}
762
762
  * @private
763
763
  */
764
764
  static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
@@ -865,7 +865,7 @@ shaka.util.StreamUtils = class {
865
865
 
866
866
  // Generate each combination of video and audio config as a separate
867
867
  // MediaDecodingConfiguration, inside the main "batch".
868
- /** @type {!Array.<!MediaDecodingConfiguration>} */
868
+ /** @type {!Array<!MediaDecodingConfiguration>} */
869
869
  const mediaDecodingConfigBatch = [];
870
870
  if (videoConfigs.length == 0) {
871
871
  videoConfigs.push(null);
@@ -917,101 +917,117 @@ shaka.util.StreamUtils = class {
917
917
  usePersistentLicenses ? ['persistent-license'] : ['temporary'];
918
918
 
919
919
  for (const keySystem of drmInfoByKeySystems.keys()) {
920
- const modifiedMediaDecodingConfigBatch = [];
921
- for (const base of mediaDecodingConfigBatch) {
922
- // Create a copy of the mediaDecodingConfig.
923
- const config = /** @type {!MediaDecodingConfiguration} */
924
- (Object.assign({}, base));
925
-
926
- const drmInfos = drmInfoByKeySystems.get(keySystem);
927
-
928
- /** @type {!MediaCapabilitiesKeySystemConfiguration} */
929
- const keySystemConfig = {
930
- keySystem: keySystem,
931
- initDataType: 'cenc',
932
- persistentState: persistentState,
933
- distinctiveIdentifier: 'optional',
934
- sessionTypes: sessionTypes,
935
- };
920
+ const drmInfos = drmInfoByKeySystems.get(keySystem);
921
+
922
+ // Get all the robustness info so that we can avoid using nested
923
+ // loops when we just need the robustness.
924
+ const drmInfosByRobustness = new Map();
925
+ for (const info of drmInfos) {
926
+ const keyName = `${info.videoRobustness},${info.audioRobustness}`;
927
+ if (!drmInfosByRobustness.get(keyName)) {
928
+ drmInfosByRobustness.set(keyName, []);
929
+ }
930
+ drmInfosByRobustness.get(keyName).push(info);
931
+ }
936
932
 
937
- for (const info of drmInfos) {
938
- if (info.initData && info.initData.length) {
939
- const initDataTypes = new Set();
940
- for (const initData of info.initData) {
941
- initDataTypes.add(initData.initDataType);
942
- }
943
- if (initDataTypes.size > 1) {
944
- shaka.log.v2('DrmInfo contains more than one initDataType,',
945
- 'and we use the initDataType of the first initData.',
946
- info);
933
+ for (const drmInfosRobustness of drmInfosByRobustness.values()) {
934
+ const modifiedMediaDecodingConfigBatch = [];
935
+ for (const base of mediaDecodingConfigBatch) {
936
+ // Create a copy of the mediaDecodingConfig.
937
+ const config = /** @type {!MediaDecodingConfiguration} */
938
+ (Object.assign({}, base));
939
+
940
+
941
+ /** @type {!MediaCapabilitiesKeySystemConfiguration} */
942
+ const keySystemConfig = {
943
+ keySystem: keySystem,
944
+ initDataType: 'cenc',
945
+ persistentState: persistentState,
946
+ distinctiveIdentifier: 'optional',
947
+ sessionTypes: sessionTypes,
948
+ };
949
+
950
+ for (const info of drmInfosRobustness) {
951
+ if (info.initData && info.initData.length) {
952
+ const initDataTypes = new Set();
953
+ for (const initData of info.initData) {
954
+ initDataTypes.add(initData.initDataType);
955
+ }
956
+ if (initDataTypes.size > 1) {
957
+ shaka.log.v2('DrmInfo contains more than one initDataType,',
958
+ 'and we use the initDataType of the first initData.',
959
+ info);
960
+ }
961
+ keySystemConfig.initDataType = info.initData[0].initDataType;
947
962
  }
948
- keySystemConfig.initDataType = info.initData[0].initDataType;
949
- }
950
963
 
951
- if (info.distinctiveIdentifierRequired) {
952
- keySystemConfig.distinctiveIdentifier = 'required';
953
- }
954
- if (info.persistentStateRequired) {
955
- keySystemConfig.persistentState = 'required';
956
- }
957
- if (info.sessionType) {
958
- keySystemConfig.sessionTypes = [info.sessionType];
959
- }
964
+ if (info.distinctiveIdentifierRequired) {
965
+ keySystemConfig.distinctiveIdentifier = 'required';
966
+ }
967
+ if (info.persistentStateRequired) {
968
+ keySystemConfig.persistentState = 'required';
969
+ }
970
+ if (info.sessionType) {
971
+ keySystemConfig.sessionTypes = [info.sessionType];
972
+ }
960
973
 
961
- if (audio) {
962
- if (!keySystemConfig.audio) {
963
- // KeySystemTrackConfiguration
964
- keySystemConfig.audio = {
965
- robustness: info.audioRobustness,
966
- };
967
- if (info.encryptionScheme) {
968
- keySystemConfig.audio.encryptionScheme = info.encryptionScheme;
974
+ if (audio) {
975
+ if (!keySystemConfig.audio) {
976
+ // KeySystemTrackConfiguration
977
+ keySystemConfig.audio = {
978
+ robustness: info.audioRobustness,
979
+ };
980
+ if (info.encryptionScheme) {
981
+ keySystemConfig.audio.encryptionScheme =
982
+ info.encryptionScheme;
983
+ }
984
+ } else {
985
+ if (info.encryptionScheme) {
986
+ keySystemConfig.audio.encryptionScheme =
987
+ keySystemConfig.audio.encryptionScheme ||
988
+ info.encryptionScheme;
989
+ }
990
+ keySystemConfig.audio.robustness =
991
+ keySystemConfig.audio.robustness ||
992
+ info.audioRobustness;
969
993
  }
970
- } else {
971
- if (info.encryptionScheme) {
972
- keySystemConfig.audio.encryptionScheme =
973
- keySystemConfig.audio.encryptionScheme ||
974
- info.encryptionScheme;
994
+ // See: https://github.com/shaka-project/shaka-player/issues/4659
995
+ if (keySystemConfig.audio.robustness == '') {
996
+ delete keySystemConfig.audio.robustness;
975
997
  }
976
- keySystemConfig.audio.robustness =
977
- keySystemConfig.audio.robustness ||
978
- info.audioRobustness;
979
998
  }
980
- // See: https://github.com/shaka-project/shaka-player/issues/4659
981
- if (keySystemConfig.audio.robustness == '') {
982
- delete keySystemConfig.audio.robustness;
983
- }
984
- }
985
999
 
986
- if (video) {
987
- if (!keySystemConfig.video) {
988
- // KeySystemTrackConfiguration
989
- keySystemConfig.video = {
990
- robustness: info.videoRobustness,
991
- };
992
- if (info.encryptionScheme) {
993
- keySystemConfig.video.encryptionScheme = info.encryptionScheme;
1000
+ if (video) {
1001
+ if (!keySystemConfig.video) {
1002
+ // KeySystemTrackConfiguration
1003
+ keySystemConfig.video = {
1004
+ robustness: info.videoRobustness,
1005
+ };
1006
+ if (info.encryptionScheme) {
1007
+ keySystemConfig.video.encryptionScheme =
1008
+ info.encryptionScheme;
1009
+ }
1010
+ } else {
1011
+ if (info.encryptionScheme) {
1012
+ keySystemConfig.video.encryptionScheme =
1013
+ keySystemConfig.video.encryptionScheme ||
1014
+ info.encryptionScheme;
1015
+ }
1016
+ keySystemConfig.video.robustness =
1017
+ keySystemConfig.video.robustness ||
1018
+ info.videoRobustness;
994
1019
  }
995
- } else {
996
- if (info.encryptionScheme) {
997
- keySystemConfig.video.encryptionScheme =
998
- keySystemConfig.video.encryptionScheme ||
999
- info.encryptionScheme;
1020
+ // See: https://github.com/shaka-project/shaka-player/issues/4659
1021
+ if (keySystemConfig.video.robustness == '') {
1022
+ delete keySystemConfig.video.robustness;
1000
1023
  }
1001
- keySystemConfig.video.robustness =
1002
- keySystemConfig.video.robustness ||
1003
- info.videoRobustness;
1004
- }
1005
- // See: https://github.com/shaka-project/shaka-player/issues/4659
1006
- if (keySystemConfig.video.robustness == '') {
1007
- delete keySystemConfig.video.robustness;
1008
1024
  }
1009
1025
  }
1026
+ config.keySystemConfiguration = keySystemConfig;
1027
+ modifiedMediaDecodingConfigBatch.push(config);
1010
1028
  }
1011
- config.keySystemConfiguration = keySystemConfig;
1012
- modifiedMediaDecodingConfigBatch.push(config);
1029
+ configs.push(modifiedMediaDecodingConfigBatch);
1013
1030
  }
1014
- configs.push(modifiedMediaDecodingConfigBatch);
1015
1031
  }
1016
1032
  return configs;
1017
1033
  }
@@ -1066,12 +1082,12 @@ shaka.util.StreamUtils = class {
1066
1082
  // Convert avc1 codec string from RFC-4281 to RFC-6381 for
1067
1083
  // MediaSource.isTypeSupported
1068
1084
  // Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
1069
- const avcdata = codec.split('.');
1070
- if (avcdata.length == 3) {
1071
- let result = avcdata.shift() + '.';
1072
- result += parseInt(avcdata.shift(), 10).toString(16);
1085
+ const avcData = codec.split('.');
1086
+ if (avcData.length == 3) {
1087
+ let result = avcData.shift() + '.';
1088
+ result += parseInt(avcData.shift(), 10).toString(16);
1073
1089
  result +=
1074
- ('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
1090
+ ('000' + parseInt(avcData.shift(), 10).toString(16)).slice(-4);
1075
1091
  return result;
1076
1092
  }
1077
1093
  } else if (codec == 'vp9') {
@@ -1097,7 +1113,7 @@ shaka.util.StreamUtils = class {
1097
1113
 
1098
1114
 
1099
1115
  /**
1100
- * Alters the given Manifest to filter out any streams uncompatible with the
1116
+ * Alters the given Manifest to filter out any streams incompatible with the
1101
1117
  * current variant.
1102
1118
  *
1103
1119
  * @param {?shaka.extern.Variant} currentVariant
@@ -1196,7 +1212,7 @@ shaka.util.StreamUtils = class {
1196
1212
 
1197
1213
  /**
1198
1214
  * @param {string} minImage
1199
- * @return {!Promise.<boolean>}
1215
+ * @return {!Promise<boolean>}
1200
1216
  * @private
1201
1217
  */
1202
1218
  static isImageSupported_(minImage) {
@@ -1261,7 +1277,7 @@ shaka.util.StreamUtils = class {
1261
1277
  /** @type {?string} */
1262
1278
  const videoCodec = video ? video.codecs : null;
1263
1279
 
1264
- /** @type {!Array.<string>} */
1280
+ /** @type {!Array<string>} */
1265
1281
  const codecs = [];
1266
1282
  if (videoCodec) {
1267
1283
  codecs.push(videoCodec);
@@ -1270,7 +1286,7 @@ shaka.util.StreamUtils = class {
1270
1286
  codecs.push(audioCodec);
1271
1287
  }
1272
1288
 
1273
- /** @type {!Array.<string>} */
1289
+ /** @type {!Array<string>} */
1274
1290
  const mimeTypes = [];
1275
1291
  if (video) {
1276
1292
  mimeTypes.push(video.mimeType);
@@ -1281,7 +1297,7 @@ shaka.util.StreamUtils = class {
1281
1297
  /** @type {?string} */
1282
1298
  const mimeType = mimeTypes[0] || null;
1283
1299
 
1284
- /** @type {!Array.<string>} */
1300
+ /** @type {!Array<string>} */
1285
1301
  const kinds = [];
1286
1302
  if (audio) {
1287
1303
  kinds.push(audio.kind);
@@ -1292,7 +1308,7 @@ shaka.util.StreamUtils = class {
1292
1308
  /** @type {?string} */
1293
1309
  const kind = kinds[0] || null;
1294
1310
 
1295
- /** @type {!Set.<string>} */
1311
+ /** @type {!Set<string>} */
1296
1312
  const roles = new Set();
1297
1313
  if (audio) {
1298
1314
  for (const role of audio.roles) {
@@ -1657,8 +1673,8 @@ shaka.util.StreamUtils = class {
1657
1673
 
1658
1674
  /**
1659
1675
  * Filters out unplayable variants.
1660
- * @param {!Array.<!shaka.extern.Variant>} variants
1661
- * @return {!Array.<!shaka.extern.Variant>}
1676
+ * @param {!Array<!shaka.extern.Variant>} variants
1677
+ * @return {!Array<!shaka.extern.Variant>}
1662
1678
  */
1663
1679
  static getPlayableVariants(variants) {
1664
1680
  return variants.filter((variant) => {
@@ -1801,7 +1817,7 @@ shaka.util.StreamUtils = class {
1801
1817
  * Get all non-null streams in the variant as an array.
1802
1818
  *
1803
1819
  * @param {shaka.extern.Variant} variant
1804
- * @return {!Array.<shaka.extern.Stream>}
1820
+ * @return {!Array<shaka.extern.Stream>}
1805
1821
  */
1806
1822
  static getVariantStreams(variant) {
1807
1823
  const streams = [];
@@ -1838,7 +1854,7 @@ shaka.util.StreamUtils = class {
1838
1854
  * Set the best iframe stream to the original stream.
1839
1855
  *
1840
1856
  * @param {!shaka.extern.Stream} stream
1841
- * @param {!Array.<!shaka.extern.Stream>} iFrameStreams
1857
+ * @param {!Array<!shaka.extern.Stream>} iFrameStreams
1842
1858
  */
1843
1859
  static setBetterIFrameStream(stream, iFrameStreams) {
1844
1860
  if (!iFrameStreams.length) {
@@ -1929,7 +1945,7 @@ shaka.util.StreamUtils = class {
1929
1945
  * A cache of results from mediaCapabilities.decodingInfo, indexed by the
1930
1946
  * (stringified) decodingConfig.
1931
1947
  *
1932
- * @type {Object.<(!string), (!MediaCapabilitiesDecodingInfo)>}
1948
+ * @type {Object<(!string), (!MediaCapabilitiesDecodingInfo)>}
1933
1949
  * @private
1934
1950
  */
1935
1951
  shaka.util.StreamUtils.decodingConfigCache_ = {};
@@ -1947,7 +1963,7 @@ shaka.util.StreamUtils.DecodingAttributes = {
1947
1963
  };
1948
1964
 
1949
1965
  /**
1950
- * @private {!Map.<string, boolean>}
1966
+ * @private {!Map<string, boolean>}
1951
1967
  */
1952
1968
  shaka.util.StreamUtils.supportedImageMimeTypes_ = new Map()
1953
1969
  .set('image/svg+xml', true)
@@ -1959,6 +1975,7 @@ shaka.util.StreamUtils.supportedImageMimeTypes_ = new Map()
1959
1975
  * @const {string}
1960
1976
  * @private
1961
1977
  */
1978
+ // cspell: disable-next-line
1962
1979
  shaka.util.StreamUtils.minWebPImage_ = 'data:image/webp;base64,UklGRjoAAABXRU' +
1963
1980
  'JQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwY' +
1964
1981
  'AAA';
@@ -1967,6 +1984,7 @@ shaka.util.StreamUtils.minWebPImage_ = 'data:image/webp;base64,UklGRjoAAABXRU' +
1967
1984
  * @const {string}
1968
1985
  * @private
1969
1986
  */
1987
+ // cspell: disable-next-line
1970
1988
  shaka.util.StreamUtils.minAvifImage_ = 'data:image/avif;base64,AAAAIGZ0eXBhdm' +
1971
1989
  'lmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljd' +
1972
1990
  'AAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEA' +
@@ -1976,7 +1994,7 @@ shaka.util.StreamUtils.minAvifImage_ = 'data:image/avif;base64,AAAAIGZ0eXBhdm' +
1976
1994
  'CVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';
1977
1995
 
1978
1996
  /**
1979
- * @const {!Map.<string, string>}
1997
+ * @const {!Map<string, string>}
1980
1998
  * @private
1981
1999
  */
1982
2000
  shaka.util.StreamUtils.minImage_ = new Map()
@@ -334,7 +334,10 @@ shaka.util.StringUtils = class {
334
334
 
335
335
  /** @private {!shaka.util.Lazy.<function(!TypedArray):string>} */
336
336
  shaka.util.StringUtils.fromCharCodeImpl_ = new shaka.util.Lazy(() => {
337
- /** @param {number} size @return {boolean} */
337
+ /**
338
+ * @param {number} size
339
+ * @return {boolean}
340
+ */
338
341
  const supportsChunkSize = (size) => {
339
342
  try {
340
343
  // The compiler will complain about suspicious value if this isn't
@@ -344,7 +347,6 @@ shaka.util.StringUtils.fromCharCodeImpl_ = new shaka.util.Lazy(() => {
344
347
  // This can't use the spread operator, or it blows up on Xbox One.
345
348
  // So we use apply() instead, which is normally not allowed.
346
349
  // See issue #2186 for more details.
347
- // eslint-disable-next-line no-restricted-syntax
348
350
  const foo = String.fromCharCode.apply(null, buffer);
349
351
  goog.asserts.assert(foo, 'Should get value');
350
352
  return foo.length > 0; // Actually use "foo", so it's not compiled out.
@@ -367,7 +369,6 @@ shaka.util.StringUtils.fromCharCodeImpl_ = new shaka.util.Lazy(() => {
367
369
  // This can't use the spread operator, or it blows up on Xbox One.
368
370
  // So we use apply() instead, which is normally not allowed.
369
371
  // See issue #2186 for more details.
370
- // eslint-disable-next-line no-restricted-syntax
371
372
  ret += String.fromCharCode.apply(null, subArray); // Issue #2186
372
373
  }
373
374
  return ret;
@@ -22,7 +22,7 @@ shaka.util.SwitchHistory = class {
22
22
  /** @private {?shaka.extern.Stream} */
23
23
  this.currentText_ = null;
24
24
 
25
- /** @private {!Array.<shaka.extern.TrackChoice>} */
25
+ /** @private {!Array<shaka.extern.TrackChoice>} */
26
26
  this.history_ = [];
27
27
  }
28
28
 
@@ -74,7 +74,7 @@ shaka.util.SwitchHistory = class {
74
74
  * Get a copy of the switch history. This will make sure to expose no internal
75
75
  * references.
76
76
  *
77
- * @return {!Array.<shaka.extern.TrackChoice>}
77
+ * @return {!Array<shaka.extern.TrackChoice>}
78
78
  */
79
79
  getCopy() {
80
80
  const copy = [];
package/lib/util/tXml.js CHANGED
@@ -13,7 +13,7 @@ goog.require('shaka.log');
13
13
  /**
14
14
  * This code is a modified version of the tXml library.
15
15
  *
16
- * @author: Tobias Nickel
16
+ * @author Tobias Nickel
17
17
  * created: 06.04.2015
18
18
  * https://github.com/TobiasNickel/tXml
19
19
  */
@@ -57,6 +57,7 @@ shaka.util.TXml = class {
57
57
  * Parse some data
58
58
  * @param {string} xmlString
59
59
  * @param {string=} expectedRootElemName
60
+ * @param {boolean=} includeParent
60
61
  * @return {shaka.extern.xml.Node | null}
61
62
  */
62
63
  static parseXmlString(xmlString, expectedRootElemName,
@@ -114,7 +115,7 @@ shaka.util.TXml = class {
114
115
  * with no validation and some failure tolerance
115
116
  * @param {string} S your XML to parse
116
117
  * @param {boolean} includeParent
117
- * @return {Array.<shaka.extern.xml.Node>}
118
+ * @return {Array<shaka.extern.xml.Node>}
118
119
  */
119
120
  static parse(S, includeParent) {
120
121
  let pos = 0;
@@ -132,9 +133,12 @@ shaka.util.TXml = class {
132
133
 
133
134
  /**
134
135
  * parsing a list of entries
136
+ * @param {string} tagName
137
+ * @param {boolean=} preserveSpace
138
+ * @return {!Array<shaka.extern.xml.Node | string>}
135
139
  */
136
140
  function parseChildren(tagName, preserveSpace = false) {
137
- /** @type {Array.<shaka.extern.xml.Node | string>} */
141
+ /** @type {!Array<shaka.extern.xml.Node | string>} */
138
142
  const children = [];
139
143
  while (S[pos]) {
140
144
  if (S.charCodeAt(pos) == openBracketCC) {
@@ -152,7 +156,6 @@ shaka.util.TXml = class {
152
156
  indexOfCloseTag = closeTag.indexOf(shortTag);
153
157
  }
154
158
  }
155
- // eslint-disable-next-line no-restricted-syntax
156
159
  if (indexOfCloseTag == -1) {
157
160
  const parsedText = S.substring(0, pos).split('\n');
158
161
  throw new Error(
@@ -229,7 +232,8 @@ shaka.util.TXml = class {
229
232
  }
230
233
 
231
234
  /**
232
- * returns the text outside of texts until the first '<'
235
+ * returns the text outside of texts until the first '<'
236
+ * @return {string}
233
237
  */
234
238
  function parseText() {
235
239
  const start = pos;
@@ -305,9 +309,9 @@ shaka.util.TXml = class {
305
309
  pos--;
306
310
  }
307
311
  if (name.startsWith('xmlns:')) {
308
- const segs = name.split(':');
312
+ const segments = name.split(':');
309
313
  shaka.util.TXml.setKnownNameSpace(
310
- /** @type {string} */ (value), segs[1]);
314
+ /** @type {string} */ (value), segments[1]);
311
315
  }
312
316
  if (tagName === 'tt' &&
313
317
  name === 'xml:space' &&
@@ -353,9 +357,9 @@ shaka.util.TXml = class {
353
357
  */
354
358
  function parseString() {
355
359
  const startChar = S[pos];
356
- const startpos = pos + 1;
357
- pos = S.indexOf(startChar, startpos);
358
- return S.slice(startpos, pos);
360
+ const startPos = pos + 1;
361
+ pos = S.indexOf(startChar, startPos);
362
+ return S.slice(startPos, pos);
359
363
  }
360
364
 
361
365
  return parseChildren('');
@@ -382,7 +386,7 @@ shaka.util.TXml = class {
382
386
  /**
383
387
  * gets child XML elements.
384
388
  * @param {!shaka.extern.xml.Node} elem The parent XML element.
385
- * @return {!Array.<!shaka.extern.xml.Node>} The child XML elements.
389
+ * @return {!Array<!shaka.extern.xml.Node>} The child XML elements.
386
390
  */
387
391
  static getChildNodes(elem) {
388
392
  const found = [];
@@ -401,7 +405,7 @@ shaka.util.TXml = class {
401
405
  * Finds child XML elements.
402
406
  * @param {!shaka.extern.xml.Node} elem The parent XML element.
403
407
  * @param {string} name The child XML element's tag name.
404
- * @return {!Array.<!shaka.extern.xml.Node>} The child XML elements.
408
+ * @return {!Array<!shaka.extern.xml.Node>} The child XML elements.
405
409
  */
406
410
  static findChildren(elem, name) {
407
411
  const found = [];
@@ -459,8 +463,8 @@ shaka.util.TXml = class {
459
463
  * Finds child XML elements recursively.
460
464
  * @param {!shaka.extern.xml.Node} elem The parent XML element.
461
465
  * @param {string} name The child XML element's tag name.
462
- * @param {!Array.<!shaka.extern.xml.Node>} found accumulator for found nodes
463
- * @return {!Array.<!shaka.extern.xml.Node>} The child XML elements.
466
+ * @param {!Array<!shaka.extern.xml.Node>} found accumulator for found nodes
467
+ * @return {!Array<!shaka.extern.xml.Node>} The child XML elements.
464
468
  */
465
469
  static getElementsByTagName(elem, name, found = []) {
466
470
  if (elem.tagName === name) {
@@ -516,7 +520,7 @@ shaka.util.TXml = class {
516
520
  * @param {function(string): (T|null)} parseFunction A function that parses
517
521
  * the attribute.
518
522
  * @param {(T|null)=} defaultValue The attribute's default value, if not
519
- * specified, the attibute's default value is null.
523
+ * specified, the attribute's default value is null.
520
524
  * @return {(T|null)} The parsed attribute on success, or the attribute's
521
525
  * default value if the attribute does not exist or could not be parsed.
522
526
  * @template T
@@ -550,7 +554,7 @@ shaka.util.TXml = class {
550
554
  * @param {!shaka.extern.xml.Node} elem The parent XML element.
551
555
  * @param {string} ns The child XML element's namespace URI.
552
556
  * @param {string} name The child XML element's local name.
553
- * @return {!Array.<!shaka.extern.xml.Node>} The child XML elements.
557
+ * @return {!Array<!shaka.extern.xml.Node>} The child XML elements.
554
558
  */
555
559
  static findChildrenNS(elem, ns, name) {
556
560
  const schemaNS = shaka.util.TXml.getKnownNameSpace(ns);
@@ -569,7 +573,7 @@ shaka.util.TXml = class {
569
573
  /**
570
574
  * Gets a namespace-qualified attribute.
571
575
  * @param {!shaka.extern.xml.Node} elem The element to get from.
572
- * @param {!Array.<string>} nsList The lis of namespace URIs.
576
+ * @param {!Array<string>} nsList The lis of namespace URIs.
573
577
  * @param {string} name The local name of the attribute.
574
578
  * @return {?string} The attribute's value, or null if not present.
575
579
  */
@@ -859,6 +863,7 @@ shaka.util.TXml = class {
859
863
  * @param {!Array<shaka.extern.xml.Node>} nodes
860
864
  * @param {!string} attribute
861
865
  * @param {!number} value
866
+ * @return {?number}
862
867
  * @private
863
868
  */
864
869
  static nodePositionByAttribute_(nodes, attribute, value) {