shaka-player 4.3.0 → 4.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build-and-test.yaml +20 -0
- package/AUTHORS +2 -0
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTORS +2 -0
- package/README.md +1 -1
- package/conditional/dummy_cast_proxy.js +120 -0
- package/dist/demo.compiled.debug.js +104 -110
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +104 -110
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +8 -6
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +54 -60
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +54 -60
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +19 -2
- package/dist/shaka-player.compiled.debug.d.ts +19 -2
- package/dist/shaka-player.compiled.debug.externs.js +102 -80
- package/dist/shaka-player.compiled.debug.js +657 -652
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +102 -80
- package/dist/shaka-player.compiled.js +904 -901
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +19 -2
- package/dist/shaka-player.ui.debug.d.ts +19 -2
- package/dist/shaka-player.ui.debug.externs.js +102 -80
- package/dist/shaka-player.ui.debug.js +703 -698
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +102 -80
- package/dist/shaka-player.ui.js +1029 -1026
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/welcome.md +2 -2
- package/externs/shaka/mp4_parser.js +3 -0
- package/externs/shaka/player.js +2 -1
- package/lib/ads/ad_manager.js +2 -2
- package/lib/cea/cea708_window.js +2 -2
- package/lib/cea/mp4_cea_parser.js +37 -16
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +49 -23
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/media_source_capabilities.js +36 -0
- package/lib/media/media_source_engine.js +28 -7
- package/lib/media/segment_reference.js +3 -0
- package/lib/media/streaming_engine.js +33 -10
- package/lib/media/transmuxer.js +6 -4
- package/lib/player.js +10 -6
- package/lib/polyfill/abort_controller.js +143 -0
- package/lib/polyfill/media_capabilities.js +65 -11
- package/lib/polyfill/patchedmediakeys_apple.js +33 -28
- package/lib/text/ttml_text_parser.js +32 -7
- package/lib/text/ui_text_displayer.js +17 -0
- package/lib/util/mp4_box_parsers.js +24 -0
- package/lib/util/mp4_parser.js +35 -0
- package/lib/util/player_configuration.js +10 -3
- package/lib/util/stream_utils.js +35 -13
- package/package.json +4 -4
- package/shaka-player.uncompiled.js +1 -0
- package/test/ads/ad_manager_unit.js +3 -1
- package/test/dash/dash_parser_manifest_unit.js +1 -0
- package/test/hls/hls_parser_unit.js +132 -0
- package/test/media/drm_engine_unit.js +11 -1
- package/test/media/media_source_engine_integration.js +78 -2
- package/test/player_integration.js +10 -3
- package/test/polyfill/media_capabilities_unit.js +259 -0
- package/test/polyfill/patchedmediakeys_apple_unit.js +6 -0
- package/test/test/boot.js +29 -11
- package/test/test/util/manifest_parser_util.js +4 -3
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +6 -5
- package/test/test/util/util.js +3 -2
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/ttml_text_parser_unit.js +119 -86
- package/test/text/ui_text_displayer_unit.js +76 -0
- package/test/util/stream_utils_unit.js +18 -0
- package/test/util/xml_utils_unit.js +6 -1
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/controls.js +49 -36
package/lib/util/stream_utils.js
CHANGED
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.StreamUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.media.Capabilities');
|
|
11
12
|
goog.require('shaka.text.TextEngine');
|
|
12
13
|
goog.require('shaka.util.Functional');
|
|
13
14
|
goog.require('shaka.util.LanguageUtils');
|
|
@@ -437,25 +438,31 @@ shaka.util.StreamUtils = class {
|
|
|
437
438
|
// See: https://github.com/shaka-project/shaka-player/issues/3860
|
|
438
439
|
const video = variant.video;
|
|
439
440
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
441
|
+
const Capabilities = shaka.media.Capabilities;
|
|
440
442
|
if (video) {
|
|
441
|
-
let videoCodecs =
|
|
443
|
+
let videoCodecs =
|
|
444
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
|
|
442
445
|
// For multiplexed streams. Here we must check the audio of the
|
|
443
446
|
// stream to see if it is compatible.
|
|
444
447
|
if (video.codecs.includes(',')) {
|
|
445
448
|
const allCodecs = video.codecs.split(',');
|
|
446
449
|
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
447
450
|
ContentType.VIDEO, allCodecs);
|
|
448
|
-
|
|
451
|
+
videoCodecs =
|
|
452
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
453
|
+
let audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
449
454
|
ContentType.AUDIO, allCodecs);
|
|
455
|
+
audioCodecs =
|
|
456
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
450
457
|
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
451
458
|
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
452
|
-
if (!
|
|
459
|
+
if (!Capabilities.isTypeSupported(audioFullType)) {
|
|
453
460
|
return false;
|
|
454
461
|
}
|
|
455
462
|
}
|
|
456
463
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
457
464
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
458
|
-
if (!
|
|
465
|
+
if (!Capabilities.isTypeSupported(fullType)) {
|
|
459
466
|
return false;
|
|
460
467
|
}
|
|
461
468
|
}
|
|
@@ -465,7 +472,7 @@ shaka.util.StreamUtils = class {
|
|
|
465
472
|
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
466
473
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
467
474
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
468
|
-
if (!
|
|
475
|
+
if (!Capabilities.isTypeSupported(fullType)) {
|
|
469
476
|
return false;
|
|
470
477
|
}
|
|
471
478
|
}
|
|
@@ -566,7 +573,8 @@ shaka.util.StreamUtils = class {
|
|
|
566
573
|
const allCodecs = video.codecs.split(',');
|
|
567
574
|
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
568
575
|
ContentType.VIDEO, allCodecs);
|
|
569
|
-
videoCodecs =
|
|
576
|
+
videoCodecs =
|
|
577
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
570
578
|
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
571
579
|
ContentType.AUDIO, allCodecs);
|
|
572
580
|
|
|
@@ -580,7 +588,7 @@ shaka.util.StreamUtils = class {
|
|
|
580
588
|
spatialRendering: false,
|
|
581
589
|
};
|
|
582
590
|
}
|
|
583
|
-
videoCodecs = shaka.util.StreamUtils.
|
|
591
|
+
videoCodecs = shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
584
592
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
585
593
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
586
594
|
// VideoConfiguration
|
|
@@ -749,15 +757,29 @@ shaka.util.StreamUtils = class {
|
|
|
749
757
|
|
|
750
758
|
|
|
751
759
|
/**
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
* mediaCapabilities enabled.
|
|
755
|
-
*
|
|
760
|
+
* Generates the correct video codec for MediaDecodingConfiguration and
|
|
761
|
+
* for MediaSource.isTypeSupported.
|
|
756
762
|
* @param {string} codec
|
|
757
763
|
* @return {string}
|
|
764
|
+
* @private
|
|
758
765
|
*/
|
|
759
|
-
static
|
|
760
|
-
if (codec
|
|
766
|
+
static getCorrectVideoCodecs_(codec) {
|
|
767
|
+
if (codec.includes('avc1')) {
|
|
768
|
+
// Convert avc1 codec string from RFC-4281 to RFC-6381 for
|
|
769
|
+
// MediaSource.isTypeSupported
|
|
770
|
+
// Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
|
|
771
|
+
const avcdata = codec.split('.');
|
|
772
|
+
if (avcdata.length == 3) {
|
|
773
|
+
let result = avcdata.shift() + '.';
|
|
774
|
+
result += parseInt(avcdata.shift(), 10).toString(16);
|
|
775
|
+
result +=
|
|
776
|
+
('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
} else if (codec == 'vp9') {
|
|
780
|
+
// MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate
|
|
781
|
+
// vp9 codec strings into 'vp09...', to allow such content to play with
|
|
782
|
+
// mediaCapabilities enabled.
|
|
761
783
|
// This means profile 0, level 4.1, 8-bit color. This supports 1080p @
|
|
762
784
|
// 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
|
|
763
785
|
//
|
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.2",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
|
|
29
29
|
"esprima": "^4.0.1",
|
|
30
30
|
"fontfaceonload": "^1.0.2",
|
|
31
|
-
"google-closure-compiler-java": "^
|
|
32
|
-
"google-closure-deps": "
|
|
33
|
-
"google-closure-library": "^
|
|
31
|
+
"google-closure-compiler-java": "^20221102.0.0",
|
|
32
|
+
"google-closure-deps": "^20221102.0.0",
|
|
33
|
+
"google-closure-library": "^20221102.0.0",
|
|
34
34
|
"htmlhint": "^1.1.3",
|
|
35
35
|
"jasmine-ajax": "^4.0.0",
|
|
36
36
|
"jimp": "^0.16.1",
|
|
@@ -32,6 +32,7 @@ goog.require('shaka.offline.OfflineManifestParser');
|
|
|
32
32
|
goog.require('shaka.offline.OfflineScheme');
|
|
33
33
|
goog.require('shaka.offline.Storage');
|
|
34
34
|
goog.require('shaka.offline.indexeddb.StorageMechanism');
|
|
35
|
+
goog.require('shaka.polyfill.AbortController');
|
|
35
36
|
goog.require('shaka.polyfill.Aria');
|
|
36
37
|
goog.require('shaka.polyfill.EncryptionScheme');
|
|
37
38
|
goog.require('shaka.polyfill.Fullscreen');
|
|
@@ -115,7 +115,9 @@ describe('Ad manager', () => {
|
|
|
115
115
|
startedEventB = /** @type {!google.ima.AdEvent} */ (
|
|
116
116
|
new shaka.util.FakeEvent(google.ima.AdEvent.Type.STARTED));
|
|
117
117
|
startedEventB.getAd = () => {
|
|
118
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
isLinear: () => true,
|
|
120
|
+
};
|
|
119
121
|
};
|
|
120
122
|
}
|
|
121
123
|
makeMocks();
|
|
@@ -2198,6 +2198,7 @@ describe('DashParser Manifest', () => {
|
|
|
2198
2198
|
/** @type {shaka.extern.Manifest} */
|
|
2199
2199
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2200
2200
|
expect(manifest.imageStreams.length).toBe(1);
|
|
2201
|
+
expect(manifest.presentationTimeline.getMaxSegmentDuration()).toBe(1);
|
|
2201
2202
|
const imageStream = manifest.imageStreams[0];
|
|
2202
2203
|
expect(imageStream.width).toBe(1024);
|
|
2203
2204
|
expect(imageStream.height).toBe(1152);
|
|
@@ -40,6 +40,7 @@ describe('HlsParser', () => {
|
|
|
40
40
|
|
|
41
41
|
afterEach(() => {
|
|
42
42
|
shaka.log.alwaysWarn = originalAlwaysWarn;
|
|
43
|
+
parser.stop();
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
beforeEach(() => {
|
|
@@ -1089,6 +1090,77 @@ describe('HlsParser', () => {
|
|
|
1089
1090
|
await testHlsParser(master, media, manifest);
|
|
1090
1091
|
});
|
|
1091
1092
|
|
|
1093
|
+
// https://github.com/shaka-project/shaka-player/issues/4759
|
|
1094
|
+
it('makes roles available without loading tracks', async () => {
|
|
1095
|
+
const master = [
|
|
1096
|
+
'#EXTM3U\n',
|
|
1097
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
1098
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1099
|
+
'video\n',
|
|
1100
|
+
|
|
1101
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
|
|
1102
|
+
'NAME="English",URI="audio"\n',
|
|
1103
|
+
|
|
1104
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
|
|
1105
|
+
'CHARACTERISTICS="public.accessibility.describes-video,',
|
|
1106
|
+
'public.accessibility.describes-music-and-sound",',
|
|
1107
|
+
'NAME="English (describes-video)",URI="audio2"\n',
|
|
1108
|
+
|
|
1109
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
|
|
1110
|
+
'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
|
|
1111
|
+
'URI="text"\n',
|
|
1112
|
+
|
|
1113
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
|
|
1114
|
+
'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
|
|
1115
|
+
'CHARACTERISTICS="public.accessibility.describes-spoken-dialog,',
|
|
1116
|
+
'public.accessibility.describes-music-and-sound",',
|
|
1117
|
+
'URI="text2"\n',
|
|
1118
|
+
].join('');
|
|
1119
|
+
|
|
1120
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
1121
|
+
manifest.anyTimeline();
|
|
1122
|
+
manifest.addPartialVariant((variant) => {
|
|
1123
|
+
variant.language = 'en';
|
|
1124
|
+
variant.addPartialStream(ContentType.VIDEO);
|
|
1125
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1126
|
+
stream.language = 'en';
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
manifest.addPartialVariant((variant) => {
|
|
1130
|
+
variant.language = 'en';
|
|
1131
|
+
variant.addPartialStream(ContentType.VIDEO);
|
|
1132
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1133
|
+
stream.language = 'en';
|
|
1134
|
+
stream.roles = [
|
|
1135
|
+
'public.accessibility.describes-video',
|
|
1136
|
+
'public.accessibility.describes-music-and-sound',
|
|
1137
|
+
];
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
manifest.addPartialTextStream((stream) => {
|
|
1141
|
+
stream.language = 'en';
|
|
1142
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1143
|
+
});
|
|
1144
|
+
manifest.addPartialTextStream((stream) => {
|
|
1145
|
+
stream.language = 'en';
|
|
1146
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1147
|
+
stream.roles = [
|
|
1148
|
+
'public.accessibility.describes-spoken-dialog',
|
|
1149
|
+
'public.accessibility.describes-music-and-sound',
|
|
1150
|
+
];
|
|
1151
|
+
});
|
|
1152
|
+
manifest.sequenceMode = true;
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
1156
|
+
|
|
1157
|
+
// NOTE: Not using testHlsParser here because that unconditionally loads all
|
|
1158
|
+
// streams. We need to test the behavior specifically when streams are
|
|
1159
|
+
// _not_ loaded.
|
|
1160
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
1161
|
+
expect(actual).toEqual(manifest);
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1092
1164
|
it('gets mime type from header request', async () => {
|
|
1093
1165
|
const master = [
|
|
1094
1166
|
'#EXTM3U\n',
|
|
@@ -4262,4 +4334,64 @@ describe('HlsParser', () => {
|
|
|
4262
4334
|
expect(actualVideo0.mimeType).toBe('video/mp2t');
|
|
4263
4335
|
expect(actualVideo1.mimeType).toBe('video/mp2t');
|
|
4264
4336
|
});
|
|
4337
|
+
|
|
4338
|
+
it('lazy-loads AAC content without filtering it out', async () => {
|
|
4339
|
+
const master = [
|
|
4340
|
+
'#EXTM3U\n',
|
|
4341
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",URI="audio"\n',
|
|
4342
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="spa",URI="audio2"\n',
|
|
4343
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
4344
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
4345
|
+
'video\n',
|
|
4346
|
+
].join('');
|
|
4347
|
+
|
|
4348
|
+
const video = [
|
|
4349
|
+
'#EXTM3U\n',
|
|
4350
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
4351
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
4352
|
+
'#EXT-X-MEDIA-SEQUENCE:1\n',
|
|
4353
|
+
'#EXTINF:5,\n',
|
|
4354
|
+
'video1.ts\n',
|
|
4355
|
+
'#EXTINF:5,\n',
|
|
4356
|
+
'video2.ts\n',
|
|
4357
|
+
].join('');
|
|
4358
|
+
|
|
4359
|
+
const audio = [
|
|
4360
|
+
'#EXTM3U\n',
|
|
4361
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
4362
|
+
'#EXT-X-MEDIA-SEQUENCE:3\n',
|
|
4363
|
+
'#EXTINF:5,\n',
|
|
4364
|
+
'audio1.aac\n',
|
|
4365
|
+
'#EXTINF:5,\n',
|
|
4366
|
+
'audio2.aac\n',
|
|
4367
|
+
].join('');
|
|
4368
|
+
|
|
4369
|
+
fakeNetEngine
|
|
4370
|
+
.setResponseText('test:/master', master)
|
|
4371
|
+
.setResponseText('test:/audio', audio)
|
|
4372
|
+
.setResponseText('test:/audio2', audio)
|
|
4373
|
+
.setResponseText('test:/video', video)
|
|
4374
|
+
.setResponseValue('test:/init.mp4', initSegmentData);
|
|
4375
|
+
|
|
4376
|
+
const actualManifest = await parser.start('test:/master', playerInterface);
|
|
4377
|
+
expect(actualManifest.variants.length).toBe(2);
|
|
4378
|
+
|
|
4379
|
+
const actualAudio0 = actualManifest.variants[0].audio;
|
|
4380
|
+
const actualAudio1 = actualManifest.variants[1].audio;
|
|
4381
|
+
|
|
4382
|
+
// Before loading, all MIME types agree, and are defaulted to audio/mp4.
|
|
4383
|
+
expect(actualAudio0.mimeType).toBe('audio/mp4');
|
|
4384
|
+
expect(actualAudio1.mimeType).toBe('audio/mp4');
|
|
4385
|
+
|
|
4386
|
+
await actualAudio0.createSegmentIndex();
|
|
4387
|
+
|
|
4388
|
+
// After loading just ONE stream, all MIME types agree again, and have been
|
|
4389
|
+
// updated to reflect the AAC content found inside the loaded playlist.
|
|
4390
|
+
// This is how we avoid having the unloaded tracks filtered out during
|
|
4391
|
+
// startup.
|
|
4392
|
+
expect(actualAudio0.mimeType).toBe('audio/aac');
|
|
4393
|
+
expect(actualAudio0.codecs).toBe('');
|
|
4394
|
+
expect(actualAudio1.mimeType).toBe('audio/aac');
|
|
4395
|
+
expect(actualAudio1.codecs).toBe('');
|
|
4396
|
+
});
|
|
4265
4397
|
});
|
|
@@ -120,11 +120,17 @@ describe('DrmEngine', () => {
|
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
drmEngine = new shaka.media.DrmEngine(playerInterface);
|
|
123
|
+
|
|
123
124
|
config = shaka.util.PlayerConfiguration.createDefault().drm;
|
|
124
125
|
config.servers = {
|
|
125
126
|
'drm.abc': 'http://abc.drm/license',
|
|
126
127
|
'drm.def': 'http://def.drm/license',
|
|
127
128
|
};
|
|
129
|
+
// Some platforms, such as Xbox, default to parseInbandPssh: true, which
|
|
130
|
+
// ignores encrypted events. So set it explicitly to false, and let
|
|
131
|
+
// individual tests set it to true where relevant.
|
|
132
|
+
config.parseInbandPsshEnabled = false;
|
|
133
|
+
|
|
128
134
|
drmEngine.configure(config);
|
|
129
135
|
});
|
|
130
136
|
|
|
@@ -2548,7 +2554,11 @@ describe('DrmEngine', () => {
|
|
|
2548
2554
|
*/
|
|
2549
2555
|
async function sendEncryptedEvent(
|
|
2550
2556
|
initDataType = 'cenc', initData = new Uint8Array(1), keyId = null) {
|
|
2551
|
-
|
|
2557
|
+
// For some platforms, such as Xbox, where parseInbandPssh defaults to
|
|
2558
|
+
// true, this listener may never be set.
|
|
2559
|
+
if (mockVideo.on['encrypted']) {
|
|
2560
|
+
mockVideo.on['encrypted']({initDataType, initData, keyId});
|
|
2561
|
+
}
|
|
2552
2562
|
|
|
2553
2563
|
await Util.shortDelay();
|
|
2554
2564
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
describe('MediaSourceEngine', () => {
|
|
8
8
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
9
|
+
const Cue = shaka.text.Cue;
|
|
10
|
+
const Util = shaka.test.Util;
|
|
9
11
|
const presentationDuration = 840;
|
|
10
12
|
|
|
11
13
|
/** @type {!HTMLVideoElement} */
|
|
@@ -18,6 +20,64 @@ describe('MediaSourceEngine', () => {
|
|
|
18
20
|
let metadata;
|
|
19
21
|
// TODO: add text streams to MSE integration tests
|
|
20
22
|
|
|
23
|
+
const mp4CeaCue0 = jasmine.objectContaining({
|
|
24
|
+
startTime: Util.closeTo(0.067, 0.001),
|
|
25
|
+
endTime: Util.closeTo(1, 0.001),
|
|
26
|
+
textAlign: Cue.textAlign.CENTER,
|
|
27
|
+
nestedCues: [
|
|
28
|
+
jasmine.objectContaining({
|
|
29
|
+
startTime: Util.closeTo(0.067, 0.001),
|
|
30
|
+
endTime: Util.closeTo(1, 0.001),
|
|
31
|
+
payload: 'eng:⠀00:00:00:00',
|
|
32
|
+
textAlign: Cue.textAlign.CENTER,
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const tsCeaCue0 = jasmine.objectContaining({
|
|
38
|
+
startTime: Util.closeTo(0.767, 0.001),
|
|
39
|
+
endTime: Util.closeTo(4.972, 0.001),
|
|
40
|
+
textAlign: Cue.textAlign.CENTER,
|
|
41
|
+
payload: 'These are 608 captions\n(top left)',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const tsCeaCue1 = jasmine.objectContaining({
|
|
45
|
+
startTime: Util.closeTo(5.305, 0.001),
|
|
46
|
+
endTime: Util.closeTo(11.979, 0.001),
|
|
47
|
+
textAlign: Cue.textAlign.CENTER,
|
|
48
|
+
payload: 'These are 608 captions\n(middle)',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const tsCeaCue2 = jasmine.objectContaining({
|
|
52
|
+
startTime: Util.closeTo(12.312, 0.001),
|
|
53
|
+
endTime: Util.closeTo(19.319, 0.001),
|
|
54
|
+
textAlign: Cue.textAlign.CENTER,
|
|
55
|
+
payload: 'These are 608 captions\n(bottom left)',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// The same segments as above, but offset by 40 seconds (yes, 40), which is
|
|
59
|
+
// also 2 segments.
|
|
60
|
+
const tsCeaCue3 = jasmine.objectContaining({
|
|
61
|
+
startTime: Util.closeTo(40.767, 0.001),
|
|
62
|
+
endTime: Util.closeTo(44.972, 0.001),
|
|
63
|
+
textAlign: Cue.textAlign.CENTER,
|
|
64
|
+
payload: 'These are 608 captions\n(top left)',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const tsCeaCue4 = jasmine.objectContaining({
|
|
68
|
+
startTime: Util.closeTo(45.305, 0.001),
|
|
69
|
+
endTime: Util.closeTo(51.979, 0.001),
|
|
70
|
+
textAlign: Cue.textAlign.CENTER,
|
|
71
|
+
payload: 'These are 608 captions\n(middle)',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const tsCeaCue5 = jasmine.objectContaining({
|
|
75
|
+
startTime: Util.closeTo(52.312, 0.001),
|
|
76
|
+
endTime: Util.closeTo(59.319, 0.001),
|
|
77
|
+
textAlign: Cue.textAlign.CENTER,
|
|
78
|
+
payload: 'These are 608 captions\n(bottom left)',
|
|
79
|
+
});
|
|
80
|
+
|
|
21
81
|
/**
|
|
22
82
|
* We use a fake text displayer so that we can check if CEA text is being
|
|
23
83
|
* passed through the system correctly.
|
|
@@ -394,9 +454,14 @@ describe('MediaSourceEngine', () => {
|
|
|
394
454
|
// platforms with native TS support.
|
|
395
455
|
await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
|
|
396
456
|
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
|
|
457
|
+
|
|
397
458
|
await append(ContentType.VIDEO, 0);
|
|
398
459
|
|
|
399
460
|
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
|
|
461
|
+
|
|
462
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
|
|
463
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
|
|
464
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
|
|
400
465
|
});
|
|
401
466
|
|
|
402
467
|
it('extracts CEA-708 captions from previous segment from hls', async () => {
|
|
@@ -413,9 +478,19 @@ describe('MediaSourceEngine', () => {
|
|
|
413
478
|
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
|
|
414
479
|
|
|
415
480
|
await append(ContentType.VIDEO, 2);
|
|
481
|
+
|
|
482
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
|
|
483
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue3]);
|
|
484
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue4]);
|
|
485
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue5]);
|
|
486
|
+
|
|
487
|
+
textDisplayer.appendSpy.calls.reset();
|
|
416
488
|
await appendWithSeek(ContentType.VIDEO, 0);
|
|
417
489
|
|
|
418
|
-
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(
|
|
490
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
|
|
491
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
|
|
492
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
|
|
493
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
|
|
419
494
|
});
|
|
420
495
|
|
|
421
496
|
it('buffers partial TS video segments in sequence mode', async () => {
|
|
@@ -468,7 +543,8 @@ describe('MediaSourceEngine', () => {
|
|
|
468
543
|
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
|
|
469
544
|
await appendWithClosedCaptions(ContentType.VIDEO, 0);
|
|
470
545
|
|
|
471
|
-
expect(textDisplayer.appendSpy).
|
|
546
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(1);
|
|
547
|
+
expect(textDisplayer.appendSpy).toHaveBeenCalledWith([mp4CeaCue0]);
|
|
472
548
|
});
|
|
473
549
|
|
|
474
550
|
it('extracts ID3 metadata from TS', async () => {
|
|
@@ -970,7 +970,7 @@ describe('Player', () => {
|
|
|
970
970
|
expect(getBufferedBehind()).toBe(20); // Buffered to start still.
|
|
971
971
|
video.currentTime = 50;
|
|
972
972
|
await waitUntilBuffered(30);
|
|
973
|
-
expect(getBufferedBehind()).toBeLessThan(
|
|
973
|
+
expect(getBufferedBehind()).toBeLessThan(40); // 30 + segment_size
|
|
974
974
|
|
|
975
975
|
player.configure('streaming.bufferBehind', 10);
|
|
976
976
|
// We only evict content when we append a segment, so increase the
|
|
@@ -997,7 +997,7 @@ describe('Player', () => {
|
|
|
997
997
|
}
|
|
998
998
|
|
|
999
999
|
async function waitUntilBuffered(amount) {
|
|
1000
|
-
for (let i = 0; i <
|
|
1000
|
+
for (let i = 0; i < 50; i++) {
|
|
1001
1001
|
// We buffer from an internal segment, so this shouldn't take long to
|
|
1002
1002
|
// buffer.
|
|
1003
1003
|
await Util.delay(0.1); // eslint-disable-line no-await-in-loop
|
|
@@ -1005,7 +1005,14 @@ describe('Player', () => {
|
|
|
1005
1005
|
return;
|
|
1006
1006
|
}
|
|
1007
1007
|
}
|
|
1008
|
-
|
|
1008
|
+
|
|
1009
|
+
const ranges =
|
|
1010
|
+
shaka.media.TimeRangesUtils.getBufferedInfo(video.buffered);
|
|
1011
|
+
const currentTime = video.currentTime;
|
|
1012
|
+
const target = currentTime + amount;
|
|
1013
|
+
|
|
1014
|
+
throw new Error('Timeout waiting to buffer! ' +
|
|
1015
|
+
JSON.stringify({ranges, currentTime, target}));
|
|
1009
1016
|
}
|
|
1010
1017
|
}); // describe('buffering')
|
|
1011
1018
|
|