shaka-player 4.9.22 → 4.9.23

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.
@@ -1006,12 +1006,16 @@ shaka.dash.DashParser = class {
1006
1006
  getContextIdsFromPath_(paths) {
1007
1007
  let periodId = '';
1008
1008
  let adaptationSetId = '';
1009
+ let adaptationSetPosition = -1;
1009
1010
  let representationId = '';
1010
1011
  for (const node of paths) {
1011
1012
  if (node.name === 'Period') {
1012
1013
  periodId = node.id;
1013
1014
  } else if (node.name === 'AdaptationSet') {
1014
1015
  adaptationSetId = node.id;
1016
+ if (node.position !== null) {
1017
+ adaptationSetPosition = node.position;
1018
+ }
1015
1019
  } else if (node.name === 'Representation') {
1016
1020
  representationId = node.id;
1017
1021
  }
@@ -1023,14 +1027,27 @@ shaka.dash.DashParser = class {
1023
1027
  if (representationId) {
1024
1028
  contextIds.push(periodId + ',' + representationId);
1025
1029
  } else {
1026
- for (const context of this.contextCache_.values()) {
1027
- if (context.period.id === periodId &&
1028
- context.adaptationSet.id === adaptationSetId &&
1029
- context.representation.id) {
1030
- contextIds.push(periodId + ',' + context.representation.id);
1030
+ if (adaptationSetId) {
1031
+ for (const context of this.contextCache_.values()) {
1032
+ if (context.period.id === periodId &&
1033
+ context.adaptationSet.id === adaptationSetId &&
1034
+ context.representation.id) {
1035
+ contextIds.push(periodId + ',' + context.representation.id);
1036
+ }
1037
+ }
1038
+ } else {
1039
+ if (adaptationSetPosition > -1) {
1040
+ for (const context of this.contextCache_.values()) {
1041
+ if (context.period.id === periodId &&
1042
+ context.adaptationSet.position === adaptationSetPosition &&
1043
+ context.representation.id) {
1044
+ contextIds.push(periodId + ',' + context.representation.id);
1045
+ }
1046
+ }
1031
1047
  }
1032
1048
  }
1033
1049
  }
1050
+
1034
1051
  return contextIds;
1035
1052
  }
1036
1053
 
@@ -1423,7 +1440,8 @@ shaka.dash.DashParser = class {
1423
1440
  const adaptationSetNodes =
1424
1441
  TXml.findChildren(periodInfo.node, 'AdaptationSet');
1425
1442
  const adaptationSets = adaptationSetNodes
1426
- .map((node) => this.parseAdaptationSet_(context, node))
1443
+ .map((node, position) =>
1444
+ this.parseAdaptationSet_(context, position, node))
1427
1445
  .filter(Functional.isNotNull);
1428
1446
 
1429
1447
  // For dynamic manifests, we use rep IDs internally, and they must be
@@ -1530,11 +1548,12 @@ shaka.dash.DashParser = class {
1530
1548
  * Parses an AdaptationSet XML element.
1531
1549
  *
1532
1550
  * @param {shaka.dash.DashParser.Context} context
1551
+ * @param {number} position
1533
1552
  * @param {!shaka.extern.xml.Node} elem The AdaptationSet element.
1534
1553
  * @return {?shaka.dash.DashParser.AdaptationInfo}
1535
1554
  * @private
1536
1555
  */
1537
- parseAdaptationSet_(context, elem) {
1556
+ parseAdaptationSet_(context, position, elem) {
1538
1557
  const TXml = shaka.util.TXml;
1539
1558
  const Functional = shaka.util.Functional;
1540
1559
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
@@ -1542,6 +1561,7 @@ shaka.dash.DashParser = class {
1542
1561
  const ContentProtection = shaka.dash.ContentProtection;
1543
1562
 
1544
1563
  context.adaptationSet = this.createFrame_(elem, context.period, null);
1564
+ context.adaptationSet.position = position;
1545
1565
 
1546
1566
  let main = false;
1547
1567
  const roleElements = TXml.findChildren(elem, 'Role');
@@ -2199,6 +2219,7 @@ shaka.dash.DashParser = class {
2199
2219
  pixelAspectRatio: frameRef.pixelAspectRatio,
2200
2220
  emsgSchemeIdUris: frameRef.emsgSchemeIdUris,
2201
2221
  id: frameRef.id,
2222
+ position: frameRef.position,
2202
2223
  numChannels: frameRef.numChannels,
2203
2224
  audioSamplingRate: frameRef.audioSamplingRate,
2204
2225
  availabilityTimeOffset: frameRef.availabilityTimeOffset,
@@ -2949,6 +2970,7 @@ shaka.dash.DashParser.RequestSegmentCallback;
2949
2970
  * pixelAspectRatio: (string|undefined),
2950
2971
  * emsgSchemeIdUris: !Array.<string>,
2951
2972
  * id: ?string,
2973
+ * position: (number|undefined),
2952
2974
  * language: ?string,
2953
2975
  * numChannels: ?number,
2954
2976
  * audioSamplingRate: ?number,
@@ -2988,6 +3010,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
2988
3010
  * emsg registered schemeIdUris.
2989
3011
  * @property {?string} id
2990
3012
  * The ID of the element.
3013
+ * @property {number|undefined} position
3014
+ * Position of the element used for indexing in case of no id
2991
3015
  * @property {?string} language
2992
3016
  * The original language of the element.
2993
3017
  * @property {?number} numChannels
@@ -120,9 +120,13 @@ shaka.hls.Tag = class {
120
120
  /**
121
121
  * Create the string key of the tag.
122
122
  *
123
+ * @param {boolean} keepAllAtributtes
123
124
  * @return {string}
124
125
  */
125
- getTagKey() {
126
+ getTagKey(keepAllAtributtes) {
127
+ if (keepAllAtributtes) {
128
+ return this.toString();
129
+ }
126
130
  const attributesToSkip = new Set()
127
131
  .add('AUDIO')
128
132
  .add('VIDEO')
@@ -1375,7 +1375,7 @@ shaka.hls.HlsParser = class {
1375
1375
 
1376
1376
  const groupedTags = {};
1377
1377
  for (const tag of mediaTags) {
1378
- const key = tag.getTagKey();
1378
+ const key = tag.getTagKey(!this.contentSteeringManager_);
1379
1379
  if (!groupedTags[key]) {
1380
1380
  groupedTags[key] = [tag];
1381
1381
  } else {
@@ -1456,7 +1456,7 @@ shaka.hls.HlsParser = class {
1456
1456
 
1457
1457
  const groupedTags = {};
1458
1458
  for (const tag of tags) {
1459
- const key = tag.getTagKey();
1459
+ const key = tag.getTagKey(!this.contentSteeringManager_);
1460
1460
  if (!groupedTags[key]) {
1461
1461
  groupedTags[key] = [tag];
1462
1462
  } else {
@@ -1082,13 +1082,15 @@ shaka.media.MediaSourceEngine = class {
1082
1082
  bufferedBefore, bufferedAfter);
1083
1083
  if (newBuffered) {
1084
1084
  const segmentDuration = reference.endTime - reference.startTime;
1085
+ const timeAdded = newBuffered.end - newBuffered.start;
1085
1086
  // Check end times instead of start times. We may be overwriting a
1086
1087
  // buffer and only the end changes, and that would be fine.
1087
1088
  // Also, exclude tiny segments. Sometimes alignment segments as small
1088
1089
  // as 33ms are seen in Google DAI content. For such tiny segments,
1089
1090
  // half a segment duration would be no issue.
1090
1091
  const offset = Math.abs(newBuffered.end - reference.endTime);
1091
- if (segmentDuration > 0.100 && offset > segmentDuration / 2) {
1092
+ if (segmentDuration > 0.100 && (offset > segmentDuration / 2 ||
1093
+ Math.abs(segmentDuration - timeAdded) > 0.030)) {
1092
1094
  shaka.log.error('Possible encoding problem detected!',
1093
1095
  'Unexpected buffered range for reference', reference,
1094
1096
  'from URIs', reference.getUris(),
@@ -1993,6 +1993,8 @@ shaka.media.StreamingEngine = class {
1993
1993
  const MimeUtils = shaka.util.MimeUtils;
1994
1994
  const StreamingEngine = shaka.media.StreamingEngine;
1995
1995
  const logPrefix = StreamingEngine.logPrefix_(mediaState);
1996
+ const nullLastReferences = mediaState.lastSegmentReference == null &&
1997
+ mediaState.lastInitSegmentReference == null;
1996
1998
 
1997
1999
  /** @type {!Array.<!Promise>} */
1998
2000
  const operations = [];
@@ -2190,8 +2192,19 @@ shaka.media.StreamingEngine = class {
2190
2192
  throw error;
2191
2193
  }
2192
2194
  };
2195
+ let initSegmentTime = reference.startTime;
2196
+ if (nullLastReferences) {
2197
+ const bufferEnd =
2198
+ this.playerInterface_.mediaSourceEngine.bufferEnd(mediaState.type);
2199
+ if (bufferEnd != null) {
2200
+ // Adjust init segment appendance time if we have something in
2201
+ // a buffer, i.e. due to running switchVariant() with non zero
2202
+ // safe margin value.
2203
+ initSegmentTime = bufferEnd;
2204
+ }
2205
+ }
2193
2206
  this.playerInterface_.onInitSegmentAppended(
2194
- reference.startTime, reference.initSegmentReference);
2207
+ initSegmentTime, reference.initSegmentReference);
2195
2208
  operations.push(append());
2196
2209
  }
2197
2210
  }
package/lib/player.js CHANGED
@@ -1728,7 +1728,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1728
1728
  this.dispatchEvent(shaka.Player.makeEvent_(
1729
1729
  shaka.util.FakeEvent.EventName.Loaded));
1730
1730
  } catch (error) {
1731
- if (error.code != shaka.util.Error.Code.LOAD_INTERRUPTED) {
1731
+ if (error && error.code != shaka.util.Error.Code.LOAD_INTERRUPTED) {
1732
1732
  await this.unload(/* initializeMediaSource= */ false);
1733
1733
  }
1734
1734
  throw error;
@@ -2787,8 +2787,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2787
2787
 
2788
2788
  // By setting |src| we are done "loading" with src=. We don't need to set
2789
2789
  // the current time because |playhead| will do that for us.
2790
- mediaElement.src = this.cmcdManager_.appendSrcData(
2791
- this.assetUri_, mimeType);
2790
+ let playbackUri = this.cmcdManager_.appendSrcData(this.assetUri_, mimeType);
2791
+ // Apply temporal clipping using playRangeStart and playRangeEnd based
2792
+ // in https://www.w3.org/TR/media-frags/
2793
+ if (!playbackUri.includes('#t=') &&
2794
+ (this.config_.playRangeStart > 0 ||
2795
+ isFinite(this.config_.playRangeEnd))) {
2796
+ playbackUri += '#t=';
2797
+ if (this.config_.playRangeStart > 0) {
2798
+ playbackUri += this.config_.playRangeStart;
2799
+ }
2800
+ if (isFinite(this.config_.playRangeEnd)) {
2801
+ playbackUri += ',' + this.config_.playRangeEnd;
2802
+ }
2803
+ }
2804
+ mediaElement.src = playbackUri;
2792
2805
 
2793
2806
  // Tizen 3 / WebOS won't load anything unless you call load() explicitly,
2794
2807
  // no matter the value of the preload attribute. This is harmful on some
@@ -6166,10 +6179,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6166
6179
  let updateState = 'playing';
6167
6180
  if (this.bufferObserver_.getState() == State.STARVING) {
6168
6181
  updateState = 'buffering';
6169
- } else if (this.video_.paused) {
6170
- updateState = 'paused';
6171
6182
  } else if (this.video_.ended) {
6172
6183
  updateState = 'ended';
6184
+ } else if (this.video_.paused) {
6185
+ updateState = 'paused';
6173
6186
  }
6174
6187
  const stateChanged = history.update(updateState);
6175
6188
 
@@ -7072,18 +7085,25 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7072
7085
  /**
7073
7086
  * Turn the media element's error object into a Shaka Player error object.
7074
7087
  *
7088
+ * @param {boolean=} printAllErrors
7075
7089
  * @return {shaka.util.Error}
7076
7090
  * @private
7077
7091
  */
7078
- videoErrorToShakaError_() {
7092
+ videoErrorToShakaError_(printAllErrors = true) {
7079
7093
  goog.asserts.assert(this.video_.error,
7080
7094
  'Video error expected, but missing!');
7081
7095
  if (!this.video_.error) {
7096
+ if (printAllErrors) {
7097
+ return new shaka.util.Error(
7098
+ shaka.util.Error.Severity.CRITICAL,
7099
+ shaka.util.Error.Category.MEDIA,
7100
+ shaka.util.Error.Code.VIDEO_ERROR);
7101
+ }
7082
7102
  return null;
7083
7103
  }
7084
7104
 
7085
7105
  const code = this.video_.error.code;
7086
- if (code == 1 /* MEDIA_ERR_ABORTED */) {
7106
+ if (!printAllErrors && code == 1 /* MEDIA_ERR_ABORTED */) {
7087
7107
  // Ignore this error code, which should only occur when navigating away or
7088
7108
  // deliberately stopping playback of HTTP content.
7089
7109
  return null;
@@ -7115,7 +7135,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7115
7135
  * @private
7116
7136
  */
7117
7137
  onVideoError_(event) {
7118
- const error = this.videoErrorToShakaError_();
7138
+ const error = this.videoErrorToShakaError_(/* printAllErrors= */ false);
7119
7139
  if (!error) {
7120
7140
  return;
7121
7141
  }
@@ -7484,7 +7504,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7484
7504
  * @export
7485
7505
  */
7486
7506
  // eslint-disable-next-line no-useless-concat
7487
- shaka.Player.version = 'v4.9.22' + '-uncompiled'; // x-release-please-version
7507
+ shaka.Player.version = 'v4.9.23' + '-uncompiled'; // x-release-please-version
7488
7508
 
7489
7509
  // Initialize the deprecation system using the version string we just set
7490
7510
  // on the player.
@@ -58,14 +58,11 @@ shaka.polyfill.AbortController = class {
58
58
 
59
59
  /**
60
60
  * @summary A polyfill for AbortSignal, part of the AbortController API.
61
- * @implements {AbortSignal}
61
+ * @extends AbortSignal
62
62
  */
63
- shaka.polyfill.AbortController.AbortSignal =
64
- class extends shaka.util.FakeEventTarget {
63
+ shaka.polyfill.AbortController.AbortSignal = class {
65
64
  /** */
66
65
  constructor() {
67
- super();
68
-
69
66
  /** @private {boolean} */
70
67
  this.aborted_ = false;
71
68
 
@@ -74,6 +71,24 @@ class extends shaka.util.FakeEventTarget {
74
71
 
75
72
  /** @type {?function(!Event)} */
76
73
  this.onabort = null;
74
+
75
+ /** @private {!shaka.util.FakeEventTarget} */
76
+ this.eventTarget_ = new shaka.util.FakeEventTarget();
77
+ }
78
+
79
+ /** @override */
80
+ addEventListener(type, listener, options) {
81
+ return this.eventTarget_.addEventListener(type, listener, options);
82
+ }
83
+
84
+ /** @override */
85
+ removeEventListener(type, listener, options) {
86
+ return this.eventTarget_.removeEventListener(type, listener, options);
87
+ }
88
+
89
+ /** @override */
90
+ dispatchEvent(event) {
91
+ return this.eventTarget_.dispatchEvent(event);
77
92
  }
78
93
 
79
94
  /** @override */
@@ -81,12 +96,12 @@ class extends shaka.util.FakeEventTarget {
81
96
  return this.aborted_;
82
97
  }
83
98
 
84
- /** @return {*} */
99
+ /** @override */
85
100
  get reason() {
86
101
  return this.reason_;
87
102
  }
88
103
 
89
- /** @return {void} */
104
+ /** @override */
90
105
  throwIfAborted() {
91
106
  if (this.aborted_) {
92
107
  throw this.reason_;
@@ -119,21 +134,14 @@ class extends shaka.util.FakeEventTarget {
119
134
  this.dispatchEvent(event);
120
135
  }
121
136
 
122
-
123
- /**
124
- * @param {*=} reason
125
- * @return {!AbortSignal}
126
- */
137
+ /** @override */
127
138
  static abort(reason) {
128
139
  const signal = new shaka.polyfill.AbortController.AbortSignal();
129
140
  signal.doAbort_(reason);
130
141
  return signal;
131
142
  }
132
143
 
133
- /**
134
- * @param {number} timeMs
135
- * @return {!AbortSignal}
136
- */
144
+ /** @override */
137
145
  static timeout(timeMs) {
138
146
  const signal = new shaka.polyfill.AbortController.AbortSignal();
139
147
 
@@ -842,7 +842,7 @@ shaka.text.UITextDisplayer = class {
842
842
  if (cue.position !== null) {
843
843
  style.position = 'absolute';
844
844
  if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
845
- style.right = cue.position + '%';
845
+ style.right = (100 - cue.position) + '%';
846
846
  style.width = 'auto';
847
847
  } else {
848
848
  style.bottom = cue.position + '%';
@@ -1382,6 +1382,13 @@ shaka.util.StreamUtils = class {
1382
1382
  track.hdr = video.hdr || null;
1383
1383
  track.colorGamut = video.colorGamut || null;
1384
1384
  track.videoLayout = video.videoLayout || null;
1385
+
1386
+ if (videoCodec.includes(',')) {
1387
+ track.channelsCount = video.channelsCount;
1388
+ track.audioSamplingRate = video.audioSamplingRate;
1389
+ track.spatialAudio = video.spatialAudio;
1390
+ track.originalLanguage = video.originalLanguage;
1391
+ }
1385
1392
  }
1386
1393
 
1387
1394
  if (audio) {
package/lib/util/tXml.js CHANGED
@@ -753,11 +753,18 @@ shaka.util.TXml = class {
753
753
  // We only want the id attribute in which case
754
754
  // /'(.*?)'/ will suffice to get it.
755
755
  const idAttr = path.match(/(@id='(.*?)')/);
756
+ const tAttr = path.match(/(@t='(\d+)')/);
757
+ const numberIndex = path.match(/(@n='(\d+)')/);
756
758
  const position = path.match(/\[(\d+)\]/);
757
759
  returnPaths.push({
758
760
  name: nodeName[0],
759
761
  id: idAttr ?
760
762
  idAttr[0].match(/'(.*?)'/)[0].replace(/'/gm, '') : null,
763
+ t: tAttr ?
764
+ Number(tAttr[0].match(/'(.*?)'/)[0].replace(/'/gm, '')) : null,
765
+ n: numberIndex ?
766
+ Number(numberIndex[0].match(/'(.*?)'/)[0].replace(/'/gm, '')):
767
+ null,
761
768
  // position is counted from 1, so make it readable for devs
762
769
  position: position ? Number(position[1]) - 1 : null,
763
770
  attribute: path.split('/@')[1] || null,
@@ -787,6 +794,14 @@ shaka.util.TXml = class {
787
794
  const position = patchNode.attributes['pos'] || null;
788
795
 
789
796
  let index = lastNode.position;
797
+ if (index == null) {
798
+ if (lastNode.t !== null) {
799
+ index = TXml.nodePositionByAttribute_(nodes, 't', lastNode.t);
800
+ }
801
+ if (lastNode.n !== null) {
802
+ index = TXml.nodePositionByAttribute_(nodes, 'n', lastNode.n);
803
+ }
804
+ }
790
805
  if (index === null) {
791
806
  index = position === 'prepend' ? 0 : nodes.length;
792
807
  } else if (position === 'prepend') {
@@ -798,7 +813,7 @@ shaka.util.TXml = class {
798
813
  const attribute = lastNode.attribute;
799
814
 
800
815
  // Modify attribute
801
- if (attribute) {
816
+ if (attribute && nodes[index]) {
802
817
  TXml.modifyNodeAttribute(nodes[index], action, attribute,
803
818
  TXml.getContents(patchNode) || '');
804
819
  // Rearrange nodes
@@ -814,6 +829,28 @@ shaka.util.TXml = class {
814
829
  }
815
830
 
816
831
 
832
+ /**
833
+ * Search the node index by the t attribute
834
+ * and return the index. if not found return null
835
+ * @param {!Array<shaka.extern.xml.Node>} nodes
836
+ * @param {!string} attribute
837
+ * @param {!number} value
838
+ * @private
839
+ */
840
+ static nodePositionByAttribute_(nodes, attribute, value) {
841
+ let index = 0;
842
+ for (const node of nodes) {
843
+ const attrs = node.attributes;
844
+ const val = Number(attrs[attribute]);
845
+ if (val === value) {
846
+ return index;
847
+ }
848
+ index++;
849
+ }
850
+ return null;
851
+ }
852
+
853
+
817
854
  /**
818
855
  * @param {!shaka.extern.xml.Node} node
819
856
  * @param {string} action
@@ -875,6 +912,8 @@ shaka.util.TXml.knownNameSpaces_ = new Map([]);
875
912
  * @typedef {{
876
913
  * name: string,
877
914
  * id: ?string,
915
+ * t: ?number,
916
+ * n: ?number,
878
917
  * position: ?number,
879
918
  * attribute: ?string
880
919
  * }}
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.9.22",
4
+ "version": "4.9.23",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -31,8 +31,8 @@
31
31
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
32
32
  "esprima": "^4.0.1",
33
33
  "fontfaceonload": "^1.0.2",
34
- "google-closure-compiler-java": "^20221102.0.0",
35
- "google-closure-deps": "^20221102.0.0",
34
+ "google-closure-compiler-java": "^20240317.0.0",
35
+ "google-closure-deps": "^20230802.0.0",
36
36
  "google-closure-library": "^20221102.0.0",
37
37
  "htmlhint": "^1.1.3",
38
38
  "jasmine-ajax": "^4.0.0",
@@ -83,7 +83,10 @@ shaka.ui.PresentationTimeTracker = class extends shaka.ui.Element {
83
83
  const seekRangeSize = seekRange.end - seekRange.start;
84
84
  const Utils = shaka.ui.Utils;
85
85
 
86
- if (this.player.isLive()) {
86
+ if (!isFinite(seekRangeSize)) {
87
+ this.setValue_(this.localization.resolve(shaka.ui.Locales.Ids.LIVE));
88
+ this.currentTime_.disabled = true;
89
+ } else if (this.player.isLive()) {
87
90
  // The amount of time we are behind the live edge.
88
91
  const behindLive = Math.floor(seekRange.end - displayTime);
89
92
  displayTime = Math.max(0, behindLive);
@@ -259,9 +259,17 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
259
259
  if (height == 2160) {
260
260
  text = '4K';
261
261
  }
262
- const frameRate = track.frameRate;
263
- if (frameRate && (frameRate >= 50 || frameRate <= 20)) {
264
- text += Math.round(track.frameRate);
262
+ const frameRates = new Set();
263
+ for (const item of tracks) {
264
+ if (item.frameRate) {
265
+ frameRates.add(Math.round(item.frameRate));
266
+ }
267
+ }
268
+ if (frameRates.size > 1) {
269
+ const frameRate = track.frameRate;
270
+ if (frameRate && (frameRate >= 50 || frameRate <= 20)) {
271
+ text += Math.round(track.frameRate);
272
+ }
265
273
  }
266
274
  if (track.hdr == 'PQ' || track.hdr == 'HLG') {
267
275
  text += ' (HDR)';
package/ui/seek_bar.js CHANGED
@@ -470,7 +470,8 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
470
470
  const seekRangeSize = seekRange.end - seekRange.start;
471
471
 
472
472
  if (this.player.isLive() &&
473
- seekRangeSize < shaka.ui.Constants.MIN_SEEK_WINDOW_TO_SHOW_SEEKBAR) {
473
+ (seekRangeSize < shaka.ui.Constants.MIN_SEEK_WINDOW_TO_SHOW_SEEKBAR ||
474
+ !isFinite(seekRangeSize))) {
474
475
  return false;
475
476
  }
476
477