shaka-player 4.5.0 → 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 (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +98 -0
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTORS +2 -0
  6. package/README.md +38 -32
  7. package/demo/common/asset.js +11 -17
  8. package/demo/common/assets.js +117 -11
  9. package/demo/config.js +28 -1
  10. package/demo/custom.js +52 -38
  11. package/demo/main.js +11 -3
  12. package/demo/search.js +7 -0
  13. package/dist/controls.css +1 -1
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +133 -124
  16. package/dist/demo.compiled.debug.map +3 -3
  17. package/dist/demo.compiled.js +133 -124
  18. package/dist/demo.compiled.map +3 -3
  19. package/dist/deps.js +20 -18
  20. package/dist/locales.js +35 -31
  21. package/dist/receiver.compiled.debug.js +58 -50
  22. package/dist/receiver.compiled.debug.map +3 -3
  23. package/dist/receiver.compiled.js +58 -50
  24. package/dist/receiver.compiled.map +3 -3
  25. package/dist/shaka-player.compiled.d.ts +440 -390
  26. package/dist/shaka-player.compiled.debug.d.ts +440 -390
  27. package/dist/shaka-player.compiled.debug.externs.js +162 -77
  28. package/dist/shaka-player.compiled.debug.js +786 -736
  29. package/dist/shaka-player.compiled.debug.map +4 -4
  30. package/dist/shaka-player.compiled.externs.js +162 -77
  31. package/dist/shaka-player.compiled.js +1154 -1121
  32. package/dist/shaka-player.compiled.map +4 -4
  33. package/dist/shaka-player.ui.d.ts +491 -437
  34. package/dist/shaka-player.ui.debug.d.ts +491 -437
  35. package/dist/shaka-player.ui.debug.externs.js +167 -77
  36. package/dist/shaka-player.ui.debug.js +877 -811
  37. package/dist/shaka-player.ui.debug.map +4 -4
  38. package/dist/shaka-player.ui.externs.js +167 -77
  39. package/dist/shaka-player.ui.js +1362 -1318
  40. package/dist/shaka-player.ui.map +4 -4
  41. package/docs/tutorials/ad_monetization.md +54 -0
  42. package/docs/tutorials/basic-usage.md +2 -1
  43. package/docs/tutorials/errors.md +2 -1
  44. package/docs/tutorials/offline.md +10 -8
  45. package/docs/tutorials/plugins.md +5 -0
  46. package/docs/tutorials/ui-customization.md +13 -0
  47. package/docs/tutorials/ui.md +2 -1
  48. package/docs/tutorials/upgrade.md +3 -0
  49. package/externs/managedmediasource.js +24 -0
  50. package/externs/shaka/abr_manager.js +6 -2
  51. package/externs/shaka/cea.js +7 -0
  52. package/externs/shaka/codecs.js +5 -3
  53. package/externs/shaka/manifest.js +21 -5
  54. package/externs/shaka/manifest_parser.js +17 -0
  55. package/externs/shaka/net.js +2 -2
  56. package/externs/shaka/offline.js +9 -2
  57. package/externs/shaka/player.js +53 -7
  58. package/externs/shaka/text.js +3 -1
  59. package/externs/shaka/transmuxer.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +25 -8
  61. package/lib/ads/media_tailor_ad_manager.js +14 -3
  62. package/lib/cast/cast_receiver.js +1 -1
  63. package/lib/cea/cea_decoder.js +28 -0
  64. package/lib/cea/dummy_caption_decoder.js +5 -0
  65. package/lib/dash/dash_parser.js +249 -83
  66. package/lib/dash/mpd_utils.js +52 -31
  67. package/lib/dash/segment_base.js +4 -5
  68. package/lib/dash/segment_list.js +5 -4
  69. package/lib/dash/segment_template.js +83 -26
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +761 -649
  72. package/lib/hls/hls_utils.js +64 -5
  73. package/lib/hls/manifest_text_parser.js +7 -11
  74. package/lib/media/adaptation_set.js +20 -13
  75. package/lib/media/adaptation_set_criteria.js +70 -18
  76. package/lib/media/closed_caption_parser.js +13 -0
  77. package/lib/media/content_workarounds.js +6 -0
  78. package/lib/media/drm_engine.js +8 -13
  79. package/lib/media/manifest_parser.js +8 -113
  80. package/lib/media/media_source_capabilities.js +10 -3
  81. package/lib/media/media_source_engine.js +75 -21
  82. package/lib/media/play_rate_controller.js +8 -38
  83. package/lib/media/presentation_timeline.js +7 -1
  84. package/lib/media/quality_observer.js +1 -1
  85. package/lib/media/segment_index.js +5 -5
  86. package/lib/media/segment_prefetch.js +92 -24
  87. package/lib/media/segment_reference.js +67 -1
  88. package/lib/media/segment_utils.js +343 -0
  89. package/lib/media/stall_detector.js +0 -3
  90. package/lib/media/streaming_engine.js +41 -21
  91. package/lib/mss/mss_parser.js +18 -2
  92. package/lib/net/networking_engine.js +66 -12
  93. package/lib/net/networking_utils.js +95 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  96. package/lib/offline/manifest_converter.js +2 -0
  97. package/lib/offline/offline_manifest_parser.js +10 -0
  98. package/lib/offline/storage.js +11 -7
  99. package/lib/player.js +993 -1502
  100. package/lib/polyfill/mediasource.js +22 -1
  101. package/lib/text/mp4_ttml_parser.js +2 -2
  102. package/lib/text/srt_text_parser.js +2 -2
  103. package/lib/text/stub_text_displayer.js +51 -0
  104. package/lib/text/text_engine.js +3 -2
  105. package/lib/text/ttml_text_parser.js +28 -5
  106. package/lib/text/ui_text_displayer.js +4 -0
  107. package/lib/text/vtt_text_parser.js +7 -2
  108. package/lib/transmuxer/aac_transmuxer.js +1 -1
  109. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  110. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  111. package/lib/transmuxer/h264.js +5 -4
  112. package/lib/transmuxer/h265.js +5 -4
  113. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  114. package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
  115. package/lib/transmuxer/mss_transmuxer.js +1 -1
  116. package/lib/transmuxer/ts_transmuxer.js +33 -13
  117. package/lib/util/buffer_utils.js +26 -4
  118. package/lib/util/content_steering_manager.js +358 -0
  119. package/lib/util/error.js +2 -6
  120. package/lib/util/fairplay_utils.js +1 -1
  121. package/lib/util/fake_event.js +5 -1
  122. package/lib/util/id3_utils.js +50 -1
  123. package/lib/util/manifest_parser_utils.js +12 -8
  124. package/lib/util/mp4_generator.js +2 -4
  125. package/lib/util/mutex.js +59 -0
  126. package/lib/util/object_utils.js +1 -1
  127. package/lib/util/periods.js +154 -179
  128. package/lib/util/platform.js +4 -3
  129. package/lib/util/player_configuration.js +35 -6
  130. package/lib/util/stream_utils.js +105 -98
  131. package/lib/util/ts_parser.js +124 -73
  132. package/lib/util/uint8array_utils.js +5 -1
  133. package/lib/util/xml_utils.js +5 -0
  134. package/maintained-branches.md +2 -2
  135. package/package.json +1 -1
  136. package/roadmap.md +15 -5
  137. package/shaka-player.uncompiled.js +0 -1
  138. package/test/abr/simple_abr_manager_unit.js +33 -33
  139. package/test/ads/ad_manager_unit.js +3 -2
  140. package/test/cast/cast_receiver_integration.js +3 -4
  141. package/test/cast/cast_utils_unit.js +2 -0
  142. package/test/cea/cea_decoder_unit.js +7 -0
  143. package/test/codec_switching/codec_switching_integration.js +2 -1
  144. package/test/dash/dash_parser_integration.js +2 -1
  145. package/test/dash/dash_parser_live_unit.js +0 -19
  146. package/test/dash/dash_parser_manifest_unit.js +494 -2
  147. package/test/dash/mpd_utils_unit.js +61 -30
  148. package/test/hls/hls_live_unit.js +12 -3
  149. package/test/hls/hls_parser_unit.js +377 -27
  150. package/test/hls/manifest_text_parser_unit.js +32 -99
  151. package/test/media/adaptation_set_criteria_unit.js +172 -21
  152. package/test/media/adaptation_set_unit.js +1 -0
  153. package/test/media/content_workarounds_unit.js +1 -1
  154. package/test/media/drm_engine_unit.js +3 -3
  155. package/test/media/media_source_engine_unit.js +1 -1
  156. package/test/media/play_rate_controller_unit.js +0 -48
  157. package/test/media/segment_index_unit.js +2 -1
  158. package/test/mss/mss_player_integration.js +3 -2
  159. package/test/net/networking_engine_unit.js +3 -3
  160. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  161. package/test/offline/manifest_convert_unit.js +9 -0
  162. package/test/offline/offline_integration.js +2 -1
  163. package/test/offline/storage_integration.js +21 -9
  164. package/test/player_external.js +4 -2
  165. package/test/player_integration.js +15 -4
  166. package/test/player_load_graph_integration.js +47 -151
  167. package/test/player_src_equals_external.js +1 -0
  168. package/test/player_src_equals_integration.js +71 -4
  169. package/test/player_unit.js +80 -27
  170. package/test/test/util/fake_media_source_engine.js +5 -0
  171. package/test/test/util/fake_networking_engine.js +9 -1
  172. package/test/test/util/fake_segment_prefetch.js +3 -0
  173. package/test/test/util/manifest_generator.js +4 -0
  174. package/test/test/util/manifest_parser_util.js +14 -2
  175. package/test/test/util/offline_utils.js +1 -0
  176. package/test/test/util/simple_fakes.js +2 -2
  177. package/test/test/util/streaming_engine_util.js +3 -0
  178. package/test/test/util/test_scheme.js +6 -0
  179. package/test/test/util/ui_utils.js +4 -3
  180. package/test/text/mp4_ttml_parser_unit.js +4 -4
  181. package/test/text/srt_text_parser_unit.js +1 -1
  182. package/test/text/text_displayer_layout_unit.js +1 -1
  183. package/test/text/text_engine_unit.js +6 -0
  184. package/test/text/ttml_text_parser_unit.js +33 -2
  185. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  186. package/test/transmuxer/transmuxer_integration.js +33 -3
  187. package/test/ui/ad_ui_unit.js +2 -2
  188. package/test/ui/ui_customization_unit.js +17 -16
  189. package/test/ui/ui_integration.js +30 -20
  190. package/test/ui/ui_unit.js +25 -19
  191. package/test/util/buffer_utils_unit.js +1 -1
  192. package/test/util/content_steering_manager_unit.js +427 -0
  193. package/test/util/id3_utils_unit.js +52 -0
  194. package/test/util/stream_utils_unit.js +17 -113
  195. package/test/util/ts_parser_unit.js +3 -3
  196. package/third_party/closure-uri/uri.js +13 -1
  197. package/ui/audio_language_selection.js +9 -2
  198. package/ui/externs/ui.js +17 -2
  199. package/ui/language_utils.js +43 -8
  200. package/ui/less/range_elements.less +33 -0
  201. package/ui/locales/en.json +1 -0
  202. package/ui/locales/es-419.json +1 -0
  203. package/ui/locales/es.json +1 -0
  204. package/ui/locales/source.json +4 -0
  205. package/ui/localization.js +10 -0
  206. package/ui/remote_button.js +3 -1
  207. package/ui/resolution_selection.js +47 -10
  208. package/ui/seek_bar.js +296 -3
  209. package/ui/text_selection.js +2 -1
  210. package/ui/ui.js +6 -2
  211. package/lib/routing/node.js +0 -23
  212. package/lib/routing/payload.js +0 -47
  213. package/lib/routing/walker.js +0 -587
  214. package/test/routing/walker_unit.js +0 -505
@@ -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
  }
@@ -1516,6 +1489,7 @@ shaka.util.PeriodCombiner = class {
1516
1489
  spatialAudio: false,
1517
1490
  closedCaptions: null,
1518
1491
  external: false,
1492
+ fastSwitching: false,
1519
1493
  };
1520
1494
  }
1521
1495
 
@@ -1556,6 +1530,7 @@ shaka.util.PeriodCombiner = class {
1556
1530
  closedCaptions: null,
1557
1531
  accessibilityPurpose: null,
1558
1532
  external: false,
1533
+ fastSwitching: false,
1559
1534
  };
1560
1535
  }
1561
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
 
@@ -463,7 +464,7 @@ shaka.util.Platform = class {
463
464
  */
464
465
  static requiresEncryptionInfoInAllInitSegments() {
465
466
  const Platform = shaka.util.Platform;
466
- return Platform.isTizen() || Platform.isXboxOne();
467
+ return Platform.isTizen() || Platform.isXboxOne() || Platform.isOrange();
467
468
  }
468
469
 
469
470
  /**
@@ -91,6 +91,7 @@ shaka.util.PlayerConfiguration = class {
91
91
  // segments to detect key changes.
92
92
  parseInbandPsshEnabled: shaka.util.Platform.isXboxOne(),
93
93
  minHdcpVersion: '',
94
+ ignoreDuplicateInitData: !shaka.util.Platform.isTizen2(),
94
95
  };
95
96
 
96
97
  const manifest = {
@@ -148,6 +149,8 @@ shaka.util.PlayerConfiguration = class {
148
149
  liveSegmentsDelay: 3,
149
150
  sequenceMode: shaka.util.Platform.supportsSequenceMode(),
150
151
  ignoreManifestTimestampsInSegmentsMode: false,
152
+ disableCodecGuessing: false,
153
+ allowLowLatencyByteRangeOptimization: true,
151
154
  },
152
155
  mss: {
153
156
  manifestPreprocessor: (element) => {
@@ -215,15 +218,19 @@ shaka.util.PlayerConfiguration = class {
215
218
  liveSync: false,
216
219
  liveSyncMaxLatency: 1,
217
220
  liveSyncPlaybackRate: 1.1,
221
+ liveSyncMinLatency: 0,
222
+ liveSyncMinPlaybackRate: 1,
218
223
  };
219
224
 
220
- // WebOS, Tizen, and Chromecast have long hardware pipelines that respond
221
- // slowly to seeking. Therefore we should not seek when we detect a stall
225
+ // WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
226
+ // that respond slowly to seeking.
227
+ // Therefore we should not seek when we detect a stall
222
228
  // on one of these platforms. Instead, default stallSkip to 0 to force the
223
229
  // stall detector to pause and play instead.
224
230
  if (shaka.util.Platform.isWebOS() ||
225
231
  shaka.util.Platform.isTizen() ||
226
- shaka.util.Platform.isChromecast()) {
232
+ shaka.util.Platform.isChromecast() ||
233
+ shaka.util.Platform.isHisense()) {
227
234
  streaming.stallSkip = 0;
228
235
  }
229
236
 
@@ -344,6 +351,7 @@ shaka.util.PlayerConfiguration = class {
344
351
  preferredTextRole: '',
345
352
  preferredAudioChannelCount: 2,
346
353
  preferredVideoHdrLevel: 'AUTO',
354
+ preferredVideoLayout: '',
347
355
  preferredVideoCodecs: [],
348
356
  preferredAudioCodecs: [],
349
357
  preferForcedSubs: false,
@@ -374,7 +382,8 @@ shaka.util.PlayerConfiguration = class {
374
382
  // eslint-disable-next-line require-await
375
383
  offline.trackSelectionCallback = async (tracks) => {
376
384
  return shaka.util.PlayerConfiguration.defaultTrackSelect(
377
- tracks, config.preferredAudioLanguage);
385
+ tracks, config.preferredAudioLanguage,
386
+ config.preferredVideoHdrLevel);
378
387
  };
379
388
 
380
389
  return config;
@@ -415,14 +424,34 @@ shaka.util.PlayerConfiguration = class {
415
424
  /**
416
425
  * @param {!Array.<shaka.extern.Track>} tracks
417
426
  * @param {string} preferredAudioLanguage
427
+ * @param {string} preferredVideoHdrLevel
418
428
  * @return {!Array.<shaka.extern.Track>}
419
429
  */
420
- static defaultTrackSelect(tracks, preferredAudioLanguage) {
430
+ static defaultTrackSelect(
431
+ tracks, preferredAudioLanguage, preferredVideoHdrLevel) {
421
432
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
422
433
  const LanguageUtils = shaka.util.LanguageUtils;
423
434
 
435
+ let hdrLevel = preferredVideoHdrLevel;
436
+ if (hdrLevel == 'AUTO') {
437
+ // Auto detect the ideal HDR level.
438
+ if (window.matchMedia('(color-gamut: p3)').matches) {
439
+ hdrLevel = 'PQ';
440
+ } else {
441
+ hdrLevel = 'SDR';
442
+ }
443
+ }
444
+
424
445
  /** @type {!Array.<shaka.extern.Track>} */
425
- const allVariants = tracks.filter((track) => track.type == 'variant');
446
+ const allVariants = tracks.filter((track) => {
447
+ if (track.type != 'variant') {
448
+ return false;
449
+ }
450
+ if (track.hdr && track.hdr != hdrLevel) {
451
+ return false;
452
+ }
453
+ return true;
454
+ });
426
455
 
427
456
  /** @type {!Array.<shaka.extern.Track>} */
428
457
  let selectedVariants = [];