shaka-player 4.9.32 → 4.9.34

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 (56) hide show
  1. package/dist/deps.js +6 -5
  2. package/dist/locales.js +36 -36
  3. package/dist/shaka-player.compiled.d.ts +11 -3
  4. package/dist/shaka-player.compiled.debug.d.ts +11 -3
  5. package/dist/shaka-player.compiled.debug.externs.js +8 -5
  6. package/dist/shaka-player.compiled.debug.js +453 -440
  7. package/dist/shaka-player.compiled.debug.map +4 -4
  8. package/dist/shaka-player.compiled.externs.js +8 -5
  9. package/dist/shaka-player.compiled.js +1281 -1271
  10. package/dist/shaka-player.compiled.map +4 -4
  11. package/dist/shaka-player.ui.d.ts +11 -3
  12. package/dist/shaka-player.ui.debug.d.ts +11 -3
  13. package/dist/shaka-player.ui.debug.externs.js +8 -5
  14. package/dist/shaka-player.ui.debug.js +483 -470
  15. package/dist/shaka-player.ui.debug.map +4 -4
  16. package/dist/shaka-player.ui.externs.js +8 -5
  17. package/dist/shaka-player.ui.js +1478 -1468
  18. package/dist/shaka-player.ui.map +4 -4
  19. package/externs/managedmediasource.js +6 -0
  20. package/externs/shaka/codecs.js +21 -0
  21. package/externs/shaka/offline.js +4 -0
  22. package/lib/cast/cast_utils.js +1 -0
  23. package/lib/hls/hls_parser.js +26 -5
  24. package/lib/media/drm_engine.js +78 -18
  25. package/lib/media/media_source_capabilities.js +3 -2
  26. package/lib/media/media_source_engine.js +117 -36
  27. package/lib/media/segment_utils.js +57 -47
  28. package/lib/offline/download_info.js +5 -0
  29. package/lib/offline/download_manager.js +54 -0
  30. package/lib/offline/manifest_converter.js +1 -0
  31. package/lib/offline/storage.js +11 -2
  32. package/lib/player.js +89 -53
  33. package/lib/polyfill/media_capabilities.js +1 -1
  34. package/lib/text/simple_text_displayer.js +9 -1
  35. package/lib/text/ui_text_displayer.js +2 -14
  36. package/lib/text/vtt_text_parser.js +9 -4
  37. package/lib/transmuxer/aac_transmuxer.js +11 -3
  38. package/lib/transmuxer/ac3_transmuxer.js +11 -3
  39. package/lib/transmuxer/ec3_transmuxer.js +11 -3
  40. package/lib/transmuxer/h264.js +143 -70
  41. package/lib/transmuxer/mp3_transmuxer.js +11 -3
  42. package/lib/transmuxer/ts_transmuxer.js +68 -28
  43. package/lib/util/abortable_operation.js +3 -0
  44. package/lib/util/drm_utils.js +20 -0
  45. package/lib/util/functional.js +27 -0
  46. package/lib/util/mime_utils.js +2 -0
  47. package/lib/util/number_utils.js +37 -0
  48. package/lib/util/object_utils.js +83 -0
  49. package/lib/util/platform.js +48 -40
  50. package/lib/util/stream_utils.js +27 -106
  51. package/lib/util/ts_parser.js +111 -85
  52. package/package.json +2 -2
  53. package/ui/controls.js +4 -0
  54. package/ui/fast_forward_button.js +7 -0
  55. package/ui/play_button.js +1 -1
  56. package/ui/rewind_button.js +7 -0
@@ -7,6 +7,7 @@
7
7
  goog.provide('shaka.offline.DownloadInfo');
8
8
 
9
9
  goog.require('shaka.util.Networking');
10
+ goog.require('shaka.util.Uint8ArrayUtils');
10
11
  goog.requireType('shaka.media.InitSegmentReference');
11
12
  goog.requireType('shaka.media.SegmentReference');
12
13
 
@@ -43,6 +44,10 @@ shaka.offline.DownloadInfo = class {
43
44
  * @return {string}
44
45
  */
45
46
  static idForSegmentRef(ref) {
47
+ const segmentData = ref.getSegmentData();
48
+ if (segmentData) {
49
+ return shaka.util.Uint8ArrayUtils.toBase64(segmentData);
50
+ }
46
51
  // Escape the URIs using encodeURI, to make sure that a weirdly formed URI
47
52
  // cannot cause two unrelated refs to be considered equivalent.
48
53
  return ref.getUris().map((uri) => '{' + encodeURI(uri) + '}').join('') +
@@ -168,6 +168,60 @@ shaka.offline.DownloadManager = class {
168
168
  return newPromise;
169
169
  }
170
170
 
171
+ /**
172
+ * Add already-downloaded data to a group.
173
+ *
174
+ * @param {number} groupId
175
+ * The group to add this segment to. If the group does not exist, a new
176
+ * group will be created.
177
+ * @param {!BufferSource} queueData
178
+ * @param {number} estimateId
179
+ * @param {boolean} isInitSegment
180
+ * @param {function(BufferSource):!Promise} onDownloaded
181
+ * The callback for when this request has been downloaded. Downloading for
182
+ * |group| will pause until the promise returned by |onDownloaded| resolves.
183
+ * @return {!Promise} Resolved when this request is complete.
184
+ */
185
+ queueData(groupId, queueData, estimateId, isInitSegment, onDownloaded) {
186
+ this.destroyer_.ensureNotDestroyed();
187
+
188
+ const group = this.groups_.get(groupId) || Promise.resolve();
189
+
190
+ // Add another download to the group.
191
+ const newPromise = group.then(() => {
192
+ // Make sure we stop downloading if we have been destroyed.
193
+ if (this.destroyer_.destroyed()) {
194
+ throw new shaka.util.Error(
195
+ shaka.util.Error.Severity.CRITICAL,
196
+ shaka.util.Error.Category.STORAGE,
197
+ shaka.util.Error.Code.OPERATION_ABORTED);
198
+ }
199
+
200
+ // Update initData
201
+ if (isInitSegment) {
202
+ const segmentBytes = shaka.util.BufferUtils.toUint8(queueData);
203
+ const pssh = new shaka.util.Pssh(segmentBytes);
204
+ for (const key in pssh.data) {
205
+ const index = Number(key);
206
+ const data = pssh.data[index];
207
+ const systemId = pssh.systemIds[index];
208
+ this.onInitData_(data, systemId);
209
+ }
210
+ }
211
+
212
+ // Update all our internal stats.
213
+ this.estimator_.close(estimateId, queueData.byteLength);
214
+ this.onProgress_(
215
+ this.estimator_.getEstimatedProgress(),
216
+ this.estimator_.getTotalDownloaded());
217
+
218
+ return onDownloaded(queueData);
219
+ });
220
+
221
+ this.groups_.set(groupId, newPromise);
222
+ return newPromise;
223
+ }
224
+
171
225
  /**
172
226
  * Add additional async work to the group work queue.
173
227
  *
@@ -212,6 +212,7 @@ shaka.offline.ManifestConverter = class {
212
212
  spatialAudio: streamDB.spatialAudio,
213
213
  closedCaptions: streamDB.closedCaptions,
214
214
  tilesLayout: streamDB.tilesLayout,
215
+ mssPrivateData: streamDB.mssPrivateData,
215
216
  accessibilityPurpose: null,
216
217
  external: streamDB.external,
217
218
  fastSwitching: streamDB.fastSwitching,
@@ -474,8 +474,16 @@ shaka.offline.Storage = class {
474
474
  pendingDataSize += data.byteLength;
475
475
  };
476
476
 
477
- downloader.queue(download.groupId,
478
- request, estimateId, isInitSegment, onDownloaded);
477
+ const ref = /** @type {!shaka.media.SegmentReference} */ (
478
+ download.ref);
479
+ const segmentData = ref.getSegmentData();
480
+ if (segmentData) {
481
+ downloader.queueData(download.groupId,
482
+ segmentData, estimateId, isInitSegment, onDownloaded);
483
+ } else {
484
+ downloader.queue(download.groupId,
485
+ request, estimateId, isInitSegment, onDownloaded);
486
+ }
479
487
  }
480
488
  await downloader.waitToFinish();
481
489
 
@@ -1335,6 +1343,7 @@ shaka.offline.Storage = class {
1335
1343
  spatialAudio: stream.spatialAudio,
1336
1344
  closedCaptions: stream.closedCaptions,
1337
1345
  tilesLayout: stream.tilesLayout,
1346
+ mssPrivateData: stream.mssPrivateData,
1338
1347
  external: stream.external,
1339
1348
  fastSwitching: stream.fastSwitching,
1340
1349
  };
package/lib/player.js CHANGED
@@ -56,6 +56,7 @@ goog.require('shaka.util.ManifestParserUtils');
56
56
  goog.require('shaka.util.MediaReadyState');
57
57
  goog.require('shaka.util.MimeUtils');
58
58
  goog.require('shaka.util.Mutex');
59
+ goog.require('shaka.util.NumberUtils');
59
60
  goog.require('shaka.util.ObjectUtils');
60
61
  goog.require('shaka.util.Platform');
61
62
  goog.require('shaka.util.PlayerConfiguration');
@@ -705,7 +706,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
705
706
  this.externalSrcEqualsThumbnailsStreams_ = [];
706
707
 
707
708
  /** @private {number} */
708
- this.completionPercent_ = NaN;
709
+ this.completionPercent_ = -1;
709
710
 
710
711
  /** @private {?shaka.extern.PlayerConfiguration} */
711
712
  this.config_ = this.defaultConfig_();
@@ -1446,7 +1447,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1446
1447
  // after several playbacks, and they are not able anymore to properly
1447
1448
  // create MediaKeys objects. To prevent it, clear the cache after
1448
1449
  // each playback.
1449
- if (this.config_.streaming.clearDecodingCache) {
1450
+ if (this.config_ && this.config_.streaming.clearDecodingCache) {
1450
1451
  shaka.util.StreamUtils.clearDecodingConfigCache();
1451
1452
  shaka.util.DrmUtils.clearMediaKeySystemAccessMap();
1452
1453
  }
@@ -1457,7 +1458,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1457
1458
 
1458
1459
  this.externalSrcEqualsThumbnailsStreams_ = [];
1459
1460
 
1460
- this.completionPercent_ = NaN;
1461
+ this.completionPercent_ = -1;
1461
1462
 
1462
1463
  // Make sure that the app knows of the new buffering state.
1463
1464
  this.updateBufferState_();
@@ -2524,7 +2525,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2524
2525
  if (this.manifest_.nextUrl) {
2525
2526
  if (this.config_.streaming.preloadNextUrlWindow > 0) {
2526
2527
  const onTimeUpdate = async () => {
2527
- const timeToEnd = this.video_.duration - this.video_.currentTime;
2528
+ const timeToEnd = this.seekRange().end - this.video_.currentTime;
2528
2529
  if (!isNaN(timeToEnd)) {
2529
2530
  if (timeToEnd <= this.config_.streaming.preloadNextUrlWindow) {
2530
2531
  this.loadEventManager_.unlisten(
@@ -4059,10 +4060,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4059
4060
  // seekable range. This covers both plain mp4s and native HLS playbacks.
4060
4061
  if (this.video_ && this.video_.src) {
4061
4062
  const seekable = this.video_.seekable;
4062
- if (seekable.length) {
4063
+ if (seekable && seekable.length) {
4064
+ const playRangeStart =
4065
+ this.config_ ? this.config_.playRangeStart : 0;
4066
+ const start = Math.max(seekable.start(0), playRangeStart);
4067
+ const playRangeEnd =
4068
+ this.config_ ? this.config_.playRangeEnd : Infinity;
4069
+ const end = Math.min(seekable.end(seekable.length - 1), playRangeEnd);
4063
4070
  return {
4064
- 'start': seekable.start(0),
4065
- 'end': seekable.end(seekable.length - 1),
4071
+ 'start': start,
4072
+ 'end': end,
4066
4073
  };
4067
4074
  }
4068
4075
  }
@@ -4220,12 +4227,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4220
4227
  }
4221
4228
  this.trickPlayEventManager_.removeAll();
4222
4229
 
4223
- if (this.video_.paused) {
4224
- // Our fast forward is implemented with playbackRate and needs the video
4225
- // to be playing (to not be paused) to take immediate effect.
4226
- // If the video is paused, "unpause" it.
4227
- this.video_.play();
4228
- }
4229
4230
  this.playRateController_.set(rate);
4230
4231
 
4231
4232
  if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
@@ -5389,7 +5390,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5389
5390
 
5390
5391
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
5391
5392
 
5392
- let duration = this.video_.duration;
5393
+ const seekRange = this.seekRange();
5394
+ let duration = seekRange.end - seekRange.start;
5393
5395
  if (this.manifest_) {
5394
5396
  duration = this.manifest_.presentationTimeline.getDuration();
5395
5397
  }
@@ -5502,7 +5504,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5502
5504
  }
5503
5505
 
5504
5506
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
5505
- let duration = this.video_.duration;
5507
+ const seekRange = this.seekRange();
5508
+ let duration = seekRange.end - seekRange.start;
5506
5509
  if (this.manifest_) {
5507
5510
  duration = this.manifest_.presentationTimeline.getDuration();
5508
5511
  }
@@ -5928,9 +5931,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5928
5931
  const Platform = shaka.util.Platform;
5929
5932
  if (this.videoContainer_ &&
5930
5933
  (!Platform.safariVersion() || document.fullscreenEnabled)) {
5931
- const latestConfig = this.getConfiguration();
5932
5934
  return new shaka.text.UITextDisplayer(
5933
- this.video_, this.videoContainer_, latestConfig.textDisplayer);
5935
+ this.video_, this.videoContainer_);
5934
5936
  } else {
5935
5937
  // eslint-disable-next-line no-restricted-syntax
5936
5938
  if (HTMLMediaElement.prototype.addTextTrack) {
@@ -6198,7 +6200,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6198
6200
  let updateState = 'playing';
6199
6201
  if (this.bufferObserver_.getState() == State.STARVING) {
6200
6202
  updateState = 'buffering';
6201
- } else if (this.video_.ended) {
6203
+ } else if (this.isEnded()) {
6202
6204
  updateState = 'ended';
6203
6205
  } else if (this.video_.paused) {
6204
6206
  updateState = 'paused';
@@ -6370,41 +6372,62 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6370
6372
  if (!this.video_) {
6371
6373
  return;
6372
6374
  }
6373
- let hasNewCompletionPercent = false;
6374
- const completionRatio = this.video_.currentTime / this.video_.duration;
6375
- if (!isNaN(completionRatio)) {
6376
- const percent = Math.round(100 * completionRatio);
6377
- if (isNaN(this.completionPercent_)) {
6378
- this.completionPercent_ = percent;
6379
- hasNewCompletionPercent = true;
6380
- } else {
6381
- const newCompletionPercent = Math.max(this.completionPercent_, percent);
6382
- if (this.completionPercent_ != newCompletionPercent) {
6383
- this.completionPercent_ = newCompletionPercent;
6384
- hasNewCompletionPercent = true;
6375
+
6376
+ const isQuartile = (quartilePercent, currentPercent) => {
6377
+ const NumberUtils = shaka.util.NumberUtils;
6378
+
6379
+ if ((NumberUtils.isFloatEqual(quartilePercent, currentPercent) ||
6380
+ currentPercent > quartilePercent) &&
6381
+ this.completionPercent_ < quartilePercent) {
6382
+ this.completionPercent_ = quartilePercent;
6383
+ return true;
6384
+ }
6385
+ return false;
6386
+ };
6387
+
6388
+ const checkEnded = () => {
6389
+ if (this.config_ && this.config_.playRangeEnd != Infinity) {
6390
+ // Make sure the video stops when we reach the end.
6391
+ // This is required when there is a custom playRangeEnd specified.
6392
+ if (this.isEnded()) {
6393
+ this.video_.pause();
6385
6394
  }
6386
6395
  }
6396
+ };
6397
+
6398
+ const seekRange = this.seekRange();
6399
+ const duration = seekRange.end - seekRange.start;
6400
+ const completionRatio =
6401
+ duration > 0 ? this.video_.currentTime / duration : 0;
6402
+
6403
+ if (isNaN(completionRatio)) {
6404
+ return;
6387
6405
  }
6388
- if (hasNewCompletionPercent) {
6389
- let event;
6390
- if (this.completionPercent_ == 0) {
6391
- event = shaka.Player.makeEvent_(shaka.util.FakeEvent.EventName.Started);
6392
- } else if (this.completionPercent_ == 25) {
6393
- event = shaka.Player.makeEvent_(
6394
- shaka.util.FakeEvent.EventName.FirstQuartile);
6395
- } else if (this.completionPercent_ == 50) {
6396
- event = shaka.Player.makeEvent_(
6397
- shaka.util.FakeEvent.EventName.Midpoint);
6398
- } else if (this.completionPercent_ == 75) {
6399
- event = shaka.Player.makeEvent_(
6400
- shaka.util.FakeEvent.EventName.ThirdQuartile);
6401
- } else if (this.completionPercent_ == 100) {
6402
- event = shaka.Player.makeEvent_(
6403
- shaka.util.FakeEvent.EventName.Complete);
6404
- }
6405
- if (event) {
6406
- this.dispatchEvent(event);
6407
- }
6406
+
6407
+ const percent = completionRatio * 100;
6408
+
6409
+ let event;
6410
+ if (isQuartile(0, percent)) {
6411
+ event = shaka.Player.makeEvent_(shaka.util.FakeEvent.EventName.Started);
6412
+ } else if (isQuartile(25, percent)) {
6413
+ event = shaka.Player.makeEvent_(
6414
+ shaka.util.FakeEvent.EventName.FirstQuartile);
6415
+ } else if (isQuartile(50, percent)) {
6416
+ event = shaka.Player.makeEvent_(
6417
+ shaka.util.FakeEvent.EventName.Midpoint);
6418
+ } else if (isQuartile(75, percent)) {
6419
+ event = shaka.Player.makeEvent_(
6420
+ shaka.util.FakeEvent.EventName.ThirdQuartile);
6421
+ } else if (isQuartile(100, percent)) {
6422
+ event = shaka.Player.makeEvent_(
6423
+ shaka.util.FakeEvent.EventName.Complete);
6424
+ checkEnded();
6425
+ } else {
6426
+ checkEnded();
6427
+ }
6428
+
6429
+ if (event) {
6430
+ this.dispatchEvent(event);
6408
6431
  }
6409
6432
  }
6410
6433
 
@@ -7440,7 +7463,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7440
7463
 
7441
7464
  // This is a strong guarantee that we are buffered to the end, because it
7442
7465
  // means the playhead is already at that end.
7443
- if (this.video_.ended) {
7466
+ if (this.isEnded()) {
7444
7467
  return true;
7445
7468
  }
7446
7469
 
@@ -7480,7 +7503,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7480
7503
 
7481
7504
  // This is a strong guarantee that we are buffered to the end, because it
7482
7505
  // means the playhead is already at that end.
7483
- if (this.video_.ended) {
7506
+ if (this.isEnded()) {
7484
7507
  return true;
7485
7508
  }
7486
7509
 
@@ -7508,6 +7531,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7508
7531
  shaka.util.Error.Category.PLAYER,
7509
7532
  shaka.util.Error.Code.LOAD_INTERRUPTED);
7510
7533
  }
7534
+
7535
+ /**
7536
+ * Indicate if the video has ended.
7537
+ *
7538
+ * @return {boolean}
7539
+ * @export
7540
+ */
7541
+ isEnded() {
7542
+ if (!this.video_ || this.video_.ended) {
7543
+ return true;
7544
+ }
7545
+ return this.video_.currentTime >= this.seekRange().end;
7546
+ }
7511
7547
  };
7512
7548
 
7513
7549
  /**
@@ -7549,7 +7585,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7549
7585
  * @export
7550
7586
  */
7551
7587
  // eslint-disable-next-line no-useless-concat, max-len
7552
- shaka.Player.version = 'v4.9.32' + '-uncompiled'; // x-release-please-version
7588
+ shaka.Player.version = 'v4.9.34' + '-uncompiled'; // x-release-please-version
7553
7589
 
7554
7590
  // Initialize the deprecation system using the version string we just set
7555
7591
  // on the player.
@@ -328,7 +328,7 @@ shaka.polyfill.MediaCapabilities = class {
328
328
  let displayType = videoConfig.contentType;
329
329
  if (videoConfig.width && videoConfig.height) {
330
330
  // All Chromecast can support 720p videos
331
- if (videoConfig.width > 1280 || videoConfig.height > 720) {
331
+ if (videoConfig.width > 1280 && videoConfig.height > 720) {
332
332
  displayType +=
333
333
  `; width=${videoConfig.width}; height=${videoConfig.height}`;
334
334
  }
@@ -91,6 +91,9 @@ shaka.text.SimpleTextDisplayer = class {
91
91
  * @export
92
92
  */
93
93
  append(cues) {
94
+ if (!this.textTrack_) {
95
+ return;
96
+ }
94
97
  const flattenedCues = shaka.text.Utils.getCuesToFlatten(cues);
95
98
 
96
99
  // Convert cues.
@@ -178,6 +181,9 @@ shaka.text.SimpleTextDisplayer = class {
178
181
  * @export
179
182
  */
180
183
  isTextVisible() {
184
+ if (!this.textTrack_) {
185
+ return false;
186
+ }
181
187
  return this.textTrack_.mode == 'showing';
182
188
  }
183
189
 
@@ -186,7 +192,9 @@ shaka.text.SimpleTextDisplayer = class {
186
192
  * @export
187
193
  */
188
194
  setTextVisibility(on) {
189
- this.textTrack_.mode = on ? 'showing' : 'hidden';
195
+ if (this.textTrack_) {
196
+ this.textTrack_.mode = on ? 'showing' : 'hidden';
197
+ }
190
198
  }
191
199
 
192
200
  /**
@@ -8,7 +8,6 @@
8
8
  goog.provide('shaka.text.UITextDisplayer');
9
9
 
10
10
  goog.require('goog.asserts');
11
- goog.require('shaka.Deprecate');
12
11
  goog.require('shaka.log');
13
12
  goog.require('shaka.text.Cue');
14
13
  goog.require('shaka.text.CueRegion');
@@ -31,9 +30,8 @@ shaka.text.UITextDisplayer = class {
31
30
  * Constructor.
32
31
  * @param {HTMLMediaElement} video
33
32
  * @param {HTMLElement} videoContainer
34
- * @param {shaka.extern.TextDisplayerConfiguration} config
35
33
  */
36
- constructor(video, videoContainer, config) {
34
+ constructor(video, videoContainer) {
37
35
  goog.asserts.assert(videoContainer, 'videoContainer should be valid.');
38
36
 
39
37
  if (!document.fullscreenEnabled) {
@@ -74,22 +72,12 @@ shaka.text.UITextDisplayer = class {
74
72
 
75
73
  this.videoContainer_.appendChild(this.textContainer_);
76
74
 
77
- if (!config || !config.captionsUpdatePeriod) {
78
- shaka.Deprecate.deprecateFeature(5,
79
- 'UITextDisplayer w/ config',
80
- 'Please migrate to initializing UITextDisplayer with a config.');
81
- }
82
-
83
- /** @private {number} */
84
- const updatePeriod = (config && config.captionsUpdatePeriod) ?
85
- config.captionsUpdatePeriod : 0.25;
86
-
87
75
  /** @private {shaka.util.Timer} */
88
76
  this.captionsTimer_ = new shaka.util.Timer(() => {
89
77
  if (!this.video_.paused) {
90
78
  this.updateCaptions_();
91
79
  }
92
- }).tickEvery(updatePeriod);
80
+ }).tickEvery(/* seconds= */ 0.25);
93
81
 
94
82
  /**
95
83
  * Maps cues to cue elements. Specifically points out the wrapper element of
@@ -84,10 +84,15 @@ shaka.text.VttTextParser = class {
84
84
  // HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
85
85
  let offset = time.vttOffset;
86
86
 
87
- // Only use 'X-TIMESTAMP-MAP' with HLS. This overrides offset above.
88
- if (blocks[0].includes('X-TIMESTAMP-MAP') &&
89
- this.manifestType_ == shaka.media.ManifestParser.HLS) {
90
- offset = this.computeHlsOffset_(blocks[0], time);
87
+ if (this.manifestType_ == shaka.media.ManifestParser.HLS) {
88
+ // Only use 'X-TIMESTAMP-MAP' with HLS.
89
+ if (blocks[0].includes('X-TIMESTAMP-MAP')) {
90
+ offset = this.computeHlsOffset_(blocks[0], time);
91
+ } else if (time.periodStart && time.vttOffset == time.periodStart) {
92
+ // In the case where X-TIMESTAMP-MAP is not used and it is HLS, we
93
+ // should not use offset unless segment-relative times are used.
94
+ offset = 0;
95
+ }
91
96
  }
92
97
 
93
98
  // Parse VTT regions.
@@ -35,6 +35,9 @@ shaka.transmuxer.AacTransmuxer = class {
35
35
 
36
36
  /** @private {!Map.<string, !Uint8Array>} */
37
37
  this.initSegments = new Map();
38
+
39
+ /** @private {?Uint8Array} */
40
+ this.lastInitSegment_ = null;
38
41
  }
39
42
 
40
43
 
@@ -215,11 +218,16 @@ shaka.transmuxer.AacTransmuxer = class {
215
218
  } else {
216
219
  initSegment = this.initSegments.get(initSegmentKey);
217
220
  }
221
+ const appendInitSegment = this.lastInitSegment_ !== initSegment;
218
222
  const segmentData = mp4Generator.segmentData();
219
-
223
+ this.lastInitSegment_ = initSegment;
220
224
  this.frameIndex_++;
221
- const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
222
- return Promise.resolve(transmuxData);
225
+ if (appendInitSegment) {
226
+ const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
227
+ return Promise.resolve(transmuxData);
228
+ } else {
229
+ return Promise.resolve(segmentData);
230
+ }
223
231
  }
224
232
  };
225
233
 
@@ -34,6 +34,9 @@ shaka.transmuxer.Ac3Transmuxer = class {
34
34
 
35
35
  /** @private {!Map.<string, !Uint8Array>} */
36
36
  this.initSegments = new Map();
37
+
38
+ /** @private {?Uint8Array} */
39
+ this.lastInitSegment_ = null;
37
40
  }
38
41
 
39
42
 
@@ -203,11 +206,16 @@ shaka.transmuxer.Ac3Transmuxer = class {
203
206
  } else {
204
207
  initSegment = this.initSegments.get(initSegmentKey);
205
208
  }
209
+ const appendInitSegment = this.lastInitSegment_ !== initSegment;
206
210
  const segmentData = mp4Generator.segmentData();
207
-
211
+ this.lastInitSegment_ = initSegment;
208
212
  this.frameIndex_++;
209
- const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
210
- return Promise.resolve(transmuxData);
213
+ if (appendInitSegment) {
214
+ const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
215
+ return Promise.resolve(transmuxData);
216
+ } else {
217
+ return Promise.resolve(segmentData);
218
+ }
211
219
  }
212
220
  };
213
221
 
@@ -34,6 +34,9 @@ shaka.transmuxer.Ec3Transmuxer = class {
34
34
 
35
35
  /** @private {!Map.<string, !Uint8Array>} */
36
36
  this.initSegments = new Map();
37
+
38
+ /** @private {?Uint8Array} */
39
+ this.lastInitSegment_ = null;
37
40
  }
38
41
 
39
42
 
@@ -203,11 +206,16 @@ shaka.transmuxer.Ec3Transmuxer = class {
203
206
  } else {
204
207
  initSegment = this.initSegments.get(initSegmentKey);
205
208
  }
209
+ const appendInitSegment = this.lastInitSegment_ !== initSegment;
206
210
  const segmentData = mp4Generator.segmentData();
207
-
211
+ this.lastInitSegment_ = initSegment;
208
212
  this.frameIndex_++;
209
- const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
210
- return Promise.resolve(transmuxData);
213
+ if (appendInitSegment) {
214
+ const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
215
+ return Promise.resolve(transmuxData);
216
+ } else {
217
+ return Promise.resolve(segmentData);
218
+ }
211
219
  }
212
220
  };
213
221