shaka-player 3.2.0 → 3.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +3 -0
- package/CHANGELOG.md +182 -1
- package/CONTRIBUTORS +3 -0
- package/README.md +4 -4
- package/build/checkversion.py +1 -1
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/build/test.py +6 -0
- package/demo/common/assets.js +1 -1
- package/demo/index.html +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +8 -8
- package/dist/locales.js +37 -37
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +195 -5
- package/dist/shaka-player.compiled.debug.d.ts +195 -5
- package/dist/shaka-player.compiled.debug.externs.js +226 -14
- package/dist/shaka-player.compiled.debug.js +424 -399
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +226 -14
- package/dist/shaka-player.compiled.js +826 -812
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +197 -16
- package/dist/shaka-player.ui.debug.d.ts +197 -16
- package/dist/shaka-player.ui.debug.externs.js +230 -27
- package/dist/shaka-player.ui.debug.js +466 -442
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +230 -27
- package/dist/shaka-player.ui.js +946 -932
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +25 -0
- package/externs/shaka/player.js +8 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +20 -15
- package/lib/ads/ad_manager.js +46 -26
- package/lib/ads/client_side_ad_manager.js +82 -88
- package/lib/ads/server_side_ad_manager.js +45 -13
- package/lib/cast/cast_proxy.js +6 -2
- package/lib/cast/cast_receiver.js +5 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +131 -24
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +74 -17
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +2 -1
- package/lib/media/segment_reference.js +19 -1
- package/lib/media/streaming_engine.js +8 -4
- package/lib/net/networking_engine.js +6 -1
- package/lib/offline/download_manager.js +1 -2
- package/lib/offline/storage.js +8 -9
- package/lib/player.js +129 -59
- package/lib/polyfill/aria.js +12 -2
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +73 -21
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +6 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +135 -87
- package/lib/text/ui_text_displayer.js +213 -80
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/text/web_vtt_generator.js +21 -12
- package/lib/util/fake_event.js +53 -7
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +105 -13
- package/lib/util/xml_utils.js +50 -12
- package/package.json +3 -6
- package/roadmap.md +24 -22
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +43 -0
- package/test/dash/dash_parser_manifest_unit.js +222 -3
- package/test/hls/hls_parser_unit.js +79 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_integration.js +86 -2
- package/test/media/drm_engine_unit.js +57 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +145 -0
- package/test/player_integration.js +79 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +14 -4
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +11 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- package/test/text/ui_text_displayer_unit.js +93 -20
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/text/web_vtt_generator_unit.js +58 -3
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +21 -8
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/stream_utils_unit.js +60 -3
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +11 -6
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +6 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +40 -87
- package/ui/pip_button.js +2 -3
- package/ui/seek_bar.js +1 -2
- package/ui/text_selection.js +1 -0
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
goog.require('shaka.test.UiUtils');
|
|
8
7
|
goog.require('shaka.test.Util');
|
|
9
8
|
goog.require('shaka.text.Cue');
|
|
10
9
|
goog.require('shaka.text.UITextDisplayer');
|
|
@@ -51,8 +50,7 @@ describe('UITextDisplayer', () => {
|
|
|
51
50
|
/** @type {!HTMLElement} */ (document.createElement('div'));
|
|
52
51
|
videoContainer.style.height = `${videoContainerHeight}px`;
|
|
53
52
|
document.body.appendChild(videoContainer);
|
|
54
|
-
video = shaka.test.
|
|
55
|
-
videoContainer.appendChild(video);
|
|
53
|
+
video = new shaka.test.FakeVideo();
|
|
56
54
|
});
|
|
57
55
|
|
|
58
56
|
beforeEach(() => {
|
|
@@ -86,20 +84,16 @@ describe('UITextDisplayer', () => {
|
|
|
86
84
|
// Wait until updateCaptions_() gets called.
|
|
87
85
|
await shaka.test.Util.delay(0.5);
|
|
88
86
|
|
|
89
|
-
const textContainer =
|
|
90
|
-
|
|
91
|
-
const captions =
|
|
92
|
-
textContainer.querySelector('span:not(.shaka-text-wrapper)');
|
|
87
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
88
|
+
const captions = textContainer.querySelector('div');
|
|
93
89
|
const cssObj = parseCssText(captions.style.cssText);
|
|
94
90
|
|
|
95
91
|
const expectCssObj = {
|
|
96
92
|
'color': 'green',
|
|
97
|
-
'background-color': 'black',
|
|
98
93
|
'direction': 'ltr',
|
|
99
94
|
'font-size': '10px',
|
|
100
95
|
'font-style': 'normal',
|
|
101
96
|
'font-weight': 400,
|
|
102
|
-
'line-height': 2,
|
|
103
97
|
'text-align': 'center',
|
|
104
98
|
};
|
|
105
99
|
|
|
@@ -116,6 +110,8 @@ describe('UITextDisplayer', () => {
|
|
|
116
110
|
}
|
|
117
111
|
|
|
118
112
|
expect(cssObj).toEqual(jasmine.objectContaining(expectCssObj));
|
|
113
|
+
expect(parseCssText(textContainer.querySelector('span').style.cssText))
|
|
114
|
+
.toEqual(jasmine.objectContaining({'background-color': 'black'}));
|
|
119
115
|
});
|
|
120
116
|
|
|
121
117
|
it('correctly displays styles for nested cues', async () => {
|
|
@@ -139,15 +135,13 @@ describe('UITextDisplayer', () => {
|
|
|
139
135
|
await shaka.test.Util.delay(0.5);
|
|
140
136
|
|
|
141
137
|
// Verify styles applied to the nested cues.
|
|
142
|
-
const textContainer =
|
|
143
|
-
videoContainer.querySelector('.shaka-text-container');
|
|
138
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
144
139
|
const captions =
|
|
145
140
|
textContainer.querySelector('span:not(.shaka-text-wrapper)');
|
|
146
141
|
const cssObj = parseCssText(captions.style.cssText);
|
|
147
142
|
|
|
148
143
|
const expectCssObj = {
|
|
149
144
|
'color': 'green',
|
|
150
|
-
'background-color': 'black',
|
|
151
145
|
'font-size': '10px',
|
|
152
146
|
'font-style': 'normal',
|
|
153
147
|
'font-weight': 400,
|
|
@@ -167,6 +161,8 @@ describe('UITextDisplayer', () => {
|
|
|
167
161
|
}
|
|
168
162
|
|
|
169
163
|
expect(cssObj).toEqual(jasmine.objectContaining(expectCssObj));
|
|
164
|
+
expect(parseCssText(captions.querySelector('span').style.cssText))
|
|
165
|
+
.toEqual(jasmine.objectContaining({'background-color': 'black'}));
|
|
170
166
|
});
|
|
171
167
|
|
|
172
168
|
it('correctly displays styles for cellResolution units', async () => {
|
|
@@ -193,10 +189,8 @@ describe('UITextDisplayer', () => {
|
|
|
193
189
|
// style.
|
|
194
190
|
const expectedLinePadding = '11.25px';
|
|
195
191
|
|
|
196
|
-
const textContainer =
|
|
197
|
-
|
|
198
|
-
const captions =
|
|
199
|
-
textContainer.querySelector('span:not(.shaka-text-wrapper)');
|
|
192
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
193
|
+
const captions = textContainer.querySelector('div');
|
|
200
194
|
const cssObj = parseCssText(captions.style.cssText);
|
|
201
195
|
expect(cssObj).toEqual(
|
|
202
196
|
jasmine.objectContaining({
|
|
@@ -224,10 +218,8 @@ describe('UITextDisplayer', () => {
|
|
|
224
218
|
// videoContainerHeight=450px and tts:fontSize="90%" on the default style.
|
|
225
219
|
const expectedFontSize = '27px';
|
|
226
220
|
|
|
227
|
-
const textContainer =
|
|
228
|
-
|
|
229
|
-
const captions =
|
|
230
|
-
textContainer.querySelector('span:not(.shaka-text-wrapper)');
|
|
221
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
222
|
+
const captions = textContainer.querySelector('div');
|
|
231
223
|
const cssObj = parseCssText(captions.style.cssText);
|
|
232
224
|
expect(cssObj).toEqual(
|
|
233
225
|
jasmine.objectContaining({'font-size': expectedFontSize}));
|
|
@@ -320,4 +312,85 @@ describe('UITextDisplayer', () => {
|
|
|
320
312
|
// duplicates.
|
|
321
313
|
expect(captions.length).toBe(3);
|
|
322
314
|
});
|
|
315
|
+
|
|
316
|
+
it('hides currently displayed cue when removed', async () => {
|
|
317
|
+
const cue = new shaka.text.Cue(0, 50, 'One');
|
|
318
|
+
textDisplayer.setTextVisibility(true);
|
|
319
|
+
textDisplayer.append([cue]);
|
|
320
|
+
video.currentTime = 10;
|
|
321
|
+
await shaka.test.Util.delay(0.5);
|
|
322
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
323
|
+
|
|
324
|
+
let cueElements = textContainer.querySelectorAll('div');
|
|
325
|
+
expect(cueElements.length).toBe(1);
|
|
326
|
+
expect(cueElements[0].textContent).toBe('One');
|
|
327
|
+
|
|
328
|
+
textDisplayer.remove(0, 100);
|
|
329
|
+
|
|
330
|
+
cueElements = textContainer.querySelectorAll('div');
|
|
331
|
+
expect(cueElements.length).toBe(0);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('hides and shows nested cues at appropriate times', async () => {
|
|
335
|
+
const parentCue1 = new shaka.text.Cue(0, 100, '');
|
|
336
|
+
const cue1 = new shaka.text.Cue(0, 50, 'One');
|
|
337
|
+
parentCue1.nestedCues.push(cue1);
|
|
338
|
+
const cue2 = new shaka.text.Cue(25, 75, 'Two');
|
|
339
|
+
parentCue1.nestedCues.push(cue2);
|
|
340
|
+
const cue3 = new shaka.text.Cue(50, 100, 'Three');
|
|
341
|
+
parentCue1.nestedCues.push(cue3);
|
|
342
|
+
|
|
343
|
+
const parentCue2 = new shaka.text.Cue(90, 190, '');
|
|
344
|
+
const cue4 = new shaka.text.Cue(90, 130, 'Four');
|
|
345
|
+
parentCue2.nestedCues.push(cue4);
|
|
346
|
+
|
|
347
|
+
textDisplayer.setTextVisibility(true);
|
|
348
|
+
textDisplayer.append([parentCue1, parentCue2]);
|
|
349
|
+
|
|
350
|
+
video.currentTime = 10;
|
|
351
|
+
await shaka.test.Util.delay(0.5);
|
|
352
|
+
/** @type {Element} */
|
|
353
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
354
|
+
let parentCueElements = textContainer.querySelectorAll('div');
|
|
355
|
+
|
|
356
|
+
expect(parentCueElements.length).toBe(1);
|
|
357
|
+
expect(parentCueElements[0].textContent).toBe('One');
|
|
358
|
+
|
|
359
|
+
video.currentTime = 35;
|
|
360
|
+
await shaka.test.Util.delay(0.5);
|
|
361
|
+
parentCueElements = textContainer.querySelectorAll('div');
|
|
362
|
+
expect(parentCueElements.length).toBe(1);
|
|
363
|
+
expect(parentCueElements[0].textContent).toBe('OneTwo');
|
|
364
|
+
|
|
365
|
+
video.currentTime = 60;
|
|
366
|
+
await shaka.test.Util.delay(0.5);
|
|
367
|
+
parentCueElements = textContainer.querySelectorAll('div');
|
|
368
|
+
expect(parentCueElements.length).toBe(1);
|
|
369
|
+
expect(parentCueElements[0].textContent).toBe('TwoThree');
|
|
370
|
+
|
|
371
|
+
video.currentTime = 85;
|
|
372
|
+
await shaka.test.Util.delay(0.5);
|
|
373
|
+
parentCueElements = textContainer.querySelectorAll('div');
|
|
374
|
+
expect(parentCueElements.length).toBe(1);
|
|
375
|
+
expect(parentCueElements[0].textContent).toBe('Three');
|
|
376
|
+
|
|
377
|
+
video.currentTime = 95;
|
|
378
|
+
await shaka.test.Util.delay(0.5);
|
|
379
|
+
parentCueElements = textContainer.querySelectorAll('div');
|
|
380
|
+
expect(parentCueElements.length).toBe(2);
|
|
381
|
+
expect(parentCueElements[0].textContent).toBe('Three');
|
|
382
|
+
expect(parentCueElements[1].textContent).toBe('Four');
|
|
383
|
+
|
|
384
|
+
video.currentTime = 105;
|
|
385
|
+
await shaka.test.Util.delay(0.5);
|
|
386
|
+
parentCueElements = textContainer.querySelectorAll('div');
|
|
387
|
+
expect(parentCueElements.length).toBe(1);
|
|
388
|
+
expect(parentCueElements[0].textContent).toBe('Four');
|
|
389
|
+
|
|
390
|
+
video.currentTime = 150;
|
|
391
|
+
await shaka.test.Util.delay(0.5);
|
|
392
|
+
parentCueElements = textContainer.querySelectorAll('div');
|
|
393
|
+
expect(parentCueElements.length).toBe(1);
|
|
394
|
+
expect(parentCueElements[0].textContent).toBe('');
|
|
395
|
+
});
|
|
323
396
|
});
|
|
@@ -924,6 +924,18 @@ describe('VttTextParser', () => {
|
|
|
924
924
|
payload: '<c.lime><b>Parse fail 2</c></b>',
|
|
925
925
|
nestedCues: [],
|
|
926
926
|
},
|
|
927
|
+
{
|
|
928
|
+
startTime: 100,
|
|
929
|
+
endTime: 110,
|
|
930
|
+
payload: '<c.lime>forward slash 1/2 in text</c>',
|
|
931
|
+
nestedCues: [],
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
startTime: 110,
|
|
935
|
+
endTime: 120,
|
|
936
|
+
payload: '<c.lime>less or more < > in text</c>',
|
|
937
|
+
nestedCues: [],
|
|
938
|
+
},
|
|
927
939
|
],
|
|
928
940
|
'WEBVTT\n\n' +
|
|
929
941
|
'00:00:20.000 --> 00:00:40.000\n' +
|
|
@@ -939,7 +951,11 @@ describe('VttTextParser', () => {
|
|
|
939
951
|
'00:01:20.000 --> 00:01:30.000\n' +
|
|
940
952
|
'<b><c.lime>Parse fail 1</b></c>\n\n' +
|
|
941
953
|
'00:01:30.000 --> 00:01:40.000\n' +
|
|
942
|
-
'<c.lime><b>Parse fail 2</c></b
|
|
954
|
+
'<c.lime><b>Parse fail 2</c></b>\n\n' +
|
|
955
|
+
'00:01:40.000 --> 00:01:50.000\n' +
|
|
956
|
+
'<c.lime>forward slash 1/2 in text</c>\n\n' +
|
|
957
|
+
'00:01:50.000 --> 00:02:00.000\n' +
|
|
958
|
+
'<c.lime>less or more < > in text</c>',
|
|
943
959
|
{periodStart: 0, segmentStart: 0, segmentEnd: 0});
|
|
944
960
|
});
|
|
945
961
|
|
|
@@ -9,7 +9,7 @@ goog.require('shaka.text.WebVttGenerator');
|
|
|
9
9
|
|
|
10
10
|
describe('WebVttGenerator', () => {
|
|
11
11
|
it('supports no cues', () => {
|
|
12
|
-
verifyHelper([], 'WEBVTT\n\n');
|
|
12
|
+
verifyHelper([], [], 'WEBVTT\n\n');
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
it('convert cues to WebVTT', () => {
|
|
@@ -26,6 +26,8 @@ describe('WebVttGenerator', () => {
|
|
|
26
26
|
const shakaCue5 = new shaka.text.Cue(53, 54, 'Test5');
|
|
27
27
|
shakaCue5.textAlign = shaka.text.Cue.textAlign.END;
|
|
28
28
|
|
|
29
|
+
const adCuePoints = [];
|
|
30
|
+
|
|
29
31
|
verifyHelper(
|
|
30
32
|
[
|
|
31
33
|
shakaCue1,
|
|
@@ -34,6 +36,7 @@ describe('WebVttGenerator', () => {
|
|
|
34
36
|
shakaCue4,
|
|
35
37
|
shakaCue5,
|
|
36
38
|
],
|
|
39
|
+
adCuePoints,
|
|
37
40
|
'WEBVTT\n\n' +
|
|
38
41
|
'00:00:20.000 --> 00:00:40.000 align:left vertical:lr\n' +
|
|
39
42
|
'Test\n\n' +
|
|
@@ -68,19 +71,71 @@ describe('WebVttGenerator', () => {
|
|
|
68
71
|
nestedCue4.textDecoration.push(shaka.text.Cue.textDecoration.UNDERLINE);
|
|
69
72
|
|
|
70
73
|
shakaCue.nestedCues = [nestedCue1, nestedCue2, nestedCue3, nestedCue4];
|
|
74
|
+
|
|
75
|
+
const adCuePoints = [];
|
|
76
|
+
|
|
71
77
|
verifyHelper(
|
|
72
78
|
[shakaCue],
|
|
79
|
+
adCuePoints,
|
|
73
80
|
'WEBVTT\n\n' +
|
|
74
81
|
'00:00:10.000 --> 00:00:20.000 align:middle\n' +
|
|
75
82
|
'<i><u>Test1</u></i><b><i>Test2</i></b>Test3<u>Test4</u>\n\n');
|
|
76
83
|
});
|
|
77
84
|
|
|
85
|
+
it('computes the time with ad cue points', () => {
|
|
86
|
+
const shakaCue1 = new shaka.text.Cue(20, 30, 'Test');
|
|
87
|
+
shakaCue1.textAlign = shaka.text.Cue.textAlign.LEFT;
|
|
88
|
+
shakaCue1.writingMode = shaka.text.Cue.writingMode.VERTICAL_LEFT_TO_RIGHT;
|
|
89
|
+
const shakaCue2 = new shaka.text.Cue(40, 50, 'Test2');
|
|
90
|
+
shakaCue2.textAlign = shaka.text.Cue.textAlign.RIGHT;
|
|
91
|
+
shakaCue2.writingMode = shaka.text.Cue.writingMode.VERTICAL_RIGHT_TO_LEFT;
|
|
92
|
+
const shakaCue3 = new shaka.text.Cue(50, 51, 'Test3');
|
|
93
|
+
shakaCue3.textAlign = shaka.text.Cue.textAlign.CENTER;
|
|
94
|
+
const shakaCue4 = new shaka.text.Cue(52, 53, 'Test4');
|
|
95
|
+
shakaCue4.textAlign = shaka.text.Cue.textAlign.START;
|
|
96
|
+
const shakaCue5 = new shaka.text.Cue(53, 54, 'Test5');
|
|
97
|
+
shakaCue5.textAlign = shaka.text.Cue.textAlign.END;
|
|
98
|
+
|
|
99
|
+
const adCuePoints = [
|
|
100
|
+
{
|
|
101
|
+
start: 0,
|
|
102
|
+
end: 10,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
start: 35,
|
|
106
|
+
end: 45,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
verifyHelper(
|
|
111
|
+
[
|
|
112
|
+
shakaCue1,
|
|
113
|
+
shakaCue2,
|
|
114
|
+
shakaCue3,
|
|
115
|
+
shakaCue4,
|
|
116
|
+
shakaCue5,
|
|
117
|
+
],
|
|
118
|
+
adCuePoints,
|
|
119
|
+
'WEBVTT\n\n' +
|
|
120
|
+
'00:00:30.000 --> 00:00:40.000 align:left vertical:lr\n' +
|
|
121
|
+
'Test\n\n' +
|
|
122
|
+
'00:01:00.000 --> 00:01:10.000 align:right vertical:rl\n' +
|
|
123
|
+
'Test2\n\n' +
|
|
124
|
+
'00:01:10.000 --> 00:01:11.000 align:middle\n' +
|
|
125
|
+
'Test3\n\n' +
|
|
126
|
+
'00:01:12.000 --> 00:01:13.000 align:start\n' +
|
|
127
|
+
'Test4\n\n' +
|
|
128
|
+
'00:01:13.000 --> 00:01:14.000 align:end\n' +
|
|
129
|
+
'Test5\n\n');
|
|
130
|
+
});
|
|
131
|
+
|
|
78
132
|
/**
|
|
79
133
|
* @param {!Array} cues
|
|
134
|
+
* @param {!Array} adCuePoints
|
|
80
135
|
* @param {string} text
|
|
81
136
|
*/
|
|
82
|
-
function verifyHelper(cues, text) {
|
|
83
|
-
const result = shaka.text.WebVttGenerator.convert(cues);
|
|
137
|
+
function verifyHelper(cues, adCuePoints, text) {
|
|
138
|
+
const result = shaka.text.WebVttGenerator.convert(cues, adCuePoints);
|
|
84
139
|
expect(text).toBe(result);
|
|
85
140
|
}
|
|
86
141
|
});
|
|
@@ -198,7 +198,7 @@ describe('Localization', () => {
|
|
|
198
198
|
localization.changeLocale([ELVISH_WOODLAND]);
|
|
199
199
|
|
|
200
200
|
expect(events.length).toBe(1);
|
|
201
|
-
expect(events[0]
|
|
201
|
+
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
|
|
@@ -214,23 +214,23 @@ describe('Localization', () => {
|
|
|
214
214
|
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
215
215
|
|
|
216
216
|
expect(events.length).toBe(1);
|
|
217
|
-
expect(events[0]
|
|
217
|
+
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
218
218
|
|
|
219
219
|
// We should see an event telling us that dwarfish is still missing.
|
|
220
220
|
localization.insert(ELVISH_WOODLAND, new Map());
|
|
221
221
|
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
222
222
|
|
|
223
223
|
expect(events.length).toBe(2);
|
|
224
|
-
expect(events[0]
|
|
225
|
-
expect(events[1]
|
|
224
|
+
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
225
|
+
expect(events[1]['locales']).toEqual([DWARFISH_NORTH]);
|
|
226
226
|
|
|
227
227
|
// There should be no more events now that we added the last language.
|
|
228
228
|
localization.insert(DWARFISH_NORTH, new Map());
|
|
229
229
|
localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
230
230
|
|
|
231
231
|
expect(events.length).toBe(2);
|
|
232
|
-
expect(events[0]
|
|
233
|
-
expect(events[1]
|
|
232
|
+
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
233
|
+
expect(events[1]['locales']).toEqual([DWARFISH_NORTH]);
|
|
234
234
|
});
|
|
235
235
|
});
|
|
236
236
|
|
|
@@ -373,8 +373,8 @@ describe('Localization', () => {
|
|
|
373
373
|
// it was in the fallback (HALFLING) but not our preferred language
|
|
374
374
|
// (EVLISH).
|
|
375
375
|
expect(events.length).toBe(1);
|
|
376
|
-
expect(events[0]
|
|
377
|
-
expect(events[0]
|
|
376
|
+
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
|
|
377
|
+
expect(events[0]['missing']).toEqual(['hello']);
|
|
378
378
|
});
|
|
379
379
|
|
|
380
380
|
it('fires when key/value is missing from preferred but found in base',
|
|
@@ -400,8 +400,8 @@ describe('Localization', () => {
|
|
|
400
400
|
// because it was in the base (ELVISH) but not our preferred language
|
|
401
401
|
// (EVLISH_WOODLAND).
|
|
402
402
|
expect(events.length).toBe(1);
|
|
403
|
-
expect(events[0]
|
|
404
|
-
expect(events[0]
|
|
403
|
+
expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
|
|
404
|
+
expect(events[0]['missing']).toEqual(['Best Wishes']);
|
|
405
405
|
});
|
|
406
406
|
|
|
407
407
|
it('fires when key/value is missing from preferred but found in sibling',
|
|
@@ -427,8 +427,8 @@ describe('Localization', () => {
|
|
|
427
427
|
// understand" because it was in a sibling locale (HALFLING_SHIRE)
|
|
428
428
|
// but not our preferred language (HALFLING_COMMON).
|
|
429
429
|
expect(events.length).toBe(1);
|
|
430
|
-
expect(events[0]
|
|
431
|
-
expect(events[0]
|
|
430
|
+
expect(events[0]['locales']).toEqual([HALFLING_COMMON]);
|
|
431
|
+
expect(events[0]['missing']).toEqual(['Do you understand']);
|
|
432
432
|
});
|
|
433
433
|
|
|
434
434
|
it('fires when key/value is missing from some preferred languages',
|
|
@@ -454,8 +454,9 @@ describe('Localization', () => {
|
|
|
454
454
|
|
|
455
455
|
// There should have no missing localization events.
|
|
456
456
|
expect(events.length).toBe(1);
|
|
457
|
-
expect(events[0]
|
|
458
|
-
|
|
457
|
+
expect(events[0]['locales']).toEqual(
|
|
458
|
+
[ELVISH_WOODLAND, DWARFISH_NORTH]);
|
|
459
|
+
expect(events[0]['missing']).toEqual(['may your forge burn bright']);
|
|
459
460
|
});
|
|
460
461
|
});
|
|
461
462
|
|
|
@@ -34,11 +34,8 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
34
34
|
*/
|
|
35
35
|
let beforeScreenshot;
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
// to have this change tolerance in our tests. So far, all past bugs in our
|
|
40
|
-
// implementation that we have tests for would exceed this threshold by a lot.
|
|
41
|
-
const threshold = 160; // px
|
|
37
|
+
// A minimum similarity score for screenshots, between 0 and 1.
|
|
38
|
+
const minSimilarity = 0.95;
|
|
42
39
|
|
|
43
40
|
const originalCast = window.chrome && window.chrome.cast;
|
|
44
41
|
|
|
@@ -141,7 +138,9 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
141
138
|
cue.region.id = '1';
|
|
142
139
|
// Position the cue *explicitly* at the bottom of the screen.
|
|
143
140
|
cue.region.viewportAnchorX = 0; // %
|
|
144
|
-
cue.region.viewportAnchorY =
|
|
141
|
+
cue.region.viewportAnchorY = 90; // %
|
|
142
|
+
cue.region.width = 100; // %
|
|
143
|
+
cue.region.height = 10; // %
|
|
145
144
|
textDisplayer.append([cue]);
|
|
146
145
|
|
|
147
146
|
await checkScreenshot('ui', 'cue-with-controls');
|
|
@@ -194,6 +193,7 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
194
193
|
|
|
195
194
|
// The video must be played a little now, after the cues were appended,
|
|
196
195
|
// but before the screenshot.
|
|
196
|
+
video.playbackRate = 1;
|
|
197
197
|
video.play();
|
|
198
198
|
await waiter.failOnTimeout(false).timeoutAfter(5)
|
|
199
199
|
.waitForMovement(video);
|
|
@@ -201,10 +201,13 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
201
201
|
|
|
202
202
|
// Seek to a time when cues should be showing.
|
|
203
203
|
video.currentTime = time;
|
|
204
|
+
// Get into a playing state, but without movement.
|
|
205
|
+
video.playbackRate = 0;
|
|
206
|
+
video.play();
|
|
204
207
|
|
|
205
208
|
// Add a short delay to ensure that the system has caught up and that
|
|
206
209
|
// native text displayers have been updated by the browser.
|
|
207
|
-
await Util.
|
|
210
|
+
await Util.delay(0.1);
|
|
208
211
|
};
|
|
209
212
|
});
|
|
210
213
|
|
|
@@ -314,6 +317,16 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
314
317
|
await checkScreenshot(prefix, 'nested-cues-with-linebreak');
|
|
315
318
|
});
|
|
316
319
|
|
|
320
|
+
// Regression test for #3600
|
|
321
|
+
it('cue positioning', async () => {
|
|
322
|
+
const cue = new shaka.text.Cue(0, 1, 'Text');
|
|
323
|
+
cue.line = 10;
|
|
324
|
+
cue.lineInterpretation = shaka.text.Cue.lineInterpretation.PERCENTAGE;
|
|
325
|
+
textDisplayer.append([cue]);
|
|
326
|
+
|
|
327
|
+
await checkScreenshot(prefix, 'cue-position');
|
|
328
|
+
});
|
|
329
|
+
|
|
317
330
|
// Regression test for #2157 and #2584
|
|
318
331
|
it('region positioning', async () => {
|
|
319
332
|
const nestedCue = new shaka.text.Cue(
|
|
@@ -463,6 +476,6 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
463
476
|
return Util.checkScreenshot(
|
|
464
477
|
/* element= */ videoContainer,
|
|
465
478
|
prefix + '-' + baseName,
|
|
466
|
-
|
|
479
|
+
minSimilarity);
|
|
467
480
|
}
|
|
468
481
|
});
|
|
@@ -149,4 +149,32 @@ describe('Mp4BoxParsers', () => {
|
|
|
149
149
|
expect(defaultSampleDuration).toBe(expectedDefaultSampleDuration);
|
|
150
150
|
expect(baseMediaDecodeTime).toBe(expectedBaseMediaDecodeTime);
|
|
151
151
|
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Test on parsing an incomplete TKHD V1 box, since the parser doesn't
|
|
155
|
+
* parse the other fields
|
|
156
|
+
*
|
|
157
|
+
* Explanation on the Uint8Array:
|
|
158
|
+
* [
|
|
159
|
+
* <creation_time, 8 bytes>,
|
|
160
|
+
* <modification_time, 8 bytes>,
|
|
161
|
+
* <track_id, 4 bytes>
|
|
162
|
+
* ]
|
|
163
|
+
*
|
|
164
|
+
* Time is a 32B integer expressed in seconds since Jan 1, 1904, 0000 UTC
|
|
165
|
+
*
|
|
166
|
+
*/
|
|
167
|
+
it('parses TKHD v1 box', () => {
|
|
168
|
+
const tkhdBox = new Uint8Array([
|
|
169
|
+
0x00, 0x00, 0x00, 0x00, 0xDC, 0xBF, 0x0F, 0xD7, // Creation time
|
|
170
|
+
0x00, 0x00, 0x00, 0x00, 0xDC, 0xBF, 0x0F, 0xD7, // Modification time
|
|
171
|
+
0x00, 0x00, 0x00, 0x01, // Track ID
|
|
172
|
+
// Remaining fields are not processed in parseTKHD()
|
|
173
|
+
]);
|
|
174
|
+
const reader = new shaka.util.DataViewReader(
|
|
175
|
+
tkhdBox, shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
|
|
176
|
+
const parsedTkhd = shaka.util.Mp4BoxParsers
|
|
177
|
+
.parseTKHD(reader, /* version= */ 1);
|
|
178
|
+
expect(parsedTkhd.trackId).toBe(1);
|
|
179
|
+
});
|
|
152
180
|
});
|
|
@@ -488,7 +488,23 @@ describe('StreamUtils', () => {
|
|
|
488
488
|
});
|
|
489
489
|
|
|
490
490
|
await StreamUtils.getDecodingInfosForVariants(manifest.variants,
|
|
491
|
-
/* usePersistentLicenses= */false);
|
|
491
|
+
/* usePersistentLicenses= */false, /* srcEquals= */ false);
|
|
492
|
+
expect(manifest.variants.length).toBeTruthy();
|
|
493
|
+
expect(manifest.variants[0].decodingInfos.length).toBe(1);
|
|
494
|
+
expect(manifest.variants[0].decodingInfos[0].supported).toBeTruthy();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('for srcEquals content', async () => {
|
|
498
|
+
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
499
|
+
manifest.addVariant(0, (variant) => {
|
|
500
|
+
variant.addVideo(1, (stream) => {
|
|
501
|
+
stream.mime('video/mp4', 'avc1.4d400d');
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
await StreamUtils.getDecodingInfosForVariants(manifest.variants,
|
|
507
|
+
/* usePersistentLicenses= */false, /* srcEquals= */ true);
|
|
492
508
|
expect(manifest.variants.length).toBeTruthy();
|
|
493
509
|
expect(manifest.variants[0].decodingInfos.length).toBe(1);
|
|
494
510
|
expect(manifest.variants[0].decodingInfos[0].supported).toBeTruthy();
|
|
@@ -517,10 +533,51 @@ describe('StreamUtils', () => {
|
|
|
517
533
|
});
|
|
518
534
|
|
|
519
535
|
await StreamUtils.getDecodingInfosForVariants(manifest.variants,
|
|
520
|
-
/* usePersistentLicenses= */false);
|
|
536
|
+
/* usePersistentLicenses= */false, /* srcEquals= */ false);
|
|
521
537
|
expect(manifest.variants.length).toBe(1);
|
|
522
538
|
expect(manifest.variants[0].decodingInfos.length).toBe(0);
|
|
523
539
|
});
|
|
540
|
+
|
|
541
|
+
it('includes transferFunction in config when hdr', async () => {
|
|
542
|
+
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
|
|
543
|
+
|
|
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
|
+
});
|
|
554
|
+
});
|
|
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
|
+
});
|
|
560
|
+
});
|
|
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
|
+
});
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
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
|
+
}
|
|
580
|
+
});
|
|
524
581
|
});
|
|
525
582
|
|
|
526
583
|
describe('filterManifest', () => {
|
|
@@ -820,7 +877,7 @@ describe('StreamUtils', () => {
|
|
|
820
877
|
});
|
|
821
878
|
|
|
822
879
|
await StreamUtils.getDecodingInfosForVariants(manifest.variants,
|
|
823
|
-
/* usePersistentLicenses= */false);
|
|
880
|
+
/* usePersistentLicenses= */false, /* srcEquals= */ false);
|
|
824
881
|
|
|
825
882
|
shaka.util.StreamUtils.chooseCodecsAndFilterManifest(manifest,
|
|
826
883
|
/* preferredVideoCodecs= */[],
|
package/ui/cast_button.js
CHANGED
|
@@ -101,9 +101,8 @@ shaka.ui.CastButton = class extends shaka.ui.Element {
|
|
|
101
101
|
} catch (error) {
|
|
102
102
|
this.castButton_.disabled = false;
|
|
103
103
|
if (error.code != shaka.util.Error.Code.CAST_CANCELED_BY_USER) {
|
|
104
|
-
this.controls.dispatchEvent(new shaka.util.FakeEvent(
|
|
105
|
-
|
|
106
|
-
}));
|
|
104
|
+
this.controls.dispatchEvent(new shaka.util.FakeEvent(
|
|
105
|
+
'error', (new Map()).set('detail', error)));
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
108
|
}
|
package/ui/controls.js
CHANGED
|
@@ -198,6 +198,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
198
198
|
* @export
|
|
199
199
|
*/
|
|
200
200
|
async destroy() {
|
|
201
|
+
if (document.pictureInPictureElement == this.localVideo_) {
|
|
202
|
+
await document.exitPictureInPicture();
|
|
203
|
+
}
|
|
204
|
+
|
|
201
205
|
if (this.eventManager_) {
|
|
202
206
|
this.eventManager_.release();
|
|
203
207
|
this.eventManager_ = null;
|
|
@@ -249,6 +253,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
249
253
|
|
|
250
254
|
this.localization_ = null;
|
|
251
255
|
this.pressedKeys_.clear();
|
|
256
|
+
|
|
257
|
+
// FakeEventTarget implements IReleasable
|
|
258
|
+
super.release();
|
|
252
259
|
}
|
|
253
260
|
|
|
254
261
|
|
|
@@ -569,9 +576,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
569
576
|
}
|
|
570
577
|
}
|
|
571
578
|
} catch (error) {
|
|
572
|
-
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
573
|
-
|
|
574
|
-
}));
|
|
579
|
+
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
580
|
+
'error', (new Map()).set('detail', error)));
|
|
575
581
|
}
|
|
576
582
|
}
|
|
577
583
|
}
|
|
@@ -1158,9 +1164,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
1158
1164
|
/** @private */
|
|
1159
1165
|
onCastStatusChange_() {
|
|
1160
1166
|
const isCasting = this.castProxy_.isCasting();
|
|
1161
|
-
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
1162
|
-
|
|
1163
|
-
}));
|
|
1167
|
+
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
1168
|
+
'caststatuschanged', (new Map()).set('newStatus', isCasting)));
|
|
1164
1169
|
|
|
1165
1170
|
if (isCasting) {
|
|
1166
1171
|
this.controlsContainer_.setAttribute('casting', 'true');
|
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] {
|