shaka-player 3.1.1 → 3.1.5
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/AUTHORS +7 -2
- package/CHANGELOG.md +199 -0
- package/CONTRIBUTORS +9 -3
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/demo/common/assets.js +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +7 -7
- package/dist/locales.js +35 -33
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +171 -3
- package/dist/shaka-player.compiled.debug.d.ts +171 -3
- package/dist/shaka-player.compiled.debug.externs.js +179 -1
- package/dist/shaka-player.compiled.debug.js +365 -344
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +179 -1
- package/dist/shaka-player.compiled.js +806 -798
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +171 -3
- package/dist/shaka-player.ui.debug.d.ts +171 -3
- package/dist/shaka-player.ui.debug.externs.js +181 -3
- package/dist/shaka-player.ui.debug.js +458 -435
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -3
- package/dist/shaka-player.ui.js +927 -918
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/docs/tutorials/index.json +2 -2
- package/docs/tutorials/upgrade-v3.md +38 -0
- package/docs/tutorials/upgrade.md +3 -3
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/player.js +6 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +1 -0
- package/lib/ads/ad_manager.js +29 -11
- package/lib/ads/client_side_ad_manager.js +76 -86
- package/lib/ads/server_side_ad_manager.js +26 -11
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +100 -5
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +7 -4
- package/lib/hls/hls_parser.js +44 -17
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/presentation_timeline.js +3 -1
- package/lib/media/segment_reference.js +8 -0
- package/lib/media/streaming_engine.js +28 -19
- package/lib/net/networking_engine.js +2 -1
- package/lib/player.js +108 -54
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +76 -22
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +17 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +135 -87
- package/lib/text/ui_text_displayer.js +233 -83
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +111 -35
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +79 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +2 -2
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +220 -2
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/hls/hls_parser_unit.js +5 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +56 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +148 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +101 -0
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +4 -1
- package/test/test/util/simple_fakes.js +5 -3
- package/test/test/util/ttml_utils.js +81 -0
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- package/test/text/ui_text_displayer_unit.js +95 -18
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +52 -1
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/periods_unit.js +190 -2
- package/test/util/stream_utils_unit.js +59 -2
- package/ui/airplay_button.js +5 -0
- package/ui/big_play_button.js +10 -0
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +8 -6
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +10 -5
- package/ui/locales/en.json +1 -0
- package/ui/locales/ru.json +6 -6
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +28 -51
- package/ui/pip_button.js +2 -3
- package/ui/play_button.js +10 -10
- package/ui/small_play_button.js +12 -0
- package/ui/text_selection.js +1 -0
- package/docs/tutorials/upgrade-v2-4.md +0 -481
|
@@ -38,6 +38,14 @@ describe('DashParser Manifest', () => {
|
|
|
38
38
|
|
|
39
39
|
/** @type {!string} */
|
|
40
40
|
const thumbnailScheme = 'http://dashif.org/guidelines/thumbnail_tile';
|
|
41
|
+
/**
|
|
42
|
+
* CICP scheme. parameter must be one of the following: "ColourPrimaries",
|
|
43
|
+
* "TransferCharacteristics", or "MatrixCoefficients".
|
|
44
|
+
*
|
|
45
|
+
* @param {string} parameter
|
|
46
|
+
* @return {string}
|
|
47
|
+
*/
|
|
48
|
+
const cicpScheme = (parameter) => `urn:mpeg:mpegB:cicp:${parameter}`;
|
|
41
49
|
|
|
42
50
|
beforeAll(async () => {
|
|
43
51
|
mp4Index = await shaka.test.Util.fetch(mp4IndexSegmentUri);
|
|
@@ -1822,7 +1830,7 @@ describe('DashParser Manifest', () => {
|
|
|
1822
1830
|
expect(textStream.kind).toBe('caption');
|
|
1823
1831
|
});
|
|
1824
1832
|
|
|
1825
|
-
it('converts Roles element to "forced"', async () => {
|
|
1833
|
+
it('converts Roles element to "forced" (old role)', async () => {
|
|
1826
1834
|
const manifestText = [
|
|
1827
1835
|
'<MPD minBufferTime="PT75S">',
|
|
1828
1836
|
' <Period id="1" duration="PT30S">',
|
|
@@ -1854,7 +1862,40 @@ describe('DashParser Manifest', () => {
|
|
|
1854
1862
|
expect(textStream.forced).toBe(true);
|
|
1855
1863
|
});
|
|
1856
1864
|
|
|
1857
|
-
it('
|
|
1865
|
+
it('converts Roles element to "forced"', async () => {
|
|
1866
|
+
const manifestText = [
|
|
1867
|
+
'<MPD minBufferTime="PT75S">',
|
|
1868
|
+
' <Period id="1" duration="PT30S">',
|
|
1869
|
+
' <AdaptationSet id="1" contentType="text">',
|
|
1870
|
+
' <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" ',
|
|
1871
|
+
' value="captions" />',
|
|
1872
|
+
' <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" ',
|
|
1873
|
+
' value="forced-subtitle" />',
|
|
1874
|
+
' <Accessibility schemeIdUri="foobar" value="bar" />',
|
|
1875
|
+
' <Representation id="text-en" mimeType="text/webvtt">',
|
|
1876
|
+
' <BaseURL>t-en.vtt</BaseURL>',
|
|
1877
|
+
' </Representation>',
|
|
1878
|
+
' </AdaptationSet>',
|
|
1879
|
+
' <AdaptationSet id="1" mimeType="video/mp4">',
|
|
1880
|
+
' <Representation id="video-sd" width="640" height="480">',
|
|
1881
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1882
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1883
|
+
' </Representation>',
|
|
1884
|
+
' </AdaptationSet>',
|
|
1885
|
+
' </Period>',
|
|
1886
|
+
'</MPD>',
|
|
1887
|
+
].join('\n');
|
|
1888
|
+
|
|
1889
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1890
|
+
/** @type {shaka.extern.Manifest} */
|
|
1891
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1892
|
+
const textStream = manifest.textStreams[0];
|
|
1893
|
+
expect(textStream.roles).toEqual(['captions', 'forced-subtitle']);
|
|
1894
|
+
expect(textStream.forced).toBe(true);
|
|
1895
|
+
});
|
|
1896
|
+
|
|
1897
|
+
it('supports HDR signaling via profiles', async () => {
|
|
1898
|
+
// (DASH-IF IOP v4.3 10.3.3.)
|
|
1858
1899
|
const hdrProfile =
|
|
1859
1900
|
'http://dashif.org/guidelines/dash-if-uhd#hevc-hdr-pq10';
|
|
1860
1901
|
const manifestText = [
|
|
@@ -1885,6 +1926,110 @@ describe('DashParser Manifest', () => {
|
|
|
1885
1926
|
expect(stream.hdr).toBe('PQ');
|
|
1886
1927
|
});
|
|
1887
1928
|
|
|
1929
|
+
it('supports HDR signaling via SupplementalProperty', async () => {
|
|
1930
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1931
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1932
|
+
const pq = 16;
|
|
1933
|
+
const manifestText = [
|
|
1934
|
+
'<MPD minBufferTime="PT75S">',
|
|
1935
|
+
' <Period id="1" duration="PT30S">',
|
|
1936
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1937
|
+
` <SupplementalProperty schemeIdUri="${hdrScheme}" value="${pq}" />`,
|
|
1938
|
+
' <Representation codecs="hvc1.2.4.L150.90">',
|
|
1939
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1940
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1941
|
+
' </Representation>',
|
|
1942
|
+
' </AdaptationSet>',
|
|
1943
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1944
|
+
' <Representation id="audio-en">',
|
|
1945
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1946
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1947
|
+
' </Representation>',
|
|
1948
|
+
' </AdaptationSet>',
|
|
1949
|
+
' </Period>',
|
|
1950
|
+
'</MPD>',
|
|
1951
|
+
].join('\n');
|
|
1952
|
+
|
|
1953
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1954
|
+
|
|
1955
|
+
/** @type {shaka.extern.Manifest} */
|
|
1956
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1957
|
+
expect(manifest.variants.length).toBe(1);
|
|
1958
|
+
const stream = manifest.variants[0].video;
|
|
1959
|
+
expect(stream.hdr).toBe('PQ');
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
it('supports HDR signaling via EssentialProperty', async () => {
|
|
1963
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1964
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1965
|
+
const hlg = 18;
|
|
1966
|
+
const manifestText = [
|
|
1967
|
+
'<MPD minBufferTime="PT75S">',
|
|
1968
|
+
' <Period id="1" duration="PT30S">',
|
|
1969
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1970
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="${hlg}" />`,
|
|
1971
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
1972
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1973
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1974
|
+
' </Representation>',
|
|
1975
|
+
' </AdaptationSet>',
|
|
1976
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1977
|
+
' <Representation id="audio-en">',
|
|
1978
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1979
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1980
|
+
' </Representation>',
|
|
1981
|
+
' </AdaptationSet>',
|
|
1982
|
+
' </Period>',
|
|
1983
|
+
'</MPD>',
|
|
1984
|
+
].join('\n');
|
|
1985
|
+
|
|
1986
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1987
|
+
|
|
1988
|
+
/** @type {shaka.extern.Manifest} */
|
|
1989
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1990
|
+
expect(manifest.variants.length).toBe(1);
|
|
1991
|
+
const stream = manifest.variants[0].video;
|
|
1992
|
+
expect(stream.hdr).toBe('HLG');
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1995
|
+
it('supports SDR signalling via EssentialProperty', async () => {
|
|
1996
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1997
|
+
const scheme = cicpScheme('TransferCharacteristics');
|
|
1998
|
+
const sdrValues = [1, 6, 13, 14, 15];
|
|
1999
|
+
const manifestPromises = [];
|
|
2000
|
+
for (const value of sdrValues) {
|
|
2001
|
+
const manifestText = [
|
|
2002
|
+
'<MPD minBufferTime="PT75S">',
|
|
2003
|
+
' <Period id="1" duration="PT30S">',
|
|
2004
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2005
|
+
` <EssentialProperty schemeIdUri="${scheme}" value="${value}" />`,
|
|
2006
|
+
' <Representation codecs="avc1.640028">',
|
|
2007
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2008
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2009
|
+
' </Representation>',
|
|
2010
|
+
' </AdaptationSet>',
|
|
2011
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2012
|
+
' <Representation id="audio-en">',
|
|
2013
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2014
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2015
|
+
' </Representation>',
|
|
2016
|
+
' </AdaptationSet>',
|
|
2017
|
+
' </Period>',
|
|
2018
|
+
'</MPD>',
|
|
2019
|
+
].join('\n');
|
|
2020
|
+
|
|
2021
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2022
|
+
|
|
2023
|
+
manifestPromises.push(parser.start('dummy://foo', playerInterface));
|
|
2024
|
+
}
|
|
2025
|
+
const manifests = await Promise.all(manifestPromises);
|
|
2026
|
+
for (const manifest of manifests) {
|
|
2027
|
+
expect(manifest.variants.length).toBe(1);
|
|
2028
|
+
const stream = manifest.variants[0].video;
|
|
2029
|
+
expect(stream.hdr).toBe('SDR');
|
|
2030
|
+
}
|
|
2031
|
+
});
|
|
2032
|
+
|
|
1888
2033
|
it('Does not error when image adaptation sets are present', async () => {
|
|
1889
2034
|
const manifestText = [
|
|
1890
2035
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -2108,4 +2253,77 @@ describe('DashParser Manifest', () => {
|
|
|
2108
2253
|
expect(uri).not.toContain('/p2/');
|
|
2109
2254
|
}
|
|
2110
2255
|
});
|
|
2256
|
+
|
|
2257
|
+
/**
|
|
2258
|
+
* @param {!Array.<number>} periods Start time of multiple periods
|
|
2259
|
+
* @return {string}
|
|
2260
|
+
*/
|
|
2261
|
+
function buildManifestWithPeriodStartTime(periods) {
|
|
2262
|
+
const mpdTemplate = [
|
|
2263
|
+
`<MPD type="dynamic"`,
|
|
2264
|
+
'availabilityStartTime="1970-01-01T00:00:00Z"',
|
|
2265
|
+
'timeShiftBufferDepth="PT10H">',
|
|
2266
|
+
' %(periods)s',
|
|
2267
|
+
'</MPD>',
|
|
2268
|
+
].join('\n');
|
|
2269
|
+
const periodTemplate = (id, period, duration) => {
|
|
2270
|
+
return [
|
|
2271
|
+
` <Period id="${id}" start="PT${period}S">`,
|
|
2272
|
+
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
|
|
2273
|
+
' <SegmentTemplate startNumber="1" media="l-$Number$.mp4">',
|
|
2274
|
+
' <SegmentTimeline>',
|
|
2275
|
+
` <S t="0" d="${duration}" />`,
|
|
2276
|
+
' </SegmentTimeline>',
|
|
2277
|
+
' </SegmentTemplate>',
|
|
2278
|
+
' <Representation id="1"/>',
|
|
2279
|
+
' </AdaptationSet>',
|
|
2280
|
+
' </Period>',
|
|
2281
|
+
].join('\n');
|
|
2282
|
+
};
|
|
2283
|
+
const periodXmls = periods.map((period, i) => {
|
|
2284
|
+
const duration = i+1 === periods.length ? 10 : periods[i+1] - period;
|
|
2285
|
+
// Period start time as ID here. If we use index then there will be
|
|
2286
|
+
// periods with same period ID and different start time which are invalid.
|
|
2287
|
+
return periodTemplate(period, period, duration);
|
|
2288
|
+
});
|
|
2289
|
+
return sprintf(mpdTemplate, {
|
|
2290
|
+
periods: periodXmls.join('\n'),
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
// Bug description: Inconsistent period start time in the manifests due
|
|
2295
|
+
// to failover triggered in backend servers
|
|
2296
|
+
|
|
2297
|
+
// When one of the servers is down, the manifest will be served by other
|
|
2298
|
+
// redundant servers. The period start time might become out of sync
|
|
2299
|
+
// during the switch-over/recovery.
|
|
2300
|
+
|
|
2301
|
+
it('skip periods that are earlier than max period start time', async () => {
|
|
2302
|
+
const sources = [
|
|
2303
|
+
buildManifestWithPeriodStartTime([5, 15]),
|
|
2304
|
+
buildManifestWithPeriodStartTime([6, 15]), // simulate out-of-sync of +1s
|
|
2305
|
+
buildManifestWithPeriodStartTime([4, 15]), // simulate out-of-sync of -1s
|
|
2306
|
+
];
|
|
2307
|
+
const segments = [];
|
|
2308
|
+
|
|
2309
|
+
for (const source of sources) {
|
|
2310
|
+
fakeNetEngine.setResponseText('dummy://foo', source);
|
|
2311
|
+
/** @type {shaka.extern.Manifest} */
|
|
2312
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2313
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2314
|
+
const video = manifest.variants[0].video;
|
|
2315
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2316
|
+
await video.createSegmentIndex();
|
|
2317
|
+
goog.asserts.assert(video.segmentIndex, 'Null segmentIndex!');
|
|
2318
|
+
segments.push(Array.from(video.segmentIndex));
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
// Expect identical segments
|
|
2322
|
+
expect(segments[0].length).toBe(2);
|
|
2323
|
+
expect(segments[1].length).toBe(2);
|
|
2324
|
+
expect(segments[0][0].startTime).toBe(5);
|
|
2325
|
+
expect(segments[1][0].startTime).toBe(5);
|
|
2326
|
+
expect(segments[0][1].startTime).toBe(15);
|
|
2327
|
+
expect(segments[1][1].startTime).toBe(15);
|
|
2328
|
+
});
|
|
2111
2329
|
});
|
|
@@ -127,9 +127,9 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
127
127
|
// The first segment is number 1 and position 0.
|
|
128
128
|
// Although the segment is 60 seconds long, it is clipped to the period
|
|
129
129
|
// duration of 30 seconds.
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
];
|
|
130
|
+
const ref = ManifestParser.makeReference('s1.mp4', 0, 30, baseUri);
|
|
131
|
+
ref.trueEndTime = 60;
|
|
132
|
+
const references = [ref];
|
|
133
133
|
await Dash.testSegmentIndex(source, references);
|
|
134
134
|
});
|
|
135
135
|
|
|
@@ -783,7 +783,7 @@ describe('HlsParser', () => {
|
|
|
783
783
|
'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud2"\n',
|
|
784
784
|
'video2\n',
|
|
785
785
|
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
786
|
-
'URI="audio"\n',
|
|
786
|
+
'DEFAULT=YES,URI="audio"\n',
|
|
787
787
|
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud2",LANGUAGE="fr",',
|
|
788
788
|
'URI="audio2"\n',
|
|
789
789
|
].join('');
|
|
@@ -801,6 +801,7 @@ describe('HlsParser', () => {
|
|
|
801
801
|
manifest.anyTimeline();
|
|
802
802
|
manifest.addPartialVariant((variant) => {
|
|
803
803
|
variant.bandwidth = 200;
|
|
804
|
+
variant.primary = true;
|
|
804
805
|
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
805
806
|
stream.size(960, 540);
|
|
806
807
|
});
|
|
@@ -810,6 +811,7 @@ describe('HlsParser', () => {
|
|
|
810
811
|
});
|
|
811
812
|
manifest.addPartialVariant((variant) => {
|
|
812
813
|
variant.bandwidth = 300;
|
|
814
|
+
variant.primary = false;
|
|
813
815
|
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
814
816
|
stream.size(960, 540);
|
|
815
817
|
});
|
|
@@ -1307,6 +1309,8 @@ describe('HlsParser', () => {
|
|
|
1307
1309
|
// stream.
|
|
1308
1310
|
const timeline = actual.presentationTimeline;
|
|
1309
1311
|
expect(timeline.getDuration()).toBe(10);
|
|
1312
|
+
expect(timeline.getSeekRangeStart()).toBe(0);
|
|
1313
|
+
expect(timeline.getSeekRangeEnd()).toBe(10);
|
|
1310
1314
|
|
|
1311
1315
|
expect(actual.textStreams.length).toBe(1);
|
|
1312
1316
|
expect(actual.variants.length).toBe(1);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.require('shaka.media.ClosedCaptionParser');
|
|
8
|
+
goog.require('shaka.test.Util');
|
|
9
|
+
|
|
10
|
+
describe('ClosedCaptionParser', () => {
|
|
11
|
+
it('can handle empty caption packets', async () => {
|
|
12
|
+
const initSegment = await shaka.test.Util.fetch(
|
|
13
|
+
'base/test/test/assets/empty_caption_video_init.mp4');
|
|
14
|
+
const videoSegment = await shaka.test.Util.fetch(
|
|
15
|
+
'base/test/test/assets/empty_caption_video_segment.mp4');
|
|
16
|
+
const parser = new shaka.media.ClosedCaptionParser();
|
|
17
|
+
parser.init(initSegment);
|
|
18
|
+
parser.parseFrom(videoSegment);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -2278,6 +2278,49 @@ function testDrmEngine(useMediaCapabilities) {
|
|
|
2278
2278
|
[drmInfoAudio]);
|
|
2279
2279
|
expect(returned).toEqual([drmInfoDesired]);
|
|
2280
2280
|
});
|
|
2281
|
+
|
|
2282
|
+
it('dedupes the merged init data based on keyId matching', () => {
|
|
2283
|
+
const serverCert = new Uint8Array(0);
|
|
2284
|
+
const drmInfoVideo = {
|
|
2285
|
+
keySystem: 'drm.abc',
|
|
2286
|
+
licenseServerUri: 'http://abc.drm/license',
|
|
2287
|
+
distinctiveIdentifierRequired: false,
|
|
2288
|
+
persistentStateRequired: true,
|
|
2289
|
+
videoRobustness: 'really_really_ridiculously_good',
|
|
2290
|
+
serverCertificate: serverCert,
|
|
2291
|
+
serverCertificateUri: '',
|
|
2292
|
+
initData: [{keyId: 'v-init'}],
|
|
2293
|
+
keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
|
|
2294
|
+
};
|
|
2295
|
+
const drmInfoAudio = {
|
|
2296
|
+
keySystem: 'drm.abc',
|
|
2297
|
+
licenseServerUri: undefined,
|
|
2298
|
+
distinctiveIdentifierRequired: true,
|
|
2299
|
+
persistentStateRequired: false,
|
|
2300
|
+
audioRobustness: 'good',
|
|
2301
|
+
serverCertificate: undefined,
|
|
2302
|
+
serverCertificateUri: '',
|
|
2303
|
+
initData: [{keyId: 'v-init'}, {keyId: 'a-init'}],
|
|
2304
|
+
keyIds: new Set(['eadbeefdeadbeefdeadbeefdeadbeefd']),
|
|
2305
|
+
};
|
|
2306
|
+
const drmInfoDesired = {
|
|
2307
|
+
keySystem: 'drm.abc',
|
|
2308
|
+
licenseServerUri: 'http://abc.drm/license',
|
|
2309
|
+
distinctiveIdentifierRequired: true,
|
|
2310
|
+
persistentStateRequired: true,
|
|
2311
|
+
audioRobustness: 'good',
|
|
2312
|
+
videoRobustness: 'really_really_ridiculously_good',
|
|
2313
|
+
serverCertificate: serverCert,
|
|
2314
|
+
initData: [{keyId: 'v-init'}, {keyId: 'a-init'}],
|
|
2315
|
+
keyIds: new Set([
|
|
2316
|
+
'deadbeefdeadbeefdeadbeefdeadbeef',
|
|
2317
|
+
'eadbeefdeadbeefdeadbeefdeadbeefd',
|
|
2318
|
+
]),
|
|
2319
|
+
};
|
|
2320
|
+
const returned = shaka.media.DrmEngine.getCommonDrmInfos([drmInfoVideo],
|
|
2321
|
+
[drmInfoAudio]);
|
|
2322
|
+
expect(returned).toEqual([drmInfoDesired]);
|
|
2323
|
+
});
|
|
2281
2324
|
}); // describe('getCommonDrmInfos')
|
|
2282
2325
|
|
|
2283
2326
|
describe('configure', () => {
|
|
@@ -2412,6 +2455,19 @@ function testDrmEngine(useMediaCapabilities) {
|
|
|
2412
2455
|
await expectAsync(drmEngine.removeSession('abc'))
|
|
2413
2456
|
.toBeRejectedWith(expected);
|
|
2414
2457
|
});
|
|
2458
|
+
|
|
2459
|
+
// Regression test for #3534
|
|
2460
|
+
it('does not remove the same session again on destroy', async () => {
|
|
2461
|
+
updatePromise.resolve();
|
|
2462
|
+
expect(session1.remove).not.toHaveBeenCalled();
|
|
2463
|
+
await drmEngine.removeSession('abc');
|
|
2464
|
+
expect(session1.remove).toHaveBeenCalled();
|
|
2465
|
+
session1.remove.calls.reset();
|
|
2466
|
+
await drmEngine.destroy();
|
|
2467
|
+
// The session should only be removed ONCE. If it's double-removed, it
|
|
2468
|
+
// will make a (non-fatal) DOMException.
|
|
2469
|
+
expect(session1.remove).not.toHaveBeenCalled();
|
|
2470
|
+
});
|
|
2415
2471
|
});
|
|
2416
2472
|
|
|
2417
2473
|
describe('expiration', () => {
|
|
@@ -1296,12 +1296,13 @@ describe('Playhead', () => {
|
|
|
1296
1296
|
});
|
|
1297
1297
|
|
|
1298
1298
|
// Regression test for https://github.com/google/shaka-player/issues/2987
|
|
1299
|
-
it('does gap jump if paused at 0', () => {
|
|
1299
|
+
it('does gap jump if paused at 0 and has autoplay', () => {
|
|
1300
1300
|
const buffered = [{start: 10, end: 20}];
|
|
1301
1301
|
video.buffered = createFakeBuffered(buffered);
|
|
1302
1302
|
video.currentTime = 0;
|
|
1303
1303
|
video.readyState = HTMLMediaElement.HAVE_ENOUGH_DATA;
|
|
1304
1304
|
video.paused = true;
|
|
1305
|
+
video.autoplay = true;
|
|
1305
1306
|
|
|
1306
1307
|
config.jumpLargeGaps = true;
|
|
1307
1308
|
playhead = new shaka.media.MediaSourcePlayhead(
|
|
@@ -1319,6 +1320,31 @@ describe('Playhead', () => {
|
|
|
1319
1320
|
expect(video.currentTime).toBe(10);
|
|
1320
1321
|
});
|
|
1321
1322
|
|
|
1323
|
+
// Regression test for https://github.com/google/shaka-player/issues/3451
|
|
1324
|
+
it('doesn\'t gap jump if paused at 0 and hasn\'t autoplay', () => {
|
|
1325
|
+
const buffered = [{start: 10, end: 20}];
|
|
1326
|
+
video.buffered = createFakeBuffered(buffered);
|
|
1327
|
+
video.currentTime = 0;
|
|
1328
|
+
video.readyState = HTMLMediaElement.HAVE_ENOUGH_DATA;
|
|
1329
|
+
video.paused = true;
|
|
1330
|
+
video.autoplay = false;
|
|
1331
|
+
|
|
1332
|
+
config.jumpLargeGaps = true;
|
|
1333
|
+
playhead = new shaka.media.MediaSourcePlayhead(
|
|
1334
|
+
video,
|
|
1335
|
+
manifest,
|
|
1336
|
+
config,
|
|
1337
|
+
/* startTime= */ 0,
|
|
1338
|
+
Util.spyFunc(onSeek),
|
|
1339
|
+
Util.spyFunc(onEvent));
|
|
1340
|
+
|
|
1341
|
+
playhead.notifyOfBufferingChange();
|
|
1342
|
+
jasmine.clock().tick(500);
|
|
1343
|
+
|
|
1344
|
+
// There should NOT have been a gap jump.
|
|
1345
|
+
expect(video.currentTime).toBe(0);
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1322
1348
|
/**
|
|
1323
1349
|
* @param {string} name
|
|
1324
1350
|
* @param {SeekTestInfo} data
|
|
@@ -290,6 +290,25 @@ describe('PresentationTimeline', () => {
|
|
|
290
290
|
// last segment time (50)
|
|
291
291
|
expect(timeline.getSegmentAvailabilityEnd()).toBe(50);
|
|
292
292
|
});
|
|
293
|
+
|
|
294
|
+
it('calculates time when there a transition of live to static', () => {
|
|
295
|
+
const timeline = makeLiveTimeline(/* availability= */ 20);
|
|
296
|
+
|
|
297
|
+
const ref1 = makeSegmentReference(0, 10);
|
|
298
|
+
const ref2 = makeSegmentReference(10, 20);
|
|
299
|
+
const ref3 = makeSegmentReference(20, 30);
|
|
300
|
+
const ref4 = makeSegmentReference(30, 40);
|
|
301
|
+
const ref5 = makeSegmentReference(40, 50);
|
|
302
|
+
|
|
303
|
+
setElapsed(50);
|
|
304
|
+
timeline.notifySegments([ref1, ref2, ref3, ref4, ref5]);
|
|
305
|
+
|
|
306
|
+
expect(timeline.getSegmentAvailabilityEnd()).toBe(50);
|
|
307
|
+
|
|
308
|
+
timeline.setStatic(true);
|
|
309
|
+
|
|
310
|
+
expect(timeline.getSegmentAvailabilityEnd()).toBe(50);
|
|
311
|
+
});
|
|
293
312
|
});
|
|
294
313
|
|
|
295
314
|
describe('getDuration', () => {
|
|
@@ -419,8 +419,9 @@ describe('StreamingEngine', () => {
|
|
|
419
419
|
// 3. Playhead seeks to force us back inside the window
|
|
420
420
|
// 4. (maybe) seek if there is a gap at the period boundary
|
|
421
421
|
// 5. (maybe) seek to flush a pipeline stall
|
|
422
|
+
// 6. (maybe) on slower platforms (e.g. GitHub actions)
|
|
422
423
|
expect(seekCount).toBeGreaterThan(2);
|
|
423
|
-
expect(seekCount).toBeLessThan(
|
|
424
|
+
expect(seekCount).toBeLessThan(7);
|
|
424
425
|
});
|
|
425
426
|
});
|
|
426
427
|
|
|
@@ -97,6 +97,9 @@ describe('StreamingEngine', () => {
|
|
|
97
97
|
/** @type {!shaka.media.StreamingEngine} */
|
|
98
98
|
let streamingEngine;
|
|
99
99
|
|
|
100
|
+
/** @type {function(function(), number)} */
|
|
101
|
+
let realSetTimeout;
|
|
102
|
+
|
|
100
103
|
/**
|
|
101
104
|
* Runs the fake event loop.
|
|
102
105
|
* @param {function()=} callback An optional callback that is executed
|
|
@@ -116,6 +119,7 @@ describe('StreamingEngine', () => {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
beforeAll(() => {
|
|
122
|
+
realSetTimeout = window.setTimeout;
|
|
119
123
|
jasmine.clock().install();
|
|
120
124
|
jasmine.clock().mockDate();
|
|
121
125
|
});
|
|
@@ -822,6 +826,53 @@ describe('StreamingEngine', () => {
|
|
|
822
826
|
.toHaveBeenCalledWith('video', 0, lt20, gt40);
|
|
823
827
|
});
|
|
824
828
|
|
|
829
|
+
// Regression test for https://github.com/google/shaka-player/issues/3717
|
|
830
|
+
it('applies fudge factors for the duration', async () => {
|
|
831
|
+
setupVod();
|
|
832
|
+
|
|
833
|
+
// In #3717, the duration was just barely large enough to encompass an
|
|
834
|
+
// additional segment, but that segment didn't exist, so playback never
|
|
835
|
+
// completed. Here, we set the duration to just beyond the 3rd segment, and
|
|
836
|
+
// we make the 4th segment fail when requested.
|
|
837
|
+
const duration = 30.000000005;
|
|
838
|
+
timeline.getDuration.and.returnValue(duration);
|
|
839
|
+
|
|
840
|
+
const targetUri = '1_video_3'; // The URI of the 4th video segment.
|
|
841
|
+
failRequestsForTarget(netEngine, targetUri);
|
|
842
|
+
|
|
843
|
+
mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
|
|
844
|
+
createStreamingEngine();
|
|
845
|
+
|
|
846
|
+
// Here we go!
|
|
847
|
+
streamingEngine.switchVariant(variant);
|
|
848
|
+
streamingEngine.switchTextStream(textStream);
|
|
849
|
+
await streamingEngine.start();
|
|
850
|
+
playing = true;
|
|
851
|
+
await runTest();
|
|
852
|
+
|
|
853
|
+
// The end of the stream should have been reached, and the 4th segment from
|
|
854
|
+
// each type should never have been requested.
|
|
855
|
+
expect(mediaSourceEngine.endOfStream).toHaveBeenCalled();
|
|
856
|
+
|
|
857
|
+
const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
858
|
+
|
|
859
|
+
netEngine.expectRequest('0_audio_0', segmentType);
|
|
860
|
+
netEngine.expectRequest('0_video_0', segmentType);
|
|
861
|
+
netEngine.expectRequest('0_text_0', segmentType);
|
|
862
|
+
|
|
863
|
+
netEngine.expectRequest('0_audio_1', segmentType);
|
|
864
|
+
netEngine.expectRequest('0_video_1', segmentType);
|
|
865
|
+
netEngine.expectRequest('0_text_1', segmentType);
|
|
866
|
+
|
|
867
|
+
netEngine.expectRequest('1_audio_2', segmentType);
|
|
868
|
+
netEngine.expectRequest('1_video_2', segmentType);
|
|
869
|
+
netEngine.expectRequest('1_text_2', segmentType);
|
|
870
|
+
|
|
871
|
+
netEngine.expectNoRequest('1_audio_3', segmentType);
|
|
872
|
+
netEngine.expectNoRequest('1_video_3', segmentType);
|
|
873
|
+
netEngine.expectNoRequest('1_text_3', segmentType);
|
|
874
|
+
});
|
|
875
|
+
|
|
825
876
|
it('does not buffer one media type ahead of another', async () => {
|
|
826
877
|
setupVod();
|
|
827
878
|
mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
|
|
@@ -835,7 +886,7 @@ describe('StreamingEngine', () => {
|
|
|
835
886
|
// This would let some media types buffer faster than others if unchecked.
|
|
836
887
|
netEngineDelays.text = 0.1;
|
|
837
888
|
netEngineDelays.audio = 1.0;
|
|
838
|
-
netEngineDelays.video =
|
|
889
|
+
netEngineDelays.video = 5.0; // Need init segment and media segment
|
|
839
890
|
|
|
840
891
|
mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
|
|
841
892
|
// Call to the underlying implementation.
|
|
@@ -1389,6 +1440,66 @@ describe('StreamingEngine', () => {
|
|
|
1389
1440
|
});
|
|
1390
1441
|
});
|
|
1391
1442
|
|
|
1443
|
+
it('into unbuffered regions when nothing is buffered ' +
|
|
1444
|
+
'and mediaState is performing an update', async () => {
|
|
1445
|
+
// Here we go!
|
|
1446
|
+
streamingEngine.switchVariant(variant);
|
|
1447
|
+
streamingEngine.switchTextStream(textStream);
|
|
1448
|
+
// ensure init source buffer promise does not resolve before seeked()
|
|
1449
|
+
// so mediaState remains in "performingUpdate" state
|
|
1450
|
+
const initSourceBufferPromise = new shaka.util.PublicPromise();
|
|
1451
|
+
mediaSourceEngine.setStreamProperties.and
|
|
1452
|
+
.returnValue(initSourceBufferPromise);
|
|
1453
|
+
await streamingEngine.start();
|
|
1454
|
+
playing = true;
|
|
1455
|
+
|
|
1456
|
+
// tick to trigger mediaState updates
|
|
1457
|
+
jasmine.clock().tick(1);
|
|
1458
|
+
// give a chance for fetchAndAppend_ to be invoked
|
|
1459
|
+
// by async onUpdate_ callback
|
|
1460
|
+
await Util.shortDelay(realSetTimeout);
|
|
1461
|
+
|
|
1462
|
+
// Nothing is buffered yet.
|
|
1463
|
+
expect(mediaSourceEngine.segments).toEqual({
|
|
1464
|
+
audio: [false, false, false, false],
|
|
1465
|
+
video: [false, false, false, false],
|
|
1466
|
+
text: [false, false, false, false],
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
// Seek forward to an unbuffered region in the first Period.
|
|
1470
|
+
presentationTimeInSeconds = 15;
|
|
1471
|
+
streamingEngine.seeked();
|
|
1472
|
+
|
|
1473
|
+
// resolve initSourceBufferPromise after seeked(), waitingToClearBuffer
|
|
1474
|
+
// should have been set, so this will now trigger the actual flush of
|
|
1475
|
+
// the buffer
|
|
1476
|
+
initSourceBufferPromise.resolve();
|
|
1477
|
+
mediaSourceEngine.setStreamProperties.and
|
|
1478
|
+
.returnValue(Promise.resolve());
|
|
1479
|
+
|
|
1480
|
+
// allow mediaState update to resolve
|
|
1481
|
+
await Util.shortDelay(realSetTimeout);
|
|
1482
|
+
|
|
1483
|
+
onTick.and.callFake(() => {
|
|
1484
|
+
expect(mediaSourceEngine.clear).toHaveBeenCalled();
|
|
1485
|
+
onTick.and.stub();
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
await runTest(Util.spyFunc(onTick));
|
|
1489
|
+
|
|
1490
|
+
// Verify buffers.
|
|
1491
|
+
expect(mediaSourceEngine.initSegments).toEqual({
|
|
1492
|
+
audio: [false, true],
|
|
1493
|
+
video: [false, true],
|
|
1494
|
+
text: [],
|
|
1495
|
+
});
|
|
1496
|
+
expect(mediaSourceEngine.segments).toEqual({
|
|
1497
|
+
audio: [false, true, true, true],
|
|
1498
|
+
video: [false, true, true, true],
|
|
1499
|
+
text: [false, true, true, true],
|
|
1500
|
+
});
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1392
1503
|
it('into unbuffered regions near segment start', async () => {
|
|
1393
1504
|
// Here we go!
|
|
1394
1505
|
streamingEngine.switchVariant(variant);
|
|
@@ -2170,13 +2281,13 @@ describe('StreamingEngine', () => {
|
|
|
2170
2281
|
.bind(mediaSourceEngine);
|
|
2171
2282
|
|
|
2172
2283
|
mediaSourceEngine.remove.and.callFake((type, start, end) => {
|
|
2173
|
-
expect(presentationTimeInSeconds).
|
|
2284
|
+
expect(presentationTimeInSeconds).toBeGreaterThanOrEqual(20);
|
|
2174
2285
|
expect(start).toBe(0);
|
|
2175
2286
|
expect(end).toBe(10);
|
|
2176
2287
|
|
|
2177
2288
|
if (mediaSourceEngine.remove.calls.count() == 3) {
|
|
2178
2289
|
mediaSourceEngine.remove.and.callFake((type, start, end) => {
|
|
2179
|
-
expect(presentationTimeInSeconds).
|
|
2290
|
+
expect(presentationTimeInSeconds).toBeGreaterThanOrEqual(30);
|
|
2180
2291
|
expect(start).toBe(10);
|
|
2181
2292
|
expect(end).toBe(20);
|
|
2182
2293
|
return originalRemove(type, start, end);
|
|
@@ -3388,6 +3499,40 @@ describe('StreamingEngine', () => {
|
|
|
3388
3499
|
segmentAvailability.end++;
|
|
3389
3500
|
}
|
|
3390
3501
|
|
|
3502
|
+
/**
|
|
3503
|
+
* @param {!shaka.test.FakeNetworkingEngine} netEngine A NetworkingEngine
|
|
3504
|
+
* look-alike.
|
|
3505
|
+
* @param {string} targetUri
|
|
3506
|
+
* @param {shaka.util.Error.Code=} errorCode
|
|
3507
|
+
*/
|
|
3508
|
+
function failRequestsForTarget(
|
|
3509
|
+
netEngine, targetUri, errorCode=shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
3510
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
3511
|
+
const originalNetEngineRequest = netEngine.request.bind(netEngine);
|
|
3512
|
+
|
|
3513
|
+
netEngine.request = jasmine.createSpy('request').and.callFake(
|
|
3514
|
+
(requestType, request) => {
|
|
3515
|
+
if (request.uris[0] == targetUri) {
|
|
3516
|
+
const data = [targetUri];
|
|
3517
|
+
|
|
3518
|
+
if (errorCode == shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
3519
|
+
data.push(404);
|
|
3520
|
+
data.push('');
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
// The compiler still sees the error code parameter as potentially
|
|
3524
|
+
// undefined, even though we gave it a default value.
|
|
3525
|
+
goog.asserts.assert(errorCode != undefined, 'Undefined error code');
|
|
3526
|
+
|
|
3527
|
+
return shaka.util.AbortableOperation.failed(new shaka.util.Error(
|
|
3528
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
3529
|
+
shaka.util.Error.Category.NETWORK,
|
|
3530
|
+
errorCode, data));
|
|
3531
|
+
}
|
|
3532
|
+
return originalNetEngineRequest(requestType, request);
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3391
3536
|
/**
|
|
3392
3537
|
* @param {!shaka.test.FakeNetworkingEngine} netEngine A NetworkingEngine
|
|
3393
3538
|
* look-alike.
|