shaka-player 4.3.12 → 4.3.14
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/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +7 -0
- package/.github/workflows/release-please.yaml +2 -27
- package/AUTHORS +5 -0
- package/CHANGELOG.md +108 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +6 -0
- package/README.md +2 -0
- package/demo/common/asset.js +5 -1
- package/demo/common/assets.js +16 -2
- package/demo/common/message_ids.js +1 -0
- package/demo/custom.js +2 -0
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/demo/main.js +1 -1
- package/dist/demo.compiled.debug.js +56 -54
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +56 -54
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +6 -6
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +24 -22
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +24 -22
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +26 -6
- package/dist/shaka-player.compiled.debug.d.ts +26 -6
- package/dist/shaka-player.compiled.debug.externs.js +79 -49
- package/dist/shaka-player.compiled.debug.js +426 -410
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +79 -49
- package/dist/shaka-player.compiled.js +912 -901
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +38 -7
- package/dist/shaka-player.ui.debug.d.ts +38 -7
- package/dist/shaka-player.ui.debug.externs.js +80 -49
- package/dist/shaka-player.ui.debug.js +460 -442
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +80 -49
- package/dist/shaka-player.ui.js +1060 -1049
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/offline.md +1 -1
- package/docs/tutorials/plugins.md +4 -0
- package/docs/tutorials/text-displayer.md +15 -0
- package/docs/tutorials/ui-customization.md +29 -0
- package/docs/tutorials/ui.md +4 -1
- package/externs/lcevc.js +6 -0
- package/externs/shaka/manifest_parser.js +8 -0
- package/externs/shaka/player.js +1 -1
- package/externs/shaka/text.js +3 -1
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/client_side_ad.js +9 -0
- package/lib/ads/client_side_ad_manager.js +7 -2
- package/lib/ads/server_side_ad_manager.js +4 -0
- package/lib/cast/cast_proxy.js +18 -0
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/cea_decoder.js +11 -1
- package/lib/dash/dash_parser.js +79 -42
- package/lib/dash/mpd_utils.js +39 -27
- package/lib/hls/hls_parser.js +148 -49
- package/lib/hls/hls_utils.js +5 -5
- package/lib/media/content_workarounds.js +30 -0
- package/lib/media/drm_engine.js +16 -12
- package/lib/media/media_source_engine.js +4 -2
- package/lib/media/play_rate_controller.js +1 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_reference.js +16 -7
- package/lib/media/streaming_engine.js +64 -54
- package/lib/media/transmuxer.js +1 -1
- package/lib/media/video_wrapper.js +4 -3
- package/lib/offline/offline_manifest_parser.js +5 -0
- package/lib/offline/session_deleter.js +0 -8
- package/lib/offline/storage.js +4 -1
- package/lib/player.js +79 -18
- package/lib/polyfill/media_capabilities.js +23 -3
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/simple_text_displayer.js +7 -6
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +33 -5
- package/lib/text/ui_text_displayer.js +81 -15
- package/lib/text/vtt_text_parser.js +13 -0
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/cmcd_manager.js +16 -1
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/manifest_parser_utils.js +20 -8
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +91 -36
- package/lib/util/platform.js +39 -2
- package/lib/util/player_configuration.js +5 -3
- package/lib/util/stream_utils.js +36 -28
- package/lib/util/ts_parser.js +3 -3
- package/lib/util/xml_utils.js +5 -0
- package/package.json +2 -2
- package/test/cea/cea_decoder_unit.js +40 -0
- package/test/dash/dash_parser_live_unit.js +5 -8
- package/test/dash/dash_parser_manifest_unit.js +5 -0
- package/test/dash/dash_parser_segment_base_unit.js +5 -0
- package/test/dash/dash_parser_segment_list_unit.js +21 -17
- package/test/dash/dash_parser_segment_template_unit.js +5 -0
- package/test/hls/hls_live_unit.js +33 -0
- package/test/hls/hls_parser_unit.js +52 -60
- package/test/media/buffering_observer_unit.js +13 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +30 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/transmuxer_integration.js +2 -2
- package/test/offline/storage_integration.js +3 -0
- package/test/player_integration.js +40 -40
- package/test/player_unit.js +31 -0
- package/test/polyfill/media_capabilities_unit.js +12 -6
- package/test/test/util/dash_parser_util.js +16 -7
- package/test/test/util/fake_drm_engine.js +7 -0
- package/test/test/util/layout_tests.js +2 -1
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/test_scheme.js +3 -0
- package/test/test/util/waiter.js +5 -0
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/simple_text_displayer_unit.js +1 -1
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +49 -18
- package/test/text/ui_text_displayer_unit.js +9 -0
- package/test/text/vtt_text_parser_unit.js +2 -17
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/periods_unit.js +113 -0
- package/test/util/ts_parser_unit.js +1 -1
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/externs/ui.js +24 -0
- package/ui/language_utils.js +11 -5
- package/ui/play_button.js +20 -0
- package/ui/playback_rate_selection.js +4 -0
- package/ui/presentation_time.js +5 -5
- package/ui/small_play_button.js +2 -2
- package/ui/volume_bar.js +10 -6
- /package/.github/{config.yml → ISSUE_TEMPLATE/config.yml} +0 -0
package/lib/util/platform.js
CHANGED
|
@@ -199,6 +199,15 @@ shaka.util.Platform = class {
|
|
|
199
199
|
!shaka.util.Platform.isEdge();
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Check if the current platform is Firefox.
|
|
204
|
+
*
|
|
205
|
+
* @return {boolean}
|
|
206
|
+
*/
|
|
207
|
+
static isFirefox() {
|
|
208
|
+
return shaka.util.Platform.userAgentContains_('Firefox');
|
|
209
|
+
}
|
|
210
|
+
|
|
202
211
|
/**
|
|
203
212
|
* Check if the current platform is from Apple.
|
|
204
213
|
*
|
|
@@ -213,10 +222,12 @@ shaka.util.Platform = class {
|
|
|
213
222
|
return !!navigator.vendor && navigator.vendor.includes('Apple') &&
|
|
214
223
|
!shaka.util.Platform.isTizen() &&
|
|
215
224
|
!shaka.util.Platform.isEOS() &&
|
|
225
|
+
!shaka.util.Platform.isAPL() &&
|
|
216
226
|
!shaka.util.Platform.isVirginMedia() &&
|
|
217
227
|
!shaka.util.Platform.isOrange() &&
|
|
218
228
|
!shaka.util.Platform.isPS4() &&
|
|
219
|
-
!shaka.util.Platform.isAmazonFireTV()
|
|
229
|
+
!shaka.util.Platform.isAmazonFireTV() &&
|
|
230
|
+
!shaka.util.Platform.isWPE();
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
/**
|
|
@@ -271,6 +282,14 @@ shaka.util.Platform = class {
|
|
|
271
282
|
return shaka.util.Platform.userAgentContains_('AFT');
|
|
272
283
|
}
|
|
273
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Check if the current platform is Comcast X1.
|
|
287
|
+
* @return {boolean}
|
|
288
|
+
*/
|
|
289
|
+
static isWPE() {
|
|
290
|
+
return shaka.util.Platform.userAgentContains_('WPE');
|
|
291
|
+
}
|
|
292
|
+
|
|
274
293
|
/**
|
|
275
294
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
276
295
|
*
|
|
@@ -327,6 +346,15 @@ shaka.util.Platform = class {
|
|
|
327
346
|
return shaka.util.Platform.userAgentContains_('PC=EOS');
|
|
328
347
|
}
|
|
329
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Check if the current platform is an APL set-top box.
|
|
351
|
+
*
|
|
352
|
+
* @return {boolean}
|
|
353
|
+
*/
|
|
354
|
+
static isAPL() {
|
|
355
|
+
return shaka.util.Platform.userAgentContains_('PC=APL');
|
|
356
|
+
}
|
|
357
|
+
|
|
330
358
|
/**
|
|
331
359
|
* Guesses if the platform is a mobile one (iOS or Android).
|
|
332
360
|
*
|
|
@@ -368,6 +396,15 @@ shaka.util.Platform = class {
|
|
|
368
396
|
return navigator.platform.toLowerCase().includes('mac');
|
|
369
397
|
}
|
|
370
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Return true if the platform is a Android, regardless of the browser.
|
|
401
|
+
*
|
|
402
|
+
* @return {boolean}
|
|
403
|
+
*/
|
|
404
|
+
static isAndroid() {
|
|
405
|
+
return shaka.util.Platform.userAgentContains_('Android');
|
|
406
|
+
}
|
|
407
|
+
|
|
371
408
|
/**
|
|
372
409
|
* Check if the user agent contains a key. This is the best way we know of
|
|
373
410
|
* right now to detect platforms. If there is a better way, please send a
|
|
@@ -429,7 +466,7 @@ shaka.util.Platform = class {
|
|
|
429
466
|
*/
|
|
430
467
|
static requiresEncryptionInfoInAllInitSegments() {
|
|
431
468
|
const Platform = shaka.util.Platform;
|
|
432
|
-
return Platform.isTizen() || Platform.isXboxOne();
|
|
469
|
+
return Platform.isTizen() || Platform.isXboxOne() || Platform.isOrange();
|
|
433
470
|
}
|
|
434
471
|
|
|
435
472
|
/**
|
|
@@ -189,13 +189,15 @@ shaka.util.PlayerConfiguration = class {
|
|
|
189
189
|
parsePrftBox: false,
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
-
// WebOS, Tizen, and
|
|
193
|
-
// slowly to seeking.
|
|
192
|
+
// WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
|
|
193
|
+
// that respond slowly to seeking.
|
|
194
|
+
// Therefore we should not seek when we detect a stall
|
|
194
195
|
// on one of these platforms. Instead, default stallSkip to 0 to force the
|
|
195
196
|
// stall detector to pause and play instead.
|
|
196
197
|
if (shaka.util.Platform.isWebOS() ||
|
|
197
198
|
shaka.util.Platform.isTizen() ||
|
|
198
|
-
shaka.util.Platform.isChromecast()
|
|
199
|
+
shaka.util.Platform.isChromecast() ||
|
|
200
|
+
shaka.util.Platform.isHisense()) {
|
|
199
201
|
streaming.stallSkip = 0;
|
|
200
202
|
}
|
|
201
203
|
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -434,19 +434,36 @@ shaka.util.StreamUtils = class {
|
|
|
434
434
|
|
|
435
435
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(
|
|
436
436
|
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
|
|
437
|
+
|
|
438
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
439
|
+
const Capabilities = shaka.media.Capabilities;
|
|
440
|
+
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
441
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
442
|
+
const StreamUtils = shaka.util.StreamUtils;
|
|
443
|
+
const isXboxOne = shaka.util.Platform.isXboxOne();
|
|
444
|
+
|
|
445
|
+
const isFirefoxAndroid = shaka.util.Platform.isFirefox() &&
|
|
446
|
+
shaka.util.Platform.isAndroid();
|
|
447
|
+
|
|
437
448
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
438
449
|
// See: https://github.com/shaka-project/shaka-player/issues/3860
|
|
439
450
|
const video = variant.video;
|
|
451
|
+
const videoWidth = (video && video.width) || 0;
|
|
452
|
+
const videoHeight = (video && video.height) || 0;
|
|
440
453
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
454
|
+
// See: https://github.com/shaka-project/shaka-player/issues/3380
|
|
455
|
+
// Note: it makes sense to drop early
|
|
456
|
+
if (isXboxOne && video &&
|
|
457
|
+
(videoWidth > 1920 || videoHeight > 1080) &&
|
|
458
|
+
(video.codecs.includes('avc1.') || video.codecs.includes('avc3.'))
|
|
459
|
+
) {
|
|
460
|
+
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
461
|
+
StreamUtils.getVariantSummaryString_(variant));
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
446
464
|
|
|
447
465
|
if (video) {
|
|
448
466
|
let videoCodecs = StreamUtils.getCorrectVideoCodecs_(video.codecs);
|
|
449
|
-
|
|
450
467
|
// For multiplexed streams. Here we must check the audio of the
|
|
451
468
|
// stream to see if it is compatible.
|
|
452
469
|
if (video.codecs.includes(',')) {
|
|
@@ -455,7 +472,6 @@ shaka.util.StreamUtils = class {
|
|
|
455
472
|
videoCodecs = ManifestParserUtils.guessCodecs(
|
|
456
473
|
ContentType.VIDEO, allCodecs);
|
|
457
474
|
videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
458
|
-
|
|
459
475
|
let audioCodecs = ManifestParserUtils.guessCodecs(
|
|
460
476
|
ContentType.AUDIO, allCodecs);
|
|
461
477
|
audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
@@ -484,6 +500,17 @@ shaka.util.StreamUtils = class {
|
|
|
484
500
|
|
|
485
501
|
const audio = variant.audio;
|
|
486
502
|
|
|
503
|
+
// See: https://github.com/shaka-project/shaka-player/issues/6111
|
|
504
|
+
// It seems that Firefox Android reports that it supports
|
|
505
|
+
// Opus + Widevine, but it is not actually supported.
|
|
506
|
+
// It makes sense to drop early.
|
|
507
|
+
if (isFirefoxAndroid && audio && audio.encrypted &&
|
|
508
|
+
audio.codecs.toLowerCase().includes('opus')) {
|
|
509
|
+
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
510
|
+
StreamUtils.getVariantSummaryString_(variant));
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
487
514
|
if (audio) {
|
|
488
515
|
const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
489
516
|
const fullType = MimeUtils.getFullOrConvertedType(
|
|
@@ -497,17 +524,6 @@ shaka.util.StreamUtils = class {
|
|
|
497
524
|
audio.codecs = codecs;
|
|
498
525
|
}
|
|
499
526
|
|
|
500
|
-
// See: https://github.com/shaka-project/shaka-player/issues/3380
|
|
501
|
-
if (shaka.util.Platform.isXboxOne() && video &&
|
|
502
|
-
((video.width && video.width > 1920) ||
|
|
503
|
-
(video.height && video.height > 1080)) &&
|
|
504
|
-
(video.codecs.includes('avc1.') ||
|
|
505
|
-
video.codecs.includes('avc3.'))) {
|
|
506
|
-
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
507
|
-
StreamUtils.getVariantSummaryString_(variant));
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
527
|
const supported = variant.decodingInfos.some((decodingInfo) => {
|
|
512
528
|
return decodingInfo.supported;
|
|
513
529
|
});
|
|
@@ -790,7 +806,7 @@ shaka.util.StreamUtils = class {
|
|
|
790
806
|
// currently don't support 'fLaC', while 'flac' is supported by most
|
|
791
807
|
// major browsers.
|
|
792
808
|
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
|
793
|
-
if (codecs === 'fLaC') {
|
|
809
|
+
if (codecs === 'fLaC' && !shaka.util.Platform.isSafari()) {
|
|
794
810
|
return 'flac';
|
|
795
811
|
}
|
|
796
812
|
|
|
@@ -799,15 +815,7 @@ shaka.util.StreamUtils = class {
|
|
|
799
815
|
return 'opus';
|
|
800
816
|
}
|
|
801
817
|
|
|
802
|
-
|
|
803
|
-
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
804
|
-
// See https://github.com/shaka-project/shaka-player/issues/2989 for
|
|
805
|
-
// details.
|
|
806
|
-
if (shaka.util.Platform.isTizen()) {
|
|
807
|
-
return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
|
|
808
|
-
} else {
|
|
809
|
-
return codecs;
|
|
810
|
-
}
|
|
818
|
+
return codecs;
|
|
811
819
|
}
|
|
812
820
|
|
|
813
821
|
|
package/lib/util/ts_parser.js
CHANGED
|
@@ -122,15 +122,15 @@ shaka.util.TsParser = class {
|
|
|
122
122
|
// for example
|
|
123
123
|
// NOTE this is only the PID of the track as found in TS,
|
|
124
124
|
// but we are not using this for MP4 track IDs.
|
|
125
|
-
if (
|
|
125
|
+
if (parsedPIDs.video != -1) {
|
|
126
126
|
this.videoPid_ = parsedPIDs.video;
|
|
127
127
|
this.videoCodec_ = parsedPIDs.videoCodec;
|
|
128
128
|
}
|
|
129
|
-
if (
|
|
129
|
+
if (parsedPIDs.audio != -1) {
|
|
130
130
|
this.audioPid_ = parsedPIDs.audio;
|
|
131
131
|
this.audioCodec_ = parsedPIDs.audioCodec;
|
|
132
132
|
}
|
|
133
|
-
if (
|
|
133
|
+
if (parsedPIDs.id3 != -1) {
|
|
134
134
|
this.id3Pid_ = parsedPIDs.id3;
|
|
135
135
|
}
|
|
136
136
|
|
package/lib/util/xml_utils.js
CHANGED
|
@@ -384,6 +384,11 @@ shaka.util.XmlUtils = class {
|
|
|
384
384
|
return null;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
// Cobalt browser doesn't support document.createNodeIterator.
|
|
388
|
+
if (!('createNodeIterator' in document)) {
|
|
389
|
+
return rootElem;
|
|
390
|
+
}
|
|
391
|
+
|
|
387
392
|
// SECURITY: Verify that the document does not contain elements from the
|
|
388
393
|
// HTML or SVG namespaces, which could trigger script execution and XSS.
|
|
389
394
|
const iterator = document.createNodeIterator(
|
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.14",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"karma-coverage": "^2.2.0",
|
|
43
43
|
"karma-jasmine": "^4.0.1",
|
|
44
44
|
"karma-jasmine-ajax": "^0.1.13",
|
|
45
|
-
"karma-local-wd-launcher": "^1.6.
|
|
45
|
+
"karma-local-wd-launcher": "^1.6.7",
|
|
46
46
|
"karma-opera-launcher": "^1.0.0",
|
|
47
47
|
"karma-sourcemap-loader": "^0.3.8",
|
|
48
48
|
"karma-spec-reporter": "^0.0.34",
|
|
@@ -44,6 +44,39 @@ describe('CeaDecoder', () => {
|
|
|
44
44
|
decoder.clear();
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
it('painton captions on CC4', () => {
|
|
48
|
+
const controlCount = 0x03;
|
|
49
|
+
const captionData = 0xc0 | controlCount;
|
|
50
|
+
const paintonCaptionCC4Packet = new Uint8Array([
|
|
51
|
+
...atscCaptionInitBytes, captionData, /* padding= */ 0xff,
|
|
52
|
+
0xfd, 0x9d, 0x29, // Paint-on mode (RDC control code).
|
|
53
|
+
0xfd, 0xf4, 0xe5, // t, e
|
|
54
|
+
0xfd, 0x73, 0xf4, // s, t
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const startTimeCaption1 = 1;
|
|
58
|
+
const startTimeCaption2 = 2;
|
|
59
|
+
const expectedText = 'test';
|
|
60
|
+
|
|
61
|
+
const topLevelCue = new shaka.text.Cue(startTimeCaption1,
|
|
62
|
+
startTimeCaption2, '');
|
|
63
|
+
topLevelCue.nestedCues = [
|
|
64
|
+
CeaUtils.createDefaultCue(
|
|
65
|
+
startTimeCaption1, startTimeCaption2, expectedText),
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const expectedCaptions = [{
|
|
69
|
+
stream: 'CC4',
|
|
70
|
+
cue: topLevelCue,
|
|
71
|
+
}];
|
|
72
|
+
|
|
73
|
+
decoder.extract(paintonCaptionCC4Packet, startTimeCaption1);
|
|
74
|
+
decoder.extract(eraseDisplayedMemory, startTimeCaption2);
|
|
75
|
+
const captions = decoder.decode();
|
|
76
|
+
|
|
77
|
+
expect(captions).toEqual(expectedCaptions);
|
|
78
|
+
});
|
|
79
|
+
|
|
47
80
|
it('green and underlined popon caption data on CC3', () => {
|
|
48
81
|
const controlCount = 0x08;
|
|
49
82
|
const captionData = 0xc0 | controlCount;
|
|
@@ -486,6 +519,13 @@ describe('CeaDecoder', () => {
|
|
|
486
519
|
|
|
487
520
|
expect(decoder.reset).toHaveBeenCalledTimes(1);
|
|
488
521
|
});
|
|
522
|
+
|
|
523
|
+
it('does not attempt to extract SEI packet that is too short', () => {
|
|
524
|
+
const badData = new Uint8Array([0xb5]);
|
|
525
|
+
decoder.extract(badData, 0);
|
|
526
|
+
const captions = decoder.decode();
|
|
527
|
+
expect(captions.length).toBe(0);
|
|
528
|
+
});
|
|
489
529
|
});
|
|
490
530
|
|
|
491
531
|
describe('decodes CEA-708', () => {
|
|
@@ -607,12 +607,12 @@ describe('DashParser Live', () => {
|
|
|
607
607
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
608
608
|
});
|
|
609
609
|
|
|
610
|
-
it('
|
|
610
|
+
it('still updates when @minimumUpdatePeriod is zero', async () => {
|
|
611
611
|
const lines = [
|
|
612
612
|
'<SegmentTemplate startNumber="1" media="s$Number$.mp4" duration="2" />',
|
|
613
613
|
];
|
|
614
614
|
// updateTime parameter sets @minimumUpdatePeriod in the manifest.
|
|
615
|
-
const manifestText = makeSimpleLiveManifestText(lines, updateTime);
|
|
615
|
+
const manifestText = makeSimpleLiveManifestText(lines, /* updateTime= */ 0);
|
|
616
616
|
|
|
617
617
|
/** @type {!jasmine.Spy} */
|
|
618
618
|
const tickAfter = updateTickSpy();
|
|
@@ -622,16 +622,14 @@ describe('DashParser Live', () => {
|
|
|
622
622
|
await parser.start('dummy://foo', playerInterface);
|
|
623
623
|
|
|
624
624
|
expect(tickAfter).toHaveBeenCalledTimes(1);
|
|
625
|
-
const delay = tickAfter.calls.mostRecent().args[0];
|
|
626
|
-
expect(delay).toBe(updateTime);
|
|
627
625
|
});
|
|
628
626
|
|
|
629
|
-
it('
|
|
627
|
+
it('uses @minimumUpdatePeriod', async () => {
|
|
630
628
|
const lines = [
|
|
631
629
|
'<SegmentTemplate startNumber="1" media="s$Number$.mp4" duration="2" />',
|
|
632
630
|
];
|
|
633
631
|
// updateTime parameter sets @minimumUpdatePeriod in the manifest.
|
|
634
|
-
const manifestText = makeSimpleLiveManifestText(lines,
|
|
632
|
+
const manifestText = makeSimpleLiveManifestText(lines, updateTime);
|
|
635
633
|
|
|
636
634
|
/** @type {!jasmine.Spy} */
|
|
637
635
|
const tickAfter = updateTickSpy();
|
|
@@ -640,10 +638,9 @@ describe('DashParser Live', () => {
|
|
|
640
638
|
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
641
639
|
await parser.start('dummy://foo', playerInterface);
|
|
642
640
|
|
|
643
|
-
const waitTime = shaka.dash.DashParser['MIN_UPDATE_PERIOD_'];
|
|
644
641
|
expect(tickAfter).toHaveBeenCalledTimes(1);
|
|
645
642
|
const delay = tickAfter.calls.mostRecent().args[0];
|
|
646
|
-
expect(delay).toBe(
|
|
643
|
+
expect(delay).toBe(updateTime);
|
|
647
644
|
});
|
|
648
645
|
|
|
649
646
|
it('does not update when @minimumUpdatePeriod is missing', async () => {
|
|
@@ -43,6 +43,11 @@ describe('DashParser SegmentBase', () => {
|
|
|
43
43
|
};
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
// Dash parser stop is synchronous.
|
|
48
|
+
parser.stop();
|
|
49
|
+
});
|
|
50
|
+
|
|
46
51
|
it('requests init data for WebM', async () => {
|
|
47
52
|
const source = [
|
|
48
53
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
@@ -351,24 +351,28 @@ describe('DashParser SegmentList', () => {
|
|
|
351
351
|
updateDuration: () => {},
|
|
352
352
|
newDrmInfo: (stream) => {},
|
|
353
353
|
};
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
354
|
+
try {
|
|
355
|
+
const manifest = await dashParser.start('dummy://foo', playerInterface);
|
|
356
|
+
const stream = manifest.variants[0].video;
|
|
357
|
+
await stream.createSegmentIndex();
|
|
358
|
+
goog.asserts.assert(stream.segmentIndex, 'Expected index to be created');
|
|
358
359
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
360
|
+
// Don't use Dash.testSegmentIndex since it uses SegmentIndex.find, which
|
|
361
|
+
// doesn't reproduce this issue. We want to use Array.from which uses
|
|
362
|
+
// the iterator.
|
|
363
|
+
const expected = [
|
|
364
|
+
ManifestParser.makeReference('s1.mp4', 0, 10, baseUri),
|
|
365
|
+
ManifestParser.makeReference('s2.mp4', 10, 20, baseUri),
|
|
366
|
+
ManifestParser.makeReference('s3.mp4', 20, 30, baseUri),
|
|
366
367
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
368
|
+
ManifestParser.makeReference('s3.mp4', 30, 40, baseUri),
|
|
369
|
+
ManifestParser.makeReference('s4.mp4', 40, 50, baseUri),
|
|
370
|
+
ManifestParser.makeReference('s5.mp4', 50, 60, baseUri),
|
|
371
|
+
];
|
|
372
|
+
const actual = Array.from(stream.segmentIndex);
|
|
373
|
+
expect(actual).toEqual(expected);
|
|
374
|
+
} finally {
|
|
375
|
+
dashParser.stop();
|
|
376
|
+
}
|
|
373
377
|
});
|
|
374
378
|
});
|
|
@@ -501,10 +501,43 @@ describe('HlsParser live', () => {
|
|
|
501
501
|
|
|
502
502
|
it('sets 3 times target duration as presentation delay if not configured',
|
|
503
503
|
async () => {
|
|
504
|
+
const media = [
|
|
505
|
+
'#EXTM3U\n',
|
|
506
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
507
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
508
|
+
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
509
|
+
'#EXTINF:2,\n',
|
|
510
|
+
'main.mp4\n',
|
|
511
|
+
'#EXTINF:2,\n',
|
|
512
|
+
'main.mp4\n',
|
|
513
|
+
'#EXTINF:2,\n',
|
|
514
|
+
'main.mp4\n',
|
|
515
|
+
'#EXTINF:2,\n',
|
|
516
|
+
'main.mp4\n',
|
|
517
|
+
'#EXTINF:2,\n',
|
|
518
|
+
'main.mp4\n',
|
|
519
|
+
'#EXTINF:2,\n',
|
|
520
|
+
'main.mp4\n',
|
|
521
|
+
].join('');
|
|
504
522
|
const manifest = await testInitialManifest(master, media);
|
|
505
523
|
expect(manifest.presentationTimeline.getDelay()).toBe(15);
|
|
506
524
|
});
|
|
507
525
|
|
|
526
|
+
it('sets 1 times target duration as presentation delay if there are not enough segments', async () => { // eslint-disable-line max-len
|
|
527
|
+
const media = [
|
|
528
|
+
'#EXTM3U\n',
|
|
529
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
530
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
531
|
+
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
532
|
+
'#EXTINF:2,\n',
|
|
533
|
+
'main.mp4\n',
|
|
534
|
+
'#EXTINF:2,\n',
|
|
535
|
+
'main.mp4\n',
|
|
536
|
+
].join('');
|
|
537
|
+
const manifest = await testInitialManifest(master, media);
|
|
538
|
+
expect(manifest.presentationTimeline.getDelay()).toBe(5);
|
|
539
|
+
});
|
|
540
|
+
|
|
508
541
|
it('sets presentation delay for low latency mode', async () => {
|
|
509
542
|
const mediaWithLowLatency = [
|
|
510
543
|
'#EXTM3U\n',
|
|
@@ -739,6 +739,45 @@ describe('HlsParser', () => {
|
|
|
739
739
|
await testHlsParser(master, media, manifest);
|
|
740
740
|
});
|
|
741
741
|
|
|
742
|
+
it('parses audio+video variant with global closed captions', async () => {
|
|
743
|
+
const master = [
|
|
744
|
+
'#EXTM3U\n',
|
|
745
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",CHANNELS="2",',
|
|
746
|
+
'URI="audio"\n',
|
|
747
|
+
'#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="cap1",LANGUAGE="eng",',
|
|
748
|
+
'INSTREAM-ID="CC1"\n',
|
|
749
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
750
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
751
|
+
'video\n',
|
|
752
|
+
].join('');
|
|
753
|
+
|
|
754
|
+
const media = [
|
|
755
|
+
'#EXTM3U\n',
|
|
756
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
757
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
758
|
+
'#EXTINF:5,\n',
|
|
759
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
760
|
+
'main.mp4',
|
|
761
|
+
].join('');
|
|
762
|
+
|
|
763
|
+
const closedCaptions = new Map([['CC1', 'en']]);
|
|
764
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
765
|
+
manifest.anyTimeline();
|
|
766
|
+
manifest.addPartialVariant((variant) => {
|
|
767
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
768
|
+
stream.closedCaptions = closedCaptions;
|
|
769
|
+
stream.mime('video/mp4', 'avc1');
|
|
770
|
+
});
|
|
771
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
772
|
+
stream.mime('audio/mp4', 'mp4a');
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
manifest.sequenceMode = true;
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
await testHlsParser(master, media, manifest);
|
|
779
|
+
});
|
|
780
|
+
|
|
742
781
|
it('parses audio+video variant with no closed captions', async () => {
|
|
743
782
|
const master = [
|
|
744
783
|
'#EXTM3U\n',
|
|
@@ -1114,63 +1153,6 @@ describe('HlsParser', () => {
|
|
|
1114
1153
|
expect(references[6].discontinuitySequence).toBe(3);
|
|
1115
1154
|
});
|
|
1116
1155
|
|
|
1117
|
-
it('sets reference timetampOffset based on discontinuity start time',
|
|
1118
|
-
async () => {
|
|
1119
|
-
const master = [
|
|
1120
|
-
'#EXTM3U\n',
|
|
1121
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=2000000,CODECS="avc1"\n',
|
|
1122
|
-
'video\n',
|
|
1123
|
-
].join('');
|
|
1124
|
-
|
|
1125
|
-
const media = [
|
|
1126
|
-
'#EXTM3U\n',
|
|
1127
|
-
'#EXT-X-VERSION:3\n',
|
|
1128
|
-
'#EXT-X-TARGETDURATION:5\n',
|
|
1129
|
-
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
1130
|
-
'#EXTINF:3,\n',
|
|
1131
|
-
'clip0-video-0.ts\n',
|
|
1132
|
-
'#EXTINF:1,\n',
|
|
1133
|
-
'clip0-video-1.ts\n',
|
|
1134
|
-
'#EXT-X-DISCONTINUITY\n',
|
|
1135
|
-
'#EXTINF:2,\n',
|
|
1136
|
-
'clip1-video-1.ts\n',
|
|
1137
|
-
'#EXTINF:3,\n',
|
|
1138
|
-
'clip1-video-2.ts\n',
|
|
1139
|
-
'#EXT-X-DISCONTINUITY\n',
|
|
1140
|
-
'#EXTINF:1,\n',
|
|
1141
|
-
'media-clip2-video-0.ts\n',
|
|
1142
|
-
'#EXTINF:1,\n',
|
|
1143
|
-
'media-clip2-video-1.ts\n',
|
|
1144
|
-
'#EXT-X-DISCONTINUITY\n',
|
|
1145
|
-
'#EXTINF:4,\n',
|
|
1146
|
-
'media-clip3-video-1.ts\n',
|
|
1147
|
-
'#EXT-X-ENDLIST\n',
|
|
1148
|
-
].join('');
|
|
1149
|
-
|
|
1150
|
-
fakeNetEngine
|
|
1151
|
-
.setResponseText('test:/master', master)
|
|
1152
|
-
.setResponseText('test:/video', media);
|
|
1153
|
-
|
|
1154
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
1155
|
-
await manifest.variants[0].video.createSegmentIndex();
|
|
1156
|
-
|
|
1157
|
-
const segmentIndex = manifest.variants[0].video.segmentIndex;
|
|
1158
|
-
const references = [];
|
|
1159
|
-
|
|
1160
|
-
for (let i = 0; i < 7; i++) {
|
|
1161
|
-
references.push(segmentIndex.get(i));
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
expect(references[0].timestampOffset).toBe(0);
|
|
1165
|
-
expect(references[1].timestampOffset).toBe(0);
|
|
1166
|
-
expect(references[2].timestampOffset).toBe(4);
|
|
1167
|
-
expect(references[3].timestampOffset).toBe(4);
|
|
1168
|
-
expect(references[4].timestampOffset).toBe(9);
|
|
1169
|
-
expect(references[5].timestampOffset).toBe(9);
|
|
1170
|
-
expect(references[6].timestampOffset).toBe(11);
|
|
1171
|
-
},
|
|
1172
|
-
);
|
|
1173
|
-
|
|
1174
1156
|
it('parses characteristics from audio tags', async () => {
|
|
1175
1157
|
const master = [
|
|
1176
1158
|
'#EXTM3U\n',
|
|
@@ -2983,7 +2965,8 @@ describe('HlsParser', () => {
|
|
|
2983
2965
|
const media = [
|
|
2984
2966
|
'#EXTM3U\n',
|
|
2985
2967
|
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2986
|
-
'#EXT-X-
|
|
2968
|
+
'#EXT-X-BYTERANGE:616@0\n',
|
|
2969
|
+
'#EXT-X-MAP:URI="init.mp4"\n',
|
|
2987
2970
|
'#EXTINF:5,\n',
|
|
2988
2971
|
'main.mp4\n',
|
|
2989
2972
|
'#EXT-X-MAP:URI="init2.mp4",BYTERANGE="616@0"\n',
|
|
@@ -3011,8 +2994,15 @@ describe('HlsParser', () => {
|
|
|
3011
2994
|
// uri.
|
|
3012
2995
|
const initSegments = Array.from(actualVideo.segmentIndex).map(
|
|
3013
2996
|
(seg) => seg.initSegmentReference);
|
|
3014
|
-
expect(initSegments
|
|
3015
|
-
|
|
2997
|
+
expect(initSegments.length).toBe(2);
|
|
2998
|
+
const firstInitSegment = initSegments[0];
|
|
2999
|
+
expect(firstInitSegment.getUris()[0]).toBe('test:/init.mp4');
|
|
3000
|
+
expect(firstInitSegment.startByte).toBe(0);
|
|
3001
|
+
expect(firstInitSegment.endByte).toBe(615);
|
|
3002
|
+
const secondInitSegment = initSegments[1];
|
|
3003
|
+
expect(secondInitSegment.getUris()[0]).toBe('test:/init2.mp4');
|
|
3004
|
+
expect(secondInitSegment.startByte).toBe(0);
|
|
3005
|
+
expect(secondInitSegment.endByte).toBe(615);
|
|
3016
3006
|
});
|
|
3017
3007
|
|
|
3018
3008
|
it('parses variants encrypted with AES-128', async () => {
|
|
@@ -3132,8 +3122,10 @@ describe('HlsParser', () => {
|
|
|
3132
3122
|
|
|
3133
3123
|
const firstMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(0);
|
|
3134
3124
|
expect(firstMp4Segment.hlsAes128Key).toBeDefined();
|
|
3125
|
+
expect(firstMp4Segment.initSegmentReference.hlsAes128Key).toBeDefined();
|
|
3135
3126
|
const secondMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(1);
|
|
3136
3127
|
expect(secondMp4Segment.hlsAes128Key).toBeNull();
|
|
3128
|
+
expect(secondMp4Segment.initSegmentReference.hlsAes128Key).toBeDefined();
|
|
3137
3129
|
|
|
3138
3130
|
const tsAesEncryptionVideo = actual.variants[2].video;
|
|
3139
3131
|
await tsAesEncryptionVideo.createSegmentIndex();
|
|
@@ -11,15 +11,28 @@ describe('BufferingObserver', () => {
|
|
|
11
11
|
const thresholdAfterStarving = 5;
|
|
12
12
|
const thresholdAfterSatisfied = 2;
|
|
13
13
|
|
|
14
|
+
const originalDateNow = Date.now;
|
|
15
|
+
|
|
16
|
+
/** @type {!Date} */
|
|
17
|
+
let baseTime;
|
|
18
|
+
|
|
14
19
|
/** @type {!shaka.media.BufferingObserver} */
|
|
15
20
|
let controller;
|
|
16
21
|
|
|
17
22
|
beforeEach(() => {
|
|
23
|
+
// Any test using Date needs to have Date mocked to avoid flake.
|
|
24
|
+
baseTime = new Date(2015, 11, 30);
|
|
25
|
+
Date.now = () => baseTime.getTime();
|
|
26
|
+
|
|
18
27
|
controller = new BufferingObserver(
|
|
19
28
|
thresholdAfterStarving,
|
|
20
29
|
thresholdAfterSatisfied);
|
|
21
30
|
});
|
|
22
31
|
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
Date.now = originalDateNow;
|
|
34
|
+
});
|
|
35
|
+
|
|
23
36
|
describe('when satisfied', () => {
|
|
24
37
|
beforeEach(() => {
|
|
25
38
|
controller.setState(State.SATISFIED);
|