shaka-player 3.3.17 → 3.3.19

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 (82) hide show
  1. package/.github/workflows/build-and-test.yaml +4 -4
  2. package/.github/workflows/demo-version-index.yaml +6 -0
  3. package/.github/workflows/selenium-lab-tests.yaml +78 -17
  4. package/.github/workflows/update-screenshots.yaml +142 -0
  5. package/AUTHORS +2 -1
  6. package/CHANGELOG.md +44 -0
  7. package/CONTRIBUTORS +4 -1
  8. package/demo/common/asset.js +15 -0
  9. package/demo/common/assets.js +4 -2
  10. package/demo/common/message_ids.js +1 -0
  11. package/demo/custom.js +19 -0
  12. package/demo/locales/en.json +1 -0
  13. package/demo/locales/source.json +4 -0
  14. package/demo/main.js +14 -0
  15. package/dist/controls.css +1 -1
  16. package/dist/demo.compiled.debug.js +146 -144
  17. package/dist/demo.compiled.debug.map +3 -3
  18. package/dist/demo.compiled.js +146 -144
  19. package/dist/demo.compiled.map +3 -3
  20. package/dist/deps.js +1 -1
  21. package/dist/locales.js +31 -31
  22. package/dist/receiver.compiled.debug.js +15 -15
  23. package/dist/receiver.compiled.debug.map +2 -2
  24. package/dist/receiver.compiled.js +15 -15
  25. package/dist/receiver.compiled.map +2 -2
  26. package/dist/shaka-player.compiled.d.ts +13 -2
  27. package/dist/shaka-player.compiled.debug.d.ts +13 -2
  28. package/dist/shaka-player.compiled.debug.js +160 -158
  29. package/dist/shaka-player.compiled.debug.map +3 -3
  30. package/dist/shaka-player.compiled.js +338 -335
  31. package/dist/shaka-player.compiled.map +3 -3
  32. package/dist/shaka-player.ui.d.ts +13 -2
  33. package/dist/shaka-player.ui.debug.d.ts +13 -2
  34. package/dist/shaka-player.ui.debug.js +224 -222
  35. package/dist/shaka-player.ui.debug.map +3 -3
  36. package/dist/shaka-player.ui.js +501 -498
  37. package/dist/shaka-player.ui.map +3 -3
  38. package/docs/tutorials/errors.md +94 -0
  39. package/docs/tutorials/fairplay.md +2 -2
  40. package/docs/tutorials/index.json +2 -0
  41. package/docs/tutorials/widevine-service-certs.md +45 -0
  42. package/externs/ima.js +9 -0
  43. package/externs/shaka/net.js +1 -0
  44. package/externs/shaka/player.js +13 -7
  45. package/lib/ads/client_side_ad_manager.js +11 -5
  46. package/lib/dash/dash_parser.js +49 -48
  47. package/lib/hls/hls_parser.js +2 -0
  48. package/lib/media/media_source_engine.js +9 -4
  49. package/lib/media/streaming_engine.js +8 -3
  50. package/lib/media/transmuxer.js +13 -7
  51. package/lib/media/video_wrapper.js +3 -3
  52. package/lib/net/http_fetch_plugin.js +79 -57
  53. package/lib/net/networking_engine.js +1 -0
  54. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  55. package/lib/player.js +19 -2
  56. package/lib/polyfill/media_capabilities.js +15 -8
  57. package/lib/polyfill/mediasource.js +7 -2
  58. package/lib/text/mp4_vtt_parser.js +4 -0
  59. package/lib/text/text_engine.js +1 -0
  60. package/lib/text/ui_text_displayer.js +1 -0
  61. package/lib/text/vtt_text_parser.js +4 -5
  62. package/lib/util/manifest_parser_utils.js +1 -2
  63. package/lib/util/mime_utils.js +12 -1
  64. package/lib/util/periods.js +5 -5
  65. package/lib/util/stream_utils.js +28 -16
  66. package/maintained-branches.md +3 -4
  67. package/package.json +1 -1
  68. package/test/media/content_workarounds_unit.js +43 -0
  69. package/test/media/drm_engine_integration.js +4 -17
  70. package/test/media/streaming_engine_unit.js +6 -1
  71. package/test/net/networking_engine_unit.js +6 -1
  72. package/test/player_integration.js +39 -0
  73. package/test/player_unit.js +54 -0
  74. package/test/polyfill/media_capabilities_unit.js +20 -23
  75. package/test/test/assets/text-clip-alt.vtt +63 -0
  76. package/test/test/util/fake_drm_engine.js +3 -0
  77. package/test/test/util/simple_fakes.js +3 -0
  78. package/test/test/util/test_scheme.js +9 -14
  79. package/test/text/mp4_vtt_parser_unit.js +8 -0
  80. package/test/text/vtt_text_parser_unit.js +26 -2
  81. package/test/text/web_vtt_layout_integration.js +0 -3
  82. package/test/util/mime_utils_unit.js +10 -0
@@ -398,6 +398,45 @@ describe('Player', () => {
398
398
  player.setTextTrackVisibility(true);
399
399
  expect(getTracksActive()).toEqual([false, true]);
400
400
  });
401
+
402
+ // https://github.com/shaka-project/shaka-player/issues/4821
403
+ it('loads a single text stream', async () => {
404
+ player.configure({preferredTextLanguage: 'en'});
405
+ await player.load('test:sintel_no_text_compiled');
406
+
407
+ // Add preferred language text track.
408
+ const locationUri = new goog.Uri(location.href);
409
+ const partialUri = new goog.Uri('/base/test/test/assets/text-clip.vtt');
410
+ const absoluteUri = locationUri.resolve(partialUri);
411
+ await player.addTextTrackAsync(
412
+ absoluteUri.toString(), 'en', 'subtitles', 'text/vtt');
413
+
414
+ // Add alternate language text track.
415
+ // Two text tracks with same timings but different text
416
+ // are necessary for test.
417
+ const partialUri2 =
418
+ new goog.Uri('/base/test/test/assets/text-clip-alt.vtt');
419
+ const absoluteUri2 = locationUri.resolve(partialUri2);
420
+ await player.addTextTrackAsync(
421
+ absoluteUri2.toString(), 'fr', 'subtitles', 'text/vtt');
422
+
423
+ const textTracks = player.getTextTracks();
424
+ expect(textTracks.length).toBe(2);
425
+ expect(textTracks[0].language).toBe('en');
426
+ expect(textTracks[1].language).toBe('fr');
427
+
428
+ // Enable text visibilty and immediately change language.
429
+ // Only one set of cues should be active.
430
+ // Cues should be of the selected language track.
431
+ player.setTextTrackVisibility(true);
432
+ player.selectTextLanguage('fr');
433
+ video.currentTime = 5;
434
+ video.play();
435
+ await waiter.waitForMovementOrFailOnTimeout(video, 10);
436
+
437
+ expect(video.textTracks[0].activeCues.length).toBe(1);
438
+ expect(player.getTextTracks()[1].active).toBe(true);
439
+ });
401
440
  }); // describe('setTextTrackVisibility')
402
441
 
403
442
  describe('plays', () => {
@@ -370,6 +370,60 @@ describe('Player', () => {
370
370
  expect(streamingEngine.unloadTextStream).not.toHaveBeenCalled();
371
371
  });
372
372
  });
373
+
374
+ describe('when config.streaming.preferNativeHls is set to true', () => {
375
+ beforeEach(() => {
376
+ shaka.media.ManifestParser.registerParserByMime(
377
+ 'application/x-mpegurl',
378
+ () => new shaka.test.FakeManifestParser(manifest));
379
+ });
380
+
381
+ afterEach(() => {
382
+ shaka.media.ManifestParser.unregisterParserByMime(
383
+ 'application/x-mpegurl');
384
+ video.canPlayType.calls.reset();
385
+ });
386
+
387
+ it('only applies to HLS streams', async () => {
388
+ video.canPlayType.and.returnValue('maybe');
389
+ spyOn(shaka.util.Platform, 'anyMediaElement').and.returnValue(video);
390
+ spyOn(shaka.util.Platform, 'supportsMediaSource').and.returnValue(true);
391
+ spyOn(shaka.util.Platform, 'isApple').and.returnValue(false);
392
+ // Make sure player.load() resolves for src=
393
+ spyOn(shaka.util.MediaReadyState, 'waitForReadyState').and.callFake(
394
+ (mediaElement, readyState, eventManager, callback) => {
395
+ callback();
396
+ });
397
+
398
+ player.configure({
399
+ streaming: {
400
+ preferNativeHls: true,
401
+ useNativeHlsOnSafari: false,
402
+ },
403
+ });
404
+
405
+ await player.load(fakeManifestUri, undefined, 'application/x-mpegurl');
406
+
407
+ expect(player.getLoadMode()).toBe(shaka.Player.LoadMode.SRC_EQUALS);
408
+ });
409
+
410
+ it('does not apply to non-HLS streams', async () => {
411
+ video.canPlayType.and.returnValue('maybe');
412
+ spyOn(shaka.util.Platform, 'supportsMediaSource').and.returnValue(true);
413
+ spyOn(shaka.util.Platform, 'isApple').and.returnValue(false);
414
+
415
+ player.configure({
416
+ streaming: {
417
+ preferNativeHls: true,
418
+ useNativeHlsOnSafari: false,
419
+ },
420
+ });
421
+
422
+ await player.load(fakeManifestUri, 0, fakeMimeType);
423
+
424
+ expect(player.getLoadMode()).toBe(shaka.Player.LoadMode.MEDIA_SOURCE);
425
+ });
426
+ });
373
427
  }); // describe('load/unload')
374
428
 
375
429
  describe('getConfiguration', () => {
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  describe('MediaCapabilities', () => {
8
- const Util = shaka.test.Util;
9
8
  const originalCast = window['cast'];
10
9
  const originalVendor = navigator.vendor;
11
10
  const originalUserAgent = navigator.userAgent;
@@ -68,7 +67,6 @@ describe('MediaCapabilities', () => {
68
67
  width: 512,
69
68
  },
70
69
  };
71
- shaka.polyfill.MediaCapabilities.memoizedMediaKeySystemAccessRequests_ = {};
72
70
 
73
71
  mockCanDisplayType = jasmine.createSpy('canDisplayType');
74
72
  mockCanDisplayType.and.returnValue(false);
@@ -168,30 +166,29 @@ describe('MediaCapabilities', () => {
168
166
  expect(result.keySystemAccess).toEqual(mockResult);
169
167
  });
170
168
 
171
- it('throws when the cast namespace is not available', async () => {
172
- // Temporarily remove window.cast to trigger error. It's restored after
173
- // every test.
174
- delete window['cast'];
169
+ it('falls back to isTypeSupported() when cast namespace is not available',
170
+ async () => {
171
+ // Temporarily remove window.cast to trigger error. It's restored
172
+ // after every test.
173
+ delete window['cast'];
175
174
 
176
- const isChromecastSpy =
177
- spyOn(shaka['util']['Platform'],
178
- 'isChromecast').and.returnValue(true);
179
- const expected = Util.jasmineError(new shaka.util.Error(
180
- shaka.util.Error.Severity.CRITICAL,
181
- shaka.util.Error.Category.CAST,
182
- shaka.util.Error.Code.CAST_API_UNAVAILABLE));
183
- const isTypeSupportedSpy =
184
- spyOn(window['MediaSource'], 'isTypeSupported').and.returnValue(true);
175
+ const isChromecastSpy =
176
+ spyOn(shaka['util']['Platform'],
177
+ 'isChromecast').and.returnValue(true);
178
+ const isTypeSupportedSpy =
179
+ spyOn(window['MediaSource'], 'isTypeSupported')
180
+ .and.returnValue(true);
185
181
 
186
- shaka.polyfill.MediaCapabilities.install();
187
- await expectAsync(
188
- navigator.mediaCapabilities.decodingInfo(mockDecodingConfig))
189
- .toBeRejectedWith(expected);
182
+ shaka.polyfill.MediaCapabilities.install();
183
+ await navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
190
184
 
191
- expect(isTypeSupportedSpy).not.toHaveBeenCalled();
192
- // 1 (during install()) + 1 (for video config check).
193
- expect(isChromecastSpy).toHaveBeenCalledTimes(2);
194
- });
185
+ expect(mockCanDisplayType).not.toHaveBeenCalled();
186
+ // 1 (during install()) + 1 (for video config check).
187
+ expect(isChromecastSpy).toHaveBeenCalledTimes(2);
188
+ // 1 (fallback in canCastDisplayType()) +
189
+ // 1 (mockDecodingConfig.audio).
190
+ expect(isTypeSupportedSpy).toHaveBeenCalledTimes(2);
191
+ });
195
192
 
196
193
  it('falls back to isTypeSupported() when canDisplayType() missing',
197
194
  async () => {
@@ -0,0 +1,63 @@
1
+ WEBVTT
2
+
3
+ 00:04.000 --> 00:07.300
4
+ Journal de bord du capitaine.
5
+ Date stellaire 41636.9.
6
+
7
+ 00:07.500 --> 00:11.800
8
+ Comme nous le craignions, l'épave
9
+ du cargo Odin de la Fédération,
10
+
11
+ 00:12.100 --> 00:16.400
12
+ percuté par un astéroïde,
13
+ ne révèle aucun signe de vie.
14
+
15
+ 00:16.700 --> 00:19.000
16
+ Mais trois nacelles
17
+ de sauvetage manquaient,
18
+
19
+ 00:19.300 --> 00:21.900
20
+ laissant supposer
21
+ qu'il y aurait des survivants.
22
+
23
+ 00:22.700 --> 00:27.700
24
+ - Mise sur orbite Angel One, paré.
25
+ - Quel genre d'endroit, Data ?
26
+
27
+ 00:28.200 --> 00:31.600
28
+ Planète de Classe-M.
29
+ Faune et flore à base de carbone.
30
+
31
+ 00:31.900 --> 00:34.300
32
+ Population éparse
33
+ et forme de vie intelligente.
34
+
35
+ 00:34.600 --> 00:38.400
36
+ Développement technique équivalent
37
+ au milieu du 20ème siècle terrien.
38
+
39
+ 00:38.800 --> 00:41.000
40
+ C'est presque un retour aux sources.
41
+
42
+ 00:41.300 --> 00:43.600
43
+ Si jamais des survivants
44
+ ont pu aller si loin,
45
+
46
+ 00:43.800 --> 00:49.000
47
+ c'était la planète la plus proche.
48
+ La distance qui nous a pris 2 jours
49
+
50
+ 00:49.500 --> 00:52.400
51
+ aurait pris 5 mois aux nacelles
52
+ de sauvetage du Odin.
53
+
54
+ 00:52.600 --> 00:54.700
55
+ Cinq mois, six jours, onze heures,
56
+ deux min...
57
+
58
+ 00:54.900 --> 00:58.400
59
+ - Merci, Data.
60
+ - ..et 57 secondes.
61
+
62
+ 00:58.600 --> 01:01.300
63
+ Signal audio provenant d'Angel One.
@@ -81,6 +81,9 @@ shaka.test.FakeDrmEngine = class {
81
81
  /** @type {!jasmine.Spy} */
82
82
  this.supportsVariant = jasmine.createSpy('supportsVariant');
83
83
  this.supportsVariant.and.returnValue(true);
84
+
85
+ /** @type {!jasmine.Spy} */
86
+ this.setSrcEquals = jasmine.createSpy('setSrcEquals');
84
87
  }
85
88
 
86
89
  /**
@@ -225,6 +225,9 @@ shaka.test.FakeVideo = class {
225
225
 
226
226
  /** @type {!jasmine.Spy} */
227
227
  this.dispatchEvent = jasmine.createSpy('dispatchEvent');
228
+
229
+ /** @type {!jasmine.Spy} */
230
+ this.canPlayType = jasmine.createSpy('canPlayType');
228
231
  }
229
232
  };
230
233
 
@@ -444,6 +444,7 @@ const widevineDrmServers = {
444
444
 
445
445
  /** @type {AVMetadataType} */
446
446
  const axinomMultiDrmVideoSegment = {
447
+ // Taken from Axinom's v6 test vector.
447
448
  initSegmentUri: '/base/test/test/assets/multidrm-video-init.mp4',
448
449
  mdhdOffset: 0x1d1,
449
450
  segmentUri: '/base/test/test/assets/multidrm-video-segment.mp4',
@@ -458,6 +459,7 @@ const axinomMultiDrmVideoSegment = {
458
459
 
459
460
  /** @type {AVMetadataType} */
460
461
  const axinomMultiDrmAudioSegment = {
462
+ // Taken from Axinom's v6 test vector.
461
463
  initSegmentUri: '/base/test/test/assets/multidrm-audio-init.mp4',
462
464
  mdhdOffset: 0x192,
463
465
  segmentUri: '/base/test/test/assets/multidrm-audio-segment.mp4',
@@ -472,20 +474,15 @@ const axinomMultiDrmAudioSegment = {
472
474
 
473
475
  /** @type {!Object.<string, string>} */
474
476
  const axinomDrmServers = {
477
+ // NOTE: These are not Axinom's actual servers. These are test servers for
478
+ // Widevine and PlayReady that let us specify the known key IDs and keys for
479
+ // Axinom's v6 test vectors. Axinom's own servers started returning 403
480
+ // errors for these older test vectors, and we were forced to switch to
481
+ // something stable and independent.
475
482
  'com.widevine.alpha':
476
- 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
483
+ 'https://cwip-shaka-proxy.appspot.com/specific_key?blodJidXR9eARuql0dNLWg=GX8m9XLIZNIzizrl0RTqnA',
477
484
  'com.microsoft.playready':
478
- 'https://drm-playready-licensing.axtest.net/AcquireLicense',
479
- };
480
-
481
- /** @type {!Object.<string, string>} */
482
- const axinomDrmHeaders = {
483
- 'X-AxDRM-Message':
484
- 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5' +
485
- 'X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc' +
486
- '2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIj' +
487
- 'oiNmU1YTFkMjYtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflO' +
488
- 'Pv9qHnu3ZWJNZ12jgkqTabmwXbDWk_47tLNE',
485
+ 'https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(kid:6e5a1d26-2757-47d7-8046-eaa5d1d34b5a,contentkey:GX8m9XLIZNIzizrl0RTqnA==,sl:150)',
489
486
  };
490
487
 
491
488
  /** @type {TextMetadataType} */
@@ -581,7 +578,6 @@ shaka.test.TestScheme.DATA = {
581
578
  audio: axinomMultiDrmAudioSegment,
582
579
  text: vttSegment,
583
580
  licenseServers: axinomDrmServers,
584
- licenseRequestHeaders: axinomDrmHeaders,
585
581
  duration: 30,
586
582
  },
587
583
 
@@ -593,7 +589,6 @@ shaka.test.TestScheme.DATA = {
593
589
  initData: undefined,
594
590
  }),
595
591
  licenseServers: axinomDrmServers,
596
- licenseRequestHeaders: axinomDrmHeaders,
597
592
  duration: 30,
598
593
  },
599
594
 
@@ -170,6 +170,14 @@ describe('Mp4VttParser', () => {
170
170
  verifyHelper(cues, result);
171
171
  });
172
172
 
173
+ it('handles empty media segments', () => {
174
+ const parser = new shaka.text.Mp4VttParser();
175
+ parser.parseInit(vttInitSegment);
176
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
177
+ const result = parser.parseMedia(new Uint8Array(0), time);
178
+ verifyHelper([], result);
179
+ });
180
+
173
181
  it('rejects init segment with no vtt', () => {
174
182
  const error = shaka.test.Util.jasmineError(new shaka.util.Error(
175
183
  shaka.util.Error.Severity.CRITICAL,
@@ -776,11 +776,35 @@ describe('VttTextParser', () => {
776
776
  it('support escaped html payload', () => {
777
777
  verifyHelper(
778
778
  [
779
- {startTime: 20.1, endTime: 40.505, payload: '"Test & 1"\u{a0}'},
779
+ {
780
+ startTime: 20.1,
781
+ endTime: 40.505,
782
+ payload: '"Test & 1"\u{a0}',
783
+ },
784
+ {
785
+ startTime: 41,
786
+ endTime: 42,
787
+ payload: '',
788
+ nestedCues: [
789
+ {
790
+ startTime: 41,
791
+ endTime: 42,
792
+ payload: 'Test',
793
+ fontStyle: Cue.fontStyle.ITALIC,
794
+ },
795
+ {
796
+ startTime: 41,
797
+ endTime: 42,
798
+ payload: '&',
799
+ },
800
+ ],
801
+ },
780
802
  ],
781
803
  'WEBVTT\n\n' +
782
804
  '00:00:20.100 --> 00:00:40.505\n' +
783
- '&quot;Test &amp; 1&quot;&nbsp;',
805
+ '&quot;Test &amp; 1&quot;&nbsp;\n\n' +
806
+ '00:00:41.000 --> 00:00:42.000\n' +
807
+ '<i>Test</i>&amp;',
784
808
  {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
785
809
  });
786
810
 
@@ -265,7 +265,6 @@ filterDescribe('WebVTT layout', shaka.test.TextLayoutTests.supported, () => {
265
265
  await helper.checkScreenshot('line-85-position-50-size-63');
266
266
  });
267
267
 
268
- // FIXME: UI version is slightly wrong: gaps between lines when bold
269
268
  it('bold long', async () => {
270
269
  parseAndDisplay([
271
270
  'WEBVTT\n',
@@ -278,7 +277,6 @@ filterDescribe('WebVTT layout', shaka.test.TextLayoutTests.supported, () => {
278
277
  await helper.checkScreenshot('bold-long');
279
278
  });
280
279
 
281
- // FIXME: UI version is slightly wrong: gaps between lines when italic
282
280
  it('italic long', async () => {
283
281
  parseAndDisplay([
284
282
  'WEBVTT\n',
@@ -291,7 +289,6 @@ filterDescribe('WebVTT layout', shaka.test.TextLayoutTests.supported, () => {
291
289
  await helper.checkScreenshot('italic-long');
292
290
  });
293
291
 
294
- // FIXME: UI version is slightly wrong: gaps between lines when italic
295
292
  it('underline long', async () => {
296
293
  parseAndDisplay([
297
294
  'WEBVTT\n',
@@ -51,4 +51,14 @@ describe('MimeUtils', () => {
51
51
  expect(getNormalizedCodec('dvh1.05')).toBe('dovi');
52
52
  expect(getNormalizedCodec('dvhe.05')).toBe('dovi');
53
53
  });
54
+
55
+ it('isHlsType', () => {
56
+ const isHlsType = (mimeType) => shaka.util.MimeUtils.isHlsType(mimeType);
57
+
58
+ expect(isHlsType('application/x-mpegurl')).toBe(true);
59
+ expect(isHlsType('application/vnd.apple.mpegurl')).toBe(true);
60
+ expect(isHlsType('application/dash+xml')).toBe(false);
61
+ expect(isHlsType('application/vnd.ms-sstr+xml')).toBe(false);
62
+ expect(isHlsType('foo')).toBe(false);
63
+ });
54
64
  });