shaka-player 4.2.4 → 4.2.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 (70) hide show
  1. package/.github/workflows/build-and-test.yaml +13 -0
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +35 -0
  4. package/CONTRIBUTORS +2 -0
  5. package/demo/index.html +1 -1
  6. package/dist/demo.compiled.debug.js +64 -64
  7. package/dist/demo.compiled.debug.map +2 -2
  8. package/dist/demo.compiled.js +64 -64
  9. package/dist/demo.compiled.map +2 -2
  10. package/dist/deps.js +3 -2
  11. package/dist/locales.js +30 -30
  12. package/dist/receiver.compiled.debug.js +12 -12
  13. package/dist/receiver.compiled.debug.map +2 -2
  14. package/dist/receiver.compiled.js +12 -12
  15. package/dist/receiver.compiled.map +2 -2
  16. package/dist/shaka-player.compiled.d.ts +10 -0
  17. package/dist/shaka-player.compiled.debug.d.ts +10 -0
  18. package/dist/shaka-player.compiled.debug.externs.js +93 -79
  19. package/dist/shaka-player.compiled.debug.js +585 -577
  20. package/dist/shaka-player.compiled.debug.map +4 -4
  21. package/dist/shaka-player.compiled.externs.js +93 -79
  22. package/dist/shaka-player.compiled.js +816 -814
  23. package/dist/shaka-player.compiled.map +4 -4
  24. package/dist/shaka-player.ui.d.ts +10 -0
  25. package/dist/shaka-player.ui.debug.d.ts +10 -0
  26. package/dist/shaka-player.ui.debug.externs.js +93 -79
  27. package/dist/shaka-player.ui.debug.js +615 -606
  28. package/dist/shaka-player.ui.debug.map +4 -4
  29. package/dist/shaka-player.ui.externs.js +93 -79
  30. package/dist/shaka-player.ui.js +964 -962
  31. package/dist/shaka-player.ui.map +4 -4
  32. package/docs/tutorials/ui.md +7 -7
  33. package/docs/tutorials/welcome.md +2 -2
  34. package/externs/shaka/player.js +2 -1
  35. package/lib/ads/ad_manager.js +2 -2
  36. package/lib/cea/cea708_window.js +2 -2
  37. package/lib/dash/segment_template.js +1 -1
  38. package/lib/hls/hls_parser.js +24 -6
  39. package/lib/media/content_workarounds.js +12 -0
  40. package/lib/media/media_source_engine.js +16 -0
  41. package/lib/media/segment_reference.js +3 -0
  42. package/lib/media/streaming_engine.js +33 -10
  43. package/lib/player.js +6 -2
  44. package/lib/polyfill/abort_controller.js +143 -0
  45. package/lib/polyfill/media_capabilities.js +59 -9
  46. package/lib/text/ttml_text_parser.js +32 -7
  47. package/lib/text/ui_text_displayer.js +22 -5
  48. package/lib/util/platform.js +9 -1
  49. package/lib/util/player_configuration.js +10 -3
  50. package/lib/util/stream_utils.js +30 -10
  51. package/package.json +1 -1
  52. package/shaka-player.uncompiled.js +1 -0
  53. package/test/dash/dash_parser_manifest_unit.js +1 -0
  54. package/test/hls/hls_parser_unit.js +1 -0
  55. package/test/media/drm_engine_unit.js +11 -1
  56. package/test/media/media_source_engine_integration.js +78 -2
  57. package/test/player_integration.js +10 -3
  58. package/test/polyfill/media_capabilities_unit.js +261 -0
  59. package/test/test/boot.js +29 -11
  60. package/test/test/util/manifest_parser_util.js +4 -3
  61. package/test/test/util/stream_generator.js +212 -8
  62. package/test/test/util/test_scheme.js +5 -4
  63. package/test/test/util/util.js +3 -2
  64. package/test/text/mp4_ttml_parser_unit.js +6 -4
  65. package/test/text/ttml_text_parser_unit.js +119 -86
  66. package/test/text/ui_text_displayer_unit.js +76 -0
  67. package/test/ui/text_displayer_layout_unit.js +14 -0
  68. package/test/util/stream_utils_unit.js +18 -0
  69. package/test/util/xml_utils_unit.js +6 -1
  70. package/ui/controls.js +49 -36
@@ -136,7 +136,7 @@ shaka.text.TtmlTextParser = class {
136
136
  }
137
137
 
138
138
  const cue = TtmlTextParser.parseCue_(
139
- body, time.periodStart, rateInfo, metadataElements, styles,
139
+ body, time, rateInfo, metadataElements, styles,
140
140
  regionElements, cueRegions, whitespaceTrim,
141
141
  cellResolutionInfo, /* parentCueElement= */ null,
142
142
  /* isContent= */ false);
@@ -157,7 +157,7 @@ shaka.text.TtmlTextParser = class {
157
157
  * Parses a TTML node into a Cue.
158
158
  *
159
159
  * @param {!Node} cueNode
160
- * @param {number} offset
160
+ * @param {shaka.extern.TextParser.TimeContext} timeContext
161
161
  * @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
162
162
  * @param {!Array.<!Element>} metadataElements
163
163
  * @param {!Array.<!Element>} styles
@@ -171,7 +171,7 @@ shaka.text.TtmlTextParser = class {
171
171
  * @private
172
172
  */
173
173
  static parseCue_(
174
- cueNode, offset, rateInfo, metadataElements, styles, regionElements,
174
+ cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
175
175
  cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent) {
176
176
  /** @type {Element} */
177
177
  let cueElement;
@@ -236,7 +236,7 @@ shaka.text.TtmlTextParser = class {
236
236
  for (const childNode of cueElement.childNodes) {
237
237
  const nestedCue = shaka.text.TtmlTextParser.parseCue_(
238
238
  childNode,
239
- offset,
239
+ timeContext,
240
240
  rateInfo,
241
241
  metadataElements,
242
242
  styles,
@@ -293,15 +293,20 @@ shaka.text.TtmlTextParser = class {
293
293
  if (start == null) {
294
294
  start = 0;
295
295
  }
296
- start += offset;
296
+ start += timeContext.periodStart;
297
297
 
298
298
  // If end is null, that means the duration is effectively infinite.
299
299
  if (end == null) {
300
300
  end = Infinity;
301
301
  } else {
302
- end += offset;
302
+ end += timeContext.periodStart;
303
303
  }
304
304
 
305
+ // Clip times to segment boundaries.
306
+ // https://github.com/shaka-project/shaka-player/issues/4631
307
+ start = Math.max(start, timeContext.segmentStart);
308
+ end = Math.min(end, timeContext.segmentEnd);
309
+
305
310
  if (!hasTimeAttributes && nestedCues.length > 0) {
306
311
  // If no time is defined for this cue, base the timing information on
307
312
  // the time of the nested cues. In the case of multiple nested cues with
@@ -569,8 +574,28 @@ shaka.text.TtmlTextParser = class {
569
574
 
570
575
  const fontFamily = TtmlTextParser.getStyleAttribute_(
571
576
  cueElement, region, styles, 'fontFamily', shouldInheritRegionStyles);
577
+ // See https://github.com/sandflow/imscJS/blob/1.1.3/src/main/js/html.js#L1384
572
578
  if (fontFamily) {
573
- cue.fontFamily = fontFamily;
579
+ switch (fontFamily) {
580
+ case 'monospaceSerif':
581
+ cue.fontFamily = 'Courier New,Liberation Mono,Courier,monospace';
582
+ break;
583
+ case 'proportionalSansSerif':
584
+ cue.fontFamily = 'Arial,Helvetica,Liberation Sans,sans-serif';
585
+ break;
586
+ case 'sansSerif':
587
+ cue.fontFamily = 'sans-serif';
588
+ break;
589
+ case 'monospaceSansSerif':
590
+ cue.fontFamily = 'Consolas,monospace';
591
+ break;
592
+ case 'proportionalSerif':
593
+ cue.fontFamily = 'serif';
594
+ break;
595
+ default:
596
+ cue.fontFamily = fontFamily;
597
+ break;
598
+ }
574
599
  }
575
600
 
576
601
  const fontWeight = TtmlTextParser.getStyleAttribute_(
@@ -199,6 +199,19 @@ shaka.text.UITextDisplayer = class {
199
199
  this.isTextVisible_ = on;
200
200
  }
201
201
 
202
+ /**
203
+ * @private
204
+ */
205
+ isElementUnderTextContainer_(elemToCheck) {
206
+ while (elemToCheck != null) {
207
+ if (elemToCheck == this.textContainer_) {
208
+ return true;
209
+ }
210
+ elemToCheck = elemToCheck.parentElement;
211
+ }
212
+ return false;
213
+ }
214
+
202
215
  /**
203
216
  * @param {!Array.<!shaka.extern.Cue>} cues
204
217
  * @param {!HTMLElement} container
@@ -260,6 +273,9 @@ shaka.text.UITextDisplayer = class {
260
273
  cueRegistry = this.currentCuesMap_.get(cue);
261
274
  wrapper = cueRegistry.wrapper;
262
275
  updateDOM = true;
276
+ } else if (!this.isElementUnderTextContainer_(wrapper)) {
277
+ // We found that the wrapper needs to be in the DOM
278
+ updateDOM = true;
263
279
  }
264
280
  }
265
281
 
@@ -276,6 +292,7 @@ shaka.text.UITextDisplayer = class {
276
292
  const topCue = parents.pop();
277
293
  goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
278
294
  }
295
+
279
296
  if (updateDOM) {
280
297
  for (const element of toUproot) {
281
298
  // NOTE: Because we uproot shared region elements, too, we might hit an
@@ -578,7 +595,7 @@ shaka.text.UITextDisplayer = class {
578
595
  // (for vertical growing left) is aligned at the line.
579
596
  // TODO: Implement line alignment with line number.
580
597
  // TODO: Implement lineAlignment of 'CENTER'.
581
- if (cue.line) {
598
+ if (cue.line != null) {
582
599
  if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
583
600
  // When setting absolute positioning, you need to set x/y/width/height
584
601
  // so the element is positioned correctly. Set these as default and
@@ -591,21 +608,21 @@ shaka.text.UITextDisplayer = class {
591
608
  if (cue.lineAlign == Cue.lineAlign.START) {
592
609
  style.top = cue.line + '%';
593
610
  } else if (cue.lineAlign == Cue.lineAlign.END) {
594
- style.bottom = cue.line + '%';
611
+ style.bottom = (100 - cue.line) + '%';
595
612
  }
596
613
  } else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
597
614
  style.height = '100%';
598
615
  if (cue.lineAlign == Cue.lineAlign.START) {
599
616
  style.left = cue.line + '%';
600
617
  } else if (cue.lineAlign == Cue.lineAlign.END) {
601
- style.right = cue.line + '%';
618
+ style.right = (100 - cue.line) + '%';
602
619
  }
603
620
  } else {
604
621
  style.height = '100%';
605
622
  if (cue.lineAlign == Cue.lineAlign.START) {
606
623
  style.right = cue.line + '%';
607
624
  } else if (cue.lineAlign == Cue.lineAlign.END) {
608
- style.left = cue.line + '%';
625
+ style.left = (100 - cue.line) + '%';
609
626
  }
610
627
  }
611
628
  }
@@ -615,7 +632,7 @@ shaka.text.UITextDisplayer = class {
615
632
 
616
633
  // The position defines the indent of the text container in the
617
634
  // direction defined by the writing direction.
618
- if (cue.position) {
635
+ if (cue.position != null) {
619
636
  if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
620
637
  style.paddingLeft = cue.position;
621
638
  } else {
@@ -174,7 +174,7 @@ shaka.util.Platform = class {
174
174
  * @return {?number} A major version number or null if not Chromium-based.
175
175
  */
176
176
  static chromeVersion() {
177
- if (!shaka.util.Platform.userAgentContains_('Chrome')) {
177
+ if (!shaka.util.Platform.isChrome()) {
178
178
  return null;
179
179
  }
180
180
 
@@ -213,6 +213,7 @@ shaka.util.Platform = class {
213
213
  return !!navigator.vendor && navigator.vendor.includes('Apple') &&
214
214
  !shaka.util.Platform.isTizen() &&
215
215
  !shaka.util.Platform.isEOS() &&
216
+ !shaka.util.Platform.isVirginMedia() &&
216
217
  !shaka.util.Platform.isPS4() &&
217
218
  !shaka.util.Platform.isAmazonFireTV();
218
219
  }
@@ -237,6 +238,13 @@ shaka.util.Platform = class {
237
238
  return shaka.util.Platform.userAgentContains_('PlayStation 4');
238
239
  }
239
240
 
241
+ /**
242
+ * Check if the current platform is Virgin Media device.
243
+ */
244
+ static isVirginMedia() {
245
+ return shaka.util.Platform.userAgentContains_('VirginMedia');
246
+ }
247
+
240
248
  /**
241
249
  * Check if the current platform is Amazon Fire TV.
242
250
  * https://developer.amazon.com/docs/fire-tv/identify-amazon-fire-tv-devices.html
@@ -11,6 +11,7 @@ goog.require('shaka.abr.SimpleAbrManager');
11
11
  goog.require('shaka.log');
12
12
  goog.require('shaka.net.NetworkingEngine');
13
13
  goog.require('shaka.util.ConfigUtils');
14
+ goog.require('shaka.util.FairPlayUtils');
14
15
  goog.require('shaka.util.LanguageUtils');
15
16
  goog.require('shaka.util.ManifestParserUtils');
16
17
  goog.require('shaka.util.Platform');
@@ -67,9 +68,15 @@ shaka.util.PlayerConfiguration = class {
67
68
  advanced: {}, // key is arbitrary key system ID, value is a record type
68
69
  delayLicenseRequestUntilPlayed: false,
69
70
  initDataTransform: (initData, initDataType, drmInfo) => {
70
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
71
- [initData, initDataType, drmInfo],
72
- initData);
71
+ const keySystem = drmInfo.keySystem;
72
+ if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
73
+ const cert = drmInfo.serverCertificate;
74
+ const contentId =
75
+ shaka.util.FairPlayUtils.defaultGetContentId(initData);
76
+ initData = shaka.util.FairPlayUtils.initDataTransform(
77
+ initData, contentId, cert);
78
+ }
79
+ return initData;
73
80
  },
74
81
  logLicenseExchange: false,
75
82
  updateExpirationTime: 1,
@@ -438,15 +438,20 @@ shaka.util.StreamUtils = class {
438
438
  const video = variant.video;
439
439
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
440
440
  if (video) {
441
- let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
441
+ let videoCodecs =
442
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
442
443
  // For multiplexed streams. Here we must check the audio of the
443
444
  // stream to see if it is compatible.
444
445
  if (video.codecs.includes(',')) {
445
446
  const allCodecs = video.codecs.split(',');
446
447
  videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
447
448
  ContentType.VIDEO, allCodecs);
448
- const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
449
+ videoCodecs =
450
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
451
+ let audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
449
452
  ContentType.AUDIO, allCodecs);
453
+ audioCodecs =
454
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
450
455
  const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
451
456
  video.mimeType, audioCodecs, ContentType.AUDIO);
452
457
  if (!MediaSource.isTypeSupported(audioFullType)) {
@@ -566,7 +571,8 @@ shaka.util.StreamUtils = class {
566
571
  const allCodecs = video.codecs.split(',');
567
572
  videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
568
573
  ContentType.VIDEO, allCodecs);
569
- videoCodecs = shaka.util.StreamUtils.patchVp9(videoCodecs);
574
+ videoCodecs =
575
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
570
576
  const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
571
577
  ContentType.AUDIO, allCodecs);
572
578
 
@@ -580,7 +586,7 @@ shaka.util.StreamUtils = class {
580
586
  spatialRendering: false,
581
587
  };
582
588
  }
583
- videoCodecs = shaka.util.StreamUtils.patchVp9(videoCodecs);
589
+ videoCodecs = shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
584
590
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
585
591
  video.mimeType, videoCodecs, ContentType.VIDEO);
586
592
  // VideoConfiguration
@@ -749,15 +755,29 @@ shaka.util.StreamUtils = class {
749
755
 
750
756
 
751
757
  /**
752
- * MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
753
- * codec strings into 'vp09...', to allow such content to play with
754
- * mediaCapabilities enabled.
755
- *
758
+ * Generates the correct video codec for MediaDecodingConfiguration and
759
+ * for MediaSource.isTypeSupported.
756
760
  * @param {string} codec
757
761
  * @return {string}
762
+ * @private
758
763
  */
759
- static patchVp9(codec) {
760
- if (codec == 'vp9') {
764
+ static getCorrectVideoCodecs_(codec) {
765
+ if (codec.includes('avc1')) {
766
+ // Convert avc1 codec string from RFC-4281 to RFC-6381 for
767
+ // MediaSource.isTypeSupported
768
+ // Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
769
+ const avcdata = codec.split('.');
770
+ if (avcdata.length == 3) {
771
+ let result = avcdata.shift() + '.';
772
+ result += parseInt(avcdata.shift(), 10).toString(16);
773
+ result +=
774
+ ('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
775
+ return result;
776
+ }
777
+ } else if (codec == 'vp9') {
778
+ // MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate
779
+ // vp9 codec strings into 'vp09...', to allow such content to play with
780
+ // mediaCapabilities enabled.
761
781
  // This means profile 0, level 4.1, 8-bit color. This supports 1080p @
762
782
  // 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
763
783
  //
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": "4.2.4",
4
+ "version": "4.2.6",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -32,6 +32,7 @@ goog.require('shaka.offline.OfflineManifestParser');
32
32
  goog.require('shaka.offline.OfflineScheme');
33
33
  goog.require('shaka.offline.Storage');
34
34
  goog.require('shaka.offline.indexeddb.StorageMechanism');
35
+ goog.require('shaka.polyfill.AbortController');
35
36
  goog.require('shaka.polyfill.Aria');
36
37
  goog.require('shaka.polyfill.EncryptionScheme');
37
38
  goog.require('shaka.polyfill.Fullscreen');
@@ -2106,6 +2106,7 @@ describe('DashParser Manifest', () => {
2106
2106
  /** @type {shaka.extern.Manifest} */
2107
2107
  const manifest = await parser.start('dummy://foo', playerInterface);
2108
2108
  expect(manifest.imageStreams.length).toBe(1);
2109
+ expect(manifest.presentationTimeline.getMaxSegmentDuration()).toBe(1);
2109
2110
  const imageStream = manifest.imageStreams[0];
2110
2111
  expect(imageStream.width).toBe(1024);
2111
2112
  expect(imageStream.height).toBe(1152);
@@ -38,6 +38,7 @@ describe('HlsParser', () => {
38
38
 
39
39
  afterEach(() => {
40
40
  shaka.log.alwaysWarn = originalAlwaysWarn;
41
+ parser.stop();
41
42
  });
42
43
 
43
44
  beforeEach(() => {
@@ -120,11 +120,17 @@ describe('DrmEngine', () => {
120
120
  };
121
121
 
122
122
  drmEngine = new shaka.media.DrmEngine(playerInterface);
123
+
123
124
  config = shaka.util.PlayerConfiguration.createDefault().drm;
124
125
  config.servers = {
125
126
  'drm.abc': 'http://abc.drm/license',
126
127
  'drm.def': 'http://def.drm/license',
127
128
  };
129
+ // Some platforms, such as Xbox, default to parseInbandPssh: true, which
130
+ // ignores encrypted events. So set it explicitly to false, and let
131
+ // individual tests set it to true where relevant.
132
+ config.parseInbandPsshEnabled = false;
133
+
128
134
  drmEngine.configure(config);
129
135
  });
130
136
 
@@ -2548,7 +2554,11 @@ describe('DrmEngine', () => {
2548
2554
  */
2549
2555
  async function sendEncryptedEvent(
2550
2556
  initDataType = 'cenc', initData = new Uint8Array(1), keyId = null) {
2551
- mockVideo.on['encrypted']({initDataType, initData, keyId});
2557
+ // For some platforms, such as Xbox, where parseInbandPssh defaults to
2558
+ // true, this listener may never be set.
2559
+ if (mockVideo.on['encrypted']) {
2560
+ mockVideo.on['encrypted']({initDataType, initData, keyId});
2561
+ }
2552
2562
 
2553
2563
  await Util.shortDelay();
2554
2564
  }
@@ -6,6 +6,8 @@
6
6
 
7
7
  describe('MediaSourceEngine', () => {
8
8
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
9
+ const Cue = shaka.text.Cue;
10
+ const Util = shaka.test.Util;
9
11
  const presentationDuration = 840;
10
12
 
11
13
  /** @type {!HTMLVideoElement} */
@@ -18,6 +20,64 @@ describe('MediaSourceEngine', () => {
18
20
  let metadata;
19
21
  // TODO: add text streams to MSE integration tests
20
22
 
23
+ const mp4CeaCue0 = jasmine.objectContaining({
24
+ startTime: Util.closeTo(0.067, 0.001),
25
+ endTime: Util.closeTo(1, 0.001),
26
+ textAlign: Cue.textAlign.CENTER,
27
+ nestedCues: [
28
+ jasmine.objectContaining({
29
+ startTime: Util.closeTo(0.067, 0.001),
30
+ endTime: Util.closeTo(1, 0.001),
31
+ payload: 'eng:⠀00:00:00:00',
32
+ textAlign: Cue.textAlign.CENTER,
33
+ }),
34
+ ],
35
+ });
36
+
37
+ const tsCeaCue0 = jasmine.objectContaining({
38
+ startTime: Util.closeTo(0.767, 0.001),
39
+ endTime: Util.closeTo(4.972, 0.001),
40
+ textAlign: Cue.textAlign.CENTER,
41
+ payload: 'These are 608 captions\n(top left)',
42
+ });
43
+
44
+ const tsCeaCue1 = jasmine.objectContaining({
45
+ startTime: Util.closeTo(5.305, 0.001),
46
+ endTime: Util.closeTo(11.979, 0.001),
47
+ textAlign: Cue.textAlign.CENTER,
48
+ payload: 'These are 608 captions\n(middle)',
49
+ });
50
+
51
+ const tsCeaCue2 = jasmine.objectContaining({
52
+ startTime: Util.closeTo(12.312, 0.001),
53
+ endTime: Util.closeTo(19.319, 0.001),
54
+ textAlign: Cue.textAlign.CENTER,
55
+ payload: 'These are 608 captions\n(bottom left)',
56
+ });
57
+
58
+ // The same segments as above, but offset by 40 seconds (yes, 40), which is
59
+ // also 2 segments.
60
+ const tsCeaCue3 = jasmine.objectContaining({
61
+ startTime: Util.closeTo(40.767, 0.001),
62
+ endTime: Util.closeTo(44.972, 0.001),
63
+ textAlign: Cue.textAlign.CENTER,
64
+ payload: 'These are 608 captions\n(top left)',
65
+ });
66
+
67
+ const tsCeaCue4 = jasmine.objectContaining({
68
+ startTime: Util.closeTo(45.305, 0.001),
69
+ endTime: Util.closeTo(51.979, 0.001),
70
+ textAlign: Cue.textAlign.CENTER,
71
+ payload: 'These are 608 captions\n(middle)',
72
+ });
73
+
74
+ const tsCeaCue5 = jasmine.objectContaining({
75
+ startTime: Util.closeTo(52.312, 0.001),
76
+ endTime: Util.closeTo(59.319, 0.001),
77
+ textAlign: Cue.textAlign.CENTER,
78
+ payload: 'These are 608 captions\n(bottom left)',
79
+ });
80
+
21
81
  /**
22
82
  * We use a fake text displayer so that we can check if CEA text is being
23
83
  * passed through the system correctly.
@@ -387,9 +447,14 @@ describe('MediaSourceEngine', () => {
387
447
  // platforms with native TS support.
388
448
  await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ true);
389
449
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
450
+
390
451
  await append(ContentType.VIDEO, 0);
391
452
 
392
453
  expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
454
+
455
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
456
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
457
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
393
458
  });
394
459
 
395
460
  it('extracts CEA-708 captions from previous segment from hls', async () => {
@@ -406,9 +471,19 @@ describe('MediaSourceEngine', () => {
406
471
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
407
472
 
408
473
  await append(ContentType.VIDEO, 2);
474
+
475
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
476
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue3]);
477
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue4]);
478
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue5]);
479
+
480
+ textDisplayer.appendSpy.calls.reset();
409
481
  await appendWithSeek(ContentType.VIDEO, 0);
410
482
 
411
- expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(6);
483
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
484
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
485
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
486
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
412
487
  });
413
488
 
414
489
  it('buffers partial TS video segments in sequence mode', async () => {
@@ -461,6 +536,7 @@ describe('MediaSourceEngine', () => {
461
536
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
462
537
  await appendWithClosedCaptions(ContentType.VIDEO, 0);
463
538
 
464
- expect(textDisplayer.appendSpy).toHaveBeenCalled();
539
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(1);
540
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([mp4CeaCue0]);
465
541
  });
466
542
  });
@@ -898,7 +898,7 @@ describe('Player', () => {
898
898
  expect(getBufferedBehind()).toBe(20); // Buffered to start still.
899
899
  video.currentTime = 50;
900
900
  await waitUntilBuffered(30);
901
- expect(getBufferedBehind()).toBeLessThan(30);
901
+ expect(getBufferedBehind()).toBeLessThan(40); // 30 + segment_size
902
902
 
903
903
  player.configure('streaming.bufferBehind', 10);
904
904
  // We only evict content when we append a segment, so increase the
@@ -925,7 +925,7 @@ describe('Player', () => {
925
925
  }
926
926
 
927
927
  async function waitUntilBuffered(amount) {
928
- for (let i = 0; i < 25; i++) {
928
+ for (let i = 0; i < 50; i++) {
929
929
  // We buffer from an internal segment, so this shouldn't take long to
930
930
  // buffer.
931
931
  await Util.delay(0.1); // eslint-disable-line no-await-in-loop
@@ -933,7 +933,14 @@ describe('Player', () => {
933
933
  return;
934
934
  }
935
935
  }
936
- throw new Error('Timeout waiting to buffer');
936
+
937
+ const ranges =
938
+ shaka.media.TimeRangesUtils.getBufferedInfo(video.buffered);
939
+ const currentTime = video.currentTime;
940
+ const target = currentTime + amount;
941
+
942
+ throw new Error('Timeout waiting to buffer! ' +
943
+ JSON.stringify({ranges, currentTime, target}));
937
944
  }
938
945
  }); // describe('buffering')
939
946