shaka-player 4.13.0 → 4.13.2

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 (67) hide show
  1. package/CONTRIBUTORS +1 -0
  2. package/dist/deps.js +4 -4
  3. package/dist/locales.js +36 -36
  4. package/dist/shaka-player.compiled.d.ts +1 -1
  5. package/dist/shaka-player.compiled.debug.d.ts +1 -1
  6. package/dist/shaka-player.compiled.debug.externs.js +2 -9
  7. package/dist/shaka-player.compiled.debug.js +491 -478
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +2 -9
  10. package/dist/shaka-player.compiled.js +1455 -1443
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +1 -1
  13. package/dist/shaka-player.ui.debug.d.ts +1 -1
  14. package/dist/shaka-player.ui.debug.externs.js +2 -9
  15. package/dist/shaka-player.ui.debug.js +544 -530
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +2 -9
  18. package/dist/shaka-player.ui.js +1627 -1613
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/eslint.config.mjs +14 -1
  21. package/lib/cast/cast_proxy.js +50 -12
  22. package/lib/cast/cast_receiver.js +6 -9
  23. package/lib/cast/cast_sender.js +14 -11
  24. package/lib/cast/cast_utils.js +43 -46
  25. package/lib/cea/mp4_cea_parser.js +23 -23
  26. package/lib/dash/content_protection.js +1 -1
  27. package/lib/dash/dash_parser.js +39 -29
  28. package/lib/dash/mpd_utils.js +8 -9
  29. package/lib/dash/segment_list.js +2 -2
  30. package/lib/dash/segment_template.js +9 -9
  31. package/lib/debug/log.js +16 -17
  32. package/lib/hls/hls_parser.js +122 -99
  33. package/lib/media/closed_caption_parser.js +5 -5
  34. package/lib/media/content_workarounds.js +78 -0
  35. package/lib/media/example_based_criteria.js +4 -3
  36. package/lib/media/manifest_parser.js +8 -12
  37. package/lib/media/media_source_engine.js +136 -119
  38. package/lib/media/preload_manager.js +10 -5
  39. package/lib/media/region_observer.js +9 -8
  40. package/lib/media/region_timeline.js +10 -8
  41. package/lib/media/streaming_engine.js +25 -9
  42. package/lib/mss/mss_parser.js +6 -7
  43. package/lib/net/http_plugin_utils.js +1 -1
  44. package/lib/net/networking_engine.js +7 -7
  45. package/lib/net/networking_utils.js +38 -39
  46. package/lib/offline/stream_bandwidth_estimator.js +8 -8
  47. package/lib/player.js +98 -47
  48. package/lib/polyfill/media_capabilities.js +8 -9
  49. package/lib/text/cue.js +6 -2
  50. package/lib/text/text_engine.js +7 -7
  51. package/lib/text/ttml_text_parser.js +14 -16
  52. package/lib/text/ui_text_displayer.js +25 -4
  53. package/lib/transmuxer/ac3_transmuxer.js +6 -1
  54. package/lib/transmuxer/transmuxer_engine.js +6 -6
  55. package/lib/util/manifest_parser_utils.js +7 -8
  56. package/lib/util/mp4_generator.js +23 -8
  57. package/lib/util/mp4_parser.js +10 -10
  58. package/lib/util/multi_map.js +22 -20
  59. package/lib/util/platform.js +40 -9
  60. package/lib/util/player_configuration.js +1 -1
  61. package/lib/util/stream_utils.js +35 -17
  62. package/package.json +2 -2
  63. package/project-words.txt +1 -0
  64. package/third_party/closure-uri/uri.js +13 -13
  65. package/ui/ad_statistics_button.js +17 -16
  66. package/ui/audio_language_selection.js +6 -1
  67. package/ui/statistics_button.js +36 -35
package/eslint.config.mjs CHANGED
@@ -81,7 +81,6 @@ export default [
81
81
  },
82
82
  rules: {
83
83
  // Things the compiler already takes care of, with more precision: {{{
84
- 'no-console': 'off',
85
84
  'no-eq-null': 'off',
86
85
  'no-eval': 'off',
87
86
  'no-undef': 'off',
@@ -132,6 +131,7 @@ export default [
132
131
  'guard-for-in': 'off',
133
132
  'no-alert': 'error',
134
133
  'no-caller': 'error',
134
+ 'no-console': 'error',
135
135
  'no-div-regex': 'error',
136
136
  'no-extend-native': 'error', // May conflict with future polyfills
137
137
  'no-extra-label': 'error',
@@ -340,4 +340,17 @@ export default [
340
340
  'jsdoc/require-jsdoc': 'off',
341
341
  },
342
342
  },
343
+ {
344
+ files: [
345
+ 'karma.conf.js',
346
+ 'build/**/*.js',
347
+ 'demo/**/*.js',
348
+ 'lib/debug/asserts.js',
349
+ 'lib/debug/log.js',
350
+ 'test/**/**.js',
351
+ ],
352
+ rules: {
353
+ 'no-console': 'off',
354
+ },
355
+ },
343
356
  ];
@@ -81,17 +81,21 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
81
81
  this.compiledToExternNames_ = new Map();
82
82
 
83
83
  /** @private {shaka.cast.CastSender} */
84
- this.sender_ = new shaka.cast.CastSender(
85
- receiverAppId,
86
- () => this.onCastStatusChanged_(),
87
- () => this.onFirstCastStateUpdate_(),
88
- (targetName, event) => this.onRemoteEvent_(targetName, event),
89
- () => this.onResumeLocal_(),
90
- () => this.getInitState_(),
91
- androidReceiverCompatible);
92
-
84
+ this.sender_ = null;
93
85
 
94
- this.init_();
86
+ if (window.chrome) {
87
+ this.sender_ = new shaka.cast.CastSender(
88
+ receiverAppId,
89
+ () => this.onCastStatusChanged_(),
90
+ () => this.onFirstCastStateUpdate_(),
91
+ (targetName, event) => this.onRemoteEvent_(targetName, event),
92
+ () => this.onResumeLocal_(),
93
+ () => this.getInitState_(),
94
+ androidReceiverCompatible);
95
+ this.init_();
96
+ } else {
97
+ this.initWithoutSender_();
98
+ }
95
99
  }
96
100
 
97
101
  /**
@@ -163,6 +167,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
163
167
  * @export
164
168
  */
165
169
  canCast() {
170
+ if (!this.sender_) {
171
+ return false;
172
+ }
166
173
  return this.sender_.apiReady() && this.sender_.hasReceivers();
167
174
  }
168
175
 
@@ -171,6 +178,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
171
178
  * @export
172
179
  */
173
180
  isCasting() {
181
+ if (!this.sender_) {
182
+ return false;
183
+ }
174
184
  return this.sender_.isCasting();
175
185
  }
176
186
 
@@ -179,6 +189,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
179
189
  * @export
180
190
  */
181
191
  receiverName() {
192
+ if (!this.sender_) {
193
+ return '';
194
+ }
182
195
  return this.sender_.receiverName();
183
196
  }
184
197
 
@@ -188,6 +201,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
188
201
  * @export
189
202
  */
190
203
  async cast() {
204
+ if (!this.sender_) {
205
+ return;
206
+ }
191
207
  // TODO: transfer manually-selected tracks?
192
208
  // TODO: transfer side-loaded text tracks?
193
209
 
@@ -208,6 +224,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
208
224
  * @export
209
225
  */
210
226
  setAppData(appData) {
227
+ if (!this.sender_) {
228
+ return;
229
+ }
211
230
  this.sender_.setAppData(appData);
212
231
  }
213
232
 
@@ -216,6 +235,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
216
235
  * @export
217
236
  */
218
237
  suggestDisconnect() {
238
+ if (!this.sender_) {
239
+ return;
240
+ }
219
241
  this.sender_.showDisconnectDialog();
220
242
  }
221
243
 
@@ -224,6 +246,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
224
246
  * @export
225
247
  */
226
248
  forceDisconnect() {
249
+ if (!this.sender_) {
250
+ return;
251
+ }
227
252
  this.sender_.forceDisconnect();
228
253
  }
229
254
 
@@ -243,6 +268,10 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
243
268
  this.receiverAppId_ = newAppId;
244
269
  this.androidReceiverCompatible_ = newCastAndroidReceiver;
245
270
 
271
+ if (!this.sender_) {
272
+ return;
273
+ }
274
+
246
275
  // Destroy the old sender
247
276
  this.sender_.forceDisconnect();
248
277
  await this.sender_.destroy();
@@ -262,6 +291,15 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
262
291
  this.sender_.init();
263
292
  }
264
293
 
294
+ /**
295
+ * Initialize the Proxies without Cast sender.
296
+ * @private
297
+ */
298
+ initWithoutSender_() {
299
+ this.videoProxy_ = /** @type {Object} */(this.localVideo_);
300
+ this.playerProxy_ = /** @type {Object} */(this.localPlayer_);
301
+ }
302
+
265
303
  /**
266
304
  * Initialize the Proxies and the Cast sender.
267
305
  * @private
@@ -721,8 +759,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
721
759
  // If we are casting, but the first update has not come in yet, use local
722
760
  // getters, but not local methods.
723
761
  if (this.sender_.isCasting() && !this.sender_.hasRemoteProperties()) {
724
- if (shaka.cast.CastUtils.PlayerGetterMethods[name] ||
725
- shaka.cast.CastUtils.LargePlayerGetterMethods[name]) {
762
+ if (shaka.cast.CastUtils.PlayerGetterMethods.has(name) ||
763
+ shaka.cast.CastUtils.LargePlayerGetterMethods.has(name)) {
726
764
  const value = /** @type {Object} */(this.localPlayer_)[name];
727
765
  goog.asserts.assert(typeof value == 'function',
728
766
  'only methods on Player');
@@ -484,19 +484,17 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
484
484
  if (this.player_.isLive()) {
485
485
  const PlayerGetterMethodsThatRequireLive =
486
486
  shaka.cast.CastUtils.PlayerGetterMethodsThatRequireLive;
487
- for (const name in PlayerGetterMethodsThatRequireLive) {
488
- const frequency = PlayerGetterMethodsThatRequireLive[name];
487
+ PlayerGetterMethodsThatRequireLive.forEach((frequency, name) => {
489
488
  if (this.updateNumber_ % frequency == 0) {
490
489
  update['player'][name] = /** @type {Object} */ (this.player_)[name]();
491
490
  }
492
- }
491
+ });
493
492
  }
494
- for (const name in shaka.cast.CastUtils.PlayerGetterMethods) {
495
- const frequency = shaka.cast.CastUtils.PlayerGetterMethods[name];
493
+ shaka.cast.CastUtils.PlayerGetterMethods.forEach((frequency, name) => {
496
494
  if (this.updateNumber_ % frequency == 0) {
497
495
  update['player'][name] = /** @type {Object} */ (this.player_)[name]();
498
496
  }
499
- }
497
+ });
500
498
 
501
499
  // Volume attributes are tied to the system volume.
502
500
  const manager = cast.receiver.CastReceiverManager.getInstance();
@@ -512,8 +510,7 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
512
510
  }, this.shakaBus_);
513
511
 
514
512
  // Getters with large outputs each get sent in their own update message.
515
- for (const name in shaka.cast.CastUtils.LargePlayerGetterMethods) {
516
- const frequency = shaka.cast.CastUtils.LargePlayerGetterMethods[name];
513
+ shaka.cast.CastUtils.LargePlayerGetterMethods.forEach((frequency, name) => {
517
514
  if (this.updateNumber_ % frequency == 0) {
518
515
  const update = {'player': {}};
519
516
  update['player'][name] = /** @type {Object} */ (this.player_)[name]();
@@ -523,7 +520,7 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
523
520
  'update': update,
524
521
  }, this.shakaBus_);
525
522
  }
526
- }
523
+ });
527
524
 
528
525
  // Only start progressing the update number once data is loaded,
529
526
  // just in case any of the "rarely changing" properties with less frequent
@@ -91,8 +91,8 @@ shaka.cast.CastSender = class {
91
91
  /** @private {number} */
92
92
  this.nextAsyncCallId_ = 0;
93
93
 
94
- /** @private {Object<string, !shaka.util.PublicPromise>} */
95
- this.asyncCallPromises_ = {};
94
+ /** @private {Map<string, !shaka.util.PublicPromise>} */
95
+ this.asyncCallPromises_ = new Map();
96
96
 
97
97
  /** @private {shaka.util.PublicPromise} */
98
98
  this.castPromise_ = null;
@@ -350,7 +350,7 @@ shaka.cast.CastSender = class {
350
350
  return (...args) => this.remoteCall_(targetName, property, ...args);
351
351
  }
352
352
  } else if (targetName == 'player') {
353
- if (CastUtils.PlayerGetterMethodsThatRequireLive[property]) {
353
+ if (CastUtils.PlayerGetterMethodsThatRequireLive.has(property)) {
354
354
  const isLive = this.get('player', 'isLive')();
355
355
  goog.asserts.assert(isLive,
356
356
  property + ' should be called on a live stream!');
@@ -367,8 +367,8 @@ shaka.cast.CastSender = class {
367
367
  return (...args) =>
368
368
  this.remoteAsyncCall_(targetName, property, ...args);
369
369
  }
370
- if (CastUtils.PlayerGetterMethods[property] ||
371
- CastUtils.LargePlayerGetterMethods[property]) {
370
+ if (CastUtils.PlayerGetterMethods.has(property) ||
371
+ CastUtils.LargePlayerGetterMethods.has(property)) {
372
372
  return () => this.propertyGetter_(targetName, property);
373
373
  }
374
374
  }
@@ -491,7 +491,7 @@ shaka.cast.CastSender = class {
491
491
  const p = new shaka.util.PublicPromise();
492
492
  const id = this.nextAsyncCallId_.toString();
493
493
  this.nextAsyncCallId_++;
494
- this.asyncCallPromises_[id] = p;
494
+ this.asyncCallPromises_.set(id, p);
495
495
 
496
496
  try {
497
497
  this.sendMessage_({
@@ -618,9 +618,12 @@ shaka.cast.CastSender = class {
618
618
  * @private
619
619
  */
620
620
  rejectAllPromises_() {
621
- for (const id in this.asyncCallPromises_) {
622
- const p = this.asyncCallPromises_[id];
623
- delete this.asyncCallPromises_[id];
621
+ if (!this.asyncCallPromises_) {
622
+ return;
623
+ }
624
+ for (const id of this.asyncCallPromises_.keys()) {
625
+ const p = this.asyncCallPromises_.get(id);
626
+ this.asyncCallPromises_.delete(id);
624
627
 
625
628
  // Reject pending async operations as if they were interrupted.
626
629
  // At the moment, load() is the only async operation we are worried about.
@@ -669,8 +672,8 @@ shaka.cast.CastSender = class {
669
672
  case 'asyncComplete': {
670
673
  const id = message['id'];
671
674
  const error = message['error'];
672
- const p = this.asyncCallPromises_[id];
673
- delete this.asyncCallPromises_[id];
675
+ const p = this.asyncCallPromises_.get(id);
676
+ this.asyncCallPromises_.delete(id);
674
677
 
675
678
  goog.asserts.assert(p, 'Unexpected async id');
676
679
  if (!p) {
@@ -300,54 +300,52 @@ shaka.cast.CastUtils.VideoVoidMethods = [
300
300
  * Player getter methods that are proxied while casting.
301
301
  * The key is the method, the value is the frequency of updates.
302
302
  * Frequency 1 translates to every update; frequency 2 to every 2 updates, etc.
303
- * @const {!Object<string, number>}
303
+ * @const {!Map<string, number>}
304
304
  */
305
- shaka.cast.CastUtils.PlayerGetterMethods = {
306
- // NOTE: The 'drmInfo' property is not proxied, as it is very large.
307
- 'getAssetUri': 2,
308
- 'getAudioLanguages': 4,
309
- 'getAudioLanguagesAndRoles': 4,
310
- 'getBufferFullness': 1,
311
- 'getBufferedInfo': 2,
312
- 'getExpiration': 2,
313
- 'getKeyStatuses': 2,
314
- // NOTE: The 'getManifest' property is not proxied, as it is very large.
315
- // NOTE: The 'getManifestParserFactory' property is not proxied, as it would
316
- // not serialize.
317
- 'getPlaybackRate': 2,
318
- 'getTextLanguages': 4,
319
- 'getTextLanguagesAndRoles': 4,
320
- 'getImageTracks': 2,
321
- 'getThumbnails': 2,
322
- 'isAudioOnly': 10,
323
- 'isBuffering': 1,
324
- 'isInProgress': 1,
325
- 'isLive': 10,
326
- 'isTextTrackVisible': 1,
327
- 'keySystem': 10,
328
- 'seekRange': 1,
329
- 'getLoadMode': 10,
330
- 'getManifestType': 10,
331
- 'isFullyLoaded': 1,
332
- 'isEnded': 1,
333
- };
305
+ shaka.cast.CastUtils.PlayerGetterMethods = new Map()
306
+ // NOTE: The 'drmInfo' property is not proxied, as it is very large.
307
+ .set('getAssetUri', 2)
308
+ .set('getAudioLanguages', 4)
309
+ .set('getAudioLanguagesAndRoles', 4)
310
+ .set('getBufferFullness', 1)
311
+ .set('getBufferedInfo', 2)
312
+ .set('getExpiration', 2)
313
+ .set('getKeyStatuses', 2)
314
+ // NOTE: The 'getManifest' property is not proxied, as it is very large.
315
+ // NOTE: The 'getManifestParserFactory' property is not proxied, as it would
316
+ // not serialize.
317
+ .set('getPlaybackRate', 2)
318
+ .set('getTextLanguages', 4)
319
+ .set('getTextLanguagesAndRoles', 4)
320
+ .set('getImageTracks', 2)
321
+ .set('getThumbnails', 2)
322
+ .set('isAudioOnly', 10)
323
+ .set('isBuffering', 1)
324
+ .set('isInProgress', 1)
325
+ .set('isLive', 10)
326
+ .set('isTextTrackVisible', 1)
327
+ .set('keySystem', 10)
328
+ .set('seekRange', 1)
329
+ .set('getLoadMode', 10)
330
+ .set('getManifestType', 10)
331
+ .set('isFullyLoaded', 1)
332
+ .set('isEnded', 1);
334
333
 
335
334
 
336
335
  /**
337
336
  * Player getter methods with data large enough to be sent in their own update
338
337
  * messages, to reduce the size of each message. The format of this is
339
338
  * identical to PlayerGetterMethods.
340
- * @const {!Object<string, number>}
339
+ * @const {!Map<string, number>}
341
340
  */
342
- shaka.cast.CastUtils.LargePlayerGetterMethods = {
343
- // NOTE: The 'getSharedConfiguration' property is not proxied as it would
344
- // not be possible to share a reference.
345
- 'getConfiguration': 4,
346
- 'getConfigurationForLowLatency': 4,
347
- 'getStats': 5,
348
- 'getTextTracks': 2,
349
- 'getVariantTracks': 2,
350
- };
341
+ shaka.cast.CastUtils.LargePlayerGetterMethods = new Map()
342
+ // NOTE: The 'getSharedConfiguration' property is not proxied as it would
343
+ // not be possible to share a reference.
344
+ .set('getConfiguration', 4)
345
+ .set('getConfigurationForLowLatency', 4)
346
+ .set('getStats', 5)
347
+ .set('getTextTracks', 2)
348
+ .set('getVariantTracks', 2);
351
349
 
352
350
 
353
351
  /**
@@ -355,13 +353,12 @@ shaka.cast.CastUtils.LargePlayerGetterMethods = {
355
353
  * a livestream.
356
354
  * The key is the method, the value is the frequency of updates.
357
355
  * Frequency 1 translates to every update; frequency 2 to every 2 updates, etc.
358
- * @const {!Object<string, number>}
356
+ * @const {!Map<string, number>}
359
357
  */
360
- shaka.cast.CastUtils.PlayerGetterMethodsThatRequireLive = {
361
- 'getPlayheadTimeAsDate': 1,
362
- 'getPresentationStartTimeAsDate': 20,
363
- 'getSegmentAvailabilityDuration': 20,
364
- };
358
+ shaka.cast.CastUtils.PlayerGetterMethodsThatRequireLive = new Map()
359
+ .set('getPlayheadTimeAsDate', 1)
360
+ .set('getPresentationStartTimeAsDate', 20)
361
+ .set('getSegmentAvailabilityDuration', 20);
365
362
 
366
363
 
367
364
  /**
@@ -389,8 +389,9 @@ shaka.cea.Mp4CeaParser = class {
389
389
  * @private
390
390
  */
391
391
  setBitstreamFormat_(codec) {
392
- if (codec in shaka.cea.Mp4CeaParser.CodecBitstreamMap_) {
393
- this.bitstreamFormat_ = shaka.cea.Mp4CeaParser.CodecBitstreamMap_[codec];
392
+ if (shaka.cea.Mp4CeaParser.CodecBitstreamMap_.has(codec)) {
393
+ this.bitstreamFormat_ =
394
+ shaka.cea.Mp4CeaParser.CodecBitstreamMap_.get(codec);
394
395
  }
395
396
  }
396
397
  };
@@ -403,27 +404,26 @@ shaka.cea.Mp4CeaParser.BitstreamFormat = {
403
404
  H266: 3,
404
405
  };
405
406
 
406
- /** @private {Object<string, shaka.cea.Mp4CeaParser.BitstreamFormat>} */
407
- shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = {
408
- // AVC
409
- 'avc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
410
- 'avc3': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
411
- // Dolby Vision based in AVC
412
- 'dvav': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
413
- 'dva1': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
414
- // HEVC
415
- 'hev1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
416
- 'hvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
417
- // Dolby Vision based in HEVC
418
- 'dvh1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
419
- 'dvhe': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
420
- // VVC
421
- 'vvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
422
- 'vvi1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
423
- // Dolby Vision based in VVC
424
- 'dvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
425
- 'dvi1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
426
- };
407
+ /** @private {Map<string, shaka.cea.Mp4CeaParser.BitstreamFormat>} */
408
+ shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = new Map()
409
+ // AVC
410
+ .set('avc1', shaka.cea.Mp4CeaParser.BitstreamFormat.H264)
411
+ .set('avc3', shaka.cea.Mp4CeaParser.BitstreamFormat.H264)
412
+ // Dolby Vision based in AVC
413
+ .set('dvav', shaka.cea.Mp4CeaParser.BitstreamFormat.H264)
414
+ .set('dva1', shaka.cea.Mp4CeaParser.BitstreamFormat.H264)
415
+ // HEVC
416
+ .set('hev1', shaka.cea.Mp4CeaParser.BitstreamFormat.H265)
417
+ .set('hvc1', shaka.cea.Mp4CeaParser.BitstreamFormat.H265)
418
+ // Dolby Vision based in HEVC
419
+ .set('dvh1', shaka.cea.Mp4CeaParser.BitstreamFormat.H265)
420
+ .set('dvhe', shaka.cea.Mp4CeaParser.BitstreamFormat.H265)
421
+ // VVC
422
+ .set('vvc1', shaka.cea.Mp4CeaParser.BitstreamFormat.H266)
423
+ .set('vvi1', shaka.cea.Mp4CeaParser.BitstreamFormat.H266)
424
+ // Dolby Vision based in VVC
425
+ .set('dvc1', shaka.cea.Mp4CeaParser.BitstreamFormat.H266)
426
+ .set('dvi1', shaka.cea.Mp4CeaParser.BitstreamFormat.H266);
427
427
 
428
428
  /**
429
429
  * @typedef {{
@@ -203,7 +203,7 @@ shaka.dash.ContentProtection = class {
203
203
  * @return {string}
204
204
  */
205
205
  static getFairPlayLicenseUrl(element) {
206
- if (shaka.util.Platform.isMediaKeysPolyfilled()) {
206
+ if (shaka.util.Platform.isMediaKeysPolyfilled('apple')) {
207
207
  throw new shaka.util.Error(
208
208
  shaka.util.Error.Severity.CRITICAL,
209
209
  shaka.util.Error.Category.MANIFEST,
@@ -98,22 +98,22 @@ shaka.dash.DashParser = class {
98
98
  * A map of IDs to Stream objects.
99
99
  * ID: Period@id,Representation@id
100
100
  * e.g.: '1,23'
101
- * @private {!Object<string, !shaka.extern.Stream>}
101
+ * @private {!Map<string, !shaka.extern.Stream>}
102
102
  */
103
- this.streamMap_ = {};
103
+ this.streamMap_ = new Map();
104
104
 
105
105
  /**
106
106
  * A map of Period IDs to Stream Map IDs.
107
107
  * Use to have direct access to streamMap key.
108
- * @private {!Object<string, !Array<string>>}
108
+ * @private {!Map<string, !Array<string>>}
109
109
  */
110
- this.indexStreamMap_ = {};
110
+ this.indexStreamMap_ = new Map();
111
111
 
112
112
  /**
113
113
  * A map of period ids to their durations
114
- * @private {!Object<string, number>}
114
+ * @private {!Map<string, number>}
115
115
  */
116
- this.periodDurations_ = {};
116
+ this.periodDurations_ = new Map();
117
117
 
118
118
  /** @private {shaka.util.PeriodCombiner} */
119
119
  this.periodCombiner_ = new shaka.util.PeriodCombiner();
@@ -265,7 +265,7 @@ shaka.dash.DashParser = class {
265
265
  stop() {
266
266
  // When the parser stops, release all segment indexes, which stops their
267
267
  // timers, as well.
268
- for (const stream of Object.values(this.streamMap_)) {
268
+ for (const stream of this.streamMap_.values()) {
269
269
  if (stream.segmentIndex) {
270
270
  stream.segmentIndex.release();
271
271
  }
@@ -279,8 +279,8 @@ shaka.dash.DashParser = class {
279
279
  this.config_ = null;
280
280
  this.manifestUris_ = [];
281
281
  this.manifest_ = null;
282
- this.streamMap_ = {};
283
- this.indexStreamMap_ = {};
282
+ this.streamMap_.clear();
283
+ this.indexStreamMap_.clear();
284
284
  this.contextCache_.clear();
285
285
  this.manifestPatchContext_ = {
286
286
  mpdId: '',
@@ -690,7 +690,7 @@ shaka.dash.DashParser = class {
690
690
  // https://github.com/shaka-project/shaka-player/issues/3169#issuecomment-823580634
691
691
  const availabilityStart =
692
692
  presentationTimeline.getSegmentAvailabilityStart();
693
- for (const stream of Object.values(this.streamMap_)) {
693
+ for (const stream of this.streamMap_.values()) {
694
694
  if (stream.segmentIndex) {
695
695
  stream.segmentIndex.evict(availabilityStart);
696
696
  }
@@ -1178,7 +1178,7 @@ shaka.dash.DashParser = class {
1178
1178
  /** @type {shaka.dash.DashParser.Context} */
1179
1179
  const context = this.contextCache_.get(contextId);
1180
1180
 
1181
- const currentStream = this.streamMap_[contextId];
1181
+ const currentStream = this.streamMap_.get(contextId);
1182
1182
  goog.asserts.assert(currentStream, 'stream should exist');
1183
1183
 
1184
1184
  if (currentStream.segmentIndex) {
@@ -1426,7 +1426,7 @@ shaka.dash.DashParser = class {
1426
1426
  periods.push(period);
1427
1427
 
1428
1428
  if (context.period.id && periodDuration) {
1429
- this.periodDurations_[context.period.id] = periodDuration;
1429
+ this.periodDurations_.set(context.period.id, periodDuration);
1430
1430
  }
1431
1431
 
1432
1432
  if (periodDuration == null) {
@@ -1490,14 +1490,14 @@ shaka.dash.DashParser = class {
1490
1490
  * @private
1491
1491
  */
1492
1492
  cleanStreamMap_() {
1493
- const oldPeriodIds = Object.keys(this.indexStreamMap_);
1493
+ const oldPeriodIds = Array.from(this.indexStreamMap_.keys());
1494
1494
  const diffPeriodsIDs = oldPeriodIds.filter((pId) => {
1495
1495
  return !this.lastManifestUpdatePeriodIds_.includes(pId);
1496
1496
  });
1497
1497
  for (const pId of diffPeriodsIDs) {
1498
1498
  let shouldDeleteIndex = true;
1499
- for (const contextId of this.indexStreamMap_[pId]) {
1500
- const stream = this.streamMap_[contextId];
1499
+ for (const contextId of this.indexStreamMap_.get(pId)) {
1500
+ const stream = this.streamMap_.get(contextId);
1501
1501
  if (!stream) {
1502
1502
  continue;
1503
1503
  }
@@ -1506,12 +1506,12 @@ shaka.dash.DashParser = class {
1506
1506
  continue;
1507
1507
  }
1508
1508
  if (this.periodCombiner_) {
1509
- this.periodCombiner_.deleteStream(this.streamMap_[contextId], pId);
1509
+ this.periodCombiner_.deleteStream(stream, pId);
1510
1510
  }
1511
- delete this.streamMap_[contextId];
1511
+ this.streamMap_.delete(contextId);
1512
1512
  }
1513
1513
  if (shouldDeleteIndex) {
1514
- delete this.indexStreamMap_[pId];
1514
+ this.indexStreamMap_.delete(pId);
1515
1515
  }
1516
1516
  }
1517
1517
  }
@@ -1983,6 +1983,10 @@ shaka.dash.DashParser = class {
1983
1983
  // Duplicate representations with their supplementalCodecs
1984
1984
  const obj = shaka.util.ObjectUtils.cloneObject(rep);
1985
1985
  obj.attributes['codecs'] = supplementalCodecs.split(' ').join(',');
1986
+ if (obj.attributes['id']) {
1987
+ obj.attributes['supplementalId'] =
1988
+ obj.attributes['id'] + '_supplementalCodecs';
1989
+ }
1986
1990
  supplementalRepresentations.push(obj);
1987
1991
  }
1988
1992
  }
@@ -2048,8 +2052,9 @@ shaka.dash.DashParser = class {
2048
2052
  }
2049
2053
 
2050
2054
  const repIds = representations
2051
- .map((node) => { return node.attributes['id']; })
2052
- .filter(shaka.util.Functional.isNotNull);
2055
+ .map((node) => {
2056
+ return node.attributes['supplementalId'] || node.attributes['id'];
2057
+ }).filter(shaka.util.Functional.isNotNull);
2053
2058
 
2054
2059
  return {
2055
2060
  id: adaptationId,
@@ -2305,7 +2310,7 @@ shaka.dash.DashParser = class {
2305
2310
  const codecs = context.representation.codecs;
2306
2311
  // Detect the presence of Dolby Atmos audio content based on the codec and
2307
2312
  // bandwidth. Bandwidth above 384 kbps is a good indicator of Atmos content.
2308
- const isAtmos = codecs.includes('ec-3') && context.bandwidth >= 384000;
2313
+ const isAtmos = codecs.includes('ec-3') && context.bandwidth > 384000;
2309
2314
  let spatialAudio = false;
2310
2315
  if (hasJoc || isAtmos) {
2311
2316
  spatialAudio = true;
@@ -2357,8 +2362,8 @@ shaka.dash.DashParser = class {
2357
2362
  /** @type {shaka.extern.Stream} */
2358
2363
  let stream;
2359
2364
 
2360
- if (contextId && this.streamMap_[contextId]) {
2361
- stream = this.streamMap_[contextId];
2365
+ if (contextId && this.streamMap_.has(contextId)) {
2366
+ stream = this.streamMap_.get(contextId);
2362
2367
  } else {
2363
2368
  stream = {
2364
2369
  id: this.globalId_++,
@@ -2416,13 +2421,13 @@ shaka.dash.DashParser = class {
2416
2421
  }
2417
2422
  };
2418
2423
 
2419
- if (contextId && context.dynamic && !this.streamMap_[contextId]) {
2424
+ if (contextId && context.dynamic && !this.streamMap_.has(contextId)) {
2420
2425
  const periodId = context.period.id || '';
2421
- if (!this.indexStreamMap_[periodId]) {
2422
- this.indexStreamMap_[periodId] = [];
2426
+ if (!this.indexStreamMap_.has(periodId)) {
2427
+ this.indexStreamMap_.set(periodId, []);
2423
2428
  }
2424
- this.streamMap_[contextId] = stream;
2425
- this.indexStreamMap_[periodId].push(contextId);
2429
+ this.streamMap_.set(contextId, stream);
2430
+ this.indexStreamMap_.get(periodId).push(contextId);
2426
2431
  }
2427
2432
 
2428
2433
  return stream;
@@ -2580,6 +2585,7 @@ shaka.dash.DashParser = class {
2580
2585
  const evalDivision = TXml.evalDivision;
2581
2586
 
2582
2587
  const id = elem.attributes['id'];
2588
+ const supplementalId = elem.attributes['supplementalId'];
2583
2589
  const uriObjs = TXml.findChildren(elem, 'BaseURL');
2584
2590
  let calculatedBaseUris;
2585
2591
  let someLocationValid = false;
@@ -2694,7 +2700,8 @@ shaka.dash.DashParser = class {
2694
2700
  frameRate: frameRate,
2695
2701
  pixelAspectRatio: pixelAspectRatio,
2696
2702
  emsgSchemeIdUris: emsgSchemeIdUris,
2697
- id: id,
2703
+ id: supplementalId || id,
2704
+ originalId: id,
2698
2705
  language: elem.attributes['lang'],
2699
2706
  numChannels: numChannels,
2700
2707
  audioSamplingRate: audioSamplingRate,
@@ -3243,6 +3250,7 @@ shaka.dash.DashParser.RequestSegmentCallback;
3243
3250
  * pixelAspectRatio: (string|undefined),
3244
3251
  * emsgSchemeIdUris: !Array<string>,
3245
3252
  * id: ?string,
3253
+ * originalId: ?string,
3246
3254
  * position: (number|undefined),
3247
3255
  * language: ?string,
3248
3256
  * numChannels: ?number,
@@ -3284,6 +3292,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
3284
3292
  * emsg registered schemeIdUris.
3285
3293
  * @property {?string} id
3286
3294
  * The ID of the element.
3295
+ * @property {?string} originalId
3296
+ * The original ID of the element.
3287
3297
  * @property {number|undefined} position
3288
3298
  * Position of the element used for indexing in case of no id
3289
3299
  * @property {?string} language