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.
Files changed (94) hide show
  1. package/AUTHORS +3 -0
  2. package/CHANGELOG.md +56 -0
  3. package/CONTRIBUTORS +3 -0
  4. package/build/generateExterns.js +1 -1
  5. package/demo/common/assets.js +1 -1
  6. package/dist/controls.css +2 -2
  7. package/dist/controls.css.map +1 -1
  8. package/dist/demo.compiled.debug.js +1 -1
  9. package/dist/demo.compiled.js +1 -1
  10. package/dist/deps.js +1 -1
  11. package/dist/locales.js +35 -35
  12. package/dist/receiver.compiled.debug.js +1 -1
  13. package/dist/receiver.compiled.js +1 -1
  14. package/dist/shaka-player.compiled.d.ts +1 -1
  15. package/dist/shaka-player.compiled.debug.d.ts +1 -1
  16. package/dist/shaka-player.compiled.debug.externs.js +1 -1
  17. package/dist/shaka-player.compiled.debug.js +187 -183
  18. package/dist/shaka-player.compiled.debug.map +3 -3
  19. package/dist/shaka-player.compiled.externs.js +1 -1
  20. package/dist/shaka-player.compiled.js +799 -799
  21. package/dist/shaka-player.compiled.map +2 -2
  22. package/dist/shaka-player.ui.d.ts +1 -1
  23. package/dist/shaka-player.ui.debug.d.ts +1 -1
  24. package/dist/shaka-player.ui.debug.externs.js +3 -3
  25. package/dist/shaka-player.ui.debug.js +264 -260
  26. package/dist/shaka-player.ui.debug.map +4 -4
  27. package/dist/shaka-player.ui.externs.js +3 -3
  28. package/dist/shaka-player.ui.js +896 -895
  29. package/dist/shaka-player.ui.map +4 -4
  30. package/docs/tutorials/fairplay.md +1 -1
  31. package/karma.conf.js +1 -0
  32. package/lib/ads/client_side_ad_manager.js +46 -86
  33. package/lib/ads/server_side_ad_manager.js +7 -10
  34. package/lib/cast/cast_proxy.js +3 -2
  35. package/lib/cast/cast_receiver.js +2 -1
  36. package/lib/cast/cast_sender.js +2 -2
  37. package/lib/dash/dash_parser.js +93 -2
  38. package/lib/dash/segment_list.js +1 -1
  39. package/lib/dash/segment_template.js +1 -1
  40. package/lib/hls/hls_parser.js +30 -17
  41. package/lib/media/drm_engine.js +7 -0
  42. package/lib/media/gap_jumping_controller.js +4 -2
  43. package/lib/media/media_source_engine.js +1 -2
  44. package/lib/media/streaming_engine.js +2 -1
  45. package/lib/net/networking_engine.js +2 -1
  46. package/lib/player.js +22 -22
  47. package/lib/polyfill/media_capabilities.js +7 -1
  48. package/lib/polyfill/mediasource.js +6 -0
  49. package/lib/polyfill/orientation.js +1 -1
  50. package/lib/polyfill/patchedmediakeys_apple.js +4 -4
  51. package/lib/polyfill/patchedmediakeys_ms.js +4 -4
  52. package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
  53. package/lib/text/mp4_ttml_parser.js +3 -0
  54. package/lib/text/text_engine.js +14 -2
  55. package/lib/text/ttml_text_parser.js +5 -0
  56. package/lib/text/ui_text_displayer.js +85 -37
  57. package/lib/util/fake_event.js +53 -7
  58. package/lib/util/periods.js +4 -1
  59. package/lib/util/platform.js +13 -1
  60. package/lib/util/stream_utils.js +13 -0
  61. package/package.json +1 -1
  62. package/test/cast/cast_proxy_unit.js +12 -6
  63. package/test/cast/cast_utils_unit.js +1 -1
  64. package/test/dash/dash_parser_manifest_unit.js +187 -1
  65. package/test/hls/hls_parser_unit.js +5 -1
  66. package/test/media/closed_caption_parser_unit.js +6 -1
  67. package/test/media/drm_engine_unit.js +43 -0
  68. package/test/player_integration.js +38 -0
  69. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  70. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  71. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  72. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  73. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  74. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  75. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  76. package/test/test/assets/3675/dash_0.mpd +30 -0
  77. package/test/test/assets/3675/dash_1.mpd +30 -0
  78. package/test/test/assets/3675/dash_2.mpd +30 -0
  79. package/test/test/assets/3675/dash_3.mpd +30 -0
  80. package/test/test/assets/3675/dash_4.mpd +30 -0
  81. package/test/test/assets/3675/dash_5.mpd +26 -0
  82. package/test/test/assets/3675/init-stream0.m4s +0 -0
  83. package/test/test/util/fake_ad_manager.js +1 -1
  84. package/test/text/ttml_text_parser_unit.js +9 -0
  85. package/test/text/ui_text_displayer_unit.js +29 -17
  86. package/test/ui/localization_unit.js +15 -14
  87. package/test/ui/text_displayer_layout_unit.js +13 -1
  88. package/test/util/stream_utils_unit.js +35 -0
  89. package/ui/cast_button.js +2 -3
  90. package/ui/controls.js +4 -6
  91. package/ui/less/containers.less +3 -7
  92. package/ui/locales/ru.json +6 -6
  93. package/ui/localization.js +28 -51
  94. package/ui/pip_button.js +2 -3
@@ -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
  *
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "3.1.3",
4
+ "version": "3.1.4",
5
5
  "homepage": "https://github.com/google/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -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('timeupdate', {detail: 8675309});
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('timeupdate', {detail: 8675309});
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('timeupdate', {detail: 8675309});
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('buffering', {detail: 8675309});
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('buffering', {detail: 8675309});
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('buffering', {detail: 8675309});
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 = new FakeEvent(deserialized['type'], deserialized);
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});
@@ -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>