shaka-player 4.2.5 → 4.2.7
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 +13 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +31 -0
- package/CONTRIBUTORS +1 -0
- package/dist/deps.js +1 -1
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.d.ts +10 -0
- package/dist/shaka-player.compiled.debug.externs.js +12 -0
- package/dist/shaka-player.compiled.debug.js +252 -246
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +12 -0
- package/dist/shaka-player.compiled.js +713 -708
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +10 -0
- package/dist/shaka-player.ui.debug.d.ts +10 -0
- package/dist/shaka-player.ui.debug.externs.js +12 -0
- package/dist/shaka-player.ui.debug.js +266 -260
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +12 -0
- package/dist/shaka-player.ui.js +844 -840
- package/dist/shaka-player.ui.map +3 -3
- package/lib/ads/ad_manager.js +2 -2
- package/lib/hls/hls_parser.js +63 -18
- package/lib/media/media_source_engine.js +49 -3
- package/lib/media/segment_index.js +5 -10
- package/lib/media/segment_reference.js +42 -0
- package/lib/media/streaming_engine.js +47 -12
- package/lib/player.js +12 -2
- package/lib/text/ui_text_displayer.js +24 -4
- package/lib/text/web_vtt_generator.js +21 -9
- package/lib/util/dom_utils.js +3 -1
- package/lib/util/stream_utils.js +6 -0
- package/lib/util/timer.js +17 -0
- package/package.json +1 -1
- package/test/hls/hls_live_unit.js +8 -30
- package/test/hls/hls_parser_unit.js +30 -0
- package/test/media/segment_index_unit.js +28 -0
- package/test/player_integration.js +37 -3
- package/test/player_unit.js +4 -2
- package/test/test/boot.js +29 -11
- package/test/test/util/manifest_parser_util.js +4 -3
- package/test/test/util/test_scheme.js +2 -2
- package/test/text/ui_text_displayer_unit.js +70 -0
- package/ui/controls.js +5 -1
package/lib/util/timer.js
CHANGED
|
@@ -78,6 +78,11 @@ shaka.util.Timer = class {
|
|
|
78
78
|
tickEvery(seconds) {
|
|
79
79
|
this.stop();
|
|
80
80
|
|
|
81
|
+
if (goog.DEBUG) {
|
|
82
|
+
// Capture the stack trace by making a fake error.
|
|
83
|
+
const stackTrace = Error('Timer created').stack;
|
|
84
|
+
shaka.util.Timer.activeTimers.set(this, stackTrace);
|
|
85
|
+
}
|
|
81
86
|
this.ticker_ = new shaka.util.DelayedTick(() => {
|
|
82
87
|
// Schedule the timer again first. |onTick_| could cancel the timer and
|
|
83
88
|
// rescheduling first simplifies the implementation.
|
|
@@ -99,5 +104,17 @@ shaka.util.Timer = class {
|
|
|
99
104
|
this.ticker_.stop();
|
|
100
105
|
this.ticker_ = null;
|
|
101
106
|
}
|
|
107
|
+
if (goog.DEBUG) {
|
|
108
|
+
shaka.util.Timer.activeTimers.delete(this);
|
|
109
|
+
}
|
|
102
110
|
}
|
|
103
111
|
};
|
|
112
|
+
|
|
113
|
+
if (goog.DEBUG) {
|
|
114
|
+
/**
|
|
115
|
+
* Tracks all active timer instances, along with the stack trace that created
|
|
116
|
+
* that timer.
|
|
117
|
+
* @type {!Map.<!shaka.util.Timer, string>}
|
|
118
|
+
*/
|
|
119
|
+
shaka.util.Timer.activeTimers = new Map();
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -520,20 +520,24 @@ describe('HlsParser live', () => {
|
|
|
520
520
|
});
|
|
521
521
|
});
|
|
522
522
|
|
|
523
|
-
it('sets
|
|
523
|
+
it('sets discontinuity sequence numbers', async () => {
|
|
524
524
|
const ref1 = makeReference(
|
|
525
525
|
'test:/main.mp4', 0, 2, /* syncTime= */ null,
|
|
526
526
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
527
527
|
/* timestampOffset= */ 0);
|
|
528
|
+
ref1.discontinuitySequence = 30;
|
|
528
529
|
|
|
529
|
-
// Expect the timestamp offset to be set for the segment after the
|
|
530
|
-
// EXT-X-DISCONTINUITY tag.
|
|
531
530
|
const ref2 = makeReference(
|
|
532
531
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null,
|
|
533
532
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
534
533
|
/* timestampOffset= */ 0);
|
|
534
|
+
ref2.discontinuitySequence = 31;
|
|
535
535
|
|
|
536
|
-
await testInitialManifest(
|
|
536
|
+
const manifest = await testInitialManifest(
|
|
537
|
+
master, mediaWithDiscontinuity, [ref1, ref2]);
|
|
538
|
+
|
|
539
|
+
await testUpdate(
|
|
540
|
+
manifest, mediaWithUpdatedDiscontinuitySegment, [ref2]);
|
|
537
541
|
});
|
|
538
542
|
|
|
539
543
|
// Test for https://github.com/shaka-project/shaka-player/issues/4223
|
|
@@ -725,32 +729,6 @@ describe('HlsParser live', () => {
|
|
|
725
729
|
shaka.net.NetworkingEngine.RequestType.MANIFEST);
|
|
726
730
|
});
|
|
727
731
|
|
|
728
|
-
it('reuses cached timestamp offset for segments with discontinuity',
|
|
729
|
-
async () => {
|
|
730
|
-
const ref1 = makeReference(
|
|
731
|
-
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
732
|
-
const ref2 = makeReference(
|
|
733
|
-
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
734
|
-
|
|
735
|
-
const manifest = await testInitialManifest(
|
|
736
|
-
master, mediaWithDiscontinuity, [ref1, ref2]);
|
|
737
|
-
|
|
738
|
-
fakeNetEngine.request.calls.reset();
|
|
739
|
-
await testUpdate(
|
|
740
|
-
manifest, mediaWithUpdatedDiscontinuitySegment, [ref2]);
|
|
741
|
-
|
|
742
|
-
// Only one request should be made, and it's for the playlist.
|
|
743
|
-
// Expect to use the cached timestamp offset for the main2.mp4
|
|
744
|
-
// segment, without fetching the start time again.
|
|
745
|
-
expect(fakeNetEngine.request).toHaveBeenCalledTimes(1);
|
|
746
|
-
fakeNetEngine.expectRequest(
|
|
747
|
-
'test:/video',
|
|
748
|
-
shaka.net.NetworkingEngine.RequestType.MANIFEST);
|
|
749
|
-
fakeNetEngine.expectNoRequest(
|
|
750
|
-
'test:/main.mp4',
|
|
751
|
-
shaka.net.NetworkingEngine.RequestType.SEGMENT);
|
|
752
|
-
});
|
|
753
|
-
|
|
754
732
|
it('request playlist delta updates to skip segments', async () => {
|
|
755
733
|
const mediaWithDeltaUpdates = [
|
|
756
734
|
'#EXTM3U\n',
|
|
@@ -2524,6 +2524,9 @@ describe('HlsParser', () => {
|
|
|
2524
2524
|
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2525
2525
|
'#EXTINF:5,\n',
|
|
2526
2526
|
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2527
|
+
'main.mp4\n',
|
|
2528
|
+
'#EXTINF:5,\n',
|
|
2529
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2527
2530
|
'main.mp4',
|
|
2528
2531
|
].join('');
|
|
2529
2532
|
|
|
@@ -2535,6 +2538,10 @@ describe('HlsParser', () => {
|
|
|
2535
2538
|
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2536
2539
|
'#EXTINF:5,\n',
|
|
2537
2540
|
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2541
|
+
'main.mp4\n',
|
|
2542
|
+
'#EXTINF:5,\n',
|
|
2543
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
2544
|
+
'#EXT-X-KEY:METHOD=NONE\n',
|
|
2538
2545
|
'main.mp4',
|
|
2539
2546
|
].join('');
|
|
2540
2547
|
|
|
@@ -2544,6 +2551,9 @@ describe('HlsParser', () => {
|
|
|
2544
2551
|
'#EXT-X-KEY:METHOD=AES-128,',
|
|
2545
2552
|
'URI="800k.key"\n',
|
|
2546
2553
|
'#EXTINF:5,\n',
|
|
2554
|
+
'main.ts\n',
|
|
2555
|
+
'#EXTINF:5,\n',
|
|
2556
|
+
'#EXT-X-KEY:METHOD=NONE\n',
|
|
2547
2557
|
'main.ts',
|
|
2548
2558
|
].join('');
|
|
2549
2559
|
|
|
@@ -2596,6 +2606,26 @@ describe('HlsParser', () => {
|
|
|
2596
2606
|
|
|
2597
2607
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2598
2608
|
expect(actual).toEqual(manifest);
|
|
2609
|
+
|
|
2610
|
+
const mp4AesEncryptionVideo = actual.variants[1].video;
|
|
2611
|
+
await mp4AesEncryptionVideo.createSegmentIndex();
|
|
2612
|
+
goog.asserts.assert(mp4AesEncryptionVideo.segmentIndex != null,
|
|
2613
|
+
'Null segmentIndex!');
|
|
2614
|
+
|
|
2615
|
+
const firstMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(0);
|
|
2616
|
+
expect(firstMp4Segment.hlsAes128Key).toBeDefined();
|
|
2617
|
+
const secondMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(1);
|
|
2618
|
+
expect(secondMp4Segment.hlsAes128Key).toBeNull();
|
|
2619
|
+
|
|
2620
|
+
const tsAesEncryptionVideo = actual.variants[2].video;
|
|
2621
|
+
await tsAesEncryptionVideo.createSegmentIndex();
|
|
2622
|
+
goog.asserts.assert(tsAesEncryptionVideo.segmentIndex != null,
|
|
2623
|
+
'Null segmentIndex!');
|
|
2624
|
+
|
|
2625
|
+
const firstTsSegment = tsAesEncryptionVideo.segmentIndex.get(0);
|
|
2626
|
+
expect(firstTsSegment.hlsAes128Key).toBeDefined();
|
|
2627
|
+
const secondTsSegment = tsAesEncryptionVideo.segmentIndex.get(1);
|
|
2628
|
+
expect(secondTsSegment.hlsAes128Key).toBeNull();
|
|
2599
2629
|
});
|
|
2600
2630
|
|
|
2601
2631
|
it('fails on AES-128 if WebCrypto APIs are not available', async () => {
|
|
@@ -532,6 +532,34 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
|
|
|
532
532
|
goog.asserts.assert(position1 != null, 'Position should not be null!');
|
|
533
533
|
expect(index1.get(position1)).toBe(references2[1]);
|
|
534
534
|
});
|
|
535
|
+
|
|
536
|
+
it('does not duplicate references with rounding errors', () => {
|
|
537
|
+
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
538
|
+
const references1 = [
|
|
539
|
+
makeReference(uri(10), 10, 20),
|
|
540
|
+
makeReference(uri(20), 20, 30),
|
|
541
|
+
];
|
|
542
|
+
const index1 = new shaka.media.SegmentIndex(references1);
|
|
543
|
+
|
|
544
|
+
// 0.24 microseconds: an insignificant rounding error.
|
|
545
|
+
const tinyError = 0.24e-6;
|
|
546
|
+
|
|
547
|
+
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
548
|
+
const references2 = [
|
|
549
|
+
makeReference(uri(10), 10, 20),
|
|
550
|
+
// The difference between this and the equivalent old reference is an
|
|
551
|
+
// insignificant rounding error.
|
|
552
|
+
makeReference(uri(20), 20 + tinyError, 30 + tinyError),
|
|
553
|
+
makeReference(uri(30), 30 + tinyError, 40),
|
|
554
|
+
];
|
|
555
|
+
|
|
556
|
+
index1.merge(references2);
|
|
557
|
+
expect(index1.references.length).toBe(3);
|
|
558
|
+
expect(index1.references[0]).toEqual(references1[0]);
|
|
559
|
+
// The new references replaced the old one.
|
|
560
|
+
expect(index1.references[1]).toEqual(references2[1]);
|
|
561
|
+
expect(index1.references[2]).toEqual(references2[2]);
|
|
562
|
+
});
|
|
535
563
|
});
|
|
536
564
|
|
|
537
565
|
describe('evict', () => {
|
|
@@ -72,6 +72,33 @@ describe('Player', () => {
|
|
|
72
72
|
});
|
|
73
73
|
}); // describe('attach')
|
|
74
74
|
|
|
75
|
+
describe('destroy', () => {
|
|
76
|
+
// Regression test for:
|
|
77
|
+
// https://github.com/shaka-project/shaka-player/issues/4850
|
|
78
|
+
it('does not leave any lingering timers', async () => {
|
|
79
|
+
shaka.util.Timer.activeTimers.clear();
|
|
80
|
+
|
|
81
|
+
// Unlike the other tests in this file, this uses an uncompiled build of
|
|
82
|
+
// Shaka, so that we don't need to expose shaka.util.Timer.activeTimers.
|
|
83
|
+
player = new shaka.Player(video);
|
|
84
|
+
waiter.setPlayer(player);
|
|
85
|
+
|
|
86
|
+
// Play the video for a little while.
|
|
87
|
+
await player.load('test:sintel');
|
|
88
|
+
video.play();
|
|
89
|
+
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
|
|
90
|
+
|
|
91
|
+
// Destroy the player.
|
|
92
|
+
await player.destroy();
|
|
93
|
+
|
|
94
|
+
// Are there any timers left?
|
|
95
|
+
for (const timer of shaka.util.Timer.activeTimers.keys()) {
|
|
96
|
+
const stackTrace = shaka.util.Timer.activeTimers.get(timer);
|
|
97
|
+
fail('Lingering timer exists! Stack trace of creation: ' + stackTrace);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
75
102
|
describe('updateStartTime() in manifestparsed event handler', () => {
|
|
76
103
|
it('does not get segments prior to startTime', async () => {
|
|
77
104
|
player.addEventListener('manifestparsed', () => {
|
|
@@ -898,7 +925,7 @@ describe('Player', () => {
|
|
|
898
925
|
expect(getBufferedBehind()).toBe(20); // Buffered to start still.
|
|
899
926
|
video.currentTime = 50;
|
|
900
927
|
await waitUntilBuffered(30);
|
|
901
|
-
expect(getBufferedBehind()).toBeLessThan(
|
|
928
|
+
expect(getBufferedBehind()).toBeLessThan(40); // 30 + segment_size
|
|
902
929
|
|
|
903
930
|
player.configure('streaming.bufferBehind', 10);
|
|
904
931
|
// We only evict content when we append a segment, so increase the
|
|
@@ -925,7 +952,7 @@ describe('Player', () => {
|
|
|
925
952
|
}
|
|
926
953
|
|
|
927
954
|
async function waitUntilBuffered(amount) {
|
|
928
|
-
for (let i = 0; i <
|
|
955
|
+
for (let i = 0; i < 50; i++) {
|
|
929
956
|
// We buffer from an internal segment, so this shouldn't take long to
|
|
930
957
|
// buffer.
|
|
931
958
|
await Util.delay(0.1); // eslint-disable-line no-await-in-loop
|
|
@@ -933,7 +960,14 @@ describe('Player', () => {
|
|
|
933
960
|
return;
|
|
934
961
|
}
|
|
935
962
|
}
|
|
936
|
-
|
|
963
|
+
|
|
964
|
+
const ranges =
|
|
965
|
+
shaka.media.TimeRangesUtils.getBufferedInfo(video.buffered);
|
|
966
|
+
const currentTime = video.currentTime;
|
|
967
|
+
const target = currentTime + amount;
|
|
968
|
+
|
|
969
|
+
throw new Error('Timeout waiting to buffer! ' +
|
|
970
|
+
JSON.stringify({ranges, currentTime, target}));
|
|
937
971
|
}
|
|
938
972
|
}); // describe('buffering')
|
|
939
973
|
|
package/test/player_unit.js
CHANGED
|
@@ -3390,7 +3390,9 @@ describe('Player', () => {
|
|
|
3390
3390
|
variant.addAudio(0, (stream) => {
|
|
3391
3391
|
stream.channelsCount = 6;
|
|
3392
3392
|
stream.audioSamplingRate = 48000;
|
|
3393
|
-
|
|
3393
|
+
// ac-3 is rewritten as ec-3 on Tizen, so for the stability of this
|
|
3394
|
+
// test case, use ec-3.
|
|
3395
|
+
stream.codecs = 'ec-3';
|
|
3394
3396
|
});
|
|
3395
3397
|
});
|
|
3396
3398
|
|
|
@@ -3415,7 +3417,7 @@ describe('Player', () => {
|
|
|
3415
3417
|
expect(abrManager.variants.length).toBe(1);
|
|
3416
3418
|
// It should be the 6-channel variant, based on our preference.
|
|
3417
3419
|
expect(abrManager.variants[0].audio.channelsCount).toBe(6);
|
|
3418
|
-
expect(abrManager.variants[0].audio.codecs).toBe('
|
|
3420
|
+
expect(abrManager.variants[0].audio.codecs).toBe('ec-3');
|
|
3419
3421
|
});
|
|
3420
3422
|
});
|
|
3421
3423
|
|
package/test/test/boot.js
CHANGED
|
@@ -66,22 +66,40 @@ function failTestsOnNamespacedElementOrAttributeNames() {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* Fail the current test on a given error, constructed with a given header and
|
|
70
|
+
* the full stack trace of the error.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} messageHeader
|
|
73
|
+
* @param {!Error} error
|
|
71
74
|
*/
|
|
72
|
-
function
|
|
75
|
+
function failOnError(messageHeader, error) {
|
|
76
|
+
let message = `${messageHeader}: ${error}`;
|
|
77
|
+
// Shaka errors have the stack trace in their toString() already, so don't
|
|
78
|
+
// add it again. For native errors, we need to see where it came from.
|
|
79
|
+
if (error && error.stack && !(error instanceof shaka.util.Error)) {
|
|
80
|
+
message += '\n' + error.stack;
|
|
81
|
+
}
|
|
82
|
+
fail(message);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Listen for unhandled errors and Promise rejections (which may occur after a
|
|
87
|
+
* test) and convert them into test failures.
|
|
88
|
+
*/
|
|
89
|
+
function failTestsOnUnhandledErrors() {
|
|
73
90
|
// https://developer.mozilla.org/en-US/docs/Web/Events/unhandledrejection
|
|
74
91
|
window.addEventListener('unhandledrejection', (event) => {
|
|
75
92
|
/** @type {?} */
|
|
76
93
|
const error = event.reason;
|
|
77
|
-
|
|
94
|
+
failOnError('Unhandled rejection in Promise', error);
|
|
95
|
+
});
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/error_event
|
|
98
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent
|
|
99
|
+
window.addEventListener('error', (event) => {
|
|
100
|
+
/** @type {?} */
|
|
101
|
+
const error = event['error'];
|
|
102
|
+
failOnError('Unhandled error', error);
|
|
85
103
|
});
|
|
86
104
|
}
|
|
87
105
|
|
|
@@ -387,7 +405,7 @@ function configureJasmineEnvironment() {
|
|
|
387
405
|
function setupTestEnvironment() {
|
|
388
406
|
failTestsOnFailedAssertions();
|
|
389
407
|
failTestsOnNamespacedElementOrAttributeNames();
|
|
390
|
-
|
|
408
|
+
failTestsOnUnhandledErrors();
|
|
391
409
|
disableScrollbars();
|
|
392
410
|
workAroundLegacyEdgePromiseIssues();
|
|
393
411
|
|
|
@@ -73,7 +73,7 @@ shaka.test.ManifestParser = class {
|
|
|
73
73
|
const appendWindowStart = /** @type {?} */(jasmine.any(Number));
|
|
74
74
|
const appendWindowEnd = /** @type {?} */(jasmine.any(Number));
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
const ref = new shaka.media.SegmentReference(
|
|
77
77
|
start, end, getUris, startByte, endByte,
|
|
78
78
|
initSegmentReference,
|
|
79
79
|
timestampOffset,
|
|
@@ -82,7 +82,8 @@ shaka.test.ManifestParser = class {
|
|
|
82
82
|
partialReferences,
|
|
83
83
|
tilesLayout,
|
|
84
84
|
/* tileDuration= */ undefined,
|
|
85
|
-
syncTime
|
|
86
|
-
);
|
|
85
|
+
syncTime);
|
|
86
|
+
ref.discontinuitySequence = /** @type {?} */(jasmine.any(Number));
|
|
87
|
+
return ref;
|
|
87
88
|
}
|
|
88
89
|
};
|
|
@@ -405,7 +405,7 @@ const sintelAudioSegment = {
|
|
|
405
405
|
mdhdOffset: 0x1b6,
|
|
406
406
|
segmentUri: '/base/test/test/assets/sintel-audio-segment.mp4',
|
|
407
407
|
tfdtOffset: 0x3c,
|
|
408
|
-
segmentDuration: 10
|
|
408
|
+
segmentDuration: 10,
|
|
409
409
|
mimeType: 'audio/mp4',
|
|
410
410
|
codecs: 'mp4a.40.2',
|
|
411
411
|
};
|
|
@@ -431,7 +431,7 @@ const sintelEncryptedAudio = {
|
|
|
431
431
|
mdhdOffset: 0x1b6,
|
|
432
432
|
segmentUri: '/base/test/test/assets/encrypted-sintel-audio-segment.mp4',
|
|
433
433
|
tfdtOffset: 0x3c,
|
|
434
|
-
segmentDuration: 10
|
|
434
|
+
segmentDuration: 10,
|
|
435
435
|
mimeType: 'audio/mp4',
|
|
436
436
|
codecs: 'mp4a.40.2',
|
|
437
437
|
initData:
|
|
@@ -537,4 +537,74 @@ describe('UITextDisplayer', () => {
|
|
|
537
537
|
'.shaka-text-region');
|
|
538
538
|
expect(allRegionElements.length).toBe(1);
|
|
539
539
|
});
|
|
540
|
+
|
|
541
|
+
it('creates separate regions when dimensions differ but id same', () => {
|
|
542
|
+
const identicalRegionId = 'regionId';
|
|
543
|
+
|
|
544
|
+
const cueRegion1 = new shaka.text.CueRegion();
|
|
545
|
+
const cueRegion2 = new shaka.text.CueRegion();
|
|
546
|
+
cueRegion1.id = identicalRegionId;
|
|
547
|
+
cueRegion2.id = identicalRegionId;
|
|
548
|
+
|
|
549
|
+
cueRegion1.height = 80;
|
|
550
|
+
cueRegion1.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
551
|
+
cueRegion1.width = 80;
|
|
552
|
+
cueRegion1.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
553
|
+
|
|
554
|
+
cueRegion2.height = 160; // the only difference!
|
|
555
|
+
cueRegion2.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
556
|
+
cueRegion2.width = 80;
|
|
557
|
+
cueRegion2.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
558
|
+
|
|
559
|
+
cueRegion1.viewportAnchorX = 10;
|
|
560
|
+
cueRegion1.viewportAnchorY = 10;
|
|
561
|
+
cueRegion1.viewportAnchorUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
562
|
+
|
|
563
|
+
cueRegion2.viewportAnchorX = 10;
|
|
564
|
+
cueRegion2.viewportAnchorY = 10;
|
|
565
|
+
cueRegion2.viewportAnchorUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
566
|
+
|
|
567
|
+
// These all attach to the same region, but only one region element should
|
|
568
|
+
// be created.
|
|
569
|
+
const firstBatchOfCues = [
|
|
570
|
+
new shaka.text.Cue(0, 100, ''),
|
|
571
|
+
new shaka.text.Cue(0, 100, ''),
|
|
572
|
+
new shaka.text.Cue(0, 100, ''),
|
|
573
|
+
];
|
|
574
|
+
for (const cue of firstBatchOfCues) {
|
|
575
|
+
cue.displayAlign = shaka.text.Cue.displayAlign.CENTER;
|
|
576
|
+
cue.region = cueRegion1;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Another batch for the other region
|
|
580
|
+
const secondBatchOfCues = [
|
|
581
|
+
new shaka.text.Cue(0, 100, ''),
|
|
582
|
+
new shaka.text.Cue(0, 100, ''),
|
|
583
|
+
new shaka.text.Cue(0, 100, ''),
|
|
584
|
+
];
|
|
585
|
+
for (const cue of secondBatchOfCues) {
|
|
586
|
+
cue.displayAlign = shaka.text.Cue.displayAlign.CENTER;
|
|
587
|
+
cue.region = cueRegion2;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
textDisplayer.setTextVisibility(true);
|
|
591
|
+
textDisplayer.append(firstBatchOfCues);
|
|
592
|
+
textDisplayer.append(secondBatchOfCues);
|
|
593
|
+
updateCaptions();
|
|
594
|
+
|
|
595
|
+
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
|
596
|
+
const allRegionElements = textContainer.querySelectorAll(
|
|
597
|
+
'.shaka-text-region');
|
|
598
|
+
|
|
599
|
+
// Verify that the nested cues are attached to respective region element.
|
|
600
|
+
expect(allRegionElements.length).toBe(2);
|
|
601
|
+
|
|
602
|
+
const childrenOfOne = Array.from(allRegionElements[0].childNodes).filter(
|
|
603
|
+
(e) => e.nodeType == Node.ELEMENT_NODE);
|
|
604
|
+
expect(childrenOfOne.length).toBe(3);
|
|
605
|
+
|
|
606
|
+
const childrenOfTwo = Array.from(allRegionElements[1].childNodes).filter(
|
|
607
|
+
(e) => e.nodeType == Node.ELEMENT_NODE);
|
|
608
|
+
expect(childrenOfTwo.length).toBe(3);
|
|
609
|
+
});
|
|
540
610
|
});
|
package/ui/controls.js
CHANGED
|
@@ -599,7 +599,11 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
599
599
|
if (this.config_.forceLandscapeOnFullscreen && screen.orientation) {
|
|
600
600
|
// Locking to 'landscape' should let it be either
|
|
601
601
|
// 'landscape-primary' or 'landscape-secondary' as appropriate.
|
|
602
|
-
|
|
602
|
+
// We ignore errors from this specific call, since it creates noise
|
|
603
|
+
// on desktop otherwise.
|
|
604
|
+
try {
|
|
605
|
+
await screen.orientation.lock('landscape');
|
|
606
|
+
} catch (error) {}
|
|
603
607
|
}
|
|
604
608
|
} else {
|
|
605
609
|
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
|