shaka-player 4.3.14 → 4.3.16
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/appspot.yaml +4 -4
- package/.github/workflows/build-and-test.yaml +23 -10
- package/.github/workflows/custom-actions/set-commit-status/action.yaml +29 -7
- package/.github/workflows/demo-version-index.yaml +3 -3
- package/.github/workflows/nightly-demo.yaml +4 -4
- package/.github/workflows/release-please.yaml +25 -24
- package/.github/workflows/report-incremental-coverage.yaml +1 -1
- package/.github/workflows/selenium-lab-tests.yaml +30 -18
- package/.github/workflows/shaka-bot-commands/lib.sh +37 -12
- package/.github/workflows/shaka-bot-commands/main.sh +4 -2
- package/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/talk-to-shaka-bot.yaml +2 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/.github/workflows/update-screenshots.yaml +25 -38
- package/CHANGELOG.md +37 -0
- package/demo/demo.less +1 -5
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +0 -1
- package/dist/locales.js +32 -32
- package/dist/shaka-player.compiled.d.ts +6 -10
- package/dist/shaka-player.compiled.debug.d.ts +6 -10
- package/dist/shaka-player.compiled.debug.externs.js +13 -11
- package/dist/shaka-player.compiled.debug.js +222 -217
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +13 -11
- package/dist/shaka-player.compiled.js +855 -853
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +6 -10
- package/dist/shaka-player.ui.debug.d.ts +6 -10
- package/dist/shaka-player.ui.debug.externs.js +13 -11
- package/dist/shaka-player.ui.debug.js +171 -167
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +13 -11
- package/dist/shaka-player.ui.js +950 -948
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/faq.md +25 -0
- package/docs/tutorials/offline.md +3 -0
- package/docs/tutorials/ui.md +4 -4
- package/externs/shaka/player.js +32 -19
- package/lib/cast/cast_proxy.js +1 -3
- package/lib/cast/cast_sender.js +8 -11
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/dash/dash_parser.js +3 -0
- package/lib/dash/segment_template.js +5 -0
- package/lib/deprecate/deprecate.js +1 -1
- package/lib/hls/hls_parser.js +34 -17
- package/lib/media/drm_engine.js +17 -3
- package/lib/media/manifest_parser.js +2 -1
- package/lib/media/presentation_timeline.js +23 -0
- package/lib/media/segment_index.js +10 -0
- package/lib/media/streaming_engine.js +1 -13
- package/lib/player.js +19 -5
- package/lib/polyfill/mediasource.js +5 -36
- package/lib/text/ui_text_displayer.js +10 -1
- package/lib/text/vtt_text_parser.js +2 -1
- package/lib/util/error.js +1 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/stream_utils.js +8 -5
- package/package.json +2 -2
- package/test/cast/cast_proxy_unit.js +0 -46
- package/test/cast/cast_sender_unit.js +26 -26
- package/test/cea/cea608_memory_unit.js +3 -2
- package/test/hls/hls_live_unit.js +52 -0
- package/test/hls/hls_parser_unit.js +28 -0
- package/test/media/media_source_engine_integration.js +1 -1
- package/test/player_integration.js +9 -1
- package/test/player_load_graph_integration.js +13 -7
- package/test/player_src_equals_integration.js +2 -2
- package/test/player_unit.js +22 -4
- package/test/text/vtt_text_parser_unit.js +31 -0
- package/ui/less/containers.less +1 -0
- package/lib/polyfill/storage_estimate.js +0 -57
|
@@ -72,7 +72,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
72
72
|
|
|
73
73
|
/** @private {shaka.util.Timer} */
|
|
74
74
|
this.captionsTimer_ = new shaka.util.Timer(() => {
|
|
75
|
-
this.
|
|
75
|
+
if (!this.video_.paused) {
|
|
76
|
+
this.updateCaptions_();
|
|
77
|
+
}
|
|
76
78
|
}).tickEvery(updatePeriod);
|
|
77
79
|
|
|
78
80
|
/**
|
|
@@ -93,6 +95,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
93
95
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
94
96
|
});
|
|
95
97
|
|
|
98
|
+
this.eventManager_.listen(this.video_, 'seeking', () => {
|
|
99
|
+
this.updateCaptions_(/* forceUpdate= */ true);
|
|
100
|
+
});
|
|
101
|
+
|
|
96
102
|
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
97
103
|
// Whenever the natural width or natural height of the video changes
|
|
98
104
|
// (including, for example, because the selected video track was changed),
|
|
@@ -339,6 +345,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
339
345
|
const cueRegistry = this.currentCuesMap_.get(cue);
|
|
340
346
|
goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
|
|
341
347
|
if (cueRegistry.regionElement) {
|
|
348
|
+
if (cueRegistry.regionElement.contains(container)) {
|
|
349
|
+
cueRegistry.regionElement.removeChild(container);
|
|
350
|
+
}
|
|
342
351
|
container.appendChild(cueRegistry.regionElement);
|
|
343
352
|
cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
|
|
344
353
|
} else {
|
|
@@ -673,13 +673,14 @@ shaka.text.VttTextParser = class {
|
|
|
673
673
|
refCue.fontFamily, cue.fontFamily);
|
|
674
674
|
cue.fontSize = VttTextParser.getOrDefault_(
|
|
675
675
|
refCue.fontSize, cue.fontSize);
|
|
676
|
+
cue.textShadow = VttTextParser.getOrDefault_(
|
|
677
|
+
refCue.textShadow, cue.textShadow);
|
|
676
678
|
|
|
677
679
|
// Overwrite with new values as unable to determine
|
|
678
680
|
// if new value is set or not
|
|
679
681
|
cue.fontWeight = refCue.fontWeight;
|
|
680
682
|
cue.fontStyle = refCue.fontStyle;
|
|
681
683
|
cue.opacity = refCue.opacity;
|
|
682
|
-
cue.textShadow = refCue.textShadow;
|
|
683
684
|
cue.wrapLine = refCue.wrapLine;
|
|
684
685
|
}
|
|
685
686
|
|
package/lib/util/error.js
CHANGED
|
@@ -506,6 +506,7 @@ shaka.util.Error.Code = {
|
|
|
506
506
|
* <li>Configure the server to accept a HEAD request for the manifest.
|
|
507
507
|
* </ul>
|
|
508
508
|
* <br> error.data[0] is the manifest URI.
|
|
509
|
+
* <br> error.data[1] is the detected or specified MIME type.
|
|
509
510
|
*/
|
|
510
511
|
'UNABLE_TO_GUESS_MANIFEST_TYPE': 4000,
|
|
511
512
|
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -806,8 +806,12 @@ shaka.util.StreamUtils = class {
|
|
|
806
806
|
// currently don't support 'fLaC', while 'flac' is supported by most
|
|
807
807
|
// major browsers.
|
|
808
808
|
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
|
809
|
-
if (codecs
|
|
810
|
-
|
|
809
|
+
if (codecs.toLowerCase() == 'flac') {
|
|
810
|
+
if (!shaka.util.Platform.isSafari()) {
|
|
811
|
+
return 'flac';
|
|
812
|
+
} else {
|
|
813
|
+
return 'fLaC';
|
|
814
|
+
}
|
|
811
815
|
}
|
|
812
816
|
|
|
813
817
|
// The same is true for 'Opus'.
|
|
@@ -1273,8 +1277,6 @@ shaka.util.StreamUtils = class {
|
|
|
1273
1277
|
* @return {shaka.extern.Track}
|
|
1274
1278
|
*/
|
|
1275
1279
|
static html5TextTrackToTrack(textTrack) {
|
|
1276
|
-
const CLOSED_CAPTION_MIMETYPE =
|
|
1277
|
-
shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE;
|
|
1278
1280
|
const StreamUtils = shaka.util.StreamUtils;
|
|
1279
1281
|
|
|
1280
1282
|
/** @type {shaka.extern.Track} */
|
|
@@ -1283,7 +1285,8 @@ shaka.util.StreamUtils = class {
|
|
|
1283
1285
|
track.type = 'text';
|
|
1284
1286
|
track.originalTextId = textTrack.id;
|
|
1285
1287
|
if (textTrack.kind == 'captions') {
|
|
1286
|
-
|
|
1288
|
+
// See: https://github.com/shaka-project/shaka-player/issues/6233
|
|
1289
|
+
track.mimeType = 'unknown';
|
|
1287
1290
|
}
|
|
1288
1291
|
if (textTrack.kind == 'subtitles') {
|
|
1289
1292
|
track.mimeType = 'text/vtt';
|
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": "4.3.
|
|
4
|
+
"version": "4.3.16",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"karma-spec-reporter": "^0.0.34",
|
|
49
49
|
"karma-webdriver-launcher": "^1.0.8",
|
|
50
50
|
"lcevc_dil.js": "^1.1.20",
|
|
51
|
-
"less": "
|
|
51
|
+
"less": "^4.2.0",
|
|
52
52
|
"less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
|
|
53
53
|
"material-design-lite": "^1.3.0",
|
|
54
54
|
"mux.js": "^6.2.0",
|
|
@@ -114,52 +114,6 @@ describe('CastProxy', () => {
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
describe('cast', () => {
|
|
117
|
-
it('pauses the local video', () => {
|
|
118
|
-
proxy.cast();
|
|
119
|
-
expect(mockVideo.pause).toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('passes initial state to sender', () => {
|
|
123
|
-
mockVideo.loop = true;
|
|
124
|
-
mockVideo.playbackRate = 3;
|
|
125
|
-
mockVideo.currentTime = 12;
|
|
126
|
-
const fakeConfig = {key: 'value'};
|
|
127
|
-
mockPlayer.getConfiguration.and.returnValue(fakeConfig);
|
|
128
|
-
mockPlayer.isTextTrackVisible.and.returnValue(false);
|
|
129
|
-
const fakeManifestUri = 'foo://bar';
|
|
130
|
-
mockPlayer.getAssetUri.and.returnValue(fakeManifestUri);
|
|
131
|
-
|
|
132
|
-
proxy.cast();
|
|
133
|
-
const calls = mockSender.cast.calls;
|
|
134
|
-
expect(calls.count()).toBe(1);
|
|
135
|
-
if (calls.count()) {
|
|
136
|
-
const state = calls.argsFor(0)[0];
|
|
137
|
-
// Video state goes directly:
|
|
138
|
-
expect(state.video.loop).toBe(mockVideo.loop);
|
|
139
|
-
expect(state.video.playbackRate).toBe(mockVideo.playbackRate);
|
|
140
|
-
// Player state uses corresponding setter names:
|
|
141
|
-
expect(state.player.configure).toEqual(fakeConfig);
|
|
142
|
-
expect(state['playerAfterLoad'].setTextTrackVisibility).toBe(false);
|
|
143
|
-
// Manifest URI:
|
|
144
|
-
expect(state.manifest).toBe(fakeManifestUri);
|
|
145
|
-
// Start time:
|
|
146
|
-
expect(state.startTime).toBe(mockVideo.currentTime);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('does not provide a start time if the video has ended', () => {
|
|
151
|
-
mockVideo.ended = true;
|
|
152
|
-
mockVideo.currentTime = 12;
|
|
153
|
-
|
|
154
|
-
proxy.cast();
|
|
155
|
-
const calls = mockSender.cast.calls;
|
|
156
|
-
expect(calls.count()).toBe(1);
|
|
157
|
-
if (calls.count()) {
|
|
158
|
-
const state = calls.argsFor(0)[0];
|
|
159
|
-
expect(state.startTime).toBe(null);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
117
|
it('unloads the local player after casting is complete', async () => {
|
|
164
118
|
/** @type {!shaka.util.PublicPromise} */
|
|
165
119
|
const p = new shaka.util.PublicPromise();
|
|
@@ -157,7 +157,7 @@ describe('CastSender', () => {
|
|
|
157
157
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
158
158
|
shaka.util.Error.Category.CAST,
|
|
159
159
|
shaka.util.Error.Code.CAST_API_UNAVAILABLE));
|
|
160
|
-
await expectAsync(sender.cast(
|
|
160
|
+
await expectAsync(sender.cast()).toBeRejectedWith(expected);
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
it('fails when there are no receivers', async () => {
|
|
@@ -168,7 +168,7 @@ describe('CastSender', () => {
|
|
|
168
168
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
169
169
|
shaka.util.Error.Category.CAST,
|
|
170
170
|
shaka.util.Error.Code.NO_CAST_RECEIVERS));
|
|
171
|
-
await expectAsync(sender.cast(
|
|
171
|
+
await expectAsync(sender.cast()).toBeRejectedWith(expected);
|
|
172
172
|
});
|
|
173
173
|
|
|
174
174
|
it('creates a session and sends an "init" message', async () => {
|
|
@@ -177,7 +177,7 @@ describe('CastSender', () => {
|
|
|
177
177
|
fakeReceiverAvailability(true);
|
|
178
178
|
expect(sender.hasReceivers()).toBe(true);
|
|
179
179
|
|
|
180
|
-
const p = sender.cast(
|
|
180
|
+
const p = sender.cast();
|
|
181
181
|
fakeSessionConnection();
|
|
182
182
|
|
|
183
183
|
await p;
|
|
@@ -220,7 +220,7 @@ describe('CastSender', () => {
|
|
|
220
220
|
sender.init();
|
|
221
221
|
fakeReceiverAvailability(true);
|
|
222
222
|
|
|
223
|
-
const p = sender.cast(
|
|
223
|
+
const p = sender.cast();
|
|
224
224
|
fakeSessionConnectionFailure(metadata.castErrorCode);
|
|
225
225
|
|
|
226
226
|
const expected = Util.jasmineError(new shaka.util.Error(
|
|
@@ -236,7 +236,7 @@ describe('CastSender', () => {
|
|
|
236
236
|
sender.init();
|
|
237
237
|
fakeReceiverAvailability(true);
|
|
238
238
|
|
|
239
|
-
const p = sender.cast(
|
|
239
|
+
const p = sender.cast();
|
|
240
240
|
fakeSessionConnection();
|
|
241
241
|
|
|
242
242
|
await p;
|
|
@@ -244,14 +244,14 @@ describe('CastSender', () => {
|
|
|
244
244
|
shaka.util.Error.Severity.RECOVERABLE,
|
|
245
245
|
shaka.util.Error.Category.CAST,
|
|
246
246
|
shaka.util.Error.Code.ALREADY_CASTING));
|
|
247
|
-
await expectAsync(sender.cast(
|
|
247
|
+
await expectAsync(sender.cast()).toBeRejectedWith(expected);
|
|
248
248
|
});
|
|
249
249
|
});
|
|
250
250
|
|
|
251
251
|
it('re-uses old sessions', async () => {
|
|
252
252
|
sender.init();
|
|
253
253
|
fakeReceiverAvailability(true);
|
|
254
|
-
const p = sender.cast(
|
|
254
|
+
const p = sender.cast();
|
|
255
255
|
fakeSessionConnection();
|
|
256
256
|
const oldMockSession = mockSession;
|
|
257
257
|
await p;
|
|
@@ -289,7 +289,7 @@ describe('CastSender', () => {
|
|
|
289
289
|
it('doesn\'t re-use stopped sessions', async () => {
|
|
290
290
|
sender.init();
|
|
291
291
|
fakeReceiverAvailability(true);
|
|
292
|
-
const p = sender.cast(
|
|
292
|
+
const p = sender.cast();
|
|
293
293
|
fakeSessionConnection();
|
|
294
294
|
await p;
|
|
295
295
|
await sender.destroy();
|
|
@@ -334,7 +334,7 @@ describe('CastSender', () => {
|
|
|
334
334
|
sender.init();
|
|
335
335
|
fakeReceiverAvailability(true);
|
|
336
336
|
sender.setAppData(fakeAppData);
|
|
337
|
-
const p = sender.cast(
|
|
337
|
+
const p = sender.cast();
|
|
338
338
|
fakeSessionConnection();
|
|
339
339
|
|
|
340
340
|
await p;
|
|
@@ -347,7 +347,7 @@ describe('CastSender', () => {
|
|
|
347
347
|
it('sends a special "appData" message if casting', async () => {
|
|
348
348
|
sender.init();
|
|
349
349
|
fakeReceiverAvailability(true);
|
|
350
|
-
const p = sender.cast(
|
|
350
|
+
const p = sender.cast();
|
|
351
351
|
fakeSessionConnection();
|
|
352
352
|
await p;
|
|
353
353
|
|
|
@@ -390,7 +390,7 @@ describe('CastSender', () => {
|
|
|
390
390
|
it('is not triggered if making a new session', async () => {
|
|
391
391
|
sender.init();
|
|
392
392
|
fakeReceiverAvailability(true);
|
|
393
|
-
const p = sender.cast(
|
|
393
|
+
const p = sender.cast();
|
|
394
394
|
fakeSessionConnection();
|
|
395
395
|
await p;
|
|
396
396
|
|
|
@@ -437,7 +437,7 @@ describe('CastSender', () => {
|
|
|
437
437
|
it('is triggered by an "event" message', async () => {
|
|
438
438
|
sender.init();
|
|
439
439
|
fakeReceiverAvailability(true);
|
|
440
|
-
const p = sender.cast(
|
|
440
|
+
const p = sender.cast();
|
|
441
441
|
fakeSessionConnection();
|
|
442
442
|
await p;
|
|
443
443
|
|
|
@@ -460,7 +460,7 @@ describe('CastSender', () => {
|
|
|
460
460
|
it('is triggered when casting ends', async () => {
|
|
461
461
|
sender.init();
|
|
462
462
|
fakeReceiverAvailability(true);
|
|
463
|
-
const p = sender.cast(
|
|
463
|
+
const p = sender.cast();
|
|
464
464
|
fakeSessionConnection();
|
|
465
465
|
await p;
|
|
466
466
|
|
|
@@ -477,7 +477,7 @@ describe('CastSender', () => {
|
|
|
477
477
|
it('opens the dialog if we are casting', async () => {
|
|
478
478
|
sender.init();
|
|
479
479
|
fakeReceiverAvailability(true);
|
|
480
|
-
const p = sender.cast(
|
|
480
|
+
const p = sender.cast();
|
|
481
481
|
fakeSessionConnection();
|
|
482
482
|
await p;
|
|
483
483
|
|
|
@@ -502,7 +502,7 @@ describe('CastSender', () => {
|
|
|
502
502
|
it('returns most recent properties from "update" messages', async () => {
|
|
503
503
|
sender.init();
|
|
504
504
|
fakeReceiverAvailability(true);
|
|
505
|
-
const p = sender.cast(
|
|
505
|
+
const p = sender.cast();
|
|
506
506
|
fakeSessionConnection();
|
|
507
507
|
await p;
|
|
508
508
|
|
|
@@ -545,7 +545,7 @@ describe('CastSender', () => {
|
|
|
545
545
|
it('simple methods trigger "call" messages', async () => {
|
|
546
546
|
sender.init();
|
|
547
547
|
fakeReceiverAvailability(true);
|
|
548
|
-
const p = sender.cast(
|
|
548
|
+
const p = sender.cast();
|
|
549
549
|
fakeSessionConnection();
|
|
550
550
|
await p;
|
|
551
551
|
|
|
@@ -568,7 +568,7 @@ describe('CastSender', () => {
|
|
|
568
568
|
method = null;
|
|
569
569
|
sender.init();
|
|
570
570
|
fakeReceiverAvailability(true);
|
|
571
|
-
const p = sender.cast(
|
|
571
|
+
const p = sender.cast();
|
|
572
572
|
fakeSessionConnection();
|
|
573
573
|
await p;
|
|
574
574
|
|
|
@@ -655,7 +655,7 @@ describe('CastSender', () => {
|
|
|
655
655
|
it('overrides any cached properties', async () => {
|
|
656
656
|
sender.init();
|
|
657
657
|
fakeReceiverAvailability(true);
|
|
658
|
-
const p = sender.cast(
|
|
658
|
+
const p = sender.cast();
|
|
659
659
|
fakeSessionConnection();
|
|
660
660
|
await p;
|
|
661
661
|
|
|
@@ -675,7 +675,7 @@ describe('CastSender', () => {
|
|
|
675
675
|
it('causes a "set" message to be sent', async () => {
|
|
676
676
|
sender.init();
|
|
677
677
|
fakeReceiverAvailability(true);
|
|
678
|
-
const p = sender.cast(
|
|
678
|
+
const p = sender.cast();
|
|
679
679
|
fakeSessionConnection();
|
|
680
680
|
await p;
|
|
681
681
|
|
|
@@ -691,7 +691,7 @@ describe('CastSender', () => {
|
|
|
691
691
|
it('can be used before we have an "update" message', async () => {
|
|
692
692
|
sender.init();
|
|
693
693
|
fakeReceiverAvailability(true);
|
|
694
|
-
const p = sender.cast(
|
|
694
|
+
const p = sender.cast();
|
|
695
695
|
fakeSessionConnection();
|
|
696
696
|
await p;
|
|
697
697
|
|
|
@@ -705,7 +705,7 @@ describe('CastSender', () => {
|
|
|
705
705
|
it('is true only after we have an "update" message', async () => {
|
|
706
706
|
sender.init();
|
|
707
707
|
fakeReceiverAvailability(true);
|
|
708
|
-
const p = sender.cast(
|
|
708
|
+
const p = sender.cast();
|
|
709
709
|
fakeSessionConnection();
|
|
710
710
|
await p;
|
|
711
711
|
|
|
@@ -723,7 +723,7 @@ describe('CastSender', () => {
|
|
|
723
723
|
it('disconnects and cancels all async operations', async () => {
|
|
724
724
|
sender.init();
|
|
725
725
|
fakeReceiverAvailability(true);
|
|
726
|
-
const cast = sender.cast(
|
|
726
|
+
const cast = sender.cast();
|
|
727
727
|
fakeSessionConnection();
|
|
728
728
|
await cast;
|
|
729
729
|
|
|
@@ -756,7 +756,7 @@ describe('CastSender', () => {
|
|
|
756
756
|
it('transfers playback to local device', async () => {
|
|
757
757
|
sender.init();
|
|
758
758
|
fakeReceiverAvailability(true);
|
|
759
|
-
const cast = sender.cast(
|
|
759
|
+
const cast = sender.cast();
|
|
760
760
|
fakeSessionConnection();
|
|
761
761
|
await cast;
|
|
762
762
|
|
|
@@ -772,7 +772,7 @@ describe('CastSender', () => {
|
|
|
772
772
|
it('succeeds even if session.stop() throws', async () => {
|
|
773
773
|
sender.init();
|
|
774
774
|
fakeReceiverAvailability(true);
|
|
775
|
-
const cast = sender.cast(
|
|
775
|
+
const cast = sender.cast();
|
|
776
776
|
fakeSessionConnection();
|
|
777
777
|
await cast;
|
|
778
778
|
|
|
@@ -792,7 +792,7 @@ describe('CastSender', () => {
|
|
|
792
792
|
beforeEach(async () => {
|
|
793
793
|
sender.init();
|
|
794
794
|
fakeReceiverAvailability(true);
|
|
795
|
-
const cast = sender.cast(
|
|
795
|
+
const cast = sender.cast();
|
|
796
796
|
fakeSessionConnection();
|
|
797
797
|
await cast;
|
|
798
798
|
|
|
@@ -837,7 +837,7 @@ describe('CastSender', () => {
|
|
|
837
837
|
it('cancels all async operations', async () => {
|
|
838
838
|
sender.init();
|
|
839
839
|
fakeReceiverAvailability(true);
|
|
840
|
-
const cast = sender.cast(
|
|
840
|
+
const cast = sender.cast();
|
|
841
841
|
fakeSessionConnection();
|
|
842
842
|
await cast;
|
|
843
843
|
|
|
@@ -46,11 +46,12 @@ describe('Cea608Memory', () => {
|
|
|
46
46
|
it('adds and emits a series of special characters from the buffer', () => {
|
|
47
47
|
const startTime = 1;
|
|
48
48
|
const endTime = 2;
|
|
49
|
-
const expectedText = '
|
|
49
|
+
const expectedText = '½¿ èôÇ©ë»ö{ß│';
|
|
50
50
|
const charGroups = [
|
|
51
51
|
{
|
|
52
52
|
set: CharSet.SPECIAL_NORTH_AMERICAN,
|
|
53
|
-
|
|
53
|
+
// Note TS is not at either end to avoid side effect of trim()
|
|
54
|
+
chars: [0x32, 0x33, 0x39, 0x3a, 0x3e], // ½, ¿, TS, è, ô
|
|
54
55
|
},
|
|
55
56
|
|
|
56
57
|
{
|
|
@@ -776,6 +776,58 @@ describe('HlsParser live', () => {
|
|
|
776
776
|
manifest.variants[1].video, [ref4]);
|
|
777
777
|
});
|
|
778
778
|
|
|
779
|
+
describe('when ignoreManifestProgramDateTime is set', () => {
|
|
780
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
781
|
+
config.hls.ignoreManifestProgramDateTime = true;
|
|
782
|
+
|
|
783
|
+
it('does not reset segment times when switching', async () => {
|
|
784
|
+
parser.configure(config);
|
|
785
|
+
|
|
786
|
+
const ref1 = makeReference(
|
|
787
|
+
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
788
|
+
const ref2 = makeReference(
|
|
789
|
+
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
790
|
+
|
|
791
|
+
const secondVariant = [
|
|
792
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",',
|
|
793
|
+
'RESOLUTION=1200x940,FRAME-RATE=60\n',
|
|
794
|
+
'video2',
|
|
795
|
+
].join('');
|
|
796
|
+
const masterWithTwoVariants = master + secondVariant;
|
|
797
|
+
configureNetEngineForInitialManifest(masterWithTwoVariants,
|
|
798
|
+
mediaWithAdditionalSegment, mediaWithAdditionalSegment2);
|
|
799
|
+
|
|
800
|
+
const manifest = await parser.start('test:/master', playerInterface);
|
|
801
|
+
await manifest.variants[0].video.createSegmentIndex();
|
|
802
|
+
ManifestParser.verifySegmentIndex(
|
|
803
|
+
manifest.variants[0].video, [ref1, ref2]);
|
|
804
|
+
expect(manifest.variants[1].video.segmentIndex).toBeNull();
|
|
805
|
+
|
|
806
|
+
// In the initial playlist, we know the earliest start time is 0, at
|
|
807
|
+
// EXT-X-MEDIA-SEQUENCE of 0.
|
|
808
|
+
expect(
|
|
809
|
+
manifest.variants[0].video.segmentIndex.earliestReference()
|
|
810
|
+
.getStartTime())
|
|
811
|
+
.toBe(0);
|
|
812
|
+
|
|
813
|
+
// Update.
|
|
814
|
+
fakeNetEngine
|
|
815
|
+
.setResponseText('test:/video', mediaWithRemovedSegment)
|
|
816
|
+
.setResponseText('test:/video2', mediaWithRemovedSegment2);
|
|
817
|
+
await delayForUpdatePeriod();
|
|
818
|
+
|
|
819
|
+
// Switch. The new variant starts at EXT-X-MEDIA-SEQUENCE of 1.
|
|
820
|
+
await manifest.variants[0].video.closeSegmentIndex();
|
|
821
|
+
await manifest.variants[1].video.createSegmentIndex();
|
|
822
|
+
|
|
823
|
+
// The earliest start time of the new segmentIndex should therefore be
|
|
824
|
+
// 2.
|
|
825
|
+
expect(manifest.variants[0].video.segmentIndex).toBeNull();
|
|
826
|
+
const segIdx = manifest.variants[1].video.segmentIndex;
|
|
827
|
+
expect(segIdx.earliestReference().getStartTime()).toBe(2);
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
|
|
779
831
|
it('handles switching during update', async () => {
|
|
780
832
|
const ref1 = makeReference(
|
|
781
833
|
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
@@ -3743,6 +3743,34 @@ describe('HlsParser', () => {
|
|
|
3743
3743
|
await verifyError(master, media, error, /* onCreateSegmentIndex= */ true);
|
|
3744
3744
|
});
|
|
3745
3745
|
|
|
3746
|
+
it('if SAMPLE-AES encryption with MSE and mp2t content', async () => {
|
|
3747
|
+
const master = [
|
|
3748
|
+
'#EXTM3U\n',
|
|
3749
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
3750
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
3751
|
+
'video\n',
|
|
3752
|
+
].join('');
|
|
3753
|
+
|
|
3754
|
+
const media = [
|
|
3755
|
+
'#EXTM3U\n',
|
|
3756
|
+
'#EXT-X-TARGETDURATION:6\n',
|
|
3757
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3758
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES,',
|
|
3759
|
+
'URI="fake",\n',
|
|
3760
|
+
'#EXTINF:5,\n',
|
|
3761
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
3762
|
+
'main.ts',
|
|
3763
|
+
].join('');
|
|
3764
|
+
|
|
3765
|
+
const error = new shaka.util.Error(
|
|
3766
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
3767
|
+
shaka.util.Error.Category.MANIFEST,
|
|
3768
|
+
Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
|
|
3769
|
+
|
|
3770
|
+
await verifyError(master, media, error, /* onCreateSegmentIndex= */ true);
|
|
3771
|
+
});
|
|
3772
|
+
|
|
3773
|
+
|
|
3746
3774
|
describe('if required tags are missing', () => {
|
|
3747
3775
|
/**
|
|
3748
3776
|
* @param {string} master
|
|
@@ -1197,13 +1197,21 @@ describe('Player', () => {
|
|
|
1197
1197
|
'com.widevine.alpha': bogusUrl,
|
|
1198
1198
|
'com.microsoft.playready': bogusUrl,
|
|
1199
1199
|
});
|
|
1200
|
-
|
|
1200
|
+
|
|
1201
|
+
// This load may be interrupted, so ignore errors and don't wait.
|
|
1202
|
+
const loadPromise =
|
|
1203
|
+
player.load('test:sintel-enc_compiled').catch(() => {});
|
|
1201
1204
|
|
|
1202
1205
|
await errorPromise;
|
|
1203
1206
|
expect(unloadPromise).not.toBeNull();
|
|
1207
|
+
|
|
1204
1208
|
if (unloadPromise) {
|
|
1205
1209
|
await unloadPromise;
|
|
1206
1210
|
}
|
|
1211
|
+
|
|
1212
|
+
// This should be done, and errors ignored. But don't leave any Promise
|
|
1213
|
+
// unresolved.
|
|
1214
|
+
await loadPromise;
|
|
1207
1215
|
});
|
|
1208
1216
|
}); // describe('unloading')
|
|
1209
1217
|
|
|
@@ -464,11 +464,11 @@ describe('Player Load Graph', () => {
|
|
|
464
464
|
|
|
465
465
|
// Make the two requests one-after-another so that we don't have any idle
|
|
466
466
|
// time between them.
|
|
467
|
-
const
|
|
468
|
-
const
|
|
467
|
+
const attach = player.attach(video);
|
|
468
|
+
const load = player.load('test:sintel');
|
|
469
469
|
|
|
470
|
-
await
|
|
471
|
-
await expectAsync(
|
|
470
|
+
await attach;
|
|
471
|
+
await expectAsync(load).toBeRejected();
|
|
472
472
|
|
|
473
473
|
// Wait a couple interrupter cycles to allow the player to enter idle
|
|
474
474
|
// state.
|
|
@@ -506,10 +506,12 @@ describe('Player Load Graph', () => {
|
|
|
506
506
|
// Normally the player would initialize media source after attaching to
|
|
507
507
|
// the media element, however since we don't support media source, it
|
|
508
508
|
// should stop at the attach state.
|
|
509
|
-
player.attach(video, /* initMediaSource= */ true);
|
|
509
|
+
const attach = player.attach(video, /* initMediaSource= */ true);
|
|
510
510
|
|
|
511
511
|
const event = await spyIsCalled(stateIdleSpy);
|
|
512
512
|
expect(event.state).toBe('attach');
|
|
513
|
+
|
|
514
|
+
await attach;
|
|
513
515
|
});
|
|
514
516
|
|
|
515
517
|
it('loading ignores media source path', async () => {
|
|
@@ -518,10 +520,12 @@ describe('Player Load Graph', () => {
|
|
|
518
520
|
// Normally the player would load content like this with the media source
|
|
519
521
|
// path, but since we don't have media source support, it should use the
|
|
520
522
|
// src= path.
|
|
521
|
-
player.load(SMALL_MP4_CONTENT_URI);
|
|
523
|
+
const load = player.load(SMALL_MP4_CONTENT_URI);
|
|
522
524
|
|
|
523
525
|
const event = await spyIsCalled(stateIdleSpy);
|
|
524
526
|
expect(event.state).toBe('src-equals');
|
|
527
|
+
|
|
528
|
+
await load;
|
|
525
529
|
});
|
|
526
530
|
|
|
527
531
|
it('unloading ignores init media source flag', async () => {
|
|
@@ -531,10 +535,12 @@ describe('Player Load Graph', () => {
|
|
|
531
535
|
// Normally the player would try to go to the media source state because
|
|
532
536
|
// we are saying to initialize media source after unloading, but since we
|
|
533
537
|
// don't have media source, it should stop at the attach state.
|
|
534
|
-
player.unload(/* initMediaSource= */ true);
|
|
538
|
+
const unload = player.unload(/* initMediaSource= */ true);
|
|
535
539
|
|
|
536
540
|
const event = await spyIsCalled(stateIdleSpy);
|
|
537
541
|
expect(event.state).toBe('attach');
|
|
542
|
+
|
|
543
|
+
await unload;
|
|
538
544
|
});
|
|
539
545
|
});
|
|
540
546
|
|
|
@@ -114,8 +114,8 @@ describe('Player Src Equals', () => {
|
|
|
114
114
|
expect(video.currentTime).toBeLessThan(2.0);
|
|
115
115
|
|
|
116
116
|
// Trigger a seek and then wait for the seek to take effect.
|
|
117
|
-
// This seek target is very close to the duration of the video.
|
|
118
|
-
video.currentTime =
|
|
117
|
+
// This seek target is very close to the duration of the video (10.01s).
|
|
118
|
+
video.currentTime = 9.5;
|
|
119
119
|
await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
|
|
120
120
|
|
|
121
121
|
// Make sure the playhead is roughly where we expect it to be after
|
package/test/player_unit.js
CHANGED
|
@@ -559,15 +559,21 @@ describe('Player', () => {
|
|
|
559
559
|
});
|
|
560
560
|
|
|
561
561
|
describe('when config.streaming.preferNativeHls is set to true', () => {
|
|
562
|
-
|
|
562
|
+
beforeAll(() => {
|
|
563
563
|
shaka.media.ManifestParser.registerParserByMime(
|
|
564
564
|
'application/x-mpegurl',
|
|
565
565
|
() => new shaka.test.FakeManifestParser(manifest));
|
|
566
566
|
});
|
|
567
567
|
|
|
568
|
+
afterAll(() => {
|
|
569
|
+
// IMPORTANT: restore the ORIGINAL parser. DO NOT just unregister the
|
|
570
|
+
// fake!
|
|
571
|
+
shaka.media.ManifestParser.registerParserByMime(
|
|
572
|
+
'application/x-mpegurl',
|
|
573
|
+
() => new shaka.hls.HlsParser());
|
|
574
|
+
});
|
|
575
|
+
|
|
568
576
|
afterEach(() => {
|
|
569
|
-
shaka.media.ManifestParser.unregisterParserByMime(
|
|
570
|
-
'application/x-mpegurl');
|
|
571
577
|
video.canPlayType.calls.reset();
|
|
572
578
|
});
|
|
573
579
|
|
|
@@ -3513,11 +3519,14 @@ describe('Player', () => {
|
|
|
3513
3519
|
});
|
|
3514
3520
|
|
|
3515
3521
|
describe('getPlayheadTimeAsDate()', () => {
|
|
3522
|
+
/** @type {?shaka.media.PresentationTimeline} */
|
|
3523
|
+
let timeline;
|
|
3516
3524
|
beforeEach(async () => {
|
|
3517
|
-
|
|
3525
|
+
timeline = new shaka.media.PresentationTimeline(300, 0);
|
|
3518
3526
|
timeline.setStatic(false);
|
|
3519
3527
|
|
|
3520
3528
|
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3529
|
+
goog.asserts.assert(timeline, 'timeline must be non-null');
|
|
3521
3530
|
manifest.presentationTimeline = timeline;
|
|
3522
3531
|
manifest.addVariant(0, (variant) => {
|
|
3523
3532
|
variant.addVideo(1);
|
|
@@ -3535,6 +3544,15 @@ describe('Player', () => {
|
|
|
3535
3544
|
// (300 (presentation start time) + 20 (playhead time)) * 1000 (ms/sec)
|
|
3536
3545
|
expect(liveTimeUtc).toEqual(new Date(320 * 1000));
|
|
3537
3546
|
});
|
|
3547
|
+
|
|
3548
|
+
it('uses program date time', () => {
|
|
3549
|
+
timeline.setInitialProgramDateTime(100);
|
|
3550
|
+
playhead.getTime.and.returnValue(20);
|
|
3551
|
+
|
|
3552
|
+
const liveTimeUtc = player.getPlayheadTimeAsDate();
|
|
3553
|
+
// (100 (program date time) + 20 (playhead time)) * 1000 (ms/sec)
|
|
3554
|
+
expect(liveTimeUtc).toEqual(new Date(120 * 1000));
|
|
3555
|
+
});
|
|
3538
3556
|
});
|
|
3539
3557
|
|
|
3540
3558
|
it('rejects empty manifests', async () => {
|