shaka-player 4.11.19 → 4.11.21

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.
@@ -1374,7 +1374,8 @@ shaka.extern.MssManifestConfiguration;
1374
1374
  * @property {number} defaultPresentationDelay
1375
1375
  * For DASH, it's a default <code>presentationDelay</code> value if
1376
1376
  * <code>suggestedPresentationDelay</code> is missing in the MPEG DASH
1377
- * manifest. The default value is <code>1.5 * minBufferTime</code> if not
1377
+ * manifest. The default value is the lower of <code>1.5 *
1378
+ * minBufferTime</code> and <code>segmentAvailabilityDuration</code> if not
1378
1379
  * configured or set as 0.
1379
1380
  * For HLS, the default value is 3 segments duration if not configured or
1380
1381
  * set as 0.
@@ -1798,10 +1799,12 @@ shaka.extern.LiveSyncConfiguration;
1798
1799
  * <br>
1799
1800
  * Defaults to <code>0.5</code>.
1800
1801
  * @property {boolean} infiniteLiveStreamDuration
1801
- * If <code>true</code>, the media source live duration
1802
- * set as a<code>Infinity</code>
1802
+ * If <code>true</code>, the media source duration of livestreams will be set
1803
+ * to <code>Infinity</code>. Otherwise, it will be set to a high but
1804
+ * non-infinite number (2^32).
1803
1805
  * <br>
1804
- * Defaults to <code>false</code>.
1806
+ * Defaults to <code>false</code> except on Safari 17 or above whose default
1807
+ * value is <code>true</code>.
1805
1808
  * @property {number} preloadNextUrlWindow
1806
1809
  * The window of time at the end of the presentation to begin preloading the
1807
1810
  * next URL, such as one specified by a urn:mpeg:dash:chaining:2016 element
@@ -169,24 +169,16 @@ shaka.cea.Cea708Service = class {
169
169
  const firstByte = dtvccPacket.readByte().value;
170
170
  const secondByte = dtvccPacket.readByte().value;
171
171
 
172
- const isTextBlock = (b) => {
173
- return (b >= 0x20 && b <= 0x7f) || (b >= 0xa0 && b <= 0xff);
172
+ const toHexString = (byteArray) => {
173
+ return byteArray.map((byte) => {
174
+ return ('0' + (byte & 0xFF).toString(16)).slice(-2);
175
+ }).join('');
174
176
  };
175
-
176
- if (isTextBlock(firstByte) && isTextBlock(secondByte)) {
177
- const toHexString = (byteArray) => {
178
- return byteArray.map((byte) => {
179
- return ('0' + (byte & 0xFF).toString(16)).slice(-2);
180
- }).join('');
181
- };
182
- const unicode = toHexString([firstByte, secondByte]);
183
- // Takes a unicode hex string and creates a single character.
184
- const char = String.fromCharCode(parseInt(unicode, 16));
185
- this.currentWindow_.setCharacter(char);
186
- return null;
187
- } else {
188
- dtvccPacket.rewind(2);
189
- }
177
+ const unicode = toHexString([firstByte, secondByte]);
178
+ // Takes a unicode hex string and creates a single character.
179
+ const char = String.fromCharCode(parseInt(unicode, 16));
180
+ this.currentWindow_.setCharacter(char);
181
+ return null;
190
182
  }
191
183
 
192
184
  const window = this.currentWindow_;
@@ -696,10 +696,11 @@ shaka.dash.DashParser = class {
696
696
  // that the complex logic of multi-Period eviction and period-flattening
697
697
  // is correct. See also:
698
698
  // https://github.com/shaka-project/shaka-player/issues/3169#issuecomment-823580634
699
+ const availabilityStart =
700
+ presentationTimeline.getSegmentAvailabilityStart();
699
701
  for (const stream of Object.values(this.streamMap_)) {
700
702
  if (stream.segmentIndex) {
701
- stream.segmentIndex.evict(
702
- presentationTimeline.getSegmentAvailabilityStart());
703
+ stream.segmentIndex.evict(availabilityStart);
703
704
  }
704
705
  }
705
706
  } else {
@@ -708,18 +709,28 @@ shaka.dash.DashParser = class {
708
709
  // feasible choices for the value. Nothing older than
709
710
  // timeShiftBufferDepth is still available, and anything less than
710
711
  // minBufferTime will cause buffering issues.
711
- //
712
- // We have decided that our default will be the configured value, or
713
- // 1.5 * minBufferTime if not configured. This is fairly conservative.
714
- // Content providers should provide a suggestedPresentationDelay whenever
715
- // possible to optimize the live streaming experience.
716
- const defaultPresentationDelay =
717
- this.config_.defaultPresentationDelay || minBufferTime * 1.5;
718
- const presentationDelay = suggestedPresentationDelay != null ?
719
- suggestedPresentationDelay : defaultPresentationDelay;
712
+ let delay = 0;
713
+ if (suggestedPresentationDelay != null) {
714
+ // 1. If a suggestedPresentationDelay is provided by the manifest, that
715
+ // will be used preferentially.
716
+ // This is given a minimum bound of segmentAvailabilityDuration.
717
+ // Content providers should provide a suggestedPresentationDelay
718
+ // whenever possible to optimize the live streaming experience.
719
+ delay = Math.min(
720
+ suggestedPresentationDelay,
721
+ segmentAvailabilityDuration || Infinity);
722
+ } else if (this.config_.defaultPresentationDelay > 0) {
723
+ // 2. If the developer provides a value for
724
+ // "manifest.defaultPresentationDelay", that is used as a fallback.
725
+ delay = this.config_.defaultPresentationDelay;
726
+ } else {
727
+ // 3. Otherwise, we default to the lower of segmentAvailabilityDuration
728
+ // and 1.5 * minBufferTime. This is fairly conservative.
729
+ delay = Math.min(
730
+ minBufferTime * 1.5, segmentAvailabilityDuration || Infinity);
731
+ }
720
732
  presentationTimeline = new shaka.media.PresentationTimeline(
721
- presentationStartTime, presentationDelay,
722
- this.config_.dash.autoCorrectDrift);
733
+ presentationStartTime, delay, this.config_.dash.autoCorrectDrift);
723
734
  }
724
735
 
725
736
  presentationTimeline.setStatic(mpdType == 'static');
@@ -131,24 +131,6 @@ shaka.dash.SegmentTemplate = class {
131
131
 
132
132
  shaka.log.debug(`New manifest ${periodStart} - ${periodEnd}`);
133
133
 
134
- /* When to fit segments. All refactors should honor/update this table:
135
- *
136
- * | dynamic | infinite | last | should | notes |
137
- * | | period | period | fit | |
138
- * | ------- | -------- | ------ | ------ | ------------------------- |
139
- * | F | F | X | T | typical VOD |
140
- * | F | T | X | X | impossible: infinite VOD |
141
- * | T | F | F | T | typical live, old period |
142
- * | T | F | T | F | typical IPR |
143
- * | T | T | F | X | impossible: old, infinite |
144
- * | T | T | T | F | typical live, new period |
145
- */
146
-
147
- // We never fit the final period of dynamic content, which could be
148
- // infinite live (with no limit to fit to) or IPR (which would expand the
149
- // most recent segment to the end of the presentation).
150
- const shouldFit = !(context.dynamic && context.periodInfo.isLastPeriod);
151
-
152
134
  if (!segmentIndex) {
153
135
  shaka.log.debug(`Creating TSI with end ${periodEnd}`);
154
136
  segmentIndex = new TimelineSegmentIndex(
@@ -160,14 +142,13 @@ shaka.dash.SegmentTemplate = class {
160
142
  periodStart,
161
143
  periodEnd,
162
144
  initSegmentReference,
163
- shouldFit,
164
145
  aesKey,
165
146
  context.representation.segmentSequenceCadence,
166
147
  );
167
148
  } else {
168
149
  const tsi = /** @type {!TimelineSegmentIndex} */(segmentIndex);
169
150
  tsi.appendTemplateInfo(
170
- info, periodStart, periodEnd, shouldFit, initSegmentReference);
151
+ info, periodStart, periodEnd, initSegmentReference);
171
152
 
172
153
  const availabilityStart =
173
154
  context.presentationTimeline.getSegmentAvailabilityStart();
@@ -177,7 +158,9 @@ shaka.dash.SegmentTemplate = class {
177
158
  if (info.timeline &&
178
159
  context.adaptationSet.contentType !== 'image' &&
179
160
  context.adaptationSet.contentType !== 'text') {
180
- const timeline = info.timeline;
161
+ const tsi = /** @type {!TimelineSegmentIndex} */(segmentIndex);
162
+ // getTimeline is the info.timeline but fitted to the period.
163
+ const timeline = tsi.getTimeline();
181
164
  context.presentationTimeline.notifyTimeRange(
182
165
  timeline,
183
166
  periodStart);
@@ -195,7 +178,7 @@ shaka.dash.SegmentTemplate = class {
195
178
  if (segmentIndex instanceof shaka.dash.TimelineSegmentIndex &&
196
179
  segmentIndex.isEmpty()) {
197
180
  segmentIndex.appendTemplateInfo(info, periodStart,
198
- periodEnd, shouldFit, initSegmentReference);
181
+ periodEnd, initSegmentReference);
199
182
  }
200
183
  return Promise.resolve(segmentIndex);
201
184
  },
@@ -744,40 +727,45 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
744
727
  * @param {number} periodStart
745
728
  * @param {number} periodEnd
746
729
  * @param {shaka.media.InitSegmentReference} initSegmentReference
747
- * @param {boolean} shouldFit
748
730
  * @param {shaka.extern.aesKey|undefined} aesKey
749
731
  * @param {number} segmentSequenceCadence
750
732
  */
751
733
  constructor(templateInfo, representationId, bandwidth, getBaseUris,
752
- urlParams, periodStart, periodEnd, initSegmentReference, shouldFit,
734
+ urlParams, periodStart, periodEnd, initSegmentReference,
753
735
  aesKey, segmentSequenceCadence) {
754
736
  super([]);
755
737
 
756
738
  /** @private {?shaka.dash.SegmentTemplate.SegmentTemplateInfo} */
757
739
  this.templateInfo_ = templateInfo;
740
+
758
741
  /** @private {?string} */
759
742
  this.representationId_ = representationId;
743
+
760
744
  /** @private {number} */
761
745
  this.bandwidth_ = bandwidth;
746
+
762
747
  /** @private {function():Array.<string>} */
763
748
  this.getBaseUris_ = getBaseUris;
749
+
764
750
  /** @private {function():string} */
765
751
  this.urlParams_ = urlParams;
752
+
766
753
  /** @private {number} */
767
754
  this.periodStart_ = periodStart;
755
+
768
756
  /** @private {number} */
769
757
  this.periodEnd_ = periodEnd;
758
+
770
759
  /** @private {shaka.media.InitSegmentReference} */
771
760
  this.initSegmentReference_ = initSegmentReference;
761
+
772
762
  /** @private {shaka.extern.aesKey|undefined} */
773
763
  this.aesKey_ = aesKey;
764
+
774
765
  /** @private {number} */
775
766
  this.segmentSequenceCadence_ = segmentSequenceCadence;
776
767
 
777
-
778
- if (shouldFit) {
779
- this.fitTimeline();
780
- }
768
+ this.fitTimeline();
781
769
  }
782
770
 
783
771
  /**
@@ -845,11 +833,9 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
845
833
  * @param {shaka.dash.SegmentTemplate.SegmentTemplateInfo} info
846
834
  * @param {number} periodStart
847
835
  * @param {number} periodEnd
848
- * @param {boolean} shouldFit
849
836
  * @param {shaka.media.InitSegmentReference} initSegmentReference
850
837
  */
851
- appendTemplateInfo(info, periodStart, periodEnd, shouldFit,
852
- initSegmentReference) {
838
+ appendTemplateInfo(info, periodStart, periodEnd, initSegmentReference) {
853
839
  this.updateInitSegmentReference(initSegmentReference);
854
840
  if (!this.templateInfo_) {
855
841
  this.templateInfo_ = info;
@@ -883,9 +869,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
883
869
  }
884
870
  }
885
871
 
886
- if (shouldFit) {
887
- this.fitTimeline();
888
- }
872
+ this.fitTimeline();
889
873
  }
890
874
 
891
875
  /**
@@ -930,14 +914,19 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
930
914
  return;
931
915
  }
932
916
  const timeline = this.templateInfo_.timeline;
933
- while (timeline.length) {
934
- const lastTimePeriod = timeline[timeline.length - 1];
935
- if (lastTimePeriod.start >= this.periodEnd_) {
936
- timeline.pop();
917
+ goog.asserts.assert(timeline, 'Timeline should be non-null!');
918
+ const newTimeline = [];
919
+ for (const range of timeline) {
920
+ if (range.start >= this.periodEnd_) {
921
+ // Starts after end of period.
922
+ } else if (range.end <= 0) {
923
+ // Ends before start of period.
937
924
  } else {
938
- break;
925
+ // Usable.
926
+ newTimeline.push(range);
939
927
  }
940
928
  }
929
+ this.templateInfo_.timeline = newTimeline;
941
930
 
942
931
  this.evict(this.periodStart_);
943
932
 
@@ -947,6 +936,19 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
947
936
  // find/get() methods whenever possible.
948
937
  }
949
938
 
939
+ /**
940
+ * Get the current timeline
941
+ * @return {!Array<shaka.media.PresentationTimeline.TimeRange>}
942
+ */
943
+ getTimeline() {
944
+ if (!this.templateInfo_) {
945
+ return [];
946
+ }
947
+ const timeline = this.templateInfo_.timeline;
948
+ goog.asserts.assert(timeline, 'Timeline should be non-null!');
949
+ return timeline;
950
+ }
951
+
950
952
  /**
951
953
  * @override
952
954
  */
@@ -314,4 +314,5 @@ shaka.hls.ManifestTextParser.SEGMENT_TAGS = [
314
314
  'EXT-X-DATERANGE',
315
315
  'EXT-X-MAP',
316
316
  'EXT-X-GAP',
317
+ 'EXT-X-TILES',
317
318
  ];
@@ -53,6 +53,9 @@ shaka.media.GapJumpingController = class {
53
53
  /** @private {shaka.util.EventManager} */
54
54
  this.eventManager_ = new shaka.util.EventManager();
55
55
 
56
+ /** @private {boolean} */
57
+ this.started_ = false;
58
+
56
59
  /** @private {boolean} */
57
60
  this.seekingEventReceived_ = false;
58
61
 
@@ -90,7 +93,7 @@ shaka.media.GapJumpingController = class {
90
93
  */
91
94
  this.gapJumpTimer_ = new shaka.util.Timer(() => {
92
95
  this.onPollGapJump_();
93
- }).tickEvery(this.config_.gapJumpTimerTime);
96
+ });
94
97
  }
95
98
 
96
99
 
@@ -134,16 +137,22 @@ shaka.media.GapJumpingController = class {
134
137
  * @param {number} startTime
135
138
  */
136
139
  onStarted(startTime) {
140
+ this.started_ = true;
137
141
  if (this.video_.seeking && !this.seekingEventReceived_) {
138
142
  this.seekingEventReceived_ = true;
139
143
  this.startTime_ = startTime;
140
144
  }
145
+ if (this.gapJumpTimer_) {
146
+ this.gapJumpTimer_.tickEvery(this.config_.gapJumpTimerTime);
147
+ }
148
+ this.onPollGapJump_();
141
149
  }
142
150
 
143
151
  /** Called when a seek has started. */
144
152
  onSeeking() {
145
153
  this.seekingEventReceived_ = true;
146
154
  this.hadSegmentAppended_ = false;
155
+ this.onPollGapJump_();
147
156
  }
148
157
 
149
158
 
@@ -167,6 +176,10 @@ shaka.media.GapJumpingController = class {
167
176
  if (this.video_.readyState == 0) {
168
177
  return;
169
178
  }
179
+ // Don't gap jump before playback started
180
+ if (!this.started_) {
181
+ return;
182
+ }
170
183
  // Do not gap jump if seeking has begun, but the seeking event has not
171
184
  // yet fired for this particular seek.
172
185
  if (this.video_.seeking) {
@@ -1301,20 +1301,24 @@ shaka.media.MediaSourceEngine = class {
1301
1301
  null));
1302
1302
  }
1303
1303
  }
1304
- operations.push(this.enqueueOperation_(
1305
- contentType,
1306
- () => this.setAppendWindow_(
1307
- contentType, appendWindowStart, appendWindowEnd),
1308
- null));
1309
- if (this.needSplitMuxedContent_) {
1304
+ if (appendWindowStart != 0 || appendWindowEnd != Infinity) {
1310
1305
  operations.push(this.enqueueOperation_(
1311
- ContentType.AUDIO,
1306
+ contentType,
1312
1307
  () => this.setAppendWindow_(
1313
- ContentType.AUDIO, appendWindowStart, appendWindowEnd),
1308
+ contentType, appendWindowStart, appendWindowEnd),
1314
1309
  null));
1310
+ if (this.needSplitMuxedContent_) {
1311
+ operations.push(this.enqueueOperation_(
1312
+ ContentType.AUDIO,
1313
+ () => this.setAppendWindow_(
1314
+ ContentType.AUDIO, appendWindowStart, appendWindowEnd),
1315
+ null));
1316
+ }
1315
1317
  }
1316
1318
 
1317
- await Promise.all(operations);
1319
+ if (operations.length) {
1320
+ await Promise.all(operations);
1321
+ }
1318
1322
  }
1319
1323
 
1320
1324
  /**
@@ -2522,7 +2522,7 @@ shaka.media.StreamingEngine = class {
2522
2522
  const segmentIndex = mediaState.stream.segmentIndex;
2523
2523
  if (segmentIndex instanceof shaka.media.MetaSegmentIndex) {
2524
2524
  segmentIndex.evict(
2525
- this.manifest_.presentationTimeline.getSeekRangeStart());
2525
+ this.manifest_.presentationTimeline.getSegmentAvailabilityStart());
2526
2526
  }
2527
2527
 
2528
2528
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
@@ -88,6 +88,7 @@ shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_ = {
88
88
  'ogg': 'audio/ogg',
89
89
  'mpg': 'video/mpeg',
90
90
  'mpeg': 'video/mpeg',
91
+ 'mov': 'video/quicktime',
91
92
  'm3u8': 'application/x-mpegurl',
92
93
  'mpd': 'application/dash+xml',
93
94
  'ism': 'application/vnd.ms-sstr+xml',
package/lib/player.js CHANGED
@@ -8109,7 +8109,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
8109
8109
  if (!this.video_ || this.video_.ended) {
8110
8110
  return true;
8111
8111
  }
8112
- return this.video_.currentTime >= this.seekRange().end;
8112
+ return this.fullyLoaded_ && !this.isLive() &&
8113
+ this.video_.currentTime >= this.seekRange().end;
8113
8114
  }
8114
8115
  };
8115
8116
 
@@ -8152,7 +8153,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
8152
8153
  * @export
8153
8154
  */
8154
8155
  // eslint-disable-next-line no-useless-concat, max-len
8155
- shaka.Player.version = 'v4.11.19' + '-uncompiled'; // x-release-please-version
8156
+ shaka.Player.version = 'v4.11.21' + '-uncompiled'; // x-release-please-version
8156
8157
 
8157
8158
  // Initialize the deprecation system using the version string we just set
8158
8159
  // on the player.
@@ -274,6 +274,11 @@ shaka.util.PlayerConfiguration = class {
274
274
  shaka.util.Platform.isTizen(),
275
275
  };
276
276
 
277
+ const safariVersion = shaka.util.Platform.safariVersion();
278
+ if (safariVersion && safariVersion >= 17) {
279
+ streaming.infiniteLiveStreamDuration = true;
280
+ }
281
+
277
282
  // WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
278
283
  // that respond slowly to seeking.
279
284
  // Therefore we should not seek when we detect a stall
@@ -531,7 +536,13 @@ shaka.util.PlayerConfiguration = class {
531
536
  if (hdrLevel == 'AUTO') {
532
537
  // Auto detect the ideal HDR level.
533
538
  if (window.matchMedia('(color-gamut: p3)').matches) {
534
- hdrLevel = 'PQ';
539
+ const someHLG = tracks.some((track) => {
540
+ if (track.hdr && track.hdr == 'HLG') {
541
+ return true;
542
+ }
543
+ return false;
544
+ });
545
+ hdrLevel = someHLG ? 'HLG' : 'PQ';
535
546
  } else {
536
547
  hdrLevel = 'SDR';
537
548
  }
package/lib/util/tXml.js CHANGED
@@ -556,8 +556,9 @@ shaka.util.TXml = class {
556
556
  const schemaNS = shaka.util.TXml.getKnownNameSpace(ns);
557
557
  const found = [];
558
558
  if (elem.children) {
559
+ const tagName = schemaNS ? `${schemaNS}:${name}` : name;
559
560
  for (const child of elem.children) {
560
- if (child && child.tagName === `${schemaNS}:${name}`) {
561
+ if (child && child.tagName === tagName) {
561
562
  found.push(child);
562
563
  }
563
564
  }
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.11.19",
4
+ "version": "4.11.21",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -100,6 +100,7 @@
100
100
  },
101
101
  "license": "Apache-2.0",
102
102
  "scripts": {
103
+ "build": "python build/all.py",
103
104
  "prepack": "clean-package",
104
105
  "postpack": "clean-package restore",
105
106
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
package/ui/controls.js CHANGED
@@ -1791,12 +1791,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
1791
1791
  goog.asserts.assert(
1792
1792
  this.seekBar_, 'Caller of seek_ must check for seekBar_ first!');
1793
1793
 
1794
- this.seekBar_.changeTo(currentTime);
1795
-
1796
- if (this.isOpaque()) {
1797
- // Only update the time and seek range if it's visible.
1798
- this.updateTimeAndSeekRange_();
1799
- }
1794
+ this.video_.currentTime = currentTime;
1795
+ this.updateTimeAndSeekRange_();
1800
1796
  }
1801
1797
 
1802
1798
  /**
@@ -61,7 +61,6 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
61
61
  this.bar.classList.add('shaka-range-element');
62
62
  this.bar.classList.add(...barClassNames);
63
63
  this.bar.type = 'range';
64
- // TODO(#2027): step=any causes keyboard nav problems on IE 11.
65
64
  this.bar.step = 'any';
66
65
  this.bar.min = '0';
67
66
  this.bar.max = '1';