shaka-player 4.2.3 → 4.3.0

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 (147) hide show
  1. package/.github/workflows/sync-labels.yaml +1 -1
  2. package/.github/workflows/update-issues.yaml +1 -1
  3. package/AUTHORS +5 -0
  4. package/CHANGELOG.md +85 -39
  5. package/CONTRIBUTORS +5 -0
  6. package/README.md +14 -0
  7. package/demo/common/assets.js +58 -0
  8. package/demo/common/message_ids.js +22 -1
  9. package/demo/config.js +89 -5
  10. package/demo/demo.less +7 -1
  11. package/demo/index.html +8 -1
  12. package/demo/locales/en.json +21 -1
  13. package/demo/locales/source.json +82 -2
  14. package/demo/main.js +57 -1
  15. package/demo/search.js +2 -0
  16. package/demo/visualizer.js +337 -0
  17. package/demo/visualizer_button.js +103 -0
  18. package/dist/controls.css +1 -1
  19. package/dist/controls.css.map +1 -1
  20. package/dist/demo.compiled.debug.js +83 -58
  21. package/dist/demo.compiled.debug.map +4 -4
  22. package/dist/demo.compiled.js +83 -58
  23. package/dist/demo.compiled.map +4 -4
  24. package/dist/demo.css +1 -1
  25. package/dist/demo.css.map +1 -1
  26. package/dist/deps.js +18 -11
  27. package/dist/locales.js +32 -32
  28. package/dist/receiver.compiled.debug.js +23 -19
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +23 -19
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +178 -27
  33. package/dist/shaka-player.compiled.debug.d.ts +178 -27
  34. package/dist/shaka-player.compiled.debug.externs.js +163 -32
  35. package/dist/shaka-player.compiled.debug.js +488 -422
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +163 -32
  38. package/dist/shaka-player.compiled.js +895 -857
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +178 -27
  41. package/dist/shaka-player.ui.debug.d.ts +178 -27
  42. package/dist/shaka-player.ui.debug.externs.js +163 -32
  43. package/dist/shaka-player.ui.debug.js +519 -450
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +163 -32
  46. package/dist/shaka-player.ui.js +1053 -1015
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/design/lcevc-architecture.png +0 -0
  49. package/docs/design/lcevc-demo.png +0 -0
  50. package/docs/design/lcevc-integration.md +72 -0
  51. package/docs/tutorials/fairplay.md +7 -0
  52. package/docs/tutorials/faq.md +1 -1
  53. package/docs/tutorials/ui.md +7 -7
  54. package/docs/tutorials/upgrade.md +6 -0
  55. package/externs/ima.js +13 -1
  56. package/externs/lcevc.js +81 -0
  57. package/externs/mux.js +4 -42
  58. package/externs/shaka/ads.js +11 -1
  59. package/externs/shaka/manifest_parser.js +5 -1
  60. package/externs/shaka/player.js +178 -15
  61. package/lib/abr/simple_abr_manager.js +7 -0
  62. package/lib/ads/ad_manager.js +0 -8
  63. package/lib/ads/client_side_ad.js +15 -0
  64. package/lib/ads/client_side_ad_manager.js +21 -8
  65. package/lib/ads/server_side_ad.js +15 -0
  66. package/lib/ads/server_side_ad_manager.js +3 -3
  67. package/lib/cast/cast_proxy.js +2 -1
  68. package/lib/cast/cast_receiver.js +19 -5
  69. package/lib/cast/cast_sender.js +2 -1
  70. package/lib/cast/cast_utils.js +18 -6
  71. package/lib/cea/dummy_cea_parser.js +28 -0
  72. package/lib/cea/i_cea_parser.js +14 -2
  73. package/lib/cea/mp4_cea_parser.js +87 -5
  74. package/lib/config/auto_show_text.js +33 -0
  75. package/lib/dash/dash_parser.js +1 -1
  76. package/lib/hls/hls_parser.js +799 -376
  77. package/lib/lcevc/lcevc_dil.js +152 -0
  78. package/lib/media/closed_caption_parser.js +9 -6
  79. package/lib/media/drm_engine.js +14 -6
  80. package/lib/media/media_source_engine.js +112 -26
  81. package/lib/media/playhead.js +23 -1
  82. package/lib/media/presentation_timeline.js +26 -11
  83. package/lib/media/segment_reference.js +49 -1
  84. package/lib/media/streaming_engine.js +145 -24
  85. package/lib/media/transmuxer.js +79 -18
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +477 -85
  88. package/lib/polyfill/patchedmediakeys_apple.js +55 -1
  89. package/lib/text/ui_text_displayer.js +5 -5
  90. package/lib/text/vtt_text_parser.js +35 -1
  91. package/lib/util/dom_utils.js +0 -1
  92. package/lib/util/error.js +17 -0
  93. package/lib/util/fake_event.js +2 -0
  94. package/lib/util/id3_utils.js +300 -0
  95. package/lib/util/manifest_parser_utils.js +4 -0
  96. package/lib/util/mime_utils.js +3 -6
  97. package/lib/util/mp4_box_parsers.js +15 -5
  98. package/lib/util/platform.js +9 -1
  99. package/lib/util/player_configuration.js +23 -1
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +4 -1
  103. package/roadmap.md +27 -7
  104. package/test/ads/ad_manager_unit.js +180 -29
  105. package/test/cast/cast_receiver_integration.js +97 -16
  106. package/test/cast/cast_receiver_unit.js +17 -12
  107. package/test/cast/cast_utils_unit.js +4 -1
  108. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  109. package/test/dash/dash_parser_live_unit.js +1 -0
  110. package/test/dash/dash_parser_manifest_unit.js +93 -1
  111. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  112. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  113. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  114. package/test/demo/demo_unit.js +2 -1
  115. package/test/hls/hls_live_unit.js +253 -19
  116. package/test/hls/hls_parser_unit.js +500 -12
  117. package/test/media/closed_caption_parser_unit.js +2 -1
  118. package/test/media/drm_engine_integration.js +3 -1
  119. package/test/media/media_source_engine_integration.js +102 -6
  120. package/test/media/media_source_engine_unit.js +10 -5
  121. package/test/media/playhead_unit.js +41 -0
  122. package/test/media/streaming_engine_integration.js +17 -1
  123. package/test/media/streaming_engine_unit.js +180 -24
  124. package/test/media/transmuxer_integration.js +21 -10
  125. package/test/player_integration.js +224 -82
  126. package/test/player_unit.js +92 -2
  127. package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
  128. package/test/test/assets/id3-metadata.aac +0 -0
  129. package/test/test/assets/id3-metadata.ts +0 -0
  130. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  131. package/test/test/assets/thumbnails.vtt +10 -0
  132. package/test/test/util/dash_parser_util.js +2 -0
  133. package/test/test/util/fake_ad.js +21 -0
  134. package/test/test/util/fake_media_source_engine.js +7 -0
  135. package/test/test/util/id3_generator.js +100 -0
  136. package/test/test/util/manifest_generator.js +3 -6
  137. package/test/test/util/simple_fakes.js +7 -0
  138. package/test/test/util/stream_generator.js +38 -0
  139. package/test/test/util/test_scheme.js +43 -14
  140. package/test/text/vtt_text_parser_unit.js +11 -0
  141. package/test/ui/text_displayer_layout_unit.js +14 -0
  142. package/test/util/id3_utils_unit.js +116 -0
  143. package/test/util/ts_parser_unit.js +77 -0
  144. package/ui/controls.js +4 -1
  145. package/ui/less/containers.less +7 -0
  146. package/ui/settings_menu.js +11 -2
  147. package/ui/ui.js +43 -4
@@ -10,7 +10,8 @@ describe('ClosedCaptionParser', () => {
10
10
  'base/test/test/assets/empty_caption_video_init.mp4');
11
11
  const videoSegment = await shaka.test.Util.fetch(
12
12
  'base/test/test/assets/empty_caption_video_segment.mp4');
13
- const parser = new shaka.media.ClosedCaptionParser();
13
+ const mimeType = 'video/mp4';
14
+ const parser = new shaka.media.ClosedCaptionParser(mimeType);
14
15
  parser.init(initSegment);
15
16
  parser.parseFrom(videoSegment);
16
17
  });
@@ -126,8 +126,10 @@ describe('DrmEngine', () => {
126
126
 
127
127
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
128
128
  video,
129
- new shaka.test.FakeClosedCaptionParser(),
130
129
  new shaka.test.FakeTextDisplayer());
130
+ const mediaSourceConfig =
131
+ shaka.util.PlayerConfiguration.createDefault().mediaSource;
132
+ mediaSourceEngine.configure(mediaSourceConfig);
131
133
 
132
134
  const expectedObject = new Map();
133
135
  expectedObject.set(ContentType.AUDIO, audioStream);
@@ -26,6 +26,9 @@ describe('MediaSourceEngine', () => {
26
26
  */
27
27
  let textDisplayer;
28
28
 
29
+ /** @type {!jasmine.Spy} */
30
+ let onMetadata;
31
+
29
32
  beforeAll(() => {
30
33
  video = shaka.test.UiUtils.createVideoElement();
31
34
  document.body.appendChild(video);
@@ -37,10 +40,14 @@ describe('MediaSourceEngine', () => {
37
40
 
38
41
  textDisplayer = new shaka.test.FakeTextDisplayer();
39
42
 
43
+ onMetadata = jasmine.createSpy('onMetadata');
44
+
40
45
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
41
46
  video,
42
- new shaka.media.ClosedCaptionParser(),
43
- textDisplayer);
47
+ textDisplayer,
48
+ shaka.test.Util.spyFunc(onMetadata));
49
+ const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
50
+ mediaSourceEngine.configure(config);
44
51
 
45
52
  mediaSource = /** @type {?} */(mediaSourceEngine)['mediaSource_'];
46
53
  expect(video.src).toBeTruthy();
@@ -70,6 +77,18 @@ describe('MediaSourceEngine', () => {
70
77
  type, segment, reference, /* hasClosedCaptions= */ false);
71
78
  }
72
79
 
80
+ function appendWithSeek(type, segmentNumber) {
81
+ const segment = generators[type]
82
+ .getSegment(segmentNumber, Date.now() / 1000);
83
+ const reference = dummyReference(type, segmentNumber);
84
+ return mediaSourceEngine.appendBuffer(
85
+ type,
86
+ segment,
87
+ reference,
88
+ /* hasClosedCaptions= */ false,
89
+ /* seeked= */ true);
90
+ }
91
+
73
92
  function appendInitWithClosedCaptions(type) {
74
93
  const segment = generators[type].getInitSegment(Date.now() / 1000);
75
94
  const reference = null;
@@ -371,15 +390,34 @@ describe('MediaSourceEngine', () => {
371
390
  const initObject = new Map();
372
391
  initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
373
392
  initObject.set(ContentType.TEXT, getFakeStream(metadata.text));
374
- // Call with forceTransmuxTS = true, so that it will transmux even on
393
+ // Call with forceTransmux = true, so that it will transmux even on
375
394
  // platforms with native TS support.
376
- await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ true);
395
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
377
396
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
378
397
  await append(ContentType.VIDEO, 0);
379
398
 
380
399
  expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
381
400
  });
382
401
 
402
+ it('extracts CEA-708 captions from previous segment from hls', async () => {
403
+ // Load TS file with CEA-708 captions.
404
+ metadata = shaka.test.TestScheme.DATA['cea-708_ts'];
405
+ generators = shaka.test.TestScheme.GENERATORS['cea-708_ts'];
406
+
407
+ const initObject = new Map();
408
+ initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
409
+ initObject.set(ContentType.TEXT, getFakeStream(metadata.text));
410
+ // Call with forceTransmux = true, so that it will transmux even on
411
+ // platforms with native TS support.
412
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
413
+ mediaSourceEngine.setSelectedClosedCaptionId('CC1');
414
+
415
+ await append(ContentType.VIDEO, 2);
416
+ await appendWithSeek(ContentType.VIDEO, 0);
417
+
418
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(6);
419
+ });
420
+
383
421
  it('buffers partial TS video segments in sequence mode', async () => {
384
422
  metadata = shaka.test.TestScheme.DATA['cea-708_ts'];
385
423
  generators = shaka.test.TestScheme.GENERATORS['cea-708_ts'];
@@ -389,7 +427,7 @@ describe('MediaSourceEngine', () => {
389
427
  initObject.set(videoType, getFakeStream(metadata.video));
390
428
 
391
429
  await mediaSourceEngine.init(
392
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
430
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
393
431
  await mediaSourceEngine.setDuration(presentationDuration);
394
432
  await mediaSourceEngine.setStreamProperties(
395
433
  videoType,
@@ -424,7 +462,7 @@ describe('MediaSourceEngine', () => {
424
462
  const initObject = new Map();
425
463
  initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
426
464
 
427
- await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ false);
465
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
428
466
  await mediaSourceEngine.setDuration(presentationDuration);
429
467
  await appendInitWithClosedCaptions(ContentType.VIDEO);
430
468
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
@@ -432,4 +470,62 @@ describe('MediaSourceEngine', () => {
432
470
 
433
471
  expect(textDisplayer.appendSpy).toHaveBeenCalled();
434
472
  });
473
+
474
+ it('extracts ID3 metadata from TS', async () => {
475
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_ts'];
476
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_ts'];
477
+
478
+ const audioType = ContentType.AUDIO;
479
+ const initObject = new Map();
480
+ initObject.set(audioType, getFakeStream(metadata.audio));
481
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
482
+ await append(ContentType.AUDIO, 0);
483
+
484
+ expect(onMetadata).toHaveBeenCalled();
485
+ });
486
+
487
+ it('extracts ID3 metadata from TS when transmuxing', async () => {
488
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_ts'];
489
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_ts'];
490
+
491
+ const audioType = ContentType.AUDIO;
492
+ const initObject = new Map();
493
+ initObject.set(audioType, getFakeStream(metadata.audio));
494
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
495
+ await append(ContentType.AUDIO, 0);
496
+
497
+ expect(onMetadata).toHaveBeenCalled();
498
+ });
499
+
500
+ it('extracts ID3 metadata from AAC', async () => {
501
+ if (!MediaSource.isTypeSupported('audio/aac')) {
502
+ return;
503
+ }
504
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
505
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
506
+
507
+ const audioType = ContentType.AUDIO;
508
+ const initObject = new Map();
509
+ initObject.set(audioType, getFakeStream(metadata.audio));
510
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
511
+ await append(ContentType.AUDIO, 0);
512
+
513
+ expect(onMetadata).toHaveBeenCalled();
514
+ });
515
+
516
+ it('extracts ID3 metadata from AAC when transmuxing', async () => {
517
+ if (!MediaSource.isTypeSupported('audio/aac')) {
518
+ return;
519
+ }
520
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
521
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
522
+
523
+ const audioType = ContentType.AUDIO;
524
+ const initObject = new Map();
525
+ initObject.set(audioType, getFakeStream(metadata.audio));
526
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
527
+ await append(ContentType.AUDIO, 0);
528
+
529
+ expect(onMetadata).toHaveBeenCalled();
530
+ });
435
531
  });
@@ -106,7 +106,9 @@ describe('MediaSourceEngine', () => {
106
106
  shaka.media.Transmuxer = /** @type {?} */ (function() {
107
107
  return /** @type {?} */ (mockTransmuxer);
108
108
  });
109
- shaka.media.Transmuxer.convertTsCodecs = originalTransmuxer.convertTsCodecs;
109
+ shaka.media.Transmuxer.convertCodecs = (mimeType, contentType) => {
110
+ return 'video/mp4; codecs="avc1.42E01E"';
111
+ };
110
112
  shaka.media.Transmuxer.isSupported = (mimeType, contentType) => {
111
113
  return mimeType == 'tsMimetype';
112
114
  };
@@ -150,8 +152,12 @@ describe('MediaSourceEngine', () => {
150
152
  mockTextDisplayer = new shaka.test.FakeTextDisplayer();
151
153
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
152
154
  video,
153
- mockClosedCaptionParser,
154
155
  mockTextDisplayer);
156
+ mediaSourceEngine.getCaptionParser = () => {
157
+ return mockClosedCaptionParser;
158
+ };
159
+ const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
160
+ mediaSourceEngine.configure(config);
155
161
  });
156
162
 
157
163
  afterEach(() => {
@@ -204,7 +210,6 @@ describe('MediaSourceEngine', () => {
204
210
  it('creates a MediaSource object and sets video.src', () => {
205
211
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
206
212
  video,
207
- new shaka.test.FakeClosedCaptionParser(),
208
213
  new shaka.test.FakeTextDisplayer());
209
214
 
210
215
  expect(createMediaSourceSpy).toHaveBeenCalled();
@@ -631,7 +636,7 @@ describe('MediaSourceEngine', () => {
631
636
  videoSourceBuffer.mode = 'sequence';
632
637
 
633
638
  await mediaSourceEngine.init(
634
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
639
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
635
640
 
636
641
  expect(videoSourceBuffer.timestampOffset).toBe(0);
637
642
 
@@ -657,7 +662,7 @@ describe('MediaSourceEngine', () => {
657
662
  initObject.set(ContentType.VIDEO, fakeVideoStream);
658
663
 
659
664
  await mediaSourceEngine.init(
660
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
665
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
661
666
 
662
667
  // First, mock the scenario where timestampOffset is set to help align
663
668
  // text segments. In this case, SourceBuffer mode is still 'segments'.
@@ -161,6 +161,8 @@ describe('Playhead', () => {
161
161
  Util.spyFunc(onSeek),
162
162
  Util.spyFunc(onEvent));
163
163
 
164
+ playhead.ready();
165
+
164
166
  expect(video.currentTime).toBe(5);
165
167
  expect(playhead.getTime()).toBe(5);
166
168
 
@@ -182,6 +184,8 @@ describe('Playhead', () => {
182
184
  Util.spyFunc(onSeek),
183
185
  Util.spyFunc(onEvent));
184
186
 
187
+ playhead.ready();
188
+
185
189
  expect(video.addEventListener).toHaveBeenCalledWith(
186
190
  'loadedmetadata', jasmine.any(Function), jasmine.anything());
187
191
  expect(video.addEventListener).not.toHaveBeenCalledWith(
@@ -223,6 +227,8 @@ describe('Playhead', () => {
223
227
  Util.spyFunc(onSeek),
224
228
  Util.spyFunc(onEvent));
225
229
 
230
+ playhead.ready();
231
+
226
232
  video.on['seeking']();
227
233
  expect(playhead.getTime()).toBe(5);
228
234
  expect(video.currentTime).toBe(5);
@@ -264,6 +270,8 @@ describe('Playhead', () => {
264
270
  Util.spyFunc(onSeek),
265
271
  Util.spyFunc(onEvent));
266
272
 
273
+ playhead.ready();
274
+
267
275
  expect(playhead.getTime()).toBe(59); // duration - durationBackoff
268
276
  expect(video.currentTime).toBe(59); // duration - durationBackoff
269
277
  });
@@ -302,6 +310,8 @@ describe('Playhead', () => {
302
310
  Util.spyFunc(onSeek),
303
311
  Util.spyFunc(onEvent));
304
312
 
313
+ playhead.ready();
314
+
305
315
  expect(playhead.getTime()).toBe(30);
306
316
  });
307
317
 
@@ -314,6 +324,8 @@ describe('Playhead', () => {
314
324
  Util.spyFunc(onSeek),
315
325
  Util.spyFunc(onEvent));
316
326
 
327
+ playhead.ready();
328
+
317
329
  expect(video.addEventListener).toHaveBeenCalledWith(
318
330
  'loadedmetadata', jasmine.any(Function), jasmine.anything());
319
331
 
@@ -347,6 +359,8 @@ describe('Playhead', () => {
347
359
  Util.spyFunc(onSeek),
348
360
  Util.spyFunc(onEvent));
349
361
 
362
+ playhead.ready();
363
+
350
364
  expect(video.addEventListener).toHaveBeenCalledWith(
351
365
  'loadedmetadata', jasmine.any(Function), jasmine.anything());
352
366
 
@@ -380,6 +394,8 @@ describe('Playhead', () => {
380
394
  Util.spyFunc(onSeek),
381
395
  Util.spyFunc(onEvent));
382
396
 
397
+ playhead.ready();
398
+
383
399
  // This has to periodically increment the mock date to allow the onSeeking_
384
400
  // handler to seek, if appropriate.
385
401
 
@@ -652,6 +668,8 @@ describe('Playhead', () => {
652
668
  expect(currentTime).toBe(1000);
653
669
  seekCount = 0;
654
670
 
671
+ playhead.ready();
672
+
655
673
  // The availability window slips ahead.
656
674
  timeline.getSeekRangeStart.and.returnValue(1030);
657
675
  timeline.getSeekRangeEnd.and.returnValue(1030);
@@ -696,6 +714,8 @@ describe('Playhead', () => {
696
714
  Util.spyFunc(onSeek),
697
715
  Util.spyFunc(onEvent));
698
716
 
717
+ playhead.ready();
718
+
699
719
  video.on['seeking']();
700
720
  expect(video.currentTime).toBe(5);
701
721
  expect(playhead.getTime()).toBe(5);
@@ -728,6 +748,8 @@ describe('Playhead', () => {
728
748
  Util.spyFunc(onSeek),
729
749
  Util.spyFunc(onEvent));
730
750
 
751
+ playhead.ready();
752
+
731
753
  video.on['seeking']();
732
754
  expect(video.currentTime).toBe(5);
733
755
  expect(playhead.getTime()).toBe(5);
@@ -765,6 +787,9 @@ describe('Playhead', () => {
765
787
 
766
788
  video.currentTime = 0;
767
789
  video.seeking = true;
790
+
791
+ playhead.ready();
792
+
768
793
  // "video.seeking" stays true until the buffered range intersects with
769
794
  // "video.currentTime". Playhead should correct anyway.
770
795
  video.on['seeking']();
@@ -809,6 +834,8 @@ describe('Playhead', () => {
809
834
  Util.spyFunc(onSeek),
810
835
  Util.spyFunc(onEvent));
811
836
 
837
+ playhead.ready();
838
+
812
839
  /**
813
840
  * Prevent retries on the initial start time seek. This will ensure that
814
841
  * only one call is made for the initial seek, and that any additional calls
@@ -933,6 +960,8 @@ describe('Playhead', () => {
933
960
  Util.spyFunc(onSeek),
934
961
  Util.spyFunc(onEvent));
935
962
 
963
+ playhead.ready();
964
+
936
965
  jasmine.clock().tick(500);
937
966
  for (let time = data.start; time < data.waitingAt; time++) {
938
967
  // We don't want to run tick() for 1 second because it will trigger
@@ -1142,6 +1171,8 @@ describe('Playhead', () => {
1142
1171
  Util.spyFunc(onSeek),
1143
1172
  Util.spyFunc(onEvent));
1144
1173
 
1174
+ playhead.ready();
1175
+
1145
1176
  jasmine.clock().tick(500);
1146
1177
  expect(onEvent).not.toHaveBeenCalled();
1147
1178
 
@@ -1190,6 +1221,8 @@ describe('Playhead', () => {
1190
1221
  Util.spyFunc(onSeek),
1191
1222
  Util.spyFunc(onEvent));
1192
1223
 
1224
+ playhead.ready();
1225
+
1193
1226
  playhead.notifyOfBufferingChange();
1194
1227
  jasmine.clock().tick(500);
1195
1228
 
@@ -1212,6 +1245,8 @@ describe('Playhead', () => {
1212
1245
  Util.spyFunc(onSeek),
1213
1246
  Util.spyFunc(onEvent));
1214
1247
 
1248
+ playhead.ready();
1249
+
1215
1250
  playhead.notifyOfBufferingChange();
1216
1251
  jasmine.clock().tick(500);
1217
1252
 
@@ -1236,6 +1271,8 @@ describe('Playhead', () => {
1236
1271
  Util.spyFunc(onSeek),
1237
1272
  Util.spyFunc(onEvent));
1238
1273
 
1274
+ playhead.ready();
1275
+
1239
1276
  playhead.notifyOfBufferingChange();
1240
1277
  jasmine.clock().tick(500);
1241
1278
 
@@ -1260,6 +1297,8 @@ describe('Playhead', () => {
1260
1297
  Util.spyFunc(onSeek),
1261
1298
  Util.spyFunc(onEvent));
1262
1299
 
1300
+ playhead.ready();
1301
+
1263
1302
  playhead.notifyOfBufferingChange();
1264
1303
  jasmine.clock().tick(500);
1265
1304
 
@@ -1287,6 +1326,8 @@ describe('Playhead', () => {
1287
1326
  Util.spyFunc(onSeek),
1288
1327
  Util.spyFunc(onEvent));
1289
1328
 
1329
+ playhead.ready();
1330
+
1290
1331
  jasmine.clock().tick(500);
1291
1332
  expect(onEvent).not.toHaveBeenCalled();
1292
1333
 
@@ -66,8 +66,10 @@ describe('StreamingEngine', () => {
66
66
 
67
67
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
68
68
  video,
69
- new shaka.test.FakeClosedCaptionParser(),
70
69
  new shaka.test.FakeTextDisplayer());
70
+ const mediaSourceConfig =
71
+ shaka.util.PlayerConfiguration.createDefault().mediaSource;
72
+ mediaSourceEngine.configure(mediaSourceConfig);
71
73
  waiter.setMediaSourceEngine(mediaSourceEngine);
72
74
  });
73
75
 
@@ -154,6 +156,19 @@ describe('StreamingEngine', () => {
154
156
  /* presentationDuration= */ Infinity);
155
157
  setupPlayhead();
156
158
 
159
+ // Retry on failure for live streams.
160
+ config.failureCallback = () => streamingEngine.retry(0.1);
161
+
162
+ // Ignore 404 errors in live stream tests.
163
+ onError.and.callFake((error) => {
164
+ if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
165
+ error.data[1] == 404) {
166
+ // 404 error
167
+ } else {
168
+ fail(error);
169
+ }
170
+ });
171
+
157
172
  createStreamingEngine();
158
173
  }
159
174
 
@@ -248,6 +263,7 @@ describe('StreamingEngine', () => {
248
263
  onSegmentAppended: () => playhead.notifyOfBufferingChange(),
249
264
  onInitSegmentAppended: () => {},
250
265
  beforeAppendSegment: () => Promise.resolve(),
266
+ onMetadata: () => {},
251
267
  };
252
268
  streamingEngine = new shaka.media.StreamingEngine(
253
269
  /** @type {shaka.extern.Manifest} */(manifest), playerInterface);