shaka-player 3.2.2 → 3.3.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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +6 -5
- package/CHANGELOG.md +314 -1
- package/CONTRIBUTORS +4 -2
- package/build/checkversion.py +1 -1
- package/build/conformance.textproto +183 -169
- package/build/test.py +6 -0
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +1 -0
- package/build/types/ui +2 -0
- package/demo/common/assets.js +18 -0
- package/demo/common/message_ids.js +13 -1
- package/demo/config.js +31 -1
- package/demo/index.html +1 -1
- package/demo/locales/en.json +12 -0
- package/demo/locales/source.json +48 -0
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +19 -16
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +19 -16
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +21 -14
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +11 -9
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -9
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.externs.js +293 -17
- package/dist/shaka-player.compiled.debug.js +481 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +293 -17
- package/dist/shaka-player.compiled.js +846 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +250 -30
- package/dist/shaka-player.ui.debug.d.ts +250 -30
- package/dist/shaka-player.ui.debug.externs.js +322 -28
- package/dist/shaka-player.ui.debug.js +630 -553
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +322 -28
- package/dist/shaka-player.ui.js +1048 -1006
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/cmcd.js +186 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/manifest.js +12 -1
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +31 -2
- package/externs/shaka/player.js +138 -5
- package/externs/shaka/text.js +24 -0
- package/externs/webcrypto.js +18 -0
- package/karma.conf.js +19 -15
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +1 -0
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +8 -4
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +79 -33
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +116 -23
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/quality_observer.js +274 -0
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +47 -6
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +85 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +13 -5
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/storage.js +401 -293
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +254 -27
- package/lib/polyfill/media_capabilities.js +30 -15
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/ttml_text_parser.js +109 -60
- package/lib/text/ui_text_displayer.js +19 -13
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +86 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/shaka-player.uncompiled.js +3 -0
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +144 -1
- package/test/dash/dash_parser_manifest_unit.js +37 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +76 -0
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/quality_observer_unit.js +142 -0
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +4 -2
- package/test/media/streaming_engine_unit.js +147 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +140 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +3 -2
- package/test/test/util/test_scheme.js +14 -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 +544 -228
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +36 -30
- package/third_party/closure-uri/uri.js +21 -0
- package/ui/airplay_button.js +1 -0
- package/ui/audio_language_selection.js +7 -0
- package/ui/cast_button.js +1 -0
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +44 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +14 -5
- package/ui/fullscreen_button.js +1 -0
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +12 -36
- package/ui/loop_button.js +5 -0
- package/ui/mute_button.js +2 -1
- package/ui/overflow_menu.js +5 -25
- package/ui/pip_button.js +5 -0
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +8 -9
- package/ui/resolution_selection.js +7 -0
- package/ui/rewind_button.js +15 -6
- package/ui/seek_bar.js +4 -2
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +12 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
- package/externs/media_capabilities.js +0 -16
|
@@ -16,14 +16,14 @@ describe('StreamUtils', () => {
|
|
|
16
16
|
/** @type {!jasmine.Spy} */
|
|
17
17
|
let decodingInfoSpy;
|
|
18
18
|
|
|
19
|
-
const originalDecodingInfo =
|
|
19
|
+
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
|
|
20
20
|
|
|
21
21
|
beforeEach(() => {
|
|
22
22
|
decodingInfoSpy = jasmine.createSpy('decodingInfo');
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
afterEach(() => {
|
|
26
|
-
|
|
26
|
+
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
describe('filterStreamsByLanguageAndRole', () => {
|
|
@@ -511,7 +511,7 @@ describe('StreamUtils', () => {
|
|
|
511
511
|
});
|
|
512
512
|
|
|
513
513
|
it('handles decodingInfo exception', async () => {
|
|
514
|
-
|
|
514
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
515
515
|
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
516
516
|
// If decodingInfo() fails, setDecodingInfo should finish without throwing
|
|
517
517
|
// an exception, and the variant should have no decodingInfo result.
|
|
@@ -539,38 +539,44 @@ describe('StreamUtils', () => {
|
|
|
539
539
|
});
|
|
540
540
|
|
|
541
541
|
it('includes transferFunction in config when hdr', async () => {
|
|
542
|
-
|
|
543
|
-
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
542
|
+
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
|
|
544
543
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
544
|
+
try {
|
|
545
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
546
|
+
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
547
|
+
|
|
548
|
+
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
549
|
+
manifest.addVariant(0, (variant) => {
|
|
550
|
+
variant.addVideo(0, (stream) => {
|
|
551
|
+
stream.mime('video/mp4', 'avc1.640028');
|
|
552
|
+
stream.hdr = 'SDR';
|
|
553
|
+
});
|
|
550
554
|
});
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
555
|
+
manifest.addVariant(1, (variant) => {
|
|
556
|
+
variant.addVideo(1, (stream) => {
|
|
557
|
+
stream.mime('video/mp4', 'hvc1.2.4.L150.90');
|
|
558
|
+
stream.hdr = 'PQ';
|
|
559
|
+
});
|
|
556
560
|
});
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
561
|
+
manifest.addVariant(2, (variant) => {
|
|
562
|
+
variant.addVideo(2, (stream) => {
|
|
563
|
+
stream.mime('video/mp4', 'hvc1.2.4.L153.B0');
|
|
564
|
+
stream.hdr = 'HLG';
|
|
565
|
+
});
|
|
562
566
|
});
|
|
563
567
|
});
|
|
564
|
-
});
|
|
565
568
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
569
|
+
await StreamUtils.getDecodingInfosForVariants(manifest.variants,
|
|
570
|
+
/* usePersistentLicenses= */ false, /* srcEquals= */ false);
|
|
571
|
+
expect(decodingInfoSpy.calls.argsFor(0)[0].video.transferFunction)
|
|
572
|
+
.toBe('srgb');
|
|
573
|
+
expect(decodingInfoSpy.calls.argsFor(1)[0].video.transferFunction)
|
|
574
|
+
.toBe('pq');
|
|
575
|
+
expect(decodingInfoSpy.calls.argsFor(2)[0].video.transferFunction)
|
|
576
|
+
.toBe('hlg');
|
|
577
|
+
} finally {
|
|
578
|
+
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
579
|
+
}
|
|
574
580
|
});
|
|
575
581
|
});
|
|
576
582
|
|
|
@@ -861,7 +867,7 @@ describe('StreamUtils', () => {
|
|
|
861
867
|
});
|
|
862
868
|
});
|
|
863
869
|
});
|
|
864
|
-
|
|
870
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
865
871
|
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
866
872
|
decodingInfoSpy.and.callFake((config) => {
|
|
867
873
|
const res = config.video.contentType.includes('notsmooth') ?
|
|
@@ -808,6 +808,27 @@ goog.Uri.QueryData.prototype.add = function(key, value) {
|
|
|
808
808
|
return this;
|
|
809
809
|
};
|
|
810
810
|
|
|
811
|
+
/**
|
|
812
|
+
* Sets a key value pair and removes all other keys with the same value.
|
|
813
|
+
*
|
|
814
|
+
* @param {string} key Name.
|
|
815
|
+
* @param {string} value Value.
|
|
816
|
+
* @return {!goog.Uri.QueryData} Instance of this object.
|
|
817
|
+
*/
|
|
818
|
+
goog.Uri.QueryData.prototype.set = function(key, value) {
|
|
819
|
+
this.ensureKeyMapInitialized_();
|
|
820
|
+
// Invalidate the cache.
|
|
821
|
+
this.encodedQuery_ = null;
|
|
822
|
+
|
|
823
|
+
if (!this.keyMap_.hasOwnProperty(key)) {
|
|
824
|
+
this.add(key, value);
|
|
825
|
+
} else {
|
|
826
|
+
this.keyMap_[key] = [value];
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return this;
|
|
830
|
+
};
|
|
831
|
+
|
|
811
832
|
|
|
812
833
|
/**
|
|
813
834
|
* @return {string} Encoded query string.
|
package/ui/airplay_button.js
CHANGED
|
@@ -36,6 +36,7 @@ shaka.ui.AirPlayButton = class extends shaka.ui.Element {
|
|
|
36
36
|
/** @private {!HTMLButtonElement} */
|
|
37
37
|
this.airplayButton_ = shaka.util.Dom.createButton();
|
|
38
38
|
this.airplayButton_.classList.add('shaka-airplay-button');
|
|
39
|
+
this.airplayButton_.classList.add('shaka-tooltip');
|
|
39
40
|
this.airplayButton_.ariaPressed = 'false';
|
|
40
41
|
|
|
41
42
|
/** @private {!HTMLElement} */
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
goog.provide('shaka.ui.AudioLanguageSelection');
|
|
9
9
|
|
|
10
|
+
goog.require('shaka.ui.Controls');
|
|
10
11
|
goog.require('shaka.ui.Enums');
|
|
11
12
|
goog.require('shaka.ui.LanguageUtils');
|
|
12
13
|
goog.require('shaka.ui.Locales');
|
|
@@ -31,6 +32,7 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {
|
|
|
31
32
|
super(parent, controls, shaka.ui.Enums.MaterialDesignIcons.LANGUAGE);
|
|
32
33
|
|
|
33
34
|
this.button.classList.add('shaka-language-button');
|
|
35
|
+
this.button.classList.add('shaka-tooltip-status');
|
|
34
36
|
this.menu.classList.add('shaka-audio-languages');
|
|
35
37
|
|
|
36
38
|
this.eventManager.listen(
|
|
@@ -71,6 +73,8 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {
|
|
|
71
73
|
|
|
72
74
|
this.controls.dispatchEvent(
|
|
73
75
|
new shaka.util.FakeEvent('languageselectionupdated'));
|
|
76
|
+
|
|
77
|
+
this.button.setAttribute('shaka-status', this.currentSelection.innerText);
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
/** @private */
|
|
@@ -118,3 +122,6 @@ shaka.ui.AudioLanguageSelection.Factory = class {
|
|
|
118
122
|
|
|
119
123
|
shaka.ui.OverflowMenu.registerElement(
|
|
120
124
|
'language', new shaka.ui.AudioLanguageSelection.Factory());
|
|
125
|
+
|
|
126
|
+
shaka.ui.Controls.registerElement(
|
|
127
|
+
'language', new shaka.ui.AudioLanguageSelection.Factory());
|
package/ui/cast_button.js
CHANGED
|
@@ -41,6 +41,7 @@ shaka.ui.CastButton = class extends shaka.ui.Element {
|
|
|
41
41
|
/** @private {!HTMLButtonElement} */
|
|
42
42
|
this.castButton_ = shaka.util.Dom.createButton();
|
|
43
43
|
this.castButton_.classList.add('shaka-cast-button');
|
|
44
|
+
this.castButton_.classList.add('shaka-tooltip');
|
|
44
45
|
this.castButton_.ariaPressed = 'false';
|
|
45
46
|
|
|
46
47
|
/** @private {!HTMLElement} */
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
goog.provide('shaka.ui.ContextMenu');
|
|
9
|
+
|
|
10
|
+
goog.require('goog.asserts');
|
|
11
|
+
goog.require('shaka.log');
|
|
12
|
+
goog.require('shaka.ui.Element');
|
|
13
|
+
goog.require('shaka.ui.Utils');
|
|
14
|
+
goog.require('shaka.util.Dom');
|
|
15
|
+
goog.requireType('shaka.ui.Controls');
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @extends {shaka.ui.Element}
|
|
20
|
+
* @final
|
|
21
|
+
* @export
|
|
22
|
+
*/
|
|
23
|
+
shaka.ui.ContextMenu = class extends shaka.ui.Element {
|
|
24
|
+
/**
|
|
25
|
+
* @param {!HTMLElement} parent
|
|
26
|
+
* @param {!shaka.ui.Controls} controls
|
|
27
|
+
*/
|
|
28
|
+
constructor(parent, controls) {
|
|
29
|
+
super(parent, controls);
|
|
30
|
+
|
|
31
|
+
/** @private {!shaka.extern.UIConfiguration} */
|
|
32
|
+
this.config_ = this.controls.getConfig();
|
|
33
|
+
|
|
34
|
+
/** @private {HTMLElement} */
|
|
35
|
+
this.controlsContainer_ = this.controls.getControlsContainer();
|
|
36
|
+
|
|
37
|
+
/** @private {!Array.<shaka.extern.IUIElement>} */
|
|
38
|
+
this.children_ = [];
|
|
39
|
+
|
|
40
|
+
/** @private {!HTMLElement} */
|
|
41
|
+
this.contextMenu_ = shaka.util.Dom.createHTMLElement('div');
|
|
42
|
+
this.contextMenu_.classList.add('shaka-no-propagation');
|
|
43
|
+
this.contextMenu_.classList.add('shaka-context-menu');
|
|
44
|
+
this.contextMenu_.classList.add('shaka-hidden');
|
|
45
|
+
|
|
46
|
+
this.controlsContainer_.appendChild(this.contextMenu_);
|
|
47
|
+
|
|
48
|
+
this.eventManager.listen(this.controlsContainer_, 'contextmenu', (e) => {
|
|
49
|
+
if (this.contextMenu_.classList.contains('shaka-hidden')) {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
|
|
52
|
+
const controlsLocation =
|
|
53
|
+
this.controlsContainer_.getBoundingClientRect();
|
|
54
|
+
this.contextMenu_.style.left = `${e.clientX - controlsLocation.left}px`;
|
|
55
|
+
this.contextMenu_.style.top = `${e.clientY - controlsLocation.top}px`;
|
|
56
|
+
|
|
57
|
+
shaka.ui.Utils.setDisplay(this.contextMenu_, true);
|
|
58
|
+
} else {
|
|
59
|
+
shaka.ui.Utils.setDisplay(this.contextMenu_, false);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
this.eventManager.listen(window, 'click', () => {
|
|
64
|
+
shaka.ui.Utils.setDisplay(this.contextMenu_, false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.createChildren_();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @override */
|
|
71
|
+
release() {
|
|
72
|
+
this.controlsContainer_ = null;
|
|
73
|
+
|
|
74
|
+
for (const element of this.children_) {
|
|
75
|
+
element.release();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.children_ = [];
|
|
79
|
+
super.release();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} name
|
|
84
|
+
* @param {!shaka.extern.IUIElement.Factory} factory
|
|
85
|
+
* @export
|
|
86
|
+
*/
|
|
87
|
+
static registerElement(name, factory) {
|
|
88
|
+
shaka.ui.ContextMenu.elementNamesToFactories_.set(name, factory);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
createChildren_() {
|
|
95
|
+
for (const name of this.config_.contextMenuElements) {
|
|
96
|
+
const factory =
|
|
97
|
+
shaka.ui.ContextMenu.elementNamesToFactories_.get(name);
|
|
98
|
+
if (factory) {
|
|
99
|
+
goog.asserts.assert(this.controls, 'Controls should not be null!');
|
|
100
|
+
this.children_.push(factory.create(this.contextMenu_, this.controls));
|
|
101
|
+
} else {
|
|
102
|
+
shaka.log.alwaysWarn('Unrecognized context menu element:', name);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** @private {!Map.<string, !shaka.extern.IUIElement.Factory>} */
|
|
109
|
+
shaka.ui.ContextMenu.elementNamesToFactories_ = new Map();
|
package/ui/controls.js
CHANGED
|
@@ -16,6 +16,7 @@ goog.require('shaka.log');
|
|
|
16
16
|
goog.require('shaka.ui.AdCounter');
|
|
17
17
|
goog.require('shaka.ui.AdPosition');
|
|
18
18
|
goog.require('shaka.ui.BigPlayButton');
|
|
19
|
+
goog.require('shaka.ui.ContextMenu');
|
|
19
20
|
goog.require('shaka.ui.Locales');
|
|
20
21
|
goog.require('shaka.ui.Localization');
|
|
21
22
|
goog.require('shaka.ui.SeekBar');
|
|
@@ -253,6 +254,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
253
254
|
|
|
254
255
|
this.localization_ = null;
|
|
255
256
|
this.pressedKeys_.clear();
|
|
257
|
+
|
|
258
|
+
// FakeEventTarget implements IReleasable
|
|
259
|
+
super.release();
|
|
256
260
|
}
|
|
257
261
|
|
|
258
262
|
|
|
@@ -323,6 +327,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
323
327
|
this.playButton_ = null;
|
|
324
328
|
}
|
|
325
329
|
|
|
330
|
+
if (this.contextMenu_) {
|
|
331
|
+
this.contextMenu_ = null;
|
|
332
|
+
}
|
|
333
|
+
|
|
326
334
|
if (this.controlsContainer_) {
|
|
327
335
|
shaka.util.Dom.removeAllChildren(this.controlsContainer_);
|
|
328
336
|
this.releaseChildElements_();
|
|
@@ -650,6 +658,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
650
658
|
this.addPlayButton_();
|
|
651
659
|
}
|
|
652
660
|
|
|
661
|
+
if (this.config_.customContextMenu) {
|
|
662
|
+
this.addContextMenu_();
|
|
663
|
+
}
|
|
664
|
+
|
|
653
665
|
if (!this.spinnerContainer_) {
|
|
654
666
|
this.addBufferingSpinner_();
|
|
655
667
|
}
|
|
@@ -708,6 +720,14 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
708
720
|
this.elements_.push(this.playButton_);
|
|
709
721
|
}
|
|
710
722
|
|
|
723
|
+
/** @private */
|
|
724
|
+
addContextMenu_() {
|
|
725
|
+
/** @private {shaka.ui.ContextMenu} */
|
|
726
|
+
this.contextMenu_ =
|
|
727
|
+
new shaka.ui.ContextMenu(this.controlsButtonPanel_, this);
|
|
728
|
+
this.elements_.push(this.contextMenu_);
|
|
729
|
+
}
|
|
730
|
+
|
|
711
731
|
/** @private */
|
|
712
732
|
addScrimContainer_() {
|
|
713
733
|
// This is the container that gets styled by CSS to have the
|
|
@@ -722,7 +742,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
722
742
|
/** @private {!HTMLElement} */
|
|
723
743
|
this.adPanel_ = shaka.util.Dom.createHTMLElement('div');
|
|
724
744
|
this.adPanel_.classList.add('shaka-ad-controls');
|
|
725
|
-
|
|
745
|
+
const showAdPanel = this.ad_ != null && this.ad_.isLinear();
|
|
746
|
+
shaka.ui.Utils.setDisplay(this.adPanel_, showAdPanel);
|
|
726
747
|
this.bottomControls_.appendChild(this.adPanel_);
|
|
727
748
|
|
|
728
749
|
const adPosition = new shaka.ui.AdPosition(this.adPanel_, this);
|
|
@@ -779,8 +800,12 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
779
800
|
// on the page. The click event listener on window ensures that.
|
|
780
801
|
// However, clicks on the bottom controls don't propagate to the container,
|
|
781
802
|
// so we have to explicitly hide the menus onclick here.
|
|
782
|
-
this.eventManager_.listen(this.bottomControls_, 'click', () => {
|
|
783
|
-
this
|
|
803
|
+
this.eventManager_.listen(this.bottomControls_, 'click', (e) => {
|
|
804
|
+
// We explicitly deny this measure when clicking on buttons that
|
|
805
|
+
// open submenus in the control panel.
|
|
806
|
+
if (!e.target['closest']('.shaka-overflow-button')) {
|
|
807
|
+
this.hideSettingsMenus();
|
|
808
|
+
}
|
|
784
809
|
});
|
|
785
810
|
|
|
786
811
|
this.addAdControls_();
|
|
@@ -790,6 +815,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
790
815
|
this.controlsButtonPanel_.classList.add('shaka-controls-button-panel');
|
|
791
816
|
this.controlsButtonPanel_.classList.add(
|
|
792
817
|
'shaka-show-controls-on-mouse-over');
|
|
818
|
+
if (this.config_.enableTooltips) {
|
|
819
|
+
this.controlsButtonPanel_.classList.add('shaka-tooltips-on');
|
|
820
|
+
}
|
|
793
821
|
this.bottomControls_.appendChild(this.controlsButtonPanel_);
|
|
794
822
|
|
|
795
823
|
// Create the elements specified by controlPanelElements
|
|
@@ -859,7 +887,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
859
887
|
|
|
860
888
|
|
|
861
889
|
/**
|
|
862
|
-
* Adds a container for
|
|
890
|
+
* Adds a container for client side ad UI with IMA SDK.
|
|
863
891
|
*
|
|
864
892
|
* @private
|
|
865
893
|
*/
|
|
@@ -868,6 +896,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
868
896
|
this.clientAdContainer_ = shaka.util.Dom.createHTMLElement('div');
|
|
869
897
|
this.clientAdContainer_.classList.add('shaka-client-side-ad-container');
|
|
870
898
|
shaka.ui.Utils.setDisplay(this.clientAdContainer_, false);
|
|
899
|
+
this.eventManager_.listen(this.clientAdContainer_, 'click', () => {
|
|
900
|
+
this.onContainerClick_();
|
|
901
|
+
});
|
|
871
902
|
this.videoContainer_.appendChild(this.clientAdContainer_);
|
|
872
903
|
}
|
|
873
904
|
|
|
@@ -894,6 +925,12 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
894
925
|
// Listen for click events to dismiss the settings menus.
|
|
895
926
|
this.eventManager_.listen(window, 'click', () => this.hideSettingsMenus());
|
|
896
927
|
|
|
928
|
+
// Avoid having multiple submenus open at the same time.
|
|
929
|
+
this.eventManager_.listen(
|
|
930
|
+
this, 'submenuopen', () => {
|
|
931
|
+
this.hideSettingsMenus();
|
|
932
|
+
});
|
|
933
|
+
|
|
897
934
|
this.eventManager_.listen(this.video_, 'play', () => {
|
|
898
935
|
this.onPlayStateChange_();
|
|
899
936
|
});
|
|
@@ -1100,7 +1137,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
1100
1137
|
// recent mouse movement, we're in keyboard navigation, or one of a special
|
|
1101
1138
|
// class of elements is hovered.
|
|
1102
1139
|
if (adIsPaused ||
|
|
1103
|
-
(!this.ad_ && videoIsPaused) ||
|
|
1140
|
+
((!this.ad_ || !this.ad_.isLinear()) && videoIsPaused) ||
|
|
1104
1141
|
this.recentMouseMovement_ ||
|
|
1105
1142
|
keyboardNavigationMode ||
|
|
1106
1143
|
this.isHovered_()) {
|
|
@@ -1144,14 +1181,14 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
1144
1181
|
|
|
1145
1182
|
if (this.anySettingsMenusAreOpen()) {
|
|
1146
1183
|
this.hideSettingsMenusTimer_.tickNow();
|
|
1147
|
-
} else {
|
|
1184
|
+
} else if (this.config_.singleClickForPlayAndPause) {
|
|
1148
1185
|
this.onPlayPauseClick_();
|
|
1149
1186
|
}
|
|
1150
1187
|
}
|
|
1151
1188
|
|
|
1152
1189
|
/** @private */
|
|
1153
1190
|
onPlayPauseClick_() {
|
|
1154
|
-
if (this.ad_) {
|
|
1191
|
+
if (this.ad_ && this.ad_.isLinear()) {
|
|
1155
1192
|
this.playPauseAd();
|
|
1156
1193
|
} else {
|
|
1157
1194
|
this.playPausePresentation();
|
package/ui/controls.less
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
@import "less/other_elements.less";
|
|
13
13
|
@import "less/overflow_menu.less";
|
|
14
14
|
@import "less/ad_controls.less";
|
|
15
|
+
@import "less/tooltip.less";
|
|
15
16
|
@import (css, inline) "https://fonts.googleapis.com/css?family=Roboto";
|
|
16
17
|
// NOTE: Working around google/material-design-icons#958
|
|
17
18
|
@import (css, inline) "https://fonts.googleapis.com/icon?family=Material+Icons+Round";
|
package/ui/enums.js
CHANGED
|
@@ -16,7 +16,9 @@ goog.provide('shaka.ui.Enums');
|
|
|
16
16
|
shaka.ui.Enums.MaterialDesignIcons = {
|
|
17
17
|
'FULLSCREEN': 'fullscreen',
|
|
18
18
|
'EXIT_FULLSCREEN': 'fullscreen_exit',
|
|
19
|
+
'CLOSE': 'close',
|
|
19
20
|
'CLOSED_CAPTIONS': 'closed_caption',
|
|
21
|
+
'CLOSED_CAPTIONS_OFF': 'closed_caption_disabled',
|
|
20
22
|
'CHECKMARK': 'done',
|
|
21
23
|
'LANGUAGE': 'language',
|
|
22
24
|
'PIP': 'picture_in_picture_alt',
|
|
@@ -40,4 +42,6 @@ shaka.ui.Enums.MaterialDesignIcons = {
|
|
|
40
42
|
'UNLOOP': 'repeat_on',
|
|
41
43
|
'AIRPLAY': 'airplay',
|
|
42
44
|
'REPLAY': 'replay',
|
|
45
|
+
'STATISTICS_ON': 'insert_chart_outlined',
|
|
46
|
+
'STATISTICS_OFF': 'insert_chart',
|
|
43
47
|
};
|
package/ui/externs/ui.js
CHANGED
|
@@ -63,8 +63,14 @@ shaka.extern.UIVolumeBarColors;
|
|
|
63
63
|
* @typedef {{
|
|
64
64
|
* controlPanelElements: !Array.<string>,
|
|
65
65
|
* overflowMenuButtons: !Array.<string>,
|
|
66
|
+
* contextMenuElements: !Array.<string>,
|
|
67
|
+
* statisticsList: !Array.<string>,
|
|
68
|
+
* playbackRates: !Array.<number>,
|
|
69
|
+
* fastForwardRates: !Array.<number>,
|
|
70
|
+
* rewindRates: !Array.<number>,
|
|
66
71
|
* addSeekBar: boolean,
|
|
67
72
|
* addBigPlayButton: boolean,
|
|
73
|
+
* customContextMenu: boolean,
|
|
68
74
|
* castReceiverAppId: string,
|
|
69
75
|
* clearBufferOnQualityChange: boolean,
|
|
70
76
|
* showUnbufferedStart: boolean,
|
|
@@ -73,20 +79,34 @@ shaka.extern.UIVolumeBarColors;
|
|
|
73
79
|
* trackLabelFormat: shaka.ui.Overlay.TrackLabelFormat,
|
|
74
80
|
* fadeDelay: number,
|
|
75
81
|
* doubleClickForFullscreen: boolean,
|
|
82
|
+
* singleClickForPlayAndPause: boolean,
|
|
76
83
|
* enableKeyboardPlaybackControls: boolean,
|
|
77
84
|
* enableFullscreenOnRotation: boolean,
|
|
78
|
-
* forceLandscapeOnFullscreen: boolean
|
|
85
|
+
* forceLandscapeOnFullscreen: boolean,
|
|
86
|
+
* enableTooltips: boolean
|
|
79
87
|
* }}
|
|
80
88
|
*
|
|
81
89
|
* @property {!Array.<string>} controlPanelElements
|
|
82
90
|
* The ordered list of control panel elements of the UI.
|
|
83
91
|
* @property {!Array.<string>} overflowMenuButtons
|
|
84
92
|
* The ordered list of the overflow menu buttons.
|
|
93
|
+
* @property {!Array.<string>} contextMenuElements
|
|
94
|
+
* The ordered list of buttons in the context menu.
|
|
95
|
+
* @property {!Array.<string>} statisticsList
|
|
96
|
+
* The ordered list of statistics present in the statistics container.
|
|
97
|
+
* @property {!Array.<number>} playbackRates
|
|
98
|
+
* The ordered list of rates for playback selection.
|
|
99
|
+
* @property {!Array.<number>} fastForwardRates
|
|
100
|
+
* The ordered list of rates for fast forward selection.
|
|
101
|
+
* @property {!Array.<number>} rewindRates
|
|
102
|
+
* The ordered list of rates for rewind selection.
|
|
85
103
|
* @property {boolean} addSeekBar
|
|
86
104
|
* Whether or not a seek bar should be part of the UI.
|
|
87
105
|
* @property {boolean} addBigPlayButton
|
|
88
106
|
* Whether or not a big play button in the center of the video
|
|
89
107
|
* should be part of the UI.
|
|
108
|
+
* @property {boolean} customContextMenu
|
|
109
|
+
* Whether or not a custom context menu replaces the default.
|
|
90
110
|
* @property {string} castReceiverAppId
|
|
91
111
|
* Receiver app id to use for the Chromecast support.
|
|
92
112
|
* @property {boolean} clearBufferOnQualityChange
|
|
@@ -135,6 +155,9 @@ shaka.extern.UIVolumeBarColors;
|
|
|
135
155
|
* Whether or not double-clicking on the UI should cause it to enter
|
|
136
156
|
* fullscreen.
|
|
137
157
|
* Defaults to true.
|
|
158
|
+
* @property {boolean} singleClickForPlayAndPause
|
|
159
|
+
* Whether or not clicking on the video should cause it to play or pause.
|
|
160
|
+
* Defaults to true.
|
|
138
161
|
* @property {boolean} enableKeyboardPlaybackControls
|
|
139
162
|
* Whether or not playback controls via keyboard is enabled, such as seek
|
|
140
163
|
* forward, seek backward, jump to the beginning/end of the video.
|
|
@@ -147,6 +170,10 @@ shaka.extern.UIVolumeBarColors;
|
|
|
147
170
|
* enters fullscreen. Note that this behavior is based on an experimental
|
|
148
171
|
* browser API, and may not work on all platforms.
|
|
149
172
|
* Defaults to true.
|
|
173
|
+
* @property {boolean} enableTooltips
|
|
174
|
+
* Whether or not buttons in the control panel display tooltips that contain
|
|
175
|
+
* information about their function.
|
|
176
|
+
* Defaults to false.
|
|
150
177
|
* @exportDoc
|
|
151
178
|
*/
|
|
152
179
|
shaka.extern.UIConfiguration;
|
|
@@ -32,11 +32,16 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element {
|
|
|
32
32
|
this.button_ = shaka.util.Dom.createButton();
|
|
33
33
|
this.button_.classList.add('material-icons-round');
|
|
34
34
|
this.button_.classList.add('shaka-fast-forward-button');
|
|
35
|
+
this.button_.classList.add('shaka-tooltip-status');
|
|
36
|
+
this.button_.setAttribute('shaka-status', '1x');
|
|
35
37
|
this.button_.textContent =
|
|
36
38
|
shaka.ui.Enums.MaterialDesignIcons.FAST_FORWARD;
|
|
37
39
|
this.parent.appendChild(this.button_);
|
|
38
40
|
this.updateAriaLabel_();
|
|
39
41
|
|
|
42
|
+
/** @private {!Array.<number>} */
|
|
43
|
+
this.fastForwardRates_ = this.controls.getConfig().fastForwardRates;
|
|
44
|
+
|
|
40
45
|
this.eventManager.listen(
|
|
41
46
|
this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
|
|
42
47
|
this.updateAriaLabel_();
|
|
@@ -61,7 +66,7 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element {
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
/**
|
|
64
|
-
* Cycles trick play rate between
|
|
69
|
+
* Cycles trick play rate between the selected fast forward rates.
|
|
65
70
|
* @private
|
|
66
71
|
*/
|
|
67
72
|
fastForward_() {
|
|
@@ -70,11 +75,15 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element {
|
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
const trickPlayRate = this.player.getPlaybackRate();
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
const newRateIndex = this.fastForwardRates_.indexOf(trickPlayRate) + 1;
|
|
79
|
+
|
|
80
|
+
// When the button is clicked, the next rate in this.fastForwardRates_ is
|
|
81
|
+
// selected. If no more rates are available, the first one is set.
|
|
82
|
+
const newRate = (newRateIndex != this.fastForwardRates_.length) ?
|
|
83
|
+
this.fastForwardRates_[newRateIndex] : this.fastForwardRates_[0];
|
|
77
84
|
this.player.trickPlay(newRate);
|
|
85
|
+
|
|
86
|
+
this.button_.setAttribute('shaka-status', newRate + 'x');
|
|
78
87
|
}
|
|
79
88
|
};
|
|
80
89
|
|
package/ui/fullscreen_button.js
CHANGED
|
@@ -32,6 +32,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
|
|
32
32
|
this.button_ = shaka.util.Dom.createButton();
|
|
33
33
|
this.button_.classList.add('shaka-fullscreen-button');
|
|
34
34
|
this.button_.classList.add('material-icons-round');
|
|
35
|
+
this.button_.classList.add('shaka-tooltip');
|
|
35
36
|
|
|
36
37
|
// Don't show the button if fullscreen is not supported
|
|
37
38
|
if (!document.fullscreenEnabled) {
|
package/ui/less/buttons.less
CHANGED
|
@@ -75,10 +75,6 @@
|
|
|
75
75
|
.shaka-current-time {
|
|
76
76
|
font-size: 14px;
|
|
77
77
|
color: rgb(255, 255, 255);
|
|
78
|
-
|
|
79
|
-
/* Make the time element the right size for the text, instead of defaulting
|
|
80
|
-
* to the same size as the rest of the controls. */
|
|
81
|
-
height: auto;
|
|
82
78
|
cursor: pointer;
|
|
83
79
|
|
|
84
80
|
&[disabled] {
|
package/ui/less/containers.less
CHANGED
|
@@ -195,6 +195,80 @@
|
|
|
195
195
|
align-items: center;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
.shaka-statistics-container {
|
|
199
|
+
overflow-x: hidden;
|
|
200
|
+
overflow-y: auto;
|
|
201
|
+
|
|
202
|
+
min-width: 300px;
|
|
203
|
+
|
|
204
|
+
color: white;
|
|
205
|
+
background-color: rgba(35, 35, 35, 0.9);
|
|
206
|
+
|
|
207
|
+
font-size: 14px;
|
|
208
|
+
|
|
209
|
+
padding: 5px 10px;
|
|
210
|
+
border-radius: 2px;
|
|
211
|
+
|
|
212
|
+
position: absolute;
|
|
213
|
+
z-index: 2;
|
|
214
|
+
left: 15px;
|
|
215
|
+
top: 15px;
|
|
216
|
+
|
|
217
|
+
/* Fades out with the other controls. */
|
|
218
|
+
.show-when-controls-shown();
|
|
219
|
+
|
|
220
|
+
div {
|
|
221
|
+
display: flex;
|
|
222
|
+
justify-content: space-between;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
span {
|
|
226
|
+
color: rgb(150, 150, 150);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.shaka-context-menu {
|
|
231
|
+
background-color: rgba(35, 35, 35, 0.9);
|
|
232
|
+
|
|
233
|
+
border-radius: 2px;
|
|
234
|
+
|
|
235
|
+
position: absolute;
|
|
236
|
+
z-index: 3;
|
|
237
|
+
|
|
238
|
+
button {
|
|
239
|
+
padding: 5px 10px;
|
|
240
|
+
|
|
241
|
+
width: 100%;
|
|
242
|
+
display: flex;
|
|
243
|
+
align-items: center;
|
|
244
|
+
|
|
245
|
+
color: white;
|
|
246
|
+
background: transparent;
|
|
247
|
+
border: 0;
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
|
|
250
|
+
&:hover {
|
|
251
|
+
background-color: rgba(50, 50, 50, 0.9);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
label {
|
|
256
|
+
padding: 0 20px;
|
|
257
|
+
|
|
258
|
+
align-items: flex-start;
|
|
259
|
+
|
|
260
|
+
color: white;
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.shaka-current-selection-span {
|
|
265
|
+
align-items: flex-start;
|
|
266
|
+
|
|
267
|
+
color: white;
|
|
268
|
+
cursor: pointer;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
198
272
|
.shaka-scrim-container {
|
|
199
273
|
margin: 0;
|
|
200
274
|
.fill-container();
|