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
@@ -73,23 +73,21 @@ shaka.media.MediaSourceEngine = class {
73
73
  this.textDisplayer_ = textDisplayer;
74
74
 
75
75
  /**
76
- * @private {!Object<shaka.util.ManifestParserUtils.ContentType,
77
- * SourceBuffer>}
76
+ * @private {!Map<shaka.util.ManifestParserUtils.ContentType, SourceBuffer>}
78
77
  */
79
- this.sourceBuffers_ = {};
78
+ this.sourceBuffers_ = new Map();
80
79
 
81
80
  /**
82
- * @private {!Object<shaka.util.ManifestParserUtils.ContentType,
83
- * string>}
81
+ * @private {!Map<shaka.util.ManifestParserUtils.ContentType, string>}
84
82
  */
85
- this.sourceBufferTypes_ = {};
83
+ this.sourceBufferTypes_ = new Map();
86
84
 
87
85
 
88
86
  /**
89
- * @private {!Object<shaka.util.ManifestParserUtils.ContentType,
87
+ * @private {!Map<shaka.util.ManifestParserUtils.ContentType,
90
88
  * boolean>}
91
89
  */
92
- this.expectedEncryption_ = {};
90
+ this.expectedEncryption_ = new Map();
93
91
 
94
92
  /** @private {shaka.text.TextEngine} */
95
93
  this.textEngine_ = null;
@@ -101,16 +99,17 @@ shaka.media.MediaSourceEngine = class {
101
99
  this.lcevcDec_ = lcevcDec || null;
102
100
 
103
101
  /**
104
- * @private {!Object<string,
105
- * !Array<shaka.media.MediaSourceEngine.Operation>>}
102
+ * @private {!Map<string, !Array<shaka.media.MediaSourceEngine.Operation>>}
106
103
  */
107
- this.queues_ = {};
104
+ this.queues_ = new Map();
108
105
 
109
106
  /** @private {shaka.util.EventManager} */
110
107
  this.eventManager_ = new shaka.util.EventManager();
111
108
 
112
- /** @private {!Object<string, !shaka.extern.Transmuxer>} */
113
- this.transmuxers_ = {};
109
+ /**
110
+ * @private {!Map<shaka.util.ManifestParserUtils.ContentType,
111
+ !shaka.extern.Transmuxer>} */
112
+ this.transmuxers_ = new Map();
114
113
 
115
114
  /** @private {?shaka.media.IClosedCaptionParser} */
116
115
  this.captionParser_ = null;
@@ -171,10 +170,10 @@ shaka.media.MediaSourceEngine = class {
171
170
  this.lastDuration_ = null;
172
171
 
173
172
  /**
174
- * @private {!Object<shaka.util.ManifestParserUtils.ContentType,
173
+ * @private {!Map<shaka.util.ManifestParserUtils.ContentType,
175
174
  * !shaka.util.TsParser>}
176
175
  */
177
- this.tsParsers_ = {};
176
+ this.tsParsers_ = new Map();
178
177
 
179
178
  /** @private {?number} */
180
179
  this.firstVideoTimestamp_ = null;
@@ -456,13 +455,14 @@ shaka.media.MediaSourceEngine = class {
456
455
 
457
456
  const cleanup = [];
458
457
 
459
- for (const contentType in this.queues_) {
458
+ for (const [key, q] of this.queues_) {
460
459
  // Make a local copy of the queue and the first item.
461
- const q = this.queues_[contentType];
462
460
  const inProgress = q[0];
463
461
 
462
+ const contentType = /** @type {string} */(key);
463
+
464
464
  // Drop everything else out of the original queue.
465
- this.queues_[contentType] = q.slice(0, 1);
465
+ this.queues_.set(contentType, q.slice(0, 1));
466
466
 
467
467
  // We will wait for this item to complete/fail.
468
468
  if (inProgress) {
@@ -481,8 +481,8 @@ shaka.media.MediaSourceEngine = class {
481
481
 
482
482
  await Promise.all(cleanup);
483
483
 
484
- for (const contentType in this.transmuxers_) {
485
- this.transmuxers_[contentType].destroy();
484
+ for (const transmuxer of this.transmuxers_.values()) {
485
+ transmuxer.destroy();
486
486
  }
487
487
 
488
488
  if (this.eventManager_) {
@@ -507,22 +507,23 @@ shaka.media.MediaSourceEngine = class {
507
507
  this.mediaSource_ = null;
508
508
  this.textEngine_ = null;
509
509
  this.textDisplayer_ = null;
510
- this.sourceBuffers_ = {};
511
- this.transmuxers_ = {};
510
+ this.sourceBuffers_.clear();
511
+ this.expectedEncryption_.clear();
512
+ this.transmuxers_.clear();
512
513
  this.captionParser_ = null;
513
514
  if (goog.DEBUG) {
514
- for (const contentType in this.queues_) {
515
+ for (const [contentType, q] of this.queues_) {
515
516
  goog.asserts.assert(
516
- this.queues_[contentType].length == 0,
517
+ q.length == 0,
517
518
  contentType + ' queue should be empty after destroy!');
518
519
  }
519
520
  }
520
- this.queues_ = {};
521
+ this.queues_.clear();
521
522
 
522
523
  // This object is owned by Player
523
524
  this.lcevcDec_ = null;
524
525
 
525
- this.tsParsers_ = {};
526
+ this.tsParsers_.clear();
526
527
  this.playerInterface_ = null;
527
528
  }
528
529
 
@@ -581,7 +582,7 @@ shaka.media.MediaSourceEngine = class {
581
582
  this.manifestType_ == shaka.media.ManifestParser.HLS &&
582
583
  !this.ignoreManifestTimestampsInSegmentsMode_;
583
584
 
584
- this.tsParsers_ = {};
585
+ this.tsParsers_.clear();
585
586
  this.firstVideoTimestamp_ = null;
586
587
  this.firstVideoReferenceStartTime_ = null;
587
588
  this.firstAudioTimestamp_ = null;
@@ -593,10 +594,10 @@ shaka.media.MediaSourceEngine = class {
593
594
  // eslint-disable-next-line no-await-in-loop
594
595
  await this.initSourceBuffer_(contentType, stream, stream.codecs);
595
596
  if (this.needSplitMuxedContent_) {
596
- this.queues_[ContentType.AUDIO] = [];
597
- this.queues_[ContentType.VIDEO] = [];
597
+ this.queues_.set(ContentType.AUDIO, []);
598
+ this.queues_.set(ContentType.VIDEO, []);
598
599
  } else {
599
- this.queues_[contentType] = [];
600
+ this.queues_.set(contentType, []);
600
601
  }
601
602
  }
602
603
  const audio = streamsByType.get(ContentType.AUDIO);
@@ -622,6 +623,11 @@ shaka.media.MediaSourceEngine = class {
622
623
  stream, contentType),
623
624
  'Type negotiation should happen before MediaSourceEngine.init!');
624
625
 
626
+ if (contentType == ContentType.AUDIO && codecs) {
627
+ codecs = shaka.util.StreamUtils.getCorrectAudioCodecs(
628
+ codecs, stream.mimeType);
629
+ }
630
+
625
631
  let mimeType = shaka.util.MimeUtils.getFullType(
626
632
  stream.mimeType, codecs);
627
633
  if (contentType == ContentType.TEXT) {
@@ -651,7 +657,7 @@ shaka.media.MediaSourceEngine = class {
651
657
  .findTransmuxer(mimeTypeWithAllCodecs);
652
658
  if (transmuxerPlugin) {
653
659
  const transmuxer = transmuxerPlugin();
654
- this.transmuxers_[contentType] = transmuxer;
660
+ this.transmuxers_.set(contentType, transmuxer);
655
661
  mimeType =
656
662
  transmuxer.convertCodecs(contentType, mimeTypeWithAllCodecs);
657
663
  }
@@ -686,9 +692,9 @@ shaka.media.MediaSourceEngine = class {
686
692
  this.eventManager_.listen(
687
693
  sourceBuffer, 'updateend',
688
694
  () => this.onUpdateEnd_(contentType));
689
- this.sourceBuffers_[contentType] = sourceBuffer;
690
- this.sourceBufferTypes_[contentType] = mimeType;
691
- this.expectedEncryption_[contentType] = !!stream.drmInfos.length;
695
+ this.sourceBuffers_.set(contentType, sourceBuffer);
696
+ this.sourceBufferTypes_.set(contentType, mimeType);
697
+ this.expectedEncryption_.set(contentType, !!stream.drmInfos.length);
692
698
  }
693
699
  }
694
700
 
@@ -762,7 +768,7 @@ shaka.media.MediaSourceEngine = class {
762
768
  * @return {?number} The timestamp in seconds, or null if nothing is buffered.
763
769
  */
764
770
  bufferStart(contentType) {
765
- if (!Object.keys(this.sourceBuffers_).length) {
771
+ if (!this.sourceBuffers_.size) {
766
772
  return null;
767
773
  }
768
774
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
@@ -780,7 +786,7 @@ shaka.media.MediaSourceEngine = class {
780
786
  * @return {?number} The timestamp in seconds, or null if nothing is buffered.
781
787
  */
782
788
  bufferEnd(contentType) {
783
- if (!Object.keys(this.sourceBuffers_).length) {
789
+ if (!this.sourceBuffers_.size) {
784
790
  return null;
785
791
  }
786
792
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
@@ -867,9 +873,9 @@ shaka.media.MediaSourceEngine = class {
867
873
  return null;
868
874
  }
869
875
  try {
870
- return this.sourceBuffers_[contentType].buffered;
876
+ return this.sourceBuffers_.get(contentType).buffered;
871
877
  } catch (exception) {
872
- if (contentType in this.sourceBuffers_) {
878
+ if (this.sourceBuffers_.has(contentType)) {
873
879
  // Note: previous MediaSource errors may cause access to |buffered| to
874
880
  // throw.
875
881
  shaka.log.error('failed to get buffered range for ' + contentType,
@@ -968,12 +974,12 @@ shaka.media.MediaSourceEngine = class {
968
974
  }
969
975
  } else if (!mimeType.includes('/mp4') && !mimeType.includes('/webm') &&
970
976
  shaka.util.TsParser.probe(uint8ArrayData)) {
971
- if (!this.tsParsers_[contentType]) {
972
- this.tsParsers_[contentType] = new shaka.util.TsParser();
977
+ if (!this.tsParsers_.has(contentType)) {
978
+ this.tsParsers_.set(contentType, new shaka.util.TsParser());
973
979
  } else {
974
- this.tsParsers_[contentType].clearData();
980
+ this.tsParsers_.get(contentType).clearData();
975
981
  }
976
- const tsParser = this.tsParsers_[contentType].parse(uint8ArrayData);
982
+ const tsParser = this.tsParsers_.get(contentType).parse(uint8ArrayData);
977
983
  const startTime = tsParser.getStartTime(contentType);
978
984
  if (startTime != null) {
979
985
  timestamp = startTime;
@@ -1198,16 +1204,16 @@ shaka.media.MediaSourceEngine = class {
1198
1204
  return;
1199
1205
  }
1200
1206
 
1201
- if (!this.sourceBuffers_[contentType]) {
1207
+ if (!this.sourceBuffers_.has(contentType)) {
1202
1208
  shaka.log.warning('Attempted to restore a non-existent source buffer');
1203
1209
  return;
1204
1210
  }
1205
1211
 
1206
- let timestampOffset = this.sourceBuffers_[contentType].timestampOffset;
1212
+ let timestampOffset = this.sourceBuffers_.get(contentType).timestampOffset;
1207
1213
 
1208
- let mimeType = this.sourceBufferTypes_[contentType];
1209
- if (this.transmuxers_[contentType]) {
1210
- mimeType = this.transmuxers_[contentType].getOriginalMimeType();
1214
+ let mimeType = this.sourceBufferTypes_.get(contentType);
1215
+ if (this.transmuxers_.has(contentType)) {
1216
+ mimeType = this.transmuxers_.get(contentType).getOriginalMimeType();
1211
1217
  }
1212
1218
  if (reference) {
1213
1219
  const {timestamp, metadata} = this.getTimestampAndDispatchMetadata(
@@ -1251,7 +1257,7 @@ shaka.media.MediaSourceEngine = class {
1251
1257
  // adjust TimestampOffset relative to 0 because segments do not have
1252
1258
  // any timestamp information.
1253
1259
  if (!this.sequenceMode_ &&
1254
- RAW_FORMATS.includes(this.sourceBufferTypes_[contentType])) {
1260
+ RAW_FORMATS.includes(this.sourceBufferTypes_.get(contentType))) {
1255
1261
  realTimestamp = 0;
1256
1262
  }
1257
1263
  const calculatedTimestampOffset = reference.startTime - realTimestamp;
@@ -1277,7 +1283,7 @@ shaka.media.MediaSourceEngine = class {
1277
1283
  // Prefer video for this when available.
1278
1284
  const isBestSourceBufferForTimestamps =
1279
1285
  contentType == ContentType.VIDEO ||
1280
- !(ContentType.VIDEO in this.sourceBuffers_);
1286
+ !(this.sourceBuffers_.has(ContentType.VIDEO));
1281
1287
  if (isBestSourceBufferForTimestamps) {
1282
1288
  this.textSequenceModeOffset_.resolve(timestampOffset);
1283
1289
  }
@@ -1312,8 +1318,8 @@ shaka.media.MediaSourceEngine = class {
1312
1318
  }
1313
1319
  }
1314
1320
 
1315
- if (this.transmuxers_[contentType]) {
1316
- data = await this.transmuxers_[contentType].transmux(
1321
+ if (this.transmuxers_.has(contentType)) {
1322
+ data = await this.transmuxers_.get(contentType).transmux(
1317
1323
  data, stream, reference, this.mediaSource_.duration, contentType);
1318
1324
  }
1319
1325
 
@@ -1332,7 +1338,7 @@ shaka.media.MediaSourceEngine = class {
1332
1338
  if (this.manifestType_ == shaka.media.ManifestParser.HLS &&
1333
1339
  !this.needSplitMuxedContent_ &&
1334
1340
  contentType == ContentType.AUDIO &&
1335
- this.sourceBuffers_[ContentType.VIDEO]) {
1341
+ this.sourceBuffers_.has(ContentType.VIDEO)) {
1336
1342
  const compensation = await this.audioCompensation_;
1337
1343
  // Only apply compensation if the difference is greater than 100ms
1338
1344
  if (Math.abs(compensation) > 0.1) {
@@ -1687,13 +1693,13 @@ shaka.media.MediaSourceEngine = class {
1687
1693
  // have to insert a dummy operation into the beginning of each queue,
1688
1694
  // which the 'updateend' handler will remove.
1689
1695
  if (duration < this.mediaSource_.duration) {
1690
- for (const contentType in this.sourceBuffers_) {
1696
+ for (const contentType of this.sourceBuffers_.keys()) {
1691
1697
  const dummyOperation = {
1692
1698
  start: () => {},
1693
1699
  p: new shaka.util.PublicPromise(),
1694
1700
  uri: null,
1695
1701
  };
1696
- this.queues_[contentType].unshift(dummyOperation);
1702
+ this.queues_.get(contentType).unshift(dummyOperation);
1697
1703
  }
1698
1704
  }
1699
1705
 
@@ -1766,7 +1772,7 @@ shaka.media.MediaSourceEngine = class {
1766
1772
  }
1767
1773
 
1768
1774
  // This will trigger an 'updateend' event.
1769
- this.sourceBuffers_[contentType].appendBuffer(data);
1775
+ this.sourceBuffers_.get(contentType).appendBuffer(data);
1770
1776
  }
1771
1777
 
1772
1778
  /**
@@ -1785,7 +1791,7 @@ shaka.media.MediaSourceEngine = class {
1785
1791
  }
1786
1792
 
1787
1793
  // This will trigger an 'updateend' event.
1788
- this.sourceBuffers_[contentType].remove(startTime, endTime);
1794
+ this.sourceBuffers_.get(contentType).remove(startTime, endTime);
1789
1795
  }
1790
1796
 
1791
1797
  /**
@@ -1796,18 +1802,18 @@ shaka.media.MediaSourceEngine = class {
1796
1802
  * @private
1797
1803
  */
1798
1804
  abort_(contentType) {
1805
+ const sourceBuffer = this.sourceBuffers_.get(contentType);
1799
1806
  // Save the append window, which is reset on abort().
1800
- const appendWindowStart =
1801
- this.sourceBuffers_[contentType].appendWindowStart;
1802
- const appendWindowEnd = this.sourceBuffers_[contentType].appendWindowEnd;
1807
+ const appendWindowStart = sourceBuffer.appendWindowStart;
1808
+ const appendWindowEnd = sourceBuffer.appendWindowEnd;
1803
1809
 
1804
1810
  // This will not trigger an 'updateend' event, since nothing is happening.
1805
1811
  // This is only to reset MSE internals, not to abort an actual operation.
1806
- this.sourceBuffers_[contentType].abort();
1812
+ sourceBuffer.abort();
1807
1813
 
1808
1814
  // Restore the append window.
1809
- this.sourceBuffers_[contentType].appendWindowStart = appendWindowStart;
1810
- this.sourceBuffers_[contentType].appendWindowEnd = appendWindowEnd;
1815
+ sourceBuffer.appendWindowStart = appendWindowStart;
1816
+ sourceBuffer.appendWindowEnd = appendWindowEnd;
1811
1817
 
1812
1818
  // Fake an 'updateend' event to resolve the operation.
1813
1819
  this.onUpdateEnd_(contentType);
@@ -1849,7 +1855,7 @@ shaka.media.MediaSourceEngine = class {
1849
1855
  timestampOffset += 0.001;
1850
1856
  }
1851
1857
 
1852
- this.sourceBuffers_[contentType].timestampOffset = timestampOffset;
1858
+ this.sourceBuffers_.get(contentType).timestampOffset = timestampOffset;
1853
1859
 
1854
1860
  // Fake an 'updateend' event to resolve the operation.
1855
1861
  this.onUpdateEnd_(contentType);
@@ -1863,12 +1869,13 @@ shaka.media.MediaSourceEngine = class {
1863
1869
  * @private
1864
1870
  */
1865
1871
  setAppendWindow_(contentType, appendWindowStart, appendWindowEnd) {
1872
+ const sourceBuffer = this.sourceBuffers_.get(contentType);
1866
1873
  // You can't set start > end, so first set start to 0, then set the new
1867
1874
  // end, then set the new start. That way, there are no intermediate
1868
1875
  // states which are invalid.
1869
- this.sourceBuffers_[contentType].appendWindowStart = 0;
1870
- this.sourceBuffers_[contentType].appendWindowEnd = appendWindowEnd;
1871
- this.sourceBuffers_[contentType].appendWindowStart = appendWindowStart;
1876
+ sourceBuffer.appendWindowStart = 0;
1877
+ sourceBuffer.appendWindowEnd = appendWindowEnd;
1878
+ sourceBuffer.appendWindowStart = appendWindowStart;
1872
1879
 
1873
1880
  // Fake an 'updateend' event to resolve the operation.
1874
1881
  this.onUpdateEnd_(contentType);
@@ -1879,9 +1886,9 @@ shaka.media.MediaSourceEngine = class {
1879
1886
  * @private
1880
1887
  */
1881
1888
  onError_(contentType) {
1882
- const operation = this.queues_[contentType][0];
1889
+ const operation = this.queues_.get(contentType)[0];
1883
1890
  goog.asserts.assert(operation, 'Spurious error event!');
1884
- goog.asserts.assert(!this.sourceBuffers_[contentType].updating,
1891
+ goog.asserts.assert(!this.sourceBuffers_.get(contentType).updating,
1885
1892
  'SourceBuffer should not be updating on error!');
1886
1893
  const code = this.video_.error ? this.video_.error.code : 0;
1887
1894
  operation.p.reject(new shaka.util.Error(
@@ -1904,12 +1911,12 @@ shaka.media.MediaSourceEngine = class {
1904
1911
  if (this.reloadingMediaSource_) {
1905
1912
  return;
1906
1913
  }
1907
- const operation = this.queues_[contentType][0];
1914
+ const operation = this.queues_.get(contentType)[0];
1908
1915
  goog.asserts.assert(operation, 'Spurious updateend event!');
1909
1916
  if (!operation) {
1910
1917
  return;
1911
1918
  }
1912
- goog.asserts.assert(!this.sourceBuffers_[contentType].updating,
1919
+ goog.asserts.assert(!this.sourceBuffers_.get(contentType).updating,
1913
1920
  'SourceBuffer should not be updating on updateend!');
1914
1921
  operation.p.resolve();
1915
1922
  this.popFromQueue_(contentType);
@@ -1931,9 +1938,9 @@ shaka.media.MediaSourceEngine = class {
1931
1938
  p: new shaka.util.PublicPromise(),
1932
1939
  uri,
1933
1940
  };
1934
- this.queues_[contentType].push(operation);
1941
+ this.queues_.get(contentType).push(operation);
1935
1942
 
1936
- if (this.queues_[contentType].length == 1) {
1943
+ if (this.queues_.get(contentType).length == 1) {
1937
1944
  this.startOperation_(contentType);
1938
1945
  }
1939
1946
  return operation.p;
@@ -1953,7 +1960,7 @@ shaka.media.MediaSourceEngine = class {
1953
1960
  /** @type {!Array<!shaka.util.PublicPromise>} */
1954
1961
  const allWaiters = [];
1955
1962
  /** @type {!Array<!shaka.util.ManifestParserUtils.ContentType>} */
1956
- const contentTypes = Object.keys(this.sourceBuffers_);
1963
+ const contentTypes = Array.from(this.sourceBuffers_.keys());
1957
1964
 
1958
1965
  // Enqueue a 'wait' operation onto each queue.
1959
1966
  // This operation signals its readiness when it starts.
@@ -1966,10 +1973,12 @@ shaka.media.MediaSourceEngine = class {
1966
1973
  uri: null,
1967
1974
  };
1968
1975
 
1969
- this.queues_[contentType].push(operation);
1976
+ const queue = this.queues_.get(contentType);
1977
+
1978
+ queue.push(operation);
1970
1979
  allWaiters.push(ready);
1971
1980
 
1972
- if (this.queues_[contentType].length == 1) {
1981
+ if (queue.length == 1) {
1973
1982
  operation.start();
1974
1983
  }
1975
1984
  }
@@ -1989,14 +1998,13 @@ shaka.media.MediaSourceEngine = class {
1989
1998
  // are wiped in destroy.
1990
1999
  if (goog.DEBUG) {
1991
2000
  for (const contentType of contentTypes) {
1992
- if (this.queues_[contentType].length) {
1993
- goog.asserts.assert(
1994
- this.queues_[contentType].length == 1,
2001
+ const queue = this.queues_.get(contentType);
2002
+ if (queue.length) {
2003
+ goog.asserts.assert(queue.length == 1,
1995
2004
  'Should be at most one item in queue!');
1996
- goog.asserts.assert(
1997
- allWaiters.includes(this.queues_[contentType][0].p),
2005
+ goog.asserts.assert(allWaiters.includes(queue[0].p),
1998
2006
  'The item in queue should be one of our waiters!');
1999
- this.queues_[contentType].shift();
2007
+ queue.shift();
2000
2008
  }
2001
2009
  }
2002
2010
  }
@@ -2007,7 +2015,7 @@ shaka.media.MediaSourceEngine = class {
2007
2015
  // If we did it correctly, nothing is updating.
2008
2016
  for (const contentType of contentTypes) {
2009
2017
  goog.asserts.assert(
2010
- this.sourceBuffers_[contentType].updating == false,
2018
+ this.sourceBuffers_.get(contentType).updating == false,
2011
2019
  'SourceBuffers should not be updating after a blocking op!');
2012
2020
  }
2013
2021
  }
@@ -2037,9 +2045,9 @@ shaka.media.MediaSourceEngine = class {
2037
2045
  * @private
2038
2046
  */
2039
2047
  popFromQueue_(contentType) {
2040
- goog.asserts.assert(this.queues_[contentType], 'Queue should exist');
2048
+ goog.asserts.assert(this.queues_.has(contentType), 'Queue should exist');
2041
2049
  // Remove the in-progress operation, which is now complete.
2042
- this.queues_[contentType].shift();
2050
+ this.queues_.get(contentType).shift();
2043
2051
  this.startOperation_(contentType);
2044
2052
  }
2045
2053
 
@@ -2050,7 +2058,7 @@ shaka.media.MediaSourceEngine = class {
2050
2058
  */
2051
2059
  startOperation_(contentType) {
2052
2060
  // Retrieve the next operation, if any, from the queue and start it.
2053
- const next = this.queues_[contentType][0];
2061
+ const next = this.queues_.get(contentType)[0];
2054
2062
  if (next) {
2055
2063
  try {
2056
2064
  next.start();
@@ -2118,7 +2126,7 @@ shaka.media.MediaSourceEngine = class {
2118
2126
  const Platform = shaka.util.Platform;
2119
2127
 
2120
2128
  const isInitSegment = startTime == null;
2121
- const encryptionExpected = this.expectedEncryption_[contentType];
2129
+ const encryptionExpected = this.expectedEncryption_.get(contentType);
2122
2130
  const keySystem = this.playerInterface_.getKeySystem();
2123
2131
 
2124
2132
  // If:
@@ -2136,11 +2144,19 @@ shaka.media.MediaSourceEngine = class {
2136
2144
  encryptionExpected &&
2137
2145
  Platform.requiresEncryptionInfoInAllInitSegments(keySystem) &&
2138
2146
  shaka.util.MimeUtils.getContainerType(
2139
- this.sourceBufferTypes_[contentType]) == 'mp4') {
2147
+ this.sourceBufferTypes_.get(contentType)) == 'mp4') {
2140
2148
  shaka.log.debug('Forcing fake encryption information in init segment.');
2141
2149
  segment = shaka.media.ContentWorkarounds.fakeEncryption(segment, uri);
2142
2150
  }
2143
2151
 
2152
+ if (isInitSegment &&
2153
+ Platform.requiresEC3InitSegments() &&
2154
+ shaka.util.MimeUtils.getContainerType(
2155
+ this.sourceBufferTypes_.get(contentType)) == 'mp4') {
2156
+ shaka.log.debug('Forcing fake EC-3 information in init segment.');
2157
+ segment = shaka.media.ContentWorkarounds.fakeEC3(segment);
2158
+ }
2159
+
2144
2160
  return segment;
2145
2161
  }
2146
2162
 
@@ -2158,19 +2174,20 @@ shaka.media.MediaSourceEngine = class {
2158
2174
  shaka.log.debug(`Change not supported for ${contentType}`);
2159
2175
  return;
2160
2176
  }
2177
+ const sourceBuffer = this.sourceBufferTypes_.get(contentType);
2161
2178
  shaka.log.debug(
2162
- `Change Type: ${this.sourceBufferTypes_[contentType]} -> ${mimeType}`);
2179
+ `Change Type: ${sourceBuffer} -> ${mimeType}`);
2163
2180
  if (shaka.media.Capabilities.isChangeTypeSupported()) {
2164
- if (this.transmuxers_[contentType]) {
2165
- this.transmuxers_[contentType].destroy();
2166
- delete this.transmuxers_[contentType];
2181
+ if (this.transmuxers_.has(contentType)) {
2182
+ this.transmuxers_.get(contentType).destroy();
2183
+ this.transmuxers_.delete(contentType);
2167
2184
  }
2168
2185
  if (transmuxer) {
2169
- this.transmuxers_[contentType] = transmuxer;
2186
+ this.transmuxers_.set(contentType, transmuxer);
2170
2187
  }
2171
2188
  const type = this.addExtraFeaturesToMimeType_(mimeType);
2172
- this.sourceBuffers_[contentType].changeType(type);
2173
- this.sourceBufferTypes_[contentType] = mimeType;
2189
+ this.sourceBuffers_.get(contentType).changeType(type);
2190
+ this.sourceBufferTypes_.set(contentType, mimeType);
2174
2191
  } else {
2175
2192
  shaka.log.debug('Change Type not supported');
2176
2193
  }
@@ -2224,19 +2241,18 @@ shaka.media.MediaSourceEngine = class {
2224
2241
  try {
2225
2242
  this.eventManager_.removeAll();
2226
2243
 
2227
- for (const contentType in this.transmuxers_) {
2228
- this.transmuxers_[contentType].destroy();
2244
+ for (const transmuxer of this.transmuxers_.values()) {
2245
+ transmuxer.destroy();
2229
2246
  }
2230
- for (const contentType in this.sourceBuffers_) {
2231
- const sourceBuffer = this.sourceBuffers_[contentType];
2247
+ for (const sourceBuffer of this.sourceBuffers_.values()) {
2232
2248
  try {
2233
2249
  this.mediaSource_.removeSourceBuffer(sourceBuffer);
2234
2250
  } catch (e) {
2235
2251
  shaka.log.debug('Exception on removeSourceBuffer', e);
2236
2252
  }
2237
2253
  }
2238
- this.transmuxers_ = {};
2239
- this.sourceBuffers_ = {};
2254
+ this.transmuxers_.clear();
2255
+ this.sourceBuffers_.clear();
2240
2256
 
2241
2257
  const previousDuration = this.mediaSource_.duration;
2242
2258
  this.mediaSourceOpen_ = new shaka.util.PublicPromise();
@@ -2275,8 +2291,8 @@ shaka.media.MediaSourceEngine = class {
2275
2291
  if (audio && audio.isAudioMuxedInVideo) {
2276
2292
  this.needSplitMuxedContent_ = true;
2277
2293
  }
2278
- if (this.needSplitMuxedContent_ && !this.queues_[ContentType.AUDIO]) {
2279
- this.queues_[ContentType.AUDIO] = [];
2294
+ if (this.needSplitMuxedContent_ && !this.queues_.has(ContentType.AUDIO)) {
2295
+ this.queues_.set(ContentType.AUDIO, []);
2280
2296
  }
2281
2297
 
2282
2298
  // Fake a seek to catchup the playhead.
@@ -2395,9 +2411,9 @@ shaka.media.MediaSourceEngine = class {
2395
2411
  }
2396
2412
  const MimeUtils = shaka.util.MimeUtils;
2397
2413
  const currentCodec = MimeUtils.getNormalizedCodec(
2398
- MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
2414
+ MimeUtils.getCodecs(this.sourceBufferTypes_.get(contentType)));
2399
2415
  const currentBasicType = MimeUtils.getBasicType(
2400
- this.sourceBufferTypes_[contentType]);
2416
+ this.sourceBufferTypes_.get(contentType));
2401
2417
 
2402
2418
  const realInfo = this.getRealInfo_(contentType, mimeType, codecs);
2403
2419
  const transmuxer = realInfo.transmuxer;
@@ -2411,9 +2427,9 @@ shaka.media.MediaSourceEngine = class {
2411
2427
  const muxedRealInfo =
2412
2428
  this.getRealInfo_(ContentType.AUDIO, mimeType, codecs);
2413
2429
  const muxedCurrentCodec = MimeUtils.getNormalizedCodec(
2414
- MimeUtils.getCodecs(this.sourceBufferTypes_[ContentType.AUDIO]));
2430
+ MimeUtils.getCodecs(this.sourceBufferTypes_.get(ContentType.AUDIO)));
2415
2431
  const muxedCurrentBasicType = MimeUtils.getBasicType(
2416
- this.sourceBufferTypes_[ContentType.AUDIO]);
2432
+ this.sourceBufferTypes_.get(ContentType.AUDIO));
2417
2433
  muxedContentCheck = muxedCurrentCodec == muxedRealInfo.codec &&
2418
2434
  muxedCurrentBasicType == muxedRealInfo.basicType;
2419
2435
  if (muxedRealInfo.transmuxer) {
@@ -2424,18 +2440,19 @@ shaka.media.MediaSourceEngine = class {
2424
2440
  // Current/new codecs base and basic type match then no need to switch
2425
2441
  if (currentCodec === newCodec && currentBasicType === newBasicType &&
2426
2442
  muxedContentCheck) {
2427
- if (this.transmuxers_[contentType] && !transmuxer) {
2428
- this.transmuxers_[contentType].destroy();
2429
- delete this.transmuxers_[contentType];
2430
- } else if (!this.transmuxers_[contentType] && transmuxer) {
2431
- this.transmuxers_[contentType] = transmuxer;
2443
+ if (this.transmuxers_.has(contentType) && !transmuxer) {
2444
+ this.transmuxers_.get(contentType).destroy();
2445
+ this.transmuxers_.delete(contentType);
2446
+ } else if (!this.transmuxers_.has(contentType) && transmuxer) {
2447
+ this.transmuxers_.set(contentType, transmuxer);
2432
2448
  } else if (transmuxer) {
2433
2449
  // Compare if the transmuxer is different
2434
- if (this.transmuxers_[contentType] &&
2435
- this.transmuxers_[contentType].transmux != transmuxer.transmux) {
2436
- this.transmuxers_[contentType].destroy();
2437
- delete this.transmuxers_[contentType];
2438
- this.transmuxers_[contentType] = transmuxer;
2450
+ if (this.transmuxers_.has(contentType) &&
2451
+ this.transmuxers_.get(contentType).transmux !=
2452
+ transmuxer.transmux) {
2453
+ this.transmuxers_.get(contentType).destroy();
2454
+ this.transmuxers_.delete(contentType);
2455
+ this.transmuxers_.set(contentType, transmuxer);
2439
2456
  } else {
2440
2457
  transmuxer.destroy();
2441
2458
  }
@@ -2445,7 +2462,7 @@ shaka.media.MediaSourceEngine = class {
2445
2462
 
2446
2463
  let allowChangeType = true;
2447
2464
  if (this.needSplitMuxedContent_ || (transmuxerMuxed &&
2448
- transmuxer && !this.transmuxers_[contentType])) {
2465
+ transmuxer && !this.transmuxers_.has(contentType))) {
2449
2466
  allowChangeType = false;
2450
2467
  }
2451
2468
 
@@ -2477,9 +2494,9 @@ shaka.media.MediaSourceEngine = class {
2477
2494
  }
2478
2495
  const MimeUtils = shaka.util.MimeUtils;
2479
2496
  const currentCodec = MimeUtils.getNormalizedCodec(
2480
- MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
2497
+ MimeUtils.getCodecs(this.sourceBufferTypes_.get(contentType)));
2481
2498
  const currentBasicType = MimeUtils.getBasicType(
2482
- this.sourceBufferTypes_[contentType]);
2499
+ this.sourceBufferTypes_.get(contentType));
2483
2500
 
2484
2501
  let newMimeType = shaka.util.MimeUtils.getFullType(refMimeType, refCodecs);
2485
2502
  let needTransmux = this.config_.forceTransmux;