shaka-player 3.3.3 → 3.3.6

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 (76) hide show
  1. package/.github/workflows/build-and-test.yaml +0 -1
  2. package/.github/workflows/release-please.yaml +6 -3
  3. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  4. package/CHANGELOG.md +38 -0
  5. package/README.md +4 -4
  6. package/build/README.md +2 -2
  7. package/build/test.py +7 -1
  8. package/build/types/core +0 -1
  9. package/demo/demo.less +0 -1
  10. package/dist/controls.css +3 -3
  11. package/dist/controls.css.map +1 -1
  12. package/dist/demo.css +1 -1
  13. package/dist/demo.css.map +1 -1
  14. package/dist/deps.js +2 -3
  15. package/dist/locales.js +31 -31
  16. package/dist/shaka-player.compiled.d.ts +1 -0
  17. package/dist/shaka-player.compiled.debug.d.ts +1 -0
  18. package/dist/shaka-player.compiled.debug.externs.js +1 -0
  19. package/dist/shaka-player.compiled.debug.js +152 -150
  20. package/dist/shaka-player.compiled.debug.map +4 -4
  21. package/dist/shaka-player.compiled.externs.js +1 -0
  22. package/dist/shaka-player.compiled.js +844 -842
  23. package/dist/shaka-player.compiled.map +4 -4
  24. package/dist/shaka-player.ui.d.ts +1 -0
  25. package/dist/shaka-player.ui.debug.d.ts +1 -0
  26. package/dist/shaka-player.ui.debug.externs.js +1 -0
  27. package/dist/shaka-player.ui.debug.js +224 -222
  28. package/dist/shaka-player.ui.debug.map +4 -4
  29. package/dist/shaka-player.ui.externs.js +1 -0
  30. package/dist/shaka-player.ui.js +952 -950
  31. package/dist/shaka-player.ui.map +4 -4
  32. package/karma.conf.js +4 -0
  33. package/lib/abr/simple_abr_manager.js +2 -1
  34. package/lib/dash/dash_parser.js +2 -3
  35. package/lib/hls/hls_parser.js +37 -14
  36. package/lib/media/drm_engine.js +3 -0
  37. package/lib/media/media_source_engine.js +7 -1
  38. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  39. package/lib/offline/storage.js +92 -91
  40. package/lib/player.js +165 -107
  41. package/lib/util/config_utils.js +25 -0
  42. package/lib/util/error.js +7 -0
  43. package/lib/util/manifest_parser_utils.js +1 -1
  44. package/lib/util/mime_utils.js +62 -6
  45. package/lib/util/periods.js +5 -4
  46. package/lib/util/player_configuration.js +9 -14
  47. package/lib/util/stream_utils.js +4 -2
  48. package/package.json +2 -2
  49. package/test/cast/cast_utils_unit.js +1 -0
  50. package/test/dash/dash_parser_manifest_unit.js +0 -24
  51. package/test/hls/hls_live_unit.js +86 -7
  52. package/test/hls/manifest_text_parser_unit.js +19 -0
  53. package/test/media/media_source_engine_integration.js +1 -0
  54. package/test/media/media_source_engine_unit.js +4 -4
  55. package/test/media/streaming_engine_integration.js +2 -0
  56. package/test/offline/indexeddb_storage_unit.js +1 -1
  57. package/test/offline/storage_compatibility_unit.js +1 -1
  58. package/test/player_external.js +7 -1
  59. package/test/player_integration.js +6 -10
  60. package/test/player_src_equals_integration.js +81 -83
  61. package/test/player_unit.js +98 -1
  62. package/test/test/boot.js +9 -0
  63. package/test/test/externs/filters.js +4 -0
  64. package/test/test/util/simple_fakes.js +31 -2
  65. package/test/util/mime_utils_unit.js +54 -0
  66. package/test/util/periods_unit.js +79 -0
  67. package/ui/less/ad_controls.less +2 -2
  68. package/ui/less/buttons.less +1 -1
  69. package/ui/less/containers.less +5 -5
  70. package/ui/less/general.less +1 -1
  71. package/ui/less/range_elements.less +3 -2
  72. package/ui/less/tooltip.less +3 -3
  73. package/ui/seek_bar.js +2 -1
  74. package/.github/pull_request_template.md +0 -10
  75. package/docs/tutorials/gsoc-ideas-list.md +0 -3
  76. package/lib/util/mutex.js +0 -52
@@ -573,8 +573,8 @@ shaka.util.PeriodCombiner = class {
573
573
  // TODO(#1528): Consider changing this when we support codec switching.
574
574
  const hasCodec = outputStreams.some((s) => {
575
575
  return s.mimeType == stream.mimeType &&
576
- shaka.util.MimeUtils.getCodecBase(s.codecs) ==
577
- shaka.util.MimeUtils.getCodecBase(stream.codecs);
576
+ shaka.util.MimeUtils.getNormalizedCodec(s.codecs) ==
577
+ shaka.util.MimeUtils.getNormalizedCodec(stream.codecs);
578
578
  });
579
579
  if (!hasCodec) {
580
580
  continue;
@@ -1050,10 +1050,11 @@ shaka.util.PeriodCombiner = class {
1050
1050
  * @private
1051
1051
  */
1052
1052
  static areAVStreamsCompatible_(outputStream, candidate) {
1053
- const getCodecBase = (codecs) => shaka.util.MimeUtils.getCodecBase(codecs);
1053
+ const getCodec = (codecs) =>
1054
+ shaka.util.MimeUtils.getNormalizedCodec(codecs);
1054
1055
  // Check MIME type and codecs, which should always be the same.
1055
1056
  if (candidate.mimeType != outputStream.mimeType ||
1056
- getCodecBase(candidate.codecs) != getCodecBase(outputStream.codecs)) {
1057
+ getCodec(candidate.codecs) != getCodec(outputStream.codecs)) {
1057
1058
  return false;
1058
1059
  }
1059
1060
 
@@ -17,14 +17,6 @@ goog.require('shaka.util.ManifestParserUtils');
17
17
  goog.require('shaka.util.Platform');
18
18
 
19
19
 
20
- // TODO(vaage): Many times in our configs, we need to create an empty
21
- // implementation of a method, but to avoid closure from removing unused
22
- // parameters (and breaking our merge config code) we need to use each
23
- // parameter. Is there a better solution to this problem than what we are
24
- // doing now?
25
- //
26
- // NOTE: Chrome App Content Security Policy prohibits usage of new Function()
27
-
28
20
  /**
29
21
  * @final
30
22
  * @export
@@ -109,11 +101,10 @@ shaka.util.PlayerConfiguration = class {
109
101
  'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb':
110
102
  'com.adobe.primetime',
111
103
  },
112
- // Need some operation in the callback or else closure may remove calls
113
- // to the function as it would be a no-op. The operation can't just be
114
- // a log message, because those are stripped in the compiled build.
115
104
  manifestPreprocessor: (element) => {
116
- return element;
105
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
106
+ [element],
107
+ element);
117
108
  },
118
109
  },
119
110
  hls: {
@@ -132,7 +123,9 @@ shaka.util.PlayerConfiguration = class {
132
123
  // log message, because those are stripped in the compiled build.
133
124
  failureCallback: (error) => {
134
125
  shaka.log.error('Unhandled streaming error', error);
135
- return [error];
126
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
127
+ [error],
128
+ undefined);
136
129
  },
137
130
  // When low latency streaming is enabled, rebufferingGoal will default to
138
131
  // 0.01 if not specified.
@@ -210,7 +203,9 @@ shaka.util.PlayerConfiguration = class {
210
203
  // to the function as it would be a no-op. The operation can't just be a
211
204
  // log message, because those are stripped in the compiled build.
212
205
  progressCallback: (content, progress) => {
213
- return [content, progress];
206
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
207
+ [content, progress],
208
+ undefined);
214
209
  },
215
210
 
216
211
  // By default we use persistent licenses as forces errors to surface if
@@ -283,12 +283,14 @@ shaka.util.StreamUtils = class {
283
283
  // both be considered the same codec: avc1.42c01e, avc1.4d401f
284
284
  let baseVideoCodec = '';
285
285
  if (variant.video) {
286
- baseVideoCodec = shaka.util.MimeUtils.getCodecBase(variant.video.codecs);
286
+ baseVideoCodec =
287
+ shaka.util.MimeUtils.getNormalizedCodec(variant.video.codecs);
287
288
  }
288
289
 
289
290
  let baseAudioCodec = '';
290
291
  if (variant.audio) {
291
- baseAudioCodec = shaka.util.MimeUtils.getCodecBase(variant.audio.codecs);
292
+ baseAudioCodec =
293
+ shaka.util.MimeUtils.getNormalizedCodec(variant.audio.codecs);
292
294
  }
293
295
 
294
296
  return baseVideoCodec + '-' + baseAudioCodec;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "3.3.3",
4
+ "version": "3.3.6",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -50,7 +50,7 @@
50
50
  "less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
51
51
  "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
52
52
  "material-design-lite": "^1.3.0",
53
- "mux.js": "^5.14.1",
53
+ "mux.js": "^6.1.0",
54
54
  "open-sans-fonts": "^1.6.2",
55
55
  "postcss-less": "^6.0.0",
56
56
  "pwacompat": "^2.0.17",
@@ -189,6 +189,7 @@ describe('CastUtils', () => {
189
189
  const fakeVideoStream = {
190
190
  mimeType: 'video/mp4',
191
191
  codecs: 'avc1.42c01e',
192
+ drmInfos: [],
192
193
  };
193
194
  const initSegmentUrl = '/base/test/test/assets/sintel-video-init.mp4';
194
195
  const videoSegmentUrl =
@@ -447,30 +447,6 @@ describe('DashParser Manifest', () => {
447
447
  expect(stream.closedCaptions).toEqual(expectedClosedCaptions);
448
448
  });
449
449
 
450
- it('Detects E-AC3 JOC content by SupplementalProperty', async () => {
451
- const idUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
452
- const source = [
453
- '<MPD>',
454
- ' <Period duration="PT30M">',
455
- ' <AdaptationSet mimeType="audio/mp4" lang="\u2603">',
456
- ' <Representation bandwidth="500">',
457
- ' <SupplementalProperty schemeIdUri="' + idUri + '" value="JOC"/>',
458
- ' <BaseURL>http://example.com</BaseURL>',
459
- ' <SegmentTemplate media="2.mp4" duration="1" />',
460
- ' </Representation>',
461
- ' </AdaptationSet>',
462
- ' </Period>',
463
- '</MPD>',
464
- ].join('\n');
465
-
466
- fakeNetEngine.setResponseText('dummy://foo', source);
467
-
468
- /** @type {shaka.extern.Manifest} */
469
- const manifest = await parser.start('dummy://foo', playerInterface);
470
- const stream = manifest.variants[0].audio;
471
- expect(stream.mimeType).toBe('audio/eac3-joc');
472
- });
473
-
474
450
  it('Detects spatial audio', async () => {
475
451
  const idUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
476
452
  const source = [
@@ -632,11 +632,13 @@ describe('HlsParser live', () => {
632
632
  expect(ref.startTime).not.toBeLessThan(rolloverOffset);
633
633
  });
634
634
 
635
+ // Test for https://github.com/shaka-project/shaka-player/issues/4223
635
636
  it('parses streams with partial and preload hinted segments', async () => {
636
637
  playerInterface.isLowLatencyMode = () => true;
637
638
  const mediaWithPartialSegments = [
638
639
  '#EXTM3U\n',
639
640
  '#EXT-X-TARGETDURATION:5\n',
641
+ '#EXT-X-PART-INF:PART-TARGET=1.5\n',
640
642
  '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
641
643
  '#EXT-X-MEDIA-SEQUENCE:0\n',
642
644
  // ref includes partialRef, partialRef2
@@ -670,6 +672,11 @@ describe('HlsParser live', () => {
670
672
  segmentDataStartTime + 4, /* baseUri= */ '', /* startByte= */ 200,
671
673
  /* endByte= */ 429);
672
674
 
675
+ const ref = ManifestParser.makeReference(
676
+ 'test:/main.mp4', segmentDataStartTime, segmentDataStartTime + 4,
677
+ /* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 429,
678
+ /* timestampOffset= */ 0, [partialRef, partialRef2]);
679
+
673
680
  const partialRef3 = ManifestParser.makeReference(
674
681
  'test:/partial.mp4', segmentDataStartTime + 4,
675
682
  segmentDataStartTime + 6,
@@ -679,17 +686,12 @@ describe('HlsParser live', () => {
679
686
  // so its startTime and endTime are the same.
680
687
  const preloadRef = ManifestParser.makeReference(
681
688
  'test:/partial.mp4', segmentDataStartTime + 6,
682
- segmentDataStartTime + 6,
689
+ segmentDataStartTime + 7.5,
683
690
  /* baseUri= */ '', /* startByte= */ 210, /* endByte= */ null);
684
691
 
685
- const ref = ManifestParser.makeReference(
686
- 'test:/main.mp4', segmentDataStartTime, segmentDataStartTime + 4,
687
- /* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 429,
688
- /* timestampOffset= */ 0, [partialRef, partialRef2]);
689
-
690
692
  // ref2 is not fully published yet, so it doesn't have a segment uri.
691
693
  const ref2 = ManifestParser.makeReference(
692
- '', segmentDataStartTime + 4, segmentDataStartTime + 6,
694
+ '', segmentDataStartTime + 4, segmentDataStartTime + 7.5,
693
695
  /* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
694
696
  /* timestampOffset= */ 0, [partialRef3, preloadRef]);
695
697
 
@@ -699,6 +701,83 @@ describe('HlsParser live', () => {
699
701
  ManifestParser.verifySegmentIndex(video, [ref, ref2]);
700
702
  });
701
703
 
704
+ // Test for https://github.com/shaka-project/shaka-player/issues/4223
705
+ it('ignores preload hinted segments without target duration', async () => {
706
+ playerInterface.isLowLatencyMode = () => true;
707
+
708
+ // Missing PART-TARGET, so preload hints are skipped.
709
+ const mediaWithPartialSegments = [
710
+ '#EXTM3U\n',
711
+ '#EXT-X-TARGETDURATION:5\n',
712
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
713
+ '#EXT-X-MEDIA-SEQUENCE:0\n',
714
+ '#EXTINF:4,\n',
715
+ // ref1
716
+ 'main.mp4\n',
717
+ // ref2 includes partialRef, but not preloadRef
718
+ // partialRef
719
+ '#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
720
+ // preloadRef
721
+ '#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
722
+ ].join('');
723
+
724
+ fakeNetEngine
725
+ .setResponseText('test:/master', master)
726
+ .setResponseText('test:/video', mediaWithPartialSegments)
727
+ .setResponseValue('test:/init.mp4', initSegmentData)
728
+ .setResponseValue('test:/main.mp4', segmentData)
729
+ .setResponseValue('test:/partial.mp4', segmentData)
730
+ .setResponseValue('test:/partial2.mp4', segmentData);
731
+
732
+ const ref1 = ManifestParser.makeReference(
733
+ 'test:/main.mp4', segmentDataStartTime, segmentDataStartTime + 4,
734
+ /* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
735
+ /* timestampOffset= */ 0, []);
736
+
737
+ const partialRef = ManifestParser.makeReference(
738
+ 'test:/partial.mp4', segmentDataStartTime + 4,
739
+ segmentDataStartTime + 6,
740
+ /* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209);
741
+
742
+ // ref2 is not fully published yet, so it doesn't have a segment uri.
743
+ const ref2 = ManifestParser.makeReference(
744
+ '', segmentDataStartTime + 4, segmentDataStartTime + 6,
745
+ /* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209,
746
+ /* timestampOffset= */ 0, [partialRef]);
747
+
748
+ const manifest = await parser.start('test:/master', playerInterface);
749
+ const video = manifest.variants[0].video;
750
+ await video.createSegmentIndex();
751
+ ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
752
+ });
753
+
754
+ // Test for https://github.com/shaka-project/shaka-player/issues/4185
755
+ it('does not fail on preload hints with LL mode off', async () => {
756
+ // LL mode must be off for this test!
757
+ playerInterface.isLowLatencyMode = () => false;
758
+
759
+ const mediaWithPartialSegments = [
760
+ '#EXTM3U\n',
761
+ '#EXT-X-TARGETDURATION:5\n',
762
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
763
+ '#EXT-X-PART-INF:PART-TARGET=1.5\n',
764
+ '#EXTINF:4,\n',
765
+ 'main.mp4\n',
766
+ '#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
767
+ '#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
768
+ ].join('');
769
+
770
+ fakeNetEngine
771
+ .setResponseText('test:/master', master)
772
+ .setResponseText('test:/video', mediaWithPartialSegments)
773
+ .setResponseValue('test:/init.mp4', initSegmentData)
774
+ .setResponseValue('test:/main.mp4', segmentData)
775
+ .setResponseValue('test:/partial.mp4', segmentData);
776
+
777
+ // If this throws, the test fails. Otherwise, it passes.
778
+ await parser.start('test:/master', playerInterface);
779
+ });
780
+
702
781
  describe('update', () => {
703
782
  it('adds new segments when they appear', async () => {
704
783
  const ref1 = ManifestParser.makeReference('test:/main.mp4', 2, 4);
@@ -238,6 +238,25 @@ describe('ManifestTextParser', () => {
238
238
  '#EXTM3U\n' +
239
239
  '#EXT-X-MEDIA:CODECS="avc1.64002a,mp4a.40.2",AUDIO="a1,a2"',
240
240
 
241
+ // manifest URI:
242
+ 'https://test/manifest.m3u8');
243
+
244
+ verifyPlaylist(
245
+ {
246
+ type: shaka.hls.PlaylistType.MASTER,
247
+ tags: [
248
+ new shaka.hls.Tag(/* id= */ 3, 'EXT-X-MEDIA',
249
+ [
250
+ new shaka.hls.Attribute('CODECS',
251
+ 'av01.0.08M.08,mp4a.40.2'),
252
+ ]),
253
+ ],
254
+ },
255
+
256
+ // playlist text:
257
+ '#EXTM3U\n' +
258
+ '#EXT-X-MEDIA:CODECS="av01.0.08M.08,mp4a.40.2"',
259
+
241
260
  // manifest URI:
242
261
  'https://test/manifest.m3u8');
243
262
  });
@@ -103,6 +103,7 @@ describe('MediaSourceEngine', () => {
103
103
  return {
104
104
  mimeType: streamMetadata.mimeType,
105
105
  codecs: streamMetadata.codecs,
106
+ drmInfos: [],
106
107
  };
107
108
  }
108
109
 
@@ -49,10 +49,10 @@ describe('MediaSourceEngine', () => {
49
49
  const buffer2 = /** @type {!ArrayBuffer} */ (/** @type {?} */ (2));
50
50
  const buffer3 = /** @type {!ArrayBuffer} */ (/** @type {?} */ (3));
51
51
 
52
- const fakeVideoStream = {mimeType: 'video/foo'};
53
- const fakeAudioStream = {mimeType: 'audio/foo'};
54
- const fakeTextStream = {mimeType: 'text/foo'};
55
- const fakeTransportStream = {mimeType: 'tsMimetype'};
52
+ const fakeVideoStream = {mimeType: 'video/foo', drmInfos: []};
53
+ const fakeAudioStream = {mimeType: 'audio/foo', drmInfos: []};
54
+ const fakeTextStream = {mimeType: 'text/foo', drmInfos: []};
55
+ const fakeTransportStream = {mimeType: 'tsMimetype', drmInfos: []};
56
56
 
57
57
  let audioSourceBuffer;
58
58
  let videoSourceBuffer;
@@ -622,6 +622,7 @@ describe('StreamingEngine', () => {
622
622
  width: 600,
623
623
  height: 400,
624
624
  type: shaka.util.ManifestParserUtils.ContentType.VIDEO,
625
+ drmInfos: [],
625
626
  },
626
627
  audio: {
627
628
  id: 3,
@@ -631,6 +632,7 @@ describe('StreamingEngine', () => {
631
632
  codecs: 'mp4a.40.2',
632
633
  bandwidth: 192000,
633
634
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
635
+ drmInfos: [],
634
636
  },
635
637
  }],
636
638
  };
@@ -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;
@@ -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);
@@ -120,7 +120,13 @@ describe('Player', () => {
120
120
  const networkingEngine = player.getNetworkingEngine();
121
121
  asset.applyFilters(networkingEngine);
122
122
 
123
- await player.load(asset.manifestUri);
123
+ // Rather than awaiting the load() method, catch any load() errors and
124
+ // wait on the 'canplay' event. This has the advantage that we will
125
+ // get better logging of the media state on a timeout, since that
126
+ // capabilitiy is built into the waiter for media element events.
127
+ player.load(asset.manifestUri).catch(fail);
128
+ await waiter.timeoutAfter(60).waitForEvent(video, 'canplay');
129
+
124
130
  if (asset.features) {
125
131
  const isLive = asset.features.includes(Feature.LIVE);
126
132
  expect(player.isLive()).toBe(isLive);
@@ -1053,16 +1053,16 @@ describe('Player', () => {
1053
1053
  });
1054
1054
  }); // describe('unloading')
1055
1055
 
1056
- describe('chapters', () => {
1057
- it('add external chapters in vtt format', async () => {
1056
+ describe('addChaptersTrack', () => {
1057
+ it('adds external chapters in vtt format', async () => {
1058
1058
  await player.load('test:sintel_no_text_compiled');
1059
1059
  const locationUri = new goog.Uri(location.href);
1060
1060
  const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
1061
1061
  const absoluteUri1 = locationUri.resolve(partialUri1);
1062
1062
  await player.addChaptersTrack(absoluteUri1.toString(), 'en');
1063
1063
 
1064
- await shaka.test.Util.delay(1.5);
1065
-
1064
+ // Data should be available as soon as addChaptersTrack resolves.
1065
+ // See https://github.com/shaka-project/shaka-player/issues/4186
1066
1066
  const chapters = player.getChapters('en');
1067
1067
  expect(chapters.length).toBe(3);
1068
1068
  const chapter1 = chapters[0];
@@ -1082,8 +1082,6 @@ describe('Player', () => {
1082
1082
  const absoluteUri2 = locationUri.resolve(partialUri2);
1083
1083
  await player.addChaptersTrack(absoluteUri2.toString(), 'en');
1084
1084
 
1085
- await shaka.test.Util.delay(1.5);
1086
-
1087
1085
  const chaptersUpdated = player.getChapters('en');
1088
1086
  expect(chaptersUpdated.length).toBe(6);
1089
1087
  const chapterUpdated1 = chaptersUpdated[0];
@@ -1112,15 +1110,13 @@ describe('Player', () => {
1112
1110
  expect(chapterUpdated6.endTime).toBe(61.349);
1113
1111
  });
1114
1112
 
1115
- it('add external chapters in srt format', async () => {
1113
+ it('adds external chapters in srt format', async () => {
1116
1114
  await player.load('test:sintel_no_text_compiled');
1117
1115
  const locationUri = new goog.Uri(location.href);
1118
1116
  const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
1119
1117
  const absoluteUri = locationUri.resolve(partialUri);
1120
1118
  await player.addChaptersTrack(absoluteUri.toString(), 'es');
1121
1119
 
1122
- await shaka.test.Util.delay(1.5);
1123
-
1124
1120
  const chapters = player.getChapters('es');
1125
1121
  expect(chapters.length).toBe(3);
1126
1122
  const chapter1 = chapters[0];
@@ -1136,5 +1132,5 @@ describe('Player', () => {
1136
1132
  expect(chapter3.startTime).toBe(30);
1137
1133
  expect(chapter3.endTime).toBe(61.349);
1138
1134
  });
1139
- }); // describe('chapters')
1135
+ }); // describe('addChaptersTrack')
1140
1136
  });
@@ -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.