shaka-player 3.3.4 → 4.0.1
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/config.yml +1 -0
- package/.github/workflows/build-and-test.yaml +0 -1
- package/.github/workflows/demo-version-index.yaml +8 -0
- package/.github/workflows/release-please.yaml +19 -3
- package/.github/workflows/selenium-lab-tests.yaml +0 -1
- package/.github/workflows/sync-labels.yaml +33 -0
- package/.github/workflows/update-issues.yaml +29 -0
- package/.github/workflows/validate-pr-title.yaml +35 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +209 -28
- package/CONTRIBUTORS +1 -0
- package/README.md +10 -7
- package/app-engine/demo-version-index/README.md +3 -3
- package/app-engine/demo-version-index/app.yaml +27 -1
- package/app-engine/demo-version-index/generate.py +164 -0
- package/app-engine/demo-version-index/main.py +4 -155
- package/app-engine/demo-version-index/requirements.txt +0 -1
- package/build/README.md +2 -2
- package/build/compiler.py +1 -1
- package/build/generateTsDefs.py +0 -2
- package/build/misspellings.txt +1 -0
- package/build/shaka-lab.yaml +0 -2
- package/build/test.py +7 -1
- package/build/types/core +0 -1
- package/build/types/polyfill +0 -3
- package/demo/common/asset.js +17 -1
- package/demo/common/assets.js +29 -0
- package/demo/common/message_ids.js +6 -3
- package/demo/config.js +10 -11
- package/demo/custom.js +39 -0
- package/demo/demo_utils.js +0 -1
- package/demo/locales/en.json +6 -3
- package/demo/locales/source.json +22 -10
- package/demo/main.js +9 -6
- package/demo/search.js +2 -1
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +71 -64
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +71 -64
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +16 -20
- package/dist/locales.js +33 -31
- package/dist/receiver.compiled.debug.js +32 -28
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +32 -28
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +78 -82
- package/dist/shaka-player.compiled.debug.d.ts +78 -82
- package/dist/shaka-player.compiled.debug.externs.js +127 -126
- package/dist/shaka-player.compiled.debug.js +471 -523
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +127 -126
- package/dist/shaka-player.compiled.js +844 -858
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +80 -82
- package/dist/shaka-player.ui.debug.d.ts +80 -82
- package/dist/shaka-player.ui.debug.externs.js +135 -126
- package/dist/shaka-player.ui.debug.js +531 -581
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +135 -126
- package/dist/shaka-player.ui.js +991 -1001
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/config.md +0 -2
- package/docs/tutorials/drm-config.md +5 -0
- package/docs/tutorials/fairplay.md +7 -22
- package/docs/tutorials/faq.md +0 -11
- package/docs/tutorials/index.json +2 -8
- package/docs/tutorials/plugins.md +1 -3
- package/docs/tutorials/upgrade.md +85 -11
- package/externs/polyfill.js +15 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/net.js +12 -0
- package/externs/shaka/offline.js +5 -1
- package/externs/shaka/player.js +39 -21
- package/externs/shaka/text.js +12 -12
- package/externs/sourcebuffer.js +15 -0
- package/karma.conf.js +4 -0
- package/lib/ads/client_side_ad.js +13 -2
- package/lib/ads/client_side_ad_manager.js +22 -4
- package/lib/cast/cast_utils.js +0 -1
- package/lib/dash/content_protection.js +85 -5
- package/lib/dash/dash_parser.js +3 -3
- package/lib/hls/hls_parser.js +391 -747
- package/lib/hls/manifest_text_parser.js +2 -3
- package/lib/media/drm_engine.js +102 -37
- package/lib/media/gap_jumping_controller.js +10 -45
- package/lib/media/media_source_engine.js +157 -31
- package/lib/media/playhead.js +2 -5
- package/lib/media/presentation_timeline.js +1 -2
- package/lib/media/segment_index.js +13 -46
- package/lib/media/segment_reference.js +47 -1
- package/lib/media/streaming_engine.js +28 -11
- package/lib/media/time_ranges_utils.js +4 -9
- package/lib/net/networking_engine.js +3 -0
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +100 -132
- package/lib/player.js +181 -307
- package/lib/polyfill/media_capabilities.js +12 -6
- package/lib/polyfill/mediasource.js +0 -28
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
- package/lib/text/cue.js +0 -6
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/simple_text_displayer.js +1 -7
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +19 -3
- package/lib/text/ttml_text_parser.js +8 -0
- package/lib/text/ui_text_displayer.js +1 -7
- package/lib/text/vtt_text_parser.js +27 -5
- package/lib/text/web_vtt_generator.js +1 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +16 -13
- package/lib/util/fairplay_utils.js +109 -0
- package/lib/util/functional.js +0 -42
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +50 -1
- package/lib/util/periods.js +5 -4
- package/lib/util/platform.js +45 -0
- package/lib/util/player_configuration.js +26 -20
- package/lib/util/pssh.js +23 -4
- package/lib/util/stream_utils.js +96 -24
- package/lib/util/uint8array_utils.js +0 -16
- package/lib/util/xml_utils.js +1 -1
- package/package.json +4 -1
- package/roadmap.md +19 -19
- package/shaka-player.uncompiled.js +0 -3
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/dash/dash_parser_content_protection_unit.js +44 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -24
- package/test/dash/dash_parser_segment_template_unit.js +3 -4
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +121 -220
- package/test/hls/hls_parser_unit.js +515 -432
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/media/drm_engine_unit.js +86 -120
- package/test/media/media_source_engine_integration.js +26 -3
- package/test/media/media_source_engine_unit.js +7 -6
- package/test/media/playhead_unit.js +27 -191
- package/test/media/segment_index_unit.js +15 -25
- package/test/media/streaming_engine_integration.js +4 -39
- package/test/media/streaming_engine_unit.js +3 -2
- package/test/media/time_ranges_utils_unit.js +0 -5
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/manifest_convert_unit.js +34 -4
- package/test/offline/offline_manifest_parser_unit.js +1 -0
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/offline/storage_integration.js +5 -4
- package/test/player_external.js +7 -7
- package/test/player_integration.js +22 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +124 -1
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/fake_media_source_engine.js +11 -4
- package/test/test/util/manifest_generator.js +3 -1
- package/test/test/util/manifest_parser_util.js +4 -1
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +31 -2
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/test/util/test_scheme.js +24 -1
- package/test/test/util/util.js +4 -4
- package/test/text/cue_integration.js +2 -2
- package/test/text/lrc_text_parser_unit.js +6 -7
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/mp4_vtt_parser_unit.js +6 -5
- package/test/text/sbv_text_parser_unit.js +5 -6
- package/test/text/srt_text_parser_unit.js +5 -6
- package/test/text/ssa_text_parser_unit.js +17 -29
- package/test/text/text_engine_unit.js +45 -5
- package/test/text/ttml_text_parser_unit.js +77 -77
- package/test/text/vtt_text_parser_unit.js +116 -59
- package/test/ui/ui_integration.js +0 -16
- package/test/ui/ui_unit.js +27 -0
- package/test/util/cmcd_manager_unit.js +3 -0
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/test/util/stream_utils_unit.js +23 -7
- package/ui/controls.js +65 -40
- package/ui/fullscreen_button.js +26 -6
- package/ui/less/containers.less +3 -3
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +5 -0
- package/ui/resolution_selection.js +31 -33
- package/ui/ui.js +0 -17
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
- package/docs/tutorials/upgrade-v2-5.md +0 -441
- package/docs/tutorials/upgrade-v3.md +0 -38
- package/lib/polyfill/mathround.js +0 -49
- package/lib/polyfill/patchedmediakeys_apple.js +0 -744
- package/lib/polyfill/patchedmediakeys_ms.js +0 -698
- package/lib/util/mutex.js +0 -52
- package/test/util/functional_unit.js +0 -82
|
@@ -996,6 +996,85 @@ describe('PeriodCombiner', () => {
|
|
|
996
996
|
expect(audio2.originalId).toBe('2,4');
|
|
997
997
|
});
|
|
998
998
|
|
|
999
|
+
it('Matches streams with related codecs', async () => {
|
|
1000
|
+
const stream1 = makeVideoStream(1080);
|
|
1001
|
+
stream1.originalId = '1';
|
|
1002
|
+
stream1.bandwidth = 120000;
|
|
1003
|
+
stream1.codecs = 'hvc1.1.4.L126.B0';
|
|
1004
|
+
|
|
1005
|
+
const stream2 = makeVideoStream(1080);
|
|
1006
|
+
stream2.originalId = '2';
|
|
1007
|
+
stream2.bandwidth = 120000;
|
|
1008
|
+
stream2.codecs = 'hev1.2.4.L123.B0';
|
|
1009
|
+
|
|
1010
|
+
const stream3 = makeVideoStream(1080);
|
|
1011
|
+
stream3.originalId = '3';
|
|
1012
|
+
stream3.bandwidth = 120000;
|
|
1013
|
+
stream3.codecs = 'dvhe.05.01';
|
|
1014
|
+
|
|
1015
|
+
const stream4 = makeVideoStream(1080);
|
|
1016
|
+
stream4.originalId = '4';
|
|
1017
|
+
stream4.bandwidth = 120000;
|
|
1018
|
+
stream4.codecs = 'dvh1.05.01';
|
|
1019
|
+
|
|
1020
|
+
const stream5 = makeVideoStream(1080);
|
|
1021
|
+
stream5.originalId = '5';
|
|
1022
|
+
stream5.bandwidth = 120000;
|
|
1023
|
+
stream5.codecs = 'avc1.42001f';
|
|
1024
|
+
|
|
1025
|
+
const stream6 = makeVideoStream(1080);
|
|
1026
|
+
stream6.originalId = '6';
|
|
1027
|
+
stream6.bandwidth = 120000;
|
|
1028
|
+
stream6.codecs = 'avc3.42001f';
|
|
1029
|
+
|
|
1030
|
+
const stream7 = makeVideoStream(1080);
|
|
1031
|
+
stream7.originalId = '7';
|
|
1032
|
+
stream7.bandwidth = 120000;
|
|
1033
|
+
stream7.codecs = 'vp09.00.10.08';
|
|
1034
|
+
|
|
1035
|
+
const stream8 = makeVideoStream(1080);
|
|
1036
|
+
stream8.originalId = '8';
|
|
1037
|
+
stream8.bandwidth = 120000;
|
|
1038
|
+
stream8.codecs = 'vp09.01.20.08.01';
|
|
1039
|
+
|
|
1040
|
+
/** @type {!Array.<shaka.util.PeriodCombiner.Period>} */
|
|
1041
|
+
const periods = [
|
|
1042
|
+
{
|
|
1043
|
+
id: '0',
|
|
1044
|
+
videoStreams: [
|
|
1045
|
+
stream1, stream3, stream5, stream7,
|
|
1046
|
+
],
|
|
1047
|
+
audioStreams: [],
|
|
1048
|
+
textStreams: [],
|
|
1049
|
+
imageStreams: [],
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
id: '1',
|
|
1053
|
+
videoStreams: [
|
|
1054
|
+
stream2, stream4, stream6, stream8,
|
|
1055
|
+
],
|
|
1056
|
+
audioStreams: [],
|
|
1057
|
+
textStreams: [],
|
|
1058
|
+
imageStreams: [],
|
|
1059
|
+
},
|
|
1060
|
+
];
|
|
1061
|
+
|
|
1062
|
+
await combiner.combinePeriods(periods, /* isDynamic= */ true);
|
|
1063
|
+
const variants = combiner.getVariants();
|
|
1064
|
+
expect(variants.length).toBe(4);
|
|
1065
|
+
// We can use the originalId field to see what each track is composed of.
|
|
1066
|
+
const video1 = variants[0].video;
|
|
1067
|
+
expect(video1.originalId).toBe('1,2');
|
|
1068
|
+
|
|
1069
|
+
const video2 = variants[1].video;
|
|
1070
|
+
expect(video2.originalId).toBe('3,4');
|
|
1071
|
+
|
|
1072
|
+
const video3 = variants[2].video;
|
|
1073
|
+
expect(video3.originalId).toBe('5,6');
|
|
1074
|
+
|
|
1075
|
+
const video4 = variants[3].video;
|
|
1076
|
+
expect(video4.originalId).toBe('7,8');
|
|
1077
|
+
});
|
|
999
1078
|
|
|
1000
1079
|
it('Matches streams with most roles in common', async () => {
|
|
1001
1080
|
const makeAudioStreamWithRoles = (roles) => {
|
|
@@ -619,11 +619,20 @@ describe('StreamUtils', () => {
|
|
|
619
619
|
stream.mimeType = 'image/png';
|
|
620
620
|
});
|
|
621
621
|
manifest.addImageStream(3, (stream) => {
|
|
622
|
-
stream.mimeType = 'image/
|
|
622
|
+
stream.mimeType = 'image/jpg';
|
|
623
623
|
});
|
|
624
624
|
manifest.addImageStream(4, (stream) => {
|
|
625
|
+
stream.mimeType = 'image/jpeg';
|
|
626
|
+
});
|
|
627
|
+
manifest.addImageStream(5, (stream) => {
|
|
625
628
|
stream.mimeType = 'image/bogus';
|
|
626
629
|
});
|
|
630
|
+
manifest.addImageStream(6, (stream) => {
|
|
631
|
+
stream.mimeType = 'image/avif';
|
|
632
|
+
});
|
|
633
|
+
manifest.addImageStream(7, (stream) => {
|
|
634
|
+
stream.mimeType = 'image/webp';
|
|
635
|
+
});
|
|
627
636
|
});
|
|
628
637
|
|
|
629
638
|
const noVariant = null;
|
|
@@ -631,12 +640,19 @@ describe('StreamUtils', () => {
|
|
|
631
640
|
fakeDrmEngine, noVariant, manifest);
|
|
632
641
|
|
|
633
642
|
// Covers a regression in which we would remove streams with codecs.
|
|
634
|
-
// The
|
|
635
|
-
//
|
|
636
|
-
|
|
637
|
-
expect(manifest.imageStreams
|
|
638
|
-
|
|
639
|
-
expect(manifest.imageStreams
|
|
643
|
+
// The first 4 streams should be there because they are always supported.
|
|
644
|
+
// The 5th stream should be removed because the MIME type is bogus.
|
|
645
|
+
// The 6th and 7th streams may be there, based on platform support.
|
|
646
|
+
expect(manifest.imageStreams).toContain(
|
|
647
|
+
jasmine.objectContaining({id: 1}));
|
|
648
|
+
expect(manifest.imageStreams).toContain(
|
|
649
|
+
jasmine.objectContaining({id: 2}));
|
|
650
|
+
expect(manifest.imageStreams).toContain(
|
|
651
|
+
jasmine.objectContaining({id: 3}));
|
|
652
|
+
expect(manifest.imageStreams).toContain(
|
|
653
|
+
jasmine.objectContaining({id: 4}));
|
|
654
|
+
expect(manifest.imageStreams).not.toContain(
|
|
655
|
+
jasmine.objectContaining({id: 5}));
|
|
640
656
|
});
|
|
641
657
|
|
|
642
658
|
it('filters transport streams', async () => {
|
package/ui/controls.js
CHANGED
|
@@ -9,7 +9,6 @@ goog.provide('shaka.ui.Controls');
|
|
|
9
9
|
goog.provide('shaka.ui.ControlsPanel');
|
|
10
10
|
|
|
11
11
|
goog.require('goog.asserts');
|
|
12
|
-
goog.require('shaka.Deprecate');
|
|
13
12
|
goog.require('shaka.ads.AdManager');
|
|
14
13
|
goog.require('shaka.cast.CastProxy');
|
|
15
14
|
goog.require('shaka.log');
|
|
@@ -556,33 +555,74 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
556
555
|
this.hideSettingsMenusTimer_.tickNow();
|
|
557
556
|
}
|
|
558
557
|
|
|
558
|
+
/**
|
|
559
|
+
* @return {boolean}
|
|
560
|
+
* @export
|
|
561
|
+
*/
|
|
562
|
+
isFullScreenSupported() {
|
|
563
|
+
if (document.fullscreenEnabled) {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
|
|
567
|
+
if (video.webkitSupportsFullscreen) {
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* @return {boolean}
|
|
575
|
+
* @export
|
|
576
|
+
*/
|
|
577
|
+
isFullScreenEnabled() {
|
|
578
|
+
if (document.fullscreenEnabled) {
|
|
579
|
+
return !!document.fullscreenElement;
|
|
580
|
+
}
|
|
581
|
+
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
|
|
582
|
+
if (video.webkitSupportsFullscreen) {
|
|
583
|
+
return video.webkitDisplayingFullscreen;
|
|
584
|
+
}
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
|
|
559
588
|
/** @export */
|
|
560
589
|
async toggleFullScreen() {
|
|
561
|
-
if (document.
|
|
562
|
-
if (
|
|
563
|
-
screen.orientation
|
|
564
|
-
|
|
565
|
-
await document.exitFullscreen();
|
|
566
|
-
} else {
|
|
567
|
-
// If we are in PiP mode, leave PiP mode first.
|
|
568
|
-
try {
|
|
569
|
-
if (document.pictureInPictureElement) {
|
|
570
|
-
await document.exitPictureInPicture();
|
|
590
|
+
if (document.fullscreenEnabled) {
|
|
591
|
+
if (document.fullscreenElement) {
|
|
592
|
+
if (screen.orientation) {
|
|
593
|
+
screen.orientation.unlock();
|
|
571
594
|
}
|
|
572
|
-
await
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
await
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
|
|
595
|
+
await document.exitFullscreen();
|
|
596
|
+
} else {
|
|
597
|
+
// If we are in PiP mode, leave PiP mode first.
|
|
598
|
+
try {
|
|
599
|
+
if (document.pictureInPictureElement) {
|
|
600
|
+
await document.exitPictureInPicture();
|
|
601
|
+
}
|
|
602
|
+
await this.videoContainer_.requestFullscreen({navigationUI: 'hide'});
|
|
603
|
+
if (this.config_.forceLandscapeOnFullscreen && screen.orientation) {
|
|
604
|
+
try {
|
|
605
|
+
// Locking to 'landscape' should let it be either
|
|
606
|
+
// 'landscape-primary' or 'landscape-secondary' as appropriate.
|
|
607
|
+
await screen.orientation.lock('landscape');
|
|
608
|
+
} catch (error) {
|
|
609
|
+
// If screen.orientation.lock does not work on a device, it will
|
|
610
|
+
// be rejected with an error. Suppress that error.
|
|
611
|
+
}
|
|
581
612
|
}
|
|
613
|
+
} catch (error) {
|
|
614
|
+
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
615
|
+
'error', (new Map()).set('detail', error)));
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
|
|
620
|
+
if (video.webkitSupportsFullscreen) {
|
|
621
|
+
if (video.webkitDisplayingFullscreen) {
|
|
622
|
+
video.webkitExitFullscreen();
|
|
623
|
+
} else {
|
|
624
|
+
video.webkitEnterFullscreen();
|
|
582
625
|
}
|
|
583
|
-
} catch (error) {
|
|
584
|
-
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
585
|
-
'error', (new Map()).set('detail', error)));
|
|
586
626
|
}
|
|
587
627
|
}
|
|
588
628
|
}
|
|
@@ -702,7 +742,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
702
742
|
});
|
|
703
743
|
|
|
704
744
|
this.eventManager_.listen(this.controlsContainer_, 'dblclick', () => {
|
|
705
|
-
if (this.config_.doubleClickForFullscreen &&
|
|
745
|
+
if (this.config_.doubleClickForFullscreen &&
|
|
746
|
+
this.isFullScreenSupported()) {
|
|
706
747
|
this.toggleFullScreen();
|
|
707
748
|
}
|
|
708
749
|
});
|
|
@@ -826,22 +867,6 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
826
867
|
const factory =
|
|
827
868
|
shaka.ui.ControlsPanel.elementNamesToFactories_.get(name);
|
|
828
869
|
const element = factory.create(this.controlsButtonPanel_, this);
|
|
829
|
-
|
|
830
|
-
if (typeof element.release != 'function') {
|
|
831
|
-
shaka.Deprecate.deprecateFeature(4,
|
|
832
|
-
'shaka.extern.IUIElement',
|
|
833
|
-
'Please update UI elements to have a release() method.');
|
|
834
|
-
|
|
835
|
-
// This cast works around compiler strictness about the IUIElement
|
|
836
|
-
// type being "@struct" (as ES6 classes are by default).
|
|
837
|
-
const moddableElement = /** @type {Object} */(element);
|
|
838
|
-
moddableElement['release'] = () => {
|
|
839
|
-
if (moddableElement['destroy']) {
|
|
840
|
-
moddableElement['destroy']();
|
|
841
|
-
}
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
|
|
845
870
|
this.elements_.push(element);
|
|
846
871
|
} else {
|
|
847
872
|
shaka.log.alwaysWarn('Unrecognized control panel element requested:',
|
package/ui/fullscreen_button.js
CHANGED
|
@@ -28,16 +28,16 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
|
|
28
28
|
constructor(parent, controls) {
|
|
29
29
|
super(parent, controls);
|
|
30
30
|
|
|
31
|
+
/** @private {HTMLMediaElement} */
|
|
32
|
+
this.localVideo_ = this.controls.getLocalVideo();
|
|
33
|
+
|
|
31
34
|
/** @private {!HTMLButtonElement} */
|
|
32
35
|
this.button_ = shaka.util.Dom.createButton();
|
|
33
36
|
this.button_.classList.add('shaka-fullscreen-button');
|
|
34
37
|
this.button_.classList.add('material-icons-round');
|
|
35
38
|
this.button_.classList.add('shaka-tooltip');
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
if (!document.fullscreenEnabled) {
|
|
39
|
-
this.button_.classList.add('shaka-hidden');
|
|
40
|
-
}
|
|
40
|
+
this.checkSupport_();
|
|
41
41
|
|
|
42
42
|
this.button_.textContent = shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN;
|
|
43
43
|
this.parent.appendChild(this.button_);
|
|
@@ -61,6 +61,26 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
|
|
61
61
|
this.updateIcon_();
|
|
62
62
|
this.updateAriaLabel_();
|
|
63
63
|
});
|
|
64
|
+
|
|
65
|
+
this.eventManager.listen(this.localVideo_, 'loadedmetadata', () => {
|
|
66
|
+
this.checkSupport_();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.eventManager.listen(this.localVideo_, 'loadeddata', () => {
|
|
70
|
+
this.checkSupport_();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
checkSupport_() {
|
|
78
|
+
// Don't show the button if fullscreen is not supported
|
|
79
|
+
if (!this.controls.isFullScreenSupported()) {
|
|
80
|
+
this.button_.classList.add('shaka-hidden');
|
|
81
|
+
} else {
|
|
82
|
+
this.button_.classList.remove('shaka-hidden');
|
|
83
|
+
}
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
/**
|
|
@@ -68,7 +88,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
|
|
68
88
|
*/
|
|
69
89
|
updateAriaLabel_() {
|
|
70
90
|
const LocIds = shaka.ui.Locales.Ids;
|
|
71
|
-
const label =
|
|
91
|
+
const label = this.controls.isFullScreenEnabled() ?
|
|
72
92
|
LocIds.EXIT_FULL_SCREEN : LocIds.FULL_SCREEN;
|
|
73
93
|
|
|
74
94
|
this.button_.ariaLabel = this.localization.resolve(label);
|
|
@@ -79,7 +99,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
|
|
|
79
99
|
*/
|
|
80
100
|
updateIcon_() {
|
|
81
101
|
this.button_.textContent =
|
|
82
|
-
|
|
102
|
+
this.controls.isFullScreenEnabled() ?
|
|
83
103
|
shaka.ui.Enums.MaterialDesignIcons.EXIT_FULLSCREEN :
|
|
84
104
|
shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN;
|
|
85
105
|
}
|
package/ui/less/containers.less
CHANGED
|
@@ -337,8 +337,8 @@
|
|
|
337
337
|
|
|
338
338
|
.shaka-spinner {
|
|
339
339
|
/* This uses the same trickery as the big play button define
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
the spinner's width and height. See .shaka-play-button
|
|
341
|
+
for the detailed explanation. */
|
|
342
342
|
|
|
343
343
|
/* For the padding thing to work, spinner div needs to be an
|
|
344
344
|
overlay-parent and spinner svg - an overlay child. */
|
|
@@ -351,6 +351,6 @@
|
|
|
351
351
|
height: 0;
|
|
352
352
|
|
|
353
353
|
/* Add a bit of a white shadow to keep our black spinner visible
|
|
354
|
-
|
|
354
|
+
on a black background. */
|
|
355
355
|
filter: drop-shadow(0 0 2px rgba(255 255 255 / 50%));
|
|
356
356
|
}
|
package/ui/locales/en.json
CHANGED
package/ui/locales/source.json
CHANGED
|
@@ -113,6 +113,11 @@
|
|
|
113
113
|
"message": "Playback speed",
|
|
114
114
|
"description": "Label for a button used to navigate to a submenu to choose the playback speed in the video player."
|
|
115
115
|
},
|
|
116
|
+
"QUALITY": {
|
|
117
|
+
"description": "Label for a button used to open a submenu to choose audio quality in the video player.",
|
|
118
|
+
"meaning": "Audio quality",
|
|
119
|
+
"message": "Quality"
|
|
120
|
+
},
|
|
116
121
|
"REPLAY": {
|
|
117
122
|
"description": "Label for a button used to replay the video in a video player.",
|
|
118
123
|
"message": "Replay"
|
|
@@ -61,9 +61,6 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
this.updateResolutionSelection_();
|
|
64
|
-
|
|
65
|
-
// Set up all the strings in the user's preferred language.
|
|
66
|
-
this.updateLocalizedStrings_();
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
|
|
@@ -72,27 +69,6 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
|
|
72
69
|
/** @type {!Array.<shaka.extern.Track>} */
|
|
73
70
|
let tracks = this.player.getVariantTracks();
|
|
74
71
|
|
|
75
|
-
// Hide resolution menu and button for audio-only content and src= content
|
|
76
|
-
// without resolution information.
|
|
77
|
-
// TODO: for audio-only content, this should be a bitrate selection menu
|
|
78
|
-
// instead.
|
|
79
|
-
if (tracks.length && !tracks[0].height) {
|
|
80
|
-
shaka.ui.Utils.setDisplay(this.menu, false);
|
|
81
|
-
shaka.ui.Utils.setDisplay(this.button, false);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
// Otherwise, restore it.
|
|
85
|
-
shaka.ui.Utils.setDisplay(this.button, true);
|
|
86
|
-
|
|
87
|
-
tracks.sort((t1, t2) => {
|
|
88
|
-
// We have already screened for audio-only content, but the compiler
|
|
89
|
-
// doesn't know that.
|
|
90
|
-
goog.asserts.assert(t1.height != null, 'Null height');
|
|
91
|
-
goog.asserts.assert(t2.height != null, 'Null height');
|
|
92
|
-
|
|
93
|
-
return t2.height - t1.height;
|
|
94
|
-
});
|
|
95
|
-
|
|
96
72
|
// If there is a selected variant track, then we filter out any tracks in
|
|
97
73
|
// a different language. Then we use those remaining tracks to display the
|
|
98
74
|
// available resolutions.
|
|
@@ -104,14 +80,31 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
|
|
104
80
|
track.channelsCount == selectedTrack.channelsCount);
|
|
105
81
|
}
|
|
106
82
|
|
|
107
|
-
// Remove duplicate entries with the same
|
|
108
|
-
//
|
|
83
|
+
// Remove duplicate entries with the same resolution or quality depending
|
|
84
|
+
// on content type. Pick an arbitrary one.
|
|
109
85
|
tracks = tracks.filter((track, idx) => {
|
|
110
|
-
// Keep the first one with the same height.
|
|
111
|
-
const otherIdx =
|
|
86
|
+
// Keep the first one with the same height or bandwidth.
|
|
87
|
+
const otherIdx = this.player.isAudioOnly() ?
|
|
88
|
+
tracks.findIndex((t) => t.bandwidth == track.bandwidth) :
|
|
89
|
+
tracks.findIndex((t) => t.height == track.height);
|
|
112
90
|
return otherIdx == idx;
|
|
113
91
|
});
|
|
114
92
|
|
|
93
|
+
// Sort the tracks by height or bandwidth depending on content type.
|
|
94
|
+
if (this.player.isAudioOnly()) {
|
|
95
|
+
tracks.sort((t1, t2) => {
|
|
96
|
+
goog.asserts.assert(t1.bandwidth != null, 'Null bandwidth');
|
|
97
|
+
goog.asserts.assert(t2.bandwidth != null, 'Null bandwidth');
|
|
98
|
+
return t2.bandwidth - t1.bandwidth;
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
tracks.sort((t1, t2) => {
|
|
102
|
+
goog.asserts.assert(t1.height != null, 'Null height');
|
|
103
|
+
goog.asserts.assert(t2.height != null, 'Null height');
|
|
104
|
+
return t2.height - t1.height;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
115
108
|
// Remove old shaka-resolutions
|
|
116
109
|
// 1. Save the back to menu button
|
|
117
110
|
const backButton = shaka.ui.Utils.getFirstDescendantWithClassName(
|
|
@@ -133,7 +126,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
|
|
133
126
|
() => this.onTrackSelected_(track));
|
|
134
127
|
|
|
135
128
|
const span = shaka.util.Dom.createHTMLElement('span');
|
|
136
|
-
span.textContent =
|
|
129
|
+
span.textContent = this.player.isAudioOnly() ?
|
|
130
|
+
Math.round(track.bandwidth / 1000) + ' kbits/s' : track.height + 'p';
|
|
137
131
|
button.appendChild(span);
|
|
138
132
|
|
|
139
133
|
if (!abrEnabled && track == selectedTrack) {
|
|
@@ -179,6 +173,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
|
|
179
173
|
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
|
|
180
174
|
this.controls.dispatchEvent(
|
|
181
175
|
new shaka.util.FakeEvent('resolutionselectionupdated'));
|
|
176
|
+
|
|
177
|
+
this.updateLocalizedStrings_();
|
|
182
178
|
}
|
|
183
179
|
|
|
184
180
|
|
|
@@ -200,13 +196,15 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
|
|
|
200
196
|
*/
|
|
201
197
|
updateLocalizedStrings_() {
|
|
202
198
|
const LocIds = shaka.ui.Locales.Ids;
|
|
199
|
+
const locId = this.player.isAudioOnly() ?
|
|
200
|
+
LocIds.QUALITY : LocIds.RESOLUTION;
|
|
203
201
|
|
|
204
|
-
this.button.ariaLabel = this.localization.resolve(
|
|
205
|
-
this.backButton.ariaLabel = this.localization.resolve(
|
|
202
|
+
this.button.ariaLabel = this.localization.resolve(locId);
|
|
203
|
+
this.backButton.ariaLabel = this.localization.resolve(locId);
|
|
206
204
|
this.backSpan.textContent =
|
|
207
|
-
this.localization.resolve(
|
|
205
|
+
this.localization.resolve(locId);
|
|
208
206
|
this.nameSpan.textContent =
|
|
209
|
-
this.localization.resolve(
|
|
207
|
+
this.localization.resolve(locId);
|
|
210
208
|
this.abrOnSpan_.textContent =
|
|
211
209
|
this.localization.resolve(LocIds.AUTO_QUALITY);
|
|
212
210
|
|
package/ui/ui.js
CHANGED
|
@@ -448,14 +448,6 @@ shaka.ui.Overlay.TrackLabelFormat = {
|
|
|
448
448
|
'LABEL': 3,
|
|
449
449
|
};
|
|
450
450
|
|
|
451
|
-
/*
|
|
452
|
-
* "shaka.ui.TrackLabelFormat" is deprecated and will be removed in v4.
|
|
453
|
-
*
|
|
454
|
-
* @deprecated
|
|
455
|
-
* @enum {number}
|
|
456
|
-
*/
|
|
457
|
-
shaka.ui.TrackLabelFormat = shaka.ui.Overlay.TrackLabelFormat;
|
|
458
|
-
|
|
459
451
|
/**
|
|
460
452
|
* Describes the possible reasons that the UI might fail to load.
|
|
461
453
|
*
|
|
@@ -468,15 +460,6 @@ shaka.ui.Overlay.FailReasonCode = {
|
|
|
468
460
|
};
|
|
469
461
|
|
|
470
462
|
|
|
471
|
-
/**
|
|
472
|
-
* "shaka.ui.FailReasonCode" is deprecated and will be removed in v4.
|
|
473
|
-
*
|
|
474
|
-
* @deprecated
|
|
475
|
-
* @enum {number}
|
|
476
|
-
*/
|
|
477
|
-
shaka.ui.FailReasonCode = shaka.ui.Overlay.FailReasonCode;
|
|
478
|
-
|
|
479
|
-
|
|
480
463
|
if (document.readyState == 'complete') {
|
|
481
464
|
// Don't fire this event synchronously. In a compiled bundle, the "shaka"
|
|
482
465
|
// namespace might not be exported to the window until after this point.
|