shaka-player 4.4.3 → 4.6.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 (307) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/workflows/release-please.yaml +2 -27
  3. package/AUTHORS +2 -0
  4. package/CHANGELOG.md +156 -39
  5. package/CODE_OF_CONDUCT.md +1 -1
  6. package/CONTRIBUTORS +2 -0
  7. package/README.md +54 -42
  8. package/demo/asset_card.js +18 -21
  9. package/demo/cast_receiver/index.html +0 -1
  10. package/demo/close_button.js +0 -3
  11. package/demo/common/asset.js +33 -22
  12. package/demo/common/assets.js +249 -60
  13. package/demo/config.js +219 -253
  14. package/demo/custom.js +88 -133
  15. package/demo/front.js +7 -10
  16. package/demo/index.html +19 -22
  17. package/demo/input.js +6 -11
  18. package/demo/input_container.js +8 -9
  19. package/demo/main.js +62 -126
  20. package/demo/search.js +29 -34
  21. package/demo/service_worker.js +0 -3
  22. package/demo/tooltip.js +3 -5
  23. package/demo/visualizer.js +3 -5
  24. package/demo/visualizer_button.js +1 -20
  25. package/dist/controls.css +1 -1
  26. package/dist/controls.css.map +1 -1
  27. package/dist/demo.compiled.debug.js +211 -228
  28. package/dist/demo.compiled.debug.map +4 -4
  29. package/dist/demo.compiled.js +211 -228
  30. package/dist/demo.compiled.map +4 -4
  31. package/dist/deps.js +42 -37
  32. package/dist/locales.js +34 -30
  33. package/dist/receiver.compiled.debug.js +64 -77
  34. package/dist/receiver.compiled.debug.map +4 -4
  35. package/dist/receiver.compiled.js +64 -77
  36. package/dist/receiver.compiled.map +4 -4
  37. package/dist/shaka-player.compiled.d.ts +343 -151
  38. package/dist/shaka-player.compiled.debug.d.ts +343 -151
  39. package/dist/shaka-player.compiled.debug.externs.js +356 -117
  40. package/dist/shaka-player.compiled.debug.js +952 -811
  41. package/dist/shaka-player.compiled.debug.map +4 -4
  42. package/dist/shaka-player.compiled.externs.js +356 -117
  43. package/dist/shaka-player.compiled.js +1165 -1078
  44. package/dist/shaka-player.compiled.map +4 -4
  45. package/dist/shaka-player.ui.d.ts +359 -153
  46. package/dist/shaka-player.ui.debug.d.ts +359 -153
  47. package/dist/shaka-player.ui.debug.externs.js +382 -117
  48. package/dist/shaka-player.ui.debug.js +1084 -918
  49. package/dist/shaka-player.ui.debug.map +4 -4
  50. package/dist/shaka-player.ui.externs.js +382 -117
  51. package/dist/shaka-player.ui.js +1377 -1274
  52. package/dist/shaka-player.ui.map +4 -4
  53. package/docs/design/lcevc-integration.md +14 -14
  54. package/docs/tutorials/ad_monetization.md +54 -0
  55. package/docs/tutorials/basic-usage.md +2 -1
  56. package/docs/tutorials/errors.md +2 -1
  57. package/docs/tutorials/faq.md +27 -55
  58. package/docs/tutorials/index.json +2 -1
  59. package/docs/tutorials/lcevc.md +118 -0
  60. package/docs/tutorials/offline.md +10 -8
  61. package/docs/tutorials/plugins.md +5 -0
  62. package/docs/tutorials/ui-customization.md +17 -0
  63. package/docs/tutorials/ui.md +2 -1
  64. package/docs/tutorials/upgrade.md +3 -0
  65. package/externs/css_style_declaration.js +19 -0
  66. package/externs/lcevc.js +37 -13
  67. package/externs/managedmediasource.js +24 -0
  68. package/externs/media_remote.js +68 -0
  69. package/externs/mediatailor.js +193 -0
  70. package/externs/pictureinpicture.js +11 -0
  71. package/externs/shaka/abr_manager.js +6 -2
  72. package/externs/shaka/ads.js +27 -0
  73. package/externs/shaka/cea.js +7 -0
  74. package/externs/shaka/codecs.js +5 -3
  75. package/externs/shaka/manifest.js +31 -10
  76. package/externs/shaka/manifest_parser.js +24 -1
  77. package/externs/shaka/net.js +2 -2
  78. package/externs/shaka/offline.js +13 -2
  79. package/externs/shaka/player.js +65 -8
  80. package/externs/shaka/text.js +3 -1
  81. package/externs/shaka/transmuxer.js +1 -1
  82. package/karma.conf.js +7 -3
  83. package/lib/abr/simple_abr_manager.js +66 -9
  84. package/lib/ads/ad_manager.js +104 -50
  85. package/lib/ads/media_tailor_ad.js +297 -0
  86. package/lib/ads/media_tailor_ad_manager.js +839 -0
  87. package/lib/cast/cast_receiver.js +1 -1
  88. package/lib/cea/cea_decoder.js +28 -0
  89. package/lib/cea/dummy_caption_decoder.js +5 -0
  90. package/lib/cea/ts_cea_parser.js +15 -2
  91. package/lib/config/codec_switching_strategy.js +24 -0
  92. package/lib/dash/content_protection.js +120 -2
  93. package/lib/dash/dash_parser.js +322 -93
  94. package/lib/dash/mpd_utils.js +62 -37
  95. package/lib/dash/segment_base.js +17 -10
  96. package/lib/dash/segment_list.js +18 -9
  97. package/lib/dash/segment_template.js +122 -107
  98. package/lib/dependencies/all.js +8 -7
  99. package/lib/hls/hls_classes.js +28 -43
  100. package/lib/hls/hls_parser.js +798 -658
  101. package/lib/hls/hls_utils.js +64 -5
  102. package/lib/hls/manifest_text_parser.js +7 -11
  103. package/lib/lcevc/lcevc_dec.js +220 -0
  104. package/lib/media/adaptation_set.js +32 -14
  105. package/lib/media/adaptation_set_criteria.js +86 -16
  106. package/lib/media/closed_caption_parser.js +13 -0
  107. package/lib/media/content_workarounds.js +6 -0
  108. package/lib/media/drm_engine.js +8 -13
  109. package/lib/media/manifest_parser.js +8 -113
  110. package/lib/media/media_source_capabilities.js +10 -3
  111. package/lib/media/media_source_engine.js +286 -44
  112. package/lib/media/play_rate_controller.js +8 -38
  113. package/lib/media/presentation_timeline.js +7 -1
  114. package/lib/media/quality_observer.js +1 -1
  115. package/lib/media/segment_index.js +6 -6
  116. package/lib/media/segment_prefetch.js +92 -24
  117. package/lib/media/segment_reference.js +73 -7
  118. package/lib/media/segment_utils.js +343 -0
  119. package/lib/media/stall_detector.js +0 -3
  120. package/lib/media/streaming_engine.js +73 -38
  121. package/lib/mss/mss_parser.js +19 -2
  122. package/lib/net/networking_engine.js +67 -12
  123. package/lib/net/networking_utils.js +95 -0
  124. package/lib/offline/indexeddb/v1_storage_cell.js +3 -0
  125. package/lib/offline/indexeddb/v2_storage_cell.js +3 -0
  126. package/lib/offline/manifest_converter.js +3 -0
  127. package/lib/offline/offline_manifest_parser.js +10 -0
  128. package/lib/offline/storage.js +14 -7
  129. package/lib/player.js +1098 -1537
  130. package/lib/polyfill/mediasource.js +22 -1
  131. package/lib/text/cue.js +15 -0
  132. package/lib/text/mp4_ttml_parser.js +2 -2
  133. package/lib/text/srt_text_parser.js +2 -2
  134. package/lib/text/stub_text_displayer.js +51 -0
  135. package/lib/text/text_engine.js +3 -2
  136. package/lib/text/ttml_text_parser.js +45 -5
  137. package/lib/text/ui_text_displayer.js +8 -0
  138. package/lib/text/vtt_text_parser.js +40 -22
  139. package/lib/transmuxer/aac_transmuxer.js +1 -1
  140. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  141. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  142. package/lib/transmuxer/h264.js +5 -4
  143. package/lib/transmuxer/h265.js +863 -0
  144. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  145. package/lib/transmuxer/mpeg_ts_transmuxer.js +4 -3
  146. package/lib/transmuxer/mss_transmuxer.js +1 -1
  147. package/lib/transmuxer/ts_transmuxer.js +187 -22
  148. package/lib/util/buffer_utils.js +26 -4
  149. package/lib/util/content_steering_manager.js +358 -0
  150. package/lib/util/error.js +30 -15
  151. package/lib/util/exp_golomb.js +30 -2
  152. package/lib/util/fairplay_utils.js +1 -1
  153. package/lib/util/fake_event.js +8 -1
  154. package/lib/util/id3_utils.js +50 -1
  155. package/lib/util/manifest_parser_utils.js +12 -8
  156. package/lib/util/mp4_generator.js +6 -8
  157. package/lib/util/mutex.js +59 -0
  158. package/lib/util/object_utils.js +1 -1
  159. package/lib/util/periods.js +156 -179
  160. package/lib/util/platform.js +44 -3
  161. package/lib/util/player_configuration.js +43 -6
  162. package/lib/util/state_history.js +6 -2
  163. package/lib/util/stream_utils.js +206 -283
  164. package/lib/util/ts_parser.js +335 -98
  165. package/lib/util/uint8array_utils.js +5 -1
  166. package/lib/util/xml_utils.js +5 -0
  167. package/maintained-branches.md +3 -2
  168. package/package.json +4 -6
  169. package/roadmap.md +25 -5
  170. package/shaka-player.uncompiled.js +2 -2
  171. package/support.html +0 -1
  172. package/test/abr/simple_abr_manager_unit.js +33 -33
  173. package/test/ads/ad_manager_unit.js +3 -2
  174. package/test/cast/cast_receiver_integration.js +50 -4
  175. package/test/cast/cast_utils_unit.js +2 -0
  176. package/test/cea/cea_decoder_unit.js +7 -0
  177. package/test/codec_switching/codec_switching_integration.js +182 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  179. package/test/dash/dash_parser_integration.js +74 -0
  180. package/test/dash/dash_parser_live_unit.js +2 -19
  181. package/test/dash/dash_parser_manifest_unit.js +536 -2
  182. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  183. package/test/dash/dash_parser_segment_list_unit.js +2 -0
  184. package/test/dash/dash_parser_segment_template_unit.js +2 -0
  185. package/test/dash/mpd_utils_unit.js +61 -30
  186. package/test/demo/demo_unit.js +0 -16
  187. package/test/hls/hls_live_unit.js +14 -3
  188. package/test/hls/hls_parser_unit.js +385 -33
  189. package/test/hls/manifest_text_parser_unit.js +32 -99
  190. package/test/media/adaptation_set_criteria_unit.js +284 -15
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/content_workarounds_unit.js +1 -1
  193. package/test/media/drm_engine_unit.js +3 -3
  194. package/test/media/media_source_engine_unit.js +166 -2
  195. package/test/media/play_rate_controller_unit.js +0 -48
  196. package/test/media/segment_index_unit.js +5 -4
  197. package/test/media/segment_prefetch_unit.js +1 -1
  198. package/test/media/streaming_engine_unit.js +17 -17
  199. package/test/mss/mss_parser_unit.js +2 -0
  200. package/test/mss/mss_player_integration.js +3 -2
  201. package/test/net/networking_engine_unit.js +3 -3
  202. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  203. package/test/offline/manifest_convert_unit.js +14 -0
  204. package/test/offline/offline_integration.js +2 -1
  205. package/test/offline/storage_integration.js +22 -13
  206. package/test/player_external.js +4 -2
  207. package/test/player_integration.js +15 -4
  208. package/test/player_load_graph_integration.js +47 -151
  209. package/test/player_src_equals_external.js +1 -0
  210. package/test/player_src_equals_integration.js +71 -4
  211. package/test/player_unit.js +182 -158
  212. package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
  213. package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
  214. package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
  215. package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
  216. package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
  217. package/test/test/assets/dash-aes-128/dash.mpd +18 -0
  218. package/test/test/assets/dash-aes-128/license +1 -0
  219. package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
  220. package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
  221. package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
  222. package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
  223. package/test/test/assets/hls-ts-h265/0.ts +0 -0
  224. package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
  225. package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
  226. package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
  227. package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
  228. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
  229. package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
  230. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
  231. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
  232. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
  233. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
  234. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
  235. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
  236. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
  237. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
  238. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
  239. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
  240. package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
  241. package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
  242. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
  243. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
  244. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
  245. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
  246. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
  247. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
  248. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
  249. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
  250. package/test/test/util/dash_parser_util.js +4 -0
  251. package/test/test/util/fake_ad_manager.js +20 -0
  252. package/test/test/util/fake_media_source_engine.js +13 -2
  253. package/test/test/util/fake_networking_engine.js +9 -1
  254. package/test/test/util/fake_segment_prefetch.js +3 -0
  255. package/test/test/util/manifest_generator.js +11 -3
  256. package/test/test/util/manifest_parser_util.js +14 -2
  257. package/test/test/util/mss_parser_util.js +4 -0
  258. package/test/test/util/offline_utils.js +2 -0
  259. package/test/test/util/simple_fakes.js +2 -2
  260. package/test/test/util/streaming_engine_util.js +10 -4
  261. package/test/test/util/test_scheme.js +6 -0
  262. package/test/test/util/ui_utils.js +4 -3
  263. package/test/text/mp4_ttml_parser_unit.js +4 -4
  264. package/test/text/srt_text_parser_unit.js +1 -1
  265. package/test/text/text_displayer_layout_unit.js +1 -1
  266. package/test/text/text_engine_unit.js +6 -0
  267. package/test/text/ttml_text_parser_unit.js +85 -2
  268. package/test/text/vtt_text_parser_unit.js +103 -31
  269. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  270. package/test/transmuxer/transmuxer_integration.js +150 -3
  271. package/test/ui/ad_ui_unit.js +2 -2
  272. package/test/ui/ui_customization_unit.js +17 -16
  273. package/test/ui/ui_integration.js +30 -20
  274. package/test/ui/ui_unit.js +28 -29
  275. package/test/util/buffer_utils_unit.js +1 -1
  276. package/test/util/content_steering_manager_unit.js +427 -0
  277. package/test/util/id3_utils_unit.js +52 -0
  278. package/test/util/stream_utils_unit.js +207 -169
  279. package/test/util/ts_parser_unit.js +3 -3
  280. package/third_party/closure-uri/uri.js +13 -1
  281. package/ui/audio_language_selection.js +9 -2
  282. package/ui/controls.js +125 -0
  283. package/ui/externs/ui.js +24 -2
  284. package/ui/language_utils.js +43 -8
  285. package/ui/less/range_elements.less +33 -0
  286. package/ui/locales/en.json +1 -0
  287. package/ui/locales/es-419.json +1 -0
  288. package/ui/locales/es.json +1 -0
  289. package/ui/locales/source.json +4 -0
  290. package/ui/localization.js +10 -0
  291. package/ui/pip_button.js +22 -120
  292. package/ui/remote_button.js +203 -0
  293. package/ui/resolution_selection.js +47 -10
  294. package/ui/seek_bar.js +296 -3
  295. package/ui/text_selection.js +2 -1
  296. package/ui/ui.js +11 -4
  297. package/demo/common/message_ids.js +0 -309
  298. package/demo/locales/en.json +0 -288
  299. package/demo/locales/source.json +0 -1146
  300. package/externs/mux.js +0 -69
  301. package/lib/lcevc/lcevc_dil.js +0 -152
  302. package/lib/routing/node.js +0 -23
  303. package/lib/routing/payload.js +0 -43
  304. package/lib/routing/walker.js +0 -587
  305. package/lib/transmuxer/muxjs_transmuxer.js +0 -242
  306. package/test/routing/walker_unit.js +0 -504
  307. package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
@@ -0,0 +1,59 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.util.Mutex');
8
+
9
+ goog.require('shaka.log');
10
+
11
+
12
+ /**
13
+ * @summary A simple mutex.
14
+ */
15
+ shaka.util.Mutex = class {
16
+ /** Constructs the mutex. */
17
+ constructor() {
18
+ /** @private {?string} */
19
+ this.acquiredIdentifier = null;
20
+
21
+ /** @private {!Array.<function()>} */
22
+ this.unlockQueue = [];
23
+ }
24
+
25
+ /**
26
+ * Acquires the mutex, as soon as possible.
27
+ * @param {string} identifier
28
+ * @return {!Promise}
29
+ */
30
+ async acquire(identifier) {
31
+ shaka.log.v2(identifier + ' has requested mutex');
32
+ if (this.acquiredIdentifier) {
33
+ await new Promise((resolve) => this.unlockQueue.push(resolve));
34
+ }
35
+ this.acquiredIdentifier = identifier;
36
+ shaka.log.v2(identifier + ' has acquired mutex');
37
+ }
38
+
39
+ /**
40
+ * Releases your hold on the mutex.
41
+ */
42
+ release() {
43
+ shaka.log.v2(this.acquiredIdentifier + ' has released mutex');
44
+ if (this.unlockQueue.length > 0) {
45
+ this.unlockQueue.shift()();
46
+ } else {
47
+ this.acquiredIdentifier = null;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Completely releases the mutex. Meant for use by the tests.
53
+ */
54
+ releaseAll() {
55
+ while (this.acquiredIdentifier) {
56
+ this.release();
57
+ }
58
+ }
59
+ };
@@ -19,7 +19,7 @@ shaka.util.ObjectUtils = class {
19
19
  * @return {T}
20
20
  */
21
21
  static cloneObject(arg) {
22
- const seenObjects = new Set();
22
+ const seenObjects = new WeakSet();
23
23
  // This recursively clones the value |val|, using the captured variable
24
24
  // |seenObjects| to track the objects we have already cloned.
25
25
  /** @suppress {strictMissingProperties} */
@@ -103,6 +103,37 @@ shaka.util.PeriodCombiner = class {
103
103
  return this.imageStreams_;
104
104
  }
105
105
 
106
+ /**
107
+ * Returns an object that contains arrays of streams by type
108
+ * @param {!Array.<shaka.extern.Period>} periods
109
+ * @return {{
110
+ * audioStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
111
+ * videoStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
112
+ * textStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
113
+ * imageStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>
114
+ * }}
115
+ * @private
116
+ */
117
+ getStreamsPerPeriod_(periods) {
118
+ const audioStreamsPerPeriod = [];
119
+ const videoStreamsPerPeriod = [];
120
+ const textStreamsPerPeriod = [];
121
+ const imageStreamsPerPeriod = [];
122
+
123
+ for (const period of periods) {
124
+ audioStreamsPerPeriod.push(period.audioStreams);
125
+ videoStreamsPerPeriod.push(period.videoStreams);
126
+ textStreamsPerPeriod.push(period.textStreams);
127
+ imageStreamsPerPeriod.push(period.imageStreams);
128
+ }
129
+ return {
130
+ audioStreamsPerPeriod,
131
+ videoStreamsPerPeriod,
132
+ textStreamsPerPeriod,
133
+ imageStreamsPerPeriod,
134
+ };
135
+ }
136
+
106
137
  /**
107
138
  * @param {!Array.<shaka.extern.Period>} periods
108
139
  * @param {boolean} isDynamic
@@ -113,10 +144,7 @@ shaka.util.PeriodCombiner = class {
113
144
  async combinePeriods(periods, isDynamic) {
114
145
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
115
146
 
116
- shaka.util.PeriodCombiner.filterOutAudioStreamDuplicates_(periods);
117
- shaka.util.PeriodCombiner.filterOutVideoStreamDuplicates_(periods);
118
- shaka.util.PeriodCombiner.filterOutTextStreamDuplicates_(periods);
119
- shaka.util.PeriodCombiner.filterOutImageStreamDuplicates_(periods);
147
+ shaka.util.PeriodCombiner.filterOutDuplicates_(periods);
120
148
 
121
149
  // Optimization: for single-period VOD, do nothing. This makes sure
122
150
  // single-period DASH content will be 100% accurately represented in the
@@ -151,14 +179,12 @@ shaka.util.PeriodCombiner = class {
151
179
  return;
152
180
  }
153
181
 
154
- const audioStreamsPerPeriod = periods.map(
155
- (period) => period.audioStreams);
156
- const videoStreamsPerPeriod = periods.map(
157
- (period) => period.videoStreams);
158
- const textStreamsPerPeriod = periods.map(
159
- (period) => period.textStreams);
160
- const imageStreamsPerPeriod = periods.map(
161
- (period) => period.imageStreams);
182
+ const {
183
+ audioStreamsPerPeriod,
184
+ videoStreamsPerPeriod,
185
+ textStreamsPerPeriod,
186
+ imageStreamsPerPeriod,
187
+ } = this.getStreamsPerPeriod_(periods);
162
188
 
163
189
  // It's okay to have a period with no text or images, but our algorithm
164
190
  // fails on any period without matching streams. So we add dummy streams
@@ -174,33 +200,32 @@ shaka.util.PeriodCombiner = class {
174
200
  ContentType.IMAGE));
175
201
  }
176
202
 
177
- await shaka.util.PeriodCombiner.combine_(
178
- this.audioStreams_,
179
- audioStreamsPerPeriod,
180
- firstNewPeriodIndex,
181
- shaka.util.PeriodCombiner.cloneStream_,
182
- shaka.util.PeriodCombiner.concatenateStreams_);
183
-
184
- await shaka.util.PeriodCombiner.combine_(
185
- this.videoStreams_,
186
- videoStreamsPerPeriod,
187
- firstNewPeriodIndex,
188
- shaka.util.PeriodCombiner.cloneStream_,
189
- shaka.util.PeriodCombiner.concatenateStreams_);
190
-
191
- await shaka.util.PeriodCombiner.combine_(
192
- this.textStreams_,
193
- textStreamsPerPeriod,
194
- firstNewPeriodIndex,
195
- shaka.util.PeriodCombiner.cloneStream_,
196
- shaka.util.PeriodCombiner.concatenateStreams_);
197
-
198
- await shaka.util.PeriodCombiner.combine_(
199
- this.imageStreams_,
200
- imageStreamsPerPeriod,
201
- firstNewPeriodIndex,
202
- shaka.util.PeriodCombiner.cloneStream_,
203
- shaka.util.PeriodCombiner.concatenateStreams_);
203
+ await Promise.all([
204
+ shaka.util.PeriodCombiner.combine_(
205
+ this.audioStreams_,
206
+ audioStreamsPerPeriod,
207
+ firstNewPeriodIndex,
208
+ shaka.util.PeriodCombiner.cloneStream_,
209
+ shaka.util.PeriodCombiner.concatenateStreams_),
210
+ shaka.util.PeriodCombiner.combine_(
211
+ this.videoStreams_,
212
+ videoStreamsPerPeriod,
213
+ firstNewPeriodIndex,
214
+ shaka.util.PeriodCombiner.cloneStream_,
215
+ shaka.util.PeriodCombiner.concatenateStreams_),
216
+ shaka.util.PeriodCombiner.combine_(
217
+ this.textStreams_,
218
+ textStreamsPerPeriod,
219
+ firstNewPeriodIndex,
220
+ shaka.util.PeriodCombiner.cloneStream_,
221
+ shaka.util.PeriodCombiner.concatenateStreams_),
222
+ shaka.util.PeriodCombiner.combine_(
223
+ this.imageStreams_,
224
+ imageStreamsPerPeriod,
225
+ firstNewPeriodIndex,
226
+ shaka.util.PeriodCombiner.cloneStream_,
227
+ shaka.util.PeriodCombiner.concatenateStreams_),
228
+ ]);
204
229
  }
205
230
 
206
231
  // Create variants for all audio/video combinations.
@@ -209,7 +234,8 @@ shaka.util.PeriodCombiner = class {
209
234
  if (!this.videoStreams_.length || !this.audioStreams_.length) {
210
235
  // For audio-only or video-only content, just give each stream its own
211
236
  // variant.
212
- const streams = this.videoStreams_.concat(this.audioStreams_);
237
+ const streams = this.videoStreams_.length ? this.videoStreams_ :
238
+ this.audioStreams_;
213
239
  for (const stream of streams) {
214
240
  const id = nextVariantId++;
215
241
  variants.push({
@@ -262,141 +288,90 @@ shaka.util.PeriodCombiner = class {
262
288
  }
263
289
 
264
290
  /**
265
- * @param {!Array.<shaka.extern.Period>} periods
291
+ * @param {!Array<shaka.extern.Period>} periods
266
292
  * @private
267
293
  */
268
- static filterOutAudioStreamDuplicates_(periods) {
269
- const ArrayUtils = shaka.util.ArrayUtils;
270
- // Two audio streams are considered to be duplicates of
271
- // one another if their ids are different, but all the other
272
- // information is the same.
273
- for (const period of periods) {
274
- const filteredAudios = [];
275
- for (const a1 of period.audioStreams) {
276
- let duplicate = false;
277
- for (const a2 of filteredAudios) {
278
- if (a1.id != a2.id &&
279
- a1.channelsCount == a2.channelsCount &&
280
- a1.language == a2.language &&
281
- a1.bandwidth == a2.bandwidth &&
282
- a1.label == a2.label &&
283
- a1.codecs == a2.codecs &&
284
- a1.mimeType == a2.mimeType &&
285
- ArrayUtils.hasSameElements(a1.roles, a2.roles) &&
286
- a1.audioSamplingRate == a2.audioSamplingRate &&
287
- a1.primary == a2.primary) {
288
- duplicate = true;
289
- }
290
- }
291
-
292
- if (!duplicate) {
293
- filteredAudios.push(a1);
294
- }
295
- }
296
-
297
- period.audioStreams = filteredAudios;
298
- }
299
- }
300
-
301
- /**
302
- * @param {!Array.<shaka.extern.Period>} periods
303
- * @private
304
- */
305
- static filterOutTextStreamDuplicates_(periods) {
306
- const ArrayUtils = shaka.util.ArrayUtils;
307
- // Two text streams are considered to be duplicates of
308
- // one another if their ids are different, but all the other
309
- // information is the same.
310
- for (const period of periods) {
311
- const filteredTexts = [];
312
- for (const t1 of period.textStreams) {
313
- let duplicate = false;
314
- for (const t2 of filteredTexts) {
315
- if (t1.id != t2.id &&
316
- t1.language == t2.language &&
317
- t1.label == t2.label &&
318
- t1.codecs == t2.codecs &&
319
- t1.mimeType == t2.mimeType &&
320
- t1.bandwidth == t2.bandwidth &&
321
- ArrayUtils.hasSameElements(t1.roles, t2.roles)) {
322
- duplicate = true;
323
- }
324
- }
325
-
326
- if (!duplicate) {
327
- filteredTexts.push(t1);
328
- }
329
- }
330
-
331
- period.textStreams = filteredTexts;
332
- }
333
- }
334
-
335
- /**
336
- * @param {!Array.<shaka.extern.Period>} periods
337
- * @private
338
- */
339
- static filterOutVideoStreamDuplicates_(periods) {
294
+ static filterOutDuplicates_(periods) {
295
+ const PeriodCombiner = shaka.util.PeriodCombiner;
340
296
  const ArrayUtils = shaka.util.ArrayUtils;
341
297
  const MapUtils = shaka.util.MapUtils;
342
- // Two video streams are considered to be duplicates of
343
- // one another if their ids are different, but all the other
344
- // information is the same.
298
+
345
299
  for (const period of periods) {
346
- const filteredVideos = [];
347
- for (const v1 of period.videoStreams) {
348
- let duplicate = false;
349
- for (const v2 of filteredVideos) {
350
- if (v1.id != v2.id &&
351
- v1.width == v2.width &&
352
- v1.frameRate == v2.frameRate &&
353
- v1.codecs == v2.codecs &&
354
- v1.mimeType == v2.mimeType &&
355
- v1.label == v2.label &&
300
+ // Two video streams are considered to be duplicates of
301
+ // one another if their ids are different, but all the other
302
+ // information is the same.
303
+ period.videoStreams = PeriodCombiner.filterOutStreamDuplicates_(
304
+ period.videoStreams, (v1, v2) => {
305
+ return v1.id !== v2.id &&
306
+ v1.fastSwitching == v2.fastSwitching &&
307
+ v1.width === v2.width &&
308
+ v1.frameRate === v2.frameRate &&
309
+ v1.codecs === v2.codecs &&
310
+ v1.mimeType === v2.mimeType &&
311
+ v1.label === v2.label &&
356
312
  ArrayUtils.hasSameElements(v1.roles, v2.roles) &&
357
313
  MapUtils.hasSameElements(v1.closedCaptions, v2.closedCaptions) &&
358
- v1.bandwidth == v2.bandwidth) {
359
- duplicate = true;
360
- }
361
- }
362
-
363
- if (!duplicate) {
364
- filteredVideos.push(v1);
365
- }
366
- }
367
-
368
- period.videoStreams = filteredVideos;
314
+ v1.bandwidth === v2.bandwidth;
315
+ });
316
+ // Two audio streams are considered to be duplicates of
317
+ // one another if their ids are different, but all the other
318
+ // information is the same.
319
+ period.audioStreams = PeriodCombiner.filterOutStreamDuplicates_(
320
+ period.audioStreams, (a1, a2) => {
321
+ return a1.id !== a2.id &&
322
+ a1.fastSwitching == a2.fastSwitching &&
323
+ a1.channelsCount === a2.channelsCount &&
324
+ a1.language === a2.language &&
325
+ a1.bandwidth === a2.bandwidth &&
326
+ a1.label === a2.label &&
327
+ a1.codecs === a2.codecs &&
328
+ a1.mimeType === a2.mimeType &&
329
+ ArrayUtils.hasSameElements(a1.roles, a2.roles) &&
330
+ a1.audioSamplingRate === a2.audioSamplingRate &&
331
+ a1.primary === a2.primary;
332
+ });
333
+ // Two text streams are considered to be duplicates of
334
+ // one another if their ids are different, but all the other
335
+ // information is the same.
336
+ period.textStreams = PeriodCombiner.filterOutStreamDuplicates_(
337
+ period.textStreams, (t1, t2) => {
338
+ return t1.id !== t2.id &&
339
+ t1.language === t2.language &&
340
+ t1.label === t2.label &&
341
+ t1.codecs === t2.codecs &&
342
+ t1.mimeType === t2.mimeType &&
343
+ t1.bandwidth === t2.bandwidth &&
344
+ ArrayUtils.hasSameElements(t1.roles, t2.roles);
345
+ });
346
+ // Two image streams are considered to be duplicates of
347
+ // one another if their ids are different, but all the other
348
+ // information is the same.
349
+ period.imageStreams = PeriodCombiner.filterOutStreamDuplicates_(
350
+ period.imageStreams, (i1, i2) => {
351
+ return i1.id !== i2.id &&
352
+ i1.width === i2.width &&
353
+ i1.codecs === i2.codecs &&
354
+ i1.mimeType === i2.mimeType;
355
+ });
369
356
  }
370
357
  }
371
358
 
372
359
  /**
373
- * @param {!Array.<shaka.extern.Period>} periods
360
+ * @param {!Array<shaka.extern.Stream>} streams
361
+ * @param {function(
362
+ * shaka.extern.Stream, shaka.extern.Stream): boolean} isDuplicate
363
+ * @return {!Array<shaka.extern.Stream>}
374
364
  * @private
375
365
  */
376
- static filterOutImageStreamDuplicates_(periods) {
377
- // Two image streams are considered to be duplicates of
378
- // one another if their ids are different, but all the other
379
- // information is the same.
380
- for (const period of periods) {
381
- const filteredImages = [];
382
- for (const i1 of period.imageStreams) {
383
- let duplicate = false;
384
- for (const i2 of filteredImages) {
385
- if (i1.id != i2.id &&
386
- i1.width == i2.width &&
387
- i1.codecs == i2.codecs &&
388
- i1.mimeType == i2.mimeType) {
389
- duplicate = true;
390
- }
391
- }
392
-
393
- if (!duplicate) {
394
- filteredImages.push(i1);
395
- }
366
+ static filterOutStreamDuplicates_(streams, isDuplicate) {
367
+ const filteredStreams = [];
368
+ for (const s1 of streams) {
369
+ const duplicate = filteredStreams.some((s2) => isDuplicate(s1, s2));
370
+ if (!duplicate) {
371
+ filteredStreams.push(s1);
396
372
  }
397
-
398
- period.imageStreams = filteredImages;
399
373
  }
374
+ return filteredStreams;
400
375
  }
401
376
 
402
377
  /**
@@ -784,27 +759,25 @@ shaka.util.PeriodCombiner = class {
784
759
 
785
760
  // Concatenate the new matches onto the stream, starting at the first new
786
761
  // period.
787
- for (let i = 0; i < matches.length; i++) {
788
- if (i >= firstNewPeriodIndex) {
789
- const match = matches[i];
790
- concat(outputStream, match);
791
-
792
- // We only consider an audio stream "used" if its language is related to
793
- // the output language. There are scenarios where we want to generate
794
- // separate tracks for each language, even when we are forced to connect
795
- // unrelated languages across periods.
796
- let used = true;
797
- if (outputStream.type == ContentType.AUDIO) {
798
- const relatedness = LanguageUtils.relatedness(
799
- outputStream.language, match.language);
800
- if (relatedness == 0) {
801
- used = false;
802
- }
762
+ for (let i = firstNewPeriodIndex; i < matches.length; i++) {
763
+ const match = matches[i];
764
+ concat(outputStream, match);
765
+
766
+ // We only consider an audio stream "used" if its language is related to
767
+ // the output language. There are scenarios where we want to generate
768
+ // separate tracks for each language, even when we are forced to connect
769
+ // unrelated languages across periods.
770
+ let used = true;
771
+ if (outputStream.type == ContentType.AUDIO) {
772
+ const relatedness = LanguageUtils.relatedness(
773
+ outputStream.language, match.language);
774
+ if (relatedness == 0) {
775
+ used = false;
803
776
  }
777
+ }
804
778
 
805
- if (used) {
806
- unusedStreamsPerPeriod[i].delete(match);
807
- }
779
+ if (used) {
780
+ unusedStreamsPerPeriod[i].delete(match);
808
781
  }
809
782
  }
810
783
  }
@@ -1495,6 +1468,7 @@ shaka.util.PeriodCombiner = class {
1495
1468
  return {
1496
1469
  id: 0,
1497
1470
  originalId: '',
1471
+ groupId: null,
1498
1472
  primary: false,
1499
1473
  type,
1500
1474
  mimeType: '',
@@ -1515,6 +1489,7 @@ shaka.util.PeriodCombiner = class {
1515
1489
  spatialAudio: false,
1516
1490
  closedCaptions: null,
1517
1491
  external: false,
1492
+ fastSwitching: false,
1518
1493
  };
1519
1494
  }
1520
1495
 
@@ -1532,6 +1507,7 @@ shaka.util.PeriodCombiner = class {
1532
1507
  return {
1533
1508
  id: 0,
1534
1509
  originalId: '',
1510
+ groupId: null,
1535
1511
  createSegmentIndex: () => Promise.resolve(),
1536
1512
  segmentIndex: new shaka.media.SegmentIndex([]),
1537
1513
  mimeType: '',
@@ -1554,6 +1530,7 @@ shaka.util.PeriodCombiner = class {
1554
1530
  closedCaptions: null,
1555
1531
  accessibilityPurpose: null,
1556
1532
  external: false,
1533
+ fastSwitching: false,
1557
1534
  };
1558
1535
  }
1559
1536
 
@@ -23,15 +23,16 @@ shaka.util.Platform = class {
23
23
  * @return {boolean}
24
24
  */
25
25
  static supportsMediaSource() {
26
+ const mediaSource = window.ManagedMediaSource || window.MediaSource;
26
27
  // Browsers that lack a media source implementation will have no reference
27
28
  // to |window.MediaSource|. Platforms that we see having problematic media
28
29
  // source implementations will have this reference removed via a polyfill.
29
- if (!window.MediaSource) {
30
+ if (!mediaSource) {
30
31
  return false;
31
32
  }
32
33
 
33
34
  // Some very old MediaSource implementations didn't have isTypeSupported.
34
- if (!MediaSource.isTypeSupported) {
35
+ if (!mediaSource.isTypeSupported) {
35
36
  return false;
36
37
  }
37
38
 
@@ -154,6 +155,26 @@ shaka.util.Platform = class {
154
155
  'Chrome/38.0.2125.122 Safari/537.36');
155
156
  }
156
157
 
158
+ /**
159
+ * Check if the current platform is a WebOS 4.
160
+ *
161
+ * @return {boolean}
162
+ */
163
+ static isWebOS4() {
164
+ // See: http://webostv.developer.lge.com/discover/specifications/web-engine/
165
+ return !!navigator.userAgent.match(/webOS\/4/i);
166
+ }
167
+
168
+ /**
169
+ * Check if the current platform is a WebOS 4.
170
+ *
171
+ * @return {boolean}
172
+ */
173
+ static isWebOS5() {
174
+ // See: http://webostv.developer.lge.com/discover/specifications/web-engine/
175
+ return !!navigator.userAgent.match(/webOS\/5/i);
176
+ }
177
+
157
178
  /**
158
179
  * Check if the current platform is a Google Chromecast.
159
180
  *
@@ -443,7 +464,7 @@ shaka.util.Platform = class {
443
464
  */
444
465
  static requiresEncryptionInfoInAllInitSegments() {
445
466
  const Platform = shaka.util.Platform;
446
- return Platform.isTizen() || Platform.isXboxOne();
467
+ return Platform.isTizen() || Platform.isXboxOne() || Platform.isOrange();
447
468
  }
448
469
 
449
470
  /**
@@ -460,6 +481,26 @@ shaka.util.Platform = class {
460
481
  return true;
461
482
  }
462
483
 
484
+ /**
485
+ * Returns if codec switching SMOOTH is known reliable device support.
486
+ *
487
+ * Some devices are known not to support `MediaSource.changeType`
488
+ * well. These devices should use the reload strategy. If a device
489
+ * reports that it supports `changeType` but support it reliabley
490
+ * it should be added to this list.
491
+ *
492
+ * @return {boolean}
493
+ */
494
+ static supportsSmoothCodecSwitching() {
495
+ const Platform = shaka.util.Platform;
496
+ if (Platform.isTizen2() || Platform.isTizen3() ||
497
+ Platform.isTizen4() || Platform.isWebOS3() ||
498
+ Platform.isWebOS4() || Platform.isWebOS5()) {
499
+ return false;
500
+ }
501
+ return true;
502
+ }
503
+
463
504
  /**
464
505
  * Returns true if MediaKeys is polyfilled
465
506
  *