shaka-player 4.9.34 → 4.9.36

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 (79) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +1 -0
  3. package/README.md +9 -3
  4. package/dist/controls.css +1 -1
  5. package/dist/controls.css.map +1 -1
  6. package/dist/deps.js +6 -6
  7. package/dist/locales.js +31 -31
  8. package/dist/shaka-player.compiled.d.ts +1 -0
  9. package/dist/shaka-player.compiled.debug.d.ts +1 -0
  10. package/dist/shaka-player.compiled.debug.externs.js +4 -3
  11. package/dist/shaka-player.compiled.debug.js +445 -427
  12. package/dist/shaka-player.compiled.debug.map +3 -3
  13. package/dist/shaka-player.compiled.externs.js +4 -3
  14. package/dist/shaka-player.compiled.js +1254 -1240
  15. package/dist/shaka-player.compiled.map +3 -3
  16. package/dist/shaka-player.dash.d.ts +4099 -0
  17. package/dist/shaka-player.dash.debug.d.ts +4099 -0
  18. package/dist/shaka-player.dash.debug.externs.js +4188 -0
  19. package/dist/shaka-player.dash.debug.js +2161 -0
  20. package/dist/shaka-player.dash.debug.map +8 -0
  21. package/dist/shaka-player.dash.externs.js +4188 -0
  22. package/dist/shaka-player.dash.js +1007 -0
  23. package/dist/shaka-player.dash.map +8 -0
  24. package/dist/shaka-player.hls.d.ts +4196 -0
  25. package/dist/shaka-player.hls.debug.d.ts +4196 -0
  26. package/dist/shaka-player.hls.debug.externs.js +4403 -0
  27. package/dist/shaka-player.hls.debug.js +2243 -0
  28. package/dist/shaka-player.hls.debug.map +8 -0
  29. package/dist/shaka-player.hls.externs.js +4403 -0
  30. package/dist/shaka-player.hls.js +1076 -0
  31. package/dist/shaka-player.hls.map +8 -0
  32. package/dist/shaka-player.ui.d.ts +1 -0
  33. package/dist/shaka-player.ui.debug.d.ts +1 -0
  34. package/dist/shaka-player.ui.debug.externs.js +4 -3
  35. package/dist/shaka-player.ui.debug.js +472 -454
  36. package/dist/shaka-player.ui.debug.map +3 -3
  37. package/dist/shaka-player.ui.externs.js +4 -3
  38. package/dist/shaka-player.ui.js +1495 -1484
  39. package/dist/shaka-player.ui.map +3 -3
  40. package/externs/shaka/player.js +8 -4
  41. package/lib/cast/cast_proxy.js +48 -10
  42. package/lib/cea/cea708_service.js +20 -2
  43. package/lib/cea/dtvcc_packet_builder.js +15 -0
  44. package/lib/dash/dash_parser.js +24 -13
  45. package/lib/dash/mpd_utils.js +7 -4
  46. package/lib/dash/segment_template.js +74 -56
  47. package/lib/hls/hls_parser.js +5 -4
  48. package/lib/hls/manifest_text_parser.js +1 -0
  49. package/lib/media/content_workarounds.js +110 -72
  50. package/lib/media/drm_engine.js +72 -11
  51. package/lib/media/gap_jumping_controller.js +14 -1
  52. package/lib/media/media_source_engine.js +29 -11
  53. package/lib/media/region_timeline.js +4 -3
  54. package/lib/media/segment_utils.js +28 -16
  55. package/lib/media/streaming_engine.js +38 -12
  56. package/lib/net/http_fetch_plugin.js +1 -1
  57. package/lib/net/http_plugin_utils.js +4 -1
  58. package/lib/net/networking_engine.js +8 -0
  59. package/lib/net/networking_utils.js +1 -0
  60. package/lib/player.js +109 -37
  61. package/lib/polyfill/patchedmediakeys_apple.js +21 -0
  62. package/lib/text/cue.js +6 -2
  63. package/lib/text/mp4_ttml_parser.js +94 -6
  64. package/lib/text/ui_text_displayer.js +35 -4
  65. package/lib/transmuxer/ac3_transmuxer.js +6 -1
  66. package/lib/util/config_utils.js +13 -3
  67. package/lib/util/drm_utils.js +8 -0
  68. package/lib/util/error.js +6 -0
  69. package/lib/util/mp4_generator.js +19 -4
  70. package/lib/util/periods.js +4 -2
  71. package/lib/util/platform.js +60 -12
  72. package/lib/util/player_configuration.js +13 -2
  73. package/lib/util/stream_utils.js +18 -0
  74. package/lib/util/tXml.js +3 -2
  75. package/package.json +3 -3
  76. package/ui/controls.js +2 -6
  77. package/ui/less/thumbnails.less +1 -0
  78. package/ui/statistics_button.js +3 -1
  79. package/ui/ui.js +44 -35
@@ -1204,7 +1204,8 @@ shaka.extern.MssManifestConfiguration;
1204
1204
  * A default <code>presentationDelay</code> value.
1205
1205
  * For DASH, it's a default <code>presentationDelay</code> value if
1206
1206
  * <code>suggestedPresentationDelay</code> is missing in the MPEG DASH
1207
- * manifest. The default value is <code>1.5 * minBufferTime</code> if not
1207
+ * manifest. The default value is the lower of <code>1.5 *
1208
+ * minBufferTime</code> and <code>segmentAvailabilityDuration</code> if not
1208
1209
  * configured or set as 0.
1209
1210
  * For HLS, the default value is 3 segments duration if not configured or
1210
1211
  * set as 0.
@@ -1483,9 +1484,12 @@ shaka.extern.ManifestConfiguration;
1483
1484
  * <code>vodDynamicPlaybackRateLowBufferRate</code>.
1484
1485
  * Defaults to <code>0.5</code>.
1485
1486
  * @property {boolean} infiniteLiveStreamDuration
1486
- * If <code>true</code>, the media source live duration
1487
- * set as a<code>Infinity</code>
1488
- * Defaults to <code> false </code>.
1487
+ * If <code>true</code>, the media source duration of livestreams will be set
1488
+ * to <code>Infinity</code>. Otherwise, it will be set to a high but
1489
+ * non-infinite number (2^32).
1490
+ * <br>
1491
+ * Defaults to <code>false</code> except on Safari 17 or above whose default
1492
+ * value is <code>true</code>.
1489
1493
  * @property {number} preloadNextUrlWindow
1490
1494
  * The window of time at the end of the presentation to begin preloading the
1491
1495
  * next URL, such as one specified by a urn:mpeg:dash:chaining:2016 element
@@ -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
@@ -60,7 +60,7 @@ shaka.cea.Cea708Service = class {
60
60
  // Control codes are in 1 of 4 logical groups:
61
61
  // CL (C0, C2), CR (C1, C3), GL (G0, G2), GR (G1, G2).
62
62
  if (controlCode >= 0x00 && controlCode <= 0x1f) {
63
- return this.handleC0_(controlCode, pts);
63
+ return this.handleC0_(dtvccPacket, controlCode, pts);
64
64
  } else if (controlCode >= 0x80 && controlCode <= 0x9f) {
65
65
  return this.handleC1_(dtvccPacket, controlCode, pts);
66
66
  } else if (controlCode >= 0x1000 && controlCode <= 0x101f) {
@@ -153,17 +153,35 @@ shaka.cea.Cea708Service = class {
153
153
 
154
154
  /**
155
155
  * Handles C0 group data.
156
+ * @param {!shaka.cea.DtvccPacket} dtvccPacket
156
157
  * @param {number} controlCode
157
158
  * @param {number} pts
158
159
  * @return {?shaka.extern.ICaptionDecoder.ClosedCaption}
159
160
  * @private
160
161
  */
161
- handleC0_(controlCode, pts) {
162
+ handleC0_(dtvccPacket, controlCode, pts) {
162
163
  // All these commands pertain to the current window, so ensure it exists.
163
164
  if (!this.currentWindow_) {
164
165
  return null;
165
166
  }
166
167
 
168
+ if (controlCode == 0x18) {
169
+ const firstByte = dtvccPacket.readByte().value;
170
+ const secondByte = dtvccPacket.readByte().value;
171
+
172
+ const toHexString = (byteArray) => {
173
+ return byteArray.map((byte) => {
174
+ return ('0' + (byte & 0xFF).toString(16)).slice(-2);
175
+ }).join('');
176
+ };
177
+
178
+ const unicode = toHexString([firstByte, secondByte]);
179
+ // Takes a unicode hex string and creates a single character.
180
+ const char = String.fromCharCode(parseInt(unicode, 16));
181
+ this.currentWindow_.setCharacter(char);
182
+ return null;
183
+ }
184
+
167
185
  const window = this.currentWindow_;
168
186
  let parsedClosedCaption = null;
169
187
 
@@ -161,6 +161,21 @@ shaka.cea.DtvccPacket = class {
161
161
  }
162
162
  this.pos_ += numBlocks;
163
163
  }
164
+
165
+ /**
166
+ * Rewinds the provided number of blocks in the buffer.
167
+ * @param {number} numBlocks
168
+ * @throws {!shaka.util.Error}
169
+ */
170
+ rewind(numBlocks) {
171
+ if (this.pos_ - numBlocks < 0) {
172
+ throw new shaka.util.Error(
173
+ shaka.util.Error.Severity.CRITICAL,
174
+ shaka.util.Error.Category.TEXT,
175
+ shaka.util.Error.Code.BUFFER_READ_OUT_OF_BOUNDS);
176
+ }
177
+ this.pos_ -= numBlocks;
178
+ }
164
179
  };
165
180
 
166
181
  /**
@@ -645,10 +645,11 @@ shaka.dash.DashParser = class {
645
645
  // that the complex logic of multi-Period eviction and period-flattening
646
646
  // is correct. See also:
647
647
  // https://github.com/shaka-project/shaka-player/issues/3169#issuecomment-823580634
648
+ const availabilityStart =
649
+ presentationTimeline.getSegmentAvailabilityStart();
648
650
  for (const stream of Object.values(this.streamMap_)) {
649
651
  if (stream.segmentIndex) {
650
- stream.segmentIndex.evict(
651
- presentationTimeline.getSegmentAvailabilityStart());
652
+ stream.segmentIndex.evict(availabilityStart);
652
653
  }
653
654
  }
654
655
  } else {
@@ -657,18 +658,28 @@ shaka.dash.DashParser = class {
657
658
  // feasible choices for the value. Nothing older than
658
659
  // timeShiftBufferDepth is still available, and anything less than
659
660
  // minBufferTime will cause buffering issues.
660
- //
661
- // We have decided that our default will be the configured value, or
662
- // 1.5 * minBufferTime if not configured. This is fairly conservative.
663
- // Content providers should provide a suggestedPresentationDelay whenever
664
- // possible to optimize the live streaming experience.
665
- const defaultPresentationDelay =
666
- this.config_.defaultPresentationDelay || minBufferTime * 1.5;
667
- const presentationDelay = suggestedPresentationDelay != null ?
668
- suggestedPresentationDelay : defaultPresentationDelay;
661
+ let delay = 0;
662
+ if (suggestedPresentationDelay != null) {
663
+ // 1. If a suggestedPresentationDelay is provided by the manifest, that
664
+ // will be used preferentially.
665
+ // This is given a minimum bound of segmentAvailabilityDuration.
666
+ // Content providers should provide a suggestedPresentationDelay
667
+ // whenever possible to optimize the live streaming experience.
668
+ delay = Math.min(
669
+ suggestedPresentationDelay,
670
+ segmentAvailabilityDuration || Infinity);
671
+ } else if (this.config_.defaultPresentationDelay > 0) {
672
+ // 2. If the developer provides a value for
673
+ // "manifest.defaultPresentationDelay", that is used as a fallback.
674
+ delay = this.config_.defaultPresentationDelay;
675
+ } else {
676
+ // 3. Otherwise, we default to the lower of segmentAvailabilityDuration
677
+ // and 1.5 * minBufferTime. This is fairly conservative.
678
+ delay = Math.min(
679
+ minBufferTime * 1.5, segmentAvailabilityDuration || Infinity);
680
+ }
669
681
  presentationTimeline = new shaka.media.PresentationTimeline(
670
- presentationStartTime, presentationDelay,
671
- this.config_.dash.autoCorrectDrift);
682
+ presentationStartTime, delay, this.config_.dash.autoCorrectDrift);
672
683
  }
673
684
 
674
685
  presentationTimeline.setStatic(mpdType == 'static');
@@ -279,10 +279,9 @@ shaka.dash.MpdUtils = class {
279
279
 
280
280
  const durationStr =
281
281
  MpdUtils.inheritAttribute(context, callback, 'duration');
282
- let segmentDuration = TXml.parsePositiveInt(durationStr || '');
283
- if (segmentDuration) {
284
- segmentDuration /= timescale;
285
- }
282
+ const unscaledSegmentDuration = TXml.parsePositiveInt(durationStr || '');
283
+ const segmentDuration = unscaledSegmentDuration ?
284
+ unscaledSegmentDuration / timescale : null;
286
285
 
287
286
  const startNumberStr =
288
287
  MpdUtils.inheritAttribute(context, callback, 'startNumber');
@@ -309,6 +308,7 @@ shaka.dash.MpdUtils = class {
309
308
  (unscaledPresentationTimeOffset / timescale) || 0;
310
309
  return {
311
310
  timescale: timescale,
311
+ unscaledSegmentDuration: unscaledSegmentDuration,
312
312
  segmentDuration: segmentDuration,
313
313
  startNumber: startNumber,
314
314
  scaledPresentationTimeOffset: scaledPresentationTimeOffset,
@@ -564,6 +564,7 @@ shaka.dash.MpdUtils = class {
564
564
  /**
565
565
  * @typedef {{
566
566
  * timescale: number,
567
+ * unscaledSegmentDuration: ?number,
567
568
  * segmentDuration: ?number,
568
569
  * startNumber: number,
569
570
  * scaledPresentationTimeOffset: number,
@@ -576,6 +577,8 @@ shaka.dash.MpdUtils = class {
576
577
  *
577
578
  * @property {number} timescale
578
579
  * The time-scale of the representation.
580
+ * @property {?number} unscaledSegmentDuration
581
+ * The duration of the segments in timescale units, if given.
579
582
  * @property {?number} segmentDuration
580
583
  * The duration of the segments in seconds, if given.
581
584
  * @property {number} startNumber
@@ -121,24 +121,6 @@ shaka.dash.SegmentTemplate = class {
121
121
 
122
122
  shaka.log.debug(`New manifest ${periodStart} - ${periodEnd}`);
123
123
 
124
- /* When to fit segments. All refactors should honor/update this table:
125
- *
126
- * | dynamic | infinite | last | should | notes |
127
- * | | period | period | fit | |
128
- * | ------- | -------- | ------ | ------ | ------------------------- |
129
- * | F | F | X | T | typical VOD |
130
- * | F | T | X | X | impossible: infinite VOD |
131
- * | T | F | F | T | typical live, old period |
132
- * | T | F | T | F | typical IPR |
133
- * | T | T | F | X | impossible: old, infinite |
134
- * | T | T | T | F | typical live, new period |
135
- */
136
-
137
- // We never fit the final period of dynamic content, which could be
138
- // infinite live (with no limit to fit to) or IPR (which would expand the
139
- // most recent segment to the end of the presentation).
140
- const shouldFit = !(context.dynamic && context.periodInfo.isLastPeriod);
141
-
142
124
  if (!segmentIndex) {
143
125
  shaka.log.debug(`Creating TSI with end ${periodEnd}`);
144
126
  segmentIndex = new TimelineSegmentIndex(
@@ -149,14 +131,13 @@ shaka.dash.SegmentTemplate = class {
149
131
  periodStart,
150
132
  periodEnd,
151
133
  initSegmentReference,
152
- shouldFit,
153
134
  aesKey,
154
135
  context.representation.segmentSequenceCadence,
155
136
  );
156
137
  } else {
157
138
  const tsi = /** @type {!TimelineSegmentIndex} */(segmentIndex);
158
139
  tsi.appendTemplateInfo(
159
- info, periodStart, periodEnd, shouldFit, initSegmentReference);
140
+ info, periodStart, periodEnd, initSegmentReference);
160
141
 
161
142
  const availabilityStart =
162
143
  context.presentationTimeline.getSegmentAvailabilityStart();
@@ -166,7 +147,9 @@ shaka.dash.SegmentTemplate = class {
166
147
  if (info.timeline &&
167
148
  context.adaptationSet.contentType !== 'image' &&
168
149
  context.adaptationSet.contentType !== 'text') {
169
- const timeline = info.timeline;
150
+ const tsi = /** @type {!TimelineSegmentIndex} */(segmentIndex);
151
+ // getTimeline is the info.timeline but fitted to the period.
152
+ const timeline = tsi.getTimeline();
170
153
  context.presentationTimeline.notifyTimeRange(
171
154
  timeline,
172
155
  periodStart);
@@ -184,7 +167,7 @@ shaka.dash.SegmentTemplate = class {
184
167
  if (segmentIndex instanceof shaka.dash.TimelineSegmentIndex &&
185
168
  segmentIndex.isEmpty()) {
186
169
  segmentIndex.appendTemplateInfo(info, periodStart,
187
- periodEnd, shouldFit, initSegmentReference);
170
+ periodEnd, initSegmentReference);
188
171
  }
189
172
  return Promise.resolve(segmentIndex);
190
173
  },
@@ -258,6 +241,7 @@ shaka.dash.SegmentTemplate = class {
258
241
  context, SegmentTemplate.fromInheritance_, 'index');
259
242
 
260
243
  return {
244
+ unscaledSegmentDuration: segmentInfo.unscaledSegmentDuration,
261
245
  segmentDuration: segmentInfo.segmentDuration,
262
246
  timescale: segmentInfo.timescale,
263
247
  startNumber: segmentInfo.startNumber,
@@ -304,10 +288,12 @@ shaka.dash.SegmentTemplate = class {
304
288
  if (info.indexTemplate) {
305
289
  shaka.log.info('Using the index URL template by default.');
306
290
  info.timeline = null;
291
+ info.unscaledSegmentDuration = null;
307
292
  info.segmentDuration = null;
308
293
  } else {
309
294
  goog.asserts.assert(info.timeline, 'There should be a timeline');
310
295
  shaka.log.info('Using the SegmentTimeline by default.');
296
+ info.unscaledSegmentDuration = null;
311
297
  info.segmentDuration = null;
312
298
  }
313
299
  }
@@ -399,7 +385,6 @@ shaka.dash.SegmentTemplate = class {
399
385
  segmentDuration != null, 'Segment duration must not be null!');
400
386
 
401
387
  const startNumber = info.startNumber;
402
- const timescale = info.timescale;
403
388
 
404
389
  const template = info.mediaTemplate;
405
390
  const bandwidth = context.bandwidth || null;
@@ -482,20 +467,18 @@ shaka.dash.SegmentTemplate = class {
482
467
  const positionWithinPeriod = position - startNumber;
483
468
  const segmentPeriodTime = positionWithinPeriod * segmentDuration;
484
469
 
485
- // What will appear in the actual segment files. The media timestamp is
486
- // what is expected in the $Time$ template.
487
- const segmentMediaTime = segmentPeriodTime +
488
- info.scaledPresentationTimeOffset;
470
+ const unscaledSegmentDuration = info.unscaledSegmentDuration;
471
+ goog.asserts.assert(unscaledSegmentDuration != null,
472
+ 'Segment duration must not be null!');
489
473
 
490
- const getUris = () => {
491
- let time = segmentMediaTime * timescale;
492
- if ('BigInt' in window && time > Number.MAX_SAFE_INTEGER) {
493
- time = BigInt(segmentMediaTime) * BigInt(timescale);
494
- }
495
- const mediaUri = MpdUtils.fillUriTemplate(
496
- template, id, position, /* subNumber= */ null, bandwidth, time);
497
- return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
498
- };
474
+ // The original media timestamp from the timeline is what is expected in
475
+ // the $Time$ template. (Or based on duration, in this case.) It should
476
+ // not be adjusted with presentationTimeOffset or the Period start.
477
+ let timeReplacement = positionWithinPeriod * unscaledSegmentDuration;
478
+ if ('BigInt' in window && timeReplacement > Number.MAX_SAFE_INTEGER) {
479
+ timeReplacement =
480
+ BigInt(positionWithinPeriod) * BigInt(unscaledSegmentDuration);
481
+ }
499
482
 
500
483
  // Relative to the presentation.
501
484
  const segmentStart = segmentPeriodTime + periodStart;
@@ -509,6 +492,13 @@ shaka.dash.SegmentTemplate = class {
509
492
  goog.asserts.assert(segmentStart < segmentEnd,
510
493
  'Generated a segment outside of the period!');
511
494
 
495
+ const getUris = () => {
496
+ const mediaUri = MpdUtils.fillUriTemplate(
497
+ template, id, position, /* subNumber= */ null, bandwidth,
498
+ timeReplacement);
499
+ return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
500
+ };
501
+
512
502
  const ref = new shaka.media.SegmentReference(
513
503
  segmentStart,
514
504
  segmentEnd,
@@ -661,38 +651,42 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
661
651
  * @param {number} periodStart
662
652
  * @param {number} periodEnd
663
653
  * @param {shaka.media.InitSegmentReference} initSegmentReference
664
- * @param {boolean} shouldFit
665
654
  * @param {shaka.extern.aesKey|undefined} aesKey
666
655
  * @param {number} segmentSequenceCadence
667
656
  */
668
657
  constructor(templateInfo, representationId, bandwidth, getBaseUris,
669
- periodStart, periodEnd, initSegmentReference, shouldFit,
658
+ periodStart, periodEnd, initSegmentReference,
670
659
  aesKey, segmentSequenceCadence) {
671
660
  super([]);
672
661
 
673
662
  /** @private {?shaka.dash.SegmentTemplate.SegmentTemplateInfo} */
674
663
  this.templateInfo_ = templateInfo;
664
+
675
665
  /** @private {?string} */
676
666
  this.representationId_ = representationId;
667
+
677
668
  /** @private {number} */
678
669
  this.bandwidth_ = bandwidth;
679
- /** @private {function():Array.<string>} */
670
+
671
+ /** @private {function(): Array<string>} */
680
672
  this.getBaseUris_ = getBaseUris;
673
+
681
674
  /** @private {number} */
682
675
  this.periodStart_ = periodStart;
676
+
683
677
  /** @private {number} */
684
678
  this.periodEnd_ = periodEnd;
679
+
685
680
  /** @private {shaka.media.InitSegmentReference} */
686
681
  this.initSegmentReference_ = initSegmentReference;
682
+
687
683
  /** @private {shaka.extern.aesKey|undefined} */
688
684
  this.aesKey_ = aesKey;
685
+
689
686
  /** @private {number} */
690
687
  this.segmentSequenceCadence_ = segmentSequenceCadence;
691
688
 
692
-
693
- if (shouldFit) {
694
- this.fitTimeline();
695
- }
689
+ this.fitTimeline();
696
690
  }
697
691
 
698
692
  /**
@@ -760,11 +754,9 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
760
754
  * @param {shaka.dash.SegmentTemplate.SegmentTemplateInfo} info
761
755
  * @param {number} periodStart
762
756
  * @param {number} periodEnd
763
- * @param {boolean} shouldFit
764
757
  * @param {shaka.media.InitSegmentReference} initSegmentReference
765
758
  */
766
- appendTemplateInfo(info, periodStart, periodEnd, shouldFit,
767
- initSegmentReference) {
759
+ appendTemplateInfo(info, periodStart, periodEnd, initSegmentReference) {
768
760
  this.updateInitSegmentReference(initSegmentReference);
769
761
  if (!this.templateInfo_) {
770
762
  this.templateInfo_ = info;
@@ -798,9 +790,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
798
790
  }
799
791
  }
800
792
 
801
- if (shouldFit) {
802
- this.fitTimeline();
803
- }
793
+ this.fitTimeline();
804
794
  }
805
795
 
806
796
  /**
@@ -845,14 +835,19 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
845
835
  return;
846
836
  }
847
837
  const timeline = this.templateInfo_.timeline;
848
- while (timeline.length) {
849
- const lastTimePeriod = timeline[timeline.length - 1];
850
- if (lastTimePeriod.start >= this.periodEnd_) {
851
- timeline.pop();
838
+ goog.asserts.assert(timeline, 'Timeline should be non-null!');
839
+ const newTimeline = [];
840
+ for (const range of timeline) {
841
+ if (range.start >= this.periodEnd_) {
842
+ // Starts after end of period.
843
+ } else if (range.end <= 0) {
844
+ // Ends before start of period.
852
845
  } else {
853
- break;
846
+ // Usable.
847
+ newTimeline.push(range);
854
848
  }
855
849
  }
850
+ this.templateInfo_.timeline = newTimeline;
856
851
 
857
852
  this.evict(this.periodStart_);
858
853
 
@@ -862,6 +857,19 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
862
857
  // find/get() methods whenever possible.
863
858
  }
864
859
 
860
+ /**
861
+ * Get the current timeline
862
+ * @return {!Array<shaka.media.PresentationTimeline.TimeRange>}
863
+ */
864
+ getTimeline() {
865
+ if (!this.templateInfo_) {
866
+ return [];
867
+ }
868
+ const timeline = this.templateInfo_.timeline;
869
+ goog.asserts.assert(timeline, 'Timeline should be non-null!');
870
+ return timeline;
871
+ }
872
+
865
873
  /**
866
874
  * @override
867
875
  */
@@ -925,8 +933,15 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
925
933
  if (!ref) {
926
934
  const range = this.templateInfo_.timeline[correctedPosition];
927
935
  const segmentReplacement = range.segmentPosition;
928
- const timeReplacement = this.templateInfo_
929
- .unscaledPresentationTimeOffset + range.unscaledStart;
936
+
937
+ // The original media timestamp from the timeline is what is expected in
938
+ // the $Time$ template. It should not be adjusted with
939
+ // presentationTimeOffset or the Period start, but
940
+ // unscaledPresentationTimeOffset was already subtracted from the times
941
+ // in timeline.
942
+ const timeReplacement = range.unscaledStart +
943
+ this.templateInfo_.unscaledPresentationTimeOffset;
944
+
930
945
  const timestampOffset = this.periodStart_ -
931
946
  this.templateInfo_.scaledPresentationTimeOffset;
932
947
  const trueSegmentEnd = this.periodStart_ + range.end;
@@ -1068,6 +1083,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
1068
1083
  /**
1069
1084
  * @typedef {{
1070
1085
  * timescale: number,
1086
+ * unscaledSegmentDuration: ?number,
1071
1087
  * segmentDuration: ?number,
1072
1088
  * startNumber: number,
1073
1089
  * scaledPresentationTimeOffset: number,
@@ -1084,6 +1100,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
1084
1100
  *
1085
1101
  * @property {number} timescale
1086
1102
  * The time-scale of the representation.
1103
+ * @property {?number} unscaledSegmentDuration
1104
+ * The duration of the segments in seconds, in timescale units.
1087
1105
  * @property {?number} segmentDuration
1088
1106
  * The duration of the segments in seconds, if given.
1089
1107
  * @property {number} startNumber
@@ -1013,7 +1013,8 @@ shaka.hls.HlsParser = class {
1013
1013
 
1014
1014
  if (extension == 'ts' || contentMimeType == 'video/mp2t') {
1015
1015
  const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromTs(
1016
- response.data, this.config_.disableAudio, this.config_.disableVideo);
1016
+ response.data, this.config_.disableAudio, this.config_.disableVideo,
1017
+ this.config_.disableText);
1017
1018
  if (basicInfo) {
1018
1019
  return basicInfo;
1019
1020
  }
@@ -1023,7 +1024,7 @@ shaka.hls.HlsParser = class {
1023
1024
  contentMimeType == 'audio/mp4' ||
1024
1025
  contentMimeType == 'video/iso.segment') {
1025
1026
  const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromMp4(
1026
- initData, response.data);
1027
+ initData, response.data, this.config_.disableText);
1027
1028
  if (basicInfo) {
1028
1029
  return basicInfo;
1029
1030
  }
@@ -4311,7 +4312,7 @@ shaka.hls.HlsParser = class {
4311
4312
  shaka.util.Error.Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
4312
4313
  }
4313
4314
 
4314
- if (shaka.util.Platform.isMediaKeysPolyfilled()) {
4315
+ if (shaka.util.Platform.isMediaKeysPolyfilled('apple')) {
4315
4316
  throw new shaka.util.Error(
4316
4317
  shaka.util.Error.Severity.CRITICAL,
4317
4318
  shaka.util.Error.Category.MANIFEST,
@@ -4451,7 +4452,7 @@ shaka.hls.HlsParser = class {
4451
4452
  shaka.util.Error.Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
4452
4453
  }
4453
4454
 
4454
- if (shaka.util.Platform.isMediaKeysPolyfilled()) {
4455
+ if (shaka.util.Platform.isMediaKeysPolyfilled('apple')) {
4455
4456
  throw new shaka.util.Error(
4456
4457
  shaka.util.Error.Severity.CRITICAL,
4457
4458
  shaka.util.Error.Category.MANIFEST,
@@ -313,4 +313,5 @@ shaka.hls.ManifestTextParser.SEGMENT_TAGS = [
313
313
  'EXT-X-DATERANGE',
314
314
  'EXT-X-MAP',
315
315
  'EXT-X-GAP',
316
+ 'EXT-X-TILES',
316
317
  ];