shaka-player 4.9.21 → 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.
package/externs/xbox.js CHANGED
@@ -41,3 +41,23 @@ Windows.Media.Protection.ProtectionCapabilityResult = {
41
41
  maybe: 'Maybe',
42
42
  probably: 'Probably',
43
43
  };
44
+
45
+
46
+ /** @const */
47
+ var chrome = {};
48
+
49
+
50
+ /** @const */
51
+ chrome.webview = {};
52
+
53
+
54
+ /** @const */
55
+ chrome.webview.hostObjects = {};
56
+
57
+
58
+ /** @const */
59
+ chrome.webview.hostObjects.sync = {};
60
+
61
+
62
+ /** @const */
63
+ chrome.webview.hostObjects.sync.Windows = Windows;
@@ -107,7 +107,7 @@ shaka.abr.SimpleAbrManager = class {
107
107
 
108
108
  /** @private {shaka.util.Timer} */
109
109
  this.resizeObserverTimer_ = new shaka.util.Timer(() => {
110
- if (this.config_.restrictToElementSize) {
110
+ if (this.enabled_ && this.config_.restrictToElementSize) {
111
111
  const chosenVariant = this.chooseVariant();
112
112
  if (chosenVariant) {
113
113
  this.switch_(chosenVariant, this.config_.clearBufferSwitch,
@@ -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 {
@@ -99,8 +99,8 @@ shaka.media.ClosedCaptionParser = class {
99
99
  // This is necessary when a new Period comes in DASH or a discontinuity
100
100
  // in HLS.
101
101
  this.reset();
102
- this.ceaParser_.init(initSegment);
103
102
  }
103
+ this.ceaParser_.init(initSegment);
104
104
  }
105
105
 
106
106
  /**
@@ -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(),
@@ -2012,7 +2014,7 @@ shaka.media.MediaSourceEngine = class {
2012
2014
  return false;
2013
2015
  }
2014
2016
  const MimeUtils = shaka.util.MimeUtils;
2015
- const currentCodec = MimeUtils.getCodecBase(
2017
+ const currentCodec = MimeUtils.getNormalizedCodec(
2016
2018
  MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
2017
2019
  const currentBasicType = MimeUtils.getBasicType(
2018
2020
  this.sourceBufferTypes_[contentType]);
@@ -2054,7 +2056,7 @@ shaka.media.MediaSourceEngine = class {
2054
2056
  }
2055
2057
  }
2056
2058
 
2057
- const newCodec = MimeUtils.getCodecBase(
2059
+ const newCodec = MimeUtils.getNormalizedCodec(
2058
2060
  MimeUtils.getCodecs(newMimeType));
2059
2061
  const newBasicType = MimeUtils.getBasicType(newMimeType);
2060
2062
 
@@ -2113,7 +2115,7 @@ shaka.media.MediaSourceEngine = class {
2113
2115
  return false;
2114
2116
  }
2115
2117
  const MimeUtils = shaka.util.MimeUtils;
2116
- const currentCodec = MimeUtils.getCodecBase(
2118
+ const currentCodec = MimeUtils.getNormalizedCodec(
2117
2119
  MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
2118
2120
  const currentBasicType = MimeUtils.getBasicType(
2119
2121
  this.sourceBufferTypes_[contentType]);
@@ -2140,7 +2142,7 @@ shaka.media.MediaSourceEngine = class {
2140
2142
  }
2141
2143
  }
2142
2144
 
2143
- const newCodec = MimeUtils.getCodecBase(
2145
+ const newCodec = MimeUtils.getNormalizedCodec(
2144
2146
  MimeUtils.getCodecs(newMimeType));
2145
2147
  const newBasicType = MimeUtils.getBasicType(newMimeType);
2146
2148
 
@@ -15,9 +15,10 @@ goog.require('shaka.log');
15
15
  goog.require('shaka.media.InitSegmentReference');
16
16
  goog.require('shaka.media.ManifestParser');
17
17
  goog.require('shaka.media.MediaSourceEngine');
18
+ goog.require('shaka.media.SegmentIndex');
18
19
  goog.require('shaka.media.SegmentIterator');
19
- goog.require('shaka.media.SegmentReference');
20
20
  goog.require('shaka.media.SegmentPrefetch');
21
+ goog.require('shaka.media.SegmentReference');
21
22
  goog.require('shaka.net.Backoff');
22
23
  goog.require('shaka.net.NetworkingEngine');
23
24
  goog.require('shaka.util.BufferUtils');
@@ -73,6 +74,20 @@ shaka.media.StreamingEngine = class {
73
74
  /** @private {?shaka.extern.StreamingConfiguration} */
74
75
  this.config_ = null;
75
76
 
77
+ /**
78
+ * Retains deferred SegmentIndex objects for streams which were switched
79
+ * away from during an ongoing fetchAndAppend_().
80
+ * They are released and cleared from this map when the next onUpdate_() for
81
+ * a stream's content type occurs.
82
+ * This is because at that point, it is guaranteed no logic is actively
83
+ * using information for these old streams.
84
+ *
85
+ * Lastly, the operations are stored in arrays in the off chance multiple
86
+ * ABR switches happen during an ongoing fetchAndAppend_().
87
+ * @private {!Map.<string, !Array.<?shaka.media.SegmentIndex>>}
88
+ */
89
+ this.deferredCloseSegmentIndex_ = new Map();
90
+
76
91
  /** @private {number} */
77
92
  this.bufferingGoalScale_ = 1;
78
93
 
@@ -161,6 +176,7 @@ shaka.media.StreamingEngine = class {
161
176
  state.segmentPrefetch.clearAll();
162
177
  state.segmentPrefetch = null;
163
178
  }
179
+ this.handleDeferredCloseSegmentIndexes_(state);
164
180
  }
165
181
  for (const prefetch of this.audioPrefetchMap_.values()) {
166
182
  prefetch.clearAll();
@@ -170,6 +186,7 @@ shaka.media.StreamingEngine = class {
170
186
 
171
187
  this.mediaStates_.clear();
172
188
  this.audioPrefetchMap_.clear();
189
+ this.deferredCloseSegmentIndex_.clear();
173
190
 
174
191
  this.playerInterface_ = null;
175
192
  this.manifest_ = null;
@@ -504,6 +521,29 @@ shaka.media.StreamingEngine = class {
504
521
  }
505
522
 
506
523
 
524
+ /**
525
+ * Handles deferred releases of old SegmentIndexes for the mediaState's
526
+ * content type from a previous update.
527
+ * @param {!shaka.media.StreamingEngine.MediaState_} mediaState
528
+ * @private
529
+ */
530
+ handleDeferredCloseSegmentIndexes_(mediaState) {
531
+ for (const [key, value] of this.deferredCloseSegmentIndex_.entries()) {
532
+ const streamId = /** @type {string} */ (key);
533
+ const segmentIndexArray =
534
+ /** @type {!Array.<?shaka.media.SegmentIndex>} */ (value);
535
+ if (streamId.includes(mediaState.type)) {
536
+ for (const segmentIndex of segmentIndexArray) {
537
+ if (segmentIndex) {
538
+ segmentIndex.release();
539
+ }
540
+ }
541
+ this.deferredCloseSegmentIndex_.delete(streamId);
542
+ }
543
+ }
544
+ }
545
+
546
+
507
547
  /**
508
548
  * Switches to the given Stream. |stream| may be from any Variant.
509
549
  *
@@ -572,11 +612,30 @@ shaka.media.StreamingEngine = class {
572
612
  fullMimeType, this.manifest_.sequenceMode, stream.external);
573
613
  }
574
614
 
575
- // Releases the segmentIndex of the old stream.
615
+ // Releases the segmentIndex of the old stream if safe to do so.
576
616
  // Do not close segment indexes we are prefetching.
577
617
  if (!this.audioPrefetchMap_.has(mediaState.stream)) {
578
618
  if (mediaState.stream.closeSegmentIndex) {
579
- mediaState.stream.closeSegmentIndex();
619
+ if (mediaState.performingUpdate) {
620
+ const oldStreamTag =
621
+ shaka.media.StreamingEngine.logPrefix_(mediaState);
622
+ // The ongoing update is still using the old stream's segment
623
+ // reference information.
624
+ // If we close the old stream now, the update will not complete
625
+ // correctly.
626
+ // onUpdate_() will resume the closeSegmentIndex() operation for the
627
+ // old stream when the update has finished.
628
+ if (!this.deferredCloseSegmentIndex_.has(oldStreamTag)) {
629
+ this.deferredCloseSegmentIndex_.set(
630
+ oldStreamTag, [mediaState.stream.segmentIndex]);
631
+ } else {
632
+ // We switched from stream A -> B -> back to A.
633
+ this.deferredCloseSegmentIndex_.get(oldStreamTag).push(
634
+ mediaState.stream.segmentIndex);
635
+ }
636
+ } else {
637
+ mediaState.stream.closeSegmentIndex();
638
+ }
580
639
  }
581
640
  }
582
641
 
@@ -584,8 +643,8 @@ shaka.media.StreamingEngine = class {
584
643
  mediaState.segmentIterator = null;
585
644
  mediaState.adaptation = !!adaptation;
586
645
 
587
- const streamTag = shaka.media.StreamingEngine.logPrefix_(mediaState);
588
- shaka.log.debug('switch: switching to Stream ' + streamTag);
646
+ const newStreamTag = shaka.media.StreamingEngine.logPrefix_(mediaState);
647
+ shaka.log.debug('switch: switching to Stream ' + newStreamTag);
589
648
 
590
649
  if (clearBuffer) {
591
650
  if (mediaState.clearingBuffer) {
@@ -1169,6 +1228,8 @@ shaka.media.StreamingEngine = class {
1169
1228
  return;
1170
1229
  }
1171
1230
 
1231
+ this.handleDeferredCloseSegmentIndexes_(mediaState);
1232
+
1172
1233
  // Make sure the segment index exists. If not, create the segment index.
1173
1234
  if (!mediaState.stream.segmentIndex) {
1174
1235
  const thisStream = mediaState.stream;
@@ -1343,6 +1404,8 @@ shaka.media.StreamingEngine = class {
1343
1404
  return this.config_.updateIntervalSeconds / 2;
1344
1405
  }
1345
1406
 
1407
+ // Lack of segment iterator is the best indicator stream has changed.
1408
+ const streamChanged = !mediaState.segmentIterator;
1346
1409
  const reference = this.getSegmentReferenceNeeded_(
1347
1410
  mediaState, presentationTime, bufferEnd);
1348
1411
  if (!reference) {
@@ -1352,6 +1415,14 @@ shaka.media.StreamingEngine = class {
1352
1415
  // a SegmentReference eventually.
1353
1416
  return this.config_.updateIntervalSeconds;
1354
1417
  }
1418
+ // Get media state adaptation and reset this value. By guarding it during
1419
+ // actual stream change we ensure it won't be cleaned by accident on regular
1420
+ // append.
1421
+ let adaptation = false;
1422
+ if (streamChanged && mediaState.adaptation) {
1423
+ adaptation = true;
1424
+ mediaState.adaptation = false;
1425
+ }
1355
1426
 
1356
1427
  // Do not let any one stream get far ahead of any other.
1357
1428
  let minTimeNeeded = Infinity;
@@ -1390,7 +1461,8 @@ shaka.media.StreamingEngine = class {
1390
1461
  mediaState.segmentPrefetch.prefetchSegmentsByTime(reference.startTime);
1391
1462
  }
1392
1463
 
1393
- const p = this.fetchAndAppend_(mediaState, presentationTime, reference);
1464
+ const p = this.fetchAndAppend_(mediaState, presentationTime, reference,
1465
+ adaptation);
1394
1466
  p.catch(() => {}); // TODO(#1993): Handle asynchronous errors.
1395
1467
  return null;
1396
1468
  }
@@ -1512,9 +1584,10 @@ shaka.media.StreamingEngine = class {
1512
1584
  * @param {!shaka.media.StreamingEngine.MediaState_} mediaState
1513
1585
  * @param {number} presentationTime
1514
1586
  * @param {!shaka.media.SegmentReference} reference
1587
+ * @param {boolean} adaptation
1515
1588
  * @private
1516
1589
  */
1517
- async fetchAndAppend_(mediaState, presentationTime, reference) {
1590
+ async fetchAndAppend_(mediaState, presentationTime, reference, adaptation) {
1518
1591
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1519
1592
  const StreamingEngine = shaka.media.StreamingEngine;
1520
1593
  const logPrefix = StreamingEngine.logPrefix_(mediaState);
@@ -1543,7 +1616,7 @@ shaka.media.StreamingEngine = class {
1543
1616
  shaka.util.Error.Category.NETWORK,
1544
1617
  shaka.util.Error.Code.SEGMENT_MISSING);
1545
1618
  }
1546
- await this.initSourceBuffer_(mediaState, reference);
1619
+ await this.initSourceBuffer_(mediaState, reference, adaptation);
1547
1620
  this.destroyer_.ensureNotDestroyed();
1548
1621
  if (this.fatalError_) {
1549
1622
  return;
@@ -1598,7 +1671,7 @@ shaka.media.StreamingEngine = class {
1598
1671
  remaining = remaining.subarray(offset);
1599
1672
  await this.append_(
1600
1673
  mediaState, presentationTime, stream, reference, dataToAppend,
1601
- /* isChunkedData= */ true);
1674
+ /* isChunkedData= */ true, adaptation);
1602
1675
 
1603
1676
  if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
1604
1677
  mediaState.segmentPrefetch.prefetchSegmentsByTime(
@@ -1637,8 +1710,8 @@ shaka.media.StreamingEngine = class {
1637
1710
  return;
1638
1711
  }
1639
1712
 
1640
- await this.append_(
1641
- mediaState, presentationTime, stream, reference, result);
1713
+ await this.append_(mediaState, presentationTime, stream, reference,
1714
+ result, /* chunkedData= */ false, adaptation);
1642
1715
  }
1643
1716
 
1644
1717
  if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
@@ -1669,8 +1742,8 @@ shaka.media.StreamingEngine = class {
1669
1742
  return;
1670
1743
  }
1671
1744
 
1672
- await this.append_(
1673
- mediaState, presentationTime, stream, reference, result);
1745
+ await this.append_(mediaState, presentationTime, stream, reference,
1746
+ result, /* chunkedData= */ false, adaptation);
1674
1747
  }
1675
1748
 
1676
1749
  this.destroyer_.ensureNotDestroyed();
@@ -1911,14 +1984,17 @@ shaka.media.StreamingEngine = class {
1911
1984
  *
1912
1985
  * @param {shaka.media.StreamingEngine.MediaState_} mediaState
1913
1986
  * @param {!shaka.media.SegmentReference} reference
1987
+ * @param {boolean} adaptation
1914
1988
  * @return {!Promise}
1915
1989
  * @private
1916
1990
  */
1917
- async initSourceBuffer_(mediaState, reference) {
1991
+ async initSourceBuffer_(mediaState, reference, adaptation) {
1918
1992
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1919
1993
  const MimeUtils = shaka.util.MimeUtils;
1920
1994
  const StreamingEngine = shaka.media.StreamingEngine;
1921
1995
  const logPrefix = StreamingEngine.logPrefix_(mediaState);
1996
+ const nullLastReferences = mediaState.lastSegmentReference == null &&
1997
+ mediaState.lastInitSegmentReference == null;
1922
1998
 
1923
1999
  /** @type {!Array.<!Promise>} */
1924
2000
  const operations = [];
@@ -2109,14 +2185,26 @@ shaka.media.StreamingEngine = class {
2109
2185
  mediaState.type, initSegment);
2110
2186
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
2111
2187
  mediaState.type, initSegment, /* reference= */ null,
2112
- mediaState.stream, hasClosedCaptions);
2188
+ mediaState.stream, hasClosedCaptions, mediaState.seeked,
2189
+ adaptation);
2113
2190
  } catch (error) {
2114
2191
  mediaState.lastInitSegmentReference = null;
2115
2192
  throw error;
2116
2193
  }
2117
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
+ }
2118
2206
  this.playerInterface_.onInitSegmentAppended(
2119
- reference.startTime, reference.initSegmentReference);
2207
+ initSegmentTime, reference.initSegmentReference);
2120
2208
  operations.push(append());
2121
2209
  }
2122
2210
  }
@@ -2147,11 +2235,12 @@ shaka.media.StreamingEngine = class {
2147
2235
  * @param {!shaka.media.SegmentReference} reference
2148
2236
  * @param {BufferSource} segment
2149
2237
  * @param {boolean=} isChunkedData
2238
+ * @param {boolean=} adaptation
2150
2239
  * @return {!Promise}
2151
2240
  * @private
2152
2241
  */
2153
2242
  async append_(mediaState, presentationTime, stream, reference, segment,
2154
- isChunkedData = false) {
2243
+ isChunkedData = false, adaptation = false) {
2155
2244
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
2156
2245
 
2157
2246
  const hasClosedCaptions = stream.closedCaptions &&
@@ -2195,8 +2284,6 @@ shaka.media.StreamingEngine = class {
2195
2284
  // appendBuffer() call.
2196
2285
  const seeked = mediaState.seeked;
2197
2286
  mediaState.seeked = false;
2198
- const adaptation = mediaState.adaptation;
2199
- mediaState.adaptation = false;
2200
2287
 
2201
2288
  await this.playerInterface_.beforeAppendSegment(mediaState.type, segment);
2202
2289
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
@@ -275,7 +275,8 @@ shaka.media.VideoWrapper.PlayheadMover = class {
275
275
  }
276
276
 
277
277
  // Yay! We were successful.
278
- if (this.mediaElement_.currentTime != this.originTime_) {
278
+ if (this.mediaElement_.currentTime != this.originTime_ ||
279
+ this.mediaElement_.currentTime === this.targetTime_) {
279
280
  this.timer_.stop();
280
281
  return;
281
282
  }
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.21' + '-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.