shaka-player 3.1.5 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/CHANGELOG.md +13 -1
- package/build/checkversion.py +1 -1
- package/build/test.py +6 -0
- package/dist/controls.css +1 -1
- package/dist/deps.js +3 -3
- package/dist/locales.js +29 -29
- package/dist/shaka-player.compiled.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.externs.js +5 -0
- package/dist/shaka-player.compiled.debug.js +200 -198
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +5 -0
- package/dist/shaka-player.compiled.js +609 -608
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +4 -12
- package/dist/shaka-player.ui.debug.d.ts +4 -12
- package/dist/shaka-player.ui.debug.externs.js +19 -23
- package/dist/shaka-player.ui.debug.js +77 -76
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +19 -23
- package/dist/shaka-player.ui.js +901 -901
- package/dist/shaka-player.ui.map +2 -2
- package/karma.conf.js +19 -15
- package/lib/ads/ad_manager.js +1 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/dash/dash_parser.js +31 -19
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/streaming_engine.js +1 -1
- package/lib/net/networking_engine.js +4 -0
- package/lib/offline/storage.js +10 -11
- package/lib/player.js +25 -9
- package/lib/util/fake_event_target.js +21 -1
- package/package.json +3 -6
- package/test/dash/dash_parser_live_unit.js +43 -0
- package/test/dash/dash_parser_manifest_unit.js +2 -1
- package/test/hls/hls_parser_unit.js +74 -0
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/test/util/util.js +10 -7
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/ui/controls.js +3 -0
- package/ui/localization.js +12 -36
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "3.1.
|
|
4
|
+
"version": "3.1.6",
|
|
5
5
|
"homepage": "https://github.com/google/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@chiragrupani/karma-chromium-edge-launcher": "^2.1.0",
|
|
15
|
-
"@onslip/karma-safari-launcher": "^1.0.1",
|
|
16
14
|
"@teppeis/clutz": "^1.0.29-4c95e12.v20190929",
|
|
17
15
|
"awesomplete": "^1.1.4",
|
|
18
16
|
"babel-core": "^6.26.3",
|
|
@@ -37,13 +35,11 @@
|
|
|
37
35
|
"jsdoc": "github:joeyparrish/jsdoc#45c271fa",
|
|
38
36
|
"karma": "^6.2.0",
|
|
39
37
|
"karma-babel-preprocessor": "^7.0.0",
|
|
40
|
-
"karma-chrome-launcher": "^2.2.0",
|
|
41
38
|
"karma-coverage": "^1.1.2",
|
|
42
|
-
"karma-edge-launcher": "^0.4.2",
|
|
43
|
-
"karma-firefox-launcher": "^1.1.0",
|
|
44
39
|
"karma-ie-launcher": "^1.0.0",
|
|
45
40
|
"karma-jasmine": "^3.3.1",
|
|
46
41
|
"karma-jasmine-ajax": "^0.1.13",
|
|
42
|
+
"karma-local-wd-launcher": "^1.4.1",
|
|
47
43
|
"karma-opera-launcher": "^1.0.0",
|
|
48
44
|
"karma-sourcemap-loader": "^0.3.7",
|
|
49
45
|
"karma-spec-reporter": "0.0.32",
|
|
@@ -55,6 +51,7 @@
|
|
|
55
51
|
"pwacompat": "^2.0.10",
|
|
56
52
|
"rimraf": "^2.6.3",
|
|
57
53
|
"sprintf-js": "^1.1.2",
|
|
54
|
+
"ssim.js": "^3.5.0",
|
|
58
55
|
"stylelint": "^13.8.0",
|
|
59
56
|
"stylelint-config-standard": "^20.0.0",
|
|
60
57
|
"tippy.js": "^4.3.1",
|
|
@@ -1289,6 +1289,49 @@ describe('DashParser Live', () => {
|
|
|
1289
1289
|
.toHaveBeenCalledWith(
|
|
1290
1290
|
jasmine.objectContaining({id: '2', startTime: 10, endTime: 25}));
|
|
1291
1291
|
});
|
|
1292
|
+
|
|
1293
|
+
it('will not add timeline regions outside the DVR window', async () => {
|
|
1294
|
+
const manifest = [
|
|
1295
|
+
'<MPD type="dynamic" minimumUpdatePeriod="PT' + updateTime + 'S"',
|
|
1296
|
+
' availabilityStartTime="1970-01-01T00:00:00Z"',
|
|
1297
|
+
' suggestedPresentationDelay="PT0S"',
|
|
1298
|
+
' timeShiftBufferDepth="PT30S">',
|
|
1299
|
+
' <Period id="1">',
|
|
1300
|
+
' <EventStream schemeIdUri="http://example.com" timescale="1">',
|
|
1301
|
+
' <Event id="0" presentationTime="0" duration="10"/>',
|
|
1302
|
+
' <Event id="1" presentationTime="10" duration="10"/>',
|
|
1303
|
+
' <Event id="2" presentationTime="20" duration="10"/>',
|
|
1304
|
+
' <Event id="3" presentationTime="30" duration="10"/>',
|
|
1305
|
+
' <Event id="4" presentationTime="40" duration="10"/>',
|
|
1306
|
+
' </EventStream>',
|
|
1307
|
+
' <AdaptationSet mimeType="video/mp4">',
|
|
1308
|
+
' <Representation bandwidth="1">',
|
|
1309
|
+
' <SegmentTemplate startNumber="1" media="s$Number$.mp4"',
|
|
1310
|
+
' duration="2" />',
|
|
1311
|
+
' </Representation>',
|
|
1312
|
+
' </AdaptationSet>',
|
|
1313
|
+
' </Period>',
|
|
1314
|
+
'</MPD>',
|
|
1315
|
+
].join('\n');
|
|
1316
|
+
|
|
1317
|
+
// 45 seconds after the epoch, which is also 45 seconds after the
|
|
1318
|
+
// presentation started. Only the last 30 seconds are available, from
|
|
1319
|
+
// time 15 to 45.
|
|
1320
|
+
Date.now = () => 45 * 1000;
|
|
1321
|
+
|
|
1322
|
+
fakeNetEngine.setResponseText('dummy://foo', manifest);
|
|
1323
|
+
await parser.start('dummy://foo', playerInterface);
|
|
1324
|
+
|
|
1325
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledTimes(4);
|
|
1326
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1327
|
+
jasmine.objectContaining({id: '1'}));
|
|
1328
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1329
|
+
jasmine.objectContaining({id: '2'}));
|
|
1330
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1331
|
+
jasmine.objectContaining({id: '3'}));
|
|
1332
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1333
|
+
jasmine.objectContaining({id: '4'}));
|
|
1334
|
+
});
|
|
1292
1335
|
});
|
|
1293
1336
|
|
|
1294
1337
|
it('honors clockSyncUri for in-progress recordings', async () => {
|
|
@@ -983,7 +983,8 @@ describe('DashParser Manifest', () => {
|
|
|
983
983
|
|
|
984
984
|
it('duplicate Representation ids with live', async () => {
|
|
985
985
|
const source = [
|
|
986
|
-
'<MPD minBufferTime="PT75S" type="dynamic"
|
|
986
|
+
'<MPD minBufferTime="PT75S" type="dynamic"',
|
|
987
|
+
' availabilityStartTime="1970-01-01T00:00:00Z">',
|
|
987
988
|
' <Period id="1" duration="PT30S">',
|
|
988
989
|
' <AdaptationSet mimeType="video/mp4">',
|
|
989
990
|
' <Representation id="1" bandwidth="1">',
|
|
@@ -209,6 +209,80 @@ describe('HlsParser', () => {
|
|
|
209
209
|
expect(actual).toEqual(manifest);
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
+
it('parses manifest attributes with space', async () => {
|
|
213
|
+
const master = [
|
|
214
|
+
'#EXTM3U\n',
|
|
215
|
+
'#EXT-X-MEDIA:TYPE=AUDIO, GROUP-ID="aud1", LANGUAGE="eng", ',
|
|
216
|
+
'CHANNELS="16/JOC", URI="audio"\n',
|
|
217
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES, GROUP-ID="sub1", LANGUAGE="eng", ',
|
|
218
|
+
'URI="text"\n',
|
|
219
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES, GROUP-ID="sub2", LANGUAGE="es", ',
|
|
220
|
+
'URI="text2"\n',
|
|
221
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200, CODECS="avc1,mp4a", ',
|
|
222
|
+
'RESOLUTION=960x540, FRAME-RATE=60, AUDIO="aud1"\n',
|
|
223
|
+
'video\n',
|
|
224
|
+
].join('');
|
|
225
|
+
|
|
226
|
+
const media = [
|
|
227
|
+
'#EXTM3U\n',
|
|
228
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
229
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
230
|
+
'#EXTINF:5,\n',
|
|
231
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
232
|
+
'main.mp4',
|
|
233
|
+
].join('');
|
|
234
|
+
|
|
235
|
+
const textMedia = [
|
|
236
|
+
'#EXTM3U\n',
|
|
237
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
238
|
+
'#EXTINF:5,\n',
|
|
239
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
240
|
+
'main.vtt',
|
|
241
|
+
].join('');
|
|
242
|
+
|
|
243
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
244
|
+
manifest.anyTimeline();
|
|
245
|
+
manifest.addPartialVariant((variant) => {
|
|
246
|
+
variant.language = 'en';
|
|
247
|
+
variant.bandwidth = 200;
|
|
248
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
249
|
+
stream.frameRate = 60;
|
|
250
|
+
stream.mime('video/mp4', 'avc1');
|
|
251
|
+
stream.size(960, 540);
|
|
252
|
+
});
|
|
253
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
254
|
+
stream.language = 'en';
|
|
255
|
+
stream.channelsCount = 16;
|
|
256
|
+
stream.spatialAudio = true;
|
|
257
|
+
stream.mime('audio/mp4', 'mp4a');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
manifest.addPartialTextStream((stream) => {
|
|
261
|
+
stream.language = 'en';
|
|
262
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
263
|
+
stream.mime('text/vtt', '');
|
|
264
|
+
});
|
|
265
|
+
manifest.addPartialTextStream((stream) => {
|
|
266
|
+
stream.language = 'es';
|
|
267
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
268
|
+
stream.mime('text/vtt', '');
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
fakeNetEngine
|
|
273
|
+
.setResponseText('test:/master', master)
|
|
274
|
+
.setResponseText('test:/audio', media)
|
|
275
|
+
.setResponseText('test:/video', media)
|
|
276
|
+
.setResponseText('test:/text', textMedia)
|
|
277
|
+
.setResponseText('test:/text2', textMedia)
|
|
278
|
+
.setResponseText('test:/main.vtt', vttText)
|
|
279
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
280
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
281
|
+
|
|
282
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
283
|
+
expect(actual).toEqual(manifest);
|
|
284
|
+
});
|
|
285
|
+
|
|
212
286
|
it('prioritize AVERAGE-BANDWIDTH to BANDWIDTH', async () => {
|
|
213
287
|
const master = [
|
|
214
288
|
'#EXTM3U\n',
|
|
@@ -15,6 +15,9 @@ describe('RegionObserver', () => {
|
|
|
15
15
|
/** @type {!shaka.media.RegionObserver} */
|
|
16
16
|
let observer;
|
|
17
17
|
|
|
18
|
+
/** @type {!jasmine.Spy} */
|
|
19
|
+
let onSeekRange;
|
|
20
|
+
|
|
18
21
|
/** @type {!jasmine.Spy} */
|
|
19
22
|
let onEnterRegion;
|
|
20
23
|
/** @type {!jasmine.Spy} */
|
|
@@ -23,18 +26,26 @@ describe('RegionObserver', () => {
|
|
|
23
26
|
let onSkipRegion;
|
|
24
27
|
|
|
25
28
|
beforeEach(() => {
|
|
29
|
+
onSeekRange = jasmine.createSpy('onSeekRange');
|
|
30
|
+
onSeekRange.and.returnValue({start: 0, end: 100});
|
|
31
|
+
|
|
26
32
|
onEnterRegion = jasmine.createSpy('onEnterRegion');
|
|
27
33
|
onExitRegion = jasmine.createSpy('onExitRegion');
|
|
28
34
|
onSkipRegion = jasmine.createSpy('onSkipRegion');
|
|
29
35
|
|
|
30
36
|
timeline = new shaka.media.RegionTimeline(
|
|
31
|
-
()
|
|
37
|
+
shaka.test.Util.spyFunc(onSeekRange));
|
|
32
38
|
|
|
33
39
|
observer = new shaka.media.RegionObserver(timeline);
|
|
34
|
-
observer.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
observer.addEventListener('enter', (event) => {
|
|
41
|
+
shaka.test.Util.spyFunc(onEnterRegion)(event['region'], event['seeking']);
|
|
42
|
+
});
|
|
43
|
+
observer.addEventListener('exit', (event) => {
|
|
44
|
+
shaka.test.Util.spyFunc(onExitRegion)(event['region'], event['seeking']);
|
|
45
|
+
});
|
|
46
|
+
observer.addEventListener('skip', (event) => {
|
|
47
|
+
shaka.test.Util.spyFunc(onSkipRegion)(event['region'], event['seeking']);
|
|
48
|
+
});
|
|
38
49
|
});
|
|
39
50
|
|
|
40
51
|
it('fires enter event when adding a region the playhead is in', () => {
|
|
@@ -313,6 +324,30 @@ describe('RegionObserver', () => {
|
|
|
313
324
|
expect(onSkipRegion).not.toHaveBeenCalled();
|
|
314
325
|
});
|
|
315
326
|
|
|
327
|
+
// See https://github.com/google/shaka-player/issues/3949
|
|
328
|
+
it('cleans up references to regions', async () => {
|
|
329
|
+
const region = createRegion('my-region', 0, 10);
|
|
330
|
+
timeline.addRegion(region);
|
|
331
|
+
|
|
332
|
+
// Poll so that RegionObserver will store regions.
|
|
333
|
+
poll(observer,
|
|
334
|
+
/* timeInSeconds= */ 5,
|
|
335
|
+
/* seeking= */ false);
|
|
336
|
+
|
|
337
|
+
// Hack to get a private member without the compiler complaining:
|
|
338
|
+
/** @type {Map} */
|
|
339
|
+
const regionMap = (/** @type {?} */(observer))['oldPosition_'];
|
|
340
|
+
expect(regionMap.size).not.toBe(0);
|
|
341
|
+
|
|
342
|
+
// Move the seek range so that the region will be removed.
|
|
343
|
+
onSeekRange.and.returnValue({start: 20, end: 100});
|
|
344
|
+
// Give the timeline time to filter regions
|
|
345
|
+
await shaka.test.Util.delay(
|
|
346
|
+
shaka.media.RegionTimeline.REGION_FILTER_INTERVAL * 2);
|
|
347
|
+
|
|
348
|
+
expect(regionMap.size).toBe(0);
|
|
349
|
+
});
|
|
350
|
+
|
|
316
351
|
/**
|
|
317
352
|
* @param {string} id
|
|
318
353
|
* @param {number} startTimeSeconds
|
|
@@ -14,18 +14,27 @@ describe('RegionTimeline', () => {
|
|
|
14
14
|
/** @type {!jasmine.Spy} */
|
|
15
15
|
let onNewRegion;
|
|
16
16
|
|
|
17
|
+
/** @type {!jasmine.Spy} */
|
|
18
|
+
let onRemoveRegion;
|
|
19
|
+
|
|
17
20
|
/** @type {!jasmine.Spy} */
|
|
18
21
|
let onSeekRange;
|
|
19
22
|
|
|
20
23
|
beforeEach(() => {
|
|
21
|
-
onNewRegion = jasmine.createSpy('onNewRegion');
|
|
22
|
-
|
|
23
24
|
onSeekRange = jasmine.createSpy('onSeekRange');
|
|
24
25
|
onSeekRange.and.returnValue({start: 0, end: 100});
|
|
25
|
-
|
|
26
26
|
timeline = new shaka.media.RegionTimeline(
|
|
27
27
|
shaka.test.Util.spyFunc(onSeekRange));
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
onNewRegion = jasmine.createSpy('onNewRegion');
|
|
30
|
+
timeline.addEventListener('regionadd', (event) => {
|
|
31
|
+
shaka.test.Util.spyFunc(onNewRegion)(event['region']);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
onRemoveRegion = jasmine.createSpy('onRemoveRegion');
|
|
35
|
+
timeline.addEventListener('regionremove', (event) => {
|
|
36
|
+
shaka.test.Util.spyFunc(onRemoveRegion)(event['region']);
|
|
37
|
+
});
|
|
29
38
|
});
|
|
30
39
|
|
|
31
40
|
afterEach(() => {
|
|
@@ -85,24 +94,33 @@ describe('RegionTimeline', () => {
|
|
|
85
94
|
]);
|
|
86
95
|
});
|
|
87
96
|
|
|
88
|
-
it('filters
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
it('filters regions that end before the seek range', async () => {
|
|
98
|
+
onSeekRange.and.returnValue({start: 5, end: 100});
|
|
99
|
+
|
|
100
|
+
const region1 = createRegion('urn:foo', 'my-region', 0, 3);
|
|
101
|
+
const region2 = createRegion('urn:foo', 'my-region', 3, 10);
|
|
102
|
+
const region3 = createRegion('urn:foo', 'my-region', 5, 10);
|
|
103
|
+
|
|
104
|
+
timeline.addRegion(region1);
|
|
105
|
+
timeline.addRegion(region2);
|
|
106
|
+
timeline.addRegion(region3);
|
|
107
|
+
expect(onNewRegion).toHaveBeenCalledTimes(3);
|
|
108
|
+
expect(onNewRegion).toHaveBeenCalledWith(region1);
|
|
109
|
+
expect(onNewRegion).toHaveBeenCalledWith(region2);
|
|
110
|
+
expect(onNewRegion).toHaveBeenCalledWith(region3);
|
|
111
|
+
|
|
112
|
+
let regions = Array.from(timeline.regions());
|
|
113
|
+
expect(regions.length).toBe(3);
|
|
114
|
+
|
|
115
|
+
// Give the timeline time to filter regions
|
|
116
|
+
await shaka.test.Util.delay(
|
|
117
|
+
shaka.media.RegionTimeline.REGION_FILTER_INTERVAL * 2);
|
|
118
|
+
|
|
119
|
+
regions = Array.from(timeline.regions());
|
|
120
|
+
expect(regions).toEqual([region2, region3]);
|
|
121
|
+
|
|
122
|
+
expect(onRemoveRegion).toHaveBeenCalledWith(region1);
|
|
123
|
+
});
|
|
106
124
|
|
|
107
125
|
/**
|
|
108
126
|
* @param {string} schemeIdUri
|
package/test/test/util/util.js
CHANGED
|
@@ -401,10 +401,10 @@ shaka.test.Util = class {
|
|
|
401
401
|
* within the bounds of the viewport.
|
|
402
402
|
* @param {string} name An identifier for the screenshot. Use alphanumeric
|
|
403
403
|
* plus dash and underscore only.
|
|
404
|
-
* @param {number}
|
|
404
|
+
* @param {number} minSimilarity A minimum similarity score between 0 and 1.
|
|
405
405
|
* @return {!Promise}
|
|
406
406
|
*/
|
|
407
|
-
static async checkScreenshot(element, name,
|
|
407
|
+
static async checkScreenshot(element, name, minSimilarity=1) {
|
|
408
408
|
// Make sure the DOM is up-to-date and layout has settled before continuing.
|
|
409
409
|
// Without this delay, or with a shorter delay, we sometimes get missing
|
|
410
410
|
// elements in our UITextDisplayer tests on some platforms.
|
|
@@ -448,13 +448,16 @@ shaka.test.Util = class {
|
|
|
448
448
|
const buffer = await shaka.test.Util.fetch(
|
|
449
449
|
'/screenshot/diff' + parentUrlParams + paramsString);
|
|
450
450
|
const json = shaka.util.StringUtils.fromUTF8(buffer);
|
|
451
|
-
const
|
|
451
|
+
const similarity = /** @type {number} */(JSON.parse(json));
|
|
452
452
|
|
|
453
|
-
// If the
|
|
453
|
+
// If the minimum similarity is not met, you can review the new screenshot
|
|
454
454
|
// and the diff image in the screenshots folder. Look for images that end
|
|
455
|
-
// with "-new" and "-diff".
|
|
456
|
-
//
|
|
457
|
-
|
|
455
|
+
// with "-new" and "-diff". (NOTE: The diff is a pixel-wise diff for human
|
|
456
|
+
// review, and is not produced with the same structural similarity
|
|
457
|
+
// algorithm used to detect changes in the test.) If cropping doesn't work
|
|
458
|
+
// right, you can view the full-page screenshot in the image that ends with
|
|
459
|
+
// "-full".
|
|
460
|
+
expect(similarity).withContext(name).not.toBeLessThan(minSimilarity);
|
|
458
461
|
}
|
|
459
462
|
};
|
|
460
463
|
|
|
@@ -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
|
|
|
@@ -196,6 +193,7 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
196
193
|
|
|
197
194
|
// The video must be played a little now, after the cues were appended,
|
|
198
195
|
// but before the screenshot.
|
|
196
|
+
video.playbackRate = 1;
|
|
199
197
|
video.play();
|
|
200
198
|
await waiter.failOnTimeout(false).timeoutAfter(5)
|
|
201
199
|
.waitForMovement(video);
|
|
@@ -203,10 +201,13 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
203
201
|
|
|
204
202
|
// Seek to a time when cues should be showing.
|
|
205
203
|
video.currentTime = time;
|
|
204
|
+
// Get into a playing state, but without movement.
|
|
205
|
+
video.playbackRate = 0;
|
|
206
|
+
video.play();
|
|
206
207
|
|
|
207
208
|
// Add a short delay to ensure that the system has caught up and that
|
|
208
209
|
// native text displayers have been updated by the browser.
|
|
209
|
-
await Util.
|
|
210
|
+
await Util.delay(0.1);
|
|
210
211
|
};
|
|
211
212
|
});
|
|
212
213
|
|
|
@@ -475,6 +476,6 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
475
476
|
return Util.checkScreenshot(
|
|
476
477
|
/* element= */ videoContainer,
|
|
477
478
|
prefix + '-' + baseName,
|
|
478
|
-
|
|
479
|
+
minSimilarity);
|
|
479
480
|
}
|
|
480
481
|
});
|
package/ui/controls.js
CHANGED
package/ui/localization.js
CHANGED
|
@@ -23,17 +23,18 @@ goog.require('shaka.util.LanguageUtils');
|
|
|
23
23
|
* If a string is not available, it will return the localized
|
|
24
24
|
* form in the closest related locale.
|
|
25
25
|
*
|
|
26
|
-
* @implements {EventTarget}
|
|
27
26
|
* @final
|
|
28
27
|
* @export
|
|
29
28
|
*/
|
|
30
|
-
shaka.ui.Localization = class {
|
|
29
|
+
shaka.ui.Localization = class extends shaka.util.FakeEventTarget {
|
|
31
30
|
/**
|
|
32
31
|
* @param {string} fallbackLocale
|
|
33
32
|
* The fallback locale that should be used. It will be assumed that this
|
|
34
33
|
* locale should have entries for just about every request.
|
|
35
34
|
*/
|
|
36
35
|
constructor(fallbackLocale) {
|
|
36
|
+
super();
|
|
37
|
+
|
|
37
38
|
/** @private {string} */
|
|
38
39
|
this.fallbackLocale_ = shaka.util.LanguageUtils.normalize(fallbackLocale);
|
|
39
40
|
|
|
@@ -62,41 +63,16 @@ shaka.ui.Localization = class {
|
|
|
62
63
|
* @private {!Map.<string, !Map.<string, string>>}
|
|
63
64
|
*/
|
|
64
65
|
this.localizations_ = new Map();
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The event target that we will wrap so that we can fire events
|
|
68
|
-
* without having to manage the listeners directly.
|
|
69
|
-
*
|
|
70
|
-
* @private {!EventTarget}
|
|
71
|
-
*/
|
|
72
|
-
this.events_ = new shaka.util.FakeEventTarget();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @override
|
|
77
|
-
* @export
|
|
78
|
-
*/
|
|
79
|
-
addEventListener(type, listener, options) {
|
|
80
|
-
this.events_.addEventListener(type, listener, options);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @override
|
|
85
|
-
* @export
|
|
86
|
-
*/
|
|
87
|
-
removeEventListener(type, listener, options) {
|
|
88
|
-
// Apparently Closure says we can be passed a null |option|, but we can't
|
|
89
|
-
// pass a null option, so if we get have a null-like |option|, force it to
|
|
90
|
-
// be undefined.
|
|
91
|
-
this.events_.removeEventListener(type, listener, options || undefined);
|
|
92
66
|
}
|
|
93
67
|
|
|
94
68
|
/**
|
|
95
69
|
* @override
|
|
96
70
|
* @export
|
|
97
71
|
*/
|
|
98
|
-
|
|
99
|
-
|
|
72
|
+
release() {
|
|
73
|
+
// Placeholder so that readers know this implements IReleasable (via
|
|
74
|
+
// FakeEventTarget)
|
|
75
|
+
super.release();
|
|
100
76
|
}
|
|
101
77
|
|
|
102
78
|
/**
|
|
@@ -130,7 +106,7 @@ shaka.ui.Localization = class {
|
|
|
130
106
|
(locale) => !this.localizations_.has(locale));
|
|
131
107
|
|
|
132
108
|
if (missing.length) {
|
|
133
|
-
this.
|
|
109
|
+
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
134
110
|
Class.UNKNOWN_LOCALES,
|
|
135
111
|
(new Map()).set('locales', missing)));
|
|
136
112
|
}
|
|
@@ -141,7 +117,7 @@ shaka.ui.Localization = class {
|
|
|
141
117
|
|
|
142
118
|
const data = (new Map()).set(
|
|
143
119
|
'locales', found.length ? found : [this.fallbackLocale_]);
|
|
144
|
-
this.
|
|
120
|
+
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
145
121
|
Class.LOCALE_CHANGED,
|
|
146
122
|
data));
|
|
147
123
|
}
|
|
@@ -193,7 +169,7 @@ shaka.ui.Localization = class {
|
|
|
193
169
|
// data from.
|
|
194
170
|
this.updateCurrentMap_();
|
|
195
171
|
|
|
196
|
-
this.
|
|
172
|
+
this.dispatchEvent(new FakeEvent(Class.LOCALE_UPDATED));
|
|
197
173
|
|
|
198
174
|
return this;
|
|
199
175
|
}
|
|
@@ -249,7 +225,7 @@ shaka.ui.Localization = class {
|
|
|
249
225
|
// Make a copy to avoid leaking references.
|
|
250
226
|
.set('locales', Array.from(this.currentLocales_))
|
|
251
227
|
.set('missing', id);
|
|
252
|
-
this.
|
|
228
|
+
this.dispatchEvent(new FakeEvent(Class.UNKNOWN_LOCALIZATION, data));
|
|
253
229
|
|
|
254
230
|
return '';
|
|
255
231
|
}
|
|
@@ -377,7 +353,7 @@ shaka.ui.Localization = class {
|
|
|
377
353
|
// an array.
|
|
378
354
|
.set('missing', Array.from(missing));
|
|
379
355
|
|
|
380
|
-
this.
|
|
356
|
+
this.dispatchEvent(new shaka.util.FakeEvent(
|
|
381
357
|
shaka.ui.Localization.MISSING_LOCALIZATIONS,
|
|
382
358
|
data));
|
|
383
359
|
}
|