shaka-player 4.13.1 → 4.13.2
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/CONTRIBUTORS +1 -0
- package/dist/deps.js +1 -1
- package/dist/locales.js +34 -34
- package/dist/shaka-player.compiled.debug.js +353 -345
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +1413 -1408
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.debug.js +350 -342
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +1630 -1624
- package/dist/shaka-player.ui.map +3 -3
- package/eslint.config.mjs +14 -1
- package/lib/cast/cast_proxy.js +48 -10
- package/lib/debug/log.js +16 -17
- package/lib/hls/hls_parser.js +27 -1
- package/lib/media/media_source_engine.js +124 -120
- package/lib/media/preload_manager.js +10 -5
- package/lib/media/region_observer.js +9 -8
- package/lib/media/region_timeline.js +10 -8
- package/lib/media/streaming_engine.js +25 -9
- package/lib/player.js +87 -38
- package/lib/text/cue.js +6 -2
- package/lib/text/ui_text_displayer.js +25 -4
- package/lib/util/platform.js +24 -3
- package/lib/util/stream_utils.js +13 -0
- package/package.json +1 -1
- package/project-words.txt +1 -0
- package/ui/ad_statistics_button.js +2 -0
- package/ui/statistics_button.js +3 -1
|
@@ -92,7 +92,10 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
|
|
|
92
92
|
/** @private {boolean} */
|
|
93
93
|
this.parserEntrusted_ = false;
|
|
94
94
|
|
|
95
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* @private {!shaka.media.RegionTimeline<
|
|
97
|
+
* shaka.extern.TimelineRegionInfo>}
|
|
98
|
+
*/
|
|
96
99
|
this.regionTimeline_ = typedPlayerInterface.regionTimeline;
|
|
97
100
|
|
|
98
101
|
/** @private {boolean} */
|
|
@@ -288,7 +291,7 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
|
|
|
288
291
|
/**
|
|
289
292
|
* Gets the region timeline, if it exists. Also marks that the timeline should
|
|
290
293
|
* not be released if this manager is destroyed.
|
|
291
|
-
* @return {?shaka.media.RegionTimeline}
|
|
294
|
+
* @return {?shaka.media.RegionTimeline<shaka.extern.TimelineRegionInfo>}
|
|
292
295
|
*/
|
|
293
296
|
receiveRegionTimeline() {
|
|
294
297
|
this.regionTimelineEntrusted_ = true;
|
|
@@ -296,7 +299,7 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
|
|
|
296
299
|
}
|
|
297
300
|
|
|
298
301
|
/**
|
|
299
|
-
* @return {?shaka.media.RegionTimeline}
|
|
302
|
+
* @return {?shaka.media.RegionTimeline<shaka.extern.TimelineRegionInfo>}
|
|
300
303
|
*/
|
|
301
304
|
getRegionTimeline() {
|
|
302
305
|
return this.regionTimeline_;
|
|
@@ -837,7 +840,8 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
|
|
|
837
840
|
* @typedef {{
|
|
838
841
|
* config: !shaka.extern.PlayerConfiguration,
|
|
839
842
|
* manifestPlayerInterface: !shaka.extern.ManifestParser.PlayerInterface,
|
|
840
|
-
* regionTimeline: !shaka.media.RegionTimeline
|
|
843
|
+
* regionTimeline: !shaka.media.RegionTimeline<
|
|
844
|
+
* shaka.extern.TimelineRegionInfo>,
|
|
841
845
|
* qualityObserver: ?shaka.media.QualityObserver,
|
|
842
846
|
* createDrmEngine: function():!shaka.drm.DrmEngine,
|
|
843
847
|
* networkingEngine: !shaka.net.NetworkingEngine,
|
|
@@ -849,7 +853,8 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
|
|
|
849
853
|
* @property {!shaka.extern.PlayerConfiguration} config
|
|
850
854
|
* @property {!shaka.extern.ManifestParser.PlayerInterface
|
|
851
855
|
* } manifestPlayerInterface
|
|
852
|
-
* @property {!shaka.media.RegionTimeline
|
|
856
|
+
* @property {!shaka.media.RegionTimeline<shaka.extern.TimelineRegionInfo>
|
|
857
|
+
* } regionTimeline
|
|
853
858
|
* @property {?shaka.media.QualityObserver} qualityObserver
|
|
854
859
|
* @property {function():!shaka.drm.DrmEngine} createDrmEngine
|
|
855
860
|
* @property {!shaka.net.NetworkingEngine} networkingEngine
|
|
@@ -18,6 +18,7 @@ goog.require('shaka.util.FakeEventTarget');
|
|
|
18
18
|
* ('enter', 'exit', 'skip') as the playhead moves.
|
|
19
19
|
*
|
|
20
20
|
* @implements {shaka.media.IPlayheadObserver}
|
|
21
|
+
* @template T
|
|
21
22
|
* @final
|
|
22
23
|
*/
|
|
23
24
|
shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
@@ -26,13 +27,13 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
26
27
|
* own the timeline, only uses it. This means that the observer should NOT
|
|
27
28
|
* destroy the timeline.
|
|
28
29
|
*
|
|
29
|
-
* @param {!shaka.media.RegionTimeline} timeline
|
|
30
|
+
* @param {!shaka.media.RegionTimeline<T>} timeline
|
|
30
31
|
* @param {boolean} startsPastZero
|
|
31
32
|
*/
|
|
32
33
|
constructor(timeline, startsPastZero) {
|
|
33
34
|
super();
|
|
34
35
|
|
|
35
|
-
/** @private {shaka.media.RegionTimeline} */
|
|
36
|
+
/** @private {shaka.media.RegionTimeline<T>} */
|
|
36
37
|
this.timeline_ = timeline;
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -50,8 +51,7 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
50
51
|
* When the value here differs from what we calculate, it means we moved and
|
|
51
52
|
* should fire an event.
|
|
52
53
|
*
|
|
53
|
-
* @private {!Map<shaka.
|
|
54
|
-
* shaka.media.RegionObserver.RelativePosition_>}
|
|
54
|
+
* @private {!Map<T, shaka.media.RegionObserver.RelativePosition_>}
|
|
55
55
|
*/
|
|
56
56
|
this.oldPosition_ = new Map();
|
|
57
57
|
|
|
@@ -109,7 +109,7 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
109
109
|
this.eventManager_ = new shaka.util.EventManager();
|
|
110
110
|
|
|
111
111
|
this.eventManager_.listen(this.timeline_, 'regionremove', (event) => {
|
|
112
|
-
/** @type {
|
|
112
|
+
/** @type {T} */
|
|
113
113
|
const region = event['region'];
|
|
114
114
|
this.oldPosition_.delete(region);
|
|
115
115
|
});
|
|
@@ -162,7 +162,7 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
162
162
|
* same parameters: region and seeking.
|
|
163
163
|
*
|
|
164
164
|
* @param {string} eventType
|
|
165
|
-
* @param {
|
|
165
|
+
* @param {T} region
|
|
166
166
|
* @param {boolean} seeking
|
|
167
167
|
* @private
|
|
168
168
|
*/
|
|
@@ -179,7 +179,8 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
179
179
|
* at |seconds|. We treat the region's start and end times as inclusive
|
|
180
180
|
* bounds.
|
|
181
181
|
*
|
|
182
|
-
* @
|
|
182
|
+
* @template T
|
|
183
|
+
* @param {T} region
|
|
183
184
|
* @param {number} seconds
|
|
184
185
|
* @return {shaka.media.RegionObserver.RelativePosition_}
|
|
185
186
|
* @private
|
|
@@ -218,7 +219,7 @@ shaka.media.RegionObserver.RelativePosition_ = {
|
|
|
218
219
|
* region that the playhead is interacting with and whether or not the playhead
|
|
219
220
|
* moving is part of a seek event.
|
|
220
221
|
*
|
|
221
|
-
* @typedef {function(
|
|
222
|
+
* @typedef {function(?, boolean)}
|
|
222
223
|
*/
|
|
223
224
|
shaka.media.RegionObserver.EventListener;
|
|
224
225
|
|
|
@@ -18,6 +18,7 @@ goog.require('shaka.util.Timer');
|
|
|
18
18
|
* deleted, the 'regionremove' event will be fired.
|
|
19
19
|
*
|
|
20
20
|
* @implements {shaka.util.IReleasable}
|
|
21
|
+
* @template T
|
|
21
22
|
* @final
|
|
22
23
|
*/
|
|
23
24
|
shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
@@ -27,7 +28,7 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
|
27
28
|
constructor(getSeekRange) {
|
|
28
29
|
super();
|
|
29
30
|
|
|
30
|
-
/** @private {!Set<
|
|
31
|
+
/** @private {!Set<T>} */
|
|
31
32
|
this.regions_ = new Set();
|
|
32
33
|
|
|
33
34
|
/** @private {!function():{start: number, end: number}} */
|
|
@@ -54,7 +55,7 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
|
-
* @param {
|
|
58
|
+
* @param {T} region
|
|
58
59
|
*/
|
|
59
60
|
addRegion(region) {
|
|
60
61
|
const similarRegion = this.findSimilarRegion_(region);
|
|
@@ -97,18 +98,19 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
|
97
98
|
* to be the same region. If no similar region can be found, |null| will be
|
|
98
99
|
* returned.
|
|
99
100
|
*
|
|
100
|
-
* @param {
|
|
101
|
-
* @return {?
|
|
101
|
+
* @param {T} region
|
|
102
|
+
* @return {?T}
|
|
102
103
|
* @private
|
|
103
104
|
*/
|
|
104
105
|
findSimilarRegion_(region) {
|
|
106
|
+
const isDiffNegligible = (a, b) => Math.abs(a - b) < 0.1;
|
|
105
107
|
for (const existing of this.regions_) {
|
|
106
108
|
// The same scheme ID and time range means that it is similar-enough to
|
|
107
109
|
// be the same region.
|
|
108
110
|
const isSimilar = existing.schemeIdUri == region.schemeIdUri &&
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
existing.id == region.id &&
|
|
112
|
+
isDiffNegligible(existing.startTime, region.startTime) &&
|
|
113
|
+
isDiffNegligible(existing.endTime, region.endTime);
|
|
112
114
|
|
|
113
115
|
if (isSimilar) {
|
|
114
116
|
return existing;
|
|
@@ -123,7 +125,7 @@ shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
|
123
125
|
* others to see what regions are in the timeline while not being able to
|
|
124
126
|
* change the collection.
|
|
125
127
|
*
|
|
126
|
-
* @return {!Iterable<
|
|
128
|
+
* @return {!Iterable<T>}
|
|
127
129
|
*/
|
|
128
130
|
regions() {
|
|
129
131
|
return this.regions_;
|
|
@@ -1368,12 +1368,17 @@ shaka.media.StreamingEngine = class {
|
|
|
1368
1368
|
return null;
|
|
1369
1369
|
}
|
|
1370
1370
|
|
|
1371
|
+
// Update updateIntervalSeconds according to our current playbackrate,
|
|
1372
|
+
// and always considering a minimum of 1.
|
|
1373
|
+
const updateIntervalSeconds = this.config_.updateIntervalSeconds /
|
|
1374
|
+
Math.max(1, Math.abs(this.playerInterface_.getPlaybackRate()));
|
|
1375
|
+
|
|
1371
1376
|
if (!this.playerInterface_.mediaSourceEngine.isStreamingAllowed() &&
|
|
1372
1377
|
mediaState.type != ContentType.TEXT) {
|
|
1373
1378
|
// It is not allowed to add segments yet, so we schedule an update to
|
|
1374
1379
|
// check again later. So any prediction we make now could be terribly
|
|
1375
1380
|
// invalid soon.
|
|
1376
|
-
return
|
|
1381
|
+
return updateIntervalSeconds / 2;
|
|
1377
1382
|
}
|
|
1378
1383
|
|
|
1379
1384
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
@@ -1445,9 +1450,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1445
1450
|
shaka.log.v2(logPrefix, 'buffering goal met');
|
|
1446
1451
|
|
|
1447
1452
|
// Do not try to predict the next update. Just poll according to
|
|
1448
|
-
// configuration (seconds).
|
|
1449
|
-
|
|
1450
|
-
return this.config_.updateIntervalSeconds / 2;
|
|
1453
|
+
// configuration (seconds).
|
|
1454
|
+
return updateIntervalSeconds / 2;
|
|
1451
1455
|
}
|
|
1452
1456
|
|
|
1453
1457
|
// Lack of segment iterator is the best indicator stream has changed.
|
|
@@ -1459,7 +1463,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1459
1463
|
// In any case just try again... if the manifest is incomplete or is not
|
|
1460
1464
|
// being updated then we'll idle forever; otherwise, we'll end up getting
|
|
1461
1465
|
// a SegmentReference eventually.
|
|
1462
|
-
return
|
|
1466
|
+
return updateIntervalSeconds;
|
|
1463
1467
|
}
|
|
1464
1468
|
// Get media state adaptation and reset this value. By guarding it during
|
|
1465
1469
|
// actual stream change we ensure it won't be cleaned by accident on regular
|
|
@@ -1498,7 +1502,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1498
1502
|
// For example, let video buffering catch up to audio buffering before
|
|
1499
1503
|
// fetching another audio segment.
|
|
1500
1504
|
shaka.log.v2(logPrefix, 'waiting for other streams to buffer');
|
|
1501
|
-
return
|
|
1505
|
+
return updateIntervalSeconds;
|
|
1502
1506
|
}
|
|
1503
1507
|
|
|
1504
1508
|
if (mediaState.segmentPrefetch && mediaState.segmentIterator &&
|
|
@@ -1821,7 +1825,19 @@ shaka.media.StreamingEngine = class {
|
|
|
1821
1825
|
JSON.stringify(buffered));
|
|
1822
1826
|
|
|
1823
1827
|
if (!mediaState.waitingToClearBuffer) {
|
|
1824
|
-
|
|
1828
|
+
let otherState = null;
|
|
1829
|
+
if (mediaState.type === ContentType.VIDEO) {
|
|
1830
|
+
otherState = this.mediaStates_.get(ContentType.AUDIO);
|
|
1831
|
+
} else if (mediaState.type === ContentType.AUDIO) {
|
|
1832
|
+
otherState = this.mediaStates_.get(ContentType.VIDEO);
|
|
1833
|
+
}
|
|
1834
|
+
if (otherState && otherState.type == ContentType.AUDIO) {
|
|
1835
|
+
this.playerInterface_.onSegmentAppended(reference, mediaState.stream,
|
|
1836
|
+
otherState.stream.isAudioMuxedInVideo);
|
|
1837
|
+
} else {
|
|
1838
|
+
this.playerInterface_.onSegmentAppended(reference, mediaState.stream,
|
|
1839
|
+
mediaState.stream.codecs.includes(','));
|
|
1840
|
+
}
|
|
1825
1841
|
}
|
|
1826
1842
|
|
|
1827
1843
|
// Update right away.
|
|
@@ -2885,7 +2901,7 @@ shaka.media.StreamingEngine = class {
|
|
|
2885
2901
|
* onError: function(!shaka.util.Error),
|
|
2886
2902
|
* onEvent: function(!Event),
|
|
2887
2903
|
* onSegmentAppended: function(!shaka.media.SegmentReference,
|
|
2888
|
-
* !shaka.extern.Stream),
|
|
2904
|
+
* !shaka.extern.Stream, boolean),
|
|
2889
2905
|
* onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference),
|
|
2890
2906
|
* beforeAppendSegment: function(
|
|
2891
2907
|
* shaka.util.ManifestParserUtils.ContentType,!BufferSource):Promise,
|
|
@@ -2910,7 +2926,7 @@ shaka.media.StreamingEngine = class {
|
|
|
2910
2926
|
* @property {function(!Event)} onEvent
|
|
2911
2927
|
* Called when an event occurs that should be sent to the app.
|
|
2912
2928
|
* @property {function(!shaka.media.SegmentReference,
|
|
2913
|
-
* !shaka.extern.Stream)} onSegmentAppended
|
|
2929
|
+
* !shaka.extern.Stream, boolean)} onSegmentAppended
|
|
2914
2930
|
* Called after a segment is successfully appended to a MediaSource.
|
|
2915
2931
|
* @property {function(!number,
|
|
2916
2932
|
* !shaka.media.InitSegmentReference)} onInitSegmentAppended
|
package/lib/player.js
CHANGED
|
@@ -54,6 +54,7 @@ goog.require('shaka.util.Functional');
|
|
|
54
54
|
goog.require('shaka.util.IDestroyable');
|
|
55
55
|
goog.require('shaka.util.LanguageUtils');
|
|
56
56
|
goog.require('shaka.util.ManifestParserUtils');
|
|
57
|
+
goog.require('shaka.util.MapUtils');
|
|
57
58
|
goog.require('shaka.util.MediaReadyState');
|
|
58
59
|
goog.require('shaka.util.MimeUtils');
|
|
59
60
|
goog.require('shaka.util.Mutex');
|
|
@@ -698,7 +699,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
698
699
|
/** @private {shaka.media.BufferingObserver} */
|
|
699
700
|
this.bufferObserver_ = null;
|
|
700
701
|
|
|
701
|
-
/**
|
|
702
|
+
/**
|
|
703
|
+
* @private {shaka.media.RegionTimeline<
|
|
704
|
+
* shaka.extern.TimelineRegionInfo>}
|
|
705
|
+
*/
|
|
702
706
|
this.regionTimeline_ = null;
|
|
703
707
|
|
|
704
708
|
/** @private {shaka.util.CmcdManager} */
|
|
@@ -3591,6 +3595,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3591
3595
|
|
|
3592
3596
|
// Create the region observer. This will allow us to notify the app when we
|
|
3593
3597
|
// move in and out of timeline regions.
|
|
3598
|
+
/** @type {!shaka.media.RegionObserver<shaka.extern.TimelineRegionInfo>} */
|
|
3594
3599
|
const regionObserver = new shaka.media.RegionObserver(
|
|
3595
3600
|
this.regionTimeline_, startsPastZero);
|
|
3596
3601
|
|
|
@@ -3808,10 +3813,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3808
3813
|
netEngine: this.networkingEngine_,
|
|
3809
3814
|
onError: (error) => this.onError_(error),
|
|
3810
3815
|
onEvent: (event) => this.dispatchEvent(event),
|
|
3811
|
-
onSegmentAppended: (reference, stream) => {
|
|
3816
|
+
onSegmentAppended: (reference, stream, isMuxed) => {
|
|
3812
3817
|
this.onSegmentAppended_(
|
|
3813
|
-
reference.startTime, reference.endTime, stream.type,
|
|
3814
|
-
stream.codecs.includes(','));
|
|
3818
|
+
reference.startTime, reference.endTime, stream.type, isMuxed);
|
|
3815
3819
|
},
|
|
3816
3820
|
onInitSegmentAppended: (position, initSegment) => {
|
|
3817
3821
|
const mediaQuality = initSegment.getMediaQuality();
|
|
@@ -4723,7 +4727,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4723
4727
|
shaka.log.alwaysWarn('A trick play rate of 0 is unsupported!');
|
|
4724
4728
|
return;
|
|
4725
4729
|
}
|
|
4726
|
-
this.trickPlayEventManager_.removeAll();
|
|
4727
4730
|
|
|
4728
4731
|
this.playRateController_.set(rate);
|
|
4729
4732
|
|
|
@@ -4732,26 +4735,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4732
4735
|
this.streamingEngine_.setTrickPlay(
|
|
4733
4736
|
useTrickPlayTrack && Math.abs(rate) > 1);
|
|
4734
4737
|
}
|
|
4735
|
-
|
|
4736
|
-
this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
|
|
4737
|
-
const currentTime = this.video_.currentTime;
|
|
4738
|
-
const seekRange = this.seekRange();
|
|
4739
|
-
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
4740
|
-
|
|
4741
|
-
// Cancel trick play if we hit the beginning or end of the seekable
|
|
4742
|
-
// (Sub-second accuracy not required here)
|
|
4743
|
-
if (rate > 0) {
|
|
4744
|
-
if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
|
|
4745
|
-
this.cancelTrickPlay();
|
|
4746
|
-
}
|
|
4747
|
-
} else {
|
|
4748
|
-
if (Math.floor(currentTime) <=
|
|
4749
|
-
Math.floor(seekRange.start + safeSeekOffset)) {
|
|
4750
|
-
this.cancelTrickPlay();
|
|
4751
|
-
}
|
|
4752
|
-
}
|
|
4753
|
-
});
|
|
4754
|
-
}
|
|
4738
|
+
this.setupTrickPlayEventListeners_(rate);
|
|
4755
4739
|
}
|
|
4756
4740
|
|
|
4757
4741
|
/**
|
|
@@ -5825,12 +5809,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5825
5809
|
* is playing.
|
|
5826
5810
|
*/
|
|
5827
5811
|
getLiveLatency() {
|
|
5828
|
-
if (!this.video_) {
|
|
5812
|
+
if (!this.video_ || !this.video_.currentTime) {
|
|
5829
5813
|
return null;
|
|
5830
5814
|
}
|
|
5831
5815
|
const now = this.getPresentationStartTimeAsDate().getTime() +
|
|
5832
5816
|
this.video_.currentTime * 1000;
|
|
5833
|
-
|
|
5817
|
+
|
|
5818
|
+
return Math.floor(Date.now() - now);
|
|
5834
5819
|
}
|
|
5835
5820
|
|
|
5836
5821
|
/**
|
|
@@ -6867,6 +6852,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6867
6852
|
// The playback rate has changed. This could be us or someone else.
|
|
6868
6853
|
// If this was us, setting the rate again will be a no-op.
|
|
6869
6854
|
this.playRateController_.set(newRate);
|
|
6855
|
+
|
|
6856
|
+
if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
|
|
6857
|
+
this.abrManager_.playbackRateChanged(newRate);
|
|
6858
|
+
}
|
|
6859
|
+
this.setupTrickPlayEventListeners_(newRate);
|
|
6870
6860
|
}
|
|
6871
6861
|
|
|
6872
6862
|
const event = shaka.Player.makeEvent_(
|
|
@@ -6874,6 +6864,36 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6874
6864
|
this.dispatchEvent(event);
|
|
6875
6865
|
}
|
|
6876
6866
|
|
|
6867
|
+
/**
|
|
6868
|
+
* Configures all the necessary listeners when trick play is being performed.
|
|
6869
|
+
*
|
|
6870
|
+
* @param {number} rate
|
|
6871
|
+
* @private
|
|
6872
|
+
*/
|
|
6873
|
+
setupTrickPlayEventListeners_(rate) {
|
|
6874
|
+
this.trickPlayEventManager_.removeAll();
|
|
6875
|
+
if (this.isLive()) {
|
|
6876
|
+
this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
|
|
6877
|
+
const currentTime = this.video_.currentTime;
|
|
6878
|
+
const seekRange = this.seekRange();
|
|
6879
|
+
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
6880
|
+
|
|
6881
|
+
// Cancel trick play if we hit the beginning or end of the seekable
|
|
6882
|
+
// (Sub-second accuracy not required here)
|
|
6883
|
+
if (rate > 0) {
|
|
6884
|
+
if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
|
|
6885
|
+
this.cancelTrickPlay();
|
|
6886
|
+
}
|
|
6887
|
+
} else {
|
|
6888
|
+
if (Math.floor(currentTime) <=
|
|
6889
|
+
Math.floor(seekRange.start + safeSeekOffset)) {
|
|
6890
|
+
this.cancelTrickPlay();
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
});
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
|
|
6877
6897
|
/**
|
|
6878
6898
|
* Try updating the state history. If the player has not finished
|
|
6879
6899
|
* initializing, this will be a no-op.
|
|
@@ -8042,6 +8062,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
8042
8062
|
|
|
8043
8063
|
// Only filter tracks for keys if we have some key statuses to look at.
|
|
8044
8064
|
if (keyIds.length) {
|
|
8065
|
+
const currentKeySystem = this.keySystem();
|
|
8066
|
+
const clearKeys = shaka.util.MapUtils.asMap(this.config_.drm.clearKeys);
|
|
8067
|
+
|
|
8045
8068
|
for (const variant of this.manifest_.variants) {
|
|
8046
8069
|
const streams = shaka.util.StreamUtils.getVariantStreams(variant);
|
|
8047
8070
|
|
|
@@ -8051,16 +8074,42 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
8051
8074
|
// Only update if we have key IDs for the stream. If the keys aren't
|
|
8052
8075
|
// all present, then the track should be restricted.
|
|
8053
8076
|
if (stream.keyIds.size) {
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8077
|
+
// If we are not using clearkeys, and the stream has drmInfos we
|
|
8078
|
+
// only want to check the keyIds of the keySystem we are using.
|
|
8079
|
+
// Other keySystems might have other keyIds that might not be
|
|
8080
|
+
// valid in this case. This can happen in HLS if the manifest
|
|
8081
|
+
// has Widevine with keyIds and PlayReady without keyIds and we are
|
|
8082
|
+
// using PlayReady.
|
|
8083
|
+
if (stream.drmInfos.length && !clearKeys.size) {
|
|
8084
|
+
for (const drmInfo of stream.drmInfos) {
|
|
8085
|
+
if (drmInfo.keyIds.size &&
|
|
8086
|
+
drmInfo.keySystem == currentKeySystem) {
|
|
8087
|
+
variant.allowedByKeySystem = true;
|
|
8088
|
+
|
|
8089
|
+
for (const keyId of drmInfo.keyIds) {
|
|
8090
|
+
const keyStatus =
|
|
8091
|
+
keyStatusMap[isGlobalStatus ? '00' : keyId];
|
|
8092
|
+
if (keyStatus || this.drmEngine_.hasManifestInitData()) {
|
|
8093
|
+
variant.allowedByKeySystem =
|
|
8094
|
+
variant.allowedByKeySystem &&
|
|
8095
|
+
!!keyStatus &&
|
|
8096
|
+
!restrictedStatuses.includes(keyStatus);
|
|
8097
|
+
} // if (keyStatus || this.drmEngine_.hasManifestInitData())
|
|
8098
|
+
} // for (const keyId of drmInfo.keyIds)
|
|
8099
|
+
} // if (drmInfo.keyIds.size && ...
|
|
8100
|
+
} // for (const drmInfo of stream.drmInfos
|
|
8101
|
+
} else {
|
|
8102
|
+
variant.allowedByKeySystem = true;
|
|
8103
|
+
|
|
8104
|
+
for (const keyId of stream.keyIds) {
|
|
8105
|
+
const keyStatus = keyStatusMap[isGlobalStatus ? '00' : keyId];
|
|
8106
|
+
if (keyStatus || this.drmEngine_.hasManifestInitData()) {
|
|
8107
|
+
variant.allowedByKeySystem = variant.allowedByKeySystem &&
|
|
8108
|
+
!!keyStatus && !restrictedStatuses.includes(keyStatus);
|
|
8109
|
+
}
|
|
8110
|
+
} // for (const keyId of stream.keyIds)
|
|
8111
|
+
} // if (stream.drmInfos.length && !clearKeys.size)
|
|
8112
|
+
} // if (stream.keyIds.size)
|
|
8064
8113
|
|
|
8065
8114
|
if (originalAllowed != variant.allowedByKeySystem) {
|
|
8066
8115
|
tracksChanged = true;
|
|
@@ -8386,7 +8435,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
8386
8435
|
* @export
|
|
8387
8436
|
*/
|
|
8388
8437
|
// eslint-disable-next-line no-useless-concat
|
|
8389
|
-
shaka.Player.version = 'v4.13.
|
|
8438
|
+
shaka.Player.version = 'v4.13.2' + '-uncompiled'; // x-release-please-version
|
|
8390
8439
|
|
|
8391
8440
|
// Initialize the deprecation system using the version string we just set
|
|
8392
8441
|
// on the player.
|
package/lib/text/cue.js
CHANGED
|
@@ -371,8 +371,12 @@ shaka.text.Cue = class {
|
|
|
371
371
|
// performance optimization. We can avoid the more expensive recursive
|
|
372
372
|
// checks if the top-level properties don't match.
|
|
373
373
|
// See: https://github.com/shaka-project/shaka-player/issues/3018
|
|
374
|
-
if (cue1.
|
|
375
|
-
|
|
374
|
+
if (cue1.payload != cue2.payload) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
const isDiffNegligible = (a, b) => Math.abs(a - b) < 0.001;
|
|
378
|
+
if (!isDiffNegligible(cue1.startTime, cue2.startTime) ||
|
|
379
|
+
!isDiffNegligible(cue1.endTime, cue2.endTime)) {
|
|
376
380
|
return false;
|
|
377
381
|
}
|
|
378
382
|
for (const k in cue1) {
|
|
@@ -55,6 +55,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
55
55
|
/** @private {?number} */
|
|
56
56
|
this.aspectRatio_ = null;
|
|
57
57
|
|
|
58
|
+
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
|
59
|
+
this.config_ = null;
|
|
60
|
+
|
|
58
61
|
/** @type {HTMLElement} */
|
|
59
62
|
this.textContainer_ = shaka.util.Dom.createHTMLElement('div');
|
|
60
63
|
this.textContainer_.classList.add('shaka-text-container');
|
|
@@ -77,7 +80,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
77
80
|
if (!this.video_.paused) {
|
|
78
81
|
this.updateCaptions_();
|
|
79
82
|
}
|
|
80
|
-
})
|
|
83
|
+
});
|
|
84
|
+
this.configureCaptionsTimer_();
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
87
|
* Maps cues to cue elements. Specifically points out the wrapper element of
|
|
@@ -101,6 +105,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
101
105
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
this.eventManager_.listen(this.video_, 'ratechange', () => {
|
|
109
|
+
this.configureCaptionsTimer_();
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
105
113
|
// Whenever the natural width or natural height of the video changes
|
|
106
114
|
// (including, for example, because the selected video track was changed),
|
|
@@ -137,9 +145,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
137
145
|
* @export
|
|
138
146
|
*/
|
|
139
147
|
configure(config) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
148
|
+
this.config_ = config;
|
|
149
|
+
this.configureCaptionsTimer_();
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
|
|
@@ -186,6 +193,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
186
193
|
this.cues_ = [];
|
|
187
194
|
if (this.captionsTimer_) {
|
|
188
195
|
this.captionsTimer_.stop();
|
|
196
|
+
this.captionsTimer_ = null;
|
|
189
197
|
}
|
|
190
198
|
|
|
191
199
|
this.currentCuesMap_.clear();
|
|
@@ -265,6 +273,19 @@ shaka.text.UITextDisplayer = class {
|
|
|
265
273
|
enableTextDisplayer() {
|
|
266
274
|
}
|
|
267
275
|
|
|
276
|
+
/**
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
configureCaptionsTimer_() {
|
|
280
|
+
if (this.captionsTimer_) {
|
|
281
|
+
const captionsUpdatePeriod = this.config_ ?
|
|
282
|
+
this.config_.captionsUpdatePeriod : 0.25;
|
|
283
|
+
const updateTime = captionsUpdatePeriod /
|
|
284
|
+
Math.max(1, Math.abs(this.video_.playbackRate));
|
|
285
|
+
this.captionsTimer_.tickEvery(updateTime);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
268
289
|
/**
|
|
269
290
|
* @private
|
|
270
291
|
*/
|
package/lib/util/platform.js
CHANGED
|
@@ -307,13 +307,15 @@ shaka.util.Platform = class {
|
|
|
307
307
|
!shaka.util.Platform.isTizen() &&
|
|
308
308
|
!shaka.util.Platform.isEOS() &&
|
|
309
309
|
!shaka.util.Platform.isAPL() &&
|
|
310
|
+
!shaka.util.Platform.isAPP() &&
|
|
310
311
|
!shaka.util.Platform.isVirginMedia() &&
|
|
311
312
|
!shaka.util.Platform.isOrange() &&
|
|
312
313
|
!shaka.util.Platform.isPS4() &&
|
|
313
314
|
!shaka.util.Platform.isAmazonFireTV() &&
|
|
314
315
|
!shaka.util.Platform.isComcastX1() &&
|
|
315
316
|
!shaka.util.Platform.isZenterio() &&
|
|
316
|
-
!shaka.util.Platform.isSkyQ()
|
|
317
|
+
!shaka.util.Platform.isSkyQ() &&
|
|
318
|
+
!shaka.util.Platform.isMultiChoice();
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
/**
|
|
@@ -396,6 +398,16 @@ shaka.util.Platform = class {
|
|
|
396
398
|
return shaka.util.Platform.userAgentContains_('DT_STB_BCM');
|
|
397
399
|
}
|
|
398
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Check if the current platform is a MultiChoice STB.
|
|
403
|
+
* @return {boolean}
|
|
404
|
+
*/
|
|
405
|
+
static isMultiChoice() {
|
|
406
|
+
// cspell: ignore MDMP
|
|
407
|
+
return shaka.util.Platform.userAgentContains_('MDMP1001S') ||
|
|
408
|
+
shaka.util.Platform.userAgentContains_('PS5525IMC');
|
|
409
|
+
}
|
|
410
|
+
|
|
399
411
|
/**
|
|
400
412
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
401
413
|
*
|
|
@@ -461,6 +473,14 @@ shaka.util.Platform = class {
|
|
|
461
473
|
return shaka.util.Platform.userAgentContains_('PC=APL');
|
|
462
474
|
}
|
|
463
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Check if the current platform is an APPSTB set-top box.
|
|
478
|
+
* @return {boolean}
|
|
479
|
+
*/
|
|
480
|
+
static isAPP() {
|
|
481
|
+
return shaka.util.Platform.userAgentContains_('PC=APPSTB');
|
|
482
|
+
}
|
|
483
|
+
|
|
464
484
|
/**
|
|
465
485
|
* Guesses if the platform is a mobile one (iOS or Android).
|
|
466
486
|
*
|
|
@@ -568,7 +588,8 @@ shaka.util.Platform = class {
|
|
|
568
588
|
Platform.isEOS() || Platform.isAPL() ||
|
|
569
589
|
Platform.isVirginMedia() || Platform.isOrange() ||
|
|
570
590
|
Platform.isComcastX1() || Platform.isChromecast() ||
|
|
571
|
-
Platform.isHisense() || Platform.isZenterio()
|
|
591
|
+
Platform.isHisense() || Platform.isZenterio() ||
|
|
592
|
+
Platform.isAPP() || Platform.isMultiChoice()) {
|
|
572
593
|
return true;
|
|
573
594
|
}
|
|
574
595
|
return false;
|
|
@@ -662,7 +683,7 @@ shaka.util.Platform = class {
|
|
|
662
683
|
static supportsSequenceMode() {
|
|
663
684
|
const Platform = shaka.util.Platform;
|
|
664
685
|
if (Platform.isTizen3() || Platform.isTizen2() ||
|
|
665
|
-
Platform.isWebOS3() || Platform.isPS4()) {
|
|
686
|
+
Platform.isWebOS3() || Platform.isPS4() || Platform.isPS5()) {
|
|
666
687
|
return false;
|
|
667
688
|
}
|
|
668
689
|
return true;
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -1267,6 +1267,9 @@ shaka.util.StreamUtils = class {
|
|
|
1267
1267
|
* @return {shaka.extern.Track}
|
|
1268
1268
|
*/
|
|
1269
1269
|
static variantToTrack(variant) {
|
|
1270
|
+
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
1271
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1272
|
+
|
|
1270
1273
|
/** @type {?shaka.extern.Stream} */
|
|
1271
1274
|
const audio = variant.audio;
|
|
1272
1275
|
/** @type {?shaka.extern.Stream} */
|
|
@@ -1385,6 +1388,16 @@ shaka.util.StreamUtils = class {
|
|
|
1385
1388
|
track.audioSamplingRate = video.audioSamplingRate;
|
|
1386
1389
|
track.spatialAudio = video.spatialAudio;
|
|
1387
1390
|
track.originalLanguage = video.originalLanguage;
|
|
1391
|
+
track.audioMimeType = videoMimeType;
|
|
1392
|
+
const allCodecs = videoCodec.split(',');
|
|
1393
|
+
try {
|
|
1394
|
+
track.videoCodec = ManifestParserUtils.guessCodecs(
|
|
1395
|
+
ContentType.VIDEO, allCodecs);
|
|
1396
|
+
track.audioCodec = ManifestParserUtils.guessCodecs(
|
|
1397
|
+
ContentType.AUDIO, allCodecs);
|
|
1398
|
+
} catch (e) {
|
|
1399
|
+
// Ignore this error.
|
|
1400
|
+
}
|
|
1388
1401
|
}
|
|
1389
1402
|
}
|
|
1390
1403
|
|
package/package.json
CHANGED