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.
Files changed (73) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +11 -0
  2. package/.github/workflows/appspot.yaml +39 -0
  3. package/.github/workflows/build-and-test.yaml +15 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +3 -2
  5. package/.github/workflows/update-issues.yaml +1 -0
  6. package/CHANGELOG.md +42 -0
  7. package/CONTRIBUTORS +1 -0
  8. package/demo/custom.js +1 -1
  9. package/dist/demo.compiled.debug.js +2 -2
  10. package/dist/demo.compiled.debug.map +2 -2
  11. package/dist/demo.compiled.js +2 -2
  12. package/dist/demo.compiled.map +2 -2
  13. package/dist/deps.js +3 -3
  14. package/dist/locales.js +33 -33
  15. package/dist/shaka-player.compiled.d.ts +22 -0
  16. package/dist/shaka-player.compiled.debug.d.ts +22 -0
  17. package/dist/shaka-player.compiled.debug.js +233 -232
  18. package/dist/shaka-player.compiled.debug.map +3 -3
  19. package/dist/shaka-player.compiled.js +738 -738
  20. package/dist/shaka-player.compiled.map +3 -3
  21. package/dist/shaka-player.ui.d.ts +22 -0
  22. package/dist/shaka-player.ui.debug.d.ts +22 -0
  23. package/dist/shaka-player.ui.debug.js +259 -258
  24. package/dist/shaka-player.ui.debug.map +3 -3
  25. package/dist/shaka-player.ui.js +917 -916
  26. package/dist/shaka-player.ui.map +4 -4
  27. package/docs/tutorials/drm-config.md +9 -6
  28. package/docs/tutorials/selenium-grid-config.md +60 -8
  29. package/externs/ima.js +34 -0
  30. package/karma.conf.js +50 -7
  31. package/lib/abr/simple_abr_manager.js +6 -6
  32. package/lib/cea/mp4_cea_parser.js +30 -14
  33. package/lib/dash/dash_parser.js +18 -3
  34. package/lib/hls/hls_parser.js +13 -2
  35. package/lib/media/drm_engine.js +14 -8
  36. package/lib/media/gap_jumping_controller.js +24 -2
  37. package/lib/media/media_source_engine.js +5 -3
  38. package/lib/media/playhead.js +11 -0
  39. package/lib/media/region_observer.js +19 -1
  40. package/lib/media/streaming_engine.js +7 -3
  41. package/lib/media/video_wrapper.js +6 -1
  42. package/lib/player.js +9 -8
  43. package/lib/polyfill/abort_controller.js +7 -0
  44. package/lib/polyfill/orientation.js +34 -7
  45. package/lib/text/vtt_text_parser.js +3 -1
  46. package/lib/util/manifest_parser_utils.js +5 -3
  47. package/lib/util/mp4_box_parsers.js +8 -3
  48. package/lib/util/platform.js +14 -0
  49. package/lib/util/stream_utils.js +70 -26
  50. package/package.json +2 -3
  51. package/test/cast/cast_utils_unit.js +4 -1
  52. package/test/cea/mp4_cea_parser_unit.js +26 -3
  53. package/test/dash/dash_parser_manifest_unit.js +120 -0
  54. package/test/hls/hls_parser_unit.js +222 -0
  55. package/test/media/drm_engine_integration.js +2 -2
  56. package/test/media/region_observer_unit.js +2 -1
  57. package/test/media/segment_index_unit.js +3 -3
  58. package/test/media/streaming_engine_integration.js +14 -11
  59. package/test/offline/offline_integration.js +6 -3
  60. package/test/player_external.js +19 -16
  61. package/test/player_integration.js +35 -48
  62. package/test/player_src_equals_integration.js +9 -5
  63. package/test/test/assets/multiple-trun-init.mp4 +0 -0
  64. package/test/test/assets/multiple-trun-segment.mp4 +0 -0
  65. package/test/test/util/fake_demo_main.js +7 -3
  66. package/test/test/util/layout_tests.js +2 -2
  67. package/test/test/util/ui_utils.js +9 -2
  68. package/test/test/util/util.js +6 -27
  69. package/test/test/util/waiter.js +73 -3
  70. package/test/ui/ad_ui_unit.js +9 -9
  71. package/test/util/stream_utils_unit.js +46 -0
  72. package/ui/range_element.js +5 -0
  73. 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
- const responses = await Promise.all([
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(timeline);
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
- // Wait long enough for all three new refs to be appended.
978
- // Or for all of the new refs to be passed out.
979
- await Promise.race([shaka.test.Util.delay(1), done]);
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);
@@ -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.