shaka-player 4.3.15 → 4.3.16

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 (60) hide show
  1. package/.github/workflows/appspot.yaml +4 -4
  2. package/.github/workflows/build-and-test.yaml +23 -10
  3. package/.github/workflows/custom-actions/set-commit-status/action.yaml +29 -7
  4. package/.github/workflows/demo-version-index.yaml +3 -3
  5. package/.github/workflows/nightly-demo.yaml +4 -4
  6. package/.github/workflows/release-please.yaml +25 -24
  7. package/.github/workflows/report-incremental-coverage.yaml +1 -1
  8. package/.github/workflows/selenium-lab-tests.yaml +30 -18
  9. package/.github/workflows/sync-labels.yaml +1 -1
  10. package/.github/workflows/talk-to-shaka-bot.yaml +1 -1
  11. package/.github/workflows/update-issues.yaml +1 -1
  12. package/.github/workflows/update-screenshots.yaml +25 -38
  13. package/CHANGELOG.md +28 -0
  14. package/demo/demo.less +1 -5
  15. package/dist/demo.css +1 -1
  16. package/dist/demo.css.map +1 -1
  17. package/dist/deps.js +0 -1
  18. package/dist/locales.js +31 -31
  19. package/dist/shaka-player.compiled.d.ts +6 -10
  20. package/dist/shaka-player.compiled.debug.d.ts +6 -10
  21. package/dist/shaka-player.compiled.debug.externs.js +13 -11
  22. package/dist/shaka-player.compiled.debug.js +209 -206
  23. package/dist/shaka-player.compiled.debug.map +4 -4
  24. package/dist/shaka-player.compiled.externs.js +13 -11
  25. package/dist/shaka-player.compiled.js +854 -852
  26. package/dist/shaka-player.compiled.map +4 -4
  27. package/dist/shaka-player.ui.d.ts +6 -10
  28. package/dist/shaka-player.ui.debug.d.ts +6 -10
  29. package/dist/shaka-player.ui.debug.externs.js +13 -11
  30. package/dist/shaka-player.ui.debug.js +158 -155
  31. package/dist/shaka-player.ui.debug.map +4 -4
  32. package/dist/shaka-player.ui.externs.js +13 -11
  33. package/dist/shaka-player.ui.js +949 -947
  34. package/dist/shaka-player.ui.map +4 -4
  35. package/docs/tutorials/faq.md +25 -0
  36. package/docs/tutorials/offline.md +3 -0
  37. package/docs/tutorials/ui.md +4 -4
  38. package/externs/shaka/player.js +32 -19
  39. package/lib/cast/cast_proxy.js +1 -3
  40. package/lib/cast/cast_sender.js +8 -11
  41. package/lib/cea/cea608_memory.js +1 -1
  42. package/lib/dash/dash_parser.js +3 -0
  43. package/lib/dash/segment_template.js +5 -0
  44. package/lib/hls/hls_parser.js +18 -16
  45. package/lib/media/drm_engine.js +17 -3
  46. package/lib/media/presentation_timeline.js +23 -0
  47. package/lib/media/segment_index.js +10 -0
  48. package/lib/media/streaming_engine.js +1 -13
  49. package/lib/player.js +18 -4
  50. package/lib/text/ui_text_displayer.js +10 -1
  51. package/lib/util/manifest_parser_utils.js +1 -0
  52. package/lib/util/stream_utils.js +8 -5
  53. package/package.json +1 -1
  54. package/test/cast/cast_proxy_unit.js +0 -46
  55. package/test/cast/cast_sender_unit.js +26 -26
  56. package/test/cea/cea608_memory_unit.js +3 -2
  57. package/test/hls/hls_live_unit.js +52 -0
  58. package/test/media/media_source_engine_integration.js +1 -1
  59. package/test/player_unit.js +13 -1
  60. package/lib/polyfill/storage_estimate.js +0 -57
@@ -263,6 +263,31 @@ so don't declare it using a ref(), and if you put your player instance into a
263
263
  data() object, you can prefix the property name with "$" or "_" to make Vue not
264
264
  proxy them.
265
265
 
266
+ <hr>
267
+
268
+ **Q:** My player keeps buffering after each chunk when streaming HLS live.
269
+
270
+ **A:** It is recommended to have more than 3 chunks in your chunklist. If this
271
+ is not possible, then you have to change the presentationDelay value. We offer
272
+ the following configuration:
273
+
274
+ ```js
275
+ player.configure('manifest.hls.liveSegmentsDelay', 1);
276
+ ```
277
+
278
+ <hr>
279
+
280
+ **Q:** Why can I not find the new version of Shaka Player on
281
+ ajax.googleapis.com?
282
+
283
+ **A:** When a new version of Shaka Player is released on GitHub, it has to
284
+ undergo a brief manual review process before it can be uploaded to Google's
285
+ servers. After approval, the upload process then usually takes several hours.
286
+ Therefore it is not unusual for a day or two to pass before a new version of
287
+ Shaka Player is hosted on Google's ajax.googleapis.com website. We ask for your
288
+ patience in these matters.
289
+
290
+
266
291
  [386]: https://github.com/shaka-project/shaka-player/issues/386#issuecomment-227898001
267
292
  [489]: https://github.com/shaka-project/shaka-player/issues/489#issuecomment-240466224
268
293
  [743]: https://github.com/shaka-project/shaka-player/issues/743
@@ -339,6 +339,9 @@ you want.
339
339
  At this point, the content is now stored offline and it's ready to be played.
340
340
  Next we will add functionality to play offline content.
341
341
 
342
+ Note: If you call `storage.store` twice with the same manifestUri as input,
343
+ you'll download the same manifestUri twice.
344
+
342
345
  ## Playing Offline Content
343
346
 
344
347
  Now that we have stored some content, we want to play it. To do this, resolve
@@ -16,9 +16,9 @@ Set up controls with HTML data attributes:
16
16
  <!DOCTYPE html>
17
17
  <html>
18
18
  <head>
19
- <!-- Shaka Player ui compiled library: -->
19
+ <!-- Shaka Player UI compiled library: -->
20
20
  <script src="dist/shaka-player.ui.js"></script>
21
- <!-- Shaka Player ui compiled library default CSS: -->
21
+ <!-- Shaka Player UI compiled library default CSS: -->
22
22
  <link rel="stylesheet" type="text/css" href="dist/controls.css">
23
23
  <!-- Chromecast SDK (if you want Chromecast support for your app): -->
24
24
  <script defer src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
@@ -52,7 +52,7 @@ async function init() {
52
52
  const controls = ui.getControls();
53
53
  const player = controls.getPlayer();
54
54
 
55
- // Attach player and ui to the window to make it easy to access in the JS console.
55
+ // Attach player and UI to the window to make it easy to access in the JS console.
56
56
  window.player = player;
57
57
  window.ui = ui;
58
58
 
@@ -145,7 +145,7 @@ casting to an Android receiver app.
145
145
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%"
146
146
  src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
147
147
  </div
148
-
148
+ ```
149
149
 
150
150
  #### Providing source(s) for auto load.
151
151
 
@@ -96,57 +96,70 @@ shaka.extern.StateChange;
96
96
  * again.
97
97
  *
98
98
  * @property {number} width
99
- * The width of the current video track.
99
+ * The width of the current video track. If nothing is loaded or the content
100
+ * is audio-only, NaN.
100
101
  * @property {number} height
101
- * The height of the current video track.
102
+ * The height of the current video track. If nothing is loaded or the content
103
+ * is audio-only, NaN.
102
104
  * @property {number} streamBandwidth
103
105
  * The bandwidth required for the current streams (total, in bit/sec).
104
- * It takes into account the playbackrate.
106
+ * It takes into account the playbackrate. If nothing is loaded, NaN.
105
107
  *
106
108
  * @property {number} decodedFrames
107
- * The total number of frames decoded by the Player. This may be
108
- * <code>NaN</code> if this is not supported by the browser.
109
+ * The total number of frames decoded by the Player. If not reported by the
110
+ * browser, NaN.
109
111
  * @property {number} droppedFrames
110
- * The total number of frames dropped by the Player. This may be
111
- * <code>NaN</code> if this is not supported by the browser.
112
+ * The total number of frames dropped by the Player. If not reported by the
113
+ * browser, NaN.
112
114
  * @property {number} corruptedFrames
113
- * The total number of corrupted frames dropped by the browser. This may be
114
- * <code>NaN</code> if this is not supported by the browser.
115
+ * The total number of corrupted frames dropped by the browser. If not
116
+ * reported by the browser, NaN.
115
117
  * @property {number} estimatedBandwidth
116
- * The current estimated network bandwidth (in bit/sec).
118
+ * The current estimated network bandwidth (in bit/sec). If no estimate
119
+ * available, NaN.
117
120
  *
118
121
  * @property {number} gapsJumped
119
122
  * The total number of playback gaps jumped by the GapJumpingController.
123
+ * If nothing is loaded, NaN.
120
124
  * @property {number} stallsDetected
121
125
  * The total number of playback stalls detected by the StallDetector.
126
+ * If nothing is loaded, NaN.
122
127
  *
123
128
  * @property {number} completionPercent
124
129
  * This is the greatest completion percent that the user has experienced in
125
- * playback. Also known as the "high water mark". Is NaN when there is no
126
- * known duration, such as for livestreams.
130
+ * playback. Also known as the "high water mark". If nothing is loaded, or
131
+ * the stream is live (and therefore indefinite), NaN.
127
132
  * @property {number} loadLatency
128
133
  * This is the number of seconds it took for the video element to have enough
129
134
  * data to begin playback. This is measured from the time load() is called to
130
135
  * the time the <code>'loadeddata'</code> event is fired by the media element.
136
+ * If nothing is loaded, NaN.
131
137
  * @property {number} manifestTimeSeconds
132
138
  * The amount of time it took to download and parse the manifest.
139
+ * If nothing is loaded, NaN.
133
140
  * @property {number} drmTimeSeconds
134
141
  * The amount of time it took to download the first drm key, and load that key
135
- * into the drm system.
142
+ * into the drm system. If nothing is loaded or DRM is not in use, NaN.
136
143
  * @property {number} playTime
137
- * The total time spent in a playing state in seconds.
144
+ * The total time spent in a playing state in seconds. If nothing is loaded,
145
+ * NaN.
138
146
  * @property {number} pauseTime
139
- * The total time spent in a paused state in seconds.
147
+ * The total time spent in a paused state in seconds. If nothing is loaded,
148
+ * NaN.
140
149
  * @property {number} bufferingTime
141
- * The total time spent in a buffering state in seconds.
150
+ * The total time spent in a buffering state in seconds. If nothing is
151
+ * loaded, NaN.
142
152
  * @property {number} licenseTime
143
- * The time spent on license requests during this session in seconds, or NaN.
153
+ * The time spent on license requests during this session in seconds. If DRM
154
+ * is not in use, NaN.
144
155
  * @property {number} liveLatency
145
156
  * The time between the capturing of a frame and the end user having it
146
- * displayed on their screen.
157
+ * displayed on their screen. If nothing is loaded or the content is VOD,
158
+ * NaN.
147
159
  *
148
160
  * @property {number} maxSegmentDuration
149
- * The presentation's max segment duration in seconds, or NaN.
161
+ * The presentation's max segment duration in seconds. If nothing is loaded,
162
+ * NaN.
150
163
  *
151
164
  * @property {!Array.<shaka.extern.TrackChoice>} switchHistory
152
165
  * A history of the stream changes.
@@ -188,12 +188,10 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
188
188
  * @export
189
189
  */
190
190
  async cast() {
191
- const initState = this.getInitState_();
192
-
193
191
  // TODO: transfer manually-selected tracks?
194
192
  // TODO: transfer side-loaded text tracks?
195
193
 
196
- await this.sender_.cast(initState);
194
+ await this.sender_.cast();
197
195
  if (!this.localPlayer_) {
198
196
  // We've already been destroyed.
199
197
  return;
@@ -261,12 +261,10 @@ shaka.cast.CastSender = class {
261
261
 
262
262
 
263
263
  /**
264
- * @param {shaka.cast.CastUtils.InitStateType} initState Video and player
265
- * state to be sent to the receiver.
266
264
  * @return {!Promise} Resolved when connected to a receiver. Rejected if the
267
265
  * connection fails or is canceled by the user.
268
266
  */
269
- async cast(initState) {
267
+ async cast() {
270
268
  if (!this.apiReady_) {
271
269
  throw new shaka.util.Error(
272
270
  shaka.util.Error.Severity.RECOVERABLE,
@@ -288,7 +286,7 @@ shaka.cast.CastSender = class {
288
286
 
289
287
  this.castPromise_ = new shaka.util.PublicPromise();
290
288
  chrome.cast.requestSession(
291
- (session) => this.onSessionInitiated_(initState, session),
289
+ (session) => this.onSessionInitiated_(session),
292
290
  (error) => this.onConnectionError_(error));
293
291
  await this.castPromise_;
294
292
  }
@@ -303,10 +301,9 @@ shaka.cast.CastSender = class {
303
301
  if (!this.isCasting_) {
304
302
  return;
305
303
  }
306
- const initState = this.onInitStateRequired_();
307
304
 
308
305
  chrome.cast.requestSession(
309
- (session) => this.onSessionInitiated_(initState, session),
306
+ (session) => this.onSessionInitiated_(session),
310
307
  (error) => this.onConnectionError_(error));
311
308
  }
312
309
 
@@ -401,12 +398,14 @@ shaka.cast.CastSender = class {
401
398
 
402
399
 
403
400
  /**
404
- * @param {shaka.cast.CastUtils.InitStateType} initState
405
401
  * @param {chrome.cast.Session} session
406
402
  * @private
407
403
  */
408
- onSessionInitiated_(initState, session) {
404
+ onSessionInitiated_(session) {
409
405
  shaka.log.debug('CastSender: onSessionInitiated');
406
+
407
+ const initState = this.onInitStateRequired_();
408
+
410
409
  this.onSessionCreated_(session);
411
410
 
412
411
  this.sendMessage_({
@@ -529,12 +528,10 @@ shaka.cast.CastSender = class {
529
528
  onExistingSessionJoined_(session) {
530
529
  shaka.log.debug('CastSender: onExistingSessionJoined');
531
530
 
532
- const initState = this.onInitStateRequired_();
533
-
534
531
  this.castPromise_ = new shaka.util.PublicPromise();
535
532
  this.hasJoinedExistingSession_ = true;
536
533
 
537
- this.onSessionInitiated_(initState, session);
534
+ this.onSessionInitiated_(session);
538
535
  }
539
536
 
540
537
 
@@ -288,7 +288,7 @@ shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars = new Map([
288
288
  */
289
289
  shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars = new Map([
290
290
  [0x30, '®'], [0x31, '°'], [0x32, '½'], [0x33, '¿'], [0x34, '™'], [0x35, '¢'],
291
- [0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, '⠀'], [0x3a, 'è'], [0x3b, 'â'],
291
+ [0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, ' '], [0x3a, 'è'], [0x3b, 'â'],
292
292
  [0x3c, 'ê'], [0x3d, 'î'], [0x3e, 'ô'], [0x3f, 'û'],
293
293
  ]);
294
294
 
@@ -521,6 +521,9 @@ shaka.dash.DashParser = class {
521
521
  // best latency
522
522
  if (this.lowLatencyMode_) {
523
523
  minBufferTime = 0;
524
+ const presentationDelay = suggestedPresentationDelay != null ?
525
+ suggestedPresentationDelay : this.config_.defaultPresentationDelay;
526
+ presentationTimeline.setDelay(presentationDelay);
524
527
  }
525
528
 
526
529
  // These steps are not done on manifest update.
@@ -372,6 +372,11 @@ shaka.dash.SegmentTemplate = class {
372
372
  Math.ceil(availablePeriodTimes[1] / segmentDuration) - 1,
373
373
  ];
374
374
 
375
+ // For Low Latency we can request the partial current position.
376
+ if (context.representation.availabilityTimeOffset) {
377
+ availablePeriodPositions[1]++;
378
+ }
379
+
375
380
  // In absolute positions.
376
381
  const availablePresentationPositions =
377
382
  availablePeriodPositions.map((x) => x + startNumber);
@@ -479,7 +479,7 @@ shaka.hls.HlsParser = class {
479
479
  if (goog.DEBUG) {
480
480
  const firstSequenceStartTime =
481
481
  mediaSequenceToStartTime.values().next().value;
482
- goog.asserts.assert(
482
+ shaka.log.warning(
483
483
  firstSequenceStartTime == segment0.startTime,
484
484
  'Sequence number map is not ordered as expected!');
485
485
  }
@@ -513,16 +513,18 @@ shaka.hls.HlsParser = class {
513
513
  // Drop any earlier references.
514
514
  const numSegmentsToDrop =
515
515
  this.minSequenceNumber_ - streamInfo.firstSequenceNumber;
516
- segmentIndex.dropFirstReferences(numSegmentsToDrop);
517
-
518
- // Now adjust timestamps back to begin at 0.
519
- const segmentN = segmentIndex.earliestReference();
520
- if (segmentN) {
521
- const streamOffset = -segmentN.startTime;
522
- // Modify all SegmentReferences equally.
523
- streamInfo.stream.segmentIndex.offset(streamOffset);
524
- // Update other parts of streamInfo the same way.
525
- this.offsetStreamInfo_(streamInfo, streamOffset);
516
+ if (numSegmentsToDrop > 0) {
517
+ segmentIndex.dropFirstReferences(numSegmentsToDrop);
518
+
519
+ // Now adjust timestamps back to begin at 0.
520
+ const segmentN = segmentIndex.earliestReference();
521
+ if (segmentN) {
522
+ const streamOffset = -segmentN.startTime;
523
+ // Modify all SegmentReferences equally.
524
+ streamInfo.stream.segmentIndex.offset(streamOffset);
525
+ // Update other parts of streamInfo the same way.
526
+ this.offsetStreamInfo_(streamInfo, streamOffset);
527
+ }
526
528
  }
527
529
  }
528
530
  }
@@ -848,17 +850,17 @@ shaka.hls.HlsParser = class {
848
850
  }
849
851
 
850
852
  if (!this.presentationTimeline_.isStartTimeLocked()) {
851
- const streamInfos = Array.from(this.uriToStreamInfosMap_.values());
852
- const activeStreamInfos =
853
- streamInfos.filter((s) => s.stream.segmentIndex);
854
- for (const streamInfo of activeStreamInfos) {
853
+ for (const streamInfo of this.uriToStreamInfosMap_.values()) {
854
+ if (!streamInfo.stream.segmentIndex) {
855
+ continue; // Not active.
856
+ }
855
857
  if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
856
858
  continue;
857
859
  }
858
860
  const firstReference = streamInfo.stream.segmentIndex.get(0);
859
861
  if (firstReference && firstReference.syncTime) {
860
862
  const syncTime = firstReference.syncTime;
861
- this.presentationTimeline_.setPresentationStartTime(syncTime);
863
+ this.presentationTimeline_.setInitialProgramDateTime(syncTime);
862
864
  }
863
865
  }
864
866
  }
@@ -1425,6 +1425,9 @@ shaka.media.DrmEngine = class {
1425
1425
  const req = this.playerInterface_.netEngine.request(requestType, request);
1426
1426
  response = await req.promise;
1427
1427
  } catch (error) {
1428
+ if (this.destroyer_.destroyed()) {
1429
+ return;
1430
+ }
1428
1431
  // Request failed!
1429
1432
  goog.asserts.assert(error instanceof shaka.util.Error,
1430
1433
  'Wrong NetworkingEngine error type!');
@@ -1433,9 +1436,20 @@ shaka.media.DrmEngine = class {
1433
1436
  shaka.util.Error.Category.DRM,
1434
1437
  shaka.util.Error.Code.LICENSE_REQUEST_FAILED,
1435
1438
  error);
1436
- this.onError_(shakaErr);
1437
- if (metadata && metadata.updatePromise) {
1438
- metadata.updatePromise.reject(shakaErr);
1439
+ if (this.activeSessions_.size == 1) {
1440
+ this.onError_(shakaErr);
1441
+ if (metadata && metadata.updatePromise) {
1442
+ metadata.updatePromise.reject(shakaErr);
1443
+ }
1444
+ } else {
1445
+ if (metadata && metadata.updatePromise) {
1446
+ metadata.updatePromise.reject(shakaErr);
1447
+ }
1448
+ this.activeSessions_.delete(session);
1449
+ if (this.areAllSessionsLoaded_()) {
1450
+ this.allSessionsLoaded_.resolve();
1451
+ this.keyStatusTimer_.tickAfter(/* seconds= */ 0.1);
1452
+ }
1439
1453
  }
1440
1454
  return;
1441
1455
  }
@@ -96,6 +96,9 @@ shaka.media.PresentationTimeline = class {
96
96
 
97
97
  /** @private {boolean} */
98
98
  this.startTimeLocked_ = false;
99
+
100
+ /** @private {?number} */
101
+ this.initialProgramDateTime_ = null;
99
102
  }
100
103
 
101
104
 
@@ -299,6 +302,26 @@ shaka.media.PresentationTimeline = class {
299
302
  }
300
303
 
301
304
 
305
+ /**
306
+ * Sets the initial program date time.
307
+ *
308
+ * @param {number} initialProgramDateTime
309
+ * @export
310
+ */
311
+ setInitialProgramDateTime(initialProgramDateTime) {
312
+ this.initialProgramDateTime_ = initialProgramDateTime;
313
+ }
314
+
315
+
316
+ /**
317
+ * @return {?number} The initial program date time in seconds.
318
+ * @export
319
+ */
320
+ getInitialProgramDateTime() {
321
+ return this.initialProgramDateTime_;
322
+ }
323
+
324
+
302
325
  /**
303
326
  * Gives PresentationTimeline a Stream's minimum segment start time.
304
327
  *
@@ -659,6 +659,16 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
659
659
  this.indexes_ = [];
660
660
  }
661
661
 
662
+ /**
663
+ * @override
664
+ * @export
665
+ */
666
+ forEachTopLevelReference(fn) {
667
+ for (const index of this.indexes_) {
668
+ index.forEachTopLevelReference(fn);
669
+ }
670
+ }
671
+
662
672
  /**
663
673
  * @override
664
674
  * @export
@@ -855,7 +855,6 @@ shaka.media.StreamingEngine = class {
855
855
  // The playhead might be seeking on startup, if a start time is set, so
856
856
  // start "seeked" as true.
857
857
  seeked: true,
858
- needsResync: false,
859
858
  recovering: false,
860
859
  hasError: false,
861
860
  operation: null,
@@ -1179,11 +1178,6 @@ shaka.media.StreamingEngine = class {
1179
1178
  shaka.log.v1(
1180
1179
  logPrefix, 'looking up segment from new stream endTime:', time);
1181
1180
 
1182
- // Using a new iterator means we need to resync the stream in sequence
1183
- // mode. The buffered range might not align perfectly with the last
1184
- // segment end time, so we may end up repeating a segment. Resyncing
1185
- // makes this safe to do.
1186
- mediaState.needsResync = true;
1187
1181
  mediaState.segmentIterator =
1188
1182
  mediaState.stream.segmentIndex.getIteratorForTime(time);
1189
1183
  const ref = mediaState.segmentIterator &&
@@ -1707,9 +1701,7 @@ shaka.media.StreamingEngine = class {
1707
1701
  // Across discontinuity bounds, we should resync timestamps for
1708
1702
  // sequence mode playbacks. The next segment appended should
1709
1703
  // land at its theoretical timestamp from the segment index.
1710
- if (reference.discontinuitySequence != lastDiscontinuitySequence ||
1711
- mediaState.needsResync) {
1712
- mediaState.needsResync = false;
1704
+ if (reference.discontinuitySequence != lastDiscontinuitySequence) {
1713
1705
  operations.push(this.playerInterface_.mediaSourceEngine.resync(
1714
1706
  mediaState.type, reference.startTime));
1715
1707
  }
@@ -2348,7 +2340,6 @@ shaka.media.StreamingEngine.PlayerInterface;
2348
2340
  * clearBufferSafeMargin: number,
2349
2341
  * clearingBuffer: boolean,
2350
2342
  * seeked: boolean,
2351
- * needsResync: boolean,
2352
2343
  * adaptation: boolean,
2353
2344
  * recovering: boolean,
2354
2345
  * hasError: boolean,
@@ -2398,9 +2389,6 @@ shaka.media.StreamingEngine.PlayerInterface;
2398
2389
  * True indicates that the presentation just seeked.
2399
2390
  * @property {boolean} adaptation
2400
2391
  * True indicates that the presentation just automatically switched variants.
2401
- * @property {boolean} needsResync
2402
- * True indicates that the stream needs to be resynced in sequence mode,
2403
- * regardless of discontinuity sequence.
2404
2392
  * @property {boolean} recovering
2405
2393
  * True indicates that the last segment was not appended because it could not
2406
2394
  * fit in the buffer.
package/lib/player.js CHANGED
@@ -2574,6 +2574,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2574
2574
  mediaElement.load();
2575
2575
  }
2576
2576
 
2577
+ // In Safari using HLS won't load anything unless you call load()
2578
+ // explicitly, no matter the value of the preload attribute.
2579
+ // Note: this only happens when there are not autoplay.
2580
+ if (mediaElement.preload != 'none' && !mediaElement.autoplay &&
2581
+ shaka.util.MimeUtils.isHlsType(mimeType) &&
2582
+ shaka.util.Platform.safariVersion()) {
2583
+ mediaElement.load();
2584
+ }
2585
+
2577
2586
  // Set the load mode last so that we know that all our components are
2578
2587
  // initialized.
2579
2588
  this.loadMode_ = shaka.Player.LoadMode.SRC_EQUALS;
@@ -3748,12 +3757,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3748
3757
  const currentTime = this.video_.currentTime;
3749
3758
  const seekRange = this.seekRange();
3750
3759
  const safeSeekOffset = this.config_.streaming.safeSeekOffset;
3760
+
3761
+ // Cancel trick play if we hit the beginning or end of the seekable
3762
+ // (Sub-second accuracy not required here)
3751
3763
  if (rate > 0) {
3752
- if (currentTime >= seekRange.end) {
3764
+ if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
3753
3765
  this.cancelTrickPlay();
3754
3766
  }
3755
3767
  } else {
3756
- if (currentTime <= (seekRange.start + safeSeekOffset)) {
3768
+ if (Math.floor(currentTime) <=
3769
+ Math.floor(seekRange.start + safeSeekOffset)) {
3757
3770
  this.cancelTrickPlay();
3758
3771
  }
3759
3772
  }
@@ -4527,7 +4540,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4527
4540
 
4528
4541
  if (this.manifest_) {
4529
4542
  const timeline = this.manifest_.presentationTimeline;
4530
- const startTime = timeline.getPresentationStartTime();
4543
+ const startTime = timeline.getInitialProgramDateTime() ||
4544
+ timeline.getPresentationStartTime();
4531
4545
  return new Date(/* ms= */ (startTime + presentationTime) * 1000);
4532
4546
  } else if (this.video_ && this.video_.getStartDate) {
4533
4547
  // Apple's native HLS gives us getStartDate(), which is only available if
@@ -7032,7 +7046,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7032
7046
  * @export
7033
7047
  */
7034
7048
  // eslint-disable-next-line no-useless-concat
7035
- shaka.Player.version = 'v4.3.15' + '-uncompiled'; // x-release-please-version
7049
+ shaka.Player.version = 'v4.3.16' + '-uncompiled'; // x-release-please-version
7036
7050
 
7037
7051
  // Initialize the deprecation system using the version string we just set
7038
7052
  // on the player.
@@ -72,7 +72,9 @@ shaka.text.UITextDisplayer = class {
72
72
 
73
73
  /** @private {shaka.util.Timer} */
74
74
  this.captionsTimer_ = new shaka.util.Timer(() => {
75
- this.updateCaptions_();
75
+ if (!this.video_.paused) {
76
+ this.updateCaptions_();
77
+ }
76
78
  }).tickEvery(updatePeriod);
77
79
 
78
80
  /**
@@ -93,6 +95,10 @@ shaka.text.UITextDisplayer = class {
93
95
  this.updateCaptions_(/* forceUpdate= */ true);
94
96
  });
95
97
 
98
+ this.eventManager_.listen(this.video_, 'seeking', () => {
99
+ this.updateCaptions_(/* forceUpdate= */ true);
100
+ });
101
+
96
102
  // From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
97
103
  // Whenever the natural width or natural height of the video changes
98
104
  // (including, for example, because the selected video track was changed),
@@ -339,6 +345,9 @@ shaka.text.UITextDisplayer = class {
339
345
  const cueRegistry = this.currentCuesMap_.get(cue);
340
346
  goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
341
347
  if (cueRegistry.regionElement) {
348
+ if (cueRegistry.regionElement.contains(container)) {
349
+ cueRegistry.regionElement.removeChild(container);
350
+ }
342
351
  container.appendChild(cueRegistry.regionElement);
343
352
  cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
344
353
  } else {
@@ -200,6 +200,7 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
200
200
  /^[ae]c-3$/,
201
201
  /^ac-4$/,
202
202
  /^dts[cex]$/, // DTS Digital Surround (dtsc), DTS Express (dtse), DTS:X (dtsx)
203
+ /^iamf/,
203
204
  ];
204
205
 
205
206
 
@@ -806,8 +806,12 @@ shaka.util.StreamUtils = class {
806
806
  // currently don't support 'fLaC', while 'flac' is supported by most
807
807
  // major browsers.
808
808
  // See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
809
- if (codecs === 'fLaC' && !shaka.util.Platform.isSafari()) {
810
- return 'flac';
809
+ if (codecs.toLowerCase() == 'flac') {
810
+ if (!shaka.util.Platform.isSafari()) {
811
+ return 'flac';
812
+ } else {
813
+ return 'fLaC';
814
+ }
811
815
  }
812
816
 
813
817
  // The same is true for 'Opus'.
@@ -1273,8 +1277,6 @@ shaka.util.StreamUtils = class {
1273
1277
  * @return {shaka.extern.Track}
1274
1278
  */
1275
1279
  static html5TextTrackToTrack(textTrack) {
1276
- const CLOSED_CAPTION_MIMETYPE =
1277
- shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE;
1278
1280
  const StreamUtils = shaka.util.StreamUtils;
1279
1281
 
1280
1282
  /** @type {shaka.extern.Track} */
@@ -1283,7 +1285,8 @@ shaka.util.StreamUtils = class {
1283
1285
  track.type = 'text';
1284
1286
  track.originalTextId = textTrack.id;
1285
1287
  if (textTrack.kind == 'captions') {
1286
- track.mimeType = CLOSED_CAPTION_MIMETYPE;
1288
+ // See: https://github.com/shaka-project/shaka-player/issues/6233
1289
+ track.mimeType = 'unknown';
1287
1290
  }
1288
1291
  if (textTrack.kind == 'subtitles') {
1289
1292
  track.mimeType = 'text/vtt';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.3.15",
4
+ "version": "4.3.16",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [