shaka-player 3.1.2 → 3.1.6
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/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +3 -0
- package/CHANGELOG.md +170 -1
- package/CONTRIBUTORS +3 -0
- package/build/checkversion.py +1 -1
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/build/test.py +6 -0
- package/demo/common/assets.js +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +7 -7
- package/dist/locales.js +35 -35
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +172 -3
- package/dist/shaka-player.compiled.debug.d.ts +172 -3
- package/dist/shaka-player.compiled.debug.externs.js +184 -1
- package/dist/shaka-player.compiled.debug.js +390 -370
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +184 -1
- package/dist/shaka-player.compiled.js +809 -805
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +174 -14
- package/dist/shaka-player.ui.debug.d.ts +174 -14
- package/dist/shaka-player.ui.debug.externs.js +200 -26
- package/dist/shaka-player.ui.debug.js +431 -412
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +200 -26
- package/dist/shaka-player.ui.js +934 -929
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +20 -15
- package/lib/ads/ad_manager.js +30 -11
- package/lib/ads/client_side_ad_manager.js +76 -86
- package/lib/ads/server_side_ad_manager.js +26 -11
- package/lib/cast/cast_proxy.js +6 -2
- package/lib/cast/cast_receiver.js +5 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +131 -24
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +44 -17
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/streaming_engine.js +8 -4
- package/lib/net/networking_engine.js +6 -1
- package/lib/offline/storage.js +10 -11
- package/lib/player.js +66 -30
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +73 -21
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +6 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +135 -87
- package/lib/text/ui_text_displayer.js +213 -80
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/fake_event.js +53 -7
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +77 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +43 -0
- package/test/dash/dash_parser_manifest_unit.js +222 -3
- package/test/hls/hls_parser_unit.js +79 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +56 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +145 -0
- package/test/player_integration.js +38 -0
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +4 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- package/test/text/ui_text_displayer_unit.js +93 -20
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +21 -8
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/stream_utils_unit.js +59 -2
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +11 -6
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +6 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +40 -87
- package/ui/pip_button.js +2 -3
- package/ui/text_selection.js +1 -0
|
@@ -154,8 +154,10 @@ shaka.media.GapJumpingController = class {
|
|
|
154
154
|
}
|
|
155
155
|
// Don't gap jump while paused, so that you don't constantly jump ahead
|
|
156
156
|
// while paused on a livestream. We make an exception for time 0, since we
|
|
157
|
-
// may be _required_ to seek on startup before play can begin
|
|
158
|
-
|
|
157
|
+
// may be _required_ to seek on startup before play can begin, but only if
|
|
158
|
+
// autoplay is enabled.
|
|
159
|
+
if (this.video_.paused && (this.video_.currentTime != 0 ||
|
|
160
|
+
(!this.video_.autoplay && this.video_.currentTime == 0))) {
|
|
159
161
|
return;
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -215,8 +217,10 @@ shaka.media.GapJumpingController = class {
|
|
|
215
217
|
this.didFireLargeGap_ = true;
|
|
216
218
|
|
|
217
219
|
// Event firing is synchronous.
|
|
218
|
-
const
|
|
219
|
-
'
|
|
220
|
+
const data = new Map()
|
|
221
|
+
.set('currentTime', currentTime)
|
|
222
|
+
.set('gapSize', jumpSize);
|
|
223
|
+
const event = new shaka.util.FakeEvent('largegap', data);
|
|
220
224
|
event.cancelable = true;
|
|
221
225
|
this.onEvent_(event);
|
|
222
226
|
|
|
@@ -48,8 +48,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
48
48
|
* @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
|
|
49
49
|
* onMetadata
|
|
50
50
|
*/
|
|
51
|
-
constructor(video, closedCaptionParser, textDisplayer,
|
|
52
|
-
onMetadata) {
|
|
51
|
+
constructor(video, closedCaptionParser, textDisplayer, onMetadata) {
|
|
53
52
|
/** @private {HTMLMediaElement} */
|
|
54
53
|
this.video_ = video;
|
|
55
54
|
|
package/lib/media/playhead.js
CHANGED
|
@@ -80,7 +80,7 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
80
80
|
// We listen for the loaded-data-event so that we know when we can
|
|
81
81
|
// interact with |currentTime|.
|
|
82
82
|
const onLoaded = () => {
|
|
83
|
-
if (this.startTime_ == null) {
|
|
83
|
+
if (this.startTime_ == null || this.startTime_ == 0) {
|
|
84
84
|
this.started_ = true;
|
|
85
85
|
} else {
|
|
86
86
|
// Startup is complete only when the video element acknowledges the
|
|
@@ -8,16 +8,19 @@ goog.provide('shaka.media.RegionObserver');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.media.IPlayheadObserver');
|
|
10
10
|
goog.require('shaka.media.RegionTimeline');
|
|
11
|
+
goog.require('shaka.util.EventManager');
|
|
12
|
+
goog.require('shaka.util.FakeEvent');
|
|
13
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* The region observer watches a region timeline and playhead, and fires events
|
|
15
|
-
* (
|
|
18
|
+
* ('enter', 'exit', 'skip') as the playhead moves.
|
|
16
19
|
*
|
|
17
20
|
* @implements {shaka.media.IPlayheadObserver}
|
|
18
21
|
* @final
|
|
19
22
|
*/
|
|
20
|
-
shaka.media.RegionObserver = class {
|
|
23
|
+
shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
21
24
|
/**
|
|
22
25
|
* Create a region observer for the given timeline. The observer does not
|
|
23
26
|
* own the timeline, only uses it. This means that the observer should NOT
|
|
@@ -26,6 +29,8 @@ shaka.media.RegionObserver = class {
|
|
|
26
29
|
* @param {!shaka.media.RegionTimeline} timeline
|
|
27
30
|
*/
|
|
28
31
|
constructor(timeline) {
|
|
32
|
+
super();
|
|
33
|
+
|
|
29
34
|
/** @private {shaka.media.RegionTimeline} */
|
|
30
35
|
this.timeline_ = timeline;
|
|
31
36
|
|
|
@@ -39,13 +44,6 @@ shaka.media.RegionObserver = class {
|
|
|
39
44
|
*/
|
|
40
45
|
this.oldPosition_ = new Map();
|
|
41
46
|
|
|
42
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
43
|
-
this.onEnter_ = (region, seeking) => {};
|
|
44
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
45
|
-
this.onExit_ = (region, seeking) => {};
|
|
46
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
47
|
-
this.onSkip_ = (region, seeking) => {};
|
|
48
|
-
|
|
49
47
|
// To make the rules easier to read, alias all the relative positions.
|
|
50
48
|
const RelativePosition = shaka.media.RegionObserver.RelativePosition_;
|
|
51
49
|
const BEFORE_THE_REGION = RelativePosition.BEFORE_THE_REGION;
|
|
@@ -62,39 +60,48 @@ shaka.media.RegionObserver = class {
|
|
|
62
60
|
{
|
|
63
61
|
weWere: null,
|
|
64
62
|
weAre: IN_THE_REGION,
|
|
65
|
-
invoke: (region, seeking) => this.
|
|
63
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
66
64
|
},
|
|
67
65
|
{
|
|
68
66
|
weWere: BEFORE_THE_REGION,
|
|
69
67
|
weAre: IN_THE_REGION,
|
|
70
|
-
invoke: (region, seeking) => this.
|
|
68
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
71
69
|
},
|
|
72
70
|
{
|
|
73
71
|
weWere: AFTER_THE_REGION,
|
|
74
72
|
weAre: IN_THE_REGION,
|
|
75
|
-
invoke: (region, seeking) => this.
|
|
73
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
76
74
|
},
|
|
77
75
|
{
|
|
78
76
|
weWere: IN_THE_REGION,
|
|
79
77
|
weAre: BEFORE_THE_REGION,
|
|
80
|
-
invoke: (region, seeking) => this.
|
|
78
|
+
invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
|
|
81
79
|
},
|
|
82
80
|
{
|
|
83
81
|
weWere: IN_THE_REGION,
|
|
84
82
|
weAre: AFTER_THE_REGION,
|
|
85
|
-
invoke: (region, seeking) => this.
|
|
83
|
+
invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
|
|
86
84
|
},
|
|
87
85
|
{
|
|
88
86
|
weWere: BEFORE_THE_REGION,
|
|
89
87
|
weAre: AFTER_THE_REGION,
|
|
90
|
-
invoke: (region, seeking) => this.
|
|
88
|
+
invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
|
|
91
89
|
},
|
|
92
90
|
{
|
|
93
91
|
weWere: AFTER_THE_REGION,
|
|
94
92
|
weAre: BEFORE_THE_REGION,
|
|
95
|
-
invoke: (region, seeking) => this.
|
|
93
|
+
invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
|
|
96
94
|
},
|
|
97
95
|
];
|
|
96
|
+
|
|
97
|
+
/** @private {shaka.util.EventManager} */
|
|
98
|
+
this.eventManager_ = new shaka.util.EventManager();
|
|
99
|
+
|
|
100
|
+
this.eventManager_.listen(this.timeline_, 'regionremove', (event) => {
|
|
101
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
102
|
+
const region = event['region'];
|
|
103
|
+
this.oldPosition_.delete(region);
|
|
104
|
+
});
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
/** @override */
|
|
@@ -105,11 +112,10 @@ shaka.media.RegionObserver = class {
|
|
|
105
112
|
// needed.
|
|
106
113
|
this.oldPosition_.clear();
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.onSkip_ = (region, seeking) => {};
|
|
115
|
+
this.eventManager_.release();
|
|
116
|
+
this.eventManager_ = null;
|
|
117
|
+
|
|
118
|
+
super.release();
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/** @override */
|
|
@@ -134,20 +140,20 @@ shaka.media.RegionObserver = class {
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
/**
|
|
137
|
-
*
|
|
143
|
+
* Dispatch events of the given type. All event types in this class have the
|
|
144
|
+
* same parameters: region and seeking.
|
|
138
145
|
*
|
|
139
|
-
* @param {
|
|
140
|
-
*
|
|
141
|
-
* @param {
|
|
142
|
-
*
|
|
143
|
-
* @param {shaka.media.RegionObserver.EventListener} onSkip
|
|
144
|
-
* The callback for when we move from before to after a region or from
|
|
145
|
-
* after to before a region.
|
|
146
|
+
* @param {string} eventType
|
|
147
|
+
* @param {shaka.extern.TimelineRegionInfo} region
|
|
148
|
+
* @param {boolean} seeking
|
|
149
|
+
* @private
|
|
146
150
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
onEvent_(eventType, region, seeking) {
|
|
152
|
+
const event = new shaka.util.FakeEvent(eventType, new Map([
|
|
153
|
+
['region', region],
|
|
154
|
+
['seeking', seeking],
|
|
155
|
+
]));
|
|
156
|
+
this.dispatchEvent(event);
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
/**
|
|
@@ -6,26 +6,30 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.media.RegionTimeline');
|
|
8
8
|
|
|
9
|
+
goog.require('shaka.util.FakeEvent');
|
|
10
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
9
11
|
goog.require('shaka.util.IReleasable');
|
|
10
12
|
goog.require('shaka.util.Timer');
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The region timeline is a set of unique timeline region info entries. When
|
|
15
|
-
* a new entry is added, the
|
|
17
|
+
* a new entry is added, the 'regionadd' event will be fired. When an entry is
|
|
18
|
+
* deleted, the 'regionremove' event will be fired.
|
|
16
19
|
*
|
|
17
20
|
* @implements {shaka.util.IReleasable}
|
|
18
21
|
* @final
|
|
19
22
|
*/
|
|
20
|
-
shaka.media.RegionTimeline = class {
|
|
23
|
+
shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
21
24
|
/**
|
|
22
25
|
* @param {!function():{start: number, end: number}} getSeekRange
|
|
23
26
|
*/
|
|
24
27
|
constructor(getSeekRange) {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
super();
|
|
29
|
+
|
|
27
30
|
/** @private {!Set.<shaka.extern.TimelineRegionInfo>} */
|
|
28
31
|
this.regions_ = new Set();
|
|
32
|
+
|
|
29
33
|
/** @private {!function():{start: number, end: number}} */
|
|
30
34
|
this.getSeekRange_ = getSeekRange;
|
|
31
35
|
|
|
@@ -44,22 +48,9 @@ shaka.media.RegionTimeline = class {
|
|
|
44
48
|
|
|
45
49
|
/** @override */
|
|
46
50
|
release() {
|
|
47
|
-
// Prevent us from holding onto any external references via the callback.
|
|
48
|
-
this.onAddRegion_ = (region) => {};
|
|
49
51
|
this.regions_.clear();
|
|
50
52
|
this.filterTimer_.stop();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Set the callbacks for events. This will override any previous calls to
|
|
55
|
-
* |setListeners|.
|
|
56
|
-
*
|
|
57
|
-
* @param {function(shaka.extern.TimelineRegionInfo)} onAddRegion
|
|
58
|
-
* Set the callback for when we add a new region. This callback will only
|
|
59
|
-
* be called when a region is unique (we reject duplicate regions).
|
|
60
|
-
*/
|
|
61
|
-
setListeners(onAddRegion) {
|
|
62
|
-
this.onAddRegion_ = onAddRegion;
|
|
53
|
+
super.release();
|
|
63
54
|
}
|
|
64
55
|
|
|
65
56
|
/**
|
|
@@ -72,7 +63,10 @@ shaka.media.RegionTimeline = class {
|
|
|
72
63
|
// instead of making the parser track it.
|
|
73
64
|
if (similarRegion == null) {
|
|
74
65
|
this.regions_.add(region);
|
|
75
|
-
|
|
66
|
+
const event = new shaka.util.FakeEvent('regionadd', new Map([
|
|
67
|
+
['region', region],
|
|
68
|
+
]));
|
|
69
|
+
this.dispatchEvent(event);
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
|
|
@@ -89,6 +83,10 @@ shaka.media.RegionTimeline = class {
|
|
|
89
83
|
// reson to store or process them.
|
|
90
84
|
if (region.endTime < seekRange.start) {
|
|
91
85
|
this.regions_.delete(region);
|
|
86
|
+
const event = new shaka.util.FakeEvent('regionremove', new Map([
|
|
87
|
+
['region', region],
|
|
88
|
+
]));
|
|
89
|
+
this.dispatchEvent(event);
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
}
|
|
@@ -623,7 +623,7 @@ shaka.media.StreamingEngine = class {
|
|
|
623
623
|
|
|
624
624
|
// Don't clear the buffer unless something is buffered. This extra
|
|
625
625
|
// check prevents extra, useless calls to clear the buffer.
|
|
626
|
-
if (somethingBuffered) {
|
|
626
|
+
if (somethingBuffered || mediaState.performingUpdate) {
|
|
627
627
|
this.forceClearBuffer_(mediaState);
|
|
628
628
|
streamCleared = true;
|
|
629
629
|
}
|
|
@@ -977,7 +977,10 @@ shaka.media.StreamingEngine = class {
|
|
|
977
977
|
unscaledBufferingGoal * this.bufferingGoalScale_;
|
|
978
978
|
|
|
979
979
|
// Check if we've buffered to the end of the presentation.
|
|
980
|
-
|
|
980
|
+
const timeUntilEnd =
|
|
981
|
+
this.manifest_.presentationTimeline.getDuration() - timeNeeded;
|
|
982
|
+
const oneMicrosecond = 1e-6;
|
|
983
|
+
if (timeUntilEnd < oneMicrosecond) {
|
|
981
984
|
// We shouldn't rebuffer if the playhead is close to the end of the
|
|
982
985
|
// presentation.
|
|
983
986
|
shaka.log.debug(logPrefix, 'buffered to end of presentation');
|
|
@@ -1641,7 +1644,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1641
1644
|
// See DASH sec. 5.10.3.3.1
|
|
1642
1645
|
// If a DASH client detects an event message box with a scheme that is not
|
|
1643
1646
|
// defined in MPD, the client is expected to ignore it.
|
|
1644
|
-
if (emsgSchemeIdUris.includes(schemeId)) {
|
|
1647
|
+
if (emsgSchemeIdUris && emsgSchemeIdUris.includes(schemeId)) {
|
|
1645
1648
|
// See DASH sec. 5.10.4.1
|
|
1646
1649
|
// A special scheme in DASH used to signal manifest updates.
|
|
1647
1650
|
if (schemeId == 'urn:mpeg:dash:event:2012') {
|
|
@@ -1662,7 +1665,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1662
1665
|
|
|
1663
1666
|
// Dispatch an event to notify the application about the emsg box.
|
|
1664
1667
|
const eventName = shaka.Player.EventName.Emsg;
|
|
1665
|
-
const
|
|
1668
|
+
const data = (new Map()).set('detail', emsg);
|
|
1669
|
+
const event = new shaka.util.FakeEvent(eventName, data);
|
|
1666
1670
|
this.playerInterface_.onEvent(event);
|
|
1667
1671
|
}
|
|
1668
1672
|
}
|
|
@@ -222,6 +222,10 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
222
222
|
this.destroyed_ = true;
|
|
223
223
|
this.requestFilters_.clear();
|
|
224
224
|
this.responseFilters_.clear();
|
|
225
|
+
|
|
226
|
+
// FakeEventTarget implements IReleasable
|
|
227
|
+
super.release();
|
|
228
|
+
|
|
225
229
|
return this.operationManager_.destroy();
|
|
226
230
|
}
|
|
227
231
|
|
|
@@ -544,7 +548,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
544
548
|
}
|
|
545
549
|
|
|
546
550
|
if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
|
|
547
|
-
const
|
|
551
|
+
const data = (new Map()).set('error', error);
|
|
552
|
+
const event = new shaka.util.FakeEvent('retry', data);
|
|
548
553
|
this.dispatchEvent(event);
|
|
549
554
|
|
|
550
555
|
// Move to the next URI.
|
package/lib/offline/storage.js
CHANGED
|
@@ -477,23 +477,22 @@ shaka.offline.Storage = class {
|
|
|
477
477
|
* @private
|
|
478
478
|
*/
|
|
479
479
|
async filterManifest_(manifest, drmEngine, config) {
|
|
480
|
-
const StreamUtils = shaka.util.StreamUtils;
|
|
481
|
-
|
|
482
480
|
// Filter the manifest based on the restrictions given in the player
|
|
483
481
|
// configuration.
|
|
484
482
|
const maxHwRes = {width: Infinity, height: Infinity};
|
|
485
|
-
StreamUtils.filterByRestrictions(
|
|
483
|
+
shaka.util.StreamUtils.filterByRestrictions(
|
|
484
|
+
manifest, config.restrictions, maxHwRes);
|
|
486
485
|
|
|
487
486
|
// Filter the manifest based on what we know media source will be able to
|
|
488
487
|
// play later (no point storing something we can't play).
|
|
489
488
|
if (config.useMediaCapabilities) {
|
|
490
|
-
StreamUtils.filterManifestByMediaCapabilities(
|
|
489
|
+
await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
|
|
491
490
|
manifest, config.offline.usePersistentLicense);
|
|
492
491
|
} else {
|
|
493
|
-
StreamUtils.filterManifestByMediaSource(manifest);
|
|
492
|
+
shaka.util.StreamUtils.filterManifestByMediaSource(manifest);
|
|
494
493
|
// Filter the manifest based on what we know our drm system will support
|
|
495
494
|
// playing later.
|
|
496
|
-
StreamUtils.filterManifestByDrm(manifest, drmEngine);
|
|
495
|
+
shaka.util.StreamUtils.filterManifestByDrm(manifest, drmEngine);
|
|
497
496
|
}
|
|
498
497
|
|
|
499
498
|
// Gather all tracks.
|
|
@@ -501,23 +500,23 @@ shaka.offline.Storage = class {
|
|
|
501
500
|
|
|
502
501
|
// Choose the codec that has the lowest average bandwidth.
|
|
503
502
|
const preferredAudioChannelCount = config.preferredAudioChannelCount;
|
|
504
|
-
StreamUtils.chooseCodecsAndFilterManifest(
|
|
503
|
+
shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
|
|
505
504
|
manifest, preferredAudioChannelCount);
|
|
506
505
|
|
|
507
506
|
for (const variant of manifest.variants) {
|
|
508
507
|
goog.asserts.assert(
|
|
509
|
-
StreamUtils.isPlayable(variant),
|
|
508
|
+
shaka.util.StreamUtils.isPlayable(variant),
|
|
510
509
|
'We should have already filtered by "is playable"');
|
|
511
510
|
|
|
512
|
-
allTracks.push(StreamUtils.variantToTrack(variant));
|
|
511
|
+
allTracks.push(shaka.util.StreamUtils.variantToTrack(variant));
|
|
513
512
|
}
|
|
514
513
|
|
|
515
514
|
for (const text of manifest.textStreams) {
|
|
516
|
-
allTracks.push(StreamUtils.textStreamToTrack(text));
|
|
515
|
+
allTracks.push(shaka.util.StreamUtils.textStreamToTrack(text));
|
|
517
516
|
}
|
|
518
517
|
|
|
519
518
|
for (const image of manifest.imageStreams) {
|
|
520
|
-
allTracks.push(StreamUtils.imageStreamToTrack(image));
|
|
519
|
+
allTracks.push(shaka.util.StreamUtils.imageStreamToTrack(image));
|
|
521
520
|
}
|
|
522
521
|
|
|
523
522
|
// Let the application choose which tracks to store.
|
package/lib/player.js
CHANGED
|
@@ -596,7 +596,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
596
596
|
enterNode: (node, has, wants) => {
|
|
597
597
|
this.dispatchEvent(this.makeEvent_(
|
|
598
598
|
/* name= */ shaka.Player.EventName.OnStateChange,
|
|
599
|
-
/* data= */
|
|
599
|
+
/* data= */ (new Map()).set('state', node.name)));
|
|
600
600
|
|
|
601
601
|
const action = actions.get(node);
|
|
602
602
|
return action(has, wants);
|
|
@@ -627,7 +627,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
627
627
|
onIdle: (node) => {
|
|
628
628
|
this.dispatchEvent(this.makeEvent_(
|
|
629
629
|
/* name= */ shaka.Player.EventName.OnStateIdle,
|
|
630
|
-
/* data= */
|
|
630
|
+
/* data= */ (new Map()).set('state', node.name)));
|
|
631
631
|
},
|
|
632
632
|
};
|
|
633
633
|
|
|
@@ -647,7 +647,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
647
647
|
|
|
648
648
|
/**
|
|
649
649
|
* @param {!shaka.Player.EventName} name
|
|
650
|
-
* @param {Object
|
|
650
|
+
* @param {Map.<string, Object>=} data
|
|
651
651
|
* @return {!shaka.util.FakeEvent}
|
|
652
652
|
* @private
|
|
653
653
|
*/
|
|
@@ -726,6 +726,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
726
726
|
await this.networkingEngine_.destroy();
|
|
727
727
|
this.networkingEngine_ = null;
|
|
728
728
|
}
|
|
729
|
+
|
|
730
|
+
// FakeEventTarget implements IReleasable
|
|
731
|
+
super.release();
|
|
729
732
|
}
|
|
730
733
|
|
|
731
734
|
/**
|
|
@@ -1155,6 +1158,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1155
1158
|
'mpg': 'video/mpeg',
|
|
1156
1159
|
'mpeg': 'video/mpeg',
|
|
1157
1160
|
'm3u8': 'application/x-mpegurl',
|
|
1161
|
+
'mpd': 'application/dash+xml',
|
|
1158
1162
|
'mp3': 'audio/mpeg',
|
|
1159
1163
|
'aac': 'audio/aac',
|
|
1160
1164
|
'flac': 'audio/flac',
|
|
@@ -1285,6 +1289,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1285
1289
|
has.mediaElement = null;
|
|
1286
1290
|
}
|
|
1287
1291
|
|
|
1292
|
+
if (this.adManager_) {
|
|
1293
|
+
// The ad manager is specific to the video, so detach it too.
|
|
1294
|
+
this.adManager_.release();
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1288
1297
|
// Clear our cached copy of the media element.
|
|
1289
1298
|
this.video_ = null;
|
|
1290
1299
|
|
|
@@ -1621,8 +1630,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1621
1630
|
// initialize it now even through it appears a little out-of-place.
|
|
1622
1631
|
this.regionTimeline_ =
|
|
1623
1632
|
new shaka.media.RegionTimeline(() => this.seekRange());
|
|
1624
|
-
this.regionTimeline_.
|
|
1633
|
+
this.regionTimeline_.addEventListener('regionadd', (event) => {
|
|
1634
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
1635
|
+
const region = event['region'];
|
|
1625
1636
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionAdded, region);
|
|
1637
|
+
|
|
1626
1638
|
if (this.adManager_) {
|
|
1627
1639
|
this.adManager_.onDashTimedMetadata(region);
|
|
1628
1640
|
}
|
|
@@ -2064,6 +2076,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2064
2076
|
decodingInfos: [],
|
|
2065
2077
|
};
|
|
2066
2078
|
|
|
2079
|
+
this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
|
|
2067
2080
|
await this.drmEngine_.initForPlayback(
|
|
2068
2081
|
[variant], /* offlineSessionIds= */ []);
|
|
2069
2082
|
await this.drmEngine_.attach(has.mediaElement);
|
|
@@ -2409,12 +2422,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2409
2422
|
goog.asserts.assert(!endTime || startTime <= endTime,
|
|
2410
2423
|
'Metadata start time should be less or equal to the end time!');
|
|
2411
2424
|
const eventName = shaka.Player.EventName.Metadata;
|
|
2412
|
-
const data =
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
};
|
|
2425
|
+
const data = new Map()
|
|
2426
|
+
.set('startTime', startTime)
|
|
2427
|
+
.set('endTime', endTime)
|
|
2428
|
+
.set('metadataType', metadataType)
|
|
2429
|
+
.set('payload', payload);
|
|
2418
2430
|
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
2419
2431
|
}
|
|
2420
2432
|
|
|
@@ -2476,7 +2488,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2476
2488
|
this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded);
|
|
2477
2489
|
}
|
|
2478
2490
|
};
|
|
2479
|
-
|
|
2480
2491
|
return new shaka.net.NetworkingEngine(onProgressUpdated_);
|
|
2481
2492
|
}
|
|
2482
2493
|
|
|
@@ -2514,21 +2525,31 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2514
2525
|
// Create the region observer. This will allow us to notify the app when we
|
|
2515
2526
|
// move in and out of timeline regions.
|
|
2516
2527
|
const regionObserver = new shaka.media.RegionObserver(this.regionTimeline_);
|
|
2517
|
-
|
|
2528
|
+
|
|
2529
|
+
regionObserver.addEventListener('enter', (event) => {
|
|
2530
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2531
|
+
const region = event['region'];
|
|
2518
2532
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionEnter, region);
|
|
2519
|
-
};
|
|
2520
|
-
|
|
2533
|
+
});
|
|
2534
|
+
|
|
2535
|
+
regionObserver.addEventListener('exit', (event) => {
|
|
2536
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2537
|
+
const region = event['region'];
|
|
2521
2538
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionExit, region);
|
|
2522
|
-
};
|
|
2523
|
-
|
|
2539
|
+
});
|
|
2540
|
+
|
|
2541
|
+
regionObserver.addEventListener('skip', (event) => {
|
|
2542
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2543
|
+
const region = event['region'];
|
|
2544
|
+
/** @type {boolean} */
|
|
2545
|
+
const seeking = event['seeking'];
|
|
2524
2546
|
// If we are seeking, we don't want to surface the enter/exit events since
|
|
2525
2547
|
// they didn't play through them.
|
|
2526
2548
|
if (!seeking) {
|
|
2527
2549
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionEnter, region);
|
|
2528
2550
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionExit, region);
|
|
2529
2551
|
}
|
|
2530
|
-
};
|
|
2531
|
-
regionObserver.setListeners(onEnterRegion, onExitRegion, onSkipRegion);
|
|
2552
|
+
});
|
|
2532
2553
|
|
|
2533
2554
|
// Now that we have all our observers, create a manager for them.
|
|
2534
2555
|
const manager = new shaka.media.PlayheadObserverManager(this.video_);
|
|
@@ -3235,6 +3256,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3235
3256
|
|
|
3236
3257
|
const tracks = [];
|
|
3237
3258
|
|
|
3259
|
+
let activeTracks = 0;
|
|
3260
|
+
|
|
3238
3261
|
// Convert each variant to a track.
|
|
3239
3262
|
for (const variant of this.manifest_.variants) {
|
|
3240
3263
|
if (!shaka.util.StreamUtils.isPlayable(variant)) {
|
|
@@ -3243,10 +3266,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3243
3266
|
|
|
3244
3267
|
const track = shaka.util.StreamUtils.variantToTrack(variant);
|
|
3245
3268
|
track.active = variant == currentVariant;
|
|
3269
|
+
if (!track.active && activeTracks != 1 && currentVariant != null &&
|
|
3270
|
+
variant.video == currentVariant.video &&
|
|
3271
|
+
variant.audio == currentVariant.audio) {
|
|
3272
|
+
track.active = true;
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
if (track.active) {
|
|
3276
|
+
activeTracks++;
|
|
3277
|
+
}
|
|
3246
3278
|
|
|
3247
3279
|
tracks.push(track);
|
|
3248
3280
|
}
|
|
3249
3281
|
|
|
3282
|
+
goog.asserts.assert(activeTracks <= 1,
|
|
3283
|
+
'It should only have one active track');
|
|
3284
|
+
|
|
3250
3285
|
return tracks;
|
|
3251
3286
|
} else if (this.video_ && this.video_.audioTracks) {
|
|
3252
3287
|
// Safari's native HLS always shows a single element in videoTracks.
|
|
@@ -4796,7 +4831,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4796
4831
|
// Surface the buffering event so that the app knows if/when we are
|
|
4797
4832
|
// buffering.
|
|
4798
4833
|
const eventName = shaka.Player.EventName.Buffering;
|
|
4799
|
-
|
|
4834
|
+
const data = (new Map()).set('buffering', isBuffering);
|
|
4835
|
+
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
4800
4836
|
}
|
|
4801
4837
|
|
|
4802
4838
|
/**
|
|
@@ -5178,10 +5214,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5178
5214
|
onVariantChanged_(from, to) {
|
|
5179
5215
|
// Delay the 'variantchanged' event so StreamingEngine has time to absorb
|
|
5180
5216
|
// the changes before the user tries to query it.
|
|
5181
|
-
const
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5217
|
+
const data = new Map()
|
|
5218
|
+
.set('oldTrack', from)
|
|
5219
|
+
.set('newTrack', to);
|
|
5220
|
+
const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, data);
|
|
5185
5221
|
this.delayDispatchEvent_(event);
|
|
5186
5222
|
}
|
|
5187
5223
|
|
|
@@ -5204,10 +5240,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5204
5240
|
|
|
5205
5241
|
/** @private */
|
|
5206
5242
|
onAbrStatusChanged_() {
|
|
5207
|
-
const
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
this.delayDispatchEvent_(event);
|
|
5243
|
+
const data = (new Map()).set('newStatus', this.config_.abr.enabled);
|
|
5244
|
+
this.delayDispatchEvent_(this.makeEvent_(
|
|
5245
|
+
shaka.Player.EventName.AbrStatusChanged, data));
|
|
5211
5246
|
}
|
|
5212
5247
|
|
|
5213
5248
|
/**
|
|
@@ -5224,7 +5259,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5224
5259
|
}
|
|
5225
5260
|
|
|
5226
5261
|
const eventName = shaka.Player.EventName.Error;
|
|
5227
|
-
const event = this.makeEvent_(eventName,
|
|
5262
|
+
const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
|
|
5228
5263
|
this.dispatchEvent(event);
|
|
5229
5264
|
if (event.defaultPrevented) {
|
|
5230
5265
|
error.handled = true;
|
|
@@ -5253,7 +5288,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5253
5288
|
eventElement: region.eventElement,
|
|
5254
5289
|
};
|
|
5255
5290
|
|
|
5256
|
-
|
|
5291
|
+
const data = (new Map()).set('detail', clone);
|
|
5292
|
+
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
5257
5293
|
}
|
|
5258
5294
|
|
|
5259
5295
|
/**
|
|
@@ -6082,7 +6118,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6082
6118
|
* @define {string} A version number taken from git at compile time.
|
|
6083
6119
|
* @export
|
|
6084
6120
|
*/
|
|
6085
|
-
shaka.Player.version = 'v3.1.
|
|
6121
|
+
shaka.Player.version = 'v3.1.6-uncompiled'; // x-release-please-version
|
|
6086
6122
|
|
|
6087
6123
|
// Initialize the deprecation system using the version string we just set
|
|
6088
6124
|
// on the player.
|
|
@@ -13,12 +13,14 @@ goog.require('shaka.polyfill');
|
|
|
13
13
|
* @see https://wicg.github.io/encrypted-media-encryption-scheme/
|
|
14
14
|
* @see https://github.com/w3c/encrypted-media/pull/457
|
|
15
15
|
* @see https://github.com/google/eme-encryption-scheme-polyfill
|
|
16
|
+
* @export
|
|
16
17
|
*/
|
|
17
18
|
shaka.polyfill.EncryptionScheme = class {
|
|
18
19
|
/**
|
|
19
20
|
* Install the polyfill if needed.
|
|
20
21
|
*
|
|
21
22
|
* @suppress {missingRequire}
|
|
23
|
+
* @export
|
|
22
24
|
*/
|
|
23
25
|
static install() {
|
|
24
26
|
EncryptionSchemePolyfills.install();
|
|
@@ -14,10 +14,12 @@ goog.require('shaka.polyfill');
|
|
|
14
14
|
* Many browsers have prefixed fullscreen methods on Element and document.
|
|
15
15
|
* See {@link https://mzl.la/2K0xcHo Using fullscreen mode} on MDN for more
|
|
16
16
|
* information.
|
|
17
|
+
* @export
|
|
17
18
|
*/
|
|
18
19
|
shaka.polyfill.Fullscreen = class {
|
|
19
20
|
/**
|
|
20
21
|
* Install the polyfill if needed.
|
|
22
|
+
* @export
|
|
21
23
|
*/
|
|
22
24
|
static install() {
|
|
23
25
|
if (!window.Document) {
|