shaka-player 4.3.15 → 4.3.16

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 (60) hide show
  1. package/.github/workflows/appspot.yaml +4 -4
  2. package/.github/workflows/build-and-test.yaml +23 -10
  3. package/.github/workflows/custom-actions/set-commit-status/action.yaml +29 -7
  4. package/.github/workflows/demo-version-index.yaml +3 -3
  5. package/.github/workflows/nightly-demo.yaml +4 -4
  6. package/.github/workflows/release-please.yaml +25 -24
  7. package/.github/workflows/report-incremental-coverage.yaml +1 -1
  8. package/.github/workflows/selenium-lab-tests.yaml +30 -18
  9. package/.github/workflows/sync-labels.yaml +1 -1
  10. package/.github/workflows/talk-to-shaka-bot.yaml +1 -1
  11. package/.github/workflows/update-issues.yaml +1 -1
  12. package/.github/workflows/update-screenshots.yaml +25 -38
  13. package/CHANGELOG.md +28 -0
  14. package/demo/demo.less +1 -5
  15. package/dist/demo.css +1 -1
  16. package/dist/demo.css.map +1 -1
  17. package/dist/deps.js +0 -1
  18. package/dist/locales.js +31 -31
  19. package/dist/shaka-player.compiled.d.ts +6 -10
  20. package/dist/shaka-player.compiled.debug.d.ts +6 -10
  21. package/dist/shaka-player.compiled.debug.externs.js +13 -11
  22. package/dist/shaka-player.compiled.debug.js +209 -206
  23. package/dist/shaka-player.compiled.debug.map +4 -4
  24. package/dist/shaka-player.compiled.externs.js +13 -11
  25. package/dist/shaka-player.compiled.js +854 -852
  26. package/dist/shaka-player.compiled.map +4 -4
  27. package/dist/shaka-player.ui.d.ts +6 -10
  28. package/dist/shaka-player.ui.debug.d.ts +6 -10
  29. package/dist/shaka-player.ui.debug.externs.js +13 -11
  30. package/dist/shaka-player.ui.debug.js +158 -155
  31. package/dist/shaka-player.ui.debug.map +4 -4
  32. package/dist/shaka-player.ui.externs.js +13 -11
  33. package/dist/shaka-player.ui.js +949 -947
  34. package/dist/shaka-player.ui.map +4 -4
  35. package/docs/tutorials/faq.md +25 -0
  36. package/docs/tutorials/offline.md +3 -0
  37. package/docs/tutorials/ui.md +4 -4
  38. package/externs/shaka/player.js +32 -19
  39. package/lib/cast/cast_proxy.js +1 -3
  40. package/lib/cast/cast_sender.js +8 -11
  41. package/lib/cea/cea608_memory.js +1 -1
  42. package/lib/dash/dash_parser.js +3 -0
  43. package/lib/dash/segment_template.js +5 -0
  44. package/lib/hls/hls_parser.js +18 -16
  45. package/lib/media/drm_engine.js +17 -3
  46. package/lib/media/presentation_timeline.js +23 -0
  47. package/lib/media/segment_index.js +10 -0
  48. package/lib/media/streaming_engine.js +1 -13
  49. package/lib/player.js +18 -4
  50. package/lib/text/ui_text_displayer.js +10 -1
  51. package/lib/util/manifest_parser_utils.js +1 -0
  52. package/lib/util/stream_utils.js +8 -5
  53. package/package.json +1 -1
  54. package/test/cast/cast_proxy_unit.js +0 -46
  55. package/test/cast/cast_sender_unit.js +26 -26
  56. package/test/cea/cea608_memory_unit.js +3 -2
  57. package/test/hls/hls_live_unit.js +52 -0
  58. package/test/media/media_source_engine_integration.js +1 -1
  59. package/test/player_unit.js +13 -1
  60. package/lib/polyfill/storage_estimate.js +0 -57
@@ -114,52 +114,6 @@ describe('CastProxy', () => {
114
114
  });
115
115
 
116
116
  describe('cast', () => {
117
- it('pauses the local video', () => {
118
- proxy.cast();
119
- expect(mockVideo.pause).toHaveBeenCalled();
120
- });
121
-
122
- it('passes initial state to sender', () => {
123
- mockVideo.loop = true;
124
- mockVideo.playbackRate = 3;
125
- mockVideo.currentTime = 12;
126
- const fakeConfig = {key: 'value'};
127
- mockPlayer.getConfiguration.and.returnValue(fakeConfig);
128
- mockPlayer.isTextTrackVisible.and.returnValue(false);
129
- const fakeManifestUri = 'foo://bar';
130
- mockPlayer.getAssetUri.and.returnValue(fakeManifestUri);
131
-
132
- proxy.cast();
133
- const calls = mockSender.cast.calls;
134
- expect(calls.count()).toBe(1);
135
- if (calls.count()) {
136
- const state = calls.argsFor(0)[0];
137
- // Video state goes directly:
138
- expect(state.video.loop).toBe(mockVideo.loop);
139
- expect(state.video.playbackRate).toBe(mockVideo.playbackRate);
140
- // Player state uses corresponding setter names:
141
- expect(state.player.configure).toEqual(fakeConfig);
142
- expect(state['playerAfterLoad'].setTextTrackVisibility).toBe(false);
143
- // Manifest URI:
144
- expect(state.manifest).toBe(fakeManifestUri);
145
- // Start time:
146
- expect(state.startTime).toBe(mockVideo.currentTime);
147
- }
148
- });
149
-
150
- it('does not provide a start time if the video has ended', () => {
151
- mockVideo.ended = true;
152
- mockVideo.currentTime = 12;
153
-
154
- proxy.cast();
155
- const calls = mockSender.cast.calls;
156
- expect(calls.count()).toBe(1);
157
- if (calls.count()) {
158
- const state = calls.argsFor(0)[0];
159
- expect(state.startTime).toBe(null);
160
- }
161
- });
162
-
163
117
  it('unloads the local player after casting is complete', async () => {
164
118
  /** @type {!shaka.util.PublicPromise} */
165
119
  const p = new shaka.util.PublicPromise();
@@ -157,7 +157,7 @@ describe('CastSender', () => {
157
157
  shaka.util.Error.Severity.RECOVERABLE,
158
158
  shaka.util.Error.Category.CAST,
159
159
  shaka.util.Error.Code.CAST_API_UNAVAILABLE));
160
- await expectAsync(sender.cast(fakeInitState)).toBeRejectedWith(expected);
160
+ await expectAsync(sender.cast()).toBeRejectedWith(expected);
161
161
  });
162
162
 
163
163
  it('fails when there are no receivers', async () => {
@@ -168,7 +168,7 @@ describe('CastSender', () => {
168
168
  shaka.util.Error.Severity.RECOVERABLE,
169
169
  shaka.util.Error.Category.CAST,
170
170
  shaka.util.Error.Code.NO_CAST_RECEIVERS));
171
- await expectAsync(sender.cast(fakeInitState)).toBeRejectedWith(expected);
171
+ await expectAsync(sender.cast()).toBeRejectedWith(expected);
172
172
  });
173
173
 
174
174
  it('creates a session and sends an "init" message', async () => {
@@ -177,7 +177,7 @@ describe('CastSender', () => {
177
177
  fakeReceiverAvailability(true);
178
178
  expect(sender.hasReceivers()).toBe(true);
179
179
 
180
- const p = sender.cast(fakeInitState);
180
+ const p = sender.cast();
181
181
  fakeSessionConnection();
182
182
 
183
183
  await p;
@@ -220,7 +220,7 @@ describe('CastSender', () => {
220
220
  sender.init();
221
221
  fakeReceiverAvailability(true);
222
222
 
223
- const p = sender.cast(fakeInitState);
223
+ const p = sender.cast();
224
224
  fakeSessionConnectionFailure(metadata.castErrorCode);
225
225
 
226
226
  const expected = Util.jasmineError(new shaka.util.Error(
@@ -236,7 +236,7 @@ describe('CastSender', () => {
236
236
  sender.init();
237
237
  fakeReceiverAvailability(true);
238
238
 
239
- const p = sender.cast(fakeInitState);
239
+ const p = sender.cast();
240
240
  fakeSessionConnection();
241
241
 
242
242
  await p;
@@ -244,14 +244,14 @@ describe('CastSender', () => {
244
244
  shaka.util.Error.Severity.RECOVERABLE,
245
245
  shaka.util.Error.Category.CAST,
246
246
  shaka.util.Error.Code.ALREADY_CASTING));
247
- await expectAsync(sender.cast(fakeInitState)).toBeRejectedWith(expected);
247
+ await expectAsync(sender.cast()).toBeRejectedWith(expected);
248
248
  });
249
249
  });
250
250
 
251
251
  it('re-uses old sessions', async () => {
252
252
  sender.init();
253
253
  fakeReceiverAvailability(true);
254
- const p = sender.cast(fakeInitState);
254
+ const p = sender.cast();
255
255
  fakeSessionConnection();
256
256
  const oldMockSession = mockSession;
257
257
  await p;
@@ -289,7 +289,7 @@ describe('CastSender', () => {
289
289
  it('doesn\'t re-use stopped sessions', async () => {
290
290
  sender.init();
291
291
  fakeReceiverAvailability(true);
292
- const p = sender.cast(fakeInitState);
292
+ const p = sender.cast();
293
293
  fakeSessionConnection();
294
294
  await p;
295
295
  await sender.destroy();
@@ -334,7 +334,7 @@ describe('CastSender', () => {
334
334
  sender.init();
335
335
  fakeReceiverAvailability(true);
336
336
  sender.setAppData(fakeAppData);
337
- const p = sender.cast(fakeInitState);
337
+ const p = sender.cast();
338
338
  fakeSessionConnection();
339
339
 
340
340
  await p;
@@ -347,7 +347,7 @@ describe('CastSender', () => {
347
347
  it('sends a special "appData" message if casting', async () => {
348
348
  sender.init();
349
349
  fakeReceiverAvailability(true);
350
- const p = sender.cast(fakeInitState);
350
+ const p = sender.cast();
351
351
  fakeSessionConnection();
352
352
  await p;
353
353
 
@@ -390,7 +390,7 @@ describe('CastSender', () => {
390
390
  it('is not triggered if making a new session', async () => {
391
391
  sender.init();
392
392
  fakeReceiverAvailability(true);
393
- const p = sender.cast(fakeInitState);
393
+ const p = sender.cast();
394
394
  fakeSessionConnection();
395
395
  await p;
396
396
 
@@ -437,7 +437,7 @@ describe('CastSender', () => {
437
437
  it('is triggered by an "event" message', async () => {
438
438
  sender.init();
439
439
  fakeReceiverAvailability(true);
440
- const p = sender.cast(fakeInitState);
440
+ const p = sender.cast();
441
441
  fakeSessionConnection();
442
442
  await p;
443
443
 
@@ -460,7 +460,7 @@ describe('CastSender', () => {
460
460
  it('is triggered when casting ends', async () => {
461
461
  sender.init();
462
462
  fakeReceiverAvailability(true);
463
- const p = sender.cast(fakeInitState);
463
+ const p = sender.cast();
464
464
  fakeSessionConnection();
465
465
  await p;
466
466
 
@@ -477,7 +477,7 @@ describe('CastSender', () => {
477
477
  it('opens the dialog if we are casting', async () => {
478
478
  sender.init();
479
479
  fakeReceiverAvailability(true);
480
- const p = sender.cast(fakeInitState);
480
+ const p = sender.cast();
481
481
  fakeSessionConnection();
482
482
  await p;
483
483
 
@@ -502,7 +502,7 @@ describe('CastSender', () => {
502
502
  it('returns most recent properties from "update" messages', async () => {
503
503
  sender.init();
504
504
  fakeReceiverAvailability(true);
505
- const p = sender.cast(fakeInitState);
505
+ const p = sender.cast();
506
506
  fakeSessionConnection();
507
507
  await p;
508
508
 
@@ -545,7 +545,7 @@ describe('CastSender', () => {
545
545
  it('simple methods trigger "call" messages', async () => {
546
546
  sender.init();
547
547
  fakeReceiverAvailability(true);
548
- const p = sender.cast(fakeInitState);
548
+ const p = sender.cast();
549
549
  fakeSessionConnection();
550
550
  await p;
551
551
 
@@ -568,7 +568,7 @@ describe('CastSender', () => {
568
568
  method = null;
569
569
  sender.init();
570
570
  fakeReceiverAvailability(true);
571
- const p = sender.cast(fakeInitState);
571
+ const p = sender.cast();
572
572
  fakeSessionConnection();
573
573
  await p;
574
574
 
@@ -655,7 +655,7 @@ describe('CastSender', () => {
655
655
  it('overrides any cached properties', async () => {
656
656
  sender.init();
657
657
  fakeReceiverAvailability(true);
658
- const p = sender.cast(fakeInitState);
658
+ const p = sender.cast();
659
659
  fakeSessionConnection();
660
660
  await p;
661
661
 
@@ -675,7 +675,7 @@ describe('CastSender', () => {
675
675
  it('causes a "set" message to be sent', async () => {
676
676
  sender.init();
677
677
  fakeReceiverAvailability(true);
678
- const p = sender.cast(fakeInitState);
678
+ const p = sender.cast();
679
679
  fakeSessionConnection();
680
680
  await p;
681
681
 
@@ -691,7 +691,7 @@ describe('CastSender', () => {
691
691
  it('can be used before we have an "update" message', async () => {
692
692
  sender.init();
693
693
  fakeReceiverAvailability(true);
694
- const p = sender.cast(fakeInitState);
694
+ const p = sender.cast();
695
695
  fakeSessionConnection();
696
696
  await p;
697
697
 
@@ -705,7 +705,7 @@ describe('CastSender', () => {
705
705
  it('is true only after we have an "update" message', async () => {
706
706
  sender.init();
707
707
  fakeReceiverAvailability(true);
708
- const p = sender.cast(fakeInitState);
708
+ const p = sender.cast();
709
709
  fakeSessionConnection();
710
710
  await p;
711
711
 
@@ -723,7 +723,7 @@ describe('CastSender', () => {
723
723
  it('disconnects and cancels all async operations', async () => {
724
724
  sender.init();
725
725
  fakeReceiverAvailability(true);
726
- const cast = sender.cast(fakeInitState);
726
+ const cast = sender.cast();
727
727
  fakeSessionConnection();
728
728
  await cast;
729
729
 
@@ -756,7 +756,7 @@ describe('CastSender', () => {
756
756
  it('transfers playback to local device', async () => {
757
757
  sender.init();
758
758
  fakeReceiverAvailability(true);
759
- const cast = sender.cast(fakeInitState);
759
+ const cast = sender.cast();
760
760
  fakeSessionConnection();
761
761
  await cast;
762
762
 
@@ -772,7 +772,7 @@ describe('CastSender', () => {
772
772
  it('succeeds even if session.stop() throws', async () => {
773
773
  sender.init();
774
774
  fakeReceiverAvailability(true);
775
- const cast = sender.cast(fakeInitState);
775
+ const cast = sender.cast();
776
776
  fakeSessionConnection();
777
777
  await cast;
778
778
 
@@ -792,7 +792,7 @@ describe('CastSender', () => {
792
792
  beforeEach(async () => {
793
793
  sender.init();
794
794
  fakeReceiverAvailability(true);
795
- const cast = sender.cast(fakeInitState);
795
+ const cast = sender.cast();
796
796
  fakeSessionConnection();
797
797
  await cast;
798
798
 
@@ -837,7 +837,7 @@ describe('CastSender', () => {
837
837
  it('cancels all async operations', async () => {
838
838
  sender.init();
839
839
  fakeReceiverAvailability(true);
840
- const cast = sender.cast(fakeInitState);
840
+ const cast = sender.cast();
841
841
  fakeSessionConnection();
842
842
  await cast;
843
843
 
@@ -46,11 +46,12 @@ describe('Cea608Memory', () => {
46
46
  it('adds and emits a series of special characters from the buffer', () => {
47
47
  const startTime = 1;
48
48
  const endTime = 2;
49
- const expectedText = '½¿èôÇ©ë»ö{ß│';
49
+ const expectedText = '½¿ èôÇ©ë»ö{ß│';
50
50
  const charGroups = [
51
51
  {
52
52
  set: CharSet.SPECIAL_NORTH_AMERICAN,
53
- chars: [0x32, 0x33, 0x3a, 0x3e], // ½, ¿, è, ô
53
+ // Note TS is not at either end to avoid side effect of trim()
54
+ chars: [0x32, 0x33, 0x39, 0x3a, 0x3e], // ½, ¿, TS, è, ô
54
55
  },
55
56
 
56
57
  {
@@ -776,6 +776,58 @@ describe('HlsParser live', () => {
776
776
  manifest.variants[1].video, [ref4]);
777
777
  });
778
778
 
779
+ describe('when ignoreManifestProgramDateTime is set', () => {
780
+ const config = shaka.util.PlayerConfiguration.createDefault().manifest;
781
+ config.hls.ignoreManifestProgramDateTime = true;
782
+
783
+ it('does not reset segment times when switching', async () => {
784
+ parser.configure(config);
785
+
786
+ const ref1 = makeReference(
787
+ 'test:/main.mp4', 0, 2, /* syncTime= */ null);
788
+ const ref2 = makeReference(
789
+ 'test:/main2.mp4', 2, 4, /* syncTime= */ null);
790
+
791
+ const secondVariant = [
792
+ '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",',
793
+ 'RESOLUTION=1200x940,FRAME-RATE=60\n',
794
+ 'video2',
795
+ ].join('');
796
+ const masterWithTwoVariants = master + secondVariant;
797
+ configureNetEngineForInitialManifest(masterWithTwoVariants,
798
+ mediaWithAdditionalSegment, mediaWithAdditionalSegment2);
799
+
800
+ const manifest = await parser.start('test:/master', playerInterface);
801
+ await manifest.variants[0].video.createSegmentIndex();
802
+ ManifestParser.verifySegmentIndex(
803
+ manifest.variants[0].video, [ref1, ref2]);
804
+ expect(manifest.variants[1].video.segmentIndex).toBeNull();
805
+
806
+ // In the initial playlist, we know the earliest start time is 0, at
807
+ // EXT-X-MEDIA-SEQUENCE of 0.
808
+ expect(
809
+ manifest.variants[0].video.segmentIndex.earliestReference()
810
+ .getStartTime())
811
+ .toBe(0);
812
+
813
+ // Update.
814
+ fakeNetEngine
815
+ .setResponseText('test:/video', mediaWithRemovedSegment)
816
+ .setResponseText('test:/video2', mediaWithRemovedSegment2);
817
+ await delayForUpdatePeriod();
818
+
819
+ // Switch. The new variant starts at EXT-X-MEDIA-SEQUENCE of 1.
820
+ await manifest.variants[0].video.closeSegmentIndex();
821
+ await manifest.variants[1].video.createSegmentIndex();
822
+
823
+ // The earliest start time of the new segmentIndex should therefore be
824
+ // 2.
825
+ expect(manifest.variants[0].video.segmentIndex).toBeNull();
826
+ const segIdx = manifest.variants[1].video.segmentIndex;
827
+ expect(segIdx.earliestReference().getStartTime()).toBe(2);
828
+ });
829
+ });
830
+
779
831
  it('handles switching during update', async () => {
780
832
  const ref1 = makeReference(
781
833
  'test:/main.mp4', 0, 2, /* syncTime= */ null);
@@ -28,7 +28,7 @@ describe('MediaSourceEngine', () => {
28
28
  jasmine.objectContaining({
29
29
  startTime: Util.closeTo(0.067, 0.001),
30
30
  endTime: Util.closeTo(1, 0.001),
31
- payload: 'eng:⠀00:00:00:00',
31
+ payload: 'eng: 00:00:00:00',
32
32
  textAlign: Cue.textAlign.CENTER,
33
33
  }),
34
34
  ],
@@ -3519,11 +3519,14 @@ describe('Player', () => {
3519
3519
  });
3520
3520
 
3521
3521
  describe('getPlayheadTimeAsDate()', () => {
3522
+ /** @type {?shaka.media.PresentationTimeline} */
3523
+ let timeline;
3522
3524
  beforeEach(async () => {
3523
- const timeline = new shaka.media.PresentationTimeline(300, 0);
3525
+ timeline = new shaka.media.PresentationTimeline(300, 0);
3524
3526
  timeline.setStatic(false);
3525
3527
 
3526
3528
  manifest = shaka.test.ManifestGenerator.generate((manifest) => {
3529
+ goog.asserts.assert(timeline, 'timeline must be non-null');
3527
3530
  manifest.presentationTimeline = timeline;
3528
3531
  manifest.addVariant(0, (variant) => {
3529
3532
  variant.addVideo(1);
@@ -3541,6 +3544,15 @@ describe('Player', () => {
3541
3544
  // (300 (presentation start time) + 20 (playhead time)) * 1000 (ms/sec)
3542
3545
  expect(liveTimeUtc).toEqual(new Date(320 * 1000));
3543
3546
  });
3547
+
3548
+ it('uses program date time', () => {
3549
+ timeline.setInitialProgramDateTime(100);
3550
+ playhead.getTime.and.returnValue(20);
3551
+
3552
+ const liveTimeUtc = player.getPlayheadTimeAsDate();
3553
+ // (100 (program date time) + 20 (playhead time)) * 1000 (ms/sec)
3554
+ expect(liveTimeUtc).toEqual(new Date(120 * 1000));
3555
+ });
3544
3556
  });
3545
3557
 
3546
3558
  it('rejects empty manifests', async () => {
@@ -1,57 +0,0 @@
1
- /*! @license
2
- * Shaka Player
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- goog.provide('shaka.polyfill.StorageEstimate');
8
-
9
- goog.require('shaka.polyfill');
10
-
11
-
12
- /**
13
- * @summary A polyfill to provide navigator.storage.estimate in old
14
- * webkit browsers.
15
- * See: https://developers.google.com/web/updates/2017/08/estimating-available-storage-space#the-present
16
- * @export
17
- */
18
- shaka.polyfill.StorageEstimate = class {
19
- /**
20
- * Install the polyfill if needed.
21
- * @export
22
- */
23
- static install() {
24
- if (navigator.storage && navigator.storage.estimate) {
25
- // No need.
26
- return;
27
- }
28
-
29
- if (navigator.webkitTemporaryStorage &&
30
- navigator.webkitTemporaryStorage.queryUsageAndQuota) {
31
- if (!('storage' in navigator)) {
32
- navigator.storage = /** @type {!StorageManager} */ ({});
33
- }
34
- navigator.storage.estimate =
35
- shaka.polyfill.StorageEstimate.storageEstimate_;
36
- }
37
- }
38
-
39
- /**
40
- * @this {StorageManager}
41
- * @return {!Promise}
42
- * @private
43
- */
44
- static storageEstimate_() {
45
- return new Promise((resolve, reject) => {
46
- navigator.webkitTemporaryStorage.queryUsageAndQuota(
47
- (usage, quota) => {
48
- resolve({usage: usage, quota: quota});
49
- },
50
- reject,
51
- );
52
- });
53
- }
54
- };
55
-
56
-
57
- shaka.polyfill.register(shaka.polyfill.StorageEstimate.install);