shaka-player 4.3.8 → 4.3.9

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 (63) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +11 -0
  2. package/.github/workflows/build-and-test.yaml +8 -0
  3. package/.github/workflows/selenium-lab-tests.yaml +3 -2
  4. package/.github/workflows/update-issues.yaml +1 -0
  5. package/CHANGELOG.md +19 -0
  6. package/dist/deps.js +1 -1
  7. package/dist/locales.js +30 -30
  8. package/dist/shaka-player.compiled.d.ts +2 -2
  9. package/dist/shaka-player.compiled.debug.d.ts +2 -2
  10. package/dist/shaka-player.compiled.debug.js +50 -48
  11. package/dist/shaka-player.compiled.debug.map +3 -3
  12. package/dist/shaka-player.compiled.js +385 -385
  13. package/dist/shaka-player.compiled.map +2 -2
  14. package/dist/shaka-player.ui.d.ts +2 -2
  15. package/dist/shaka-player.ui.debug.d.ts +2 -2
  16. package/dist/shaka-player.ui.debug.js +54 -52
  17. package/dist/shaka-player.ui.debug.map +3 -3
  18. package/dist/shaka-player.ui.js +400 -399
  19. package/dist/shaka-player.ui.map +3 -3
  20. package/docs/tutorials/selenium-grid-config.md +60 -8
  21. package/externs/shaka/manifest.js +5 -1
  22. package/externs/shaka/offline.js +5 -1
  23. package/karma.conf.js +50 -7
  24. package/lib/abr/simple_abr_manager.js +5 -5
  25. package/lib/dash/dash_parser.js +1 -0
  26. package/lib/hls/hls_parser.js +1 -0
  27. package/lib/media/media_source_engine.js +6 -5
  28. package/lib/media/streaming_engine.js +2 -2
  29. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  30. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  31. package/lib/offline/manifest_converter.js +1 -0
  32. package/lib/offline/storage.js +1 -0
  33. package/lib/player.js +9 -5
  34. package/lib/polyfill/orientation.js +34 -7
  35. package/lib/text/vtt_text_parser.js +1 -0
  36. package/lib/util/manifest_parser_utils.js +4 -2
  37. package/lib/util/periods.js +2 -0
  38. package/lib/util/platform.js +14 -0
  39. package/lib/util/stream_utils.js +69 -26
  40. package/package.json +2 -2
  41. package/test/cast/cast_utils_unit.js +4 -1
  42. package/test/hls/hls_parser_unit.js +72 -0
  43. package/test/media/adaptation_set_unit.js +1 -0
  44. package/test/media/drm_engine_integration.js +2 -2
  45. package/test/media/media_source_engine_unit.js +1 -1
  46. package/test/media/segment_index_unit.js +3 -3
  47. package/test/media/streaming_engine_integration.js +14 -11
  48. package/test/offline/manifest_convert_unit.js +5 -0
  49. package/test/offline/offline_integration.js +6 -3
  50. package/test/player_external.js +19 -16
  51. package/test/player_integration.js +35 -48
  52. package/test/player_src_equals_integration.js +9 -5
  53. package/test/test/util/fake_demo_main.js +7 -3
  54. package/test/test/util/layout_tests.js +2 -2
  55. package/test/test/util/manifest_generator.js +2 -0
  56. package/test/test/util/offline_utils.js +1 -0
  57. package/test/test/util/streaming_engine_util.js +3 -0
  58. package/test/test/util/ui_utils.js +9 -2
  59. package/test/test/util/util.js +6 -27
  60. package/test/test/util/waiter.js +73 -3
  61. package/test/ui/ad_ui_unit.js +9 -9
  62. package/test/util/stream_utils_unit.js +46 -0
  63. package/ui/range_element.js +5 -0
@@ -34,6 +34,9 @@ describe('Player', () => {
34
34
  await shaka.test.TestScheme.createManifests(compiledShaka, '_compiled');
35
35
  player = new compiledShaka.Player(video);
36
36
 
37
+ // Disable stall detection, which can interfere with playback tests.
38
+ player.configure('streaming.stallEnabled', false);
39
+
37
40
  // Grab event manager from the uncompiled library:
38
41
  eventManager = new shaka.util.EventManager();
39
42
  waiter = new shaka.test.Waiter(eventManager);
@@ -81,11 +84,15 @@ describe('Player', () => {
81
84
  // Unlike the other tests in this file, this uses an uncompiled build of
82
85
  // Shaka, so that we don't need to expose shaka.util.Timer.activeTimers.
83
86
  player = new shaka.Player(video);
87
+
88
+ // Disable stall detection, which can interfere with playback tests.
89
+ player.configure('streaming.stallEnabled', false);
90
+
84
91
  waiter.setPlayer(player);
85
92
 
86
93
  // Play the video for a little while.
87
94
  await player.load('test:sintel');
88
- video.play();
95
+ await video.play();
89
96
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
90
97
 
91
98
  // Destroy the player.
@@ -127,7 +134,7 @@ describe('Player', () => {
127
134
  }
128
135
  });
129
136
  await player.load('test:sintel_compiled');
130
- video.play();
137
+ await video.play();
131
138
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 25, 30);
132
139
  expect(results).toEqual(expected);
133
140
  });
@@ -139,7 +146,7 @@ describe('Player', () => {
139
146
  // This is tested more in player_unit.js. This is here to test the public
140
147
  // API and to check for renaming.
141
148
  await player.load('test:sintel_compiled');
142
- video.play();
149
+ await video.play();
143
150
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
144
151
 
145
152
  const stats = player.getStats();
@@ -210,7 +217,7 @@ describe('Player', () => {
210
217
 
211
218
  it('does not cause cues to be null', async () => {
212
219
  await player.load('test:sintel_compiled');
213
- video.play();
220
+ await video.play();
214
221
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
215
222
 
216
223
  // This TextTrack was created as part of load() when we set up the
@@ -249,7 +256,7 @@ describe('Player', () => {
249
256
  await player.load('test:sintel_realistic_compiled');
250
257
 
251
258
  // Play until a time at which the external cues would be on screen.
252
- video.play();
259
+ await video.play();
253
260
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 4, 20);
254
261
 
255
262
  expect(player.isTextTrackVisible()).toBe(true);
@@ -281,7 +288,7 @@ describe('Player', () => {
281
288
  await waiter.waitForEvent(player, 'texttrackvisibility');
282
289
 
283
290
  // Play until a time at which the external cues would be on screen.
284
- video.play();
291
+ await video.play();
285
292
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 4, 20);
286
293
 
287
294
  expect(player.isTextTrackVisible()).toBe(true);
@@ -352,7 +359,7 @@ describe('Player', () => {
352
359
  player.setTextTrackVisibility(true);
353
360
  player.selectTextLanguage('fr');
354
361
  video.currentTime = 5;
355
- video.play();
362
+ await video.play();
356
363
  await waiter.waitForMovementOrFailOnTimeout(video, 10);
357
364
 
358
365
  expect(video.textTracks[0].activeCues.length).toBe(1);
@@ -544,7 +551,7 @@ describe('Player', () => {
544
551
 
545
552
  it('at higher playback rates', async () => {
546
553
  await player.load('test:sintel_compiled');
547
- video.play();
554
+ await video.play();
548
555
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
549
556
 
550
557
  // Enabling trick play should change our playback rate to the same rate.
@@ -560,14 +567,12 @@ describe('Player', () => {
560
567
  });
561
568
 
562
569
  it('in sequence mode', async () => {
563
- const testSchemeMimeType = 'application/x-test-manifest';
564
- player = new compiledShaka.Player(video);
565
- await player.load('test:sintel_sequence_compiled', 0, testSchemeMimeType);
570
+ await player.load('test:sintel_sequence_compiled');
566
571
  expect(player.getManifest().sequenceMode).toBe(true);
567
572
 
568
573
  // Ensure the video plays.
569
- video.play();
570
- await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 5, 10);
574
+ await video.play();
575
+ await waiter.timeoutAfter(20).waitUntilPlayheadReaches(video, 5);
571
576
 
572
577
  // Seek the video, and see if it can continue playing from that point.
573
578
  video.currentTime = 20;
@@ -586,9 +591,8 @@ describe('Player', () => {
586
591
  // even if one is not specified in load().
587
592
  it('immediately after construction with MIME type', async () => {
588
593
  const testSchemeMimeType = 'application/x-test-manifest';
589
- player = new compiledShaka.Player(video);
590
594
  await player.load('test:sintel_compiled', 0, testSchemeMimeType);
591
- video.play();
595
+ await video.play();
592
596
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
593
597
  });
594
598
 
@@ -627,7 +631,7 @@ describe('Player', () => {
627
631
  // Regression test for https://github.com/shaka-project/shaka-player/issues/1187
628
632
  it('does not throw on destroy', async () => {
629
633
  await player.load('test:sintel_compiled');
630
- video.play();
634
+ await video.play();
631
635
  await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
632
636
  await player.unload();
633
637
  // Before we fixed #1187, the call to destroy() on textDisplayer was
@@ -873,7 +877,7 @@ describe('Player', () => {
873
877
  player.configure('streaming.observeQualityChanges', true);
874
878
 
875
879
  await player.load('test:sintel_compiled');
876
- video.play();
880
+ await video.play();
877
881
 
878
882
  // Wait for the video to start playback. If it takes longer than 10
879
883
  // seconds, fail the test.
@@ -887,7 +891,7 @@ describe('Player', () => {
887
891
  player.configure('streaming.observeQualityChanges', false);
888
892
 
889
893
  await player.load('test:sintel_compiled');
890
- video.play();
894
+ await video.play();
891
895
 
892
896
  // Wait for the video to start playback. If it takes longer than 10
893
897
  // seconds, fail the test.
@@ -916,7 +920,7 @@ describe('Player', () => {
916
920
 
917
921
  // Play the stream.
918
922
  await player.load('/base/test/test/assets/3675/dash_0.mpd');
919
- video.play();
923
+ await video.play();
920
924
 
921
925
  // Wait for the stream to be over.
922
926
  eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
@@ -1004,22 +1008,22 @@ describe('Player', () => {
1004
1008
 
1005
1009
  await player.load('test:sintel_long_compiled');
1006
1010
  video.pause();
1007
- await waitUntilBuffered(10);
1011
+ await waiter.waitUntilBuffered(video, 10);
1008
1012
 
1009
1013
  player.configure('streaming.bufferingGoal', 30);
1010
- await waitUntilBuffered(30);
1014
+ await waiter.waitUntilBuffered(video, 30);
1011
1015
 
1012
1016
  player.configure('streaming.bufferingGoal', 60);
1013
- await waitUntilBuffered(60);
1014
- await Util.delay(0.2);
1017
+ await waiter.waitUntilBuffered(video, 60);
1018
+ await Util.delay(1);
1015
1019
  expect(getBufferedAhead()).toBeLessThan(70); // 60 + segment_size
1016
1020
 
1017
1021
  // We don't remove buffered content ahead of the playhead, so seek to
1018
1022
  // clear the buffer.
1019
1023
  player.configure('streaming.bufferingGoal', 10);
1020
1024
  video.currentTime = 120;
1021
- await waitUntilBuffered(10);
1022
- await Util.delay(0.2);
1025
+ await waiter.waitUntilBuffered(video, 10);
1026
+ await Util.delay(1);
1023
1027
  expect(getBufferedAhead()).toBeLessThan(20); // 10 + segment_size
1024
1028
  });
1025
1029
 
@@ -1029,20 +1033,22 @@ describe('Player', () => {
1029
1033
 
1030
1034
  await player.load('test:sintel_long_compiled');
1031
1035
  video.pause();
1032
- await waitUntilBuffered(30);
1036
+ await waiter.waitUntilBuffered(video, 30);
1033
1037
  video.currentTime = 20;
1034
- await waitUntilBuffered(30);
1038
+ await waiter.waitUntilBuffered(video, 30);
1035
1039
 
1036
1040
  expect(getBufferedBehind()).toBe(20); // Buffered to start still.
1037
1041
  video.currentTime = 50;
1038
- await waitUntilBuffered(30);
1042
+ await waiter.waitUntilBuffered(video, 30);
1043
+ await Util.delay(1);
1039
1044
  expect(getBufferedBehind()).toBeLessThan(40); // 30 + segment_size
1040
1045
 
1041
1046
  player.configure('streaming.bufferBehind', 10);
1042
1047
  // We only evict content when we append a segment, so increase the
1043
1048
  // buffering goal so we append another segment.
1044
1049
  player.configure('streaming.bufferingGoal', 40);
1045
- await waitUntilBuffered(40);
1050
+ await waiter.waitUntilBuffered(video, 40);
1051
+ await Util.delay(1);
1046
1052
  expect(getBufferedBehind()).toBeLessThanOrEqual(10);
1047
1053
  });
1048
1054
 
@@ -1061,25 +1067,6 @@ describe('Player', () => {
1061
1067
  }
1062
1068
  return video.currentTime - start;
1063
1069
  }
1064
-
1065
- async function waitUntilBuffered(amount) {
1066
- for (let i = 0; i < 50; i++) {
1067
- // We buffer from an internal segment, so this shouldn't take long to
1068
- // buffer.
1069
- await Util.delay(0.1); // eslint-disable-line no-await-in-loop
1070
- if (getBufferedAhead() >= amount) {
1071
- return;
1072
- }
1073
- }
1074
-
1075
- const ranges =
1076
- shaka.media.TimeRangesUtils.getBufferedInfo(video.buffered);
1077
- const currentTime = video.currentTime;
1078
- const target = currentTime + amount;
1079
-
1080
- throw new Error('Timeout waiting to buffer! ' +
1081
- JSON.stringify({ranges, currentTime, target}));
1082
- }
1083
1070
  }); // describe('buffering')
1084
1071
 
1085
1072
  describe('configuration', () => {
@@ -27,6 +27,10 @@ describe('Player Src Equals', () => {
27
27
  beforeEach(() => {
28
28
  player = new shaka.Player();
29
29
  player.addEventListener('error', fail);
30
+
31
+ // Disable stall detection, which can interfere with playback tests.
32
+ player.configure('streaming.stallEnabled', false);
33
+
30
34
  eventManager = new shaka.util.EventManager();
31
35
  waiter = new shaka.test.Waiter(eventManager);
32
36
  });
@@ -101,7 +105,7 @@ describe('Player Src Equals', () => {
101
105
  expect(video.duration).not.toBeCloseTo(0);
102
106
 
103
107
  // Start playback and wait for the playhead to move.
104
- video.play();
108
+ await video.play();
105
109
  await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
106
110
 
107
111
  // Make sure the playhead is roughly where we expect it to be before
@@ -138,7 +142,7 @@ describe('Player Src Equals', () => {
138
142
  await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
139
143
 
140
144
  // For playback to begin so that we have some content buffered.
141
- video.play();
145
+ await video.play();
142
146
  await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
143
147
 
144
148
  const buffered = player.getBufferedInfo();
@@ -162,7 +166,7 @@ describe('Player Src Equals', () => {
162
166
  await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
163
167
 
164
168
  // Let playback run for a little.
165
- video.play();
169
+ await video.play();
166
170
  await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
167
171
 
168
172
  let videoRateChange = false;
@@ -279,7 +283,7 @@ describe('Player Src Equals', () => {
279
283
  expect(video.currentTime).toBeCloseTo(0);
280
284
 
281
285
  // Start playback and wait. We should see the playhead move.
282
- video.play();
286
+ await video.play();
283
287
  await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
284
288
  await shaka.test.Util.delay(1.5);
285
289
 
@@ -296,7 +300,7 @@ describe('Player Src Equals', () => {
296
300
 
297
301
  // Wait some time for playback to start so that we will have a load latency
298
302
  // value.
299
- video.play();
303
+ await video.play();
300
304
  await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
301
305
 
302
306
  // Get the stats and check that some stats have been filled in.
@@ -12,9 +12,13 @@
12
12
  */
13
13
  shaka.test.FakeDemoMain = class {
14
14
  constructor() {
15
- this.video = /** @type {!HTMLVideoElement} */ (
16
- document.createElement('video'));
17
- this.player = new shaka.Player(this.video);
15
+ // Using the player's constructor argument to attach a video element seems
16
+ // to cause flaky timeouts on teardown. If a video element is needed in
17
+ // the future, please explicitly call attach(video) and await the result
18
+ // during the test setup.
19
+ /** @type {!shaka.Player} */
20
+ this.player = new shaka.Player();
21
+
18
22
  this.config_ = this.player.getConfiguration();
19
23
  this.selectedAsset = null;
20
24
 
@@ -395,7 +395,7 @@ shaka.test.NativeTextLayoutTests = class extends shaka.test.TextLayoutTests {
395
395
  // The this.video must be played a little now, after the cues were appended,
396
396
  // but before the screenshot.
397
397
  this.video.playbackRate = 1;
398
- this.video.play();
398
+ await this.video.play();
399
399
  await this.waiter.failOnTimeout(false).timeoutAfter(5)
400
400
  .waitForMovement(this.video);
401
401
  this.video.pause();
@@ -404,7 +404,7 @@ shaka.test.NativeTextLayoutTests = class extends shaka.test.TextLayoutTests {
404
404
  this.video.currentTime = time;
405
405
  // Get into a playing state, but without movement.
406
406
  this.video.playbackRate = 0;
407
- this.video.play();
407
+ await this.video.play();
408
408
 
409
409
  // Add a short delay to ensure that the system has caught up and that
410
410
  // native text displayers have been updated by the browser.
@@ -541,6 +541,8 @@ shaka.test.ManifestGenerator.Stream = class {
541
541
  this.hdr = undefined;
542
542
  /** @type {(string|undefined)} */
543
543
  this.tilesLayout = undefined;
544
+ /** @type {boolean} */
545
+ this.external = false;
544
546
  }
545
547
 
546
548
  /** @type {shaka.extern.Stream} */
@@ -56,6 +56,7 @@ shaka.test.OfflineUtils = class {
56
56
  audioSamplingRate: null,
57
57
  spatialAudio: false,
58
58
  closedCaptions: null,
59
+ external: false,
59
60
  };
60
61
  }
61
62
 
@@ -410,6 +410,7 @@ shaka.test.StreamingEngineUtil = class {
410
410
  roles: [],
411
411
  forced: false,
412
412
  spatialAudio: false,
413
+ external: false,
413
414
  };
414
415
  }
415
416
 
@@ -447,6 +448,7 @@ shaka.test.StreamingEngineUtil = class {
447
448
  roles: [],
448
449
  forced: false,
449
450
  spatialAudio: false,
451
+ external: false,
450
452
  };
451
453
  }
452
454
 
@@ -482,6 +484,7 @@ shaka.test.StreamingEngineUtil = class {
482
484
  roles: [],
483
485
  forced: false,
484
486
  spatialAudio: false,
487
+ external: false,
485
488
  };
486
489
  }
487
490
  };
@@ -115,11 +115,18 @@ shaka.test.UiUtils = class {
115
115
  // Destroying the UI destroys the controls and player inside.
116
116
  destroys.push(ui.destroy());
117
117
  }
118
- await Promise.all(destroys);
118
+
119
+ const allDestroyed = Promise.all(destroys);
120
+ // 10 seconds should be more than enough to tear down the UI.
121
+ // Adding this silent timeout fixes several tests that inconsistently hang
122
+ // during teardown and cause failures in afterEach() clauses.
123
+ await Promise.race([allDestroyed, shaka.test.Util.delay(10)]);
119
124
 
120
125
  // Now remove all the containers from the DOM.
121
126
  for (const container of containers) {
122
- container.parentElement.removeChild(container);
127
+ if (container.parentElement) {
128
+ container.parentElement.removeChild(container);
129
+ }
123
130
  }
124
131
  }
125
132
 
@@ -255,33 +255,12 @@ shaka.test.Util = class {
255
255
  * @param {string} uri
256
256
  * @return {!Promise.<!ArrayBuffer>}
257
257
  */
258
- static fetch(uri) {
259
- return new Promise(((resolve, reject) => {
260
- const xhr = new XMLHttpRequest();
261
- xhr.open('GET', uri, /* asynchronous= */ true);
262
- xhr.responseType = 'arraybuffer';
263
-
264
- xhr.onload = (event) => {
265
- if (xhr.status >= 200 &&
266
- xhr.status <= 299 &&
267
- !!xhr.response) {
268
- resolve(/** @type {!ArrayBuffer} */(xhr.response));
269
- } else {
270
- let message = '';
271
- if (xhr.response) {
272
- message = ': ' + shaka.util.StringUtils.fromUTF8(
273
- /** @type {!ArrayBuffer} */(xhr.response));
274
- }
275
- reject(xhr.status + message);
276
- }
277
- };
278
-
279
- xhr.onerror = (event) => {
280
- reject('shaka.test.Util.fetch failed: ' + uri);
281
- };
282
-
283
- xhr.send(/* body= */ null);
284
- }));
258
+ static async fetch(uri) {
259
+ const response = await fetch(uri);
260
+ if (!response.ok) {
261
+ throw new Error('shaka.test.Util.fetch failed: ' + uri);
262
+ }
263
+ return response.arrayBuffer();
285
264
  }
286
265
 
287
266
  /**
@@ -109,6 +109,8 @@ shaka.test.Waiter = class {
109
109
  * @return {!Promise}
110
110
  */
111
111
  waitUntilPlayheadReaches(mediaElement, timeGoal) {
112
+ this.macPlaybackWorkaround_(mediaElement);
113
+
112
114
  // The name of what we're waiting for
113
115
  const goalName = 'movement from ' + mediaElement.currentTime +
114
116
  ' to ' + timeGoal;
@@ -153,6 +155,8 @@ shaka.test.Waiter = class {
153
155
  * @return {!Promise}
154
156
  */
155
157
  waitForEnd(mediaElement) {
158
+ this.macPlaybackWorkaround_(mediaElement);
159
+
156
160
  // The name of what we're waiting for.
157
161
  const goalName = 'end of media';
158
162
 
@@ -207,13 +211,64 @@ shaka.test.Waiter = class {
207
211
  * Wait for the video to end or for |timeout| seconds to pass, whichever
208
212
  * occurs first. The Promise is resolved when either of these happens.
209
213
  *
210
- * @param {!HTMLMediaElement} target
214
+ * @param {!HTMLMediaElement} mediaElement
211
215
  * @param {number} timeout in seconds, after which the Promise succeeds
212
216
  * @return {!Promise}
213
217
  */
214
- waitForEndOrTimeout(target, timeout) {
218
+ waitForEndOrTimeout(mediaElement, timeout) {
215
219
  this.failOnTimeout(false).timeoutAfter(timeout);
216
- return this.waitForEnd(target);
220
+ return this.waitForEnd(mediaElement);
221
+ }
222
+
223
+ /**
224
+ * Wait until a certain amount of content is buffered.
225
+ *
226
+ * @param {!HTMLMediaElement} mediaElement
227
+ * @param {number} bufferingGoal in seconds, after which the Promise succeeds
228
+ * @return {!Promise}
229
+ */
230
+ waitUntilBuffered(mediaElement, bufferingGoal) {
231
+ // The name of what we're waiting for.
232
+ const goalName = `${bufferingGoal} seconds buffered`;
233
+
234
+ // The conditions for success.
235
+ const bufferedEnough = () => {
236
+ const end = shaka.media.TimeRangesUtils.bufferEnd(mediaElement.buffered);
237
+ if (end == null) {
238
+ return false;
239
+ }
240
+ return end - mediaElement.currentTime >= bufferingGoal;
241
+ };
242
+
243
+ if (bufferedEnough()) {
244
+ return Promise.resolve();
245
+ }
246
+
247
+ // Cleanup on timeout.
248
+ let timer = null;
249
+ const cleanup = () => {
250
+ if (timer) {
251
+ timer.stop();
252
+ }
253
+ this.eventManager_.unlisten(mediaElement, 'timeupdate');
254
+ this.eventManager_.unlisten(mediaElement, 'ended');
255
+ };
256
+
257
+ const p = new Promise((resolve) => {
258
+ const check = () => {
259
+ if (bufferedEnough()) {
260
+ cleanup();
261
+ resolve();
262
+ }
263
+ };
264
+
265
+ timer = new shaka.util.Timer(check);
266
+ timer.tickEvery(/* seconds= */ 1);
267
+ this.eventManager_.listen(mediaElement, 'timeupdate', check);
268
+ this.eventManager_.listen(mediaElement, 'ended', check);
269
+ });
270
+
271
+ return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement);
217
272
  }
218
273
 
219
274
  /**
@@ -333,4 +388,19 @@ shaka.test.Waiter = class {
333
388
  `buffered: ${JSON.stringify(buffered)}\n`;
334
389
  shaka.log.error(error.message);
335
390
  }
391
+
392
+ /**
393
+ * @param {!HTMLMediaElement} mediaElement
394
+ * @private
395
+ */
396
+ macPlaybackWorkaround_(mediaElement) {
397
+ if (shaka.util.Platform.isMac()) {
398
+ // Work around bizarre playback slowdowns that only seem to occur with
399
+ // WebDriver and only on Mac. Increasing the playback rate allows tests
400
+ // to complete without timing out.
401
+ if (mediaElement.playbackRate == 1) {
402
+ mediaElement.playbackRate = 2;
403
+ }
404
+ }
405
+ }
336
406
  };
@@ -25,15 +25,6 @@ describe('Ad UI', () => {
25
25
  shaka.Player.setAdManagerFactory(() => new shaka.test.FakeAdManager());
26
26
  });
27
27
 
28
- afterEach(async () => {
29
- await UiUtils.cleanupUI();
30
- });
31
-
32
- afterAll(() => {
33
- document.head.removeChild(cssLink);
34
- shaka.Player.setAdManagerFactory(() => new shaka.ads.AdManager());
35
- });
36
-
37
28
  beforeEach(() => {
38
29
  container =
39
30
  /** @type {!HTMLElement} */ (document.createElement('div'));
@@ -45,6 +36,15 @@ describe('Ad UI', () => {
45
36
  adManager = video['ui'].getControls().getPlayer().getAdManager();
46
37
  });
47
38
 
39
+ afterEach(async () => {
40
+ await UiUtils.cleanupUI();
41
+ });
42
+
43
+ afterAll(() => {
44
+ document.head.removeChild(cssLink);
45
+ shaka.Player.setAdManagerFactory(() => new shaka.ads.AdManager());
46
+ });
47
+
48
48
  it('is invisible if no ad is playing', () => {
49
49
  const adControlsContainer =
50
50
  UiUtils.getElementByClassName(container, 'shaka-ad-controls');
@@ -718,6 +718,52 @@ describe('StreamUtils', () => {
718
718
  expect(manifest.variants.length).toBe(1);
719
719
  });
720
720
 
721
+ it('supports fLaC codec', async () => {
722
+ if (!MediaSource.isTypeSupported('audio/mp4; codecs="flac"')) {
723
+ pending('Codec fLaC is not supported by the platform.');
724
+ }
725
+ manifest = shaka.test.ManifestGenerator.generate((manifest) => {
726
+ manifest.addVariant(0, (variant) => {
727
+ variant.addAudio(1, (stream) => {
728
+ stream.mime('audio/mp4', 'fLaC');
729
+ });
730
+ });
731
+ manifest.addVariant(2, (variant) => {
732
+ variant.addAudio(3, (stream) => {
733
+ stream.mime('audio/mp4', 'flac');
734
+ });
735
+ });
736
+ });
737
+
738
+ await shaka.util.StreamUtils.filterManifest(
739
+ fakeDrmEngine, /* currentVariant= */ null, manifest);
740
+
741
+ expect(manifest.variants.length).toBe(2);
742
+ });
743
+
744
+ it('supports Opus codec', async () => {
745
+ if (!MediaSource.isTypeSupported('audio/mp4; codecs="opus"')) {
746
+ pending('Codec Opus is not supported by the platform.');
747
+ }
748
+ manifest = shaka.test.ManifestGenerator.generate((manifest) => {
749
+ manifest.addVariant(0, (variant) => {
750
+ variant.addAudio(1, (stream) => {
751
+ stream.mime('audio/mp4', 'Opus');
752
+ });
753
+ });
754
+ manifest.addVariant(2, (variant) => {
755
+ variant.addAudio(3, (stream) => {
756
+ stream.mime('audio/mp4', 'opus');
757
+ });
758
+ });
759
+ });
760
+
761
+ await shaka.util.StreamUtils.filterManifest(
762
+ fakeDrmEngine, /* currentVariant= */ null, manifest);
763
+
764
+ expect(manifest.variants.length).toBe(2);
765
+ });
766
+
721
767
  it('supports legacy AVC1 codec', async () => {
722
768
  if (!MediaSource.isTypeSupported('video/mp4; codecs="avc1.42001e"')) {
723
769
  pending('Codec avc1.42001e is not supported by the platform.');
@@ -110,6 +110,11 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
110
110
  this.onChangeEnd();
111
111
  }
112
112
  });
113
+
114
+ this.eventManager.listen(this.bar, 'contextmenu', (e) => {
115
+ e.preventDefault();
116
+ e.stopPropagation();
117
+ });
113
118
  }
114
119
 
115
120
  /** @override */