shaka-player 3.2.6 → 3.2.7

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 (59) hide show
  1. package/.github/workflows/build-and-test.yaml +0 -1
  2. package/.github/workflows/release-please.yaml +1 -1
  3. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  4. package/CHANGELOG.md +16 -0
  5. package/build/README.md +2 -2
  6. package/build/checkversion.py +1 -1
  7. package/build/generateTsDefs.py +0 -2
  8. package/build/test.py +7 -1
  9. package/dist/controls.css +1 -1
  10. package/dist/locales.js +31 -31
  11. package/dist/shaka-player.compiled.d.ts +225 -226
  12. package/dist/shaka-player.compiled.debug.d.ts +225 -226
  13. package/dist/shaka-player.compiled.debug.externs.js +1 -0
  14. package/dist/shaka-player.compiled.debug.js +217 -212
  15. package/dist/shaka-player.compiled.debug.map +3 -3
  16. package/dist/shaka-player.compiled.externs.js +1 -0
  17. package/dist/shaka-player.compiled.js +771 -767
  18. package/dist/shaka-player.compiled.map +3 -3
  19. package/dist/shaka-player.ui.d.ts +267 -268
  20. package/dist/shaka-player.ui.debug.d.ts +267 -268
  21. package/dist/shaka-player.ui.debug.externs.js +1 -0
  22. package/dist/shaka-player.ui.debug.js +138 -133
  23. package/dist/shaka-player.ui.debug.map +3 -3
  24. package/dist/shaka-player.ui.externs.js +1 -0
  25. package/dist/shaka-player.ui.js +893 -888
  26. package/dist/shaka-player.ui.map +3 -3
  27. package/karma.conf.js +4 -0
  28. package/lib/dash/dash_parser.js +2 -3
  29. package/lib/hls/hls_parser.js +23 -11
  30. package/lib/media/media_source_engine.js +7 -1
  31. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  32. package/lib/player.js +165 -107
  33. package/lib/util/config_utils.js +25 -0
  34. package/lib/util/error.js +7 -0
  35. package/lib/util/manifest_parser_utils.js +1 -1
  36. package/lib/util/mime_utils.js +48 -1
  37. package/lib/util/periods.js +5 -4
  38. package/lib/util/player_configuration.js +9 -14
  39. package/lib/util/stream_utils.js +4 -2
  40. package/package.json +1 -1
  41. package/test/cast/cast_utils_unit.js +1 -0
  42. package/test/dash/dash_parser_manifest_unit.js +0 -24
  43. package/test/hls/hls_live_unit.js +26 -0
  44. package/test/hls/manifest_text_parser_unit.js +19 -0
  45. package/test/media/media_source_engine_integration.js +1 -0
  46. package/test/media/media_source_engine_unit.js +4 -4
  47. package/test/media/streaming_engine_integration.js +2 -0
  48. package/test/offline/indexeddb_storage_unit.js +1 -1
  49. package/test/offline/storage_compatibility_unit.js +1 -1
  50. package/test/player_external.js +7 -1
  51. package/test/player_integration.js +6 -10
  52. package/test/player_src_equals_integration.js +81 -83
  53. package/test/player_unit.js +98 -1
  54. package/test/test/boot.js +9 -0
  55. package/test/test/externs/filters.js +4 -0
  56. package/test/test/util/simple_fakes.js +31 -2
  57. package/test/util/mime_utils_unit.js +54 -0
  58. package/test/util/periods_unit.js +79 -0
  59. package/docs/tutorials/gsoc-ideas-list.md +0 -3
@@ -127,4 +127,29 @@ shaka.util.ConfigUtils = class {
127
127
  last[fieldName.substring(nameStart).replace(/\\\./g, '.')] = value;
128
128
  return configObject;
129
129
  }
130
+
131
+ /**
132
+ * Reference the input parameters so the compiler doesn't remove them from
133
+ * the calling function. Return whatever value is specified.
134
+ *
135
+ * This allows an empty or default implementation of a config callback that
136
+ * still bears the complete function signature even in compiled mode.
137
+ *
138
+ * The caller should look something like this:
139
+ *
140
+ * const callback = (a, b, c, d) => {
141
+ * return referenceParametersAndReturn(
142
+ [a, b, c, d],
143
+ a); // Can be anything, doesn't need to be one of the parameters
144
+ * };
145
+ *
146
+ * @param {!Array.<?>} parameters
147
+ * @param {T} returnValue
148
+ * @return {T}
149
+ * @template T
150
+ * @noinline
151
+ */
152
+ static referenceParametersAndReturn(parameters, returnValue) {
153
+ return parameters && returnValue;
154
+ }
130
155
  };
package/lib/util/error.js CHANGED
@@ -345,6 +345,13 @@ shaka.util.Error.Code = {
345
345
  */
346
346
  'MISSING_TEXT_PLUGIN': 2014,
347
347
 
348
+ /**
349
+ * The chapters track failed to load. The browser does not provide any
350
+ * information in this case to identify why it failed, but there may be
351
+ * details in the JavaScript console.
352
+ */
353
+ 'CHAPTERS_TRACK_FAILED': 2015,
354
+
348
355
  /**
349
356
  * Some component tried to read past the end of a buffer. The segment index,
350
357
  * init segment, or PSSH may be malformed.
@@ -167,7 +167,7 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
167
167
  /^hev/,
168
168
  /^hvc/,
169
169
  /^vp0?[89]/,
170
- /^av1$/,
170
+ /^av01/,
171
171
  ];
172
172
 
173
173
 
@@ -94,6 +94,53 @@ shaka.util.MimeUtils = class {
94
94
  return codecs.split(',');
95
95
  }
96
96
 
97
+ /**
98
+ * Get the normalized codec from a codec string,
99
+ * independently of their container.
100
+ *
101
+ * @param {string} codecString
102
+ * @return {string}
103
+ */
104
+ static getNormalizedCodec(codecString) {
105
+ const parts =
106
+ shaka.util.MimeUtils.getCodecParts_(codecString);
107
+ const base = parts[0];
108
+ const profile = parts[1].toLowerCase();
109
+ switch (true) {
110
+ case base === 'mp4a' && profile === '69':
111
+ case base === 'mp4a' && profile === '6b':
112
+ return 'mp3';
113
+ case base === 'mp4a' && profile === '66':
114
+ case base === 'mp4a' && profile === '67':
115
+ case base === 'mp4a' && profile === '68':
116
+ case base === 'mp4a' && profile === '40.2':
117
+ case base === 'mp4a' && profile === '40.02':
118
+ case base === 'mp4a' && profile === '40.5':
119
+ case base === 'mp4a' && profile === '40.05':
120
+ case base === 'mp4a' && profile === '40.29':
121
+ case base === 'mp4a' && profile === '40.42': // Extended HE-AAC
122
+ return 'aac';
123
+ case base === 'mp4a' && profile === 'a5':
124
+ return 'ac-3'; // Dolby Digital
125
+ case base === 'mp4a' && profile === 'a6':
126
+ return 'ec-3'; // Dolby Digital Plus
127
+ case base === 'mp4a' && profile === 'b2':
128
+ return 'dtsx'; // DTS:X
129
+ case base === 'mp4a' && profile === 'a9':
130
+ return 'dtsc'; // DTS Digital Surround
131
+ case base === 'avc1':
132
+ case base === 'avc3':
133
+ return 'avc'; // H264
134
+ case base === 'hvc1':
135
+ case base === 'hev1':
136
+ return 'hevc'; // H265
137
+ case base === 'dvh1':
138
+ case base === 'dvhe':
139
+ return 'dovi'; // Dolby Vision
140
+ }
141
+ return base;
142
+ }
143
+
97
144
  /**
98
145
  * Get the base codec from a codec string.
99
146
  *
@@ -150,7 +197,7 @@ shaka.util.MimeUtils = class {
150
197
 
151
198
  const base = parts[0];
152
199
 
153
- parts.pop();
200
+ parts.shift();
154
201
  const profile = parts.join('.');
155
202
 
156
203
  // Make sure that we always return a "base" and "profile".
@@ -574,8 +574,8 @@ shaka.util.PeriodCombiner = class {
574
574
  // TODO(#1528): Consider changing this when we support codec switching.
575
575
  const hasCodec = outputStreams.some((s) => {
576
576
  return s.mimeType == stream.mimeType &&
577
- shaka.util.MimeUtils.getCodecBase(s.codecs) ==
578
- shaka.util.MimeUtils.getCodecBase(stream.codecs);
577
+ shaka.util.MimeUtils.getNormalizedCodec(s.codecs) ==
578
+ shaka.util.MimeUtils.getNormalizedCodec(stream.codecs);
579
579
  });
580
580
  if (!hasCodec) {
581
581
  continue;
@@ -1016,10 +1016,11 @@ shaka.util.PeriodCombiner = class {
1016
1016
  * @private
1017
1017
  */
1018
1018
  static areAVStreamsCompatible_(outputStream, candidate) {
1019
- const getCodecBase = (codecs) => shaka.util.MimeUtils.getCodecBase(codecs);
1019
+ const getCodec = (codecs) =>
1020
+ shaka.util.MimeUtils.getNormalizedCodec(codecs);
1020
1021
  // Check MIME type and codecs, which should always be the same.
1021
1022
  if (candidate.mimeType != outputStream.mimeType ||
1022
- getCodecBase(candidate.codecs) != getCodecBase(outputStream.codecs)) {
1023
+ getCodec(candidate.codecs) != getCodec(outputStream.codecs)) {
1023
1024
  return false;
1024
1025
  }
1025
1026
 
@@ -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: {
@@ -130,7 +121,9 @@ shaka.util.PlayerConfiguration = class {
130
121
  // log message, because those are stripped in the compiled build.
131
122
  failureCallback: (error) => {
132
123
  shaka.log.error('Unhandled streaming error', error);
133
- return [error];
124
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
125
+ [error],
126
+ undefined);
134
127
  },
135
128
  // When low latency streaming is enabled, rebufferingGoal will default to
136
129
  // 0.01 if not specified.
@@ -206,7 +199,9 @@ shaka.util.PlayerConfiguration = class {
206
199
  // to the function as it would be a no-op. The operation can't just be a
207
200
  // log message, because those are stripped in the compiled build.
208
201
  progressCallback: (content, progress) => {
209
- return [content, progress];
202
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
203
+ [content, progress],
204
+ undefined);
210
205
  },
211
206
 
212
207
  // 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.2.6",
4
+ "version": "3.2.7",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -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 =
@@ -445,30 +445,6 @@ describe('DashParser Manifest', () => {
445
445
  expect(stream.closedCaptions).toEqual(expectedClosedCaptions);
446
446
  });
447
447
 
448
- it('Detects E-AC3 JOC content by SupplementalProperty', async () => {
449
- const idUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
450
- const source = [
451
- '<MPD>',
452
- ' <Period duration="PT30M">',
453
- ' <AdaptationSet mimeType="audio/mp4" lang="\u2603">',
454
- ' <Representation bandwidth="500">',
455
- ' <SupplementalProperty schemeIdUri="' + idUri + '" value="JOC"/>',
456
- ' <BaseURL>http://example.com</BaseURL>',
457
- ' <SegmentTemplate media="2.mp4" duration="1" />',
458
- ' </Representation>',
459
- ' </AdaptationSet>',
460
- ' </Period>',
461
- '</MPD>',
462
- ].join('\n');
463
-
464
- fakeNetEngine.setResponseText('dummy://foo', source);
465
-
466
- /** @type {shaka.extern.Manifest} */
467
- const manifest = await parser.start('dummy://foo', playerInterface);
468
- const stream = manifest.variants[0].audio;
469
- expect(stream.mimeType).toBe('audio/eac3-joc');
470
- });
471
-
472
448
  it('Detects spatial audio', async () => {
473
449
  const idUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
474
450
  const source = [
@@ -697,6 +697,32 @@ describe('HlsParser live', () => {
697
697
  ManifestParser.verifySegmentIndex(video, [ref, ref2]);
698
698
  });
699
699
 
700
+ // Test for https://github.com/shaka-project/shaka-player/issues/4185
701
+ it('does not fail on preload hints with LL mode off', async () => {
702
+ // LL mode must be off for this test!
703
+ playerInterface.isLowLatencyMode = () => false;
704
+
705
+ const mediaWithPartialSegments = [
706
+ '#EXTM3U\n',
707
+ '#EXT-X-TARGETDURATION:5\n',
708
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
709
+ '#EXTINF:4,\n',
710
+ 'main.mp4\n',
711
+ '#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
712
+ '#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
713
+ ].join('');
714
+
715
+ fakeNetEngine
716
+ .setResponseText('test:/master', master)
717
+ .setResponseText('test:/video', mediaWithPartialSegments)
718
+ .setResponseValue('test:/init.mp4', initSegmentData)
719
+ .setResponseValue('test:/main.mp4', segmentData)
720
+ .setResponseValue('test:/partial.mp4', segmentData);
721
+
722
+ // If this throws, the test fails. Otherwise, it passes.
723
+ await parser.start('test:/master', playerInterface);
724
+ });
725
+
700
726
  describe('update', () => {
701
727
  it('adds new segments when they appear', async () => {
702
728
  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;
@@ -621,6 +621,7 @@ describe('StreamingEngine', () => {
621
621
  width: 600,
622
622
  height: 400,
623
623
  type: shaka.util.ManifestParserUtils.ContentType.VIDEO,
624
+ drmInfos: [],
624
625
  },
625
626
  audio: {
626
627
  id: 3,
@@ -630,6 +631,7 @@ describe('StreamingEngine', () => {
630
631
  codecs: 'mp4a.40.2',
631
632
  bandwidth: 192000,
632
633
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
634
+ drmInfos: [],
633
635
  },
634
636
  }],
635
637
  };
@@ -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);
@@ -953,16 +953,16 @@ describe('Player', () => {
953
953
  });
954
954
  }); // describe('unloading')
955
955
 
956
- describe('chapters', () => {
957
- it('add external chapters in vtt format', async () => {
956
+ describe('addChaptersTrack', () => {
957
+ it('adds external chapters in vtt format', async () => {
958
958
  await player.load('test:sintel_no_text_compiled');
959
959
  const locationUri = new goog.Uri(location.href);
960
960
  const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
961
961
  const absoluteUri1 = locationUri.resolve(partialUri1);
962
962
  await player.addChaptersTrack(absoluteUri1.toString(), 'en');
963
963
 
964
- await shaka.test.Util.delay(1.5);
965
-
964
+ // Data should be available as soon as addChaptersTrack resolves.
965
+ // See https://github.com/shaka-project/shaka-player/issues/4186
966
966
  const chapters = player.getChapters('en');
967
967
  expect(chapters.length).toBe(3);
968
968
  const chapter1 = chapters[0];
@@ -982,8 +982,6 @@ describe('Player', () => {
982
982
  const absoluteUri2 = locationUri.resolve(partialUri2);
983
983
  await player.addChaptersTrack(absoluteUri2.toString(), 'en');
984
984
 
985
- await shaka.test.Util.delay(1.5);
986
-
987
985
  const chaptersUpdated = player.getChapters('en');
988
986
  expect(chaptersUpdated.length).toBe(6);
989
987
  const chapterUpdated1 = chaptersUpdated[0];
@@ -1012,15 +1010,13 @@ describe('Player', () => {
1012
1010
  expect(chapterUpdated6.endTime).toBe(61.349);
1013
1011
  });
1014
1012
 
1015
- it('add external chapters in srt format', async () => {
1013
+ it('adds external chapters in srt format', async () => {
1016
1014
  await player.load('test:sintel_no_text_compiled');
1017
1015
  const locationUri = new goog.Uri(location.href);
1018
1016
  const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
1019
1017
  const absoluteUri = locationUri.resolve(partialUri);
1020
1018
  await player.addChaptersTrack(absoluteUri.toString(), 'es');
1021
1019
 
1022
- await shaka.test.Util.delay(1.5);
1023
-
1024
1020
  const chapters = player.getChapters('es');
1025
1021
  expect(chapters.length).toBe(3);
1026
1022
  const chapter1 = chapters[0];
@@ -1036,5 +1032,5 @@ describe('Player', () => {
1036
1032
  expect(chapter3.startTime).toBe(30);
1037
1033
  expect(chapter3.endTime).toBe(61.349);
1038
1034
  });
1039
- }); // describe('chapters')
1035
+ }); // describe('addChaptersTrack')
1040
1036
  });
@@ -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.