shaka-player 2.5.1 → 2.5.5
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/.csslintrc +13 -0
- package/AUTHORS +3 -0
- package/CHANGELOG.md +156 -0
- package/CONTRIBUTORS +3 -0
- package/README.md +3 -0
- package/build/README.md +5 -5
- package/build/apps.py +3 -1
- package/build/build.py +5 -2
- package/build/check.py +17 -0
- package/build/compiler.py +49 -0
- package/build/conformance.textproto +1 -0
- package/build/docs.py +0 -6
- package/build/generateExterns.js +72 -20
- package/build/install-linux-prereqs.sh +4 -4
- package/build/shakaBuildHelpers.py +7 -8
- package/build/test.py +21 -9
- package/build/types/core +1 -0
- package/build/types/ui +6 -1
- package/demo/asset_card.js +129 -41
- package/demo/cast_receiver/receiver_app.css +27 -13
- package/demo/close_button.js +10 -15
- package/demo/common/asset.js +6 -6
- package/demo/common/assets.js +72 -14
- package/demo/config.js +28 -25
- package/demo/custom.js +20 -16
- package/demo/demo.less +107 -16
- package/demo/demo_uncompiled.js +33 -0
- package/demo/demo_utils.js +4 -3
- package/demo/front.js +12 -8
- package/demo/index.html +14 -17
- package/demo/input.js +30 -23
- package/demo/input_container.js +15 -11
- package/demo/load.js +5 -7
- package/demo/main.js +138 -49
- package/demo/search.js +40 -36
- package/demo/service_worker.js +1 -1
- package/demo/tooltip.js +16 -24
- package/dist/.csslintstamp +0 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +165 -152
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +165 -152
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +28 -11
- package/dist/receiver.compiled.debug.js +11 -11
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -11
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.debug.externs.js +232 -102
- package/dist/shaka-player.compiled.debug.js +189 -165
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +232 -102
- package/dist/shaka-player.compiled.js +567 -553
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.debug.externs.js +338 -113
- package/dist/shaka-player.ui.debug.js +296 -276
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +338 -113
- package/dist/shaka-player.ui.js +685 -671
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/drm-config.md +1 -0
- package/docs/tutorials/fairplay.md +77 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/manifest-parser.md +1 -1
- package/docs/tutorials/ui-customization.md +5 -2
- package/docs/tutorials/ui.md +10 -4
- package/docs/tutorials/welcome.md +2 -2
- package/externs/mediasession.js +16 -1
- package/externs/shaka/net.js +14 -4
- package/externs/shaka/player.js +136 -90
- package/externs/tippy.js +30 -0
- package/externs/webkitmediakeys.js +4 -0
- package/karma.conf.js +30 -24
- package/lib/cast/cast_proxy.js +94 -23
- package/lib/cast/cast_receiver.js +8 -1
- package/lib/dash/content_protection.js +33 -1
- package/lib/dash/dash_parser.js +13 -5
- package/lib/hls/hls_parser.js +20 -4
- package/lib/media/drm_engine.js +29 -2
- package/lib/media/media_source_engine.js +9 -6
- package/lib/media/play_rate_controller.js +14 -3
- package/lib/media/playhead.js +8 -3
- package/lib/media/streaming_engine.js +68 -20
- package/lib/media/transmuxer.js +2 -2
- package/lib/media/video_wrapper.js +8 -3
- package/lib/net/data_uri_plugin.js +5 -2
- package/lib/net/http_fetch_plugin.js +3 -3
- package/lib/net/http_plugin_utils.js +2 -4
- package/lib/net/http_xhr_plugin.js +2 -2
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/offline_scheme.js +7 -2
- package/lib/player.js +18 -8
- package/lib/polyfill/input_event.js +7 -11
- package/lib/polyfill/mediasource.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +2 -72
- package/lib/text/cue.js +1 -1
- package/lib/text/ttml_text_parser.js +1 -1
- package/lib/util/config_utils.js +1 -0
- package/lib/util/error.js +45 -11
- package/lib/util/event_manager.js +211 -100
- package/lib/util/fairplay_utils.js +122 -0
- package/lib/util/i_releasable.js +1 -0
- package/lib/util/player_configuration.js +7 -0
- package/lib/util/pssh.js +41 -0
- package/lib/util/string_utils.js +42 -6
- package/lib/util/timer.js +1 -1
- package/package.json +33 -33
- package/roadmap.md +96 -0
- package/shaka-player.uncompiled.js +5 -1
- package/test/dash/dash_parser_content_protection_unit.js +35 -1
- package/test/dash/dash_parser_manifest_unit.js +60 -0
- package/test/hls/hls_live_unit.js +7 -1
- package/test/hls/hls_parser_unit.js +50 -0
- package/test/media/drm_engine_unit.js +10 -4
- package/test/media/media_source_engine_integration.js +12 -0
- package/test/media/media_source_engine_unit.js +3 -1
- package/test/media/playhead_unit.js +7 -5
- package/test/media/streaming_engine_unit.js +46 -1
- package/test/media/transmuxer_integration.js +13 -0
- package/test/net/data_uri_plugin_unit.js +13 -2
- package/test/net/http_plugin_unit.js +17 -12
- package/test/net/networking_engine_unit.js +17 -8
- package/test/offline/offline_scheme_unit.js +18 -8
- package/test/player_external.js +3 -2
- package/test/player_integration.js +94 -3
- package/test/player_src_equals_integration.js +2 -2
- package/test/player_unit.js +21 -8
- package/test/test/boot.js +20 -5
- package/test/test/externs/jasmine.js +48 -0
- package/test/test/util/expect_async_polyfill.js +145 -0
- package/test/test/util/fake_networking_engine.js +1 -0
- package/test/test/util/manifest_generator.js +2 -1
- package/test/test/util/test_scheme.js +34 -2
- package/test/test/util/ui_utils.js +41 -0
- package/test/test/util/util.js +16 -8
- package/test/ui/ui_unit.js +130 -4
- package/ui/audio_language_selection.js +17 -96
- package/ui/big_play_button.js +61 -0
- package/ui/controls.js +226 -335
- package/ui/controls.less +3 -5
- package/ui/element.js +24 -6
- package/ui/externs/ui.js +62 -9
- package/ui/fullscreen_button.js +2 -52
- package/ui/less/buttons.less +10 -16
- package/ui/less/containers.less +75 -20
- package/ui/less/general.less +18 -2
- package/ui/less/other_elements.less +2 -1
- package/ui/less/overflow_menu.less +11 -10
- package/ui/less/range_elements.less +12 -10
- package/ui/less/spinner.less +2 -0
- package/ui/overflow_menu.js +1 -1
- package/ui/pip_button.js +1 -1
- package/ui/{play_pause_button.js → play_button.js} +17 -47
- package/ui/presentation_time.js +17 -12
- package/ui/range_element.js +176 -0
- package/ui/resolution_selection.js +43 -106
- package/ui/seek_bar.js +204 -0
- package/ui/settings_menu.js +113 -0
- package/ui/small_play_button.js +73 -0
- package/ui/text_displayer.js +17 -14
- package/ui/text_selection.js +42 -107
- package/ui/ui.js +17 -40
- package/ui/volume_bar.js +40 -68
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2016 Google Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @fileoverview This polyfills the expectAsync method from Jasmine 3.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// TODO: Remove once we upgrade to Jasmine 3.0+.
|
|
25
|
+
if (window.expectAsync === undefined) {
|
|
26
|
+
/** @extends {jasmine.MatchersAsync} */
|
|
27
|
+
const AsyncMatchers = class {
|
|
28
|
+
/**
|
|
29
|
+
* @param {!Promise} val
|
|
30
|
+
* @param {string=} context
|
|
31
|
+
* @param {boolean=} negated
|
|
32
|
+
*/
|
|
33
|
+
constructor(val, context = '', negated = false) {
|
|
34
|
+
this.val_ = val;
|
|
35
|
+
this.negated_ = negated;
|
|
36
|
+
this.context_ = context ? '"' + context + '" ' : '';
|
|
37
|
+
|
|
38
|
+
// Don't use a normal property to avoid infinite recursion.
|
|
39
|
+
Object.defineProperty(this, 'not', {
|
|
40
|
+
get: () => new AsyncMatchers(val, context, !negated),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @override */
|
|
45
|
+
withContext(ctx) {
|
|
46
|
+
return new AsyncMatchers(this.val_, ctx, this.negated_);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** @override */
|
|
50
|
+
async toBeResolved() {
|
|
51
|
+
await this.toBeResolvedShared(/* has_value= */ false);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @override */
|
|
55
|
+
async toBeResolvedTo(expected) {
|
|
56
|
+
await this.toBeResolvedShared(/* has_value= */ true, expected);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {boolean} hasValue
|
|
61
|
+
* @param {*=} expected
|
|
62
|
+
* @return {!Promise}
|
|
63
|
+
*/
|
|
64
|
+
async toBeResolvedShared(hasValue, expected = undefined) {
|
|
65
|
+
let msg = 'Expected ' + this.context_;
|
|
66
|
+
if (this.negated_) {
|
|
67
|
+
msg += ' not';
|
|
68
|
+
}
|
|
69
|
+
msg += ' to be resolved';
|
|
70
|
+
if (hasValue) {
|
|
71
|
+
msg += ' to ' + expected;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const actual = await this.val_;
|
|
76
|
+
if (this.negated_) {
|
|
77
|
+
if (hasValue) {
|
|
78
|
+
expect(actual).not.toEqual(expected);
|
|
79
|
+
} else {
|
|
80
|
+
fail(msg);
|
|
81
|
+
}
|
|
82
|
+
} else if (hasValue) {
|
|
83
|
+
expect(actual).toEqual(expected);
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
if (!this.negated_) {
|
|
87
|
+
fail(msg);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @override */
|
|
93
|
+
async toBeRejected() {
|
|
94
|
+
await this.toBeRejectedShared(/* has_value= */ false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @override */
|
|
98
|
+
async toBeRejectedWith(expected) {
|
|
99
|
+
await this.toBeRejectedShared(/* has_value= */ true, expected);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {boolean} hasValue
|
|
104
|
+
* @param {*=} expected
|
|
105
|
+
* @return {!Promise}
|
|
106
|
+
*/
|
|
107
|
+
async toBeRejectedShared(hasValue, expected = undefined) {
|
|
108
|
+
let msg = 'Expected ' + this.context_;
|
|
109
|
+
if (this.negated_) {
|
|
110
|
+
msg += ' not';
|
|
111
|
+
}
|
|
112
|
+
msg += ' to be rejected';
|
|
113
|
+
if (hasValue) {
|
|
114
|
+
msg += ' with ' + expected;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await this.val_;
|
|
119
|
+
if (!this.negated_) {
|
|
120
|
+
fail(msg);
|
|
121
|
+
}
|
|
122
|
+
} catch (actual) {
|
|
123
|
+
if (this.negated_) {
|
|
124
|
+
if (hasValue) {
|
|
125
|
+
expect(actual).not.toEqual(expected);
|
|
126
|
+
} else {
|
|
127
|
+
fail(msg);
|
|
128
|
+
}
|
|
129
|
+
} else if (hasValue) {
|
|
130
|
+
expect(actual).toEqual(expected);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {*} obj
|
|
138
|
+
* @return {!AsyncMatchers}
|
|
139
|
+
*/
|
|
140
|
+
window.expectAsync = (obj) => {
|
|
141
|
+
goog.asserts.assert(obj instanceof Promise, 'Must pass promise');
|
|
142
|
+
expect(obj instanceof Promise).toBe(true);
|
|
143
|
+
return new AsyncMatchers(obj);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -584,7 +584,8 @@ shaka.test.ManifestGenerator.prototype.useSegmentTemplate = function(
|
|
|
584
584
|
return Math.floor(time / segmentDuration);
|
|
585
585
|
};
|
|
586
586
|
stream.getSegmentReference = (function(index) {
|
|
587
|
-
|
|
587
|
+
goog.asserts.assert(!isNaN(index), 'Invalid index requested!');
|
|
588
|
+
if (index < 0 || index >= segmentCount || isNaN(index)) {
|
|
588
589
|
return null;
|
|
589
590
|
}
|
|
590
591
|
let getUris = function() { return [sprintf(template, index)]; };
|
|
@@ -33,6 +33,7 @@ shaka.test.TestScheme = function(uri, request, requestType) {
|
|
|
33
33
|
/** @type {shaka.extern.Response} */
|
|
34
34
|
let response = {
|
|
35
35
|
uri: uri,
|
|
36
|
+
originalUri: uri,
|
|
36
37
|
data: new ArrayBuffer(0),
|
|
37
38
|
headers: {'content-type': 'application/x-test-manifest'},
|
|
38
39
|
};
|
|
@@ -76,7 +77,7 @@ shaka.test.TestScheme = function(uri, request, requestType) {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
/** @type {shaka.extern.Response} */
|
|
79
|
-
|
|
80
|
+
const ret = {uri: uri, originalUri: uri, data: responseData, headers: {}};
|
|
80
81
|
return shaka.util.AbortableOperation.completed(ret);
|
|
81
82
|
};
|
|
82
83
|
|
|
@@ -125,6 +126,37 @@ shaka.test.TestScheme.DATA = {
|
|
|
125
126
|
duration: 30,
|
|
126
127
|
},
|
|
127
128
|
|
|
129
|
+
// Like 'sintel' above, but with a non-zero period start time.
|
|
130
|
+
// This helps expose edge cases around startup and live streams.
|
|
131
|
+
'sintel_start_at_3': {
|
|
132
|
+
periodStart: 3,
|
|
133
|
+
video: {
|
|
134
|
+
initSegmentUri: '/base/test/test/assets/sintel-video-init.mp4',
|
|
135
|
+
mdhdOffset: 0x1ba,
|
|
136
|
+
segmentUri: '/base/test/test/assets/sintel-video-segment.mp4',
|
|
137
|
+
tfdtOffset: 0x38,
|
|
138
|
+
segmentDuration: 10,
|
|
139
|
+
presentationTimeOffset: 0,
|
|
140
|
+
mimeType: 'video/mp4',
|
|
141
|
+
codecs: 'avc1.42c01e',
|
|
142
|
+
},
|
|
143
|
+
audio: {
|
|
144
|
+
initSegmentUri: '/base/test/test/assets/sintel-audio-init.mp4',
|
|
145
|
+
mdhdOffset: 0x1b6,
|
|
146
|
+
segmentUri: '/base/test/test/assets/sintel-audio-segment.mp4',
|
|
147
|
+
tfdtOffset: 0x3c,
|
|
148
|
+
segmentDuration: 10.005,
|
|
149
|
+
presentationTimeOffset: 0,
|
|
150
|
+
mimeType: 'audio/mp4',
|
|
151
|
+
codecs: 'mp4a.40.2',
|
|
152
|
+
},
|
|
153
|
+
text: {
|
|
154
|
+
uri: '/base/test/test/assets/text-clip.vtt',
|
|
155
|
+
mimeType: 'text/vtt',
|
|
156
|
+
},
|
|
157
|
+
duration: 30,
|
|
158
|
+
},
|
|
159
|
+
|
|
128
160
|
// Like 'sintel' above, but with languages and delayed setup.
|
|
129
161
|
// These extra features help expose some edge cases.
|
|
130
162
|
'sintel_realistic': {
|
|
@@ -476,7 +508,7 @@ shaka.test.TestScheme.createManifests = function(shaka, suffix) {
|
|
|
476
508
|
|
|
477
509
|
let gen = new windowShaka.test.ManifestGenerator(shaka)
|
|
478
510
|
.setPresentationDuration(data.duration)
|
|
479
|
-
.addPeriod(/* startTime= */ 0)
|
|
511
|
+
.addPeriod(/* startTime= */ data.periodStart || 0)
|
|
480
512
|
.addVariant(0);
|
|
481
513
|
|
|
482
514
|
if (data[ContentType.VIDEO]) {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2016 Google Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
goog.provide('shaka.test.UiUtils');
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
shaka.test.UiUtils = class {
|
|
23
|
+
/**
|
|
24
|
+
* Simulates a native event (e.g. 'click') on the given element.
|
|
25
|
+
*
|
|
26
|
+
* @param {EventTarget} target
|
|
27
|
+
* @param {string} name
|
|
28
|
+
*/
|
|
29
|
+
static simulateEvent(target, name) {
|
|
30
|
+
const type = {
|
|
31
|
+
'click': 'MouseEvent',
|
|
32
|
+
'dblclick': 'MouseEvent',
|
|
33
|
+
}[name] || 'CustomEvent';
|
|
34
|
+
|
|
35
|
+
// Note we can't use the MouseEvent constructor since it isn't supported on
|
|
36
|
+
// IE11.
|
|
37
|
+
const event = document.createEvent(type);
|
|
38
|
+
event.initEvent(name, true, true);
|
|
39
|
+
target.dispatchEvent(event);
|
|
40
|
+
}
|
|
41
|
+
};
|
package/test/test/util/util.js
CHANGED
|
@@ -91,18 +91,26 @@ shaka.test.Util.delay = function(seconds, realSetTimeout) {
|
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @param {!shaka.util.Error} error
|
|
96
|
+
* @return {*}
|
|
97
|
+
*/
|
|
98
|
+
shaka.test.Util.jasmineError = function(error) {
|
|
99
|
+
// NOTE: Safari will add extra properties to any thrown object, and some of
|
|
100
|
+
// the properties we compute in debug builds are unhelpful and introduce
|
|
101
|
+
// inconsistency in tests. Therefore we only capture the critical fields
|
|
102
|
+
// below.
|
|
103
|
+
const {severity, category, code, data} = error;
|
|
104
|
+
return jasmine.objectContaining({severity, category, code, data});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
|
|
94
108
|
/**
|
|
95
109
|
* @param {*} actual
|
|
96
|
-
* @param {!
|
|
110
|
+
* @param {!shaka.util.Error} expected
|
|
97
111
|
*/
|
|
98
112
|
shaka.test.Util.expectToEqualError = function(actual, expected) {
|
|
99
|
-
|
|
100
|
-
// wrap expectedError in jasmine.objectContaining to ignore them.
|
|
101
|
-
// NOTE: We now add extra properties ourselves for the sake of formatting.
|
|
102
|
-
// These, we delete from 'expected'.
|
|
103
|
-
delete expected['stack'];
|
|
104
|
-
delete expected['message'];
|
|
105
|
-
expect(actual).toEqual(jasmine.objectContaining(expected));
|
|
113
|
+
expect(actual).toEqual(shaka.test.Util.jasmineError(expected));
|
|
106
114
|
};
|
|
107
115
|
|
|
108
116
|
|
package/test/ui/ui_unit.js
CHANGED
|
@@ -181,6 +181,27 @@ describe('UI', function() {
|
|
|
181
181
|
videoContainer.appendChild(video);
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
it('goes into fullscreen on double click', async () => {
|
|
185
|
+
const config = {
|
|
186
|
+
controlPanelElements: [
|
|
187
|
+
'overflow_menu',
|
|
188
|
+
],
|
|
189
|
+
overflowMenuButtons: [
|
|
190
|
+
'quality',
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
const ui = createUIThroughAPI(videoContainer, video, config);
|
|
194
|
+
const controls = ui.getControls();
|
|
195
|
+
|
|
196
|
+
const spy = spyOn(controls, 'toggleFullScreen');
|
|
197
|
+
|
|
198
|
+
const controlsContainer =
|
|
199
|
+
videoContainer.querySelector('.shaka-controls-container');
|
|
200
|
+
shaka.test.UiUtils.simulateEvent(controlsContainer, 'dblclick');
|
|
201
|
+
await shaka.test.Util.delay(0.1);
|
|
202
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
203
|
+
});
|
|
204
|
+
|
|
184
205
|
describe('all the controls', function() {
|
|
185
206
|
/** @type {!HTMLElement} */
|
|
186
207
|
let controlsContainer;
|
|
@@ -344,6 +365,8 @@ describe('UI', function() {
|
|
|
344
365
|
describe('resolutions menu', function() {
|
|
345
366
|
/** @type {!HTMLElement} */
|
|
346
367
|
let resolutionsMenu;
|
|
368
|
+
/** @type {shaka.ui.Controls} */
|
|
369
|
+
let controls;
|
|
347
370
|
|
|
348
371
|
beforeEach(function() {
|
|
349
372
|
let config = {
|
|
@@ -354,7 +377,9 @@ describe('UI', function() {
|
|
|
354
377
|
'quality',
|
|
355
378
|
],
|
|
356
379
|
};
|
|
357
|
-
createUIThroughAPI(videoContainer, video, config);
|
|
380
|
+
const ui = createUIThroughAPI(videoContainer, video, config);
|
|
381
|
+
controls = ui.getControls();
|
|
382
|
+
player = controls.getLocalPlayer();
|
|
358
383
|
|
|
359
384
|
let resolutionsMenus =
|
|
360
385
|
videoContainer.getElementsByClassName('shaka-resolutions');
|
|
@@ -406,8 +431,107 @@ describe('UI', function() {
|
|
|
406
431
|
jasmine.any(Object), true);
|
|
407
432
|
});
|
|
408
433
|
|
|
409
|
-
|
|
410
|
-
|
|
434
|
+
it('displays resolutions based on current stream', async () => {
|
|
435
|
+
/* eslint-disable indent */
|
|
436
|
+
// A manifest with different resolutions at different
|
|
437
|
+
// languages/channel-counts to test the current resolution list is
|
|
438
|
+
// filtered.
|
|
439
|
+
const manifest = new shaka.test.ManifestGenerator()
|
|
440
|
+
.addPeriod(0)
|
|
441
|
+
.addVariant(0)
|
|
442
|
+
.primary()
|
|
443
|
+
.language('en')
|
|
444
|
+
.addVideo(1).size(320, 240)
|
|
445
|
+
.addAudio(3).channelsCount(2)
|
|
446
|
+
.addVariant(4)
|
|
447
|
+
.language('en')
|
|
448
|
+
.addVideo(5).size(640, 480)
|
|
449
|
+
.addAudio(6).channelsCount(2)
|
|
450
|
+
.addVariant(7) // Duplicate with 4
|
|
451
|
+
.language('en')
|
|
452
|
+
.addVideo(8).size(640, 480)
|
|
453
|
+
.addAudio(9).channelsCount(2)
|
|
454
|
+
.addVariant(10)
|
|
455
|
+
.language('en')
|
|
456
|
+
.addVideo(11).size(1280, 720)
|
|
457
|
+
.addAudio(12).channelsCount(1)
|
|
458
|
+
.addVariant(13)
|
|
459
|
+
.language('es')
|
|
460
|
+
.addVideo(14).size(960, 540)
|
|
461
|
+
.addAudio(15).channelsCount(2)
|
|
462
|
+
.addVariant(16)
|
|
463
|
+
.language('fr')
|
|
464
|
+
.addVideo(17).size(256, 144)
|
|
465
|
+
.addAudio(18).channelsCount(2)
|
|
466
|
+
.build();
|
|
467
|
+
/* eslint-enable indent */
|
|
468
|
+
const getResolutions = () => {
|
|
469
|
+
const resolutionButtons = videoContainer.querySelectorAll(
|
|
470
|
+
'button.explicit-resolution > span');
|
|
471
|
+
return Array.from(resolutionButtons)
|
|
472
|
+
.map((btn) => btn.innerText)
|
|
473
|
+
.sort();
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
await player.load(
|
|
477
|
+
/* uri= */ 'fake', /* startTime= */ 0, function() {
|
|
478
|
+
return new shaka.test.FakeManifestParser(manifest);
|
|
479
|
+
});
|
|
480
|
+
player.configure('abr.enabled', false);
|
|
481
|
+
|
|
482
|
+
const tracks = player.getVariantTracks();
|
|
483
|
+
const en2 =
|
|
484
|
+
tracks.find((t) => t.language == 'en' && t.channelsCount == 2);
|
|
485
|
+
const en1 =
|
|
486
|
+
tracks.find((t) => t.language == 'en' && t.channelsCount == 1);
|
|
487
|
+
const es = tracks.find((t) => t.language == 'es');
|
|
488
|
+
|
|
489
|
+
// There are 3 variants with English 2-channel, but one is a duplicate
|
|
490
|
+
// and shouldn't appear in the list.
|
|
491
|
+
goog.asserts.assert(en2, 'Unable to find tracks');
|
|
492
|
+
player.selectVariantTrack(en2, true);
|
|
493
|
+
await updateResolutionMenu();
|
|
494
|
+
expect(getResolutions()).toEqual(['240p', '480p']);
|
|
495
|
+
|
|
496
|
+
// There is 1 variant with English 1-channel.
|
|
497
|
+
goog.asserts.assert(en1, 'Unable to find tracks');
|
|
498
|
+
player.selectVariantTrack(en1, true);
|
|
499
|
+
await updateResolutionMenu();
|
|
500
|
+
expect(getResolutions()).toEqual(['720p']);
|
|
501
|
+
|
|
502
|
+
// There is 1 variant with Spanish 2-channel.
|
|
503
|
+
goog.asserts.assert(es, 'Unable to find tracks');
|
|
504
|
+
player.selectVariantTrack(es, true);
|
|
505
|
+
await updateResolutionMenu();
|
|
506
|
+
expect(getResolutions()).toEqual(['540p']);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Use internals to update the resolution menu. Our fake manifest can
|
|
511
|
+
* cause problems with startup where the Player will get stuck using
|
|
512
|
+
* "deferred" switches, so we won't get events and the resolution menu
|
|
513
|
+
* won't update.
|
|
514
|
+
*
|
|
515
|
+
* @suppress {accessControls}
|
|
516
|
+
*/
|
|
517
|
+
async function updateResolutionMenu() {
|
|
518
|
+
await shaka.test.Util.delay(0.1);
|
|
519
|
+
// TODO(#2089): We should be able to stop once we find one, but since
|
|
520
|
+
// there are multiple ResolutionMenu objects, we need to update all of
|
|
521
|
+
// them.
|
|
522
|
+
let found = false;
|
|
523
|
+
for (const elem of controls.elements_) {
|
|
524
|
+
if (elem instanceof shaka.ui.OverflowMenu) {
|
|
525
|
+
for (const child of elem.children_) {
|
|
526
|
+
if (child instanceof shaka.ui.ResolutionSelection) {
|
|
527
|
+
child.updateResolutionSelection_();
|
|
528
|
+
found = true;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
goog.asserts.assert(found, 'Unable to find resolution menu');
|
|
534
|
+
}
|
|
411
535
|
});
|
|
412
536
|
|
|
413
537
|
// TODO: integration test to test audio language menu.
|
|
@@ -531,7 +655,7 @@ describe('UI', function() {
|
|
|
531
655
|
|
|
532
656
|
confirmElementFound(container, 'shaka-play-button-container');
|
|
533
657
|
confirmElementFound(container, 'shaka-play-button');
|
|
534
|
-
confirmElementFound(container, 'shaka-spinner
|
|
658
|
+
confirmElementFound(container, 'shaka-spinner');
|
|
535
659
|
confirmElementFound(container, 'shaka-overflow-menu');
|
|
536
660
|
confirmElementFound(container, 'shaka-controls-button-panel');
|
|
537
661
|
confirmElementFound(container, 'shaka-seek-bar');
|
|
@@ -541,6 +665,7 @@ describe('UI', function() {
|
|
|
541
665
|
* @param {!HTMLElement} videoContainer
|
|
542
666
|
* @param {!HTMLMediaElement} video
|
|
543
667
|
* @param {!Object=} config
|
|
668
|
+
* @return {!shaka.ui.Overlay}
|
|
544
669
|
*/
|
|
545
670
|
function createUIThroughAPI(videoContainer, video, config) {
|
|
546
671
|
player = new shaka.Player(video);
|
|
@@ -548,6 +673,7 @@ describe('UI', function() {
|
|
|
548
673
|
config = config || {};
|
|
549
674
|
const ui = new shaka.ui.Overlay(player, videoContainer, video);
|
|
550
675
|
ui.configure(config);
|
|
676
|
+
return ui;
|
|
551
677
|
}
|
|
552
678
|
|
|
553
679
|
/**
|
|
@@ -18,31 +18,28 @@
|
|
|
18
18
|
|
|
19
19
|
goog.provide('shaka.ui.AudioLanguageSelection');
|
|
20
20
|
|
|
21
|
-
goog.require('shaka.ui.Element');
|
|
22
21
|
goog.require('shaka.ui.Enums');
|
|
23
22
|
goog.require('shaka.ui.LanguageUtils');
|
|
24
23
|
goog.require('shaka.ui.Locales');
|
|
25
24
|
goog.require('shaka.ui.Localization');
|
|
26
25
|
goog.require('shaka.ui.OverflowMenu');
|
|
27
|
-
goog.require('shaka.
|
|
28
|
-
|
|
26
|
+
goog.require('shaka.ui.SettingsMenu');
|
|
29
27
|
|
|
30
28
|
/**
|
|
31
|
-
* @extends {shaka.ui.
|
|
29
|
+
* @extends {shaka.ui.SettingsMenu}
|
|
32
30
|
* @final
|
|
33
31
|
* @export
|
|
34
32
|
*/
|
|
35
|
-
shaka.ui.AudioLanguageSelection = class extends shaka.ui.
|
|
33
|
+
shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {
|
|
36
34
|
/**
|
|
37
35
|
* @param {!HTMLElement} parent
|
|
38
36
|
* @param {!shaka.ui.Controls} controls
|
|
39
37
|
*/
|
|
40
38
|
constructor(parent, controls) {
|
|
41
|
-
super(parent, controls);
|
|
42
|
-
|
|
43
|
-
this.addLanguagesButton_();
|
|
39
|
+
super(parent, controls, shaka.ui.Enums.MaterialDesignIcons.LANGUAGE);
|
|
44
40
|
|
|
45
|
-
this.
|
|
41
|
+
this.button.classList.add('shaka-language-button');
|
|
42
|
+
this.menu.classList.add('shaka-audio-languages');
|
|
46
43
|
|
|
47
44
|
this.eventManager.listen(
|
|
48
45
|
this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
|
|
@@ -63,10 +60,6 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.Element {
|
|
|
63
60
|
this.updateAudioLanguages_();
|
|
64
61
|
});
|
|
65
62
|
|
|
66
|
-
this.eventManager.listen(this.languagesButton_, 'click', () => {
|
|
67
|
-
this.onLanguagesClick_();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
63
|
// Set up all the strings in the user's preferred language.
|
|
71
64
|
this.updateLocalizedStrings_();
|
|
72
65
|
|
|
@@ -74,89 +67,17 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.Element {
|
|
|
74
67
|
}
|
|
75
68
|
|
|
76
69
|
|
|
77
|
-
/**
|
|
78
|
-
* @private
|
|
79
|
-
*/
|
|
80
|
-
addAudioLangMenu_() {
|
|
81
|
-
/** @private {!HTMLElement} */
|
|
82
|
-
this.audioLangMenu_ = shaka.util.Dom.createHTMLElement('div');
|
|
83
|
-
this.audioLangMenu_.classList.add('shaka-audio-languages');
|
|
84
|
-
this.audioLangMenu_.classList.add('shaka-no-propagation');
|
|
85
|
-
this.audioLangMenu_.classList.add('shaka-show-controls-on-mouse-over');
|
|
86
|
-
this.audioLangMenu_.classList.add('shaka-settings-menu');
|
|
87
|
-
|
|
88
|
-
/** @private {!HTMLElement} */
|
|
89
|
-
this.backFromLanguageButton_ = shaka.util.Dom.createHTMLElement('button');
|
|
90
|
-
this.backFromLanguageButton_.classList.add('shaka-back-to-overflow-button');
|
|
91
|
-
this.audioLangMenu_.appendChild(this.backFromLanguageButton_);
|
|
92
|
-
|
|
93
|
-
const backIcon = shaka.util.Dom.createHTMLElement('i');
|
|
94
|
-
backIcon.classList.add('material-icons');
|
|
95
|
-
backIcon.textContent = shaka.ui.Enums.MaterialDesignIcons.BACK;
|
|
96
|
-
this.backFromLanguageButton_.appendChild(backIcon);
|
|
97
|
-
|
|
98
|
-
/** @private {!HTMLElement} */
|
|
99
|
-
this.backFromLanguageSpan_ = shaka.util.Dom.createHTMLElement('span');
|
|
100
|
-
this.backFromLanguageButton_.appendChild(this.backFromLanguageSpan_);
|
|
101
|
-
|
|
102
|
-
const controlsContainer = this.controls.getControlsContainer();
|
|
103
|
-
controlsContainer.appendChild(this.audioLangMenu_);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @private
|
|
109
|
-
*/
|
|
110
|
-
addLanguagesButton_() {
|
|
111
|
-
/** @private {!HTMLElement} */
|
|
112
|
-
this.languagesButton_ = shaka.util.Dom.createHTMLElement('button');
|
|
113
|
-
this.languagesButton_.classList.add('shaka-language-button');
|
|
114
|
-
|
|
115
|
-
const icon = shaka.util.Dom.createHTMLElement('i');
|
|
116
|
-
icon.classList.add('material-icons');
|
|
117
|
-
icon.textContent = shaka.ui.Enums.MaterialDesignIcons.LANGUAGE;
|
|
118
|
-
this.languagesButton_.appendChild(icon);
|
|
119
|
-
|
|
120
|
-
const label = shaka.util.Dom.createHTMLElement('label');
|
|
121
|
-
label.classList.add('shaka-overflow-button-label');
|
|
122
|
-
|
|
123
|
-
/** @private {!HTMLElement} */
|
|
124
|
-
this.languageNameSpan_ = shaka.util.Dom.createHTMLElement('span');
|
|
125
|
-
this.languageNameSpan_.classList.add('languageSpan');
|
|
126
|
-
label.appendChild(this.languageNameSpan_);
|
|
127
|
-
|
|
128
|
-
/** @private {!HTMLElement} */
|
|
129
|
-
this.currentAudioLanguage_ = shaka.util.Dom.createHTMLElement('span');
|
|
130
|
-
this.currentAudioLanguage_.classList.add('shaka-current-selection-span');
|
|
131
|
-
const language = this.player.getConfiguration().preferredAudioLanguage;
|
|
132
|
-
this.currentAudioLanguage_.textContent =
|
|
133
|
-
shaka.ui.LanguageUtils.getLanguageName(language, this.localization);
|
|
134
|
-
label.appendChild(this.currentAudioLanguage_);
|
|
135
|
-
|
|
136
|
-
this.languagesButton_.appendChild(label);
|
|
137
|
-
|
|
138
|
-
this.parent.appendChild(this.languagesButton_);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** @private */
|
|
142
|
-
onLanguagesClick_() {
|
|
143
|
-
this.controls.dispatchEvent(new shaka.util.FakeEvent('submenuopen'));
|
|
144
|
-
shaka.ui.Utils.setDisplay(this.audioLangMenu_, true);
|
|
145
|
-
// Focus on the currently selected language button.
|
|
146
|
-
shaka.ui.Utils.focusOnTheChosenItem(this.audioLangMenu_);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
70
|
/** @private */
|
|
150
71
|
updateAudioLanguages_() {
|
|
151
72
|
const tracks = this.player.getVariantTracks();
|
|
152
73
|
const languages = this.player.getAudioLanguages();
|
|
153
74
|
|
|
154
|
-
shaka.ui.LanguageUtils.updateLanguages(tracks, this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
shaka.ui.Utils.focusOnTheChosenItem(this.
|
|
75
|
+
shaka.ui.LanguageUtils.updateLanguages(tracks, this.menu,
|
|
76
|
+
languages,
|
|
77
|
+
(lang) => this.onAudioLanguageSelected_(lang), /* updateChosen */ true,
|
|
78
|
+
this.currentSelection,
|
|
79
|
+
this.localization);
|
|
80
|
+
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
|
|
160
81
|
|
|
161
82
|
// TODO: document this event
|
|
162
83
|
this.controls.dispatchEvent(
|
|
@@ -166,7 +87,7 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.Element {
|
|
|
166
87
|
/** @private */
|
|
167
88
|
onTracksChanged_() {
|
|
168
89
|
const hasVariants = this.player.getVariantTracks().length > 0;
|
|
169
|
-
shaka.ui.Utils.setDisplay(this.
|
|
90
|
+
shaka.ui.Utils.setDisplay(this.button, hasVariants);
|
|
170
91
|
this.updateAudioLanguages_();
|
|
171
92
|
}
|
|
172
93
|
|
|
@@ -185,13 +106,13 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.Element {
|
|
|
185
106
|
updateLocalizedStrings_() {
|
|
186
107
|
const LocIds = shaka.ui.Locales.Ids;
|
|
187
108
|
|
|
188
|
-
this.
|
|
109
|
+
this.backButton.setAttribute(shaka.ui.Constants.ARIA_LABEL,
|
|
189
110
|
this.localization.resolve(LocIds.BACK));
|
|
190
|
-
this.
|
|
111
|
+
this.button.setAttribute(shaka.ui.Constants.ARIA_LABEL,
|
|
191
112
|
this.localization.resolve(LocIds.LANGUAGE));
|
|
192
|
-
this.
|
|
113
|
+
this.nameSpan.textContent =
|
|
193
114
|
this.localization.resolve(LocIds.LANGUAGE);
|
|
194
|
-
this.
|
|
115
|
+
this.backSpan.textContent =
|
|
195
116
|
this.localization.resolve(LocIds.LANGUAGE);
|
|
196
117
|
}
|
|
197
118
|
};
|