shaka-player 4.3.7 → 4.3.9
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 +11 -0
- package/.github/workflows/appspot.yaml +39 -0
- package/.github/workflows/build-and-test.yaml +8 -0
- package/.github/workflows/selenium-lab-tests.yaml +3 -2
- package/.github/workflows/update-issues.yaml +1 -0
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTORS +1 -0
- package/demo/custom.js +1 -1
- package/dist/demo.compiled.debug.js +2 -2
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +2 -2
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +2 -2
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +24 -2
- package/dist/shaka-player.compiled.debug.d.ts +24 -2
- package/dist/shaka-player.compiled.debug.js +251 -247
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +796 -793
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +24 -2
- package/dist/shaka-player.ui.debug.d.ts +24 -2
- package/dist/shaka-player.ui.debug.js +289 -286
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +831 -827
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/drm-config.md +9 -6
- package/docs/tutorials/selenium-grid-config.md +60 -8
- package/externs/ima.js +34 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/offline.js +5 -1
- package/karma.conf.js +50 -7
- package/lib/abr/simple_abr_manager.js +6 -6
- package/lib/cea/mp4_cea_parser.js +24 -18
- package/lib/dash/dash_parser.js +19 -3
- package/lib/hls/hls_parser.js +24 -4
- package/lib/media/drm_engine.js +14 -8
- package/lib/media/gap_jumping_controller.js +24 -2
- package/lib/media/media_source_engine.js +11 -8
- package/lib/media/playhead.js +11 -0
- package/lib/media/region_observer.js +19 -1
- package/lib/media/streaming_engine.js +41 -21
- package/lib/media/video_wrapper.js +6 -1
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +20 -9
- package/lib/polyfill/abort_controller.js +7 -0
- package/lib/polyfill/orientation.js +34 -7
- package/lib/text/vtt_text_parser.js +3 -1
- package/lib/util/manifest_parser_utils.js +5 -3
- package/lib/util/periods.js +2 -0
- package/lib/util/platform.js +14 -0
- package/lib/util/stream_utils.js +70 -26
- package/package.json +2 -2
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/cea/mp4_cea_parser_unit.js +28 -5
- package/test/dash/dash_parser_manifest_unit.js +120 -0
- package/test/hls/hls_parser_unit.js +216 -0
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/drm_engine_integration.js +2 -2
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/region_observer_unit.js +2 -1
- package/test/media/segment_index_unit.js +3 -3
- package/test/media/streaming_engine_integration.js +14 -11
- package/test/media/streaming_engine_unit.js +26 -2
- package/test/offline/manifest_convert_unit.js +5 -0
- package/test/offline/offline_integration.js +6 -3
- package/test/player_external.js +19 -16
- package/test/player_integration.js +35 -48
- package/test/player_src_equals_integration.js +9 -5
- package/test/test/assets/multiple-trun-init.mp4 +0 -0
- package/test/test/assets/multiple-trun-segment.mp4 +0 -0
- package/test/test/util/fake_demo_main.js +7 -3
- package/test/test/util/layout_tests.js +2 -2
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/streaming_engine_util.js +3 -0
- package/test/test/util/ui_utils.js +9 -2
- package/test/test/util/util.js +6 -27
- package/test/test/util/waiter.js +73 -3
- package/test/ui/ad_ui_unit.js +9 -9
- package/test/util/stream_utils_unit.js +46 -0
- package/ui/range_element.js +5 -0
- package/ui/resolution_selection.js +7 -1
package/lib/util/stream_utils.js
CHANGED
|
@@ -437,48 +437,62 @@ shaka.util.StreamUtils = class {
|
|
|
437
437
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
438
438
|
// See: https://github.com/shaka-project/shaka-player/issues/3860
|
|
439
439
|
const video = variant.video;
|
|
440
|
+
|
|
440
441
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
441
442
|
const Capabilities = shaka.media.Capabilities;
|
|
443
|
+
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
444
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
445
|
+
const StreamUtils = shaka.util.StreamUtils;
|
|
446
|
+
|
|
442
447
|
if (video) {
|
|
443
|
-
let videoCodecs =
|
|
444
|
-
|
|
448
|
+
let videoCodecs = StreamUtils.getCorrectVideoCodecs_(video.codecs);
|
|
449
|
+
|
|
445
450
|
// For multiplexed streams. Here we must check the audio of the
|
|
446
451
|
// stream to see if it is compatible.
|
|
447
452
|
if (video.codecs.includes(',')) {
|
|
448
453
|
const allCodecs = video.codecs.split(',');
|
|
449
|
-
|
|
454
|
+
|
|
455
|
+
videoCodecs = ManifestParserUtils.guessCodecs(
|
|
450
456
|
ContentType.VIDEO, allCodecs);
|
|
451
|
-
videoCodecs =
|
|
452
|
-
|
|
453
|
-
let audioCodecs =
|
|
457
|
+
videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
458
|
+
|
|
459
|
+
let audioCodecs = ManifestParserUtils.guessCodecs(
|
|
454
460
|
ContentType.AUDIO, allCodecs);
|
|
455
|
-
audioCodecs =
|
|
456
|
-
|
|
457
|
-
const audioFullType =
|
|
461
|
+
audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
462
|
+
|
|
463
|
+
const audioFullType = MimeUtils.getFullOrConvertedType(
|
|
458
464
|
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
465
|
+
|
|
459
466
|
if (!Capabilities.isTypeSupported(audioFullType)) {
|
|
460
467
|
return false;
|
|
461
468
|
}
|
|
469
|
+
|
|
462
470
|
// Update the codec string with the (possibly) converted codecs.
|
|
463
471
|
videoCodecs = [videoCodecs, audioCodecs].join(',');
|
|
464
472
|
}
|
|
465
|
-
|
|
473
|
+
|
|
474
|
+
const fullType = MimeUtils.getFullOrConvertedType(
|
|
466
475
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
476
|
+
|
|
467
477
|
if (!Capabilities.isTypeSupported(fullType)) {
|
|
468
478
|
return false;
|
|
469
479
|
}
|
|
480
|
+
|
|
470
481
|
// Update the codec string with the (possibly) converted codecs.
|
|
471
482
|
video.codecs = videoCodecs;
|
|
472
483
|
}
|
|
484
|
+
|
|
473
485
|
const audio = variant.audio;
|
|
486
|
+
|
|
474
487
|
if (audio) {
|
|
475
|
-
const codecs =
|
|
476
|
-
|
|
477
|
-
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
488
|
+
const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
489
|
+
const fullType = MimeUtils.getFullOrConvertedType(
|
|
478
490
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
491
|
+
|
|
479
492
|
if (!Capabilities.isTypeSupported(fullType)) {
|
|
480
493
|
return false;
|
|
481
494
|
}
|
|
495
|
+
|
|
482
496
|
// Update the codec string with the (possibly) converted codecs.
|
|
483
497
|
audio.codecs = codecs;
|
|
484
498
|
}
|
|
@@ -490,7 +504,7 @@ shaka.util.StreamUtils = class {
|
|
|
490
504
|
(video.codecs.includes('avc1.') ||
|
|
491
505
|
video.codecs.includes('avc3.'))) {
|
|
492
506
|
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
493
|
-
|
|
507
|
+
StreamUtils.getVariantSummaryString_(variant));
|
|
494
508
|
return false;
|
|
495
509
|
}
|
|
496
510
|
|
|
@@ -500,7 +514,7 @@ shaka.util.StreamUtils = class {
|
|
|
500
514
|
// Filter out all unsupported variants.
|
|
501
515
|
if (!supported) {
|
|
502
516
|
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
503
|
-
|
|
517
|
+
StreamUtils.getVariantSummaryString_(variant));
|
|
504
518
|
}
|
|
505
519
|
return supported;
|
|
506
520
|
});
|
|
@@ -564,7 +578,11 @@ shaka.util.StreamUtils = class {
|
|
|
564
578
|
static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
|
|
565
579
|
const audio = variant.audio;
|
|
566
580
|
const video = variant.video;
|
|
581
|
+
|
|
567
582
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
583
|
+
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
584
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
585
|
+
const StreamUtils = shaka.util.StreamUtils;
|
|
568
586
|
|
|
569
587
|
/** @type {!MediaDecodingConfiguration} */
|
|
570
588
|
const mediaDecodingConfig = {
|
|
@@ -573,19 +591,23 @@ shaka.util.StreamUtils = class {
|
|
|
573
591
|
|
|
574
592
|
if (video) {
|
|
575
593
|
let videoCodecs = video.codecs;
|
|
594
|
+
|
|
576
595
|
// For multiplexed streams with audio+video codecs, the config should have
|
|
577
596
|
// AudioConfiguration and VideoConfiguration.
|
|
578
597
|
if (video.codecs.includes(',')) {
|
|
579
598
|
const allCodecs = video.codecs.split(',');
|
|
580
|
-
|
|
599
|
+
|
|
600
|
+
videoCodecs = ManifestParserUtils.guessCodecs(
|
|
581
601
|
ContentType.VIDEO, allCodecs);
|
|
582
|
-
videoCodecs =
|
|
583
|
-
|
|
584
|
-
|
|
602
|
+
videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
603
|
+
|
|
604
|
+
let audioCodecs = ManifestParserUtils.guessCodecs(
|
|
585
605
|
ContentType.AUDIO, allCodecs);
|
|
606
|
+
audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
586
607
|
|
|
587
|
-
const audioFullType =
|
|
608
|
+
const audioFullType = MimeUtils.getFullOrConvertedType(
|
|
588
609
|
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
610
|
+
|
|
589
611
|
mediaDecodingConfig.audio = {
|
|
590
612
|
contentType: audioFullType,
|
|
591
613
|
channels: 2,
|
|
@@ -594,9 +616,11 @@ shaka.util.StreamUtils = class {
|
|
|
594
616
|
spatialRendering: false,
|
|
595
617
|
};
|
|
596
618
|
}
|
|
597
|
-
|
|
598
|
-
|
|
619
|
+
|
|
620
|
+
videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
621
|
+
const fullType = MimeUtils.getFullOrConvertedType(
|
|
599
622
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
623
|
+
|
|
600
624
|
// VideoConfiguration
|
|
601
625
|
mediaDecodingConfig.video = {
|
|
602
626
|
contentType: fullType,
|
|
@@ -629,9 +653,8 @@ shaka.util.StreamUtils = class {
|
|
|
629
653
|
}
|
|
630
654
|
}
|
|
631
655
|
if (audio) {
|
|
632
|
-
const codecs =
|
|
633
|
-
|
|
634
|
-
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
656
|
+
const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
657
|
+
const fullType = MimeUtils.getFullOrConvertedType(
|
|
635
658
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
636
659
|
|
|
637
660
|
// AudioConfiguration
|
|
@@ -762,6 +785,20 @@ shaka.util.StreamUtils = class {
|
|
|
762
785
|
* @private
|
|
763
786
|
*/
|
|
764
787
|
static getCorrectAudioCodecs_(codecs) {
|
|
788
|
+
// According to RFC 6381 section 3.3, 'fLaC' is actually the correct
|
|
789
|
+
// codec string. We still need to map it to 'flac', as some browsers
|
|
790
|
+
// currently don't support 'fLaC', while 'flac' is supported by most
|
|
791
|
+
// major browsers.
|
|
792
|
+
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
|
793
|
+
if (codecs === 'fLaC') {
|
|
794
|
+
return 'flac';
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// The same is true for 'Opus'.
|
|
798
|
+
if (codecs === 'Opus') {
|
|
799
|
+
return 'opus';
|
|
800
|
+
}
|
|
801
|
+
|
|
765
802
|
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
766
803
|
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
767
804
|
// See https://github.com/shaka-project/shaka-player/issues/2989 for
|
|
@@ -1068,6 +1105,7 @@ shaka.util.StreamUtils = class {
|
|
|
1068
1105
|
track.frameRate = video.frameRate || null;
|
|
1069
1106
|
track.pixelAspectRatio = video.pixelAspectRatio || null;
|
|
1070
1107
|
track.videoBandwidth = video.bandwidth || null;
|
|
1108
|
+
track.hdr = video.hdr || null;
|
|
1071
1109
|
}
|
|
1072
1110
|
|
|
1073
1111
|
if (audio) {
|
|
@@ -1239,6 +1277,9 @@ shaka.util.StreamUtils = class {
|
|
|
1239
1277
|
if (textTrack.kind == 'captions') {
|
|
1240
1278
|
track.mimeType = CLOSED_CAPTION_MIMETYPE;
|
|
1241
1279
|
}
|
|
1280
|
+
if (textTrack.kind == 'subtitles') {
|
|
1281
|
+
track.mimeType = 'text/vtt';
|
|
1282
|
+
}
|
|
1242
1283
|
if (textTrack.kind) {
|
|
1243
1284
|
track.roles = [textTrack.kind];
|
|
1244
1285
|
}
|
|
@@ -1286,13 +1327,15 @@ shaka.util.StreamUtils = class {
|
|
|
1286
1327
|
* @private
|
|
1287
1328
|
*/
|
|
1288
1329
|
static html5TrackToGenericShakaTrack_(html5Track) {
|
|
1330
|
+
const language = html5Track.language;
|
|
1331
|
+
|
|
1289
1332
|
/** @type {shaka.extern.Track} */
|
|
1290
1333
|
const track = {
|
|
1291
1334
|
id: shaka.util.StreamUtils.html5TrackId(html5Track),
|
|
1292
1335
|
active: false,
|
|
1293
1336
|
type: '',
|
|
1294
1337
|
bandwidth: 0,
|
|
1295
|
-
language: shaka.util.LanguageUtils.normalize(
|
|
1338
|
+
language: shaka.util.LanguageUtils.normalize(language || 'und'),
|
|
1296
1339
|
label: html5Track.label,
|
|
1297
1340
|
kind: html5Track.kind,
|
|
1298
1341
|
width: null,
|
|
@@ -1322,6 +1365,7 @@ shaka.util.StreamUtils = class {
|
|
|
1322
1365
|
originalAudioId: null,
|
|
1323
1366
|
originalTextId: null,
|
|
1324
1367
|
originalImageId: null,
|
|
1368
|
+
originalLanguage: language,
|
|
1325
1369
|
};
|
|
1326
1370
|
|
|
1327
1371
|
return track;
|
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.9",
|
|
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.4",
|
|
46
46
|
"karma-opera-launcher": "^1.0.0",
|
|
47
47
|
"karma-sourcemap-loader": "^0.3.8",
|
|
48
48
|
"karma-spec-reporter": "^0.0.34",
|
|
@@ -171,7 +171,10 @@ describe('CastUtils', () => {
|
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
// Disable because these tests are flakey on ChromeLinux, and this whole
|
|
175
|
+
// module will be removed in
|
|
176
|
+
// https://github.com/shaka-project/shaka-player/issues/4214
|
|
177
|
+
xdescribe('TimeRanges', () => {
|
|
175
178
|
/** @type {!HTMLVideoElement} */
|
|
176
179
|
let video;
|
|
177
180
|
/** @type {!shaka.util.EventManager} */
|
|
@@ -9,6 +9,10 @@ describe('Mp4CeaParser', () => {
|
|
|
9
9
|
const ceaSegmentUri = '/base/test/test/assets/cea-segment.mp4';
|
|
10
10
|
const h265ceaInitSegmentUri = '/base/test/test/assets/h265-cea-init.mp4';
|
|
11
11
|
const h265ceaSegmentUri = '/base/test/test/assets/h265-cea-segment.mp4';
|
|
12
|
+
const multipleTrunInitSegmentUri =
|
|
13
|
+
'/base/test/test/assets/multiple-trun-init.mp4';
|
|
14
|
+
const multipleTrunSegmentUri =
|
|
15
|
+
'/base/test/test/assets/multiple-trun-segment.mp4';
|
|
12
16
|
const Util = shaka.test.Util;
|
|
13
17
|
|
|
14
18
|
/** @type {!ArrayBuffer} */
|
|
@@ -19,18 +23,27 @@ describe('Mp4CeaParser', () => {
|
|
|
19
23
|
let h265ceaInitSegment;
|
|
20
24
|
/** @type {!ArrayBuffer} */
|
|
21
25
|
let h265ceaSegment;
|
|
26
|
+
/** @type {!ArrayBuffer} */
|
|
27
|
+
let multipleTrunInitSegment;
|
|
28
|
+
/** @type {!ArrayBuffer} */
|
|
29
|
+
let multipleTrunSegment;
|
|
22
30
|
|
|
23
31
|
beforeAll(async () => {
|
|
24
|
-
|
|
32
|
+
[
|
|
33
|
+
ceaInitSegment,
|
|
34
|
+
ceaSegment,
|
|
35
|
+
h265ceaInitSegment,
|
|
36
|
+
h265ceaSegment,
|
|
37
|
+
multipleTrunInitSegment,
|
|
38
|
+
multipleTrunSegment,
|
|
39
|
+
] = await Promise.all([
|
|
25
40
|
shaka.test.Util.fetch(ceaInitSegmentUri),
|
|
26
41
|
shaka.test.Util.fetch(ceaSegmentUri),
|
|
27
42
|
shaka.test.Util.fetch(h265ceaInitSegmentUri),
|
|
28
43
|
shaka.test.Util.fetch(h265ceaSegmentUri),
|
|
44
|
+
shaka.test.Util.fetch(multipleTrunInitSegmentUri),
|
|
45
|
+
shaka.test.Util.fetch(multipleTrunSegmentUri),
|
|
29
46
|
]);
|
|
30
|
-
ceaInitSegment = responses[0];
|
|
31
|
-
ceaSegment = responses[1];
|
|
32
|
-
h265ceaInitSegment = responses[2];
|
|
33
|
-
h265ceaSegment = responses[3];
|
|
34
47
|
});
|
|
35
48
|
|
|
36
49
|
/**
|
|
@@ -89,6 +102,16 @@ describe('Mp4CeaParser', () => {
|
|
|
89
102
|
expect(ceaPackets.length).toBe(60);
|
|
90
103
|
});
|
|
91
104
|
|
|
105
|
+
it('parses cea data from a segment with multiple trun boxes', () => {
|
|
106
|
+
const ceaParser = new shaka.cea.Mp4CeaParser();
|
|
107
|
+
|
|
108
|
+
ceaParser.init(multipleTrunInitSegment);
|
|
109
|
+
const ceaPackets = ceaParser.parse(multipleTrunSegment);
|
|
110
|
+
// The first trun box references samples with 48 CEA packets.
|
|
111
|
+
// The second trun box references samples with 132 more, for a total of 180.
|
|
112
|
+
expect(ceaPackets.length).toBe(180);
|
|
113
|
+
});
|
|
114
|
+
|
|
92
115
|
it('parses an invalid init segment', () => {
|
|
93
116
|
const cea708Parser = new shaka.cea.Mp4CeaParser();
|
|
94
117
|
|
|
@@ -2023,6 +2023,66 @@ describe('DashParser Manifest', () => {
|
|
|
2023
2023
|
expect(stream.hdr).toBe('PQ');
|
|
2024
2024
|
});
|
|
2025
2025
|
|
|
2026
|
+
it('supports SupplementalProperty MatrixCoefficients', async () => {
|
|
2027
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2028
|
+
const scheme = cicpScheme('MatrixCoefficients');
|
|
2029
|
+
const manifestText = [
|
|
2030
|
+
'<MPD minBufferTime="PT75S">',
|
|
2031
|
+
' <Period id="1" duration="PT30S">',
|
|
2032
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2033
|
+
` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
|
|
2034
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2035
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2036
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2037
|
+
' </Representation>',
|
|
2038
|
+
' </AdaptationSet>',
|
|
2039
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2040
|
+
' <Representation id="audio-en">',
|
|
2041
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2042
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2043
|
+
' </Representation>',
|
|
2044
|
+
' </AdaptationSet>',
|
|
2045
|
+
' </Period>',
|
|
2046
|
+
'</MPD>',
|
|
2047
|
+
].join('\n');
|
|
2048
|
+
|
|
2049
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2050
|
+
|
|
2051
|
+
/** @type {shaka.extern.Manifest} */
|
|
2052
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2053
|
+
expect(manifest.variants.length).toBe(1);
|
|
2054
|
+
});
|
|
2055
|
+
|
|
2056
|
+
it('supports SupplementalProperty ColourPrimaries', async () => {
|
|
2057
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2058
|
+
const scheme = cicpScheme('ColourPrimaries');
|
|
2059
|
+
const manifestText = [
|
|
2060
|
+
'<MPD minBufferTime="PT75S">',
|
|
2061
|
+
' <Period id="1" duration="PT30S">',
|
|
2062
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2063
|
+
` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
|
|
2064
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2065
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2066
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2067
|
+
' </Representation>',
|
|
2068
|
+
' </AdaptationSet>',
|
|
2069
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2070
|
+
' <Representation id="audio-en">',
|
|
2071
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2072
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2073
|
+
' </Representation>',
|
|
2074
|
+
' </AdaptationSet>',
|
|
2075
|
+
' </Period>',
|
|
2076
|
+
'</MPD>',
|
|
2077
|
+
].join('\n');
|
|
2078
|
+
|
|
2079
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2080
|
+
|
|
2081
|
+
/** @type {shaka.extern.Manifest} */
|
|
2082
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2083
|
+
expect(manifest.variants.length).toBe(1);
|
|
2084
|
+
});
|
|
2085
|
+
|
|
2026
2086
|
it('supports HDR signaling via EssentialProperty', async () => {
|
|
2027
2087
|
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2028
2088
|
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
@@ -2056,6 +2116,66 @@ describe('DashParser Manifest', () => {
|
|
|
2056
2116
|
expect(stream.hdr).toBe('HLG');
|
|
2057
2117
|
});
|
|
2058
2118
|
|
|
2119
|
+
it('supports EssentialProperty MatrixCoefficients', async () => {
|
|
2120
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2121
|
+
const hdrScheme = cicpScheme('MatrixCoefficients');
|
|
2122
|
+
const manifestText = [
|
|
2123
|
+
'<MPD minBufferTime="PT75S">',
|
|
2124
|
+
' <Period id="1" duration="PT30S">',
|
|
2125
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2126
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
|
|
2127
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2128
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2129
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2130
|
+
' </Representation>',
|
|
2131
|
+
' </AdaptationSet>',
|
|
2132
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2133
|
+
' <Representation id="audio-en">',
|
|
2134
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2135
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2136
|
+
' </Representation>',
|
|
2137
|
+
' </AdaptationSet>',
|
|
2138
|
+
' </Period>',
|
|
2139
|
+
'</MPD>',
|
|
2140
|
+
].join('\n');
|
|
2141
|
+
|
|
2142
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2143
|
+
|
|
2144
|
+
/** @type {shaka.extern.Manifest} */
|
|
2145
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2146
|
+
expect(manifest.variants.length).toBe(1);
|
|
2147
|
+
});
|
|
2148
|
+
|
|
2149
|
+
it('supports EssentialProperty ColourPrimaries', async () => {
|
|
2150
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2151
|
+
const hdrScheme = cicpScheme('ColourPrimaries');
|
|
2152
|
+
const manifestText = [
|
|
2153
|
+
'<MPD minBufferTime="PT75S">',
|
|
2154
|
+
' <Period id="1" duration="PT30S">',
|
|
2155
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2156
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
|
|
2157
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2158
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2159
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2160
|
+
' </Representation>',
|
|
2161
|
+
' </AdaptationSet>',
|
|
2162
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2163
|
+
' <Representation id="audio-en">',
|
|
2164
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2165
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2166
|
+
' </Representation>',
|
|
2167
|
+
' </AdaptationSet>',
|
|
2168
|
+
' </Period>',
|
|
2169
|
+
'</MPD>',
|
|
2170
|
+
].join('\n');
|
|
2171
|
+
|
|
2172
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2173
|
+
|
|
2174
|
+
/** @type {shaka.extern.Manifest} */
|
|
2175
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2176
|
+
expect(manifest.variants.length).toBe(1);
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2059
2179
|
it('supports SDR signalling via EssentialProperty', async () => {
|
|
2060
2180
|
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2061
2181
|
const scheme = cicpScheme('TransferCharacteristics');
|
|
@@ -628,6 +628,78 @@ describe('HlsParser', () => {
|
|
|
628
628
|
await testHlsParser(master, media, manifest);
|
|
629
629
|
});
|
|
630
630
|
|
|
631
|
+
it('accepts fLaC codec as audio/mp4', async () => {
|
|
632
|
+
const master = [
|
|
633
|
+
'#EXTM3U\n',
|
|
634
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=1000000,CODECS="fLaC"\n',
|
|
635
|
+
'audio\n',
|
|
636
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=1000000,CODECS="flac"\n',
|
|
637
|
+
'audio2\n',
|
|
638
|
+
].join('');
|
|
639
|
+
|
|
640
|
+
const media = [
|
|
641
|
+
'#EXTM3U\n',
|
|
642
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
643
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
644
|
+
'#EXTINF:5,\n',
|
|
645
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
646
|
+
'main.mp4',
|
|
647
|
+
].join('');
|
|
648
|
+
|
|
649
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
650
|
+
manifest.anyTimeline();
|
|
651
|
+
manifest.addPartialVariant((variant) => {
|
|
652
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
653
|
+
stream.mime('audio/mp4', 'fLaC');
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
manifest.addPartialVariant((variant) => {
|
|
657
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
658
|
+
stream.mime('audio/mp4', 'flac');
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
manifest.sequenceMode = true;
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
await testHlsParser(master, media, manifest);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it('accepts Opus codec as audio/mp4', async () => {
|
|
668
|
+
const master = [
|
|
669
|
+
'#EXTM3U\n',
|
|
670
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=128000,CODECS="Opus"\n',
|
|
671
|
+
'audio\n',
|
|
672
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=128000,CODECS="opus"\n',
|
|
673
|
+
'audio2\n',
|
|
674
|
+
].join('');
|
|
675
|
+
|
|
676
|
+
const media = [
|
|
677
|
+
'#EXTM3U\n',
|
|
678
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
679
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
680
|
+
'#EXTINF:5,\n',
|
|
681
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
682
|
+
'main.mp4',
|
|
683
|
+
].join('');
|
|
684
|
+
|
|
685
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
686
|
+
manifest.anyTimeline();
|
|
687
|
+
manifest.addPartialVariant((variant) => {
|
|
688
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
689
|
+
stream.mime('audio/mp4', 'Opus');
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
manifest.addPartialVariant((variant) => {
|
|
693
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
694
|
+
stream.mime('audio/mp4', 'opus');
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
manifest.sequenceMode = true;
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
await testHlsParser(master, media, manifest);
|
|
701
|
+
});
|
|
702
|
+
|
|
631
703
|
it('parses audio+video variant with closed captions', async () => {
|
|
632
704
|
const master = [
|
|
633
705
|
'#EXTM3U\n',
|
|
@@ -1150,6 +1222,74 @@ describe('HlsParser', () => {
|
|
|
1150
1222
|
await testHlsParser(master, media, manifest);
|
|
1151
1223
|
});
|
|
1152
1224
|
|
|
1225
|
+
it('adds subtitle role when characteristics are empty', async () => {
|
|
1226
|
+
const master = [
|
|
1227
|
+
'#EXTM3U\n',
|
|
1228
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
1229
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1230
|
+
'video\n',
|
|
1231
|
+
|
|
1232
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
|
|
1233
|
+
'NAME="English",URI="audio"\n',
|
|
1234
|
+
|
|
1235
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
|
|
1236
|
+
'CHARACTERISTICS="public.accessibility.describes-video,',
|
|
1237
|
+
'public.accessibility.describes-music-and-sound",',
|
|
1238
|
+
'NAME="English (describes-video)",URI="audio2"\n',
|
|
1239
|
+
|
|
1240
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
|
|
1241
|
+
'NAME="English (subtitle)",DEFAULT=YES,AUTOSELECT=YES,',
|
|
1242
|
+
'URI="text"\n',
|
|
1243
|
+
|
|
1244
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
|
|
1245
|
+
'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
|
|
1246
|
+
'CHARACTERISTICS="public.accessibility.describes-music-and-sound",',
|
|
1247
|
+
'URI="text2"\n',
|
|
1248
|
+
].join('');
|
|
1249
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
1250
|
+
manifest.anyTimeline();
|
|
1251
|
+
manifest.addPartialVariant((variant) => {
|
|
1252
|
+
variant.language = 'en';
|
|
1253
|
+
variant.addPartialStream(ContentType.VIDEO);
|
|
1254
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1255
|
+
stream.language = 'en';
|
|
1256
|
+
stream.roles = [];
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
manifest.addPartialVariant((variant) => {
|
|
1260
|
+
variant.language = 'en';
|
|
1261
|
+
variant.addPartialStream(ContentType.VIDEO);
|
|
1262
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1263
|
+
stream.language = 'en';
|
|
1264
|
+
stream.roles = [
|
|
1265
|
+
'public.accessibility.describes-video',
|
|
1266
|
+
'public.accessibility.describes-music-and-sound',
|
|
1267
|
+
];
|
|
1268
|
+
});
|
|
1269
|
+
});
|
|
1270
|
+
manifest.addPartialTextStream((stream) => {
|
|
1271
|
+
stream.language = 'en';
|
|
1272
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1273
|
+
stream.roles = [
|
|
1274
|
+
'subtitle',
|
|
1275
|
+
];
|
|
1276
|
+
});
|
|
1277
|
+
manifest.addPartialTextStream((stream) => {
|
|
1278
|
+
stream.language = 'en';
|
|
1279
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1280
|
+
stream.roles = [
|
|
1281
|
+
'public.accessibility.describes-music-and-sound',
|
|
1282
|
+
];
|
|
1283
|
+
});
|
|
1284
|
+
manifest.sequenceMode = true;
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
1288
|
+
|
|
1289
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
1290
|
+
expect(actual).toEqual(manifest);
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1153
1293
|
it('parses characteristics from text tags', async () => {
|
|
1154
1294
|
const master = [
|
|
1155
1295
|
'#EXTM3U\n',
|
|
@@ -1898,6 +2038,82 @@ describe('HlsParser', () => {
|
|
|
1898
2038
|
}
|
|
1899
2039
|
});
|
|
1900
2040
|
|
|
2041
|
+
it('ignore segments with #EXTINF:0', async () => {
|
|
2042
|
+
const master = [
|
|
2043
|
+
'#EXTM3U\n',
|
|
2044
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
2045
|
+
'CHANNELS="2",URI="audio"\n',
|
|
2046
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
2047
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
2048
|
+
'video\n',
|
|
2049
|
+
].join('');
|
|
2050
|
+
|
|
2051
|
+
const video = [
|
|
2052
|
+
'#EXTM3U\n',
|
|
2053
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2054
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2055
|
+
'#EXTINF:5,\n',
|
|
2056
|
+
'main.mp4\n',
|
|
2057
|
+
'#EXTINF:0,\n',
|
|
2058
|
+
'main.mp4\n',
|
|
2059
|
+
'#EXTINF:5,\n',
|
|
2060
|
+
'main.mp4\n',
|
|
2061
|
+
].join('');
|
|
2062
|
+
|
|
2063
|
+
const audio = [
|
|
2064
|
+
'#EXTM3U\n',
|
|
2065
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2066
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
2067
|
+
'#EXTINF:5,\n',
|
|
2068
|
+
'main.mp4\n',
|
|
2069
|
+
'#EXTINF:0,\n',
|
|
2070
|
+
'main.mp4\n',
|
|
2071
|
+
'#EXTINF:5,\n',
|
|
2072
|
+
'main.mp4\n',
|
|
2073
|
+
].join('');
|
|
2074
|
+
|
|
2075
|
+
fakeNetEngine
|
|
2076
|
+
.setResponseText('test:/master', master)
|
|
2077
|
+
.setResponseText('test:/audio', audio)
|
|
2078
|
+
.setResponseText('test:/video', video)
|
|
2079
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
2080
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
2081
|
+
|
|
2082
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
2083
|
+
await loadAllStreamsFor(actual);
|
|
2084
|
+
|
|
2085
|
+
expect(actual.variants.length).toBe(1);
|
|
2086
|
+
|
|
2087
|
+
const variant = actual.variants[0];
|
|
2088
|
+
expect(variant.video).toBeTruthy();
|
|
2089
|
+
expect(variant.audio).toBeTruthy();
|
|
2090
|
+
|
|
2091
|
+
await variant.video.createSegmentIndex();
|
|
2092
|
+
goog.asserts.assert(variant.video.segmentIndex != null,
|
|
2093
|
+
'Null segmentIndex!');
|
|
2094
|
+
|
|
2095
|
+
const firstVideoReference = variant.video.segmentIndex.get(0);
|
|
2096
|
+
const secondVideoReference = variant.video.segmentIndex.get(1);
|
|
2097
|
+
const thirdVideoReference = variant.video.segmentIndex.get(2);
|
|
2098
|
+
|
|
2099
|
+
expect(firstVideoReference).not.toBe(null);
|
|
2100
|
+
expect(secondVideoReference).not.toBe(null);
|
|
2101
|
+
expect(thirdVideoReference).toBe(null);
|
|
2102
|
+
|
|
2103
|
+
|
|
2104
|
+
await variant.audio.createSegmentIndex();
|
|
2105
|
+
goog.asserts.assert(variant.audio.segmentIndex != null,
|
|
2106
|
+
'Null segmentIndex!');
|
|
2107
|
+
|
|
2108
|
+
const firstAudioReference = variant.audio.segmentIndex.get(0);
|
|
2109
|
+
const secondAudioReference = variant.audio.segmentIndex.get(1);
|
|
2110
|
+
const thirdAudioReference = variant.audio.segmentIndex.get(2);
|
|
2111
|
+
|
|
2112
|
+
expect(firstAudioReference).not.toBe(null);
|
|
2113
|
+
expect(secondAudioReference).not.toBe(null);
|
|
2114
|
+
expect(thirdAudioReference).toBe(null);
|
|
2115
|
+
});
|
|
2116
|
+
|
|
1901
2117
|
it('Disable audio does not create audio streams', async () => {
|
|
1902
2118
|
const master = [
|
|
1903
2119
|
'#EXTM3U\n',
|