shaka-player 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/config.yml +1 -0
- package/.github/workflows/release-please.yaml +6 -11
- package/CHANGELOG.md +15 -0
- package/build/misspellings.txt +1 -0
- package/dist/controls.css +1 -1
- package/dist/locales.js +32 -32
- 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 +1 -0
- package/dist/shaka-player.compiled.debug.js +170 -165
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +1 -0
- package/dist/shaka-player.compiled.js +783 -779
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +2 -1
- package/dist/shaka-player.ui.debug.d.ts +2 -1
- package/dist/shaka-player.ui.debug.externs.js +1 -0
- package/dist/shaka-player.ui.debug.js +182 -177
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +1 -0
- package/dist/shaka-player.ui.js +924 -920
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/upgrade.md +4 -2
- package/externs/shaka/player.js +11 -0
- package/karma.conf.js +4 -0
- package/lib/dash/dash_parser.js +2 -2
- package/lib/hls/hls_parser.js +20 -11
- package/lib/media/drm_engine.js +16 -0
- package/lib/media/media_source_engine.js +91 -37
- package/lib/media/streaming_engine.js +1 -2
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/player.js +165 -107
- package/lib/text/vtt_text_parser.js +7 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +7 -0
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +48 -1
- package/lib/util/periods.js +5 -4
- package/lib/util/player_configuration.js +14 -14
- package/lib/util/stream_utils.js +4 -2
- package/package.json +4 -1
- package/test/hls/hls_live_unit.js +22 -0
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/player_external.js +7 -1
- package/test/player_integration.js +6 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +98 -1
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/simple_fakes.js +31 -2
- package/test/text/vtt_text_parser_unit.js +64 -5
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/ui/resolution_selection.js +1 -1
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
package/lib/util/periods.js
CHANGED
|
@@ -573,8 +573,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
573
573
|
// TODO(#1528): Consider changing this when we support codec switching.
|
|
574
574
|
const hasCodec = outputStreams.some((s) => {
|
|
575
575
|
return s.mimeType == stream.mimeType &&
|
|
576
|
-
shaka.util.MimeUtils.
|
|
577
|
-
shaka.util.MimeUtils.
|
|
576
|
+
shaka.util.MimeUtils.getNormalizedCodec(s.codecs) ==
|
|
577
|
+
shaka.util.MimeUtils.getNormalizedCodec(stream.codecs);
|
|
578
578
|
});
|
|
579
579
|
if (!hasCodec) {
|
|
580
580
|
continue;
|
|
@@ -1050,10 +1050,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
1050
1050
|
* @private
|
|
1051
1051
|
*/
|
|
1052
1052
|
static areAVStreamsCompatible_(outputStream, candidate) {
|
|
1053
|
-
const
|
|
1053
|
+
const getCodec = (codecs) =>
|
|
1054
|
+
shaka.util.MimeUtils.getNormalizedCodec(codecs);
|
|
1054
1055
|
// Check MIME type and codecs, which should always be the same.
|
|
1055
1056
|
if (candidate.mimeType != outputStream.mimeType ||
|
|
1056
|
-
|
|
1057
|
+
getCodec(candidate.codecs) != getCodec(outputStream.codecs)) {
|
|
1057
1058
|
return false;
|
|
1058
1059
|
}
|
|
1059
1060
|
|
|
@@ -16,14 +16,6 @@ goog.require('shaka.util.ManifestParserUtils');
|
|
|
16
16
|
goog.require('shaka.util.Platform');
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
// TODO(vaage): Many times in our configs, we need to create an empty
|
|
20
|
-
// implementation of a method, but to avoid closure from removing unused
|
|
21
|
-
// parameters (and breaking our merge config code) we need to use each
|
|
22
|
-
// parameter. Is there a better solution to this problem than what we are
|
|
23
|
-
// doing now?
|
|
24
|
-
//
|
|
25
|
-
// NOTE: Chrome App Content Security Policy prohibits usage of new Function()
|
|
26
|
-
|
|
27
19
|
/**
|
|
28
20
|
* @final
|
|
29
21
|
* @export
|
|
@@ -74,6 +66,11 @@ shaka.util.PlayerConfiguration = class {
|
|
|
74
66
|
clearKeys: {}, // key is arbitrary key system ID, value must be string
|
|
75
67
|
advanced: {}, // key is arbitrary key system ID, value is a record type
|
|
76
68
|
delayLicenseRequestUntilPlayed: false,
|
|
69
|
+
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
70
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
71
|
+
[initData, initDataType, drmInfo],
|
|
72
|
+
initData);
|
|
73
|
+
},
|
|
77
74
|
logLicenseExchange: false,
|
|
78
75
|
updateExpirationTime: 1,
|
|
79
76
|
preferredKeySystems: [],
|
|
@@ -113,11 +110,10 @@ shaka.util.PlayerConfiguration = class {
|
|
|
113
110
|
'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb':
|
|
114
111
|
'com.adobe.primetime',
|
|
115
112
|
},
|
|
116
|
-
// Need some operation in the callback or else closure may remove calls
|
|
117
|
-
// to the function as it would be a no-op. The operation can't just be
|
|
118
|
-
// a log message, because those are stripped in the compiled build.
|
|
119
113
|
manifestPreprocessor: (element) => {
|
|
120
|
-
return
|
|
114
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
115
|
+
[element],
|
|
116
|
+
element);
|
|
121
117
|
},
|
|
122
118
|
},
|
|
123
119
|
hls: {
|
|
@@ -138,7 +134,9 @@ shaka.util.PlayerConfiguration = class {
|
|
|
138
134
|
// log message, because those are stripped in the compiled build.
|
|
139
135
|
failureCallback: (error) => {
|
|
140
136
|
shaka.log.error('Unhandled streaming error', error);
|
|
141
|
-
return
|
|
137
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
138
|
+
[error],
|
|
139
|
+
undefined);
|
|
142
140
|
},
|
|
143
141
|
// When low latency streaming is enabled, rebufferingGoal will default to
|
|
144
142
|
// 0.01 if not specified.
|
|
@@ -214,7 +212,9 @@ shaka.util.PlayerConfiguration = class {
|
|
|
214
212
|
// to the function as it would be a no-op. The operation can't just be a
|
|
215
213
|
// log message, because those are stripped in the compiled build.
|
|
216
214
|
progressCallback: (content, progress) => {
|
|
217
|
-
return
|
|
215
|
+
return shaka.util.ConfigUtils.referenceParametersAndReturn(
|
|
216
|
+
[content, progress],
|
|
217
|
+
undefined);
|
|
218
218
|
},
|
|
219
219
|
|
|
220
220
|
// By default we use persistent licenses as forces errors to surface if
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -283,12 +283,14 @@ shaka.util.StreamUtils = class {
|
|
|
283
283
|
// both be considered the same codec: avc1.42c01e, avc1.4d401f
|
|
284
284
|
let baseVideoCodec = '';
|
|
285
285
|
if (variant.video) {
|
|
286
|
-
baseVideoCodec =
|
|
286
|
+
baseVideoCodec =
|
|
287
|
+
shaka.util.MimeUtils.getNormalizedCodec(variant.video.codecs);
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
let baseAudioCodec = '';
|
|
290
291
|
if (variant.audio) {
|
|
291
|
-
baseAudioCodec =
|
|
292
|
+
baseAudioCodec =
|
|
293
|
+
shaka.util.MimeUtils.getNormalizedCodec(variant.audio.codecs);
|
|
292
294
|
}
|
|
293
295
|
|
|
294
296
|
return baseVideoCodec + '-' + baseAudioCodec;
|
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.0.
|
|
4
|
+
"version": "4.0.1",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -97,5 +97,8 @@
|
|
|
97
97
|
},
|
|
98
98
|
"dependencies": {
|
|
99
99
|
"eme-encryption-scheme-polyfill": "^2.0.3"
|
|
100
|
+
},
|
|
101
|
+
"engines": {
|
|
102
|
+
"node": ">=14"
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -603,6 +603,28 @@ describe('HlsParser live', () => {
|
|
|
603
603
|
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
|
|
604
604
|
});
|
|
605
605
|
|
|
606
|
+
// Test for https://github.com/shaka-project/shaka-player/issues/4185
|
|
607
|
+
it('does not fail on preload hints with LL mode off', async () => {
|
|
608
|
+
// LL mode must be off for this test!
|
|
609
|
+
playerInterface.isLowLatencyMode = () => false;
|
|
610
|
+
|
|
611
|
+
const mediaWithPartialSegments = [
|
|
612
|
+
'#EXTM3U\n',
|
|
613
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
614
|
+
'#EXTINF:4,\n',
|
|
615
|
+
'main.mp4\n',
|
|
616
|
+
'#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
|
|
617
|
+
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
618
|
+
].join('');
|
|
619
|
+
|
|
620
|
+
fakeNetEngine
|
|
621
|
+
.setResponseText('test:/master', master)
|
|
622
|
+
.setResponseText('test:/video', mediaWithPartialSegments);
|
|
623
|
+
|
|
624
|
+
// If this throws, the test fails. Otherwise, it passes.
|
|
625
|
+
await parser.start('test:/master', playerInterface);
|
|
626
|
+
});
|
|
627
|
+
|
|
606
628
|
describe('update', () => {
|
|
607
629
|
it('adds new segments when they appear', async () => {
|
|
608
630
|
const ref1 = makeReference(
|
|
@@ -238,6 +238,25 @@ describe('ManifestTextParser', () => {
|
|
|
238
238
|
'#EXTM3U\n' +
|
|
239
239
|
'#EXT-X-MEDIA:CODECS="avc1.64002a,mp4a.40.2",AUDIO="a1,a2"',
|
|
240
240
|
|
|
241
|
+
// manifest URI:
|
|
242
|
+
'https://test/manifest.m3u8');
|
|
243
|
+
|
|
244
|
+
verifyPlaylist(
|
|
245
|
+
{
|
|
246
|
+
type: shaka.hls.PlaylistType.MASTER,
|
|
247
|
+
tags: [
|
|
248
|
+
new shaka.hls.Tag(/* id= */ 3, 'EXT-X-MEDIA',
|
|
249
|
+
[
|
|
250
|
+
new shaka.hls.Attribute('CODECS',
|
|
251
|
+
'av01.0.08M.08,mp4a.40.2'),
|
|
252
|
+
]),
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// playlist text:
|
|
257
|
+
'#EXTM3U\n' +
|
|
258
|
+
'#EXT-X-MEDIA:CODECS="av01.0.08M.08,mp4a.40.2"',
|
|
259
|
+
|
|
241
260
|
// manifest URI:
|
|
242
261
|
'https://test/manifest.m3u8');
|
|
243
262
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
filterDescribe('IndexeddbStorageCell',
|
|
7
|
+
filterDescribe('IndexeddbStorageCell', offlineSupported, () => {
|
|
8
8
|
const IndexedDBUtils = shaka.test.IndexedDBUtils;
|
|
9
9
|
const OfflineUtils = shaka.test.OfflineUtils;
|
|
10
10
|
const Util = shaka.test.Util;
|
|
@@ -91,7 +91,7 @@ const compatibilityTestsMetadata = [
|
|
|
91
91
|
},
|
|
92
92
|
];
|
|
93
93
|
|
|
94
|
-
filterDescribe('Storage Compatibility',
|
|
94
|
+
filterDescribe('Storage Compatibility', offlineSupported, () => {
|
|
95
95
|
for (const metadata of compatibilityTestsMetadata) {
|
|
96
96
|
describe(metadata.name, () => {
|
|
97
97
|
makeTests(metadata);
|
package/test/player_external.js
CHANGED
|
@@ -114,7 +114,13 @@ describe('Player', () => {
|
|
|
114
114
|
const networkingEngine = player.getNetworkingEngine();
|
|
115
115
|
asset.applyFilters(networkingEngine);
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
// Rather than awaiting the load() method, catch any load() errors and
|
|
118
|
+
// wait on the 'canplay' event. This has the advantage that we will
|
|
119
|
+
// get better logging of the media state on a timeout, since that
|
|
120
|
+
// capabilitiy is built into the waiter for media element events.
|
|
121
|
+
player.load(asset.manifestUri).catch(fail);
|
|
122
|
+
await waiter.timeoutAfter(60).waitForEvent(video, 'canplay');
|
|
123
|
+
|
|
118
124
|
if (asset.features) {
|
|
119
125
|
const isLive = asset.features.includes(Feature.LIVE);
|
|
120
126
|
expect(player.isLive()).toBe(isLive);
|
|
@@ -1069,16 +1069,16 @@ describe('Player', () => {
|
|
|
1069
1069
|
});
|
|
1070
1070
|
}); // describe('unloading')
|
|
1071
1071
|
|
|
1072
|
-
describe('
|
|
1073
|
-
it('
|
|
1072
|
+
describe('addChaptersTrack', () => {
|
|
1073
|
+
it('adds external chapters in vtt format', async () => {
|
|
1074
1074
|
await player.load('test:sintel_no_text_compiled');
|
|
1075
1075
|
const locationUri = new goog.Uri(location.href);
|
|
1076
1076
|
const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
|
|
1077
1077
|
const absoluteUri1 = locationUri.resolve(partialUri1);
|
|
1078
1078
|
await player.addChaptersTrack(absoluteUri1.toString(), 'en');
|
|
1079
1079
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1080
|
+
// Data should be available as soon as addChaptersTrack resolves.
|
|
1081
|
+
// See https://github.com/shaka-project/shaka-player/issues/4186
|
|
1082
1082
|
const chapters = player.getChapters('en');
|
|
1083
1083
|
expect(chapters.length).toBe(3);
|
|
1084
1084
|
const chapter1 = chapters[0];
|
|
@@ -1098,8 +1098,6 @@ describe('Player', () => {
|
|
|
1098
1098
|
const absoluteUri2 = locationUri.resolve(partialUri2);
|
|
1099
1099
|
await player.addChaptersTrack(absoluteUri2.toString(), 'en');
|
|
1100
1100
|
|
|
1101
|
-
await shaka.test.Util.delay(1.5);
|
|
1102
|
-
|
|
1103
1101
|
const chaptersUpdated = player.getChapters('en');
|
|
1104
1102
|
expect(chaptersUpdated.length).toBe(6);
|
|
1105
1103
|
const chapterUpdated1 = chaptersUpdated[0];
|
|
@@ -1128,15 +1126,13 @@ describe('Player', () => {
|
|
|
1128
1126
|
expect(chapterUpdated6.endTime).toBe(61.349);
|
|
1129
1127
|
});
|
|
1130
1128
|
|
|
1131
|
-
it('
|
|
1129
|
+
it('adds external chapters in srt format', async () => {
|
|
1132
1130
|
await player.load('test:sintel_no_text_compiled');
|
|
1133
1131
|
const locationUri = new goog.Uri(location.href);
|
|
1134
1132
|
const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
|
|
1135
1133
|
const absoluteUri = locationUri.resolve(partialUri);
|
|
1136
1134
|
await player.addChaptersTrack(absoluteUri.toString(), 'es');
|
|
1137
1135
|
|
|
1138
|
-
await shaka.test.Util.delay(1.5);
|
|
1139
|
-
|
|
1140
1136
|
const chapters = player.getChapters('es');
|
|
1141
1137
|
expect(chapters.length).toBe(3);
|
|
1142
1138
|
const chapter1 = chapters[0];
|
|
@@ -1152,5 +1148,5 @@ describe('Player', () => {
|
|
|
1152
1148
|
expect(chapter3.startTime).toBe(30);
|
|
1153
1149
|
expect(chapter3.endTime).toBe(61.349);
|
|
1154
1150
|
});
|
|
1155
|
-
}); // describe('
|
|
1151
|
+
}); // describe('addChaptersTrack')
|
|
1156
1152
|
});
|
|
@@ -319,90 +319,88 @@ describe('Player Src Equals', () => {
|
|
|
319
319
|
expect(newTrack).toBeTruthy();
|
|
320
320
|
});
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
it('add external chapters in srt format', async () => {
|
|
382
|
-
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
|
|
383
|
-
|
|
384
|
-
const locationUri = new goog.Uri(location.href);
|
|
385
|
-
const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
|
|
386
|
-
const absoluteUri = locationUri.resolve(partialUri);
|
|
387
|
-
await player.addChaptersTrack(absoluteUri.toString(), 'es');
|
|
388
|
-
|
|
389
|
-
await shaka.test.Util.delay(1.5);
|
|
322
|
+
describe('addChaptersTrack', () => {
|
|
323
|
+
it('adds external chapters in vtt format', async () => {
|
|
324
|
+
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
|
|
325
|
+
|
|
326
|
+
const locationUri = new goog.Uri(location.href);
|
|
327
|
+
const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
|
|
328
|
+
const absoluteUri1 = locationUri.resolve(partialUri1);
|
|
329
|
+
await player.addChaptersTrack(absoluteUri1.toString(), 'en');
|
|
330
|
+
|
|
331
|
+
// Data should be available as soon as addChaptersTrack resolves.
|
|
332
|
+
// See https://github.com/shaka-project/shaka-player/issues/4186
|
|
333
|
+
const chapters = player.getChapters('en');
|
|
334
|
+
expect(chapters.length).toBe(3);
|
|
335
|
+
const chapter1 = chapters[0];
|
|
336
|
+
expect(chapter1.title).toBe('Chapter 1');
|
|
337
|
+
expect(chapter1.startTime).toBe(0);
|
|
338
|
+
expect(chapter1.endTime).toBe(5);
|
|
339
|
+
const chapter2 = chapters[1];
|
|
340
|
+
expect(chapter2.title).toBe('Chapter 2');
|
|
341
|
+
expect(chapter2.startTime).toBe(5);
|
|
342
|
+
expect(chapter2.endTime).toBe(10);
|
|
343
|
+
const chapter3 = chapters[2];
|
|
344
|
+
expect(chapter3.title).toBe('Chapter 3');
|
|
345
|
+
expect(chapter3.startTime).toBe(10);
|
|
346
|
+
expect(chapter3.endTime).toBe(20);
|
|
347
|
+
|
|
348
|
+
const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
|
|
349
|
+
const absoluteUri2 = locationUri.resolve(partialUri2);
|
|
350
|
+
await player.addChaptersTrack(absoluteUri2.toString(), 'en');
|
|
351
|
+
|
|
352
|
+
const chaptersUpdated = player.getChapters('en');
|
|
353
|
+
expect(chaptersUpdated.length).toBe(6);
|
|
354
|
+
const chapterUpdated1 = chaptersUpdated[0];
|
|
355
|
+
expect(chapterUpdated1.title).toBe('Chapter 1');
|
|
356
|
+
expect(chapterUpdated1.startTime).toBe(0);
|
|
357
|
+
expect(chapterUpdated1.endTime).toBe(5);
|
|
358
|
+
const chapterUpdated2 = chaptersUpdated[1];
|
|
359
|
+
expect(chapterUpdated2.title).toBe('Chapter 2');
|
|
360
|
+
expect(chapterUpdated2.startTime).toBe(5);
|
|
361
|
+
expect(chapterUpdated2.endTime).toBe(10);
|
|
362
|
+
const chapterUpdated3 = chaptersUpdated[2];
|
|
363
|
+
expect(chapterUpdated3.title).toBe('Chapter 3');
|
|
364
|
+
expect(chapterUpdated3.startTime).toBe(10);
|
|
365
|
+
expect(chapterUpdated3.endTime).toBe(20);
|
|
366
|
+
const chapterUpdated4 = chaptersUpdated[3];
|
|
367
|
+
expect(chapterUpdated4.title).toBe('Chapter 4');
|
|
368
|
+
expect(chapterUpdated4.startTime).toBe(20);
|
|
369
|
+
expect(chapterUpdated4.endTime).toBe(30);
|
|
370
|
+
const chapterUpdated5 = chaptersUpdated[4];
|
|
371
|
+
expect(chapterUpdated5.title).toBe('Chapter 5');
|
|
372
|
+
expect(chapterUpdated5.startTime).toBe(30);
|
|
373
|
+
expect(chapterUpdated5.endTime).toBe(40);
|
|
374
|
+
const chapterUpdated6 = chaptersUpdated[5];
|
|
375
|
+
expect(chapterUpdated6.title).toBe('Chapter 6');
|
|
376
|
+
expect(chapterUpdated6.startTime).toBe(40);
|
|
377
|
+
expect(chapterUpdated6.endTime).toBe(61.349);
|
|
378
|
+
});
|
|
390
379
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
380
|
+
it('add external chapters in srt format', async () => {
|
|
381
|
+
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
|
|
382
|
+
|
|
383
|
+
const locationUri = new goog.Uri(location.href);
|
|
384
|
+
const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
|
|
385
|
+
const absoluteUri = locationUri.resolve(partialUri);
|
|
386
|
+
await player.addChaptersTrack(absoluteUri.toString(), 'es');
|
|
387
|
+
|
|
388
|
+
const chapters = player.getChapters('es');
|
|
389
|
+
expect(chapters.length).toBe(3);
|
|
390
|
+
const chapter1 = chapters[0];
|
|
391
|
+
expect(chapter1.title).toBe('Chapter 1');
|
|
392
|
+
expect(chapter1.startTime).toBe(0);
|
|
393
|
+
expect(chapter1.endTime).toBe(5);
|
|
394
|
+
const chapter2 = chapters[1];
|
|
395
|
+
expect(chapter2.title).toBe('Chapter 2');
|
|
396
|
+
expect(chapter2.startTime).toBe(5);
|
|
397
|
+
expect(chapter2.endTime).toBe(30);
|
|
398
|
+
const chapter3 = chapters[2];
|
|
399
|
+
expect(chapter3.title).toBe('Chapter 3');
|
|
400
|
+
expect(chapter3.startTime).toBe(30);
|
|
401
|
+
expect(chapter3.endTime).toBe(61.349);
|
|
402
|
+
});
|
|
403
|
+
}); // describe('addChaptersTrack')
|
|
406
404
|
|
|
407
405
|
// Since we are not in-charge of streaming, calling |retryStreaming| should
|
|
408
406
|
// have no effect.
|
package/test/player_unit.js
CHANGED
|
@@ -124,7 +124,13 @@ describe('Player', () => {
|
|
|
124
124
|
|
|
125
125
|
player.createDrmEngine = () => drmEngine;
|
|
126
126
|
player.createNetworkingEngine = () => networkingEngine;
|
|
127
|
-
player.createPlayhead = () =>
|
|
127
|
+
player.createPlayhead = (startTime) => {
|
|
128
|
+
const callableSetStartTime =
|
|
129
|
+
shaka.test.Util.spyFunc(playhead.setStartTime);
|
|
130
|
+
callableSetStartTime(startTime);
|
|
131
|
+
playhead.setStartTime.calls.reset();
|
|
132
|
+
return playhead;
|
|
133
|
+
};
|
|
128
134
|
player.createMediaSourceEngine = () => mediaSourceEngine;
|
|
129
135
|
player.createStreamingEngine = () => streamingEngine;
|
|
130
136
|
}
|
|
@@ -156,6 +162,7 @@ describe('Player', () => {
|
|
|
156
162
|
window.MediaSource.isTypeSupported = originalIsTypeSupported;
|
|
157
163
|
shaka.media.ManifestParser.unregisterParserByMime(fakeMimeType);
|
|
158
164
|
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
165
|
+
onError.calls.reset();
|
|
159
166
|
}
|
|
160
167
|
});
|
|
161
168
|
|
|
@@ -2658,6 +2665,51 @@ describe('Player', () => {
|
|
|
2658
2665
|
expect(activeVariant.id).toBe(1);
|
|
2659
2666
|
});
|
|
2660
2667
|
|
|
2668
|
+
// Regression test for https://github.com/shaka-project/shaka-player/issues/4190
|
|
2669
|
+
it('throws only one RESTRICTIONS_CANNOT_BE_MET error', async () => {
|
|
2670
|
+
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2671
|
+
manifest.addVariant(0, (variant) => {
|
|
2672
|
+
variant.addVideo(1, (stream) => {
|
|
2673
|
+
stream.keyIds = new Set(['abc']);
|
|
2674
|
+
});
|
|
2675
|
+
});
|
|
2676
|
+
});
|
|
2677
|
+
|
|
2678
|
+
// Check that error is RESTRICTIONS_CANNOT_BE_MET
|
|
2679
|
+
onError.and.callFake((e) => {
|
|
2680
|
+
const error = e.detail;
|
|
2681
|
+
shaka.test.Util.expectToEqualError(
|
|
2682
|
+
error,
|
|
2683
|
+
new shaka.util.Error(
|
|
2684
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
2685
|
+
shaka.util.Error.Category.MANIFEST,
|
|
2686
|
+
shaka.util.Error.Code.RESTRICTIONS_CANNOT_BE_MET, {
|
|
2687
|
+
hasAppRestrictions: false,
|
|
2688
|
+
missingKeys: ['abc'],
|
|
2689
|
+
restrictedKeyStatuses: [],
|
|
2690
|
+
}));
|
|
2691
|
+
});
|
|
2692
|
+
|
|
2693
|
+
await player.load(fakeManifestUri, 0, fakeMimeType);
|
|
2694
|
+
const activeVariant = getActiveVariantTrack();
|
|
2695
|
+
expect(activeVariant.id).toBe(0);
|
|
2696
|
+
|
|
2697
|
+
abrManager.chooseIndex = 0;
|
|
2698
|
+
abrManager.chooseVariant.calls.reset();
|
|
2699
|
+
|
|
2700
|
+
onKeyStatus({'abc': 'output-restricted'});
|
|
2701
|
+
|
|
2702
|
+
// Ensure that RESTRICTIONS_CANNOT_BE_MET is thrown once
|
|
2703
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
2704
|
+
|
|
2705
|
+
// It does not call chooseVariant
|
|
2706
|
+
expect(abrManager.chooseVariant).not.toHaveBeenCalled();
|
|
2707
|
+
|
|
2708
|
+
// The first variant is disallowed.
|
|
2709
|
+
expect(manifest.variants[0].id).toBe(0);
|
|
2710
|
+
expect(manifest.variants[0].allowedByKeySystem).toBe(false);
|
|
2711
|
+
});
|
|
2712
|
+
|
|
2661
2713
|
it('doesn\'t switch if the active stream isn\'t restricted', async () => {
|
|
2662
2714
|
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2663
2715
|
manifest.addVariant(0, (variant) => {
|
|
@@ -3664,6 +3716,51 @@ describe('Player', () => {
|
|
|
3664
3716
|
});
|
|
3665
3717
|
});
|
|
3666
3718
|
|
|
3719
|
+
describe('config streaming.startAtSegmentBoundary', () => {
|
|
3720
|
+
// Test for https://github.com/shaka-project/shaka-player/issues/4188
|
|
3721
|
+
// In that issue, using streaming.startAtSegmentBoundary in v4.0.0 caused
|
|
3722
|
+
// an exception due to the use of a removed method.
|
|
3723
|
+
it('adjusts start time', async () => {
|
|
3724
|
+
player.configure('streaming.startAtSegmentBoundary', true);
|
|
3725
|
+
|
|
3726
|
+
// In order to adjust the start time to a segment boundary, we need
|
|
3727
|
+
// segment descriptions in the segment index. So create a non-default
|
|
3728
|
+
// fake manifest.
|
|
3729
|
+
const timeline = new shaka.media.PresentationTimeline(300, 0);
|
|
3730
|
+
timeline.setStatic(true);
|
|
3731
|
+
// This duration is used by useSegmentTemplate below to decide how many
|
|
3732
|
+
// references to generate.
|
|
3733
|
+
timeline.setDuration(300);
|
|
3734
|
+
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3735
|
+
manifest.presentationTimeline = timeline;
|
|
3736
|
+
manifest.addVariant(0, (variant) => {
|
|
3737
|
+
variant.addVideo(1, (stream) => {
|
|
3738
|
+
stream.useSegmentTemplate(
|
|
3739
|
+
'$Number$.mp4', /* segmentDuration= */ 10);
|
|
3740
|
+
});
|
|
3741
|
+
});
|
|
3742
|
+
});
|
|
3743
|
+
|
|
3744
|
+
await player.load(fakeManifestUri, 12, fakeMimeType);
|
|
3745
|
+
|
|
3746
|
+
expect(playhead.setStartTime).toHaveBeenCalledWith(10);
|
|
3747
|
+
});
|
|
3748
|
+
|
|
3749
|
+
it('does not fail with no segments', async () => {
|
|
3750
|
+
player.configure('streaming.startAtSegmentBoundary', true);
|
|
3751
|
+
|
|
3752
|
+
// Without useSegmentTemplate in the fake manifest, the call to
|
|
3753
|
+
// getIteratorForTime() in Player produces a null iterator. Using the
|
|
3754
|
+
// default fake manifest should cover that case. But just to make sure,
|
|
3755
|
+
// verify that we have no segments before calling load().
|
|
3756
|
+
const variant = manifest.variants[0];
|
|
3757
|
+
expect(variant.video.segmentIndex.getIteratorForTime(0)).toBeNull();
|
|
3758
|
+
expect(variant.audio.segmentIndex.getIteratorForTime(0)).toBeNull();
|
|
3759
|
+
|
|
3760
|
+
await player.load(fakeManifestUri, 0, fakeMimeType);
|
|
3761
|
+
});
|
|
3762
|
+
});
|
|
3763
|
+
|
|
3667
3764
|
/**
|
|
3668
3765
|
* Gets the currently active variant track.
|
|
3669
3766
|
* @return {shaka.extern.Track}
|
package/test/test/boot.js
CHANGED
|
@@ -242,6 +242,15 @@ window.xfilterDescribe = (describeName, cond, describeBody) => {
|
|
|
242
242
|
window['describe'] = oldDescribe;
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
/**
|
|
246
|
+
* A very short alias for filtering offline integration tests.
|
|
247
|
+
*
|
|
248
|
+
* @return {boolean}
|
|
249
|
+
*/
|
|
250
|
+
window.offlineSupported = () => {
|
|
251
|
+
return shaka.offline.StorageMuxer.support();
|
|
252
|
+
};
|
|
253
|
+
|
|
245
254
|
/**
|
|
246
255
|
* Load node modules used in testing and install them into window.
|
|
247
256
|
* @return {!Promise}
|
|
@@ -152,9 +152,27 @@ shaka.test.FakeVideo = class {
|
|
|
152
152
|
constructor(currentTime) {
|
|
153
153
|
/** @const {!Object.<string, !Function>} */
|
|
154
154
|
this.on = {}; // event listeners
|
|
155
|
+
|
|
155
156
|
/** @type {!Array.<!TextTrack>} */
|
|
156
157
|
this.textTracks = [];
|
|
157
158
|
|
|
159
|
+
// In a real video element, textTracks is an event target.
|
|
160
|
+
// Since Player listens to events on textTracks, we need to fake that
|
|
161
|
+
// interface.
|
|
162
|
+
this.textTracksEventTarget = new shaka.util.FakeEventTarget();
|
|
163
|
+
this.textTracks.addEventListener =
|
|
164
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
165
|
+
this.textTracksEventTarget.addEventListener.bind(
|
|
166
|
+
this.textTracksEventTarget);
|
|
167
|
+
this.textTracks.removeEventListener =
|
|
168
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
169
|
+
this.textTracksEventTarget.removeEventListener.bind(
|
|
170
|
+
this.textTracksEventTarget);
|
|
171
|
+
this.textTracks.dispatchEvent =
|
|
172
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
173
|
+
this.textTracksEventTarget.dispatchEvent.bind(
|
|
174
|
+
this.textTracksEventTarget);
|
|
175
|
+
|
|
158
176
|
this.currentTime = currentTime || 0;
|
|
159
177
|
this.readyState = 0;
|
|
160
178
|
this.playbackRate = 1;
|
|
@@ -173,6 +191,10 @@ shaka.test.FakeVideo = class {
|
|
|
173
191
|
jasmine.createSpy('addTextTrack').and.callFake((kind, id) => {
|
|
174
192
|
const track = new shaka.test.FakeTextTrack();
|
|
175
193
|
this.textTracks.push(track);
|
|
194
|
+
|
|
195
|
+
const trackEvent = new shaka.util.FakeEvent('addtrack', {track});
|
|
196
|
+
this.textTracksEventTarget.dispatchEvent(trackEvent);
|
|
197
|
+
|
|
176
198
|
return track;
|
|
177
199
|
});
|
|
178
200
|
|
|
@@ -308,11 +330,18 @@ shaka.test.FakePlayhead = class {
|
|
|
308
330
|
/** @type {!jasmine.Spy} */
|
|
309
331
|
this.setRebufferingGoal = jasmine.createSpy('setRebufferingGoal');
|
|
310
332
|
|
|
333
|
+
/** @private {number} */
|
|
334
|
+
this.startTime_ = 0;
|
|
335
|
+
|
|
311
336
|
/** @type {!jasmine.Spy} */
|
|
312
|
-
this.setStartTime = jasmine.createSpy('setStartTime')
|
|
337
|
+
this.setStartTime = jasmine.createSpy('setStartTime')
|
|
338
|
+
.and.callFake((value) => {
|
|
339
|
+
this.startTime_ = value;
|
|
340
|
+
});
|
|
313
341
|
|
|
314
342
|
/** @type {!jasmine.Spy} */
|
|
315
|
-
this.getTime = jasmine.createSpy('getTime')
|
|
343
|
+
this.getTime = jasmine.createSpy('getTime')
|
|
344
|
+
.and.callFake(() => this.startTime_);
|
|
316
345
|
|
|
317
346
|
/** @type {!jasmine.Spy} */
|
|
318
347
|
this.setBuffering = jasmine.createSpy('setBuffering');
|