shaka-player 4.3.0 → 4.3.2

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 (78) hide show
  1. package/.github/workflows/build-and-test.yaml +20 -0
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +44 -0
  4. package/CONTRIBUTORS +2 -0
  5. package/README.md +1 -1
  6. package/conditional/dummy_cast_proxy.js +120 -0
  7. package/dist/demo.compiled.debug.js +104 -110
  8. package/dist/demo.compiled.debug.map +3 -3
  9. package/dist/demo.compiled.js +104 -110
  10. package/dist/demo.compiled.map +3 -3
  11. package/dist/deps.js +8 -6
  12. package/dist/locales.js +32 -32
  13. package/dist/receiver.compiled.debug.js +54 -60
  14. package/dist/receiver.compiled.debug.map +3 -3
  15. package/dist/receiver.compiled.js +54 -60
  16. package/dist/receiver.compiled.map +3 -3
  17. package/dist/shaka-player.compiled.d.ts +19 -2
  18. package/dist/shaka-player.compiled.debug.d.ts +19 -2
  19. package/dist/shaka-player.compiled.debug.externs.js +102 -80
  20. package/dist/shaka-player.compiled.debug.js +657 -652
  21. package/dist/shaka-player.compiled.debug.map +4 -4
  22. package/dist/shaka-player.compiled.externs.js +102 -80
  23. package/dist/shaka-player.compiled.js +904 -901
  24. package/dist/shaka-player.compiled.map +4 -4
  25. package/dist/shaka-player.ui.d.ts +19 -2
  26. package/dist/shaka-player.ui.debug.d.ts +19 -2
  27. package/dist/shaka-player.ui.debug.externs.js +102 -80
  28. package/dist/shaka-player.ui.debug.js +703 -698
  29. package/dist/shaka-player.ui.debug.map +4 -4
  30. package/dist/shaka-player.ui.externs.js +102 -80
  31. package/dist/shaka-player.ui.js +1029 -1026
  32. package/dist/shaka-player.ui.map +4 -4
  33. package/docs/tutorials/welcome.md +2 -2
  34. package/externs/shaka/mp4_parser.js +3 -0
  35. package/externs/shaka/player.js +2 -1
  36. package/lib/ads/ad_manager.js +2 -2
  37. package/lib/cea/cea708_window.js +2 -2
  38. package/lib/cea/mp4_cea_parser.js +37 -16
  39. package/lib/dash/segment_template.js +1 -1
  40. package/lib/hls/hls_parser.js +49 -23
  41. package/lib/media/content_workarounds.js +12 -0
  42. package/lib/media/media_source_capabilities.js +36 -0
  43. package/lib/media/media_source_engine.js +28 -7
  44. package/lib/media/segment_reference.js +3 -0
  45. package/lib/media/streaming_engine.js +33 -10
  46. package/lib/media/transmuxer.js +6 -4
  47. package/lib/player.js +10 -6
  48. package/lib/polyfill/abort_controller.js +143 -0
  49. package/lib/polyfill/media_capabilities.js +65 -11
  50. package/lib/polyfill/patchedmediakeys_apple.js +33 -28
  51. package/lib/text/ttml_text_parser.js +32 -7
  52. package/lib/text/ui_text_displayer.js +17 -0
  53. package/lib/util/mp4_box_parsers.js +24 -0
  54. package/lib/util/mp4_parser.js +35 -0
  55. package/lib/util/player_configuration.js +10 -3
  56. package/lib/util/stream_utils.js +35 -13
  57. package/package.json +4 -4
  58. package/shaka-player.uncompiled.js +1 -0
  59. package/test/ads/ad_manager_unit.js +3 -1
  60. package/test/dash/dash_parser_manifest_unit.js +1 -0
  61. package/test/hls/hls_parser_unit.js +132 -0
  62. package/test/media/drm_engine_unit.js +11 -1
  63. package/test/media/media_source_engine_integration.js +78 -2
  64. package/test/player_integration.js +10 -3
  65. package/test/polyfill/media_capabilities_unit.js +259 -0
  66. package/test/polyfill/patchedmediakeys_apple_unit.js +6 -0
  67. package/test/test/boot.js +29 -11
  68. package/test/test/util/manifest_parser_util.js +4 -3
  69. package/test/test/util/stream_generator.js +212 -8
  70. package/test/test/util/test_scheme.js +6 -5
  71. package/test/test/util/util.js +3 -2
  72. package/test/text/mp4_ttml_parser_unit.js +6 -4
  73. package/test/text/ttml_text_parser_unit.js +119 -86
  74. package/test/text/ui_text_displayer_unit.js +76 -0
  75. package/test/util/stream_utils_unit.js +18 -0
  76. package/test/util/xml_utils_unit.js +6 -1
  77. package/third_party/closure-uri/uri.js +1 -1
  78. package/ui/controls.js +49 -36
@@ -8,6 +8,7 @@ goog.provide('shaka.util.StreamUtils');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
+ goog.require('shaka.media.Capabilities');
11
12
  goog.require('shaka.text.TextEngine');
12
13
  goog.require('shaka.util.Functional');
13
14
  goog.require('shaka.util.LanguageUtils');
@@ -437,25 +438,31 @@ shaka.util.StreamUtils = class {
437
438
  // See: https://github.com/shaka-project/shaka-player/issues/3860
438
439
  const video = variant.video;
439
440
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
441
+ const Capabilities = shaka.media.Capabilities;
440
442
  if (video) {
441
- let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
443
+ let videoCodecs =
444
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
442
445
  // For multiplexed streams. Here we must check the audio of the
443
446
  // stream to see if it is compatible.
444
447
  if (video.codecs.includes(',')) {
445
448
  const allCodecs = video.codecs.split(',');
446
449
  videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
447
450
  ContentType.VIDEO, allCodecs);
448
- const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
451
+ videoCodecs =
452
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
453
+ let audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
449
454
  ContentType.AUDIO, allCodecs);
455
+ audioCodecs =
456
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
450
457
  const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
451
458
  video.mimeType, audioCodecs, ContentType.AUDIO);
452
- if (!MediaSource.isTypeSupported(audioFullType)) {
459
+ if (!Capabilities.isTypeSupported(audioFullType)) {
453
460
  return false;
454
461
  }
455
462
  }
456
463
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
457
464
  video.mimeType, videoCodecs, ContentType.VIDEO);
458
- if (!MediaSource.isTypeSupported(fullType)) {
465
+ if (!Capabilities.isTypeSupported(fullType)) {
459
466
  return false;
460
467
  }
461
468
  }
@@ -465,7 +472,7 @@ shaka.util.StreamUtils = class {
465
472
  shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
466
473
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
467
474
  audio.mimeType, codecs, ContentType.AUDIO);
468
- if (!MediaSource.isTypeSupported(fullType)) {
475
+ if (!Capabilities.isTypeSupported(fullType)) {
469
476
  return false;
470
477
  }
471
478
  }
@@ -566,7 +573,8 @@ shaka.util.StreamUtils = class {
566
573
  const allCodecs = video.codecs.split(',');
567
574
  videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
568
575
  ContentType.VIDEO, allCodecs);
569
- videoCodecs = shaka.util.StreamUtils.patchVp9(videoCodecs);
576
+ videoCodecs =
577
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
570
578
  const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
571
579
  ContentType.AUDIO, allCodecs);
572
580
 
@@ -580,7 +588,7 @@ shaka.util.StreamUtils = class {
580
588
  spatialRendering: false,
581
589
  };
582
590
  }
583
- videoCodecs = shaka.util.StreamUtils.patchVp9(videoCodecs);
591
+ videoCodecs = shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
584
592
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
585
593
  video.mimeType, videoCodecs, ContentType.VIDEO);
586
594
  // VideoConfiguration
@@ -749,15 +757,29 @@ shaka.util.StreamUtils = class {
749
757
 
750
758
 
751
759
  /**
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
- *
760
+ * Generates the correct video codec for MediaDecodingConfiguration and
761
+ * for MediaSource.isTypeSupported.
756
762
  * @param {string} codec
757
763
  * @return {string}
764
+ * @private
758
765
  */
759
- static patchVp9(codec) {
760
- if (codec == 'vp9') {
766
+ static getCorrectVideoCodecs_(codec) {
767
+ if (codec.includes('avc1')) {
768
+ // Convert avc1 codec string from RFC-4281 to RFC-6381 for
769
+ // MediaSource.isTypeSupported
770
+ // Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
771
+ const avcdata = codec.split('.');
772
+ if (avcdata.length == 3) {
773
+ let result = avcdata.shift() + '.';
774
+ result += parseInt(avcdata.shift(), 10).toString(16);
775
+ result +=
776
+ ('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
777
+ return result;
778
+ }
779
+ } else if (codec == 'vp9') {
780
+ // MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate
781
+ // vp9 codec strings into 'vp09...', to allow such content to play with
782
+ // mediaCapabilities enabled.
761
783
  // This means profile 0, level 4.1, 8-bit color. This supports 1080p @
762
784
  // 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
763
785
  //
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.3.0",
4
+ "version": "4.3.2",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -28,9 +28,9 @@
28
28
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
29
29
  "esprima": "^4.0.1",
30
30
  "fontfaceonload": "^1.0.2",
31
- "google-closure-compiler-java": "^20220301.0.0",
32
- "google-closure-deps": "https://gitpkg.now.sh/google/closure-library/closure-deps?d7736da6",
33
- "google-closure-library": "^20220301.0.0",
31
+ "google-closure-compiler-java": "^20221102.0.0",
32
+ "google-closure-deps": "^20221102.0.0",
33
+ "google-closure-library": "^20221102.0.0",
34
34
  "htmlhint": "^1.1.3",
35
35
  "jasmine-ajax": "^4.0.0",
36
36
  "jimp": "^0.16.1",
@@ -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');
@@ -115,7 +115,9 @@ describe('Ad manager', () => {
115
115
  startedEventB = /** @type {!google.ima.AdEvent} */ (
116
116
  new shaka.util.FakeEvent(google.ima.AdEvent.Type.STARTED));
117
117
  startedEventB.getAd = () => {
118
- return {};
118
+ return {
119
+ isLinear: () => true,
120
+ };
119
121
  };
120
122
  }
121
123
  makeMocks();
@@ -2198,6 +2198,7 @@ describe('DashParser Manifest', () => {
2198
2198
  /** @type {shaka.extern.Manifest} */
2199
2199
  const manifest = await parser.start('dummy://foo', playerInterface);
2200
2200
  expect(manifest.imageStreams.length).toBe(1);
2201
+ expect(manifest.presentationTimeline.getMaxSegmentDuration()).toBe(1);
2201
2202
  const imageStream = manifest.imageStreams[0];
2202
2203
  expect(imageStream.width).toBe(1024);
2203
2204
  expect(imageStream.height).toBe(1152);
@@ -40,6 +40,7 @@ describe('HlsParser', () => {
40
40
 
41
41
  afterEach(() => {
42
42
  shaka.log.alwaysWarn = originalAlwaysWarn;
43
+ parser.stop();
43
44
  });
44
45
 
45
46
  beforeEach(() => {
@@ -1089,6 +1090,77 @@ describe('HlsParser', () => {
1089
1090
  await testHlsParser(master, media, manifest);
1090
1091
  });
1091
1092
 
1093
+ // https://github.com/shaka-project/shaka-player/issues/4759
1094
+ it('makes roles available without loading tracks', async () => {
1095
+ const master = [
1096
+ '#EXTM3U\n',
1097
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
1098
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
1099
+ 'video\n',
1100
+
1101
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
1102
+ 'NAME="English",URI="audio"\n',
1103
+
1104
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
1105
+ 'CHARACTERISTICS="public.accessibility.describes-video,',
1106
+ 'public.accessibility.describes-music-and-sound",',
1107
+ 'NAME="English (describes-video)",URI="audio2"\n',
1108
+
1109
+ '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
1110
+ 'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
1111
+ 'URI="text"\n',
1112
+
1113
+ '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
1114
+ 'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
1115
+ 'CHARACTERISTICS="public.accessibility.describes-spoken-dialog,',
1116
+ 'public.accessibility.describes-music-and-sound",',
1117
+ 'URI="text2"\n',
1118
+ ].join('');
1119
+
1120
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
1121
+ manifest.anyTimeline();
1122
+ manifest.addPartialVariant((variant) => {
1123
+ variant.language = 'en';
1124
+ variant.addPartialStream(ContentType.VIDEO);
1125
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
1126
+ stream.language = 'en';
1127
+ });
1128
+ });
1129
+ manifest.addPartialVariant((variant) => {
1130
+ variant.language = 'en';
1131
+ variant.addPartialStream(ContentType.VIDEO);
1132
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
1133
+ stream.language = 'en';
1134
+ stream.roles = [
1135
+ 'public.accessibility.describes-video',
1136
+ 'public.accessibility.describes-music-and-sound',
1137
+ ];
1138
+ });
1139
+ });
1140
+ manifest.addPartialTextStream((stream) => {
1141
+ stream.language = 'en';
1142
+ stream.kind = TextStreamKind.SUBTITLE;
1143
+ });
1144
+ manifest.addPartialTextStream((stream) => {
1145
+ stream.language = 'en';
1146
+ stream.kind = TextStreamKind.SUBTITLE;
1147
+ stream.roles = [
1148
+ 'public.accessibility.describes-spoken-dialog',
1149
+ 'public.accessibility.describes-music-and-sound',
1150
+ ];
1151
+ });
1152
+ manifest.sequenceMode = true;
1153
+ });
1154
+
1155
+ fakeNetEngine.setResponseText('test:/master', master);
1156
+
1157
+ // NOTE: Not using testHlsParser here because that unconditionally loads all
1158
+ // streams. We need to test the behavior specifically when streams are
1159
+ // _not_ loaded.
1160
+ const actual = await parser.start('test:/master', playerInterface);
1161
+ expect(actual).toEqual(manifest);
1162
+ });
1163
+
1092
1164
  it('gets mime type from header request', async () => {
1093
1165
  const master = [
1094
1166
  '#EXTM3U\n',
@@ -4262,4 +4334,64 @@ describe('HlsParser', () => {
4262
4334
  expect(actualVideo0.mimeType).toBe('video/mp2t');
4263
4335
  expect(actualVideo1.mimeType).toBe('video/mp2t');
4264
4336
  });
4337
+
4338
+ it('lazy-loads AAC content without filtering it out', async () => {
4339
+ const master = [
4340
+ '#EXTM3U\n',
4341
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",URI="audio"\n',
4342
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="spa",URI="audio2"\n',
4343
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
4344
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
4345
+ 'video\n',
4346
+ ].join('');
4347
+
4348
+ const video = [
4349
+ '#EXTM3U\n',
4350
+ '#EXT-X-TARGETDURATION:5\n',
4351
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
4352
+ '#EXT-X-MEDIA-SEQUENCE:1\n',
4353
+ '#EXTINF:5,\n',
4354
+ 'video1.ts\n',
4355
+ '#EXTINF:5,\n',
4356
+ 'video2.ts\n',
4357
+ ].join('');
4358
+
4359
+ const audio = [
4360
+ '#EXTM3U\n',
4361
+ '#EXT-X-TARGETDURATION:5\n',
4362
+ '#EXT-X-MEDIA-SEQUENCE:3\n',
4363
+ '#EXTINF:5,\n',
4364
+ 'audio1.aac\n',
4365
+ '#EXTINF:5,\n',
4366
+ 'audio2.aac\n',
4367
+ ].join('');
4368
+
4369
+ fakeNetEngine
4370
+ .setResponseText('test:/master', master)
4371
+ .setResponseText('test:/audio', audio)
4372
+ .setResponseText('test:/audio2', audio)
4373
+ .setResponseText('test:/video', video)
4374
+ .setResponseValue('test:/init.mp4', initSegmentData);
4375
+
4376
+ const actualManifest = await parser.start('test:/master', playerInterface);
4377
+ expect(actualManifest.variants.length).toBe(2);
4378
+
4379
+ const actualAudio0 = actualManifest.variants[0].audio;
4380
+ const actualAudio1 = actualManifest.variants[1].audio;
4381
+
4382
+ // Before loading, all MIME types agree, and are defaulted to audio/mp4.
4383
+ expect(actualAudio0.mimeType).toBe('audio/mp4');
4384
+ expect(actualAudio1.mimeType).toBe('audio/mp4');
4385
+
4386
+ await actualAudio0.createSegmentIndex();
4387
+
4388
+ // After loading just ONE stream, all MIME types agree again, and have been
4389
+ // updated to reflect the AAC content found inside the loaded playlist.
4390
+ // This is how we avoid having the unloaded tracks filtered out during
4391
+ // startup.
4392
+ expect(actualAudio0.mimeType).toBe('audio/aac');
4393
+ expect(actualAudio0.codecs).toBe('');
4394
+ expect(actualAudio1.mimeType).toBe('audio/aac');
4395
+ expect(actualAudio1.codecs).toBe('');
4396
+ });
4265
4397
  });
@@ -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.
@@ -394,9 +454,14 @@ describe('MediaSourceEngine', () => {
394
454
  // platforms with native TS support.
395
455
  await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
396
456
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
457
+
397
458
  await append(ContentType.VIDEO, 0);
398
459
 
399
460
  expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
461
+
462
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
463
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
464
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
400
465
  });
401
466
 
402
467
  it('extracts CEA-708 captions from previous segment from hls', async () => {
@@ -413,9 +478,19 @@ describe('MediaSourceEngine', () => {
413
478
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
414
479
 
415
480
  await append(ContentType.VIDEO, 2);
481
+
482
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
483
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue3]);
484
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue4]);
485
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue5]);
486
+
487
+ textDisplayer.appendSpy.calls.reset();
416
488
  await appendWithSeek(ContentType.VIDEO, 0);
417
489
 
418
- expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(6);
490
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(3);
491
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue0]);
492
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue1]);
493
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([tsCeaCue2]);
419
494
  });
420
495
 
421
496
  it('buffers partial TS video segments in sequence mode', async () => {
@@ -468,7 +543,8 @@ describe('MediaSourceEngine', () => {
468
543
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
469
544
  await appendWithClosedCaptions(ContentType.VIDEO, 0);
470
545
 
471
- expect(textDisplayer.appendSpy).toHaveBeenCalled();
546
+ expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(1);
547
+ expect(textDisplayer.appendSpy).toHaveBeenCalledWith([mp4CeaCue0]);
472
548
  });
473
549
 
474
550
  it('extracts ID3 metadata from TS', async () => {
@@ -970,7 +970,7 @@ describe('Player', () => {
970
970
  expect(getBufferedBehind()).toBe(20); // Buffered to start still.
971
971
  video.currentTime = 50;
972
972
  await waitUntilBuffered(30);
973
- expect(getBufferedBehind()).toBeLessThan(30);
973
+ expect(getBufferedBehind()).toBeLessThan(40); // 30 + segment_size
974
974
 
975
975
  player.configure('streaming.bufferBehind', 10);
976
976
  // We only evict content when we append a segment, so increase the
@@ -997,7 +997,7 @@ describe('Player', () => {
997
997
  }
998
998
 
999
999
  async function waitUntilBuffered(amount) {
1000
- for (let i = 0; i < 25; i++) {
1000
+ for (let i = 0; i < 50; i++) {
1001
1001
  // We buffer from an internal segment, so this shouldn't take long to
1002
1002
  // buffer.
1003
1003
  await Util.delay(0.1); // eslint-disable-line no-await-in-loop
@@ -1005,7 +1005,14 @@ describe('Player', () => {
1005
1005
  return;
1006
1006
  }
1007
1007
  }
1008
- throw new Error('Timeout waiting to buffer');
1008
+
1009
+ const ranges =
1010
+ shaka.media.TimeRangesUtils.getBufferedInfo(video.buffered);
1011
+ const currentTime = video.currentTime;
1012
+ const target = currentTime + amount;
1013
+
1014
+ throw new Error('Timeout waiting to buffer! ' +
1015
+ JSON.stringify({ranges, currentTime, target}));
1009
1016
  }
1010
1017
  }); // describe('buffering')
1011
1018