shaka-player 4.0.4 → 4.1.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/CHANGELOG.md +41 -34
- package/CONTRIBUTORS +1 -0
- package/demo/common/message_ids.js +1 -0
- package/demo/config.js +3 -1
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/dist/demo.compiled.debug.js +4 -4
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +4 -4
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +2 -2
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +2 -2
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +2 -2
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +14 -8
- package/dist/shaka-player.compiled.debug.d.ts +14 -8
- package/dist/shaka-player.compiled.debug.externs.js +10 -2
- package/dist/shaka-player.compiled.debug.js +168 -160
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +10 -2
- package/dist/shaka-player.compiled.js +767 -764
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +15 -9
- package/dist/shaka-player.ui.debug.d.ts +15 -9
- package/dist/shaka-player.ui.debug.externs.js +10 -2
- package/dist/shaka-player.ui.debug.js +201 -189
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +10 -2
- package/dist/shaka-player.ui.js +934 -931
- package/dist/shaka-player.ui.map +3 -3
- package/docs/design/chromecast.md +3 -1
- package/docs/tutorials/drm-config.md +16 -0
- package/docs/tutorials/ui.md +26 -0
- package/externs/chromecast.js +9 -2
- package/externs/shaka/manifest.js +7 -0
- package/externs/shaka/player.js +21 -2
- package/externs/shaka/text.js +7 -0
- package/lib/cast/cast_proxy.js +19 -8
- package/lib/cast/cast_receiver.js +3 -3
- package/lib/cast/cast_sender.js +12 -2
- package/lib/hls/hls_parser.js +12 -1
- package/lib/media/drm_engine.js +17 -8
- package/lib/media/gap_jumping_controller.js +23 -1
- package/lib/media/playhead.js +47 -4
- package/lib/media/stall_detector.js +19 -2
- package/lib/media/streaming_engine.js +8 -1
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/player.js +175 -69
- package/lib/text/cue.js +6 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +4 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event.js +39 -0
- package/lib/util/player_configuration.js +2 -0
- package/lib/util/stats.js +28 -1
- package/lib/util/stream_utils.js +7 -0
- package/package.json +1 -1
- package/test/cast/cast_proxy_unit.js +3 -1
- package/test/cast/cast_sender_unit.js +21 -6
- package/test/demo/demo_unit.js +2 -1
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/playhead_unit.js +114 -25
- package/test/media/stall_detector_unit.js +20 -2
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +1 -0
- package/test/player_integration.js +3 -0
- package/test/player_unit.js +142 -1
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/simple_fakes.js +14 -0
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/text/vtt_text_parser_unit.js +4 -0
- package/test/ui/ui_unit.js +18 -5
- package/test/util/stream_utils_unit.js +108 -0
- package/ui/controls.js +12 -8
- package/ui/externs/ui.js +10 -1
- package/ui/statistics_button.js +10 -0
- package/ui/ui.js +12 -1
|
@@ -9,6 +9,7 @@ goog.provide('shaka.media.StallDetector.Implementation');
|
|
|
9
9
|
goog.provide('shaka.media.StallDetector.MediaElementImplementation');
|
|
10
10
|
|
|
11
11
|
goog.require('shaka.media.TimeRangesUtils');
|
|
12
|
+
goog.require('shaka.util.FakeEvent');
|
|
12
13
|
goog.require('shaka.util.IReleasable');
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -23,11 +24,14 @@ shaka.media.StallDetector = class {
|
|
|
23
24
|
/**
|
|
24
25
|
* @param {shaka.media.StallDetector.Implementation} implementation
|
|
25
26
|
* @param {number} stallThresholdSeconds
|
|
27
|
+
* @param {function(!Event)} onEvent
|
|
28
|
+
* Called when an event is raised to be sent to the application.
|
|
26
29
|
*/
|
|
27
|
-
constructor(implementation, stallThresholdSeconds) {
|
|
30
|
+
constructor(implementation, stallThresholdSeconds, onEvent) {
|
|
31
|
+
/** @private {?function(!Event)} */
|
|
32
|
+
this.onEvent_ = onEvent;
|
|
28
33
|
/** @private {shaka.media.StallDetector.Implementation} */
|
|
29
34
|
this.implementation_ = implementation;
|
|
30
|
-
|
|
31
35
|
/** @private {boolean} */
|
|
32
36
|
this.wasMakingProgress_ = implementation.shouldBeMakingProgress();
|
|
33
37
|
/** @private {number} */
|
|
@@ -36,6 +40,8 @@ shaka.media.StallDetector = class {
|
|
|
36
40
|
this.lastUpdateSeconds_ = implementation.getWallSeconds();
|
|
37
41
|
/** @private {boolean} */
|
|
38
42
|
this.didJump_ = false;
|
|
43
|
+
/** @private {number} */
|
|
44
|
+
this.stallsDetected_ = 0;
|
|
39
45
|
|
|
40
46
|
/**
|
|
41
47
|
* The amount of time in seconds that we must have the same value of
|
|
@@ -53,6 +59,7 @@ shaka.media.StallDetector = class {
|
|
|
53
59
|
release() {
|
|
54
60
|
// Drop external references to make things easier on the GC.
|
|
55
61
|
this.implementation_ = null;
|
|
62
|
+
this.onEvent_ = null;
|
|
56
63
|
this.onStall_ = () => {};
|
|
57
64
|
}
|
|
58
65
|
|
|
@@ -66,6 +73,13 @@ shaka.media.StallDetector = class {
|
|
|
66
73
|
this.onStall_ = doThis;
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Returns the number of playback stalls detected.
|
|
78
|
+
*/
|
|
79
|
+
getStallsDetected() {
|
|
80
|
+
return this.stallsDetected_;
|
|
81
|
+
}
|
|
82
|
+
|
|
69
83
|
/**
|
|
70
84
|
* Have the detector update itself and fire the "on stall" callback if a stall
|
|
71
85
|
* was detected.
|
|
@@ -100,6 +114,9 @@ shaka.media.StallDetector = class {
|
|
|
100
114
|
// If the onStall_ method updated the current time, update our stored
|
|
101
115
|
// value so we don't think that was an update.
|
|
102
116
|
this.value_ = impl.getPresentationSeconds();
|
|
117
|
+
this.stallsDetected_++;
|
|
118
|
+
this.onEvent_(new shaka.util.FakeEvent(
|
|
119
|
+
shaka.util.FakeEvent.EventName.StallDetected));
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
return triggerCallback;
|
|
@@ -1215,6 +1215,13 @@ shaka.media.StreamingEngine = class {
|
|
|
1215
1215
|
mediaState.performingUpdate = true;
|
|
1216
1216
|
|
|
1217
1217
|
try {
|
|
1218
|
+
if (reference.getStatus() ==
|
|
1219
|
+
shaka.media.SegmentReference.Status.MISSING) {
|
|
1220
|
+
throw new shaka.util.Error(
|
|
1221
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
1222
|
+
shaka.util.Error.Category.NETWORK,
|
|
1223
|
+
shaka.util.Error.Code.SEGMENT_MISSING);
|
|
1224
|
+
}
|
|
1218
1225
|
await this.initSourceBuffer_(mediaState, reference);
|
|
1219
1226
|
this.destroyer_.ensureNotDestroyed();
|
|
1220
1227
|
if (this.fatalError_) {
|
|
@@ -1693,7 +1700,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1693
1700
|
};
|
|
1694
1701
|
|
|
1695
1702
|
// Dispatch an event to notify the application about the emsg box.
|
|
1696
|
-
const eventName = shaka.
|
|
1703
|
+
const eventName = shaka.util.FakeEvent.EventName.Emsg;
|
|
1697
1704
|
const data = (new Map()).set('detail', emsg);
|
|
1698
1705
|
const event = new shaka.util.FakeEvent(eventName, data);
|
|
1699
1706
|
this.playerInterface_.onEvent(event);
|
package/lib/player.js
CHANGED
|
@@ -385,6 +385,26 @@ goog.requireType('shaka.routing.Payload');
|
|
|
385
385
|
*/
|
|
386
386
|
|
|
387
387
|
|
|
388
|
+
/**
|
|
389
|
+
* @event shaka.Player.StallDetectedEvent
|
|
390
|
+
* @description Fired when a stall in playback is detected by the StallDetector.
|
|
391
|
+
* Not all stalls are caused by gaps in the buffered ranges.
|
|
392
|
+
* @property {string} type
|
|
393
|
+
* 'stalldetected'
|
|
394
|
+
* @exportDoc
|
|
395
|
+
*/
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* @event shaka.Player.GapJumpedEvent
|
|
400
|
+
* @description Fired when the GapJumpingController jumps over a gap in the
|
|
401
|
+
* buffered ranges.
|
|
402
|
+
* @property {string} type
|
|
403
|
+
* 'gapjumped'
|
|
404
|
+
* @exportDoc
|
|
405
|
+
*/
|
|
406
|
+
|
|
407
|
+
|
|
388
408
|
/**
|
|
389
409
|
* @summary The main player object for Shaka Player.
|
|
390
410
|
*
|
|
@@ -652,7 +672,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
652
672
|
},
|
|
653
673
|
enterNode: (node, has, wants) => {
|
|
654
674
|
this.dispatchEvent(this.makeEvent_(
|
|
655
|
-
/* name= */ shaka.
|
|
675
|
+
/* name= */ shaka.util.FakeEvent.EventName.OnStateChange,
|
|
656
676
|
/* data= */ (new Map()).set('state', node.name)));
|
|
657
677
|
|
|
658
678
|
const action = actions.get(node);
|
|
@@ -683,7 +703,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
683
703
|
},
|
|
684
704
|
onIdle: (node) => {
|
|
685
705
|
this.dispatchEvent(this.makeEvent_(
|
|
686
|
-
/* name= */ shaka.
|
|
706
|
+
/* name= */ shaka.util.FakeEvent.EventName.OnStateIdle,
|
|
687
707
|
/* data= */ (new Map()).set('state', node.name)));
|
|
688
708
|
},
|
|
689
709
|
};
|
|
@@ -694,6 +714,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
694
714
|
shaka.Player.createEmptyPayload_(),
|
|
695
715
|
walkerImplementation);
|
|
696
716
|
|
|
717
|
+
/** @private {shaka.util.Timer} */
|
|
718
|
+
this.checkVariantsTimer_ =
|
|
719
|
+
new shaka.util.Timer(() => this.checkVariants_());
|
|
720
|
+
|
|
697
721
|
// Even though |attach| will start in later interpreter cycles, it should be
|
|
698
722
|
// the LAST thing we do in the constructor because conceptually it relies on
|
|
699
723
|
// player having been initialized.
|
|
@@ -703,7 +727,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
703
727
|
}
|
|
704
728
|
|
|
705
729
|
/**
|
|
706
|
-
* @param {!shaka.
|
|
730
|
+
* @param {!shaka.util.FakeEvent.EventName} name
|
|
707
731
|
* @param {Map.<string, Object>=} data
|
|
708
732
|
* @return {!shaka.util.FakeEvent}
|
|
709
733
|
* @private
|
|
@@ -1129,7 +1153,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1129
1153
|
|
|
1130
1154
|
// We dispatch the loading event when someone calls |load| because we want
|
|
1131
1155
|
// to surface the user intent.
|
|
1132
|
-
this.dispatchEvent(this.makeEvent_(shaka.
|
|
1156
|
+
this.dispatchEvent(this.makeEvent_(shaka.util.FakeEvent.EventName.Loading));
|
|
1133
1157
|
|
|
1134
1158
|
// Right away we know what the asset uri and start-of-load time are. We will
|
|
1135
1159
|
// fill-in the rest of the information later.
|
|
@@ -1193,7 +1217,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1193
1217
|
events.onEnd = () => {
|
|
1194
1218
|
resolve();
|
|
1195
1219
|
// We dispatch the loaded event when the load promise is resolved
|
|
1196
|
-
this.dispatchEvent(
|
|
1220
|
+
this.dispatchEvent(
|
|
1221
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.Loaded));
|
|
1197
1222
|
};
|
|
1198
1223
|
events.onCancel = () => reject(this.createAbortLoadError_());
|
|
1199
1224
|
events.onError = (e) => reject(e);
|
|
@@ -1398,7 +1423,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1398
1423
|
await Promise.all(cleanupTasks);
|
|
1399
1424
|
|
|
1400
1425
|
// Dispatch the unloading event.
|
|
1401
|
-
this.dispatchEvent(
|
|
1426
|
+
this.dispatchEvent(
|
|
1427
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.Unloading));
|
|
1402
1428
|
|
|
1403
1429
|
// Remove everything that has to do with loading content from our payload
|
|
1404
1430
|
// since we are releasing everything that depended on it.
|
|
@@ -1412,6 +1438,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1412
1438
|
this.loadEventManager_.removeAll();
|
|
1413
1439
|
}
|
|
1414
1440
|
|
|
1441
|
+
// Stop the variant checker timer
|
|
1442
|
+
this.checkVariantsTimer_.stop();
|
|
1443
|
+
|
|
1415
1444
|
// Some observers use some playback components, shutting down the observers
|
|
1416
1445
|
// first ensures that they don't try to use the playback components
|
|
1417
1446
|
// mid-destroy.
|
|
@@ -1699,7 +1728,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1699
1728
|
this.regionTimeline_.addEventListener('regionadd', (event) => {
|
|
1700
1729
|
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
1701
1730
|
const region = event['region'];
|
|
1702
|
-
this.onRegionEvent_(
|
|
1731
|
+
this.onRegionEvent_(
|
|
1732
|
+
shaka.util.FakeEvent.EventName.TimelineRegionAdded, region);
|
|
1703
1733
|
|
|
1704
1734
|
if (this.adManager_) {
|
|
1705
1735
|
this.adManager_.onDashTimedMetadata(region);
|
|
@@ -1754,7 +1784,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1754
1784
|
|
|
1755
1785
|
// This event is fired after the manifest is parsed, but before any
|
|
1756
1786
|
// filtering takes place.
|
|
1757
|
-
const event =
|
|
1787
|
+
const event =
|
|
1788
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.ManifestParsed);
|
|
1758
1789
|
this.dispatchEvent(event);
|
|
1759
1790
|
|
|
1760
1791
|
// We require all manifests to have at least one variant.
|
|
@@ -1828,7 +1859,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1828
1859
|
},
|
|
1829
1860
|
onEvent: (e) => {
|
|
1830
1861
|
this.dispatchEvent(e);
|
|
1831
|
-
if (e.type == shaka.
|
|
1862
|
+
if (e.type == shaka.util.FakeEvent.EventName.DrmSessionUpdate &&
|
|
1863
|
+
firstEvent) {
|
|
1832
1864
|
firstEvent = false;
|
|
1833
1865
|
const now = Date.now() / 1000;
|
|
1834
1866
|
const delta = now - startTime;
|
|
@@ -1993,7 +2025,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1993
2025
|
// The event must be fired after we filter by restrictions but before the
|
|
1994
2026
|
// active stream is picked to allow those listening for the "streaming"
|
|
1995
2027
|
// event to make changes before streaming starts.
|
|
1996
|
-
this.dispatchEvent(
|
|
2028
|
+
this.dispatchEvent(
|
|
2029
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.Streaming));
|
|
1997
2030
|
|
|
1998
2031
|
// Pick the initial streams to play.
|
|
1999
2032
|
// however, we would skip switch to initial variant
|
|
@@ -2121,7 +2154,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2121
2154
|
},
|
|
2122
2155
|
onEvent: (e) => {
|
|
2123
2156
|
this.dispatchEvent(e);
|
|
2124
|
-
if (e.type == shaka.
|
|
2157
|
+
if (e.type == shaka.util.FakeEvent.EventName.DrmSessionUpdate &&
|
|
2158
|
+
firstEvent) {
|
|
2125
2159
|
firstEvent = false;
|
|
2126
2160
|
const now = Date.now() / 1000;
|
|
2127
2161
|
const delta = now - startTime;
|
|
@@ -2154,6 +2188,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2154
2188
|
const variant = {
|
|
2155
2189
|
id: 0,
|
|
2156
2190
|
language: 'und',
|
|
2191
|
+
disabledUntilTime: 0,
|
|
2157
2192
|
primary: false,
|
|
2158
2193
|
audio: null,
|
|
2159
2194
|
video: {
|
|
@@ -2346,7 +2381,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2346
2381
|
// The event doesn't mean as much for src= playback, since we don't control
|
|
2347
2382
|
// streaming. But we should fire it in this path anyway since some
|
|
2348
2383
|
// applications may be expecting it as a life-cycle event.
|
|
2349
|
-
this.dispatchEvent(
|
|
2384
|
+
this.dispatchEvent(
|
|
2385
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.Streaming));
|
|
2350
2386
|
|
|
2351
2387
|
// The "load" Promise is resolved when we have loaded the metadata. If we
|
|
2352
2388
|
// wait for the full data, that won't happen on Safari until the play button
|
|
@@ -2558,7 +2594,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2558
2594
|
dispatchMetadataEvent_(startTime, endTime, metadataType, payload) {
|
|
2559
2595
|
goog.asserts.assert(!endTime || startTime <= endTime,
|
|
2560
2596
|
'Metadata start time should be less or equal to the end time!');
|
|
2561
|
-
const eventName = shaka.
|
|
2597
|
+
const eventName = shaka.util.FakeEvent.EventName.Metadata;
|
|
2562
2598
|
const data = new Map()
|
|
2563
2599
|
.set('startTime', startTime)
|
|
2564
2600
|
.set('endTime', endTime)
|
|
@@ -2655,7 +2691,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2655
2691
|
/** @type {shaka.net.NetworkingEngine.OnHeadersReceived} */
|
|
2656
2692
|
const onHeadersReceived_ = (headers, request, requestType) => {
|
|
2657
2693
|
// Release a 'downloadheadersreceived' event.
|
|
2658
|
-
const name = shaka.
|
|
2694
|
+
const name = shaka.util.FakeEvent.EventName.DownloadHeadersReceived;
|
|
2659
2695
|
const data = new Map()
|
|
2660
2696
|
.set('headers', headers)
|
|
2661
2697
|
.set('request', request)
|
|
@@ -2665,7 +2701,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2665
2701
|
/** @type {shaka.net.NetworkingEngine.OnDownloadFailed} */
|
|
2666
2702
|
const onDownloadFailed_ = (request, error, httpResponseCode, aborted) => {
|
|
2667
2703
|
// Release a 'downloadfailed' event.
|
|
2668
|
-
const name = shaka.
|
|
2704
|
+
const name = shaka.util.FakeEvent.EventName.DownloadFailed;
|
|
2669
2705
|
const data = new Map()
|
|
2670
2706
|
.set('request', request)
|
|
2671
2707
|
.set('error', error)
|
|
@@ -2693,7 +2729,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2693
2729
|
this.manifest_,
|
|
2694
2730
|
this.config_.streaming,
|
|
2695
2731
|
startTime,
|
|
2696
|
-
() => this.onSeek_()
|
|
2732
|
+
() => this.onSeek_(),
|
|
2733
|
+
(event) => this.dispatchEvent(event));
|
|
2697
2734
|
}
|
|
2698
2735
|
|
|
2699
2736
|
/**
|
|
@@ -2715,13 +2752,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2715
2752
|
regionObserver.addEventListener('enter', (event) => {
|
|
2716
2753
|
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2717
2754
|
const region = event['region'];
|
|
2718
|
-
this.onRegionEvent_(
|
|
2755
|
+
this.onRegionEvent_(
|
|
2756
|
+
shaka.util.FakeEvent.EventName.TimelineRegionEnter, region);
|
|
2719
2757
|
});
|
|
2720
2758
|
|
|
2721
2759
|
regionObserver.addEventListener('exit', (event) => {
|
|
2722
2760
|
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2723
2761
|
const region = event['region'];
|
|
2724
|
-
this.onRegionEvent_(
|
|
2762
|
+
this.onRegionEvent_(
|
|
2763
|
+
shaka.util.FakeEvent.EventName.TimelineRegionExit, region);
|
|
2725
2764
|
});
|
|
2726
2765
|
|
|
2727
2766
|
regionObserver.addEventListener('skip', (event) => {
|
|
@@ -2732,8 +2771,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2732
2771
|
// If we are seeking, we don't want to surface the enter/exit events since
|
|
2733
2772
|
// they didn't play through them.
|
|
2734
2773
|
if (!seeking) {
|
|
2735
|
-
this.onRegionEvent_(
|
|
2736
|
-
|
|
2774
|
+
this.onRegionEvent_(
|
|
2775
|
+
shaka.util.FakeEvent.EventName.TimelineRegionEnter, region);
|
|
2776
|
+
this.onRegionEvent_(
|
|
2777
|
+
shaka.util.FakeEvent.EventName.TimelineRegionExit, region);
|
|
2737
2778
|
}
|
|
2738
2779
|
});
|
|
2739
2780
|
|
|
@@ -4089,8 +4130,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4089
4130
|
for (const chaptersTrack of chaptersTracksWithLanguage) {
|
|
4090
4131
|
if (chaptersTrack && chaptersTrack.cues) {
|
|
4091
4132
|
for (const cue of chaptersTrack.cues) {
|
|
4133
|
+
let id = cue.id;
|
|
4134
|
+
if (!id || id == '') {
|
|
4135
|
+
id = cue.startTime + '-' + cue.endTime + '-' + cue.text;
|
|
4136
|
+
}
|
|
4092
4137
|
/** @type {shaka.extern.Chapter} */
|
|
4093
4138
|
const chapter = {
|
|
4139
|
+
id: id,
|
|
4094
4140
|
title: cue.text,
|
|
4095
4141
|
startTime: cue.startTime,
|
|
4096
4142
|
endTime: cue.endTime,
|
|
@@ -4355,6 +4401,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4355
4401
|
this.stats_.setCompletionPercent(Math.round(100 * completionRatio));
|
|
4356
4402
|
}
|
|
4357
4403
|
|
|
4404
|
+
if (this.playhead_) {
|
|
4405
|
+
this.stats_.setGapsJumped(this.playhead_.getGapsJumped());
|
|
4406
|
+
this.stats_.setStallsDetected(this.playhead_.getStallsDetected());
|
|
4407
|
+
}
|
|
4408
|
+
|
|
4358
4409
|
if (element.getVideoPlaybackQuality) {
|
|
4359
4410
|
const info = element.getVideoPlaybackQuality();
|
|
4360
4411
|
|
|
@@ -5118,7 +5169,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5118
5169
|
|
|
5119
5170
|
// Surface the buffering event so that the app knows if/when we are
|
|
5120
5171
|
// buffering.
|
|
5121
|
-
const eventName = shaka.
|
|
5172
|
+
const eventName = shaka.util.FakeEvent.EventName.Buffering;
|
|
5122
5173
|
const data = (new Map()).set('buffering', isBuffering);
|
|
5123
5174
|
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
5124
5175
|
}
|
|
@@ -5153,7 +5204,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5153
5204
|
this.playRateController_.set(newRate);
|
|
5154
5205
|
}
|
|
5155
5206
|
|
|
5156
|
-
const event = this.makeEvent_(shaka.
|
|
5207
|
+
const event = this.makeEvent_(shaka.util.FakeEvent.EventName.RateChange);
|
|
5157
5208
|
this.dispatchEvent(event);
|
|
5158
5209
|
}
|
|
5159
5210
|
|
|
@@ -5253,6 +5304,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5253
5304
|
}
|
|
5254
5305
|
}
|
|
5255
5306
|
|
|
5307
|
+
/**
|
|
5308
|
+
* Re-apply restrictions to the variants, to re-enable variants that were
|
|
5309
|
+
* temporarily disabled due to network errors.
|
|
5310
|
+
* If any variants are enabled this way, a new variant might be chosen for
|
|
5311
|
+
* playback.
|
|
5312
|
+
* @private
|
|
5313
|
+
*/
|
|
5314
|
+
checkVariants_() {
|
|
5315
|
+
const tracksChanged = shaka.util.StreamUtils.applyRestrictions(
|
|
5316
|
+
this.manifest_.variants, this.config_.restrictions, this.maxHwRes_);
|
|
5317
|
+
if (tracksChanged) {
|
|
5318
|
+
this.chooseVariant_();
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
|
|
5256
5322
|
/**
|
|
5257
5323
|
* Choose a text stream from all possible text streams while taking into
|
|
5258
5324
|
* account user preference.
|
|
@@ -5485,7 +5551,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5485
5551
|
const data = new Map()
|
|
5486
5552
|
.set('oldTrack', from)
|
|
5487
5553
|
.set('newTrack', to);
|
|
5488
|
-
const event =
|
|
5554
|
+
const event =
|
|
5555
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.Adaptation, data);
|
|
5489
5556
|
this.delayDispatchEvent_(event);
|
|
5490
5557
|
}
|
|
5491
5558
|
|
|
@@ -5496,7 +5563,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5496
5563
|
onTracksChanged_() {
|
|
5497
5564
|
// Delay the 'trackschanged' event so StreamingEngine has time to absorb the
|
|
5498
5565
|
// changes before the user tries to query it.
|
|
5499
|
-
const event = this.makeEvent_(shaka.
|
|
5566
|
+
const event = this.makeEvent_(shaka.util.FakeEvent.EventName.TracksChanged);
|
|
5500
5567
|
this.delayDispatchEvent_(event);
|
|
5501
5568
|
}
|
|
5502
5569
|
|
|
@@ -5512,7 +5579,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5512
5579
|
const data = new Map()
|
|
5513
5580
|
.set('oldTrack', from)
|
|
5514
5581
|
.set('newTrack', to);
|
|
5515
|
-
const event =
|
|
5582
|
+
const event =
|
|
5583
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.VariantChanged, data);
|
|
5516
5584
|
this.delayDispatchEvent_(event);
|
|
5517
5585
|
}
|
|
5518
5586
|
|
|
@@ -5523,13 +5591,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5523
5591
|
onTextChanged_() {
|
|
5524
5592
|
// Delay the 'textchanged' event so StreamingEngine time to absorb the
|
|
5525
5593
|
// changes before the user tries to query it.
|
|
5526
|
-
const event = this.makeEvent_(shaka.
|
|
5594
|
+
const event = this.makeEvent_(shaka.util.FakeEvent.EventName.TextChanged);
|
|
5527
5595
|
this.delayDispatchEvent_(event);
|
|
5528
5596
|
}
|
|
5529
5597
|
|
|
5530
5598
|
/** @private */
|
|
5531
5599
|
onTextTrackVisibility_() {
|
|
5532
|
-
const event =
|
|
5600
|
+
const event =
|
|
5601
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.TextTrackVisibility);
|
|
5533
5602
|
this.delayDispatchEvent_(event);
|
|
5534
5603
|
}
|
|
5535
5604
|
|
|
@@ -5537,7 +5606,73 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5537
5606
|
onAbrStatusChanged_() {
|
|
5538
5607
|
const data = (new Map()).set('newStatus', this.config_.abr.enabled);
|
|
5539
5608
|
this.delayDispatchEvent_(this.makeEvent_(
|
|
5540
|
-
shaka.
|
|
5609
|
+
shaka.util.FakeEvent.EventName.AbrStatusChanged, data));
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
/**
|
|
5613
|
+
* Tries to recover from NETWORK HTTP_ERROR, temporary disabling the current
|
|
5614
|
+
* problematic variant.
|
|
5615
|
+
* @param {!shaka.util.Error} error
|
|
5616
|
+
* @return {boolean}
|
|
5617
|
+
* @private
|
|
5618
|
+
*/
|
|
5619
|
+
tryToRecoverFromError_(error) {
|
|
5620
|
+
if ((error.code != shaka.util.Error.Code.HTTP_ERROR &&
|
|
5621
|
+
error.code != shaka.util.Error.Code.SEGMENT_MISSING) ||
|
|
5622
|
+
error.category != shaka.util.Error.Category.NETWORK) {
|
|
5623
|
+
return false;
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5626
|
+
let maxDisabledTime = this.config_.streaming.maxDisabledTime;
|
|
5627
|
+
if (maxDisabledTime == 0) {
|
|
5628
|
+
if (error.code == shaka.util.Error.Code.SEGMENT_MISSING) {
|
|
5629
|
+
// Spec: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.3
|
|
5630
|
+
// The client SHOULD NOT attempt to load Media Segments that have been
|
|
5631
|
+
// marked with an EXT-X-GAP tag, or to load Partial Segments with a
|
|
5632
|
+
// GAP=YES attribute. Instead, clients are encouraged to look for
|
|
5633
|
+
// another Variant Stream of the same Rendition which does not have the
|
|
5634
|
+
// same gap, and play that instead.
|
|
5635
|
+
maxDisabledTime = 1;
|
|
5636
|
+
} else {
|
|
5637
|
+
return false;
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
|
|
5641
|
+
if (error.code == shaka.util.Error.Code.HTTP_ERROR) {
|
|
5642
|
+
shaka.log.debug('Recoverable NETWORK HTTP_ERROR, trying to recover...');
|
|
5643
|
+
}
|
|
5644
|
+
|
|
5645
|
+
// Obtain the active variant and disable it from manifest variants
|
|
5646
|
+
const activeVariantTrack = this.getVariantTracks().find((t) => t.active);
|
|
5647
|
+
goog.asserts.assert(activeVariantTrack, 'Active variant should be found');
|
|
5648
|
+
const manifest = this.manifest_;
|
|
5649
|
+
for (const variant of manifest.variants) {
|
|
5650
|
+
if (variant.id === activeVariantTrack.id) {
|
|
5651
|
+
variant.disabledUntilTime = (Date.now() / 1000) + maxDisabledTime;
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5654
|
+
|
|
5655
|
+
// Apply restrictions in order to disable variants
|
|
5656
|
+
shaka.util.StreamUtils.applyRestrictions(
|
|
5657
|
+
manifest.variants, this.config_.restrictions, this.maxHwRes_);
|
|
5658
|
+
|
|
5659
|
+
// Select for a new variant
|
|
5660
|
+
const chosenVariant = this.chooseVariant_();
|
|
5661
|
+
if (!chosenVariant) {
|
|
5662
|
+
shaka.log.warning('Not enough variants to recover from error');
|
|
5663
|
+
return false;
|
|
5664
|
+
}
|
|
5665
|
+
|
|
5666
|
+
// Get the safeMargin to ensure a seamless playback
|
|
5667
|
+
const {video} = this.getBufferedInfo();
|
|
5668
|
+
const safeMargin =
|
|
5669
|
+
video.reduce((size, {start, end}) => size + end - start, 0);
|
|
5670
|
+
|
|
5671
|
+
this.switchVariant_(chosenVariant, /* fromAdaptation= */ false,
|
|
5672
|
+
/* clearBuffers= */ true, /* safeMargin= */ safeMargin);
|
|
5673
|
+
|
|
5674
|
+
this.checkVariantsTimer_.tickAfter(maxDisabledTime);
|
|
5675
|
+
return true;
|
|
5541
5676
|
}
|
|
5542
5677
|
|
|
5543
5678
|
/**
|
|
@@ -5553,7 +5688,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5553
5688
|
return;
|
|
5554
5689
|
}
|
|
5555
5690
|
|
|
5556
|
-
|
|
5691
|
+
|
|
5692
|
+
if (this.tryToRecoverFromError_(error)) {
|
|
5693
|
+
error.handled = true;
|
|
5694
|
+
return;
|
|
5695
|
+
}
|
|
5696
|
+
|
|
5697
|
+
const eventName = shaka.util.FakeEvent.EventName.Error;
|
|
5557
5698
|
const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
|
|
5558
5699
|
this.dispatchEvent(event);
|
|
5559
5700
|
if (event.defaultPrevented) {
|
|
@@ -5567,7 +5708,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5567
5708
|
* copy here because this is the transition point between the player and the
|
|
5568
5709
|
* app.
|
|
5569
5710
|
*
|
|
5570
|
-
* @param {!shaka.
|
|
5711
|
+
* @param {!shaka.util.FakeEvent.EventName} eventName
|
|
5571
5712
|
* @param {shaka.extern.TimelineRegionInfo} region
|
|
5572
5713
|
*
|
|
5573
5714
|
* @private
|
|
@@ -5616,7 +5757,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5616
5757
|
.set('position', position);
|
|
5617
5758
|
|
|
5618
5759
|
this.dispatchEvent(this.makeEvent_(
|
|
5619
|
-
shaka.
|
|
5760
|
+
shaka.util.FakeEvent.EventName.MediaQualityChanged, data));
|
|
5620
5761
|
}
|
|
5621
5762
|
|
|
5622
5763
|
/**
|
|
@@ -5762,7 +5903,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5762
5903
|
this.parser_.onExpirationUpdated(keyId, expiration);
|
|
5763
5904
|
}
|
|
5764
5905
|
|
|
5765
|
-
const event =
|
|
5906
|
+
const event =
|
|
5907
|
+
this.makeEvent_(shaka.util.FakeEvent.EventName.ExpirationUpdated);
|
|
5766
5908
|
this.dispatchEvent(event);
|
|
5767
5909
|
}
|
|
5768
5910
|
|
|
@@ -6375,42 +6517,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6375
6517
|
}
|
|
6376
6518
|
};
|
|
6377
6519
|
|
|
6378
|
-
/**
|
|
6379
|
-
* An internal enum that contains the string values of all of the player events.
|
|
6380
|
-
* This exists primarily to act as an implicit list of events, for tests.
|
|
6381
|
-
*
|
|
6382
|
-
* @enum {string}
|
|
6383
|
-
*/
|
|
6384
|
-
shaka.Player.EventName = {
|
|
6385
|
-
AbrStatusChanged: 'abrstatuschanged',
|
|
6386
|
-
Adaptation: 'adaptation',
|
|
6387
|
-
Buffering: 'buffering',
|
|
6388
|
-
DownloadFailed: 'downloadfailed',
|
|
6389
|
-
DownloadHeadersReceived: 'downloadheadersreceived',
|
|
6390
|
-
DrmSessionUpdate: 'drmsessionupdate',
|
|
6391
|
-
Emsg: 'emsg',
|
|
6392
|
-
Error: 'error',
|
|
6393
|
-
ExpirationUpdated: 'expirationupdated',
|
|
6394
|
-
Loaded: 'loaded',
|
|
6395
|
-
Loading: 'loading',
|
|
6396
|
-
ManifestParsed: 'manifestparsed',
|
|
6397
|
-
MediaQualityChanged: 'mediaqualitychanged',
|
|
6398
|
-
Metadata: 'metadata',
|
|
6399
|
-
OnStateChange: 'onstatechange',
|
|
6400
|
-
OnStateIdle: 'onstateidle',
|
|
6401
|
-
RateChange: 'ratechange',
|
|
6402
|
-
SessionDataEvent: 'sessiondata',
|
|
6403
|
-
Streaming: 'streaming',
|
|
6404
|
-
TextChanged: 'textchanged',
|
|
6405
|
-
TextTrackVisibility: 'texttrackvisibility',
|
|
6406
|
-
TimelineRegionAdded: 'timelineregionadded',
|
|
6407
|
-
TimelineRegionEnter: 'timelineregionenter',
|
|
6408
|
-
TimelineRegionExit: 'timelineregionexit',
|
|
6409
|
-
TracksChanged: 'trackschanged',
|
|
6410
|
-
Unloading: 'unloading',
|
|
6411
|
-
VariantChanged: 'variantchanged',
|
|
6412
|
-
};
|
|
6413
|
-
|
|
6414
6520
|
|
|
6415
6521
|
/**
|
|
6416
6522
|
* In order to know what method of loading the player used for some content, we
|
|
@@ -6450,7 +6556,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6450
6556
|
* @define {string} A version number taken from git at compile time.
|
|
6451
6557
|
* @export
|
|
6452
6558
|
*/
|
|
6453
|
-
shaka.Player.version = 'v4.
|
|
6559
|
+
shaka.Player.version = 'v4.1.2-uncompiled';
|
|
6454
6560
|
|
|
6455
6561
|
// Initialize the deprecation system using the version string we just set
|
|
6456
6562
|
// on the player.
|
package/lib/text/cue.js
CHANGED
|
@@ -411,6 +411,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
411
411
|
style.paddingRight =
|
|
412
412
|
shaka.text.UITextDisplayer.convertLengthValue_(
|
|
413
413
|
cue.linePadding, cue, this.videoContainer_);
|
|
414
|
+
style.textShadow = cue.textShadow;
|
|
414
415
|
|
|
415
416
|
if (cue.backgroundImage) {
|
|
416
417
|
style.backgroundImage = 'url(\'' + cue.backgroundImage + '\')';
|
|
@@ -293,6 +293,10 @@ shaka.text.VttTextParser = class {
|
|
|
293
293
|
validStyle = true;
|
|
294
294
|
cue.opacity = parseFloat(value);
|
|
295
295
|
break;
|
|
296
|
+
case 'text-shadow':
|
|
297
|
+
validStyle = true;
|
|
298
|
+
cue.textShadow = value;
|
|
299
|
+
break;
|
|
296
300
|
case 'white-space':
|
|
297
301
|
validStyle = true;
|
|
298
302
|
cue.wrapLine = value != 'noWrap';
|
package/lib/util/error.js
CHANGED
|
@@ -267,6 +267,12 @@ shaka.util.Error.Code = {
|
|
|
267
267
|
*/
|
|
268
268
|
'ATTEMPTS_EXHAUSTED': 1010,
|
|
269
269
|
|
|
270
|
+
/**
|
|
271
|
+
* The segment is missing.
|
|
272
|
+
* <br> error.data[0] is the URI.
|
|
273
|
+
*/
|
|
274
|
+
'SEGMENT_MISSING': 1011,
|
|
275
|
+
|
|
270
276
|
|
|
271
277
|
/** The text parser failed to parse a text stream due to an invalid header. */
|
|
272
278
|
'INVALID_TEXT_HEADER': 2000,
|