shaka-player 3.1.3 → 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 +3 -0
- package/CHANGELOG.md +56 -0
- package/CONTRIBUTORS +3 -0
- package/build/generateExterns.js +1 -1
- 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 +1 -1
- package/dist/locales.js +35 -35
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.externs.js +1 -1
- package/dist/shaka-player.compiled.debug.js +187 -183
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +1 -1
- package/dist/shaka-player.compiled.js +799 -799
- package/dist/shaka-player.compiled.map +2 -2
- package/dist/shaka-player.ui.d.ts +1 -1
- package/dist/shaka-player.ui.debug.d.ts +1 -1
- package/dist/shaka-player.ui.debug.externs.js +3 -3
- package/dist/shaka-player.ui.debug.js +264 -260
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +3 -3
- package/dist/shaka-player.ui.js +896 -895
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/karma.conf.js +1 -0
- package/lib/ads/client_side_ad_manager.js +46 -86
- package/lib/ads/server_side_ad_manager.js +7 -10
- 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/dash/dash_parser.js +93 -2
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +30 -17
- package/lib/media/drm_engine.js +7 -0
- package/lib/media/gap_jumping_controller.js +4 -2
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/streaming_engine.js +2 -1
- package/lib/net/networking_engine.js +2 -1
- package/lib/player.js +22 -22
- package/lib/polyfill/media_capabilities.js +7 -1
- package/lib/polyfill/mediasource.js +6 -0
- package/lib/polyfill/orientation.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +4 -4
- package/lib/polyfill/patchedmediakeys_ms.js +4 -4
- package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +5 -0
- package/lib/text/ui_text_displayer.js +85 -37
- package/lib/util/fake_event.js +53 -7
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +13 -0
- package/package.json +1 -1
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/dash/dash_parser_manifest_unit.js +187 -1
- package/test/hls/hls_parser_unit.js +5 -1
- package/test/media/closed_caption_parser_unit.js +6 -1
- package/test/media/drm_engine_unit.js +43 -0
- package/test/player_integration.js +38 -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/util/fake_ad_manager.js +1 -1
- package/test/text/ttml_text_parser_unit.js +9 -0
- package/test/text/ui_text_displayer_unit.js +29 -17
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +13 -1
- package/test/util/stream_utils_unit.js +35 -0
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +4 -6
- package/ui/less/containers.less +3 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +28 -51
- package/ui/pip_button.js +2 -3
package/lib/util/platform.js
CHANGED
|
@@ -163,7 +163,6 @@ shaka.util.Platform = class {
|
|
|
163
163
|
!shaka.util.Platform.isEdge();
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
|
|
167
166
|
/**
|
|
168
167
|
* Check if the current platform is from Apple.
|
|
169
168
|
*
|
|
@@ -179,6 +178,19 @@ shaka.util.Platform = class {
|
|
|
179
178
|
&& !shaka.util.Platform.isTizen();
|
|
180
179
|
}
|
|
181
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Check if the current platform is Playstation 5.
|
|
183
|
+
*
|
|
184
|
+
* Returns true on Playstation 5 browsers.
|
|
185
|
+
*
|
|
186
|
+
* Returns false for Playstation 5 browsers
|
|
187
|
+
*
|
|
188
|
+
* @return {boolean}
|
|
189
|
+
*/
|
|
190
|
+
static isPS5() {
|
|
191
|
+
return shaka.util.Platform.userAgentContains_('PlayStation 5');
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
/**
|
|
183
195
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
184
196
|
*
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -501,6 +501,19 @@ shaka.util.StreamUtils = class {
|
|
|
501
501
|
// framerate must be greater than 0, otherwise the config is invalid.
|
|
502
502
|
framerate: video.frameRate || 1,
|
|
503
503
|
};
|
|
504
|
+
if (video.hdr) {
|
|
505
|
+
switch (video.hdr) {
|
|
506
|
+
case 'SDR':
|
|
507
|
+
mediaDecodingConfig.video.transferFunction = 'srgb';
|
|
508
|
+
break;
|
|
509
|
+
case 'PQ':
|
|
510
|
+
mediaDecodingConfig.video.transferFunction = 'pq';
|
|
511
|
+
break;
|
|
512
|
+
case 'HLG':
|
|
513
|
+
mediaDecodingConfig.video.transferFunction = 'hlg';
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
504
517
|
}
|
|
505
518
|
if (audio) {
|
|
506
519
|
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
package/package.json
CHANGED
|
@@ -297,7 +297,8 @@ describe('CastProxy', () => {
|
|
|
297
297
|
'timeupdate', Util.spyFunc(proxyListener));
|
|
298
298
|
|
|
299
299
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
300
|
-
const fakeEvent = new FakeEvent(
|
|
300
|
+
const fakeEvent = new FakeEvent(
|
|
301
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
301
302
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
302
303
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
303
304
|
type: 'timeupdate',
|
|
@@ -315,7 +316,8 @@ describe('CastProxy', () => {
|
|
|
315
316
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
316
317
|
|
|
317
318
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
318
|
-
const fakeEvent = new FakeEvent(
|
|
319
|
+
const fakeEvent = new FakeEvent(
|
|
320
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
319
321
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
320
322
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
321
323
|
});
|
|
@@ -332,7 +334,8 @@ describe('CastProxy', () => {
|
|
|
332
334
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
333
335
|
|
|
334
336
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
335
|
-
const fakeEvent = new FakeEvent(
|
|
337
|
+
const fakeEvent = new FakeEvent(
|
|
338
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
336
339
|
mockSender.onRemoteEvent('video', fakeEvent);
|
|
337
340
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
338
341
|
type: 'timeupdate',
|
|
@@ -446,7 +449,8 @@ describe('CastProxy', () => {
|
|
|
446
449
|
'buffering', Util.spyFunc(proxyListener));
|
|
447
450
|
|
|
448
451
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
449
|
-
const fakeEvent = new FakeEvent(
|
|
452
|
+
const fakeEvent = new FakeEvent(
|
|
453
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
450
454
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
451
455
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
452
456
|
type: 'buffering',
|
|
@@ -464,7 +468,8 @@ describe('CastProxy', () => {
|
|
|
464
468
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
465
469
|
|
|
466
470
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
467
|
-
const fakeEvent = new FakeEvent(
|
|
471
|
+
const fakeEvent = new FakeEvent(
|
|
472
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
468
473
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
469
474
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
470
475
|
});
|
|
@@ -481,7 +486,8 @@ describe('CastProxy', () => {
|
|
|
481
486
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
482
487
|
|
|
483
488
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
484
|
-
const fakeEvent = new FakeEvent(
|
|
489
|
+
const fakeEvent = new FakeEvent(
|
|
490
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
485
491
|
mockSender.onRemoteEvent('player', fakeEvent);
|
|
486
492
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
487
493
|
type: 'buffering',
|
|
@@ -122,7 +122,7 @@ describe('CastUtils', () => {
|
|
|
122
122
|
expect(typeof deserialized).toBe('object');
|
|
123
123
|
|
|
124
124
|
// The object can be used to construct a FakeEvent.
|
|
125
|
-
const fakeEvent =
|
|
125
|
+
const fakeEvent = FakeEvent.fromRealEvent(deserialized);
|
|
126
126
|
|
|
127
127
|
// The fake event has the same type and properties as the original.
|
|
128
128
|
const asObj = /** @type {!Object} */ (fakeEvent);
|
|
@@ -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
|
});
|
|
@@ -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);
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
goog.require('shaka.media.ClosedCaptionParser');
|
|
2
8
|
goog.require('shaka.test.Util');
|
|
3
9
|
|
|
@@ -12,4 +18,3 @@ describe('ClosedCaptionParser', () => {
|
|
|
12
18
|
parser.parseFrom(videoSegment);
|
|
13
19
|
});
|
|
14
20
|
});
|
|
15
|
-
|
|
@@ -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', () => {
|
|
@@ -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});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
|
4
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
5
|
+
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
|
6
|
+
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
|
7
|
+
type="dynamic"
|
|
8
|
+
minimumUpdatePeriod="PT2S"
|
|
9
|
+
suggestedPresentationDelay="PT2S"
|
|
10
|
+
availabilityStartTime="2021-10-28T07:36:27.755Z"
|
|
11
|
+
publishTime="2021-10-28T07:36:29.744Z"
|
|
12
|
+
timeShiftBufferDepth="PT10.0S"
|
|
13
|
+
maxSegmentDuration="PT2.0S"
|
|
14
|
+
minBufferTime="PT4.0S">
|
|
15
|
+
<ProgramInformation>
|
|
16
|
+
</ProgramInformation>
|
|
17
|
+
<ServiceDescription id="0">
|
|
18
|
+
</ServiceDescription>
|
|
19
|
+
<Period id="0" start="PT0.0S">
|
|
20
|
+
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
|
|
21
|
+
<Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
|
|
22
|
+
<SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
|
23
|
+
<SegmentTimeline>
|
|
24
|
+
<S t="0" d="30720" />
|
|
25
|
+
</SegmentTimeline>
|
|
26
|
+
</SegmentTemplate>
|
|
27
|
+
</Representation>
|
|
28
|
+
</AdaptationSet>
|
|
29
|
+
</Period>
|
|
30
|
+
</MPD>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
|
4
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
5
|
+
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
|
6
|
+
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
|
7
|
+
type="dynamic"
|
|
8
|
+
minimumUpdatePeriod="PT2S"
|
|
9
|
+
suggestedPresentationDelay="PT2S"
|
|
10
|
+
availabilityStartTime="2021-10-28T07:36:27.755Z"
|
|
11
|
+
publishTime="2021-10-28T07:36:31.750Z"
|
|
12
|
+
timeShiftBufferDepth="PT10.0S"
|
|
13
|
+
maxSegmentDuration="PT2.0S"
|
|
14
|
+
minBufferTime="PT4.0S">
|
|
15
|
+
<ProgramInformation>
|
|
16
|
+
</ProgramInformation>
|
|
17
|
+
<ServiceDescription id="0">
|
|
18
|
+
</ServiceDescription>
|
|
19
|
+
<Period id="0" start="PT0.0S">
|
|
20
|
+
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
|
|
21
|
+
<Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
|
|
22
|
+
<SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
|
23
|
+
<SegmentTimeline>
|
|
24
|
+
<S t="0" d="30720" r="1" />
|
|
25
|
+
</SegmentTimeline>
|
|
26
|
+
</SegmentTemplate>
|
|
27
|
+
</Representation>
|
|
28
|
+
</AdaptationSet>
|
|
29
|
+
</Period>
|
|
30
|
+
</MPD>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
|
4
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
5
|
+
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
|
6
|
+
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
|
7
|
+
type="dynamic"
|
|
8
|
+
minimumUpdatePeriod="PT2S"
|
|
9
|
+
suggestedPresentationDelay="PT2S"
|
|
10
|
+
availabilityStartTime="2021-10-28T07:36:27.755Z"
|
|
11
|
+
publishTime="2021-10-28T07:36:35.752Z"
|
|
12
|
+
timeShiftBufferDepth="PT10.0S"
|
|
13
|
+
maxSegmentDuration="PT2.0S"
|
|
14
|
+
minBufferTime="PT4.0S">
|
|
15
|
+
<ProgramInformation>
|
|
16
|
+
</ProgramInformation>
|
|
17
|
+
<ServiceDescription id="0">
|
|
18
|
+
</ServiceDescription>
|
|
19
|
+
<Period id="0" start="PT0.0S">
|
|
20
|
+
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
|
|
21
|
+
<Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
|
|
22
|
+
<SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
|
23
|
+
<SegmentTimeline>
|
|
24
|
+
<S t="0" d="30720" r="3" />
|
|
25
|
+
</SegmentTimeline>
|
|
26
|
+
</SegmentTemplate>
|
|
27
|
+
</Representation>
|
|
28
|
+
</AdaptationSet>
|
|
29
|
+
</Period>
|
|
30
|
+
</MPD>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
|
4
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
5
|
+
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
|
6
|
+
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
|
7
|
+
type="dynamic"
|
|
8
|
+
minimumUpdatePeriod="PT2S"
|
|
9
|
+
suggestedPresentationDelay="PT2S"
|
|
10
|
+
availabilityStartTime="2021-10-28T07:36:27.755Z"
|
|
11
|
+
publishTime="2021-10-28T07:36:37.752Z"
|
|
12
|
+
timeShiftBufferDepth="PT10.0S"
|
|
13
|
+
maxSegmentDuration="PT2.0S"
|
|
14
|
+
minBufferTime="PT4.0S">
|
|
15
|
+
<ProgramInformation>
|
|
16
|
+
</ProgramInformation>
|
|
17
|
+
<ServiceDescription id="0">
|
|
18
|
+
</ServiceDescription>
|
|
19
|
+
<Period id="0" start="PT0.0S">
|
|
20
|
+
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
|
|
21
|
+
<Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
|
|
22
|
+
<SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
|
23
|
+
<SegmentTimeline>
|
|
24
|
+
<S t="0" d="30720" r="4" />
|
|
25
|
+
</SegmentTimeline>
|
|
26
|
+
</SegmentTemplate>
|
|
27
|
+
</Representation>
|
|
28
|
+
</AdaptationSet>
|
|
29
|
+
</Period>
|
|
30
|
+
</MPD>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
|
4
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
5
|
+
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
|
6
|
+
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
|
7
|
+
type="dynamic"
|
|
8
|
+
minimumUpdatePeriod="PT2S"
|
|
9
|
+
suggestedPresentationDelay="PT2S"
|
|
10
|
+
availabilityStartTime="2021-10-28T07:36:27.755Z"
|
|
11
|
+
publishTime="2021-10-28T07:36:41.751Z"
|
|
12
|
+
timeShiftBufferDepth="PT10.0S"
|
|
13
|
+
maxSegmentDuration="PT2.0S"
|
|
14
|
+
minBufferTime="PT4.0S">
|
|
15
|
+
<ProgramInformation>
|
|
16
|
+
</ProgramInformation>
|
|
17
|
+
<ServiceDescription id="0">
|
|
18
|
+
</ServiceDescription>
|
|
19
|
+
<Period id="0" start="PT0.0S">
|
|
20
|
+
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1280" maxHeight="720" par="16:9">
|
|
21
|
+
<Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="122012" width="1280" height="720" sar="1:1">
|
|
22
|
+
<SegmentTemplate timescale="15360" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="3">
|
|
23
|
+
<SegmentTimeline>
|
|
24
|
+
<S t="61440" d="30720" r="4" />
|
|
25
|
+
</SegmentTimeline>
|
|
26
|
+
</SegmentTemplate>
|
|
27
|
+
</Representation>
|
|
28
|
+
</AdaptationSet>
|
|
29
|
+
</Period>
|
|
30
|
+
</MPD>
|