shaka-player 3.1.0 → 3.1.4
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 +8 -2
- package/CHANGELOG.md +209 -0
- package/CONTRIBUTORS +10 -3
- package/README.md +3 -0
- package/build/apps.py +2 -2
- package/build/check.py +2 -2
- package/build/conformance.textproto +0 -9
- 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/asset.js +11 -0
- package/demo/common/assets.js +1 -1
- package/demo/common/message_ids.js +5 -0
- package/demo/config.js +2 -1
- package/demo/custom.js +84 -3
- package/demo/demo.less +13 -1
- package/demo/demo_utils.js +1 -0
- package/demo/input.js +31 -12
- package/demo/input_container.js +24 -1
- package/demo/locales/en.json +6 -1
- package/demo/locales/source.json +20 -0
- package/demo/main.js +5 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +99 -91
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +99 -91
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +9 -9
- package/dist/locales.js +36 -34
- package/dist/receiver.compiled.debug.js +74 -70
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +74 -70
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.externs.js +179 -11
- package/dist/shaka-player.compiled.debug.js +367 -339
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +179 -11
- package/dist/shaka-player.compiled.js +818 -807
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +165 -9
- package/dist/shaka-player.ui.debug.d.ts +165 -9
- package/dist/shaka-player.ui.debug.externs.js +181 -13
- package/dist/shaka-player.ui.debug.js +459 -429
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -13
- package/dist/shaka-player.ui.js +973 -961
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +25 -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/media_capabilities.js +16 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/player.js +6 -0
- package/karma.conf.js +1 -0
- package/lib/abr/simple_abr_manager.js +1 -1
- 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 +107 -4
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +45 -15
- package/lib/dependencies/all.js +15 -15
- package/lib/hls/hls_parser.js +46 -18
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +18 -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_index.js +38 -12
- package/lib/media/segment_reference.js +8 -0
- package/lib/media/streaming_engine.js +30 -21
- package/lib/net/http_fetch_plugin.js +2 -2
- package/lib/net/networking_engine.js +3 -2
- package/lib/offline/storage.js +4 -0
- package/lib/player.js +113 -56
- 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 +62 -23
- 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 +11 -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 +52 -30
- package/lib/text/ui_text_displayer.js +255 -82
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/error.js +1 -4
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mime_utils.js +33 -0
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/mp4_parser.js +14 -0
- package/lib/util/networking.js +1 -1
- package/lib/util/periods.js +132 -37
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +46 -7
- package/package.json +3 -3
- 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 +187 -1
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/demo/demo_unit.js +0 -1
- package/test/hls/hls_parser_unit.js +45 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +59 -3
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/segment_index_unit.js +76 -3
- package/test/media/streaming_engine_unit.js +3 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +125 -6
- 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/waiter.js +36 -2
- package/test/text/ttml_text_parser_unit.js +55 -0
- 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/ui/ui_unit.js +4 -0
- 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 +56 -5
- 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 +9 -7
- 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/ui.js +1 -1
- 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);
|
|
@@ -1854,7 +1862,8 @@ describe('DashParser Manifest', () => {
|
|
|
1854
1862
|
expect(textStream.forced).toBe(true);
|
|
1855
1863
|
});
|
|
1856
1864
|
|
|
1857
|
-
it('supports HDR signaling', async () => {
|
|
1865
|
+
it('supports HDR signaling via profiles', async () => {
|
|
1866
|
+
// (DASH-IF IOP v4.3 10.3.3.)
|
|
1858
1867
|
const hdrProfile =
|
|
1859
1868
|
'http://dashif.org/guidelines/dash-if-uhd#hevc-hdr-pq10';
|
|
1860
1869
|
const manifestText = [
|
|
@@ -1885,6 +1894,110 @@ describe('DashParser Manifest', () => {
|
|
|
1885
1894
|
expect(stream.hdr).toBe('PQ');
|
|
1886
1895
|
});
|
|
1887
1896
|
|
|
1897
|
+
it('supports HDR signaling via SupplementalProperty', async () => {
|
|
1898
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1899
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1900
|
+
const pq = 16;
|
|
1901
|
+
const manifestText = [
|
|
1902
|
+
'<MPD minBufferTime="PT75S">',
|
|
1903
|
+
' <Period id="1" duration="PT30S">',
|
|
1904
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1905
|
+
` <SupplementalProperty schemeIdUri="${hdrScheme}" value="${pq}" />`,
|
|
1906
|
+
' <Representation codecs="hvc1.2.4.L150.90">',
|
|
1907
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1908
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1909
|
+
' </Representation>',
|
|
1910
|
+
' </AdaptationSet>',
|
|
1911
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1912
|
+
' <Representation id="audio-en">',
|
|
1913
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1914
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1915
|
+
' </Representation>',
|
|
1916
|
+
' </AdaptationSet>',
|
|
1917
|
+
' </Period>',
|
|
1918
|
+
'</MPD>',
|
|
1919
|
+
].join('\n');
|
|
1920
|
+
|
|
1921
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1922
|
+
|
|
1923
|
+
/** @type {shaka.extern.Manifest} */
|
|
1924
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1925
|
+
expect(manifest.variants.length).toBe(1);
|
|
1926
|
+
const stream = manifest.variants[0].video;
|
|
1927
|
+
expect(stream.hdr).toBe('PQ');
|
|
1928
|
+
});
|
|
1929
|
+
|
|
1930
|
+
it('supports HDR signaling via EssentialProperty', async () => {
|
|
1931
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1932
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1933
|
+
const hlg = 18;
|
|
1934
|
+
const manifestText = [
|
|
1935
|
+
'<MPD minBufferTime="PT75S">',
|
|
1936
|
+
' <Period id="1" duration="PT30S">',
|
|
1937
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1938
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="${hlg}" />`,
|
|
1939
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
1940
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1941
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1942
|
+
' </Representation>',
|
|
1943
|
+
' </AdaptationSet>',
|
|
1944
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1945
|
+
' <Representation id="audio-en">',
|
|
1946
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1947
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1948
|
+
' </Representation>',
|
|
1949
|
+
' </AdaptationSet>',
|
|
1950
|
+
' </Period>',
|
|
1951
|
+
'</MPD>',
|
|
1952
|
+
].join('\n');
|
|
1953
|
+
|
|
1954
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1955
|
+
|
|
1956
|
+
/** @type {shaka.extern.Manifest} */
|
|
1957
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1958
|
+
expect(manifest.variants.length).toBe(1);
|
|
1959
|
+
const stream = manifest.variants[0].video;
|
|
1960
|
+
expect(stream.hdr).toBe('HLG');
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
it('supports SDR signalling via EssentialProperty', async () => {
|
|
1964
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1965
|
+
const scheme = cicpScheme('TransferCharacteristics');
|
|
1966
|
+
const sdrValues = [1, 6, 13, 14, 15];
|
|
1967
|
+
const manifestPromises = [];
|
|
1968
|
+
for (const value of sdrValues) {
|
|
1969
|
+
const manifestText = [
|
|
1970
|
+
'<MPD minBufferTime="PT75S">',
|
|
1971
|
+
' <Period id="1" duration="PT30S">',
|
|
1972
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1973
|
+
` <EssentialProperty schemeIdUri="${scheme}" value="${value}" />`,
|
|
1974
|
+
' <Representation codecs="avc1.640028">',
|
|
1975
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1976
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1977
|
+
' </Representation>',
|
|
1978
|
+
' </AdaptationSet>',
|
|
1979
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1980
|
+
' <Representation id="audio-en">',
|
|
1981
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1982
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1983
|
+
' </Representation>',
|
|
1984
|
+
' </AdaptationSet>',
|
|
1985
|
+
' </Period>',
|
|
1986
|
+
'</MPD>',
|
|
1987
|
+
].join('\n');
|
|
1988
|
+
|
|
1989
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1990
|
+
|
|
1991
|
+
manifestPromises.push(parser.start('dummy://foo', playerInterface));
|
|
1992
|
+
}
|
|
1993
|
+
const manifests = await Promise.all(manifestPromises);
|
|
1994
|
+
for (const manifest of manifests) {
|
|
1995
|
+
expect(manifest.variants.length).toBe(1);
|
|
1996
|
+
const stream = manifest.variants[0].video;
|
|
1997
|
+
expect(stream.hdr).toBe('SDR');
|
|
1998
|
+
}
|
|
1999
|
+
});
|
|
2000
|
+
|
|
1888
2001
|
it('Does not error when image adaptation sets are present', async () => {
|
|
1889
2002
|
const manifestText = [
|
|
1890
2003
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -2108,4 +2221,77 @@ describe('DashParser Manifest', () => {
|
|
|
2108
2221
|
expect(uri).not.toContain('/p2/');
|
|
2109
2222
|
}
|
|
2110
2223
|
});
|
|
2224
|
+
|
|
2225
|
+
/**
|
|
2226
|
+
* @param {!Array.<number>} periods Start time of multiple periods
|
|
2227
|
+
* @return {string}
|
|
2228
|
+
*/
|
|
2229
|
+
function buildManifestWithPeriodStartTime(periods) {
|
|
2230
|
+
const mpdTemplate = [
|
|
2231
|
+
`<MPD type="dynamic"`,
|
|
2232
|
+
'availabilityStartTime="1970-01-01T00:00:00Z"',
|
|
2233
|
+
'timeShiftBufferDepth="PT10H">',
|
|
2234
|
+
' %(periods)s',
|
|
2235
|
+
'</MPD>',
|
|
2236
|
+
].join('\n');
|
|
2237
|
+
const periodTemplate = (id, period, duration) => {
|
|
2238
|
+
return [
|
|
2239
|
+
` <Period id="${id}" start="PT${period}S">`,
|
|
2240
|
+
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
|
|
2241
|
+
' <SegmentTemplate startNumber="1" media="l-$Number$.mp4">',
|
|
2242
|
+
' <SegmentTimeline>',
|
|
2243
|
+
` <S t="0" d="${duration}" />`,
|
|
2244
|
+
' </SegmentTimeline>',
|
|
2245
|
+
' </SegmentTemplate>',
|
|
2246
|
+
' <Representation id="1"/>',
|
|
2247
|
+
' </AdaptationSet>',
|
|
2248
|
+
' </Period>',
|
|
2249
|
+
].join('\n');
|
|
2250
|
+
};
|
|
2251
|
+
const periodXmls = periods.map((period, i) => {
|
|
2252
|
+
const duration = i+1 === periods.length ? 10 : periods[i+1] - period;
|
|
2253
|
+
// Period start time as ID here. If we use index then there will be
|
|
2254
|
+
// periods with same period ID and different start time which are invalid.
|
|
2255
|
+
return periodTemplate(period, period, duration);
|
|
2256
|
+
});
|
|
2257
|
+
return sprintf(mpdTemplate, {
|
|
2258
|
+
periods: periodXmls.join('\n'),
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// Bug description: Inconsistent period start time in the manifests due
|
|
2263
|
+
// to failover triggered in backend servers
|
|
2264
|
+
|
|
2265
|
+
// When one of the servers is down, the manifest will be served by other
|
|
2266
|
+
// redundant servers. The period start time might become out of sync
|
|
2267
|
+
// during the switch-over/recovery.
|
|
2268
|
+
|
|
2269
|
+
it('skip periods that are earlier than max period start time', async () => {
|
|
2270
|
+
const sources = [
|
|
2271
|
+
buildManifestWithPeriodStartTime([5, 15]),
|
|
2272
|
+
buildManifestWithPeriodStartTime([6, 15]), // simulate out-of-sync of +1s
|
|
2273
|
+
buildManifestWithPeriodStartTime([4, 15]), // simulate out-of-sync of -1s
|
|
2274
|
+
];
|
|
2275
|
+
const segments = [];
|
|
2276
|
+
|
|
2277
|
+
for (const source of sources) {
|
|
2278
|
+
fakeNetEngine.setResponseText('dummy://foo', source);
|
|
2279
|
+
/** @type {shaka.extern.Manifest} */
|
|
2280
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2281
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2282
|
+
const video = manifest.variants[0].video;
|
|
2283
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2284
|
+
await video.createSegmentIndex();
|
|
2285
|
+
goog.asserts.assert(video.segmentIndex, 'Null segmentIndex!');
|
|
2286
|
+
segments.push(Array.from(video.segmentIndex));
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// Expect identical segments
|
|
2290
|
+
expect(segments[0].length).toBe(2);
|
|
2291
|
+
expect(segments[1].length).toBe(2);
|
|
2292
|
+
expect(segments[0][0].startTime).toBe(5);
|
|
2293
|
+
expect(segments[1][0].startTime).toBe(5);
|
|
2294
|
+
expect(segments[0][1].startTime).toBe(15);
|
|
2295
|
+
expect(segments[1][1].startTime).toBe(15);
|
|
2296
|
+
});
|
|
2111
2297
|
});
|
|
@@ -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
|
|
package/test/demo/demo_unit.js
CHANGED
|
@@ -96,7 +96,6 @@ describe('Demo', () => {
|
|
|
96
96
|
const configPrimitives = new Set(['number', 'string', 'boolean']);
|
|
97
97
|
const exceptions = new Set()
|
|
98
98
|
.add('preferredVariantRole')
|
|
99
|
-
.add('preferredTextRole')
|
|
100
99
|
.add('playRangeStart')
|
|
101
100
|
.add('playRangeEnd')
|
|
102
101
|
.add('manifest.dash.keySystemsByURI');
|
|
@@ -209,6 +209,46 @@ describe('HlsParser', () => {
|
|
|
209
209
|
expect(actual).toEqual(manifest);
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
+
it('prioritize AVERAGE-BANDWIDTH to BANDWIDTH', async () => {
|
|
213
|
+
const master = [
|
|
214
|
+
'#EXTM3U\n',
|
|
215
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
216
|
+
'RESOLUTION=960x540,FRAME-RATE=60,',
|
|
217
|
+
'AVERAGE-BANDWIDTH=100\n',
|
|
218
|
+
'video\n',
|
|
219
|
+
].join('');
|
|
220
|
+
|
|
221
|
+
const media = [
|
|
222
|
+
'#EXTM3U\n',
|
|
223
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
224
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
225
|
+
'#EXTINF:5,\n',
|
|
226
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
227
|
+
'main.mp4',
|
|
228
|
+
].join('');
|
|
229
|
+
|
|
230
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
231
|
+
manifest.anyTimeline();
|
|
232
|
+
manifest.addPartialVariant((variant) => {
|
|
233
|
+
variant.bandwidth = 100;
|
|
234
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
235
|
+
stream.frameRate = 60;
|
|
236
|
+
stream.mime('video/mp4', 'avc1');
|
|
237
|
+
stream.size(960, 540);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
fakeNetEngine
|
|
243
|
+
.setResponseText('test:/master', master)
|
|
244
|
+
.setResponseText('test:/video', media)
|
|
245
|
+
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
246
|
+
.setResponseValue('test:/main.mp4', segmentData);
|
|
247
|
+
|
|
248
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
249
|
+
expect(actual).toEqual(manifest);
|
|
250
|
+
});
|
|
251
|
+
|
|
212
252
|
it('ignores duplicate CODECS', async () => {
|
|
213
253
|
const master = [
|
|
214
254
|
'#EXTM3U\n',
|
|
@@ -743,7 +783,7 @@ describe('HlsParser', () => {
|
|
|
743
783
|
'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud2"\n',
|
|
744
784
|
'video2\n',
|
|
745
785
|
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
746
|
-
'URI="audio"\n',
|
|
786
|
+
'DEFAULT=YES,URI="audio"\n',
|
|
747
787
|
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud2",LANGUAGE="fr",',
|
|
748
788
|
'URI="audio2"\n',
|
|
749
789
|
].join('');
|
|
@@ -761,6 +801,7 @@ describe('HlsParser', () => {
|
|
|
761
801
|
manifest.anyTimeline();
|
|
762
802
|
manifest.addPartialVariant((variant) => {
|
|
763
803
|
variant.bandwidth = 200;
|
|
804
|
+
variant.primary = true;
|
|
764
805
|
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
765
806
|
stream.size(960, 540);
|
|
766
807
|
});
|
|
@@ -770,6 +811,7 @@ describe('HlsParser', () => {
|
|
|
770
811
|
});
|
|
771
812
|
manifest.addPartialVariant((variant) => {
|
|
772
813
|
variant.bandwidth = 300;
|
|
814
|
+
variant.primary = false;
|
|
773
815
|
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
774
816
|
stream.size(960, 540);
|
|
775
817
|
});
|
|
@@ -1267,6 +1309,8 @@ describe('HlsParser', () => {
|
|
|
1267
1309
|
// stream.
|
|
1268
1310
|
const timeline = actual.presentationTimeline;
|
|
1269
1311
|
expect(timeline.getDuration()).toBe(10);
|
|
1312
|
+
expect(timeline.getSeekRangeStart()).toBe(0);
|
|
1313
|
+
expect(timeline.getSeekRangeEnd()).toBe(10);
|
|
1270
1314
|
|
|
1271
1315
|
expect(actual.textStreams.length).toBe(1);
|
|
1272
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
|
+
});
|
|
@@ -37,7 +37,7 @@ function testDrmEngine(useMediaCapabilities) {
|
|
|
37
37
|
navigator.requestMediaKeySystemAccess;
|
|
38
38
|
const originalLogError = shaka.log.error;
|
|
39
39
|
const originalBatchTime = shaka.media.DrmEngine.KEY_STATUS_BATCH_TIME;
|
|
40
|
-
const originalDecodingInfo =
|
|
40
|
+
const originalDecodingInfo = window.shakaMediaCapabilities.decodingInfo;
|
|
41
41
|
|
|
42
42
|
/** @type {!jasmine.Spy} */
|
|
43
43
|
let requestMediaKeySystemAccessSpy;
|
|
@@ -88,7 +88,7 @@ function testDrmEngine(useMediaCapabilities) {
|
|
|
88
88
|
navigator.requestMediaKeySystemAccess =
|
|
89
89
|
shaka.test.Util.spyFunc(requestMediaKeySystemAccessSpy);
|
|
90
90
|
decodingInfoSpy = jasmine.createSpy('decodingInfo');
|
|
91
|
-
|
|
91
|
+
window.shakaMediaCapabilities.decodingInfo =
|
|
92
92
|
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
93
93
|
|
|
94
94
|
logErrorSpy = jasmine.createSpy('shaka.log.error');
|
|
@@ -165,7 +165,7 @@ function testDrmEngine(useMediaCapabilities) {
|
|
|
165
165
|
|
|
166
166
|
navigator.requestMediaKeySystemAccess =
|
|
167
167
|
originalRequestMediaKeySystemAccess;
|
|
168
|
-
|
|
168
|
+
window.shakaMediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
169
169
|
shaka.log.error = originalLogError;
|
|
170
170
|
});
|
|
171
171
|
|
|
@@ -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', () => {
|
|
@@ -484,12 +484,40 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
|
|
|
484
484
|
makeReference(uri(20), 20, 30),
|
|
485
485
|
];
|
|
486
486
|
|
|
487
|
-
// The first
|
|
488
|
-
// should be discarded.
|
|
489
|
-
index1.mergeAndEvict(references2,
|
|
487
|
+
// The first two references end before the availabilityWindowStart, so
|
|
488
|
+
// they should be discarded.
|
|
489
|
+
index1.mergeAndEvict(references2, 21);
|
|
490
|
+
expect(index1.references.length).toBe(1);
|
|
491
|
+
expect(index1.references[0]).toEqual(references2[2]);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('discards segments that end before the first old segment', () => {
|
|
495
|
+
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
496
|
+
const references1 = [
|
|
497
|
+
// Assuming ref(0, 10) has been already evicted
|
|
498
|
+
makeReference(uri(10), 10, 20),
|
|
499
|
+
];
|
|
500
|
+
const index1 = new shaka.media.SegmentIndex(references1);
|
|
501
|
+
const position1 = index1.find(10);
|
|
502
|
+
|
|
503
|
+
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
504
|
+
const references2 = [
|
|
505
|
+
makeReference(uri(0), 0, 10),
|
|
506
|
+
makeReference(uri(10), 10, 20),
|
|
507
|
+
makeReference(uri(20), 20, 30),
|
|
508
|
+
];
|
|
509
|
+
|
|
510
|
+
// The new first reference ends before the first old reference starts, so
|
|
511
|
+
// it should be discarded. We will never grow the list at the beginning.
|
|
512
|
+
index1.mergeAndEvict(references2, 0);
|
|
490
513
|
expect(index1.references.length).toBe(2);
|
|
491
514
|
expect(index1.references[0]).toEqual(references2[1]);
|
|
492
515
|
expect(index1.references[1]).toEqual(references2[2]);
|
|
516
|
+
|
|
517
|
+
// The positions should be the same, as well.
|
|
518
|
+
expect(index1.find(10)).toBe(position1);
|
|
519
|
+
goog.asserts.assert(position1 != null, 'Position should not be null!');
|
|
520
|
+
expect(index1.get(position1)).toBe(references2[1]);
|
|
493
521
|
});
|
|
494
522
|
});
|
|
495
523
|
|
|
@@ -567,6 +595,12 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
|
|
|
567
595
|
makeReference(uri(20.30), 20, 30),
|
|
568
596
|
];
|
|
569
597
|
|
|
598
|
+
const additionalRefs = [
|
|
599
|
+
makeReference(uri(30.40), 30, 40),
|
|
600
|
+
makeReference(uri(40.50), 40, 50),
|
|
601
|
+
makeReference(uri(50.60), 50, 60),
|
|
602
|
+
];
|
|
603
|
+
|
|
570
604
|
const partialRefs1 = [
|
|
571
605
|
makeReference(uri(10.15), 10, 15),
|
|
572
606
|
makeReference(uri(15.20), 15, 20),
|
|
@@ -626,6 +660,45 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
|
|
|
626
660
|
expect(Array.from(index)).toEqual(inputRefs.slice(1));
|
|
627
661
|
});
|
|
628
662
|
|
|
663
|
+
it('resumes iteration after new references are added', () => {
|
|
664
|
+
const refs = inputRefs.slice();
|
|
665
|
+
const index = new shaka.media.SegmentIndex(refs);
|
|
666
|
+
|
|
667
|
+
// This simulates the pattern of calls in StreamingEngine when we buffer
|
|
668
|
+
// to the edge of a live stream.
|
|
669
|
+
const iterator = index[Symbol.iterator]();
|
|
670
|
+
iterator.next();
|
|
671
|
+
expect(iterator.current()).toBe(inputRefs[0]);
|
|
672
|
+
|
|
673
|
+
iterator.next();
|
|
674
|
+
expect(iterator.current()).toBe(inputRefs[1]);
|
|
675
|
+
|
|
676
|
+
iterator.next();
|
|
677
|
+
expect(iterator.current()).toBe(inputRefs[2]);
|
|
678
|
+
|
|
679
|
+
iterator.next();
|
|
680
|
+
expect(iterator.current()).toBe(null);
|
|
681
|
+
// After we reach the end of the iteration, we should still get null back
|
|
682
|
+
// on subsequent requests.
|
|
683
|
+
expect(iterator.current()).toBe(null);
|
|
684
|
+
expect(iterator.current()).toBe(null);
|
|
685
|
+
|
|
686
|
+
// Although we have reached the end, we should still be able to add new
|
|
687
|
+
// references and iterate to them without starting over the iteration.
|
|
688
|
+
refs.push(...additionalRefs);
|
|
689
|
+
|
|
690
|
+
expect(iterator.current()).toBe(additionalRefs[0]);
|
|
691
|
+
|
|
692
|
+
iterator.next();
|
|
693
|
+
expect(iterator.current()).toBe(additionalRefs[1]);
|
|
694
|
+
|
|
695
|
+
iterator.next();
|
|
696
|
+
expect(iterator.current()).toBe(additionalRefs[2]);
|
|
697
|
+
|
|
698
|
+
iterator.next();
|
|
699
|
+
expect(iterator.current()).toBe(null);
|
|
700
|
+
});
|
|
701
|
+
|
|
629
702
|
describe('seek', () => {
|
|
630
703
|
it('returns the matching segment', () => {
|
|
631
704
|
const index = new shaka.media.SegmentIndex(inputRefs);
|
|
@@ -835,7 +835,7 @@ describe('StreamingEngine', () => {
|
|
|
835
835
|
// This would let some media types buffer faster than others if unchecked.
|
|
836
836
|
netEngineDelays.text = 0.1;
|
|
837
837
|
netEngineDelays.audio = 1.0;
|
|
838
|
-
netEngineDelays.video =
|
|
838
|
+
netEngineDelays.video = 5.0; // Need init segment and media segment
|
|
839
839
|
|
|
840
840
|
mediaSourceEngine.appendBuffer.and.callFake((type, data, start, end) => {
|
|
841
841
|
// Call to the underlying implementation.
|
|
@@ -2170,13 +2170,13 @@ describe('StreamingEngine', () => {
|
|
|
2170
2170
|
.bind(mediaSourceEngine);
|
|
2171
2171
|
|
|
2172
2172
|
mediaSourceEngine.remove.and.callFake((type, start, end) => {
|
|
2173
|
-
expect(presentationTimeInSeconds).
|
|
2173
|
+
expect(presentationTimeInSeconds).toBeGreaterThanOrEqual(20);
|
|
2174
2174
|
expect(start).toBe(0);
|
|
2175
2175
|
expect(end).toBe(10);
|
|
2176
2176
|
|
|
2177
2177
|
if (mediaSourceEngine.remove.calls.count() == 3) {
|
|
2178
2178
|
mediaSourceEngine.remove.and.callFake((type, start, end) => {
|
|
2179
|
-
expect(presentationTimeInSeconds).
|
|
2179
|
+
expect(presentationTimeInSeconds).toBeGreaterThanOrEqual(30);
|
|
2180
2180
|
expect(start).toBe(10);
|
|
2181
2181
|
expect(end).toBe(20);
|
|
2182
2182
|
return originalRemove(type, start, end);
|
|
@@ -652,6 +652,44 @@ describe('Player', () => {
|
|
|
652
652
|
});
|
|
653
653
|
});
|
|
654
654
|
|
|
655
|
+
describe('seek range', () => {
|
|
656
|
+
// Regression test for Issue #3675.
|
|
657
|
+
it('has a reasonable seek range after going from live to VOD', async () => {
|
|
658
|
+
const netEngine = player.getNetworkingEngine();
|
|
659
|
+
const startTime = Date.now();
|
|
660
|
+
netEngine.registerRequestFilter((type, request) => {
|
|
661
|
+
if (type != shaka.net.NetworkingEngine.RequestType.MANIFEST) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
// Simulate a live stream by providing different manifests over time.
|
|
665
|
+
const time = (Date.now() - startTime) / 1000;
|
|
666
|
+
const manifestNumber = Math.min(5, Math.floor(0.5 + time / 2));
|
|
667
|
+
request.uris = [
|
|
668
|
+
'/base/test/test/assets/3675/dash_' + manifestNumber + '.mpd',
|
|
669
|
+
];
|
|
670
|
+
console.log('getting manifest', request.uris);
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
// Play the stream.
|
|
674
|
+
await player.load('/base/test/test/assets/3675/dash_0.mpd');
|
|
675
|
+
video.play();
|
|
676
|
+
|
|
677
|
+
// Wait for the stream to be over.
|
|
678
|
+
eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
|
|
679
|
+
/** @type {shaka.test.Waiter} */
|
|
680
|
+
const waiter = new shaka.test.Waiter(eventManager)
|
|
681
|
+
.timeoutAfter(20).failOnTimeout(true);
|
|
682
|
+
await waiter.waitForEnd(video);
|
|
683
|
+
|
|
684
|
+
// The stream should have transitioned to VOD by now.
|
|
685
|
+
expect(player.isLive()).toBe(false);
|
|
686
|
+
|
|
687
|
+
// Check that the final seek range is as expected.
|
|
688
|
+
const seekRange = player.seekRange();
|
|
689
|
+
expect(seekRange.end).toBe(14);
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
|
|
655
693
|
describe('buffering', () => {
|
|
656
694
|
const startBuffering = jasmine.objectContaining({buffering: true});
|
|
657
695
|
const endBuffering = jasmine.objectContaining({buffering: false});
|