shaka-player 3.3.4 → 4.0.1

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 (201) hide show
  1. package/.github/config.yml +1 -0
  2. package/.github/workflows/build-and-test.yaml +0 -1
  3. package/.github/workflows/demo-version-index.yaml +8 -0
  4. package/.github/workflows/release-please.yaml +19 -3
  5. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  6. package/.github/workflows/sync-labels.yaml +33 -0
  7. package/.github/workflows/update-issues.yaml +29 -0
  8. package/.github/workflows/validate-pr-title.yaml +35 -0
  9. package/AUTHORS +1 -0
  10. package/CHANGELOG.md +209 -28
  11. package/CONTRIBUTORS +1 -0
  12. package/README.md +10 -7
  13. package/app-engine/demo-version-index/README.md +3 -3
  14. package/app-engine/demo-version-index/app.yaml +27 -1
  15. package/app-engine/demo-version-index/generate.py +164 -0
  16. package/app-engine/demo-version-index/main.py +4 -155
  17. package/app-engine/demo-version-index/requirements.txt +0 -1
  18. package/build/README.md +2 -2
  19. package/build/compiler.py +1 -1
  20. package/build/generateTsDefs.py +0 -2
  21. package/build/misspellings.txt +1 -0
  22. package/build/shaka-lab.yaml +0 -2
  23. package/build/test.py +7 -1
  24. package/build/types/core +0 -1
  25. package/build/types/polyfill +0 -3
  26. package/demo/common/asset.js +17 -1
  27. package/demo/common/assets.js +29 -0
  28. package/demo/common/message_ids.js +6 -3
  29. package/demo/config.js +10 -11
  30. package/demo/custom.js +39 -0
  31. package/demo/demo_utils.js +0 -1
  32. package/demo/locales/en.json +6 -3
  33. package/demo/locales/source.json +22 -10
  34. package/demo/main.js +9 -6
  35. package/demo/search.js +2 -1
  36. package/dist/controls.css +1 -1
  37. package/dist/demo.compiled.debug.js +71 -64
  38. package/dist/demo.compiled.debug.map +3 -3
  39. package/dist/demo.compiled.js +71 -64
  40. package/dist/demo.compiled.map +3 -3
  41. package/dist/demo.css +1 -1
  42. package/dist/demo.css.map +1 -1
  43. package/dist/deps.js +16 -20
  44. package/dist/locales.js +33 -31
  45. package/dist/receiver.compiled.debug.js +32 -28
  46. package/dist/receiver.compiled.debug.map +3 -3
  47. package/dist/receiver.compiled.js +32 -28
  48. package/dist/receiver.compiled.map +3 -3
  49. package/dist/shaka-player.compiled.d.ts +78 -82
  50. package/dist/shaka-player.compiled.debug.d.ts +78 -82
  51. package/dist/shaka-player.compiled.debug.externs.js +127 -126
  52. package/dist/shaka-player.compiled.debug.js +471 -523
  53. package/dist/shaka-player.compiled.debug.map +4 -4
  54. package/dist/shaka-player.compiled.externs.js +127 -126
  55. package/dist/shaka-player.compiled.js +844 -858
  56. package/dist/shaka-player.compiled.map +4 -4
  57. package/dist/shaka-player.ui.d.ts +80 -82
  58. package/dist/shaka-player.ui.debug.d.ts +80 -82
  59. package/dist/shaka-player.ui.debug.externs.js +135 -126
  60. package/dist/shaka-player.ui.debug.js +531 -581
  61. package/dist/shaka-player.ui.debug.map +4 -4
  62. package/dist/shaka-player.ui.externs.js +135 -126
  63. package/dist/shaka-player.ui.js +991 -1001
  64. package/dist/shaka-player.ui.map +4 -4
  65. package/docs/tutorials/config.md +0 -2
  66. package/docs/tutorials/drm-config.md +5 -0
  67. package/docs/tutorials/fairplay.md +7 -22
  68. package/docs/tutorials/faq.md +0 -11
  69. package/docs/tutorials/index.json +2 -8
  70. package/docs/tutorials/plugins.md +1 -3
  71. package/docs/tutorials/upgrade.md +85 -11
  72. package/externs/polyfill.js +15 -0
  73. package/externs/shaka/manifest.js +5 -1
  74. package/externs/shaka/net.js +12 -0
  75. package/externs/shaka/offline.js +5 -1
  76. package/externs/shaka/player.js +39 -21
  77. package/externs/shaka/text.js +12 -12
  78. package/externs/sourcebuffer.js +15 -0
  79. package/karma.conf.js +4 -0
  80. package/lib/ads/client_side_ad.js +13 -2
  81. package/lib/ads/client_side_ad_manager.js +22 -4
  82. package/lib/cast/cast_utils.js +0 -1
  83. package/lib/dash/content_protection.js +85 -5
  84. package/lib/dash/dash_parser.js +3 -3
  85. package/lib/hls/hls_parser.js +391 -747
  86. package/lib/hls/manifest_text_parser.js +2 -3
  87. package/lib/media/drm_engine.js +102 -37
  88. package/lib/media/gap_jumping_controller.js +10 -45
  89. package/lib/media/media_source_engine.js +157 -31
  90. package/lib/media/playhead.js +2 -5
  91. package/lib/media/presentation_timeline.js +1 -2
  92. package/lib/media/segment_index.js +13 -46
  93. package/lib/media/segment_reference.js +47 -1
  94. package/lib/media/streaming_engine.js +28 -11
  95. package/lib/media/time_ranges_utils.js +4 -9
  96. package/lib/net/networking_engine.js +3 -0
  97. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  98. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  99. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  100. package/lib/offline/manifest_converter.js +1 -0
  101. package/lib/offline/storage.js +100 -132
  102. package/lib/player.js +181 -307
  103. package/lib/polyfill/media_capabilities.js +12 -6
  104. package/lib/polyfill/mediasource.js +0 -28
  105. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  106. package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
  107. package/lib/text/cue.js +0 -6
  108. package/lib/text/lrc_text_parser.js +8 -0
  109. package/lib/text/mp4_ttml_parser.js +8 -0
  110. package/lib/text/mp4_vtt_parser.js +8 -0
  111. package/lib/text/sbv_text_parser.js +8 -0
  112. package/lib/text/simple_text_displayer.js +1 -7
  113. package/lib/text/srt_text_parser.js +8 -0
  114. package/lib/text/ssa_text_parser.js +8 -0
  115. package/lib/text/text_engine.js +19 -3
  116. package/lib/text/ttml_text_parser.js +8 -0
  117. package/lib/text/ui_text_displayer.js +1 -7
  118. package/lib/text/vtt_text_parser.js +27 -5
  119. package/lib/text/web_vtt_generator.js +1 -7
  120. package/lib/util/config_utils.js +25 -0
  121. package/lib/util/error.js +16 -13
  122. package/lib/util/fairplay_utils.js +109 -0
  123. package/lib/util/functional.js +0 -42
  124. package/lib/util/manifest_parser_utils.js +1 -1
  125. package/lib/util/mime_utils.js +50 -1
  126. package/lib/util/periods.js +5 -4
  127. package/lib/util/platform.js +45 -0
  128. package/lib/util/player_configuration.js +26 -20
  129. package/lib/util/pssh.js +23 -4
  130. package/lib/util/stream_utils.js +96 -24
  131. package/lib/util/uint8array_utils.js +0 -16
  132. package/lib/util/xml_utils.js +1 -1
  133. package/package.json +4 -1
  134. package/roadmap.md +19 -19
  135. package/shaka-player.uncompiled.js +0 -3
  136. package/test/cast/cast_utils_unit.js +1 -0
  137. package/test/dash/dash_parser_content_protection_unit.js +44 -0
  138. package/test/dash/dash_parser_manifest_unit.js +0 -24
  139. package/test/dash/dash_parser_segment_template_unit.js +3 -4
  140. package/test/demo/demo_unit.js +2 -1
  141. package/test/hls/hls_live_unit.js +121 -220
  142. package/test/hls/hls_parser_unit.js +515 -432
  143. package/test/hls/manifest_text_parser_unit.js +19 -0
  144. package/test/media/drm_engine_unit.js +86 -120
  145. package/test/media/media_source_engine_integration.js +26 -3
  146. package/test/media/media_source_engine_unit.js +7 -6
  147. package/test/media/playhead_unit.js +27 -191
  148. package/test/media/segment_index_unit.js +15 -25
  149. package/test/media/streaming_engine_integration.js +4 -39
  150. package/test/media/streaming_engine_unit.js +3 -2
  151. package/test/media/time_ranges_utils_unit.js +0 -5
  152. package/test/offline/indexeddb_storage_unit.js +1 -1
  153. package/test/offline/manifest_convert_unit.js +34 -4
  154. package/test/offline/offline_manifest_parser_unit.js +1 -0
  155. package/test/offline/storage_compatibility_unit.js +1 -1
  156. package/test/offline/storage_integration.js +5 -4
  157. package/test/player_external.js +7 -7
  158. package/test/player_integration.js +22 -10
  159. package/test/player_src_equals_integration.js +81 -83
  160. package/test/player_unit.js +124 -1
  161. package/test/test/boot.js +9 -0
  162. package/test/test/externs/filters.js +4 -0
  163. package/test/test/util/fake_media_source_engine.js +11 -4
  164. package/test/test/util/manifest_generator.js +3 -1
  165. package/test/test/util/manifest_parser_util.js +4 -1
  166. package/test/test/util/offline_utils.js +1 -0
  167. package/test/test/util/simple_fakes.js +31 -2
  168. package/test/test/util/streaming_engine_util.js +1 -0
  169. package/test/test/util/test_scheme.js +24 -1
  170. package/test/test/util/util.js +4 -4
  171. package/test/text/cue_integration.js +2 -2
  172. package/test/text/lrc_text_parser_unit.js +6 -7
  173. package/test/text/mp4_ttml_parser_unit.js +6 -4
  174. package/test/text/mp4_vtt_parser_unit.js +6 -5
  175. package/test/text/sbv_text_parser_unit.js +5 -6
  176. package/test/text/srt_text_parser_unit.js +5 -6
  177. package/test/text/ssa_text_parser_unit.js +17 -29
  178. package/test/text/text_engine_unit.js +45 -5
  179. package/test/text/ttml_text_parser_unit.js +77 -77
  180. package/test/text/vtt_text_parser_unit.js +116 -59
  181. package/test/ui/ui_integration.js +0 -16
  182. package/test/ui/ui_unit.js +27 -0
  183. package/test/util/cmcd_manager_unit.js +3 -0
  184. package/test/util/mime_utils_unit.js +54 -0
  185. package/test/util/periods_unit.js +79 -0
  186. package/test/util/stream_utils_unit.js +23 -7
  187. package/ui/controls.js +65 -40
  188. package/ui/fullscreen_button.js +26 -6
  189. package/ui/less/containers.less +3 -3
  190. package/ui/locales/en.json +1 -0
  191. package/ui/locales/source.json +5 -0
  192. package/ui/resolution_selection.js +31 -33
  193. package/ui/ui.js +0 -17
  194. package/docs/tutorials/gsoc-ideas-list.md +0 -3
  195. package/docs/tutorials/upgrade-v2-5.md +0 -441
  196. package/docs/tutorials/upgrade-v3.md +0 -38
  197. package/lib/polyfill/mathround.js +0 -49
  198. package/lib/polyfill/patchedmediakeys_apple.js +0 -744
  199. package/lib/polyfill/patchedmediakeys_ms.js +0 -698
  200. package/lib/util/mutex.js +0 -52
  201. package/test/util/functional_unit.js +0 -82
@@ -693,22 +693,6 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
693
693
  expect(iterator.current()).toBe(null);
694
694
  });
695
695
 
696
- describe('seek', () => {
697
- it('returns the matching segment', () => {
698
- const index = new shaka.media.SegmentIndex(inputRefs);
699
- const iterator = index[Symbol.iterator]();
700
- expect(iterator.seek(10)).toBe(inputRefs[1]);
701
- expect(iterator.current()).toBe(inputRefs[1]);
702
- });
703
-
704
- it('returns the matching segment with partial segments', () => {
705
- const index = new shaka.media.SegmentIndex(inputRefsWithPartial);
706
- const iterator = index[Symbol.iterator]();
707
- expect(iterator.seek(8)).toBe(inputRefsWithPartial[0]);
708
- expect(iterator.seek(10)).toBe(partialRefs1[0]);
709
- });
710
- });
711
-
712
696
  describe('next', () => {
713
697
  it('starts with the first segment', () => {
714
698
  const index = new shaka.media.SegmentIndex(inputRefs);
@@ -725,20 +709,20 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
725
709
  expect(iterator.next().value).toBe(null);
726
710
  });
727
711
 
728
- it('iterates forward after a seek', () => {
712
+ it('iterates from getIteratorForTime', () => {
729
713
  const index = new shaka.media.SegmentIndex(inputRefs);
730
- const iterator = index[Symbol.iterator]();
731
- expect(iterator.seek(10)).toBe(inputRefs[1]);
714
+ const iterator = index.getIteratorForTime(10);
715
+ expect(iterator.next().value).toBe(inputRefs[1]);
732
716
  expect(iterator.current()).toBe(inputRefs[1]);
733
717
  expect(iterator.next().value).toBe(inputRefs[2]);
734
718
  expect(iterator.current()).toBe(inputRefs[2]);
735
719
  expect(iterator.next().value).toBe(null);
736
720
  });
737
721
 
738
- it('iterates forward after a seek with partial segments', () => {
722
+ it('iterates from getIteratorForTime with partial segments', () => {
739
723
  const index = new shaka.media.SegmentIndex(inputRefsWithPartial);
740
- const iterator = index[Symbol.iterator]();
741
- expect(iterator.seek(10)).toBe(partialRefs1[0]);
724
+ const iterator = index.getIteratorForTime(10);
725
+ expect(iterator.next().value).toBe(partialRefs1[0]);
742
726
  expect(iterator.current()).toBe(partialRefs1[0]);
743
727
  expect(iterator.next().value).toBe(partialRefs1[1]);
744
728
  expect(iterator.current()).toBe(partialRefs1[1]);
@@ -935,11 +919,17 @@ describe('SegmentIndex', /** @suppress {accessControls} */ () => {
935
919
  expect(Array.from(metaIndex)).toEqual(oldRefs);
936
920
 
937
921
  // Every 0.1 seconds, return the next new ref.
938
- index1.updateEvery(0.1, () => {
939
- return [newRefs.shift()];
922
+ const done = new Promise((resolve, reject) => {
923
+ index1.updateEvery(0.1, () => {
924
+ if (newRefs.length == 0) {
925
+ resolve();
926
+ }
927
+ return [newRefs.shift()];
928
+ });
940
929
  });
941
930
  // Wait long enough for all three new refs to be appended.
942
- await shaka.test.Util.delay(0.5);
931
+ // Or for all of the new refs to be passed out.
932
+ await Promise.race([shaka.test.Util.delay(1), done]);
943
933
 
944
934
  expect(Array.from(metaIndex)).toEqual(oldRefs.concat(inputRefs2));
945
935
  });
@@ -214,8 +214,7 @@ describe('StreamingEngine', () => {
214
214
  manifest,
215
215
  config,
216
216
  /* startTime= */ null,
217
- onSeek,
218
- shaka.test.Util.spyFunc(onEvent));
217
+ onSeek);
219
218
  }
220
219
 
221
220
  function setupManifest(
@@ -403,7 +402,6 @@ describe('StreamingEngine', () => {
403
402
  // TODO: Consider also adding tests for missing frames.
404
403
  describe('gap jumping', () => {
405
404
  it('jumps small gaps at the beginning', async () => {
406
- config.smallGapLimit = 5;
407
405
  await setupGappyContent(/* gapAtStart= */ 1, /* dropSegment= */ false);
408
406
 
409
407
  // Let's go!
@@ -419,8 +417,6 @@ describe('StreamingEngine', () => {
419
417
  });
420
418
 
421
419
  it('jumps large gaps at the beginning', async () => {
422
- config.smallGapLimit = 1;
423
- config.jumpLargeGaps = true;
424
420
  await setupGappyContent(/* gapAtStart= */ 5, /* dropSegment= */ false);
425
421
 
426
422
  // Let's go!
@@ -436,7 +432,6 @@ describe('StreamingEngine', () => {
436
432
  });
437
433
 
438
434
  it('jumps small gaps in the middle', async () => {
439
- config.smallGapLimit = 20;
440
435
  await setupGappyContent(/* gapAtStart= */ 0, /* dropSegment= */ true);
441
436
 
442
437
  // Let's go!
@@ -451,11 +446,9 @@ describe('StreamingEngine', () => {
451
446
  await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 23);
452
447
  // Should be close enough to still have the gap buffered.
453
448
  expect(video.buffered.length).toBe(2);
454
- expect(onEvent).not.toHaveBeenCalled();
455
449
  });
456
450
 
457
451
  it('jumps large gaps in the middle', async () => {
458
- config.jumpLargeGaps = true;
459
452
  await setupGappyContent(/* gapAtStart= */ 0, /* dropSegment= */ true);
460
453
 
461
454
  // Let's go!
@@ -470,37 +463,6 @@ describe('StreamingEngine', () => {
470
463
  await waiter.timeoutAfter(60).waitUntilPlayheadReaches(video, 23);
471
464
  // Should be close enough to still have the gap buffered.
472
465
  expect(video.buffered.length).toBe(2);
473
- expect(onEvent).toHaveBeenCalled();
474
- });
475
-
476
- it('won\'t jump large gaps with preventDefault()', async () => {
477
- config.jumpLargeGaps = true;
478
- await setupGappyContent(/* gapAtStart= */ 0, /* dropSegment= */ true);
479
-
480
- onEvent.and.callFake((event) => {
481
- event.preventDefault();
482
- });
483
-
484
- // Let's go!
485
- streamingEngine.switchVariant(variant);
486
- await streamingEngine.start();
487
-
488
- await waiter.timeoutAfter(5).waitForEvent(video, 'loadeddata');
489
-
490
- let seekCount = 0;
491
- eventManager.listen(video, 'seeking', () => {
492
- seekCount++;
493
- });
494
-
495
- video.currentTime = 8;
496
- video.play();
497
-
498
- await shaka.test.Util.delay(5);
499
-
500
- // Edge somehow plays _into_ the gap, and Xbox One plays _through_ the
501
- // gap. Just make sure _we_ don't jump the gap by seeking. One seek is
502
- // required to start playback at time 8.
503
- expect(seekCount).toBe(1);
504
466
  });
505
467
 
506
468
  /**
@@ -610,6 +572,7 @@ describe('StreamingEngine', () => {
610
572
  minBufferTime: 2,
611
573
  textStreams: [],
612
574
  imageStreams: [],
575
+ sequenceMode: false,
613
576
  variants: [{
614
577
  id: 1,
615
578
  video: {
@@ -622,6 +585,7 @@ describe('StreamingEngine', () => {
622
585
  width: 600,
623
586
  height: 400,
624
587
  type: shaka.util.ManifestParserUtils.ContentType.VIDEO,
588
+ drmInfos: [],
625
589
  },
626
590
  audio: {
627
591
  id: 3,
@@ -631,6 +595,7 @@ describe('StreamingEngine', () => {
631
595
  codecs: 'mp4a.40.2',
632
596
  bandwidth: 192000,
633
597
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
598
+ drmInfos: [],
634
599
  },
635
600
  }],
636
601
  };
@@ -496,7 +496,8 @@ describe('StreamingEngine', () => {
496
496
  expectedMseInit.set(ContentType.VIDEO, videoStream);
497
497
  expectedMseInit.set(ContentType.TEXT, textStream);
498
498
 
499
- expect(mediaSourceEngine.init).toHaveBeenCalledWith(expectedMseInit, false);
499
+ expect(mediaSourceEngine.init).toHaveBeenCalledWith(expectedMseInit,
500
+ /** forceTransmuxTS= */ false, /** sequenceMode= */ false);
500
501
  expect(mediaSourceEngine.init).toHaveBeenCalledTimes(1);
501
502
 
502
503
  expect(mediaSourceEngine.setDuration).toHaveBeenCalledTimes(1);
@@ -801,7 +802,7 @@ describe('StreamingEngine', () => {
801
802
  asymmetricMatch: (val) => val > 40 && val <= 40.1,
802
803
  };
803
804
  expect(mediaSourceEngine.setStreamProperties)
804
- .toHaveBeenCalledWith('video', 0, lt20, gt40);
805
+ .toHaveBeenCalledWith('video', 0, lt20, gt40, false);
805
806
  });
806
807
 
807
808
  // Regression test for https://github.com/shaka-project/shaka-player/issues/3717
@@ -22,11 +22,6 @@ describe('TimeRangesUtils', () => {
22
22
  expect(TimeRangesUtils.isBuffered(b, 13)).toBe(true);
23
23
  });
24
24
 
25
- it('returns buffered when having a small gap', () => {
26
- const b = createFakeBuffered([{start: 10, end: 20}]);
27
- expect(TimeRangesUtils.isBuffered(b, 9, 1)).toBe(true);
28
- });
29
-
30
25
  // Ranges: [10-20], [30-40], [50-60]
31
26
  defineTest('returns false when before range', {time: 3, expected: false});
32
27
  defineTest('returns true inside first range', {time: 12, expected: true});
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- filterDescribe('IndexeddbStorageCell', () => window.indexedDB, () => {
7
+ filterDescribe('IndexeddbStorageCell', offlineSupported, () => {
8
8
  const IndexedDBUtils = shaka.test.IndexedDBUtils;
9
9
  const OfflineUtils = shaka.test.OfflineUtils;
10
10
  const Util = shaka.test.Util;
@@ -107,6 +107,7 @@ describe('ManifestConverter', () => {
107
107
  },
108
108
  appMetadata: null,
109
109
  creationTime: 0,
110
+ sequenceMode: false,
110
111
  };
111
112
 
112
113
  const manifest = createConverter().fromManifestDB(manifestDb);
@@ -143,6 +144,7 @@ describe('ManifestConverter', () => {
143
144
  createVideoStreamDB(1, [0]),
144
145
  createVideoStreamDB(2, [1]),
145
146
  ],
147
+ sequenceMode: false,
146
148
  };
147
149
 
148
150
  const manifest = createConverter().fromManifestDB(manifestDb);
@@ -170,6 +172,7 @@ describe('ManifestConverter', () => {
170
172
  createAudioStreamDB(1, [0]),
171
173
  createAudioStreamDB(2, [1]),
172
174
  ],
175
+ sequenceMode: false,
173
176
  };
174
177
 
175
178
  const manifest = createConverter().fromManifestDB(manifestDb);
@@ -182,6 +185,29 @@ describe('ManifestConverter', () => {
182
185
  expect(manifest.variants[1].video).toBe(null);
183
186
  });
184
187
 
188
+ it('supports containerless content', () => {
189
+ /** @type {shaka.extern.ManifestDB} */
190
+ const manifestDb = {
191
+ originalManifestUri: 'http://example.com/foo',
192
+ duration: 60,
193
+ size: 1234,
194
+ expiration: Infinity,
195
+ sessionIds: [],
196
+ drmInfo: null,
197
+ appMetadata: null,
198
+ creationTime: 0,
199
+ streams: [
200
+ createVideoStreamDB(1, [0]),
201
+ createAudioStreamDB(2, [0]),
202
+ ],
203
+ sequenceMode: true,
204
+ };
205
+
206
+ const manifest = createConverter().fromManifestDB(manifestDb);
207
+ expect(manifest.sequenceMode).toBe(true);
208
+ expect(manifest.variants.length).toBe(1);
209
+ });
210
+
185
211
  it('supports text streams', () => {
186
212
  /** @type {shaka.extern.ManifestDB} */
187
213
  const manifestDb = {
@@ -197,6 +223,7 @@ describe('ManifestConverter', () => {
197
223
  createVideoStreamDB(1, [0]),
198
224
  createTextStreamDB(2),
199
225
  ],
226
+ sequenceMode: false,
200
227
  };
201
228
 
202
229
  const manifest = createConverter().fromManifestDB(manifestDb);
@@ -235,6 +262,7 @@ describe('ManifestConverter', () => {
235
262
  createVideoStreamDB(video1, [variant1]),
236
263
  createVideoStreamDB(video2, [variant2, variant3]),
237
264
  ],
265
+ sequenceMode: false,
238
266
  };
239
267
 
240
268
  const manifest = createConverter().fromManifestDB(manifestDb);
@@ -509,8 +537,6 @@ describe('ManifestConverter', () => {
509
537
 
510
538
  // Assume that we don't have to call createSegmentIndex.
511
539
 
512
- const iterator = stream.segmentIndex[Symbol.iterator]();
513
-
514
540
  streamDb.segments.forEach((segmentDb, i) => {
515
541
  const uri = shaka.offline.OfflineUri.segment(
516
542
  'mechanism', 'cell', segmentDb.dataKey);
@@ -520,10 +546,14 @@ describe('ManifestConverter', () => {
520
546
  null;
521
547
 
522
548
  /** @type {shaka.media.SegmentReference} */
523
- const segment = iterator.seek(segmentDb.startTime);
549
+ const segment =
550
+ stream.segmentIndex
551
+ .getIteratorForTime(segmentDb.startTime).next().value;
524
552
 
525
553
  /** @type {shaka.media.SegmentReference} */
526
- const sameSegment = iterator.seek(segmentDb.endTime - 0.1);
554
+ const sameSegment =
555
+ stream.segmentIndex
556
+ .getIteratorForTime(segmentDb.endTime - 0.1).next().value;
527
557
 
528
558
  expect(segment).toBe(sameSegment);
529
559
  expect(segment.startTime).toBe(segmentDb.startTime);
@@ -210,6 +210,7 @@ filterDescribe('OfflineManifestParser', offlineManifestParserSupport, () => {
210
210
  sessionIds: [sessionId],
211
211
  drmInfo: null,
212
212
  appMetadata: {},
213
+ sequenceMode: false,
213
214
  };
214
215
 
215
216
  return manifest;
@@ -91,7 +91,7 @@ const compatibilityTestsMetadata = [
91
91
  },
92
92
  ];
93
93
 
94
- filterDescribe('Storage Compatibility', () => window.indexedDB, () => {
94
+ filterDescribe('Storage Compatibility', offlineSupported, () => {
95
95
  for (const metadata of compatibilityTestsMetadata) {
96
96
  describe(metadata.name, () => {
97
97
  makeTests(metadata);
@@ -1110,10 +1110,7 @@ filterDescribe('Storage', storageSupport, () => {
1110
1110
  aRequestIsStarted.resolve();
1111
1111
  await promise;
1112
1112
 
1113
- // All downloads for a given stream are in the same "download group",
1114
- // and will be downloaded sequentially. Thus, we expect only the first
1115
- // download to be aborted.
1116
- expect(abortCheck()).toBe(i == 0 ? true : false);
1113
+ expect(abortCheck()).toBe(true);
1117
1114
 
1118
1115
  return new ArrayBuffer(16);
1119
1116
  });
@@ -1355,6 +1352,8 @@ filterDescribe('Storage', storageSupport, () => {
1355
1352
  pixelAspectRatio: '59:54',
1356
1353
  hdr: null,
1357
1354
  mimeType: 'video/mp4,audio/mp4',
1355
+ audioMimeType: 'audio/mp4',
1356
+ videoMimeType: 'video/mp4',
1358
1357
  codecs: 'mp4,mp4',
1359
1358
  audioCodec: 'mp4',
1360
1359
  videoCodec: 'mp4',
@@ -1397,6 +1396,8 @@ filterDescribe('Storage', storageSupport, () => {
1397
1396
  pixelAspectRatio: null,
1398
1397
  hdr: null,
1399
1398
  mimeType: 'text/vtt',
1399
+ audioMimeType: null,
1400
+ videoMimeType: null,
1400
1401
  codecs: 'vtt',
1401
1402
  audioCodec: null,
1402
1403
  videoCodec: null,
@@ -107,12 +107,6 @@ describe('Player', () => {
107
107
  player.configure('manifest.dash.clockSyncUri',
108
108
  'https://shaka-player-demo.appspot.com/time.txt');
109
109
 
110
- // Make sure we don't get stuck on gaps that only appear in some
111
- // browsers (Safari, Firefox).
112
- // TODO(https://github.com/shaka-project/shaka-player/issues/1702):
113
- // Is this necessary because of a bug in Shaka Player?
114
- player.configure('streaming.jumpLargeGaps', true);
115
-
116
110
  // Add asset-specific configuration.
117
111
  player.configure(asset.getConfiguration());
118
112
 
@@ -120,7 +114,13 @@ describe('Player', () => {
120
114
  const networkingEngine = player.getNetworkingEngine();
121
115
  asset.applyFilters(networkingEngine);
122
116
 
123
- await player.load(asset.manifestUri);
117
+ // Rather than awaiting the load() method, catch any load() errors and
118
+ // wait on the 'canplay' event. This has the advantage that we will
119
+ // get better logging of the media state on a timeout, since that
120
+ // capabilitiy is built into the waiter for media element events.
121
+ player.load(asset.manifestUri).catch(fail);
122
+ await waiter.timeoutAfter(60).waitForEvent(video, 'canplay');
123
+
124
124
  if (asset.features) {
125
125
  const isLive = asset.features.includes(Feature.LIVE);
126
126
  expect(player.isLive()).toBe(isLive);
@@ -418,6 +418,22 @@ describe('Player', () => {
418
418
  expect(video.playbackRate).toBe(1);
419
419
  });
420
420
 
421
+ it('in sequence mode', async () => {
422
+ const testSchemeMimeType = 'application/x-test-manifest';
423
+ player = new compiledShaka.Player(video);
424
+ await player.load('test:sintel_sequence_compiled', 0, testSchemeMimeType);
425
+ expect(player.getManifest().sequenceMode).toBe(true);
426
+
427
+ // Ensure the video plays.
428
+ video.play();
429
+ await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 5, 10);
430
+
431
+ // Seek the video, and see if it can continue playing from that point.
432
+ video.currentTime = 20;
433
+ // Expect that we can then reach the end of the video.
434
+ await waiter.timeoutAfter(20).waitForEnd(video);
435
+ });
436
+
421
437
  // Regression test for #2326.
422
438
  //
423
439
  // 1. Construct an instance with a video element.
@@ -1053,16 +1069,16 @@ describe('Player', () => {
1053
1069
  });
1054
1070
  }); // describe('unloading')
1055
1071
 
1056
- describe('chapters', () => {
1057
- it('add external chapters in vtt format', async () => {
1072
+ describe('addChaptersTrack', () => {
1073
+ it('adds external chapters in vtt format', async () => {
1058
1074
  await player.load('test:sintel_no_text_compiled');
1059
1075
  const locationUri = new goog.Uri(location.href);
1060
1076
  const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
1061
1077
  const absoluteUri1 = locationUri.resolve(partialUri1);
1062
1078
  await player.addChaptersTrack(absoluteUri1.toString(), 'en');
1063
1079
 
1064
- await shaka.test.Util.delay(1.5);
1065
-
1080
+ // Data should be available as soon as addChaptersTrack resolves.
1081
+ // See https://github.com/shaka-project/shaka-player/issues/4186
1066
1082
  const chapters = player.getChapters('en');
1067
1083
  expect(chapters.length).toBe(3);
1068
1084
  const chapter1 = chapters[0];
@@ -1082,8 +1098,6 @@ describe('Player', () => {
1082
1098
  const absoluteUri2 = locationUri.resolve(partialUri2);
1083
1099
  await player.addChaptersTrack(absoluteUri2.toString(), 'en');
1084
1100
 
1085
- await shaka.test.Util.delay(1.5);
1086
-
1087
1101
  const chaptersUpdated = player.getChapters('en');
1088
1102
  expect(chaptersUpdated.length).toBe(6);
1089
1103
  const chapterUpdated1 = chaptersUpdated[0];
@@ -1112,15 +1126,13 @@ describe('Player', () => {
1112
1126
  expect(chapterUpdated6.endTime).toBe(61.349);
1113
1127
  });
1114
1128
 
1115
- it('add external chapters in srt format', async () => {
1129
+ it('adds external chapters in srt format', async () => {
1116
1130
  await player.load('test:sintel_no_text_compiled');
1117
1131
  const locationUri = new goog.Uri(location.href);
1118
1132
  const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
1119
1133
  const absoluteUri = locationUri.resolve(partialUri);
1120
1134
  await player.addChaptersTrack(absoluteUri.toString(), 'es');
1121
1135
 
1122
- await shaka.test.Util.delay(1.5);
1123
-
1124
1136
  const chapters = player.getChapters('es');
1125
1137
  expect(chapters.length).toBe(3);
1126
1138
  const chapter1 = chapters[0];
@@ -1136,5 +1148,5 @@ describe('Player', () => {
1136
1148
  expect(chapter3.startTime).toBe(30);
1137
1149
  expect(chapter3.endTime).toBe(61.349);
1138
1150
  });
1139
- }); // describe('chapters')
1151
+ }); // describe('addChaptersTrack')
1140
1152
  });
@@ -319,90 +319,88 @@ describe('Player Src Equals', () => {
319
319
  expect(newTrack).toBeTruthy();
320
320
  });
321
321
 
322
- it('add external chapters in vtt format', async () => {
323
- await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
324
-
325
- const locationUri = new goog.Uri(location.href);
326
- const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
327
- const absoluteUri1 = locationUri.resolve(partialUri1);
328
- await player.addChaptersTrack(absoluteUri1.toString(), 'en');
329
-
330
- await shaka.test.Util.delay(1.5);
331
-
332
- const chapters = player.getChapters('en');
333
- expect(chapters.length).toBe(3);
334
- const chapter1 = chapters[0];
335
- expect(chapter1.title).toBe('Chapter 1');
336
- expect(chapter1.startTime).toBe(0);
337
- expect(chapter1.endTime).toBe(5);
338
- const chapter2 = chapters[1];
339
- expect(chapter2.title).toBe('Chapter 2');
340
- expect(chapter2.startTime).toBe(5);
341
- expect(chapter2.endTime).toBe(10);
342
- const chapter3 = chapters[2];
343
- expect(chapter3.title).toBe('Chapter 3');
344
- expect(chapter3.startTime).toBe(10);
345
- expect(chapter3.endTime).toBe(20);
346
-
347
- const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
348
- const absoluteUri2 = locationUri.resolve(partialUri2);
349
- await player.addChaptersTrack(absoluteUri2.toString(), 'en');
350
-
351
- await shaka.test.Util.delay(1.5);
352
-
353
- const chaptersUpdated = player.getChapters('en');
354
- expect(chaptersUpdated.length).toBe(6);
355
- const chapterUpdated1 = chaptersUpdated[0];
356
- expect(chapterUpdated1.title).toBe('Chapter 1');
357
- expect(chapterUpdated1.startTime).toBe(0);
358
- expect(chapterUpdated1.endTime).toBe(5);
359
- const chapterUpdated2 = chaptersUpdated[1];
360
- expect(chapterUpdated2.title).toBe('Chapter 2');
361
- expect(chapterUpdated2.startTime).toBe(5);
362
- expect(chapterUpdated2.endTime).toBe(10);
363
- const chapterUpdated3 = chaptersUpdated[2];
364
- expect(chapterUpdated3.title).toBe('Chapter 3');
365
- expect(chapterUpdated3.startTime).toBe(10);
366
- expect(chapterUpdated3.endTime).toBe(20);
367
- const chapterUpdated4 = chaptersUpdated[3];
368
- expect(chapterUpdated4.title).toBe('Chapter 4');
369
- expect(chapterUpdated4.startTime).toBe(20);
370
- expect(chapterUpdated4.endTime).toBe(30);
371
- const chapterUpdated5 = chaptersUpdated[4];
372
- expect(chapterUpdated5.title).toBe('Chapter 5');
373
- expect(chapterUpdated5.startTime).toBe(30);
374
- expect(chapterUpdated5.endTime).toBe(40);
375
- const chapterUpdated6 = chaptersUpdated[5];
376
- expect(chapterUpdated6.title).toBe('Chapter 6');
377
- expect(chapterUpdated6.startTime).toBe(40);
378
- expect(chapterUpdated6.endTime).toBe(61.349);
379
- });
380
-
381
- it('add external chapters in srt format', async () => {
382
- await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
383
-
384
- const locationUri = new goog.Uri(location.href);
385
- const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
386
- const absoluteUri = locationUri.resolve(partialUri);
387
- await player.addChaptersTrack(absoluteUri.toString(), 'es');
388
-
389
- await shaka.test.Util.delay(1.5);
322
+ describe('addChaptersTrack', () => {
323
+ it('adds external chapters in vtt format', async () => {
324
+ await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
325
+
326
+ const locationUri = new goog.Uri(location.href);
327
+ const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
328
+ const absoluteUri1 = locationUri.resolve(partialUri1);
329
+ await player.addChaptersTrack(absoluteUri1.toString(), 'en');
330
+
331
+ // Data should be available as soon as addChaptersTrack resolves.
332
+ // See https://github.com/shaka-project/shaka-player/issues/4186
333
+ const chapters = player.getChapters('en');
334
+ expect(chapters.length).toBe(3);
335
+ const chapter1 = chapters[0];
336
+ expect(chapter1.title).toBe('Chapter 1');
337
+ expect(chapter1.startTime).toBe(0);
338
+ expect(chapter1.endTime).toBe(5);
339
+ const chapter2 = chapters[1];
340
+ expect(chapter2.title).toBe('Chapter 2');
341
+ expect(chapter2.startTime).toBe(5);
342
+ expect(chapter2.endTime).toBe(10);
343
+ const chapter3 = chapters[2];
344
+ expect(chapter3.title).toBe('Chapter 3');
345
+ expect(chapter3.startTime).toBe(10);
346
+ expect(chapter3.endTime).toBe(20);
347
+
348
+ const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
349
+ const absoluteUri2 = locationUri.resolve(partialUri2);
350
+ await player.addChaptersTrack(absoluteUri2.toString(), 'en');
351
+
352
+ const chaptersUpdated = player.getChapters('en');
353
+ expect(chaptersUpdated.length).toBe(6);
354
+ const chapterUpdated1 = chaptersUpdated[0];
355
+ expect(chapterUpdated1.title).toBe('Chapter 1');
356
+ expect(chapterUpdated1.startTime).toBe(0);
357
+ expect(chapterUpdated1.endTime).toBe(5);
358
+ const chapterUpdated2 = chaptersUpdated[1];
359
+ expect(chapterUpdated2.title).toBe('Chapter 2');
360
+ expect(chapterUpdated2.startTime).toBe(5);
361
+ expect(chapterUpdated2.endTime).toBe(10);
362
+ const chapterUpdated3 = chaptersUpdated[2];
363
+ expect(chapterUpdated3.title).toBe('Chapter 3');
364
+ expect(chapterUpdated3.startTime).toBe(10);
365
+ expect(chapterUpdated3.endTime).toBe(20);
366
+ const chapterUpdated4 = chaptersUpdated[3];
367
+ expect(chapterUpdated4.title).toBe('Chapter 4');
368
+ expect(chapterUpdated4.startTime).toBe(20);
369
+ expect(chapterUpdated4.endTime).toBe(30);
370
+ const chapterUpdated5 = chaptersUpdated[4];
371
+ expect(chapterUpdated5.title).toBe('Chapter 5');
372
+ expect(chapterUpdated5.startTime).toBe(30);
373
+ expect(chapterUpdated5.endTime).toBe(40);
374
+ const chapterUpdated6 = chaptersUpdated[5];
375
+ expect(chapterUpdated6.title).toBe('Chapter 6');
376
+ expect(chapterUpdated6.startTime).toBe(40);
377
+ expect(chapterUpdated6.endTime).toBe(61.349);
378
+ });
390
379
 
391
- const chapters = player.getChapters('es');
392
- expect(chapters.length).toBe(3);
393
- const chapter1 = chapters[0];
394
- expect(chapter1.title).toBe('Chapter 1');
395
- expect(chapter1.startTime).toBe(0);
396
- expect(chapter1.endTime).toBe(5);
397
- const chapter2 = chapters[1];
398
- expect(chapter2.title).toBe('Chapter 2');
399
- expect(chapter2.startTime).toBe(5);
400
- expect(chapter2.endTime).toBe(30);
401
- const chapter3 = chapters[2];
402
- expect(chapter3.title).toBe('Chapter 3');
403
- expect(chapter3.startTime).toBe(30);
404
- expect(chapter3.endTime).toBe(61.349);
405
- });
380
+ it('add external chapters in srt format', async () => {
381
+ await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
382
+
383
+ const locationUri = new goog.Uri(location.href);
384
+ const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
385
+ const absoluteUri = locationUri.resolve(partialUri);
386
+ await player.addChaptersTrack(absoluteUri.toString(), 'es');
387
+
388
+ const chapters = player.getChapters('es');
389
+ expect(chapters.length).toBe(3);
390
+ const chapter1 = chapters[0];
391
+ expect(chapter1.title).toBe('Chapter 1');
392
+ expect(chapter1.startTime).toBe(0);
393
+ expect(chapter1.endTime).toBe(5);
394
+ const chapter2 = chapters[1];
395
+ expect(chapter2.title).toBe('Chapter 2');
396
+ expect(chapter2.startTime).toBe(5);
397
+ expect(chapter2.endTime).toBe(30);
398
+ const chapter3 = chapters[2];
399
+ expect(chapter3.title).toBe('Chapter 3');
400
+ expect(chapter3.startTime).toBe(30);
401
+ expect(chapter3.endTime).toBe(61.349);
402
+ });
403
+ }); // describe('addChaptersTrack')
406
404
 
407
405
  // Since we are not in-charge of streaming, calling |retryStreaming| should
408
406
  // have no effect.