shaka-player 4.2.11 → 4.2.13
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 +15 -0
- package/.github/workflows/selenium-lab-tests.yaml +3 -2
- package/.github/workflows/update-issues.yaml +1 -0
- package/CHANGELOG.md +42 -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 +3 -3
- package/dist/locales.js +33 -33
- package/dist/shaka-player.compiled.d.ts +22 -0
- package/dist/shaka-player.compiled.debug.d.ts +22 -0
- package/dist/shaka-player.compiled.debug.js +233 -232
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +738 -738
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +22 -0
- package/dist/shaka-player.ui.debug.d.ts +22 -0
- package/dist/shaka-player.ui.debug.js +259 -258
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +917 -916
- 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/karma.conf.js +50 -7
- package/lib/abr/simple_abr_manager.js +6 -6
- package/lib/cea/mp4_cea_parser.js +30 -14
- package/lib/dash/dash_parser.js +18 -3
- package/lib/hls/hls_parser.js +13 -2
- package/lib/media/drm_engine.js +14 -8
- package/lib/media/gap_jumping_controller.js +24 -2
- package/lib/media/media_source_engine.js +5 -3
- package/lib/media/playhead.js +11 -0
- package/lib/media/region_observer.js +19 -1
- package/lib/media/streaming_engine.js +7 -3
- package/lib/media/video_wrapper.js +6 -1
- package/lib/player.js +9 -8
- 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/mp4_box_parsers.js +8 -3
- package/lib/util/platform.js +14 -0
- package/lib/util/stream_utils.js +70 -26
- package/package.json +2 -3
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -3
- package/test/dash/dash_parser_manifest_unit.js +120 -0
- package/test/hls/hls_parser_unit.js +222 -0
- package/test/media/drm_engine_integration.js +2 -2
- 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/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/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
|
@@ -7,20 +7,33 @@
|
|
|
7
7
|
describe('Mp4CeaParser', () => {
|
|
8
8
|
const ceaInitSegmentUri = '/base/test/test/assets/cea-init.mp4';
|
|
9
9
|
const ceaSegmentUri = '/base/test/test/assets/cea-segment.mp4';
|
|
10
|
+
const multipleTrunInitSegmentUri =
|
|
11
|
+
'/base/test/test/assets/multiple-trun-init.mp4';
|
|
12
|
+
const multipleTrunSegmentUri =
|
|
13
|
+
'/base/test/test/assets/multiple-trun-segment.mp4';
|
|
10
14
|
const Util = shaka.test.Util;
|
|
11
15
|
|
|
12
16
|
/** @type {!ArrayBuffer} */
|
|
13
17
|
let ceaInitSegment;
|
|
14
18
|
/** @type {!ArrayBuffer} */
|
|
15
19
|
let ceaSegment;
|
|
20
|
+
/** @type {!ArrayBuffer} */
|
|
21
|
+
let multipleTrunInitSegment;
|
|
22
|
+
/** @type {!ArrayBuffer} */
|
|
23
|
+
let multipleTrunSegment;
|
|
16
24
|
|
|
17
25
|
beforeAll(async () => {
|
|
18
|
-
|
|
26
|
+
[
|
|
27
|
+
ceaInitSegment,
|
|
28
|
+
ceaSegment,
|
|
29
|
+
multipleTrunInitSegment,
|
|
30
|
+
multipleTrunSegment,
|
|
31
|
+
] = await Promise.all([
|
|
19
32
|
shaka.test.Util.fetch(ceaInitSegmentUri),
|
|
20
33
|
shaka.test.Util.fetch(ceaSegmentUri),
|
|
34
|
+
shaka.test.Util.fetch(multipleTrunInitSegmentUri),
|
|
35
|
+
shaka.test.Util.fetch(multipleTrunSegmentUri),
|
|
21
36
|
]);
|
|
22
|
-
ceaInitSegment = responses[0];
|
|
23
|
-
ceaSegment = responses[1];
|
|
24
37
|
});
|
|
25
38
|
|
|
26
39
|
/**
|
|
@@ -70,6 +83,16 @@ describe('Mp4CeaParser', () => {
|
|
|
70
83
|
.toEqual(expectedCea708Packet);
|
|
71
84
|
});
|
|
72
85
|
|
|
86
|
+
it('parses cea data from a segment with multiple trun boxes', () => {
|
|
87
|
+
const ceaParser = new shaka.cea.Mp4CeaParser();
|
|
88
|
+
|
|
89
|
+
ceaParser.init(multipleTrunInitSegment);
|
|
90
|
+
const ceaPackets = ceaParser.parse(multipleTrunSegment);
|
|
91
|
+
// The first trun box references samples with 48 CEA packets.
|
|
92
|
+
// The second trun box references samples with 132 more, for a total of 180.
|
|
93
|
+
expect(ceaPackets.length).toBe(180);
|
|
94
|
+
});
|
|
95
|
+
|
|
73
96
|
it('parses an invalid init segment', () => {
|
|
74
97
|
const cea708Parser = new shaka.cea.Mp4CeaParser();
|
|
75
98
|
|
|
@@ -1931,6 +1931,66 @@ describe('DashParser Manifest', () => {
|
|
|
1931
1931
|
expect(stream.hdr).toBe('PQ');
|
|
1932
1932
|
});
|
|
1933
1933
|
|
|
1934
|
+
it('supports SupplementalProperty MatrixCoefficients', async () => {
|
|
1935
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1936
|
+
const scheme = cicpScheme('MatrixCoefficients');
|
|
1937
|
+
const manifestText = [
|
|
1938
|
+
'<MPD minBufferTime="PT75S">',
|
|
1939
|
+
' <Period id="1" duration="PT30S">',
|
|
1940
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1941
|
+
` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
|
|
1942
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
1943
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1944
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1945
|
+
' </Representation>',
|
|
1946
|
+
' </AdaptationSet>',
|
|
1947
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1948
|
+
' <Representation id="audio-en">',
|
|
1949
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1950
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1951
|
+
' </Representation>',
|
|
1952
|
+
' </AdaptationSet>',
|
|
1953
|
+
' </Period>',
|
|
1954
|
+
'</MPD>',
|
|
1955
|
+
].join('\n');
|
|
1956
|
+
|
|
1957
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1958
|
+
|
|
1959
|
+
/** @type {shaka.extern.Manifest} */
|
|
1960
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1961
|
+
expect(manifest.variants.length).toBe(1);
|
|
1962
|
+
});
|
|
1963
|
+
|
|
1964
|
+
it('supports SupplementalProperty ColourPrimaries', async () => {
|
|
1965
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1966
|
+
const scheme = cicpScheme('ColourPrimaries');
|
|
1967
|
+
const manifestText = [
|
|
1968
|
+
'<MPD minBufferTime="PT75S">',
|
|
1969
|
+
' <Period id="1" duration="PT30S">',
|
|
1970
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1971
|
+
` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
|
|
1972
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
1973
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1974
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1975
|
+
' </Representation>',
|
|
1976
|
+
' </AdaptationSet>',
|
|
1977
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1978
|
+
' <Representation id="audio-en">',
|
|
1979
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1980
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1981
|
+
' </Representation>',
|
|
1982
|
+
' </AdaptationSet>',
|
|
1983
|
+
' </Period>',
|
|
1984
|
+
'</MPD>',
|
|
1985
|
+
].join('\n');
|
|
1986
|
+
|
|
1987
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1988
|
+
|
|
1989
|
+
/** @type {shaka.extern.Manifest} */
|
|
1990
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1991
|
+
expect(manifest.variants.length).toBe(1);
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1934
1994
|
it('supports HDR signaling via EssentialProperty', async () => {
|
|
1935
1995
|
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1936
1996
|
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
@@ -1964,6 +2024,66 @@ describe('DashParser Manifest', () => {
|
|
|
1964
2024
|
expect(stream.hdr).toBe('HLG');
|
|
1965
2025
|
});
|
|
1966
2026
|
|
|
2027
|
+
it('supports EssentialProperty MatrixCoefficients', async () => {
|
|
2028
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2029
|
+
const hdrScheme = cicpScheme('MatrixCoefficients');
|
|
2030
|
+
const manifestText = [
|
|
2031
|
+
'<MPD minBufferTime="PT75S">',
|
|
2032
|
+
' <Period id="1" duration="PT30S">',
|
|
2033
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2034
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
|
|
2035
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2036
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2037
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2038
|
+
' </Representation>',
|
|
2039
|
+
' </AdaptationSet>',
|
|
2040
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2041
|
+
' <Representation id="audio-en">',
|
|
2042
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2043
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2044
|
+
' </Representation>',
|
|
2045
|
+
' </AdaptationSet>',
|
|
2046
|
+
' </Period>',
|
|
2047
|
+
'</MPD>',
|
|
2048
|
+
].join('\n');
|
|
2049
|
+
|
|
2050
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2051
|
+
|
|
2052
|
+
/** @type {shaka.extern.Manifest} */
|
|
2053
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2054
|
+
expect(manifest.variants.length).toBe(1);
|
|
2055
|
+
});
|
|
2056
|
+
|
|
2057
|
+
it('supports EssentialProperty ColourPrimaries', async () => {
|
|
2058
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2059
|
+
const hdrScheme = cicpScheme('ColourPrimaries');
|
|
2060
|
+
const manifestText = [
|
|
2061
|
+
'<MPD minBufferTime="PT75S">',
|
|
2062
|
+
' <Period id="1" duration="PT30S">',
|
|
2063
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2064
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
|
|
2065
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2066
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2067
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2068
|
+
' </Representation>',
|
|
2069
|
+
' </AdaptationSet>',
|
|
2070
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2071
|
+
' <Representation id="audio-en">',
|
|
2072
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2073
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2074
|
+
' </Representation>',
|
|
2075
|
+
' </AdaptationSet>',
|
|
2076
|
+
' </Period>',
|
|
2077
|
+
'</MPD>',
|
|
2078
|
+
].join('\n');
|
|
2079
|
+
|
|
2080
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2081
|
+
|
|
2082
|
+
/** @type {shaka.extern.Manifest} */
|
|
2083
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2084
|
+
expect(manifest.variants.length).toBe(1);
|
|
2085
|
+
});
|
|
2086
|
+
|
|
1967
2087
|
it('supports SDR signalling via EssentialProperty', async () => {
|
|
1968
2088
|
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1969
2089
|
const scheme = cicpScheme('TransferCharacteristics');
|
|
@@ -573,6 +573,78 @@ describe('HlsParser', () => {
|
|
|
573
573
|
await testHlsParser(master, media, manifest);
|
|
574
574
|
});
|
|
575
575
|
|
|
576
|
+
it('accepts fLaC codec as audio/mp4', async () => {
|
|
577
|
+
const master = [
|
|
578
|
+
'#EXTM3U\n',
|
|
579
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=1000000,CODECS="fLaC"\n',
|
|
580
|
+
'audio\n',
|
|
581
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=1000000,CODECS="flac"\n',
|
|
582
|
+
'audio2\n',
|
|
583
|
+
].join('');
|
|
584
|
+
|
|
585
|
+
const media = [
|
|
586
|
+
'#EXTM3U\n',
|
|
587
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
588
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
589
|
+
'#EXTINF:5,\n',
|
|
590
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
591
|
+
'main.mp4',
|
|
592
|
+
].join('');
|
|
593
|
+
|
|
594
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
595
|
+
manifest.anyTimeline();
|
|
596
|
+
manifest.addPartialVariant((variant) => {
|
|
597
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
598
|
+
stream.mime('audio/mp4', 'fLaC');
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
manifest.addPartialVariant((variant) => {
|
|
602
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
603
|
+
stream.mime('audio/mp4', 'flac');
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
manifest.sequenceMode = true;
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
await testHlsParser(master, media, manifest);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('accepts Opus codec as audio/mp4', async () => {
|
|
613
|
+
const master = [
|
|
614
|
+
'#EXTM3U\n',
|
|
615
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=128000,CODECS="Opus"\n',
|
|
616
|
+
'audio\n',
|
|
617
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=128000,CODECS="opus"\n',
|
|
618
|
+
'audio2\n',
|
|
619
|
+
].join('');
|
|
620
|
+
|
|
621
|
+
const media = [
|
|
622
|
+
'#EXTM3U\n',
|
|
623
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
624
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
625
|
+
'#EXTINF:5,\n',
|
|
626
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
627
|
+
'main.mp4',
|
|
628
|
+
].join('');
|
|
629
|
+
|
|
630
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
631
|
+
manifest.anyTimeline();
|
|
632
|
+
manifest.addPartialVariant((variant) => {
|
|
633
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
634
|
+
stream.mime('audio/mp4', 'Opus');
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
manifest.addPartialVariant((variant) => {
|
|
638
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
639
|
+
stream.mime('audio/mp4', 'opus');
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
manifest.sequenceMode = true;
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
await testHlsParser(master, media, manifest);
|
|
646
|
+
});
|
|
647
|
+
|
|
576
648
|
it('parses audio+video variant with closed captions', async () => {
|
|
577
649
|
const master = [
|
|
578
650
|
'#EXTM3U\n',
|
|
@@ -983,6 +1055,81 @@ describe('HlsParser', () => {
|
|
|
983
1055
|
await testHlsParser(master, media, manifest);
|
|
984
1056
|
});
|
|
985
1057
|
|
|
1058
|
+
it('adds subtitle role when characteristics are empty', async () => {
|
|
1059
|
+
const master = [
|
|
1060
|
+
'#EXTM3U\n',
|
|
1061
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
1062
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1063
|
+
'video\n',
|
|
1064
|
+
|
|
1065
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
|
|
1066
|
+
'NAME="English",URI="audio"\n',
|
|
1067
|
+
|
|
1068
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
|
|
1069
|
+
'CHARACTERISTICS="public.accessibility.describes-video,',
|
|
1070
|
+
'public.accessibility.describes-music-and-sound",',
|
|
1071
|
+
'NAME="English (describes-video)",URI="audio2"\n',
|
|
1072
|
+
|
|
1073
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
|
|
1074
|
+
'NAME="English (subtitle)",DEFAULT=YES,AUTOSELECT=YES,',
|
|
1075
|
+
'URI="text"\n',
|
|
1076
|
+
|
|
1077
|
+
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
|
|
1078
|
+
'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
|
|
1079
|
+
'CHARACTERISTICS="public.accessibility.describes-music-and-sound",',
|
|
1080
|
+
'URI="text2"\n',
|
|
1081
|
+
].join('');
|
|
1082
|
+
|
|
1083
|
+
const media = [
|
|
1084
|
+
'#EXTM3U\n',
|
|
1085
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1086
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
1087
|
+
'#EXTINF:5,\n',
|
|
1088
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
1089
|
+
'main.mp4',
|
|
1090
|
+
].join('');
|
|
1091
|
+
|
|
1092
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
1093
|
+
manifest.anyTimeline();
|
|
1094
|
+
manifest.addPartialVariant((variant) => {
|
|
1095
|
+
variant.language = 'en';
|
|
1096
|
+
variant.addPartialStream(ContentType.VIDEO);
|
|
1097
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1098
|
+
stream.language = 'en';
|
|
1099
|
+
stream.roles = [];
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
manifest.addPartialVariant((variant) => {
|
|
1103
|
+
variant.language = 'en';
|
|
1104
|
+
variant.addPartialStream(ContentType.VIDEO);
|
|
1105
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
1106
|
+
stream.language = 'en';
|
|
1107
|
+
stream.roles = [
|
|
1108
|
+
'public.accessibility.describes-video',
|
|
1109
|
+
'public.accessibility.describes-music-and-sound',
|
|
1110
|
+
];
|
|
1111
|
+
});
|
|
1112
|
+
});
|
|
1113
|
+
manifest.addPartialTextStream((stream) => {
|
|
1114
|
+
stream.language = 'en';
|
|
1115
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1116
|
+
stream.roles = [
|
|
1117
|
+
'subtitle',
|
|
1118
|
+
];
|
|
1119
|
+
});
|
|
1120
|
+
manifest.addPartialTextStream((stream) => {
|
|
1121
|
+
stream.language = 'en';
|
|
1122
|
+
stream.kind = TextStreamKind.SUBTITLE;
|
|
1123
|
+
stream.roles = [
|
|
1124
|
+
'public.accessibility.describes-music-and-sound',
|
|
1125
|
+
];
|
|
1126
|
+
});
|
|
1127
|
+
manifest.sequenceMode = true;
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
await testHlsParser(master, media, manifest);
|
|
1131
|
+
});
|
|
1132
|
+
|
|
986
1133
|
it('parses characteristics from text tags', async () => {
|
|
987
1134
|
const master = [
|
|
988
1135
|
'#EXTM3U\n',
|
|
@@ -1652,6 +1799,81 @@ describe('HlsParser', () => {
|
|
|
1652
1799
|
}
|
|
1653
1800
|
});
|
|
1654
1801
|
|
|
1802
|
+
it('ignore segments with #EXTINF:0', async () => {
|
|
1803
|
+
const master = [
|
|
1804
|
+
'#EXTM3U\n',
|
|
1805
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
1806
|
+
'CHANNELS="2",URI="audio"\n',
|
|
1807
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
1808
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
1809
|
+
'video\n',
|
|
1810
|
+
].join('');
|
|
1811
|
+
|
|
1812
|
+
const video = [
|
|
1813
|
+
'#EXTM3U\n',
|
|
1814
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1815
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
1816
|
+
'#EXTINF:5,\n',
|
|
1817
|
+
'main.mp4\n',
|
|
1818
|
+
'#EXTINF:0,\n',
|
|
1819
|
+
'main.mp4\n',
|
|
1820
|
+
'#EXTINF:5,\n',
|
|
1821
|
+
'main.mp4\n',
|
|
1822
|
+
].join('');
|
|
1823
|
+
|
|
1824
|
+
const audio = [
|
|
1825
|
+
'#EXTM3U\n',
|
|
1826
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
1827
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
1828
|
+
'#EXTINF:5,\n',
|
|
1829
|
+
'main.mp4\n',
|
|
1830
|
+
'#EXTINF:0,\n',
|
|
1831
|
+
'main.mp4\n',
|
|
1832
|
+
'#EXTINF:5,\n',
|
|
1833
|
+
'main.mp4\n',
|
|
1834
|
+
].join('');
|
|
1835
|
+
|
|
1836
|
+
fakeNetEngine
|
|
1837
|
+
.setResponseText('test:/master', master)
|
|
1838
|
+
.setResponseText('test:/audio', audio)
|
|
1839
|
+
.setResponseText('test:/video', video)
|
|
1840
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1841
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
1842
|
+
|
|
1843
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
1844
|
+
|
|
1845
|
+
expect(actual.variants.length).toBe(1);
|
|
1846
|
+
|
|
1847
|
+
const variant = actual.variants[0];
|
|
1848
|
+
expect(variant.video).toBeTruthy();
|
|
1849
|
+
expect(variant.audio).toBeTruthy();
|
|
1850
|
+
|
|
1851
|
+
await variant.video.createSegmentIndex();
|
|
1852
|
+
goog.asserts.assert(variant.video.segmentIndex != null,
|
|
1853
|
+
'Null segmentIndex!');
|
|
1854
|
+
|
|
1855
|
+
const firstVideoReference = variant.video.segmentIndex.get(0);
|
|
1856
|
+
const secondVideoReference = variant.video.segmentIndex.get(1);
|
|
1857
|
+
const thirdVideoReference = variant.video.segmentIndex.get(2);
|
|
1858
|
+
|
|
1859
|
+
expect(firstVideoReference).not.toBe(null);
|
|
1860
|
+
expect(secondVideoReference).not.toBe(null);
|
|
1861
|
+
expect(thirdVideoReference).toBe(null);
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
await variant.audio.createSegmentIndex();
|
|
1865
|
+
goog.asserts.assert(variant.audio.segmentIndex != null,
|
|
1866
|
+
'Null segmentIndex!');
|
|
1867
|
+
|
|
1868
|
+
const firstAudioReference = variant.audio.segmentIndex.get(0);
|
|
1869
|
+
const secondAudioReference = variant.audio.segmentIndex.get(1);
|
|
1870
|
+
const thirdAudioReference = variant.audio.segmentIndex.get(2);
|
|
1871
|
+
|
|
1872
|
+
expect(firstAudioReference).not.toBe(null);
|
|
1873
|
+
expect(secondAudioReference).not.toBe(null);
|
|
1874
|
+
expect(thirdAudioReference).toBe(null);
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1655
1877
|
it('Disable audio does not create audio streams', async () => {
|
|
1656
1878
|
const master = [
|
|
1657
1879
|
'#EXTM3U\n',
|
|
@@ -242,7 +242,7 @@ describe('DrmEngine', () => {
|
|
|
242
242
|
/* hasClosedCaptions= */ false);
|
|
243
243
|
|
|
244
244
|
expect(video.buffered.end(0)).toBeGreaterThan(0);
|
|
245
|
-
video.play();
|
|
245
|
+
await video.play();
|
|
246
246
|
|
|
247
247
|
const waiter = new shaka.test.Waiter(eventManager).timeoutAfter(15);
|
|
248
248
|
waiter.setMediaSourceEngine(mediaSourceEngine);
|
|
@@ -325,7 +325,7 @@ describe('DrmEngine', () => {
|
|
|
325
325
|
/* hasClosedCaptions= */ false);
|
|
326
326
|
|
|
327
327
|
expect(video.buffered.end(0)).toBeGreaterThan(0);
|
|
328
|
-
video.play();
|
|
328
|
+
await video.play();
|
|
329
329
|
|
|
330
330
|
const waiter = new shaka.test.Waiter(eventManager).timeoutAfter(15);
|
|
331
331
|
waiter.setMediaSourceEngine(mediaSourceEngine);
|
|
@@ -31,7 +31,8 @@ describe('RegionObserver', () => {
|
|
|
31
31
|
timeline = new shaka.media.RegionTimeline(
|
|
32
32
|
shaka.test.Util.spyFunc(onSeekRange));
|
|
33
33
|
|
|
34
|
-
observer = new shaka.media.RegionObserver(
|
|
34
|
+
observer = new shaka.media.RegionObserver(
|
|
35
|
+
timeline, /* startsPastZero= */ false);
|
|
35
36
|
observer.addEventListener('enter', (event) => {
|
|
36
37
|
shaka.test.Util.spyFunc(onEnterRegion)(event['region'], event['seeking']);
|
|
37
38
|
});
|
|
@@ -974,9 +974,9 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
|
|
|
974
974
|
return [newRefs.shift()];
|
|
975
975
|
});
|
|
976
976
|
});
|
|
977
|
-
|
|
978
|
-
//
|
|
979
|
-
await
|
|
977
|
+
|
|
978
|
+
// Wait for the new refs to be appended.
|
|
979
|
+
await done;
|
|
980
980
|
|
|
981
981
|
expect(Array.from(metaIndex)).toEqual(oldRefs.concat(inputRefs2));
|
|
982
982
|
});
|
|
@@ -57,6 +57,9 @@ describe('StreamingEngine', () => {
|
|
|
57
57
|
beforeEach(() => {
|
|
58
58
|
config = shaka.util.PlayerConfiguration.createDefault().streaming;
|
|
59
59
|
|
|
60
|
+
// Disable stall detection, which can interfere with playback tests.
|
|
61
|
+
config.stallEnabled = false;
|
|
62
|
+
|
|
60
63
|
onError = jasmine.createSpy('onError');
|
|
61
64
|
onError.and.callFake(fail);
|
|
62
65
|
onEvent = jasmine.createSpy('onEvent');
|
|
@@ -276,7 +279,7 @@ describe('StreamingEngine', () => {
|
|
|
276
279
|
// Let's go!
|
|
277
280
|
streamingEngine.switchVariant(variant);
|
|
278
281
|
await streamingEngine.start();
|
|
279
|
-
video.play();
|
|
282
|
+
await video.play();
|
|
280
283
|
// The overall test timeout is 120 seconds, and the content is 40
|
|
281
284
|
// seconds. It should be possible to complete this test in 100 seconds,
|
|
282
285
|
// and if not, we want the error thrown to be within the overall test's
|
|
@@ -296,7 +299,7 @@ describe('StreamingEngine', () => {
|
|
|
296
299
|
// Let's go!
|
|
297
300
|
streamingEngine.switchVariant(variant);
|
|
298
301
|
await streamingEngine.start();
|
|
299
|
-
video.play();
|
|
302
|
+
await video.play();
|
|
300
303
|
|
|
301
304
|
// Wait for playback to begin before increasing the playback rate. This
|
|
302
305
|
// improves test reliability on slow platforms like Chromecast.
|
|
@@ -310,7 +313,7 @@ describe('StreamingEngine', () => {
|
|
|
310
313
|
// Let's go!
|
|
311
314
|
streamingEngine.switchVariant(variant);
|
|
312
315
|
await streamingEngine.start();
|
|
313
|
-
video.play();
|
|
316
|
+
await video.play();
|
|
314
317
|
|
|
315
318
|
// After 35 seconds seek back 10 seconds into the first Period.
|
|
316
319
|
await waiter.timeoutAfter(80).waitUntilPlayheadReaches(video, 35);
|
|
@@ -322,7 +325,7 @@ describe('StreamingEngine', () => {
|
|
|
322
325
|
// Let's go!
|
|
323
326
|
streamingEngine.switchVariant(variant);
|
|
324
327
|
await streamingEngine.start();
|
|
325
|
-
video.play();
|
|
328
|
+
await video.play();
|
|
326
329
|
await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 20);
|
|
327
330
|
video.currentTime = 40;
|
|
328
331
|
await waiter.timeoutAfter(60).waitForEnd(video);
|
|
@@ -350,7 +353,7 @@ describe('StreamingEngine', () => {
|
|
|
350
353
|
streamingEngine.switchVariant(variant);
|
|
351
354
|
await streamingEngine.start();
|
|
352
355
|
|
|
353
|
-
video.play();
|
|
356
|
+
await video.play();
|
|
354
357
|
await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 305);
|
|
355
358
|
|
|
356
359
|
const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
@@ -370,7 +373,7 @@ describe('StreamingEngine', () => {
|
|
|
370
373
|
// Seek outside the availability window right away. The playhead
|
|
371
374
|
// should adjust the video's current time.
|
|
372
375
|
video.currentTime = segmentAvailability.end + 120;
|
|
373
|
-
video.play();
|
|
376
|
+
await video.play();
|
|
374
377
|
|
|
375
378
|
// Wait until the repositioning is complete so we don't
|
|
376
379
|
// immediately hit this case.
|
|
@@ -395,7 +398,7 @@ describe('StreamingEngine', () => {
|
|
|
395
398
|
video.currentTime = segmentAvailability.start - 120;
|
|
396
399
|
expect(video.currentTime).toBeGreaterThan(0);
|
|
397
400
|
|
|
398
|
-
video.play();
|
|
401
|
+
await video.play();
|
|
399
402
|
await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 305);
|
|
400
403
|
|
|
401
404
|
// We are playing close to the beginning of the availability window.
|
|
@@ -423,7 +426,7 @@ describe('StreamingEngine', () => {
|
|
|
423
426
|
// Let's go!
|
|
424
427
|
streamingEngine.switchVariant(variant);
|
|
425
428
|
await streamingEngine.start();
|
|
426
|
-
video.play();
|
|
429
|
+
await video.play();
|
|
427
430
|
|
|
428
431
|
await waiter.timeoutAfter(5).waitUntilPlayheadReaches(video, 0.01);
|
|
429
432
|
expect(video.buffered.length).toBeGreaterThan(0);
|
|
@@ -438,7 +441,7 @@ describe('StreamingEngine', () => {
|
|
|
438
441
|
// Let's go!
|
|
439
442
|
streamingEngine.switchVariant(variant);
|
|
440
443
|
await streamingEngine.start();
|
|
441
|
-
video.play();
|
|
444
|
+
await video.play();
|
|
442
445
|
|
|
443
446
|
await waiter.timeoutAfter(5).waitUntilPlayheadReaches(video, 0.01);
|
|
444
447
|
expect(video.buffered.length).toBeGreaterThan(0);
|
|
@@ -457,7 +460,7 @@ describe('StreamingEngine', () => {
|
|
|
457
460
|
await waiter.timeoutAfter(5).waitForEvent(video, 'loadeddata');
|
|
458
461
|
|
|
459
462
|
video.currentTime = 8;
|
|
460
|
-
video.play();
|
|
463
|
+
await video.play();
|
|
461
464
|
|
|
462
465
|
await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 23);
|
|
463
466
|
// Should be close enough to still have the gap buffered.
|
|
@@ -474,7 +477,7 @@ describe('StreamingEngine', () => {
|
|
|
474
477
|
await waiter.timeoutAfter(5).waitForEvent(video, 'loadeddata');
|
|
475
478
|
|
|
476
479
|
video.currentTime = 8;
|
|
477
|
-
video.play();
|
|
480
|
+
await video.play();
|
|
478
481
|
|
|
479
482
|
await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 23);
|
|
480
483
|
// Should be close enough to still have the gap buffered.
|
|
@@ -33,6 +33,9 @@ filterDescribe('Offline', supportsStorage, () => {
|
|
|
33
33
|
player = new shaka.Player(video);
|
|
34
34
|
player.addEventListener('error', fail);
|
|
35
35
|
|
|
36
|
+
// Disable stall detection, which can interfere with playback tests.
|
|
37
|
+
player.configure('streaming.stallEnabled', false);
|
|
38
|
+
|
|
36
39
|
eventManager = new shaka.util.EventManager();
|
|
37
40
|
waiter = new shaka.test.Waiter(eventManager);
|
|
38
41
|
waiter.setPlayer(player);
|
|
@@ -67,7 +70,7 @@ filterDescribe('Offline', supportsStorage, () => {
|
|
|
67
70
|
|
|
68
71
|
await player.load(contentUri);
|
|
69
72
|
|
|
70
|
-
video.play();
|
|
73
|
+
await video.play();
|
|
71
74
|
await playTo(/* end= */ 3, /* timeout= */ 20);
|
|
72
75
|
await player.unload();
|
|
73
76
|
await storage.remove(contentUri);
|
|
@@ -103,7 +106,7 @@ filterDescribe('Offline', supportsStorage, () => {
|
|
|
103
106
|
|
|
104
107
|
await player.load(contentUri);
|
|
105
108
|
|
|
106
|
-
video.play();
|
|
109
|
+
await video.play();
|
|
107
110
|
await playTo(/* end= */ 3, /* timeout= */ 20);
|
|
108
111
|
await player.unload();
|
|
109
112
|
await storage.remove(contentUri);
|
|
@@ -146,7 +149,7 @@ filterDescribe('Offline', supportsStorage, () => {
|
|
|
146
149
|
|
|
147
150
|
await player.load(contentUri);
|
|
148
151
|
|
|
149
|
-
video.play();
|
|
152
|
+
await video.play();
|
|
150
153
|
await playTo(/* end= */ 3, /* timeout= */ 20);
|
|
151
154
|
await player.unload();
|
|
152
155
|
await storage.remove(contentUri);
|
package/test/player_external.js
CHANGED
|
@@ -36,6 +36,24 @@ describe('Player', () => {
|
|
|
36
36
|
beforeEach(() => {
|
|
37
37
|
player = new compiledShaka.Player(video);
|
|
38
38
|
|
|
39
|
+
// Make sure we are playing the lowest res available to avoid test flake
|
|
40
|
+
// based on network issues. Note that disabling ABR and setting a low
|
|
41
|
+
// abr.defaultBandwidthEstimate would not be sufficient, because it
|
|
42
|
+
// would only affect the choice of track on the first period. When we
|
|
43
|
+
// cross a period boundary, the default bandwidth estimate will no
|
|
44
|
+
// longer be in effect, and AbrManager may choose higher res tracks for
|
|
45
|
+
// the new period. Using abr.restrictions.maxHeight will let us force
|
|
46
|
+
// AbrManager to the lowest resolution, which is its fallback when these
|
|
47
|
+
// soft restrictions cannot be met.
|
|
48
|
+
player.configure('abr.restrictions.maxHeight', 1);
|
|
49
|
+
|
|
50
|
+
// Make sure that live streams are synced against a good clock.
|
|
51
|
+
player.configure('manifest.dash.clockSyncUri',
|
|
52
|
+
'https://shaka-player-demo.appspot.com/time.txt');
|
|
53
|
+
|
|
54
|
+
// Disable stall detection, which can interfere with playback tests.
|
|
55
|
+
player.configure('streaming.stallEnabled', false);
|
|
56
|
+
|
|
39
57
|
// Grab event manager from the uncompiled library:
|
|
40
58
|
eventManager = new shaka.util.EventManager();
|
|
41
59
|
waiter = new shaka.test.Waiter(eventManager);
|
|
@@ -92,21 +110,6 @@ describe('Player', () => {
|
|
|
92
110
|
}
|
|
93
111
|
}
|
|
94
112
|
|
|
95
|
-
// Make sure we are playing the lowest res available to avoid test flake
|
|
96
|
-
// based on network issues. Note that disabling ABR and setting a low
|
|
97
|
-
// abr.defaultBandwidthEstimate would not be sufficient, because it
|
|
98
|
-
// would only affect the choice of track on the first period. When we
|
|
99
|
-
// cross a period boundary, the default bandwidth estimate will no
|
|
100
|
-
// longer be in effect, and AbrManager may choose higher res tracks for
|
|
101
|
-
// the new period. Using abr.restrictions.maxHeight will let us force
|
|
102
|
-
// AbrManager to the lowest resolution, which is its fallback when these
|
|
103
|
-
// soft restrictions cannot be met.
|
|
104
|
-
player.configure('abr.restrictions.maxHeight', 1);
|
|
105
|
-
|
|
106
|
-
// Make sure that live streams are synced against a good clock.
|
|
107
|
-
player.configure('manifest.dash.clockSyncUri',
|
|
108
|
-
'https://shaka-player-demo.appspot.com/time.txt');
|
|
109
|
-
|
|
110
113
|
// Add asset-specific configuration.
|
|
111
114
|
player.configure(asset.getConfiguration());
|
|
112
115
|
|
|
@@ -125,7 +128,7 @@ describe('Player', () => {
|
|
|
125
128
|
const isLive = asset.features.includes(Feature.LIVE);
|
|
126
129
|
expect(player.isLive()).toBe(isLive);
|
|
127
130
|
}
|
|
128
|
-
video.play();
|
|
131
|
+
await video.play();
|
|
129
132
|
|
|
130
133
|
// Wait for the video to start playback. If it takes longer than 20
|
|
131
134
|
// seconds, fail the test.
|