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/AUTHORS +2 -0
- package/CONTRIBUTORS +3 -0
- package/dist/controls.css +1 -1
- package/dist/deps.js +11 -11
- package/dist/locales.js +35 -35
- package/dist/shaka-player.compiled.debug.js +469 -471
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +1231 -1226
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.debug.js +515 -518
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +1422 -1417
- package/dist/shaka-player.ui.map +3 -3
- package/externs/xbox.js +20 -0
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/dash/dash_parser.js +31 -7
- package/lib/hls/hls_classes.js +5 -1
- package/lib/hls/hls_parser.js +2 -2
- package/lib/media/closed_caption_parser.js +1 -1
- package/lib/media/media_source_engine.js +7 -5
- package/lib/media/streaming_engine.js +106 -19
- package/lib/media/video_wrapper.js +2 -1
- package/lib/player.js +29 -9
- package/lib/polyfill/abort_controller.js +24 -16
- package/lib/text/ui_text_displayer.js +16 -13
- package/lib/text/vtt_text_parser.js +16 -1
- package/lib/util/platform.js +13 -2
- package/lib/util/stream_utils.js +44 -0
- package/lib/util/tXml.js +40 -1
- package/package.json +5 -5
- package/ui/presentation_time.js +4 -1
- package/ui/resolution_selection.js +11 -3
- package/ui/seek_bar.js +2 -1
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,
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -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
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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) =>
|
|
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
|
package/lib/hls/hls_classes.js
CHANGED
|
@@ -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')
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
588
|
-
shaka.log.debug('switch: switching to Stream ' +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2791
|
-
|
|
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.
|
|
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.
|