shaka-player 4.7.11 → 4.7.12

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 (44) hide show
  1. package/README.md +2 -4
  2. package/dist/deps.js +3 -4
  3. package/dist/locales.js +31 -31
  4. package/dist/shaka-player.compiled.d.ts +12 -10
  5. package/dist/shaka-player.compiled.debug.d.ts +12 -10
  6. package/dist/shaka-player.compiled.debug.externs.js +78 -64
  7. package/dist/shaka-player.compiled.debug.js +168 -163
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +78 -64
  10. package/dist/shaka-player.compiled.js +1134 -1128
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +12 -10
  13. package/dist/shaka-player.ui.debug.d.ts +12 -10
  14. package/dist/shaka-player.ui.debug.externs.js +78 -64
  15. package/dist/shaka-player.ui.debug.js +178 -174
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +78 -64
  18. package/dist/shaka-player.ui.js +1264 -1258
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/externs/shaka/player.js +32 -19
  21. package/lib/abr/simple_abr_manager.js +21 -26
  22. package/lib/cast/cast_proxy.js +1 -3
  23. package/lib/cast/cast_sender.js +8 -11
  24. package/lib/cea/cea608_memory.js +1 -1
  25. package/lib/cea/ts_cea_parser.js +10 -1
  26. package/lib/dash/dash_parser.js +3 -0
  27. package/lib/hls/hls_parser.js +18 -16
  28. package/lib/media/drm_engine.js +6 -3
  29. package/lib/media/media_source_engine.js +25 -12
  30. package/lib/media/presentation_timeline.js +23 -0
  31. package/lib/media/segment_utils.js +10 -1
  32. package/lib/player.js +15 -2
  33. package/lib/polyfill/media_capabilities.js +20 -1
  34. package/lib/polyfill/mediasource.js +37 -14
  35. package/lib/text/vtt_text_parser.js +51 -51
  36. package/lib/transmuxer/h264.js +3 -3
  37. package/lib/transmuxer/h265.js +3 -3
  38. package/lib/transmuxer/ts_transmuxer.js +42 -3
  39. package/lib/util/manifest_parser_utils.js +1 -0
  40. package/lib/util/mp4_parser.js +57 -0
  41. package/lib/util/platform.js +4 -0
  42. package/lib/util/ts_parser.js +99 -10
  43. package/package.json +1 -1
  44. package/lib/polyfill/storage_estimate.js +0 -57
@@ -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.
@@ -6,10 +6,11 @@
6
6
 
7
7
  goog.provide('shaka.abr.SimpleAbrManager');
8
8
 
9
- goog.require('shaka.util.IReleasable');
10
9
  goog.require('goog.asserts');
11
10
  goog.require('shaka.abr.EwmaBandwidthEstimator');
12
11
  goog.require('shaka.log');
12
+ goog.require('shaka.util.EventManager');
13
+ goog.require('shaka.util.IReleasable');
13
14
  goog.require('shaka.util.StreamUtils');
14
15
  goog.require('shaka.util.Timer');
15
16
 
@@ -50,30 +51,30 @@ shaka.abr.SimpleAbrManager = class {
50
51
  /** @private {shaka.abr.EwmaBandwidthEstimator} */
51
52
  this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
52
53
 
53
- /** @private {?function():void} */
54
- this.onNetworkInformationChange_ = null;
54
+ /** @private {!shaka.util.EventManager} */
55
+ this.eventManager_ = new shaka.util.EventManager();
55
56
 
56
57
  // Some browsers implement the Network Information API, which allows
57
58
  // retrieving information about a user's network connection. We listen
58
59
  // to the change event to be able to make quick changes in case the type
59
60
  // of connectivity changes.
60
61
  if (navigator.connection && navigator.connection.addEventListener) {
61
- this.onNetworkInformationChange_ = () => {
62
- if (this.enabled_ && this.config_.useNetworkInformation) {
63
- this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
64
- if (this.config_) {
65
- this.bandwidthEstimator_.configure(this.config_.advanced);
66
- }
67
- const chosenVariant = this.chooseVariant();
68
- if (chosenVariant && navigator.onLine) {
69
- this.switch_(chosenVariant, this.config_.clearBufferSwitch,
70
- this.config_.safeMarginSwitch);
71
- }
72
- }
73
- };
74
-
75
- navigator.connection.addEventListener(
76
- 'change', this.onNetworkInformationChange_);
62
+ this.eventManager_.listen(
63
+ /** @type {EventTarget} */(navigator.connection),
64
+ 'change',
65
+ () => {
66
+ if (this.enabled_ && this.config_.useNetworkInformation) {
67
+ this.bandwidthEstimator_ = new shaka.abr.EwmaBandwidthEstimator();
68
+ if (this.config_) {
69
+ this.bandwidthEstimator_.configure(this.config_.advanced);
70
+ }
71
+ const chosenVariant = this.chooseVariant();
72
+ if (chosenVariant && navigator.onLine) {
73
+ this.switch_(chosenVariant, this.config_.clearBufferSwitch,
74
+ this.config_.safeMarginSwitch);
75
+ }
76
+ }
77
+ });
77
78
  }
78
79
 
79
80
  /**
@@ -151,13 +152,7 @@ shaka.abr.SimpleAbrManager = class {
151
152
  */
152
153
  release() {
153
154
  // stop() should already have been called for unload
154
-
155
- if (navigator.connection && navigator.connection.removeEventListener) {
156
- navigator.connection.removeEventListener(
157
- 'change', this.onNetworkInformationChange_);
158
- this.onNetworkInformationChange_ = null;
159
- }
160
-
155
+ this.eventManager_.release();
161
156
  this.resizeObserverTimer_ = null;
162
157
  }
163
158
 
@@ -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
 
@@ -293,7 +293,7 @@ shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars = new Map([
293
293
  */
294
294
  shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars = new Map([
295
295
  [0x30, '®'], [0x31, '°'], [0x32, '½'], [0x33, '¿'], [0x34, '™'], [0x35, '¢'],
296
- [0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, '⠀'], [0x3a, 'è'], [0x3b, 'â'],
296
+ [0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, ' '], [0x3a, 'è'], [0x3b, 'â'],
297
297
  [0x3c, 'ê'], [0x3d, 'î'], [0x3e, 'ô'], [0x3f, 'û'],
298
298
  ]);
299
299
 
@@ -26,6 +26,9 @@ shaka.cea.TsCeaParser = class {
26
26
  * @const {!shaka.cea.SeiProcessor}
27
27
  */
28
28
  this.seiProcessor_ = new shaka.cea.SeiProcessor();
29
+
30
+ /** @private {?shaka.util.TsParser} */
31
+ this.tsParser_ = null;
29
32
  }
30
33
 
31
34
  /**
@@ -41,6 +44,12 @@ shaka.cea.TsCeaParser = class {
41
44
  parse(mediaSegment) {
42
45
  const CeaUtils = shaka.cea.CeaUtils;
43
46
 
47
+ if (!this.tsParser_) {
48
+ this.tsParser_ = new shaka.util.TsParser();
49
+ } else {
50
+ this.tsParser_.clearData();
51
+ }
52
+
44
53
  /** @type {!Array<!shaka.extern.ICeaParser.CaptionPacket>} **/
45
54
  const captionPackets = [];
46
55
 
@@ -48,7 +57,7 @@ shaka.cea.TsCeaParser = class {
48
57
  if (!shaka.util.TsParser.probe(uint8ArrayData)) {
49
58
  return captionPackets;
50
59
  }
51
- const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
60
+ const tsParser = this.tsParser_.parse(uint8ArrayData);
52
61
  const codecs = tsParser.getCodecs();
53
62
  const videoNalus = tsParser.getVideoNalus();
54
63
  const validNaluTypes = [];
@@ -643,6 +643,9 @@ shaka.dash.DashParser = class {
643
643
  // best latency
644
644
  if (this.lowLatencyMode_) {
645
645
  minBufferTime = 0;
646
+ const presentationDelay = suggestedPresentationDelay != null ?
647
+ suggestedPresentationDelay : this.config_.defaultPresentationDelay;
648
+ presentationTimeline.setDelay(presentationDelay);
646
649
  }
647
650
 
648
651
  // These steps are not done on manifest update.
@@ -552,7 +552,7 @@ shaka.hls.HlsParser = class {
552
552
  if (goog.DEBUG) {
553
553
  const firstSequenceStartTime =
554
554
  mediaSequenceToStartTime.values().next().value;
555
- goog.asserts.assert(
555
+ shaka.log.warning(
556
556
  firstSequenceStartTime == segment0.startTime,
557
557
  'Sequence number map is not ordered as expected!');
558
558
  }
@@ -586,16 +586,18 @@ shaka.hls.HlsParser = class {
586
586
  // Drop any earlier references.
587
587
  const numSegmentsToDrop =
588
588
  this.minSequenceNumber_ - streamInfo.firstSequenceNumber;
589
- segmentIndex.dropFirstReferences(numSegmentsToDrop);
590
-
591
- // Now adjust timestamps back to begin at 0.
592
- const segmentN = segmentIndex.earliestReference();
593
- if (segmentN) {
594
- const streamOffset = -segmentN.startTime;
595
- // Modify all SegmentReferences equally.
596
- streamInfo.stream.segmentIndex.offset(streamOffset);
597
- // Update other parts of streamInfo the same way.
598
- this.offsetStreamInfo_(streamInfo, streamOffset);
589
+ if (numSegmentsToDrop > 0) {
590
+ segmentIndex.dropFirstReferences(numSegmentsToDrop);
591
+
592
+ // Now adjust timestamps back to begin at 0.
593
+ const segmentN = segmentIndex.earliestReference();
594
+ if (segmentN) {
595
+ const streamOffset = -segmentN.startTime;
596
+ // Modify all SegmentReferences equally.
597
+ streamInfo.stream.segmentIndex.offset(streamOffset);
598
+ // Update other parts of streamInfo the same way.
599
+ this.offsetStreamInfo_(streamInfo, streamOffset);
600
+ }
599
601
  }
600
602
  }
601
603
  }
@@ -986,17 +988,17 @@ shaka.hls.HlsParser = class {
986
988
  }
987
989
 
988
990
  if (!this.presentationTimeline_.isStartTimeLocked()) {
989
- const streamInfos = Array.from(this.uriToStreamInfosMap_.values());
990
- const activeStreamInfos =
991
- streamInfos.filter((s) => s.stream.segmentIndex);
992
- for (const streamInfo of activeStreamInfos) {
991
+ for (const streamInfo of this.uriToStreamInfosMap_.values()) {
992
+ if (!streamInfo.stream.segmentIndex) {
993
+ continue; // Not active.
994
+ }
993
995
  if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
994
996
  continue;
995
997
  }
996
998
  const firstReference = streamInfo.stream.segmentIndex.get(0);
997
999
  if (firstReference && firstReference.syncTime) {
998
1000
  const syncTime = firstReference.syncTime;
999
- this.presentationTimeline_.setPresentationStartTime(syncTime);
1001
+ this.presentationTimeline_.setInitialProgramDateTime(syncTime);
1000
1002
  }
1001
1003
  }
1002
1004
  }
@@ -697,9 +697,12 @@ shaka.media.DrmEngine = class {
697
697
  initDataOverride.initDataType, initDataOverride.initData);
698
698
  }
699
699
 
700
- // If we have no sessions, we need to resolve the promise right now or else
701
- // it will never get resolved.
702
- if (!initDatas.length) {
700
+ // If there were no sessions to load, we need to resolve the promise right
701
+ // now or else it will never get resolved.
702
+ // We determine this by checking areAllSessionsLoaded_, rather than checking
703
+ // the number of initDatas, since the newInitData method can reject init
704
+ // datas in some circumstances.
705
+ if (this.areAllSessionsLoaded_()) {
703
706
  this.allSessionsLoaded_.resolve();
704
707
  }
705
708
 
@@ -151,6 +151,9 @@ shaka.media.MediaSourceEngine = class {
151
151
 
152
152
  /** @private {?number} */
153
153
  this.lastDuration_ = null;
154
+
155
+ /** @private {?shaka.util.TsParser} */
156
+ this.tsParser_ = null;
154
157
  }
155
158
 
156
159
  /**
@@ -392,6 +395,8 @@ shaka.media.MediaSourceEngine = class {
392
395
 
393
396
  // This object is owned by Player
394
397
  this.lcevcDec_ = null;
398
+
399
+ this.tsParser_ = null;
395
400
  }
396
401
 
397
402
  /**
@@ -442,6 +447,8 @@ shaka.media.MediaSourceEngine = class {
442
447
  this.manifestType_ == shaka.media.ManifestParser.HLS &&
443
448
  !this.ignoreManifestTimestampsInSegmentsMode_;
444
449
 
450
+ this.tsParser_ = null;
451
+
445
452
  for (const contentType of streamsByType.keys()) {
446
453
  const stream = streamsByType.get(contentType);
447
454
  this.initSourceBuffer_(contentType, stream, stream.codecs);
@@ -460,10 +467,9 @@ shaka.media.MediaSourceEngine = class {
460
467
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
461
468
  * @param {shaka.extern.Stream} stream
462
469
  * @param {string} codecs
463
- * @param {boolean=} forceTransmux
464
470
  * @private
465
471
  */
466
- initSourceBuffer_(contentType, stream, codecs, forceTransmux = false) {
472
+ initSourceBuffer_(contentType, stream, codecs) {
467
473
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
468
474
 
469
475
  goog.asserts.assert(
@@ -475,7 +481,7 @@ shaka.media.MediaSourceEngine = class {
475
481
  if (contentType == ContentType.TEXT) {
476
482
  this.reinitText(mimeType, this.sequenceMode_, stream.external);
477
483
  } else {
478
- let needTransmux = this.config_.forceTransmux || forceTransmux;
484
+ let needTransmux = this.config_.forceTransmux;
479
485
  if (!shaka.media.Capabilities.isTypeSupported(mimeType) ||
480
486
  (!this.sequenceMode_ &&
481
487
  shaka.util.MimeUtils.RAW_FORMATS.includes(mimeType))) {
@@ -492,10 +498,8 @@ shaka.media.MediaSourceEngine = class {
492
498
  ContentType.VIDEO, (codecs || '').split(','));
493
499
  if (audioCodec && videoCodec) {
494
500
  this.needSplitMuxedContent_ = true;
495
- this.initSourceBuffer_(
496
- ContentType.AUDIO, stream, audioCodec, /* forceTransmux= */ true);
497
- this.initSourceBuffer_(
498
- ContentType.VIDEO, stream, videoCodec, /* forceTransmux= */ true);
501
+ this.initSourceBuffer_(ContentType.AUDIO, stream, audioCodec);
502
+ this.initSourceBuffer_(ContentType.VIDEO, stream, videoCodec);
499
503
  return;
500
504
  }
501
505
  const transmuxerPlugin =
@@ -789,7 +793,12 @@ shaka.media.MediaSourceEngine = class {
789
793
  }
790
794
  } else if (!mimeType.includes('/mp4') && !mimeType.includes('/webm') &&
791
795
  shaka.util.TsParser.probe(uint8ArrayData)) {
792
- const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
796
+ if (!this.tsParser_) {
797
+ this.tsParser_ = new shaka.util.TsParser();
798
+ } else {
799
+ this.tsParser_.clearData();
800
+ }
801
+ const tsParser = this.tsParser_.parse(uint8ArrayData);
793
802
  const startTime = tsParser.getStartTime(contentType);
794
803
  if (startTime != null) {
795
804
  timestamp = startTime;
@@ -1177,6 +1186,12 @@ shaka.media.MediaSourceEngine = class {
1177
1186
  return;
1178
1187
  }
1179
1188
 
1189
+ // Reset the promise in case the timestamp offset changed during
1190
+ // a period/discontinuity transition.
1191
+ if (contentType == ContentType.VIDEO) {
1192
+ this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
1193
+ }
1194
+
1180
1195
  // Queue an abort() to help MSE splice together overlapping segments.
1181
1196
  // We set appendWindowEnd when we change periods in DASH content, and the
1182
1197
  // period transition may result in overlap.
@@ -1857,8 +1872,7 @@ shaka.media.MediaSourceEngine = class {
1857
1872
  let needTransmux = this.config_.forceTransmux;
1858
1873
  if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
1859
1874
  (!this.sequenceMode_ &&
1860
- shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType)) ||
1861
- codecs.includes(',')) {
1875
+ shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))) {
1862
1876
  needTransmux = true;
1863
1877
  }
1864
1878
  const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
@@ -1939,8 +1953,7 @@ shaka.media.MediaSourceEngine = class {
1939
1953
  let needTransmux = this.config_.forceTransmux;
1940
1954
  if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
1941
1955
  (!this.sequenceMode_ &&
1942
- shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType)) ||
1943
- stream.codecs.includes(',')) {
1956
+ shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))) {
1944
1957
  needTransmux = true;
1945
1958
  }
1946
1959
  const newMimeTypeWithAllCodecs =
@@ -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
 
@@ -339,6 +342,26 @@ shaka.media.PresentationTimeline = class {
339
342
  }
340
343
 
341
344
 
345
+ /**
346
+ * Sets the initial program date time.
347
+ *
348
+ * @param {number} initialProgramDateTime
349
+ * @export
350
+ */
351
+ setInitialProgramDateTime(initialProgramDateTime) {
352
+ this.initialProgramDateTime_ = initialProgramDateTime;
353
+ }
354
+
355
+
356
+ /**
357
+ * @return {?number} The initial program date time in seconds.
358
+ * @export
359
+ */
360
+ getInitialProgramDateTime() {
361
+ return this.initialProgramDateTime_;
362
+ }
363
+
364
+
342
365
  /**
343
366
  * Gives PresentationTimeline a Stream's minimum segment start time.
344
367
  *
@@ -55,6 +55,7 @@ shaka.media.SegmentUtils = class {
55
55
  let hasVideo = false;
56
56
  switch (tsCodecs.audio) {
57
57
  case 'aac':
58
+ case 'aac-loas':
58
59
  codecs.push('mp4a.40.2');
59
60
  hasAudio = true;
60
61
  break;
@@ -70,6 +71,10 @@ shaka.media.SegmentUtils = class {
70
71
  codecs.push('ec-3');
71
72
  hasAudio = true;
72
73
  break;
74
+ case 'opus':
75
+ codecs.push('opus');
76
+ hasAudio = true;
77
+ break;
73
78
  }
74
79
  switch (tsCodecs.video) {
75
80
  case 'avc':
@@ -88,6 +93,10 @@ shaka.media.SegmentUtils = class {
88
93
  }
89
94
  hasVideo = true;
90
95
  break;
96
+ case 'av1':
97
+ codecs.push('av01.0.01M.08');
98
+ hasVideo = true;
99
+ break;
91
100
  }
92
101
  if (!codecs.length) {
93
102
  return null;
@@ -256,7 +265,7 @@ shaka.media.SegmentUtils = class {
256
265
  // This signals an encrypted sample, which we can go inside of to
257
266
  // find the codec used.
258
267
  // Note: If encrypted, you can only have audio or video, not both.
259
- .box('enca', Mp4Parser.visualSampleEntry)
268
+ .box('enca', Mp4Parser.audioSampleEntry)
260
269
  .box('encv', Mp4Parser.visualSampleEntry)
261
270
  .box('sinf', Mp4Parser.children)
262
271
  .box('frma', (box) => {
package/lib/player.js CHANGED
@@ -4494,7 +4494,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4494
4494
 
4495
4495
  if (this.manifest_) {
4496
4496
  const timeline = this.manifest_.presentationTimeline;
4497
- const startTime = timeline.getPresentationStartTime();
4497
+ const startTime = timeline.getInitialProgramDateTime() ||
4498
+ timeline.getPresentationStartTime();
4498
4499
  return new Date(/* ms= */ (startTime + presentationTime) * 1000);
4499
4500
  } else if (this.video_ && this.video_.getStartDate) {
4500
4501
  // Apple's native HLS gives us getStartDate(), which is only available if
@@ -6099,6 +6100,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6099
6100
  // change.
6100
6101
  if (this.playhead_) {
6101
6102
  this.playhead_.notifyOfBufferingChange();
6103
+ // Skip the initial buffer gap
6104
+ const startTime = this.mediaSourceEngine_.bufferStart(contentType);
6105
+ if (
6106
+ !this.isLive() &&
6107
+ // If not paused then GapJumpingController will handle this gap.
6108
+ this.video_.paused &&
6109
+ startTime != null &&
6110
+ startTime > 0 &&
6111
+ this.playhead_.getTime() < startTime
6112
+ ) {
6113
+ this.playhead_.setStartTime(startTime);
6114
+ }
6102
6115
  }
6103
6116
  this.pollBufferState_();
6104
6117
 
@@ -6934,7 +6947,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6934
6947
  * @export
6935
6948
  */
6936
6949
  // eslint-disable-next-line no-useless-concat
6937
- shaka.Player.version = 'v4.7.11' + '-uncompiled'; // x-release-please-version
6950
+ shaka.Player.version = 'v4.7.12' + '-uncompiled'; // x-release-please-version
6938
6951
 
6939
6952
  // Initialize the deprecation system using the version string we just set
6940
6953
  // on the player.
@@ -300,7 +300,17 @@ shaka.polyfill.MediaCapabilities = class {
300
300
  // this query is specifically for HDR.
301
301
  displayType += '; eotf=smpte2084';
302
302
  }
303
- return cast.__platform__.canDisplayType(displayType);
303
+ let result = false;
304
+ if (displayType in shaka.polyfill.MediaCapabilities
305
+ .memoizedCanDisplayTypeRequests_) {
306
+ result = shaka.polyfill.MediaCapabilities
307
+ .memoizedCanDisplayTypeRequests_[displayType];
308
+ } else {
309
+ result = cast.__platform__.canDisplayType(displayType);
310
+ shaka.polyfill.MediaCapabilities
311
+ .memoizedCanDisplayTypeRequests_[displayType] = result;
312
+ }
313
+ return result;
304
314
  }
305
315
 
306
316
  /**
@@ -337,6 +347,15 @@ shaka.polyfill.MediaCapabilities.originalMcap = null;
337
347
  */
338
348
  shaka.polyfill.MediaCapabilities.memoizedMediaKeySystemAccessRequests_ = {};
339
349
 
350
+ /**
351
+ * A cache that stores the canDisplayType result of calling
352
+ * `cast.__platform__.canDisplayType`.
353
+ *
354
+ * @type {(Object<(!string), (!boolean)>)}
355
+ * @export
356
+ */
357
+ shaka.polyfill.MediaCapabilities.memoizedCanDisplayTypeRequests_ = {};
358
+
340
359
  // Install at a lower priority than MediaSource polyfill, so that we have
341
360
  // MediaSource available first.
342
361
  shaka.polyfill.register(shaka.polyfill.MediaCapabilities.install, -1);