shaka-player 4.3.10 → 4.4.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 (316) hide show
  1. package/.github/workflows/release-please.yaml +7 -0
  2. package/AUTHORS +9 -0
  3. package/CHANGELOG.md +295 -200
  4. package/CONTRIBUTORS +8 -0
  5. package/LICENSE +32 -0
  6. package/README.md +63 -10
  7. package/demo/autoTemplate.txt +40 -5
  8. package/demo/close_button.js +11 -0
  9. package/demo/common/asset.js +13 -1
  10. package/demo/common/assets.js +53 -3
  11. package/demo/common/message_ids.js +25 -1
  12. package/demo/config.js +69 -10
  13. package/demo/customWarning.txt +2 -2
  14. package/demo/index.html +3 -1
  15. package/demo/locales/en.json +26 -2
  16. package/demo/locales/source.json +105 -9
  17. package/demo/main.js +52 -9
  18. package/demo/search.js +3 -1
  19. package/demo/service_worker.js +3 -0
  20. package/dist/controls.css +1 -1
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +124 -112
  23. package/dist/demo.compiled.debug.map +3 -3
  24. package/dist/demo.compiled.js +124 -112
  25. package/dist/demo.compiled.map +3 -3
  26. package/dist/deps.js +46 -28
  27. package/dist/locales.js +28 -28
  28. package/dist/receiver.compiled.debug.js +52 -46
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +52 -46
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +676 -391
  33. package/dist/shaka-player.compiled.debug.d.ts +676 -391
  34. package/dist/shaka-player.compiled.debug.externs.js +1188 -616
  35. package/dist/shaka-player.compiled.debug.js +1045 -700
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +1188 -616
  38. package/dist/shaka-player.compiled.js +1094 -933
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +677 -392
  41. package/dist/shaka-player.ui.debug.d.ts +677 -392
  42. package/dist/shaka-player.ui.debug.externs.js +1023 -451
  43. package/dist/shaka-player.ui.debug.js +1100 -739
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +1023 -451
  46. package/dist/shaka-player.ui.js +1244 -1071
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/tutorials/ad_monetization.md +15 -1
  49. package/docs/tutorials/application-level-redirects.md +1 -1
  50. package/docs/tutorials/config.md +9 -5
  51. package/docs/tutorials/drm-config.md +61 -0
  52. package/docs/tutorials/fairplay.md +19 -4
  53. package/docs/tutorials/license-server-auth.md +4 -4
  54. package/docs/tutorials/license-wrapping.md +2 -2
  55. package/docs/tutorials/ui-customization.md +7 -3
  56. package/docs/tutorials/ui.md +7 -7
  57. package/docs/tutorials/upgrade.md +3 -0
  58. package/externs/ima.js +123 -2
  59. package/externs/isoboxer.js +217 -0
  60. package/externs/mediakeys.js +18 -0
  61. package/externs/mux.js +1 -105
  62. package/externs/pictureinpicture.js +48 -0
  63. package/externs/shaka/ads.js +61 -1
  64. package/externs/shaka/cea.js +110 -0
  65. package/externs/shaka/codecs.js +32 -0
  66. package/externs/shaka/manifest.js +101 -1
  67. package/externs/shaka/manifest_parser.js +0 -10
  68. package/externs/shaka/net.js +36 -7
  69. package/externs/shaka/offline.js +8 -1
  70. package/externs/shaka/player.js +220 -21
  71. package/externs/shaka/text.js +1 -382
  72. package/externs/shaka/transmuxer.js +59 -0
  73. package/karma.conf.js +13 -0
  74. package/lib/abr/simple_abr_manager.js +6 -3
  75. package/lib/ads/ad_manager.js +60 -2
  76. package/lib/ads/client_side_ad.js +86 -0
  77. package/lib/ads/client_side_ad_manager.js +79 -3
  78. package/lib/ads/server_side_ad.js +86 -0
  79. package/lib/ads/server_side_ad_manager.js +13 -0
  80. package/lib/cast/cast_utils.js +1 -0
  81. package/lib/cea/cea608_data_channel.js +24 -14
  82. package/lib/cea/cea608_memory.js +5 -2
  83. package/lib/cea/cea708_service.js +8 -9
  84. package/lib/cea/cea708_window.js +1 -2
  85. package/lib/cea/cea_decoder.js +23 -6
  86. package/lib/cea/cea_utils.js +25 -2
  87. package/lib/cea/dummy_caption_decoder.js +21 -0
  88. package/lib/cea/dummy_cea_parser.js +1 -3
  89. package/lib/cea/mp4_cea_parser.js +15 -10
  90. package/lib/cea/ts_cea_parser.js +69 -0
  91. package/lib/dash/content_protection.js +32 -2
  92. package/lib/dash/dash_parser.js +93 -31
  93. package/lib/dash/mpd_utils.js +6 -25
  94. package/lib/dash/segment_base.js +12 -14
  95. package/lib/dash/segment_list.js +10 -6
  96. package/lib/dash/segment_template.js +349 -32
  97. package/lib/dependencies/all.js +8 -0
  98. package/lib/hls/hls_classes.js +33 -8
  99. package/lib/hls/hls_parser.js +805 -191
  100. package/lib/hls/manifest_text_parser.js +4 -14
  101. package/lib/media/adaptation_set_criteria.js +43 -3
  102. package/lib/media/closed_caption_parser.js +74 -12
  103. package/lib/media/drm_engine.js +189 -49
  104. package/lib/media/gap_jumping_controller.js +1 -1
  105. package/lib/media/manifest_parser.js +34 -0
  106. package/lib/media/media_source_capabilities.js +10 -0
  107. package/lib/media/media_source_engine.js +475 -231
  108. package/lib/media/playhead.js +1 -1
  109. package/lib/media/presentation_timeline.js +62 -0
  110. package/lib/media/segment_index.js +50 -14
  111. package/lib/media/segment_prefetch.js +277 -0
  112. package/lib/media/segment_reference.js +70 -4
  113. package/lib/media/streaming_engine.js +258 -81
  114. package/lib/media/video_wrapper.js +15 -7
  115. package/lib/mss/content_protection.js +353 -0
  116. package/lib/mss/mss_parser.js +1081 -0
  117. package/lib/net/networking_engine.js +60 -10
  118. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  119. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  120. package/lib/offline/manifest_converter.js +6 -0
  121. package/lib/offline/storage.js +2 -4
  122. package/lib/player.js +309 -105
  123. package/lib/polyfill/media_capabilities.js +43 -3
  124. package/lib/polyfill/patchedmediakeys_apple.js +5 -0
  125. package/lib/polyfill/patchedmediakeys_nop.js +5 -0
  126. package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
  127. package/lib/text/cue.js +208 -130
  128. package/lib/text/lrc_text_parser.js +2 -2
  129. package/lib/text/mp4_vtt_parser.js +2 -2
  130. package/lib/text/sbv_text_parser.js +1 -1
  131. package/lib/text/simple_text_displayer.js +2 -1
  132. package/lib/text/srt_text_parser.js +9 -1
  133. package/lib/text/ssa_text_parser.js +2 -2
  134. package/lib/text/text_engine.js +3 -21
  135. package/lib/text/text_utils.js +71 -20
  136. package/lib/text/ui_text_displayer.js +14 -14
  137. package/lib/text/vtt_text_parser.js +22 -14
  138. package/lib/transmuxer/aac_transmuxer.js +224 -0
  139. package/lib/transmuxer/ac3.js +142 -0
  140. package/lib/transmuxer/ac3_transmuxer.js +212 -0
  141. package/lib/transmuxer/adts.js +232 -0
  142. package/lib/transmuxer/ec3.js +103 -0
  143. package/lib/transmuxer/ec3_transmuxer.js +212 -0
  144. package/lib/transmuxer/h264.js +324 -0
  145. package/lib/transmuxer/mp3_transmuxer.js +204 -0
  146. package/lib/transmuxer/mpeg_audio.js +196 -0
  147. package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
  148. package/lib/transmuxer/mss_transmuxer.js +389 -0
  149. package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
  150. package/lib/transmuxer/transmuxer_engine.js +161 -0
  151. package/lib/transmuxer/ts_transmuxer.js +738 -0
  152. package/lib/util/cmcd_manager.js +131 -71
  153. package/lib/util/error.js +62 -26
  154. package/lib/util/exp_golomb.js +220 -0
  155. package/lib/util/fairplay_utils.js +64 -13
  156. package/lib/util/id3_utils.js +56 -3
  157. package/lib/util/language_utils.js +11 -8
  158. package/lib/util/manifest_parser_utils.js +3 -0
  159. package/lib/util/mime_utils.js +44 -7
  160. package/lib/util/mp4_box_parsers.js +233 -5
  161. package/lib/util/mp4_generator.js +1381 -0
  162. package/lib/util/periods.js +40 -34
  163. package/lib/util/platform.js +17 -3
  164. package/lib/util/player_configuration.js +38 -1
  165. package/lib/util/stream_utils.js +138 -27
  166. package/lib/util/text_parser.js +1 -1
  167. package/lib/util/ts_parser.js +470 -47
  168. package/lib/util/xml_utils.js +16 -3
  169. package/maintained-branches.md +2 -2
  170. package/package.json +3 -1
  171. package/roadmap.md +21 -6
  172. package/shaka-player.uncompiled.js +18 -0
  173. package/test/abr/simple_abr_manager_unit.js +32 -4
  174. package/test/ads/ad_manager_unit.js +18 -2
  175. package/test/cast/cast_utils_unit.js +7 -2
  176. package/test/cea/cea608_memory_unit.js +24 -0
  177. package/test/cea/cea_decoder_unit.js +33 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +93 -3
  179. package/test/dash/dash_parser_live_unit.js +56 -13
  180. package/test/dash/dash_parser_manifest_unit.js +70 -2
  181. package/test/dash/dash_parser_segment_base_unit.js +14 -9
  182. package/test/dash/dash_parser_segment_template_unit.js +286 -6
  183. package/test/demo/demo_unit.js +5 -1
  184. package/test/hls/hls_live_unit.js +188 -28
  185. package/test/hls/hls_parser_unit.js +250 -109
  186. package/test/hls/manifest_text_parser_unit.js +18 -13
  187. package/test/media/adaptation_set_criteria_unit.js +46 -14
  188. package/test/media/adaptation_set_unit.js +2 -0
  189. package/test/media/drm_engine_integration.js +11 -8
  190. package/test/media/drm_engine_unit.js +220 -7
  191. package/test/media/media_source_engine_integration.js +122 -38
  192. package/test/media/media_source_engine_unit.js +133 -153
  193. package/test/media/playhead_unit.js +3 -0
  194. package/test/media/presentation_timeline_unit.js +51 -0
  195. package/test/media/segment_index_unit.js +52 -0
  196. package/test/media/segment_prefetch_unit.js +243 -0
  197. package/test/media/streaming_engine_integration.js +9 -3
  198. package/test/media/streaming_engine_unit.js +440 -59
  199. package/test/mss/mss_parser_content_protection_unit.js +49 -0
  200. package/test/mss/mss_parser_unit.js +472 -0
  201. package/test/mss/mss_player_integration.js +123 -0
  202. package/test/offline/manifest_convert_unit.js +9 -0
  203. package/test/offline/storage_integration.js +6 -1
  204. package/test/player_unit.js +386 -133
  205. package/test/polyfill/media_capabilities_unit.js +18 -0
  206. package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
  207. package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
  208. package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
  209. package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
  210. package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
  211. package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
  212. package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
  213. package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
  214. package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
  215. package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
  216. package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
  217. package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
  218. package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
  219. package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
  220. package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
  221. package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
  222. package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
  223. package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
  224. package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
  225. package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
  226. package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
  227. package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
  228. package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
  229. package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
  230. package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
  231. package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
  232. package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
  233. package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
  234. package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
  235. package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
  236. package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
  237. package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
  238. package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
  239. package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
  240. package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
  241. package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
  242. package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
  243. package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
  244. package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
  245. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
  246. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
  247. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
  248. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
  249. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
  250. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
  251. package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
  252. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
  253. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
  254. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
  255. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
  256. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
  257. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
  258. package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
  259. package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
  260. package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
  261. package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
  262. package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
  263. package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
  264. package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
  265. package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
  266. package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
  267. package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
  268. package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
  269. package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
  270. package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
  271. package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
  272. package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
  273. package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
  274. package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
  275. package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
  276. package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
  277. package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
  278. package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
  279. package/test/test/boot.js +7 -2
  280. package/test/test/util/fake_ad.js +99 -0
  281. package/test/test/util/fake_ad_manager.js +7 -1
  282. package/test/test/util/fake_networking_engine.js +41 -14
  283. package/test/test/util/fake_segment_prefetch.js +81 -0
  284. package/test/test/util/manifest_generator.js +19 -5
  285. package/test/test/util/mss_parser_util.js +115 -0
  286. package/test/test/util/offline_utils.js +1 -0
  287. package/test/test/util/simple_fakes.js +12 -3
  288. package/test/test/util/streaming_engine_util.js +14 -4
  289. package/test/test/util/ttml_utils.js +2 -2
  290. package/test/text/cue_integration.js +1 -1
  291. package/test/text/srt_text_parser_unit.js +78 -0
  292. package/test/text/text_engine_unit.js +17 -8
  293. package/test/text/ui_text_displayer_unit.js +4 -4
  294. package/test/text/vtt_text_parser_unit.js +59 -40
  295. package/test/text/web_vtt_generator_unit.js +9 -3
  296. package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
  297. package/test/transmuxer/transmuxer_engine_integration.js +60 -0
  298. package/test/transmuxer/transmuxer_integration.js +296 -0
  299. package/test/util/cmcd_manager_unit.js +162 -13
  300. package/test/util/language_utils_unit.js +9 -2
  301. package/test/util/mp4_box_parsers_unit.js +39 -4
  302. package/test/util/periods_unit.js +34 -26
  303. package/test/util/stream_utils_unit.js +60 -107
  304. package/ui/controls.js +19 -1
  305. package/ui/controls.less +1 -0
  306. package/ui/externs/ui.js +11 -1
  307. package/ui/language_utils.js +15 -0
  308. package/ui/less/thumbnails.less +26 -0
  309. package/ui/locales/oc.json +42 -0
  310. package/ui/pip_button.js +83 -2
  311. package/ui/range_element.js +15 -0
  312. package/ui/resolution_selection.js +9 -2
  313. package/ui/seek_bar.js +223 -0
  314. package/ui/ui.js +5 -0
  315. package/lib/cea/i_caption_decoder.js +0 -51
  316. package/lib/cea/i_cea_parser.js +0 -68
@@ -24,6 +24,7 @@ goog.require('shaka.util.MimeUtils');
24
24
  *
25
25
  * @implements {shaka.util.IReleasable}
26
26
  * @final
27
+ * @export
27
28
  */
28
29
  shaka.util.PeriodCombiner = class {
29
30
  /** */
@@ -72,12 +73,20 @@ shaka.util.PeriodCombiner = class {
72
73
  this.variants_ = [];
73
74
  }
74
75
 
75
- /** @return {!Array.<shaka.extern.Variant>} */
76
+ /**
77
+ * @return {!Array.<shaka.extern.Variant>}
78
+ *
79
+ * @export
80
+ */
76
81
  getVariants() {
77
82
  return this.variants_;
78
83
  }
79
84
 
80
- /** @return {!Array.<shaka.extern.Stream>} */
85
+ /**
86
+ * @return {!Array.<shaka.extern.Stream>}
87
+ *
88
+ * @export
89
+ */
81
90
  getTextStreams() {
82
91
  // Return a copy of the array because makeTextStreamsForClosedCaptions
83
92
  // may make changes to the contents of the array. Those changes should not
@@ -85,15 +94,21 @@ shaka.util.PeriodCombiner = class {
85
94
  return this.textStreams_.slice();
86
95
  }
87
96
 
88
- /** @return {!Array.<shaka.extern.Stream>} */
97
+ /**
98
+ * @return {!Array.<shaka.extern.Stream>}
99
+ *
100
+ * @export
101
+ */
89
102
  getImageStreams() {
90
103
  return this.imageStreams_;
91
104
  }
92
105
 
93
106
  /**
94
- * @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
107
+ * @param {!Array.<shaka.extern.Period>} periods
95
108
  * @param {boolean} isDynamic
96
109
  * @return {!Promise}
110
+ *
111
+ * @export
97
112
  */
98
113
  async combinePeriods(periods, isDynamic) {
99
114
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
@@ -200,6 +215,7 @@ shaka.util.PeriodCombiner = class {
200
215
  variants.push({
201
216
  id,
202
217
  language: stream.language,
218
+ disabledUntilTime: 0,
203
219
  primary: stream.primary,
204
220
  audio: stream.type == ContentType.AUDIO ? stream : null,
205
221
  video: stream.type == ContentType.VIDEO ? stream : null,
@@ -228,6 +244,7 @@ shaka.util.PeriodCombiner = class {
228
244
  variants.push({
229
245
  id,
230
246
  language: audio.language,
247
+ disabledUntilTime: 0,
231
248
  primary: audio.primary,
232
249
  audio,
233
250
  video,
@@ -245,7 +262,7 @@ shaka.util.PeriodCombiner = class {
245
262
  }
246
263
 
247
264
  /**
248
- * @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
265
+ * @param {!Array.<shaka.extern.Period>} periods
249
266
  * @private
250
267
  */
251
268
  static filterOutAudioStreamDuplicates_(periods) {
@@ -282,7 +299,7 @@ shaka.util.PeriodCombiner = class {
282
299
  }
283
300
 
284
301
  /**
285
- * @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
302
+ * @param {!Array.<shaka.extern.Period>} periods
286
303
  * @private
287
304
  */
288
305
  static filterOutTextStreamDuplicates_(periods) {
@@ -316,7 +333,7 @@ shaka.util.PeriodCombiner = class {
316
333
  }
317
334
 
318
335
  /**
319
- * @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
336
+ * @param {!Array.<shaka.extern.Period>} periods
320
337
  * @private
321
338
  */
322
339
  static filterOutVideoStreamDuplicates_(periods) {
@@ -353,7 +370,7 @@ shaka.util.PeriodCombiner = class {
353
370
  }
354
371
 
355
372
  /**
356
- * @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
373
+ * @param {!Array.<shaka.extern.Period>} periods
357
374
  * @private
358
375
  */
359
376
  static filterOutImageStreamDuplicates_(periods) {
@@ -1050,8 +1067,13 @@ shaka.util.PeriodCombiner = class {
1050
1067
  * @private
1051
1068
  */
1052
1069
  static areAVStreamsCompatible_(outputStream, candidate) {
1053
- const getCodec = (codecs) =>
1054
- shaka.util.MimeUtils.getNormalizedCodec(codecs);
1070
+ const getCodec = (codecs) => {
1071
+ if (!shaka.util.PeriodCombiner.memoizedCodecs.has(codecs)) {
1072
+ const normalizedCodec = shaka.util.MimeUtils.getNormalizedCodec(codecs);
1073
+ shaka.util.PeriodCombiner.memoizedCodecs.set(codecs, normalizedCodec);
1074
+ }
1075
+ return shaka.util.PeriodCombiner.memoizedCodecs.get(codecs);
1076
+ };
1055
1077
  // Check MIME type and codecs, which should always be the same.
1056
1078
  if (candidate.mimeType != outputStream.mimeType ||
1057
1079
  getCodec(candidate.codecs) != getCodec(outputStream.codecs)) {
@@ -1478,6 +1500,7 @@ shaka.util.PeriodCombiner = class {
1478
1500
  mimeType: '',
1479
1501
  codecs: '',
1480
1502
  language: '',
1503
+ originalLanguage: null,
1481
1504
  label: null,
1482
1505
  width: null,
1483
1506
  height: null,
@@ -1517,6 +1540,7 @@ shaka.util.PeriodCombiner = class {
1517
1540
  drmInfos: [],
1518
1541
  keyIds: new Set(),
1519
1542
  language: '',
1543
+ originalLanguage: null,
1520
1544
  label: null,
1521
1545
  type,
1522
1546
  primary: false,
@@ -1528,6 +1552,7 @@ shaka.util.PeriodCombiner = class {
1528
1552
  audioSamplingRate: null,
1529
1553
  spatialAudio: false,
1530
1554
  closedCaptions: null,
1555
+ accessibilityPurpose: null,
1531
1556
  external: false,
1532
1557
  };
1533
1558
  }
@@ -1616,30 +1641,6 @@ shaka.util.PeriodCombiner = class {
1616
1641
  }
1617
1642
  };
1618
1643
 
1619
- /**
1620
- * @typedef {{
1621
- * id: string,
1622
- * audioStreams: !Array.<shaka.extern.Stream>,
1623
- * videoStreams: !Array.<shaka.extern.Stream>,
1624
- * textStreams: !Array.<shaka.extern.Stream>,
1625
- * imageStreams: !Array.<shaka.extern.Stream>
1626
- * }}
1627
- *
1628
- * @description Contains the streams from one DASH period.
1629
- *
1630
- * @property {string} id
1631
- * The Period ID.
1632
- * @property {!Array.<shaka.extern.Stream>} audioStreams
1633
- * The audio streams from one Period.
1634
- * @property {!Array.<shaka.extern.Stream>} videoStreams
1635
- * The video streams from one Period.
1636
- * @property {!Array.<shaka.extern.Stream>} textStreams
1637
- * The text streams from one Period.
1638
- * @property {!Array.<shaka.extern.Stream>} imageStreams
1639
- * The image streams from one Period.
1640
- */
1641
- shaka.util.PeriodCombiner.Period;
1642
-
1643
1644
  /**
1644
1645
  * @enum {number}
1645
1646
  */
@@ -1648,3 +1649,8 @@ shaka.util.PeriodCombiner.BetterOrWorse = {
1648
1649
  EQUAL: 0,
1649
1650
  WORSE: -1,
1650
1651
  };
1652
+
1653
+ /**
1654
+ * @private {Map<string, string>}
1655
+ */
1656
+ shaka.util.PeriodCombiner.memoizedCodecs = new Map();
@@ -193,10 +193,10 @@ shaka.util.Platform = class {
193
193
  * @return {boolean}
194
194
  */
195
195
  static isChrome() {
196
- // The Edge user agent will also contain the "Chrome" keyword, so we need
197
- // to make sure this is not Edge.
196
+ // The Edge Legacy user agent will also contain the "Chrome" keyword, so we
197
+ // need to make sure this is not Edge Legacy.
198
198
  return shaka.util.Platform.userAgentContains_('Chrome') &&
199
- !shaka.util.Platform.isEdge();
199
+ !shaka.util.Platform.isLegacyEdge();
200
200
  }
201
201
 
202
202
  /**
@@ -432,6 +432,20 @@ shaka.util.Platform = class {
432
432
  return Platform.isTizen() || Platform.isXboxOne();
433
433
  }
434
434
 
435
+ /**
436
+ * Returns true if the platform supports SourceBuffer "sequence mode".
437
+ *
438
+ * @return {boolean}
439
+ */
440
+ static supportsSequenceMode() {
441
+ const Platform = shaka.util.Platform;
442
+ if (Platform.isTizen3() || Platform.isTizen2() ||
443
+ Platform.isWebOS3() || Platform.isPS4()) {
444
+ return false;
445
+ }
446
+ return true;
447
+ }
448
+
435
449
  /**
436
450
  * Returns true if MediaKeys is polyfilled
437
451
  *
@@ -68,6 +68,8 @@ shaka.util.PlayerConfiguration = class {
68
68
  clearKeys: {}, // key is arbitrary key system ID, value must be string
69
69
  advanced: {}, // key is arbitrary key system ID, value is a record type
70
70
  delayLicenseRequestUntilPlayed: false,
71
+ persistentSessionOnlinePlayback: false,
72
+ persistentSessionsMetadata: [],
71
73
  initDataTransform: (initData, initDataType, drmInfo) => {
72
74
  const keySystem = drmInfo.keySystem;
73
75
  if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
@@ -87,6 +89,7 @@ shaka.util.PlayerConfiguration = class {
87
89
  // change in the PSSH in media segments. We need to parse PSSH from media
88
90
  // segments to detect key changes.
89
91
  parseInbandPsshEnabled: shaka.util.Platform.isXboxOne(),
92
+ minHdcpVersion: '',
90
93
  };
91
94
 
92
95
  const manifest = {
@@ -98,6 +101,7 @@ shaka.util.PlayerConfiguration = class {
98
101
  disableThumbnails: false,
99
102
  defaultPresentationDelay: 0,
100
103
  segmentRelativeVttTiming: false,
104
+ raiseFatalErrorOnManifestUpdateRequestFailure: false,
101
105
  dash: {
102
106
  clockSyncUri: '',
103
107
  ignoreDrmInfo: false,
@@ -140,6 +144,22 @@ shaka.util.PlayerConfiguration = class {
140
144
  'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
141
145
  useSafariBehaviorForLive: true,
142
146
  liveSegmentsDelay: 3,
147
+ sequenceMode: shaka.util.Platform.supportsSequenceMode(),
148
+ ignoreManifestTimestampsInSegmentsMode: false,
149
+ },
150
+ mss: {
151
+ manifestPreprocessor: (element) => {
152
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
153
+ [element],
154
+ element);
155
+ },
156
+ sequenceMode: false,
157
+ keySystemsBySystemId: {
158
+ '9a04f079-9840-4286-ab92-e65be0885f95':
159
+ 'com.microsoft.playready',
160
+ '79f0049a-4098-8642-ab92-e65be0885f95':
161
+ 'com.microsoft.playready',
162
+ },
143
163
  },
144
164
  };
145
165
 
@@ -163,8 +183,8 @@ shaka.util.PlayerConfiguration = class {
163
183
  alwaysStreamText: false,
164
184
  startAtSegmentBoundary: false,
165
185
  gapDetectionThreshold: 0.5,
186
+ gapJumpTimerTime: 0.25 /* seconds */,
166
187
  durationBackoff: 1,
167
- forceTransmux: false,
168
188
  // Offset by 5 seconds since Chromecast takes a few seconds to start
169
189
  // playing after a seek, even when buffered.
170
190
  safeSeekOffset: 5,
@@ -187,6 +207,12 @@ shaka.util.PlayerConfiguration = class {
187
207
  observeQualityChanges: false,
188
208
  maxDisabledTime: 30,
189
209
  parsePrftBox: false,
210
+ // When low latency streaming is enabled, segmentPrefetchLimit will
211
+ // default to 2 if not specified.
212
+ segmentPrefetchLimit: 0,
213
+ liveSync: false,
214
+ liveSyncMaxLatency: 1,
215
+ liveSyncPlaybackRate: 1.1,
190
216
  };
191
217
 
192
218
  // WebOS, Tizen, and Chromecast have long hardware pipelines that respond
@@ -263,6 +289,8 @@ shaka.util.PlayerConfiguration = class {
263
289
  restrictToElementSize: false,
264
290
  restrictToScreenSize: false,
265
291
  ignoreDevicePixelRatio: false,
292
+ clearBufferSwitch: false,
293
+ safeMarginSwitch: 0,
266
294
  };
267
295
 
268
296
  const cmcd = {
@@ -281,6 +309,12 @@ shaka.util.PlayerConfiguration = class {
281
309
 
282
310
  const mediaSource = {
283
311
  sourceBufferExtraFeatures: '',
312
+ forceTransmux: false,
313
+ insertFakeEncryptionInInit: true,
314
+ };
315
+
316
+ const ads = {
317
+ customPlayheadTracker: false,
284
318
  };
285
319
 
286
320
  const AutoShowText = shaka.config.AutoShowText;
@@ -296,10 +330,12 @@ shaka.util.PlayerConfiguration = class {
296
330
  abr: abr,
297
331
  autoShowText: AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED,
298
332
  preferredAudioLanguage: '',
333
+ preferredAudioLabel: '',
299
334
  preferredTextLanguage: '',
300
335
  preferredVariantRole: '',
301
336
  preferredTextRole: '',
302
337
  preferredAudioChannelCount: 2,
338
+ preferredVideoHdrLevel: 'AUTO',
303
339
  preferredVideoCodecs: [],
304
340
  preferredAudioCodecs: [],
305
341
  preferForcedSubs: false,
@@ -321,6 +357,7 @@ shaka.util.PlayerConfiguration = class {
321
357
  textDisplayFactory: () => null,
322
358
  cmcd: cmcd,
323
359
  lcevc: lcevc,
360
+ ads: ads,
324
361
  };
325
362
 
326
363
  // Add this callback so that we can reference the preferred audio language
@@ -21,6 +21,7 @@ goog.requireType('shaka.media.DrmEngine');
21
21
 
22
22
  /**
23
23
  * @summary A set of utility functions for dealing with Streams and Manifests.
24
+ * @export
24
25
  */
25
26
  shaka.util.StreamUtils = class {
26
27
  /**
@@ -39,6 +40,7 @@ shaka.util.StreamUtils = class {
39
40
  const StreamUtils = shaka.util.StreamUtils;
40
41
 
41
42
  let variants = manifest.variants;
43
+
42
44
  // To start, choose the codecs based on configured preferences if available.
43
45
  if (preferredVideoCodecs.length || preferredAudioCodecs.length) {
44
46
  variants = StreamUtils.choosePreferredCodecs(variants,
@@ -321,6 +323,7 @@ shaka.util.StreamUtils = class {
321
323
  * This is applied separately from user restrictions because the setting
322
324
  * should not be easily replaced by the user's configuration.
323
325
  * @return {boolean}
326
+ * @export
324
327
  */
325
328
  static meetsRestrictions(variant, restrictions, maxHwRes) {
326
329
  /** @type {function(number, number, number):boolean} */
@@ -330,13 +333,6 @@ shaka.util.StreamUtils = class {
330
333
 
331
334
  const video = variant.video;
332
335
 
333
- if (variant.disabledUntilTime != 0) {
334
- if (variant.disabledUntilTime > Date.now() / 1000) {
335
- return false;
336
- }
337
- variant.disabledUntilTime = 0;
338
- }
339
-
340
336
  // |video.width| and |video.height| can be undefined, which breaks
341
337
  // the math, so make sure they are there first.
342
338
  if (video && video.width && video.height) {
@@ -409,8 +405,7 @@ shaka.util.StreamUtils = class {
409
405
  * @param {?shaka.extern.Variant} currentVariant
410
406
  * @param {shaka.extern.Manifest} manifest
411
407
  */
412
- static async filterManifest(
413
- drmEngine, currentVariant, manifest) {
408
+ static async filterManifest(drmEngine, currentVariant, manifest) {
414
409
  await shaka.util.StreamUtils.filterManifestByMediaCapabilities(manifest,
415
410
  manifest.offlineSessionIds.length > 0);
416
411
  shaka.util.StreamUtils.filterManifestByCurrentVariant(
@@ -433,7 +428,8 @@ shaka.util.StreamUtils = class {
433
428
  'MediaCapabilities should be valid.');
434
429
 
435
430
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
436
- manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
431
+ manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
432
+ /** preferredKeySystems= */ []);
437
433
  manifest.variants = manifest.variants.filter((variant) => {
438
434
  // See: https://github.com/shaka-project/shaka-player/issues/3860
439
435
  const video = variant.video;
@@ -521,6 +517,71 @@ shaka.util.StreamUtils = class {
521
517
  }
522
518
 
523
519
 
520
+ /**
521
+ * Constructs a string out of an object, similar to the JSON.stringify method.
522
+ * Unlike that method, this guarantees that the order of the keys is
523
+ * alphabetical, so it can be used as a way to reliably compare two objects.
524
+ *
525
+ * @param {!Object} obj
526
+ * @return {string}
527
+ * @private
528
+ */
529
+ static alphabeticalKeyOrderStringify_(obj) {
530
+ const keys = [];
531
+ for (const key in obj) {
532
+ keys.push(key);
533
+ }
534
+ // Alphabetically sort the keys, so they will be in a reliable order.
535
+ keys.sort();
536
+
537
+ const terms = [];
538
+ for (const key of keys) {
539
+ const escapedKey = JSON.stringify(key);
540
+ const value = obj[key];
541
+ if (value instanceof Object) {
542
+ const stringifiedValue =
543
+ shaka.util.StreamUtils.alphabeticalKeyOrderStringify_(value);
544
+ terms.push(escapedKey + ':' + stringifiedValue);
545
+ } else {
546
+ const escapedValue = JSON.stringify(value);
547
+ terms.push(escapedKey + ':' + escapedValue);
548
+ }
549
+ }
550
+ return '{' + terms.join(',') + '}';
551
+ }
552
+
553
+
554
+ /**
555
+ * Queries mediaCapabilities for the decoding info for that decoding config,
556
+ * and assigns it to the given variant.
557
+ * If that query has been done before, instead return a cached result.
558
+ * @param {!shaka.extern.Variant} variant
559
+ * @param {!MediaDecodingConfiguration} decodingConfig
560
+ * @private
561
+ */
562
+ static async getDecodingInfosForVariant_(variant, decodingConfig) {
563
+ const cacheKey =
564
+ shaka.util.StreamUtils.alphabeticalKeyOrderStringify_(decodingConfig);
565
+
566
+ try {
567
+ const cache = shaka.util.StreamUtils.decodingConfigCache_;
568
+ if (cache[cacheKey]) {
569
+ shaka.log.v2('Using cached results of mediaCapabilities.decodingInfo',
570
+ 'for key', cacheKey);
571
+ variant.decodingInfos.push(cache[cacheKey]);
572
+ } else {
573
+ const result =
574
+ await navigator.mediaCapabilities.decodingInfo(decodingConfig);
575
+ cache[cacheKey] = result;
576
+ variant.decodingInfos.push(result);
577
+ }
578
+ } catch (e) {
579
+ shaka.log.info('MediaCapabilities.decodingInfo() failed.',
580
+ JSON.stringify(decodingConfig), e);
581
+ }
582
+ }
583
+
584
+
524
585
  /**
525
586
  * Get the decodingInfo results of the variants via MediaCapabilities.
526
587
  * This should be called after the DrmEngine is created and configured, and
@@ -529,10 +590,11 @@ shaka.util.StreamUtils = class {
529
590
  * @param {!Array.<shaka.extern.Variant>} variants
530
591
  * @param {boolean} usePersistentLicenses
531
592
  * @param {boolean} srcEquals
593
+ * @param {!Array<string>} preferredKeySystems
532
594
  * @exportDoc
533
595
  */
534
596
  static async getDecodingInfosForVariants(variants, usePersistentLicenses,
535
- srcEquals) {
597
+ srcEquals, preferredKeySystems) {
536
598
  const gotDecodingInfo = variants.some((variant) =>
537
599
  variant.decodingInfos.length);
538
600
  if (gotDecodingInfo) {
@@ -540,29 +602,57 @@ shaka.util.StreamUtils = class {
540
602
  return;
541
603
  }
542
604
 
543
- const mediaCapabilities = navigator.mediaCapabilities;
544
-
545
- const operations = [];
546
- const getVariantDecodingInfos = (async (variant, decodingConfig) => {
547
- try {
548
- const result = await mediaCapabilities.decodingInfo(decodingConfig);
549
- variant.decodingInfos.push(result);
550
- } catch (e) {
551
- shaka.log.info('MediaCapabilities.decodingInfo() failed.',
552
- JSON.stringify(decodingConfig), e);
605
+ // Try to get preferred key systems first to avoid unneeded calls to CDM.
606
+ for (const preferredKeySystem of preferredKeySystems) {
607
+ let keySystemSatisfied = false;
608
+ for (const variant of variants) {
609
+ /** @type {!Array.<!MediaDecodingConfiguration>} */
610
+ const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
611
+ variant, usePersistentLicenses, srcEquals)
612
+ .filter((config) => {
613
+ const keySystem = config.keySystemConfiguration &&
614
+ config.keySystemConfiguration.keySystem;
615
+ return keySystem === preferredKeySystem;
616
+ });
617
+
618
+ // The reason we are performing this await in a loop rather than
619
+ // batching into a `promise.all` is performance related.
620
+ // https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
621
+ for (const config of decodingConfigs) {
622
+ // eslint-disable-next-line no-await-in-loop
623
+ await shaka.util.StreamUtils.getDecodingInfosForVariant_(
624
+ variant, config);
625
+ }
626
+ if (variant.decodingInfos.length) {
627
+ keySystemSatisfied = true;
628
+ }
629
+ } // for (const variant of variants)
630
+ if (keySystemSatisfied) {
631
+ // Return if any preferred key system is already satisfied.
632
+ return;
553
633
  }
554
- });
634
+ } // for (const preferredKeySystem of preferredKeySystems)
555
635
 
556
636
  for (const variant of variants) {
557
637
  /** @type {!Array.<!MediaDecodingConfiguration>} */
558
638
  const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
559
- variant, usePersistentLicenses, srcEquals);
560
-
639
+ variant, usePersistentLicenses, srcEquals)
640
+ .filter((config) => {
641
+ const keySystem = config.keySystemConfiguration &&
642
+ config.keySystemConfiguration.keySystem;
643
+ // Avoid checking preferred systems twice.
644
+ return !keySystem || !preferredKeySystems.includes(keySystem);
645
+ });
646
+
647
+ // The reason we are performing this await in a loop rather than
648
+ // batching into a `promise.all` is performance related.
649
+ // https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
561
650
  for (const config of decodingConfigs) {
562
- operations.push(getVariantDecodingInfos(variant, config));
651
+ // eslint-disable-next-line no-await-in-loop
652
+ await shaka.util.StreamUtils.getDecodingInfosForVariant_(
653
+ variant, config);
563
654
  }
564
655
  }
565
- await Promise.all(operations);
566
656
  }
567
657
 
568
658
 
@@ -1095,6 +1185,8 @@ shaka.util.StreamUtils = class {
1095
1185
  originalAudioId: null,
1096
1186
  originalTextId: null,
1097
1187
  originalImageId: null,
1188
+ accessibilityPurpose: null,
1189
+ originalLanguage: null,
1098
1190
  };
1099
1191
 
1100
1192
  if (video) {
@@ -1117,6 +1209,8 @@ shaka.util.StreamUtils = class {
1117
1209
  track.spatialAudio = audio.spatialAudio;
1118
1210
  track.label = audio.label;
1119
1211
  track.audioRoles = audio.roles;
1212
+ track.accessibilityPurpose = audio.accessibilityPurpose;
1213
+ track.originalLanguage = audio.originalLanguage;
1120
1214
  }
1121
1215
 
1122
1216
  return track;
@@ -1166,6 +1260,8 @@ shaka.util.StreamUtils = class {
1166
1260
  originalAudioId: null,
1167
1261
  originalTextId: stream.originalId,
1168
1262
  originalImageId: null,
1263
+ accessibilityPurpose: stream.accessibilityPurpose,
1264
+ originalLanguage: stream.originalLanguage,
1169
1265
  };
1170
1266
 
1171
1267
  return track;
@@ -1240,6 +1336,8 @@ shaka.util.StreamUtils = class {
1240
1336
  originalAudioId: null,
1241
1337
  originalTextId: null,
1242
1338
  originalImageId: stream.originalId,
1339
+ accessibilityPurpose: null,
1340
+ originalLanguage: null,
1243
1341
  };
1244
1342
 
1245
1343
  return track;
@@ -1365,6 +1463,7 @@ shaka.util.StreamUtils = class {
1365
1463
  originalAudioId: null,
1366
1464
  originalTextId: null,
1367
1465
  originalImageId: null,
1466
+ accessibilityPurpose: null,
1368
1467
  originalLanguage: language,
1369
1468
  };
1370
1469
 
@@ -1378,7 +1477,9 @@ shaka.util.StreamUtils = class {
1378
1477
  * @return {boolean}
1379
1478
  */
1380
1479
  static isPlayable(variant) {
1381
- return variant.allowedByApplication && variant.allowedByKeySystem;
1480
+ return variant.allowedByApplication &&
1481
+ variant.allowedByKeySystem &&
1482
+ variant.disabledUntilTime == 0;
1382
1483
  }
1383
1484
 
1384
1485
 
@@ -1640,6 +1741,16 @@ shaka.util.StreamUtils = class {
1640
1741
  };
1641
1742
 
1642
1743
 
1744
+ /**
1745
+ * A cache of results from mediaCapabilities.decodingInfo, indexed by the
1746
+ * (stringified) decodingConfig.
1747
+ *
1748
+ * @type {Object.<(!string), (!MediaCapabilitiesDecodingInfo)>}
1749
+ * @private
1750
+ */
1751
+ shaka.util.StreamUtils.decodingConfigCache_ = {};
1752
+
1753
+
1643
1754
  /** @private {number} */
1644
1755
  shaka.util.StreamUtils.nextTrackId_ = 0;
1645
1756
 
@@ -21,7 +21,7 @@ shaka.util.TextParser = class {
21
21
  * @const
22
22
  * @private {string}
23
23
  */
24
- this.data_ = data;
24
+ this.data_ = data || '';
25
25
 
26
26
  /** @private {number} */
27
27
  this.position_ = 0;