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
@@ -92,7 +92,10 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
92
92
  /** @private {boolean} */
93
93
  this.parserEntrusted_ = false;
94
94
 
95
- /** @private {!shaka.media.RegionTimeline} */
95
+ /**
96
+ * @private {!shaka.media.RegionTimeline<
97
+ * shaka.extern.TimelineRegionInfo>}
98
+ */
96
99
  this.regionTimeline_ = typedPlayerInterface.regionTimeline;
97
100
 
98
101
  /** @private {boolean} */
@@ -288,7 +291,7 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
288
291
  /**
289
292
  * Gets the region timeline, if it exists. Also marks that the timeline should
290
293
  * not be released if this manager is destroyed.
291
- * @return {?shaka.media.RegionTimeline}
294
+ * @return {?shaka.media.RegionTimeline<shaka.extern.TimelineRegionInfo>}
292
295
  */
293
296
  receiveRegionTimeline() {
294
297
  this.regionTimelineEntrusted_ = true;
@@ -296,7 +299,7 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
296
299
  }
297
300
 
298
301
  /**
299
- * @return {?shaka.media.RegionTimeline}
302
+ * @return {?shaka.media.RegionTimeline<shaka.extern.TimelineRegionInfo>}
300
303
  */
301
304
  getRegionTimeline() {
302
305
  return this.regionTimeline_;
@@ -837,7 +840,8 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
837
840
  * @typedef {{
838
841
  * config: !shaka.extern.PlayerConfiguration,
839
842
  * manifestPlayerInterface: !shaka.extern.ManifestParser.PlayerInterface,
840
- * regionTimeline: !shaka.media.RegionTimeline,
843
+ * regionTimeline: !shaka.media.RegionTimeline<
844
+ * shaka.extern.TimelineRegionInfo>,
841
845
  * qualityObserver: ?shaka.media.QualityObserver,
842
846
  * createDrmEngine: function():!shaka.drm.DrmEngine,
843
847
  * networkingEngine: !shaka.net.NetworkingEngine,
@@ -849,7 +853,8 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
849
853
  * @property {!shaka.extern.PlayerConfiguration} config
850
854
  * @property {!shaka.extern.ManifestParser.PlayerInterface
851
855
  * } manifestPlayerInterface
852
- * @property {!shaka.media.RegionTimeline} regionTimeline
856
+ * @property {!shaka.media.RegionTimeline<shaka.extern.TimelineRegionInfo>
857
+ * } regionTimeline
853
858
  * @property {?shaka.media.QualityObserver} qualityObserver
854
859
  * @property {function():!shaka.drm.DrmEngine} createDrmEngine
855
860
  * @property {!shaka.net.NetworkingEngine} networkingEngine
@@ -18,6 +18,7 @@ goog.require('shaka.util.FakeEventTarget');
18
18
  * ('enter', 'exit', 'skip') as the playhead moves.
19
19
  *
20
20
  * @implements {shaka.media.IPlayheadObserver}
21
+ * @template T
21
22
  * @final
22
23
  */
23
24
  shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
@@ -26,13 +27,13 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
26
27
  * own the timeline, only uses it. This means that the observer should NOT
27
28
  * destroy the timeline.
28
29
  *
29
- * @param {!shaka.media.RegionTimeline} timeline
30
+ * @param {!shaka.media.RegionTimeline<T>} timeline
30
31
  * @param {boolean} startsPastZero
31
32
  */
32
33
  constructor(timeline, startsPastZero) {
33
34
  super();
34
35
 
35
- /** @private {shaka.media.RegionTimeline} */
36
+ /** @private {shaka.media.RegionTimeline<T>} */
36
37
  this.timeline_ = timeline;
37
38
 
38
39
  /**
@@ -50,8 +51,7 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
50
51
  * When the value here differs from what we calculate, it means we moved and
51
52
  * should fire an event.
52
53
  *
53
- * @private {!Map<shaka.extern.TimelineRegionInfo,
54
- * shaka.media.RegionObserver.RelativePosition_>}
54
+ * @private {!Map<T, shaka.media.RegionObserver.RelativePosition_>}
55
55
  */
56
56
  this.oldPosition_ = new Map();
57
57
 
@@ -109,7 +109,7 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
109
109
  this.eventManager_ = new shaka.util.EventManager();
110
110
 
111
111
  this.eventManager_.listen(this.timeline_, 'regionremove', (event) => {
112
- /** @type {shaka.extern.TimelineRegionInfo} */
112
+ /** @type {T} */
113
113
  const region = event['region'];
114
114
  this.oldPosition_.delete(region);
115
115
  });
@@ -162,7 +162,7 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
162
162
  * same parameters: region and seeking.
163
163
  *
164
164
  * @param {string} eventType
165
- * @param {shaka.extern.TimelineRegionInfo} region
165
+ * @param {T} region
166
166
  * @param {boolean} seeking
167
167
  * @private
168
168
  */
@@ -179,7 +179,8 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
179
179
  * at |seconds|. We treat the region's start and end times as inclusive
180
180
  * bounds.
181
181
  *
182
- * @param {shaka.extern.TimelineRegionInfo} region
182
+ * @template T
183
+ * @param {T} region
183
184
  * @param {number} seconds
184
185
  * @return {shaka.media.RegionObserver.RelativePosition_}
185
186
  * @private
@@ -218,7 +219,7 @@ shaka.media.RegionObserver.RelativePosition_ = {
218
219
  * region that the playhead is interacting with and whether or not the playhead
219
220
  * moving is part of a seek event.
220
221
  *
221
- * @typedef {function(shaka.extern.TimelineRegionInfo, boolean)}
222
+ * @typedef {function(?, boolean)}
222
223
  */
223
224
  shaka.media.RegionObserver.EventListener;
224
225
 
@@ -18,6 +18,7 @@ goog.require('shaka.util.Timer');
18
18
  * deleted, the 'regionremove' event will be fired.
19
19
  *
20
20
  * @implements {shaka.util.IReleasable}
21
+ * @template T
21
22
  * @final
22
23
  */
23
24
  shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
@@ -27,7 +28,7 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
27
28
  constructor(getSeekRange) {
28
29
  super();
29
30
 
30
- /** @private {!Set<shaka.extern.TimelineRegionInfo>} */
31
+ /** @private {!Set<T>} */
31
32
  this.regions_ = new Set();
32
33
 
33
34
  /** @private {!function():{start: number, end: number}} */
@@ -54,7 +55,7 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
54
55
  }
55
56
 
56
57
  /**
57
- * @param {shaka.extern.TimelineRegionInfo} region
58
+ * @param {T} region
58
59
  */
59
60
  addRegion(region) {
60
61
  const similarRegion = this.findSimilarRegion_(region);
@@ -97,18 +98,19 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
97
98
  * to be the same region. If no similar region can be found, |null| will be
98
99
  * returned.
99
100
  *
100
- * @param {shaka.extern.TimelineRegionInfo} region
101
- * @return {?shaka.extern.TimelineRegionInfo}
101
+ * @param {T} region
102
+ * @return {?T}
102
103
  * @private
103
104
  */
104
105
  findSimilarRegion_(region) {
106
+ const isDiffNegligible = (a, b) => Math.abs(a - b) < 0.1;
105
107
  for (const existing of this.regions_) {
106
108
  // The same scheme ID and time range means that it is similar-enough to
107
109
  // be the same region.
108
110
  const isSimilar = existing.schemeIdUri == region.schemeIdUri &&
109
- existing.id == region.id &&
110
- existing.startTime == region.startTime &&
111
- existing.endTime == region.endTime;
111
+ existing.id == region.id &&
112
+ isDiffNegligible(existing.startTime, region.startTime) &&
113
+ isDiffNegligible(existing.endTime, region.endTime);
112
114
 
113
115
  if (isSimilar) {
114
116
  return existing;
@@ -123,7 +125,7 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
123
125
  * others to see what regions are in the timeline while not being able to
124
126
  * change the collection.
125
127
  *
126
- * @return {!Iterable<shaka.extern.TimelineRegionInfo>}
128
+ * @return {!Iterable<T>}
127
129
  */
128
130
  regions() {
129
131
  return this.regions_;
@@ -1368,12 +1368,17 @@ shaka.media.StreamingEngine = class {
1368
1368
  return null;
1369
1369
  }
1370
1370
 
1371
+ // Update updateIntervalSeconds according to our current playbackrate,
1372
+ // and always considering a minimum of 1.
1373
+ const updateIntervalSeconds = this.config_.updateIntervalSeconds /
1374
+ Math.max(1, Math.abs(this.playerInterface_.getPlaybackRate()));
1375
+
1371
1376
  if (!this.playerInterface_.mediaSourceEngine.isStreamingAllowed() &&
1372
1377
  mediaState.type != ContentType.TEXT) {
1373
1378
  // It is not allowed to add segments yet, so we schedule an update to
1374
1379
  // check again later. So any prediction we make now could be terribly
1375
1380
  // invalid soon.
1376
- return this.config_.updateIntervalSeconds / 2;
1381
+ return updateIntervalSeconds / 2;
1377
1382
  }
1378
1383
 
1379
1384
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
@@ -1445,9 +1450,8 @@ shaka.media.StreamingEngine = class {
1445
1450
  shaka.log.v2(logPrefix, 'buffering goal met');
1446
1451
 
1447
1452
  // Do not try to predict the next update. Just poll according to
1448
- // configuration (seconds). The playback rate can change at any time, so
1449
- // any prediction we make now could be terribly invalid soon.
1450
- return this.config_.updateIntervalSeconds / 2;
1453
+ // configuration (seconds).
1454
+ return updateIntervalSeconds / 2;
1451
1455
  }
1452
1456
 
1453
1457
  // Lack of segment iterator is the best indicator stream has changed.
@@ -1459,7 +1463,7 @@ shaka.media.StreamingEngine = class {
1459
1463
  // In any case just try again... if the manifest is incomplete or is not
1460
1464
  // being updated then we'll idle forever; otherwise, we'll end up getting
1461
1465
  // a SegmentReference eventually.
1462
- return this.config_.updateIntervalSeconds;
1466
+ return updateIntervalSeconds;
1463
1467
  }
1464
1468
  // Get media state adaptation and reset this value. By guarding it during
1465
1469
  // actual stream change we ensure it won't be cleaned by accident on regular
@@ -1498,7 +1502,7 @@ shaka.media.StreamingEngine = class {
1498
1502
  // For example, let video buffering catch up to audio buffering before
1499
1503
  // fetching another audio segment.
1500
1504
  shaka.log.v2(logPrefix, 'waiting for other streams to buffer');
1501
- return this.config_.updateIntervalSeconds;
1505
+ return updateIntervalSeconds;
1502
1506
  }
1503
1507
 
1504
1508
  if (mediaState.segmentPrefetch && mediaState.segmentIterator &&
@@ -1821,7 +1825,19 @@ shaka.media.StreamingEngine = class {
1821
1825
  JSON.stringify(buffered));
1822
1826
 
1823
1827
  if (!mediaState.waitingToClearBuffer) {
1824
- this.playerInterface_.onSegmentAppended(reference, mediaState.stream);
1828
+ let otherState = null;
1829
+ if (mediaState.type === ContentType.VIDEO) {
1830
+ otherState = this.mediaStates_.get(ContentType.AUDIO);
1831
+ } else if (mediaState.type === ContentType.AUDIO) {
1832
+ otherState = this.mediaStates_.get(ContentType.VIDEO);
1833
+ }
1834
+ if (otherState && otherState.type == ContentType.AUDIO) {
1835
+ this.playerInterface_.onSegmentAppended(reference, mediaState.stream,
1836
+ otherState.stream.isAudioMuxedInVideo);
1837
+ } else {
1838
+ this.playerInterface_.onSegmentAppended(reference, mediaState.stream,
1839
+ mediaState.stream.codecs.includes(','));
1840
+ }
1825
1841
  }
1826
1842
 
1827
1843
  // Update right away.
@@ -2885,7 +2901,7 @@ shaka.media.StreamingEngine = class {
2885
2901
  * onError: function(!shaka.util.Error),
2886
2902
  * onEvent: function(!Event),
2887
2903
  * onSegmentAppended: function(!shaka.media.SegmentReference,
2888
- * !shaka.extern.Stream),
2904
+ * !shaka.extern.Stream, boolean),
2889
2905
  * onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference),
2890
2906
  * beforeAppendSegment: function(
2891
2907
  * shaka.util.ManifestParserUtils.ContentType,!BufferSource):Promise,
@@ -2910,7 +2926,7 @@ shaka.media.StreamingEngine = class {
2910
2926
  * @property {function(!Event)} onEvent
2911
2927
  * Called when an event occurs that should be sent to the app.
2912
2928
  * @property {function(!shaka.media.SegmentReference,
2913
- * !shaka.extern.Stream)} onSegmentAppended
2929
+ * !shaka.extern.Stream, boolean)} onSegmentAppended
2914
2930
  * Called after a segment is successfully appended to a MediaSource.
2915
2931
  * @property {function(!number,
2916
2932
  * !shaka.media.InitSegmentReference)} onInitSegmentAppended
@@ -610,7 +610,7 @@ shaka.mss.MssParser = class {
610
610
  // This is specifically for text tracks.
611
611
  const subType = streamIndex.attributes['Subtype'];
612
612
  if (subType) {
613
- const role = MssParser.ROLE_MAPPING_[subType];
613
+ const role = MssParser.ROLE_MAPPING_.get(subType);
614
614
  if (role) {
615
615
  stream.roles.push(role);
616
616
  }
@@ -1107,14 +1107,13 @@ shaka.mss.MssParser.SUPPORTED_CODECS_ = [
1107
1107
  * MPEG-DASH Role and accessibility mapping for text tracks according to
1108
1108
  * ETSI TS 103 285 v1.1.1 (section 7.1.2)
1109
1109
  *
1110
- * @const {!Object<string, string>}
1110
+ * @const {!Map<string, string>}
1111
1111
  * @private
1112
1112
  */
1113
- shaka.mss.MssParser.ROLE_MAPPING_ = {
1114
- 'CAPT': 'main',
1115
- 'SUBT': 'alternate',
1116
- 'DESC': 'main',
1117
- };
1113
+ shaka.mss.MssParser.ROLE_MAPPING_ = new Map()
1114
+ .set('CAPT', 'main')
1115
+ .set('SUBT', 'alternate')
1116
+ .set('DESC', 'main');
1118
1117
 
1119
1118
 
1120
1119
  /**
@@ -32,7 +32,7 @@ shaka.net.HttpPluginUtils = class {
32
32
  requestType) {
33
33
  goog.asserts.assert(data, 'Data should be non-null!');
34
34
 
35
- if (status >= 200 && status <= 299 && status != 202) {
35
+ if ((status >= 200 && status <= 299 && status != 202) || status == 304) {
36
36
  // Most 2xx HTTP codes are success cases.
37
37
  /** @type {shaka.extern.Response} */
38
38
  const response = {
@@ -156,13 +156,13 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
156
156
  priority == undefined || priority > 0, 'explicit priority must be > 0');
157
157
  priority =
158
158
  priority || shaka.net.NetworkingEngine.PluginPriority.APPLICATION;
159
- const existing = shaka.net.NetworkingEngine.schemes_[scheme];
159
+ const existing = shaka.net.NetworkingEngine.schemes_.get(scheme);
160
160
  if (!existing || priority >= existing.priority) {
161
- shaka.net.NetworkingEngine.schemes_[scheme] = {
161
+ shaka.net.NetworkingEngine.schemes_.set(scheme, {
162
162
  priority: priority,
163
163
  plugin: plugin,
164
164
  progressSupport: progressSupport,
165
- };
165
+ });
166
166
  }
167
167
  }
168
168
 
@@ -173,7 +173,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
173
173
  * @export
174
174
  */
175
175
  static unregisterScheme(scheme) {
176
- delete shaka.net.NetworkingEngine.schemes_[scheme];
176
+ shaka.net.NetworkingEngine.schemes_.delete(scheme);
177
177
  }
178
178
 
179
179
  /**
@@ -539,7 +539,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
539
539
  // and https://tools.ietf.org/html/rfc3986#section-3.1
540
540
  scheme = scheme.toLowerCase();
541
541
 
542
- const object = shaka.net.NetworkingEngine.schemes_[scheme];
542
+ const object = shaka.net.NetworkingEngine.schemes_.get(scheme);
543
543
  const plugin = object ? object.plugin : null;
544
544
  if (!plugin) {
545
545
  return shaka.util.AbortableOperation.failed(
@@ -972,9 +972,9 @@ shaka.net.NetworkingEngine.SchemeObject;
972
972
  /**
973
973
  * Contains the scheme plugins.
974
974
  *
975
- * @private {!Object<string, shaka.net.NetworkingEngine.SchemeObject>}
975
+ * @private {!Map<string, shaka.net.NetworkingEngine.SchemeObject>}
976
976
  */
977
- shaka.net.NetworkingEngine.schemes_ = {};
977
+ shaka.net.NetworkingEngine.schemes_ = new Map();
978
978
 
979
979
  /**
980
980
  * @typedef {{
@@ -24,7 +24,7 @@ shaka.net.NetworkingUtils = class {
24
24
  static async getMimeType(uri, netEngine, retryParams) {
25
25
  const extension = shaka.net.NetworkingUtils.getExtension_(uri);
26
26
  let mimeType =
27
- shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_[extension];
27
+ shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_.get(extension);
28
28
  if (mimeType) {
29
29
  return mimeType;
30
30
  }
@@ -73,43 +73,42 @@ shaka.net.NetworkingUtils = class {
73
73
  };
74
74
 
75
75
  /**
76
- * @const {!Object<string, string>}
76
+ * @const {!Map<string, string>}
77
77
  * @private
78
78
  */
79
- shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_ = {
80
- 'mp4': 'video/mp4',
81
- 'm4v': 'video/mp4',
82
- 'm4a': 'audio/mp4',
83
- 'webm': 'video/webm',
84
- 'weba': 'audio/webm',
85
- 'mkv': 'video/webm', // Chromium browsers supports it.
86
- 'ts': 'video/mp2t',
87
- 'ogv': 'video/ogg',
88
- 'ogg': 'audio/ogg',
89
- 'mpg': 'video/mpeg',
90
- 'mpeg': 'video/mpeg',
91
- 'mov': 'video/quicktime',
92
- 'm3u8': 'application/x-mpegurl',
93
- 'mpd': 'application/dash+xml',
94
- 'ism': 'application/vnd.ms-sstr+xml',
95
- 'mp3': 'audio/mpeg',
96
- 'aac': 'audio/aac',
97
- 'flac': 'audio/flac',
98
- 'wav': 'audio/wav',
99
- 'sbv': 'text/x-subviewer',
100
- 'srt': 'text/srt',
101
- 'vtt': 'text/vtt',
102
- 'webvtt': 'text/vtt',
103
- 'ttml': 'application/ttml+xml',
104
- 'lrc': 'application/x-subtitle-lrc',
105
- 'ssa': 'text/x-ssa',
106
- 'ass': 'text/x-ssa',
107
- 'jpeg': 'image/jpeg',
108
- 'jpg': 'image/jpeg',
109
- 'png': 'image/png',
110
- 'svg': 'image/svg+xml',
111
- 'webp': 'image/webp',
112
- 'avif': 'image/avif',
113
- 'html': 'text/html',
114
- 'htm': 'text/html',
115
- };
79
+ shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_ = new Map()
80
+ .set('mp4', 'video/mp4')
81
+ .set('m4v', 'video/mp4')
82
+ .set('m4a', 'audio/mp4')
83
+ .set('webm', 'video/webm')
84
+ .set('weba', 'audio/webm')
85
+ .set('mkv', 'video/webm') // Chromium browsers supports it.
86
+ .set('ts', 'video/mp2t')
87
+ .set('ogv', 'video/ogg')
88
+ .set('ogg', 'audio/ogg')
89
+ .set('mpg', 'video/mpeg')
90
+ .set('mpeg', 'video/mpeg')
91
+ .set('mov', 'video/quicktime')
92
+ .set('m3u8', 'application/x-mpegurl')
93
+ .set('mpd', 'application/dash+xml')
94
+ .set('ism', 'application/vnd.ms-sstr+xml')
95
+ .set('mp3', 'audio/mpeg')
96
+ .set('aac', 'audio/aac')
97
+ .set('flac', 'audio/flac')
98
+ .set('wav', 'audio/wav')
99
+ .set('sbv', 'text/x-subviewer')
100
+ .set('srt', 'text/srt')
101
+ .set('vtt', 'text/vtt')
102
+ .set('webvtt', 'text/vtt')
103
+ .set('ttml', 'application/ttml+xml')
104
+ .set('lrc', 'application/x-subtitle-lrc')
105
+ .set('ssa', 'text/x-ssa')
106
+ .set('ass', 'text/x-ssa')
107
+ .set('jpeg', 'image/jpeg')
108
+ .set('jpg', 'image/jpeg')
109
+ .set('png', 'image/png')
110
+ .set('svg', 'image/svg+xml')
111
+ .set('webp', 'image/webp')
112
+ .set('avif', 'image/avif')
113
+ .set('html', 'text/html')
114
+ .set('htm', 'text/html');
@@ -19,8 +19,8 @@ goog.requireType('shaka.media.SegmentReference');
19
19
  shaka.offline.StreamBandwidthEstimator = class {
20
20
  /** */
21
21
  constructor() {
22
- /** @private {!Object<number, number>} */
23
- this.estimateByStreamId_ = {};
22
+ /** @private {!Map<number, number>} */
23
+ this.estimateByStreamId_ = new Map();
24
24
  }
25
25
 
26
26
  /**
@@ -80,7 +80,7 @@ shaka.offline.StreamBandwidthEstimator = class {
80
80
  * @private
81
81
  */
82
82
  setBitrate_(stream, bitRate) {
83
- this.estimateByStreamId_[stream] = bitRate;
83
+ this.estimateByStreamId_.set(stream, bitRate);
84
84
  }
85
85
 
86
86
  /**
@@ -89,8 +89,8 @@ shaka.offline.StreamBandwidthEstimator = class {
89
89
  * @param {shaka.extern.Stream} text
90
90
  */
91
91
  addText(text) {
92
- this.estimateByStreamId_[text.id] =
93
- shaka.offline.StreamBandwidthEstimator.DEFAULT_TEXT_BITRATE_;
92
+ this.estimateByStreamId_.set(text.id,
93
+ shaka.offline.StreamBandwidthEstimator.DEFAULT_TEXT_BITRATE_);
94
94
  }
95
95
 
96
96
  /**
@@ -99,8 +99,8 @@ shaka.offline.StreamBandwidthEstimator = class {
99
99
  * @param {shaka.extern.Stream} image
100
100
  */
101
101
  addImage(image) {
102
- this.estimateByStreamId_[image.id] = image.bandwidth ||
103
- shaka.offline.StreamBandwidthEstimator.DEFAULT_IMAGE_BITRATE_;
102
+ this.estimateByStreamId_.set(image.id, image.bandwidth ||
103
+ shaka.offline.StreamBandwidthEstimator.DEFAULT_IMAGE_BITRATE_);
104
104
  }
105
105
 
106
106
  /**
@@ -136,7 +136,7 @@ shaka.offline.StreamBandwidthEstimator = class {
136
136
  * @private
137
137
  */
138
138
  getEstimate_(id) {
139
- let bitRate = this.estimateByStreamId_[id];
139
+ let bitRate = this.estimateByStreamId_.get(id);
140
140
 
141
141
  if (bitRate == null) {
142
142
  bitRate = 0;