shaka-player 3.1.0 → 3.1.4

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 (181) hide show
  1. package/AUTHORS +8 -2
  2. package/CHANGELOG.md +209 -0
  3. package/CONTRIBUTORS +10 -3
  4. package/README.md +3 -0
  5. package/build/apps.py +2 -2
  6. package/build/check.py +2 -2
  7. package/build/conformance.textproto +0 -9
  8. package/build/generateExterns.js +1 -1
  9. package/build/misspellings.txt +1 -0
  10. package/build/shakaBuildHelpers.py +6 -5
  11. package/build/subprocessWindowsPatch.py +103 -0
  12. package/demo/common/asset.js +11 -0
  13. package/demo/common/assets.js +1 -1
  14. package/demo/common/message_ids.js +5 -0
  15. package/demo/config.js +2 -1
  16. package/demo/custom.js +84 -3
  17. package/demo/demo.less +13 -1
  18. package/demo/demo_utils.js +1 -0
  19. package/demo/input.js +31 -12
  20. package/demo/input_container.js +24 -1
  21. package/demo/locales/en.json +6 -1
  22. package/demo/locales/source.json +20 -0
  23. package/demo/main.js +5 -1
  24. package/dist/controls.css +2 -2
  25. package/dist/controls.css.map +1 -1
  26. package/dist/demo.compiled.debug.js +99 -91
  27. package/dist/demo.compiled.debug.map +3 -3
  28. package/dist/demo.compiled.js +99 -91
  29. package/dist/demo.compiled.map +3 -3
  30. package/dist/demo.css +1 -1
  31. package/dist/demo.css.map +1 -1
  32. package/dist/deps.js +9 -9
  33. package/dist/locales.js +36 -34
  34. package/dist/receiver.compiled.debug.js +74 -70
  35. package/dist/receiver.compiled.debug.map +3 -3
  36. package/dist/receiver.compiled.js +74 -70
  37. package/dist/receiver.compiled.map +3 -3
  38. package/dist/shaka-player.compiled.d.ts +165 -9
  39. package/dist/shaka-player.compiled.debug.d.ts +165 -9
  40. package/dist/shaka-player.compiled.debug.externs.js +179 -11
  41. package/dist/shaka-player.compiled.debug.js +367 -339
  42. package/dist/shaka-player.compiled.debug.map +4 -4
  43. package/dist/shaka-player.compiled.externs.js +179 -11
  44. package/dist/shaka-player.compiled.js +818 -807
  45. package/dist/shaka-player.compiled.map +4 -4
  46. package/dist/shaka-player.ui.d.ts +165 -9
  47. package/dist/shaka-player.ui.debug.d.ts +165 -9
  48. package/dist/shaka-player.ui.debug.externs.js +181 -13
  49. package/dist/shaka-player.ui.debug.js +459 -429
  50. package/dist/shaka-player.ui.debug.map +4 -4
  51. package/dist/shaka-player.ui.externs.js +181 -13
  52. package/dist/shaka-player.ui.js +973 -961
  53. package/dist/shaka-player.ui.map +4 -4
  54. package/docs/tutorials/fairplay.md +1 -1
  55. package/docs/tutorials/faq.md +25 -0
  56. package/docs/tutorials/index.json +2 -2
  57. package/docs/tutorials/upgrade-v3.md +38 -0
  58. package/docs/tutorials/upgrade.md +3 -3
  59. package/externs/ima.js +4 -0
  60. package/externs/media_capabilities.js +16 -0
  61. package/externs/shaka/ads.js +2 -0
  62. package/externs/shaka/net.js +2 -2
  63. package/externs/shaka/player.js +6 -0
  64. package/karma.conf.js +1 -0
  65. package/lib/abr/simple_abr_manager.js +1 -1
  66. package/lib/ads/ad_manager.js +29 -11
  67. package/lib/ads/client_side_ad_manager.js +76 -86
  68. package/lib/ads/server_side_ad_manager.js +26 -11
  69. package/lib/cast/cast_proxy.js +3 -2
  70. package/lib/cast/cast_receiver.js +2 -1
  71. package/lib/cast/cast_sender.js +2 -2
  72. package/lib/cea/sei_processor.js +24 -17
  73. package/lib/dash/dash_parser.js +107 -4
  74. package/lib/dash/segment_list.js +1 -1
  75. package/lib/dash/segment_template.js +45 -15
  76. package/lib/dependencies/all.js +15 -15
  77. package/lib/hls/hls_parser.js +46 -18
  78. package/lib/media/closed_caption_parser.js +3 -1
  79. package/lib/media/content_workarounds.js +18 -0
  80. package/lib/media/drm_engine.js +20 -1
  81. package/lib/media/gap_jumping_controller.js +8 -4
  82. package/lib/media/media_source_engine.js +1 -2
  83. package/lib/media/playhead.js +1 -1
  84. package/lib/media/presentation_timeline.js +3 -1
  85. package/lib/media/segment_index.js +38 -12
  86. package/lib/media/segment_reference.js +8 -0
  87. package/lib/media/streaming_engine.js +30 -21
  88. package/lib/net/http_fetch_plugin.js +2 -2
  89. package/lib/net/networking_engine.js +3 -2
  90. package/lib/offline/storage.js +4 -0
  91. package/lib/player.js +113 -56
  92. package/lib/polyfill/encryption_scheme.js +2 -0
  93. package/lib/polyfill/fullscreen.js +2 -0
  94. package/lib/polyfill/mathround.js +2 -0
  95. package/lib/polyfill/media_capabilities.js +62 -23
  96. package/lib/polyfill/mediasource.js +8 -0
  97. package/lib/polyfill/orientation.js +3 -1
  98. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  99. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  100. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  101. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  102. package/lib/polyfill/pip_webkit.js +2 -0
  103. package/lib/polyfill/storage_estimate.js +2 -0
  104. package/lib/polyfill/video_play_promise.js +2 -0
  105. package/lib/polyfill/videoplaybackquality.js +2 -0
  106. package/lib/polyfill/vttcue.js +2 -0
  107. package/lib/text/cue.js +11 -0
  108. package/lib/text/mp4_ttml_parser.js +3 -0
  109. package/lib/text/text_engine.js +14 -2
  110. package/lib/text/ttml_text_parser.js +52 -30
  111. package/lib/text/ui_text_displayer.js +255 -82
  112. package/lib/text/vtt_text_parser.js +4 -2
  113. package/lib/util/error.js +1 -4
  114. package/lib/util/fake_event.js +53 -7
  115. package/lib/util/mime_utils.js +33 -0
  116. package/lib/util/mp4_box_parsers.js +1 -1
  117. package/lib/util/mp4_parser.js +14 -0
  118. package/lib/util/networking.js +1 -1
  119. package/lib/util/periods.js +132 -37
  120. package/lib/util/platform.js +13 -1
  121. package/lib/util/stream_utils.js +46 -7
  122. package/package.json +3 -3
  123. package/test/cast/cast_proxy_unit.js +12 -6
  124. package/test/cast/cast_receiver_integration.js +1 -1
  125. package/test/cast/cast_utils_unit.js +1 -1
  126. package/test/cea/mp4_cea_parser_unit.js +26 -0
  127. package/test/dash/dash_parser_live_unit.js +1 -0
  128. package/test/dash/dash_parser_manifest_unit.js +187 -1
  129. package/test/dash/dash_parser_segment_template_unit.js +3 -3
  130. package/test/demo/demo_unit.js +0 -1
  131. package/test/hls/hls_parser_unit.js +45 -1
  132. package/test/media/closed_caption_parser_unit.js +20 -0
  133. package/test/media/drm_engine_unit.js +59 -3
  134. package/test/media/playhead_unit.js +27 -1
  135. package/test/media/presentation_timeline_unit.js +19 -0
  136. package/test/media/segment_index_unit.js +76 -3
  137. package/test/media/streaming_engine_unit.js +3 -3
  138. package/test/player_integration.js +38 -0
  139. package/test/player_unit.js +125 -6
  140. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  141. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  142. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  143. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  144. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  145. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  146. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  147. package/test/test/assets/3675/dash_0.mpd +30 -0
  148. package/test/test/assets/3675/dash_1.mpd +30 -0
  149. package/test/test/assets/3675/dash_2.mpd +30 -0
  150. package/test/test/assets/3675/dash_3.mpd +30 -0
  151. package/test/test/assets/3675/dash_4.mpd +30 -0
  152. package/test/test/assets/3675/dash_5.mpd +26 -0
  153. package/test/test/assets/3675/init-stream0.m4s +0 -0
  154. package/test/test/assets/empty_caption_video_init.mp4 +0 -0
  155. package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
  156. package/test/test/util/fake_ad_manager.js +4 -1
  157. package/test/test/util/simple_fakes.js +5 -3
  158. package/test/test/util/waiter.js +36 -2
  159. package/test/text/ttml_text_parser_unit.js +55 -0
  160. package/test/text/ui_text_displayer_unit.js +95 -18
  161. package/test/text/vtt_text_parser_unit.js +17 -1
  162. package/test/ui/localization_unit.js +15 -14
  163. package/test/ui/text_displayer_layout_unit.js +52 -1
  164. package/test/ui/ui_unit.js +4 -0
  165. package/test/util/mp4_box_parsers_unit.js +28 -0
  166. package/test/util/periods_unit.js +190 -2
  167. package/test/util/stream_utils_unit.js +56 -5
  168. package/ui/airplay_button.js +5 -0
  169. package/ui/big_play_button.js +10 -0
  170. package/ui/cast_button.js +2 -3
  171. package/ui/controls.js +9 -7
  172. package/ui/less/containers.less +10 -5
  173. package/ui/locales/en.json +1 -0
  174. package/ui/locales/ru.json +6 -6
  175. package/ui/locales/source.json +4 -0
  176. package/ui/localization.js +28 -51
  177. package/ui/pip_button.js +2 -3
  178. package/ui/play_button.js +10 -10
  179. package/ui/small_play_button.js +12 -0
  180. package/ui/ui.js +1 -1
  181. package/docs/tutorials/upgrade-v2-4.md +0 -481
@@ -4,7 +4,6 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- goog.require('shaka.test.UiUtils');
8
7
  goog.require('shaka.test.Util');
9
8
  goog.require('shaka.text.Cue');
10
9
  goog.require('shaka.text.UITextDisplayer');
@@ -51,8 +50,7 @@ describe('UITextDisplayer', () => {
51
50
  /** @type {!HTMLElement} */ (document.createElement('div'));
52
51
  videoContainer.style.height = `${videoContainerHeight}px`;
53
52
  document.body.appendChild(videoContainer);
54
- video = shaka.test.UiUtils.createVideoElement();
55
- videoContainer.appendChild(video);
53
+ video = new shaka.test.FakeVideo();
56
54
  });
57
55
 
58
56
  beforeEach(() => {
@@ -86,19 +84,16 @@ describe('UITextDisplayer', () => {
86
84
  // Wait until updateCaptions_() gets called.
87
85
  await shaka.test.Util.delay(0.5);
88
86
 
89
- const textContainer =
90
- videoContainer.querySelector('.shaka-text-container');
91
- const captions = textContainer.querySelector('span');
87
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
88
+ const captions = textContainer.querySelector('div');
92
89
  const cssObj = parseCssText(captions.style.cssText);
93
90
 
94
91
  const expectCssObj = {
95
92
  'color': 'green',
96
- 'background-color': 'black',
97
93
  'direction': 'ltr',
98
94
  'font-size': '10px',
99
95
  'font-style': 'normal',
100
96
  'font-weight': 400,
101
- 'line-height': 2,
102
97
  'text-align': 'center',
103
98
  };
104
99
 
@@ -115,6 +110,8 @@ describe('UITextDisplayer', () => {
115
110
  }
116
111
 
117
112
  expect(cssObj).toEqual(jasmine.objectContaining(expectCssObj));
113
+ expect(parseCssText(textContainer.querySelector('span').style.cssText))
114
+ .toEqual(jasmine.objectContaining({'background-color': 'black'}));
118
115
  });
119
116
 
120
117
  it('correctly displays styles for nested cues', async () => {
@@ -138,14 +135,13 @@ describe('UITextDisplayer', () => {
138
135
  await shaka.test.Util.delay(0.5);
139
136
 
140
137
  // Verify styles applied to the nested cues.
141
- const textContainer =
142
- videoContainer.querySelector('.shaka-text-container');
143
- const captions = textContainer.querySelector('span');
138
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
139
+ const captions =
140
+ textContainer.querySelector('span:not(.shaka-text-wrapper)');
144
141
  const cssObj = parseCssText(captions.style.cssText);
145
142
 
146
143
  const expectCssObj = {
147
144
  'color': 'green',
148
- 'background-color': 'black',
149
145
  'font-size': '10px',
150
146
  'font-style': 'normal',
151
147
  'font-weight': 400,
@@ -165,6 +161,8 @@ describe('UITextDisplayer', () => {
165
161
  }
166
162
 
167
163
  expect(cssObj).toEqual(jasmine.objectContaining(expectCssObj));
164
+ expect(parseCssText(captions.querySelector('span').style.cssText))
165
+ .toEqual(jasmine.objectContaining({'background-color': 'black'}));
168
166
  });
169
167
 
170
168
  it('correctly displays styles for cellResolution units', async () => {
@@ -191,9 +189,8 @@ describe('UITextDisplayer', () => {
191
189
  // style.
192
190
  const expectedLinePadding = '11.25px';
193
191
 
194
- const textContainer =
195
- videoContainer.querySelector('.shaka-text-container');
196
- const captions = textContainer.querySelector('span');
192
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
193
+ const captions = textContainer.querySelector('div');
197
194
  const cssObj = parseCssText(captions.style.cssText);
198
195
  expect(cssObj).toEqual(
199
196
  jasmine.objectContaining({
@@ -221,9 +218,8 @@ describe('UITextDisplayer', () => {
221
218
  // videoContainerHeight=450px and tts:fontSize="90%" on the default style.
222
219
  const expectedFontSize = '27px';
223
220
 
224
- const textContainer =
225
- videoContainer.querySelector('.shaka-text-container');
226
- const captions = textContainer.querySelector('span');
221
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
222
+ const captions = textContainer.querySelector('div');
227
223
  const cssObj = parseCssText(captions.style.cssText);
228
224
  expect(cssObj).toEqual(
229
225
  jasmine.objectContaining({'font-size': expectedFontSize}));
@@ -316,4 +312,85 @@ describe('UITextDisplayer', () => {
316
312
  // duplicates.
317
313
  expect(captions.length).toBe(3);
318
314
  });
315
+
316
+ it('hides currently displayed cue when removed', async () => {
317
+ const cue = new shaka.text.Cue(0, 50, 'One');
318
+ textDisplayer.setTextVisibility(true);
319
+ textDisplayer.append([cue]);
320
+ video.currentTime = 10;
321
+ await shaka.test.Util.delay(0.5);
322
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
323
+
324
+ let cueElements = textContainer.querySelectorAll('div');
325
+ expect(cueElements.length).toBe(1);
326
+ expect(cueElements[0].textContent).toBe('One');
327
+
328
+ textDisplayer.remove(0, 100);
329
+
330
+ cueElements = textContainer.querySelectorAll('div');
331
+ expect(cueElements.length).toBe(0);
332
+ });
333
+
334
+ it('hides and shows nested cues at appropriate times', async () => {
335
+ const parentCue1 = new shaka.text.Cue(0, 100, '');
336
+ const cue1 = new shaka.text.Cue(0, 50, 'One');
337
+ parentCue1.nestedCues.push(cue1);
338
+ const cue2 = new shaka.text.Cue(25, 75, 'Two');
339
+ parentCue1.nestedCues.push(cue2);
340
+ const cue3 = new shaka.text.Cue(50, 100, 'Three');
341
+ parentCue1.nestedCues.push(cue3);
342
+
343
+ const parentCue2 = new shaka.text.Cue(90, 190, '');
344
+ const cue4 = new shaka.text.Cue(90, 130, 'Four');
345
+ parentCue2.nestedCues.push(cue4);
346
+
347
+ textDisplayer.setTextVisibility(true);
348
+ textDisplayer.append([parentCue1, parentCue2]);
349
+
350
+ video.currentTime = 10;
351
+ await shaka.test.Util.delay(0.5);
352
+ /** @type {Element} */
353
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
354
+ let parentCueElements = textContainer.querySelectorAll('div');
355
+
356
+ expect(parentCueElements.length).toBe(1);
357
+ expect(parentCueElements[0].textContent).toBe('One');
358
+
359
+ video.currentTime = 35;
360
+ await shaka.test.Util.delay(0.5);
361
+ parentCueElements = textContainer.querySelectorAll('div');
362
+ expect(parentCueElements.length).toBe(1);
363
+ expect(parentCueElements[0].textContent).toBe('OneTwo');
364
+
365
+ video.currentTime = 60;
366
+ await shaka.test.Util.delay(0.5);
367
+ parentCueElements = textContainer.querySelectorAll('div');
368
+ expect(parentCueElements.length).toBe(1);
369
+ expect(parentCueElements[0].textContent).toBe('TwoThree');
370
+
371
+ video.currentTime = 85;
372
+ await shaka.test.Util.delay(0.5);
373
+ parentCueElements = textContainer.querySelectorAll('div');
374
+ expect(parentCueElements.length).toBe(1);
375
+ expect(parentCueElements[0].textContent).toBe('Three');
376
+
377
+ video.currentTime = 95;
378
+ await shaka.test.Util.delay(0.5);
379
+ parentCueElements = textContainer.querySelectorAll('div');
380
+ expect(parentCueElements.length).toBe(2);
381
+ expect(parentCueElements[0].textContent).toBe('Three');
382
+ expect(parentCueElements[1].textContent).toBe('Four');
383
+
384
+ video.currentTime = 105;
385
+ await shaka.test.Util.delay(0.5);
386
+ parentCueElements = textContainer.querySelectorAll('div');
387
+ expect(parentCueElements.length).toBe(1);
388
+ expect(parentCueElements[0].textContent).toBe('Four');
389
+
390
+ video.currentTime = 150;
391
+ await shaka.test.Util.delay(0.5);
392
+ parentCueElements = textContainer.querySelectorAll('div');
393
+ expect(parentCueElements.length).toBe(1);
394
+ expect(parentCueElements[0].textContent).toBe('');
395
+ });
319
396
  });
@@ -924,6 +924,18 @@ describe('VttTextParser', () => {
924
924
  payload: '<c.lime><b>Parse fail 2</c></b>',
925
925
  nestedCues: [],
926
926
  },
927
+ {
928
+ startTime: 100,
929
+ endTime: 110,
930
+ payload: '<c.lime>forward slash 1/2 in text</c>',
931
+ nestedCues: [],
932
+ },
933
+ {
934
+ startTime: 110,
935
+ endTime: 120,
936
+ payload: '<c.lime>less or more < > in text</c>',
937
+ nestedCues: [],
938
+ },
927
939
  ],
928
940
  'WEBVTT\n\n' +
929
941
  '00:00:20.000 --> 00:00:40.000\n' +
@@ -939,7 +951,11 @@ describe('VttTextParser', () => {
939
951
  '00:01:20.000 --> 00:01:30.000\n' +
940
952
  '<b><c.lime>Parse fail 1</b></c>\n\n' +
941
953
  '00:01:30.000 --> 00:01:40.000\n' +
942
- '<c.lime><b>Parse fail 2</c></b>',
954
+ '<c.lime><b>Parse fail 2</c></b>\n\n' +
955
+ '00:01:40.000 --> 00:01:50.000\n' +
956
+ '<c.lime>forward slash 1/2 in text</c>\n\n' +
957
+ '00:01:50.000 --> 00:02:00.000\n' +
958
+ '<c.lime>less or more < > in text</c>',
943
959
  {periodStart: 0, segmentStart: 0, segmentEnd: 0});
944
960
  });
945
961
 
@@ -198,7 +198,7 @@ describe('Localization', () => {
198
198
  localization.changeLocale([ELVISH_WOODLAND]);
199
199
 
200
200
  expect(events.length).toBe(1);
201
- expect(events[0].locales).toEqual([ELVISH_WOODLAND]);
201
+ expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
202
202
  });
203
203
 
204
204
 
@@ -214,23 +214,23 @@ describe('Localization', () => {
214
214
  localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
215
215
 
216
216
  expect(events.length).toBe(1);
217
- expect(events[0].locales).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
217
+ expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
218
218
 
219
219
  // We should see an event telling us that dwarfish is still missing.
220
220
  localization.insert(ELVISH_WOODLAND, new Map());
221
221
  localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
222
222
 
223
223
  expect(events.length).toBe(2);
224
- expect(events[0].locales).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
225
- expect(events[1].locales).toEqual([DWARFISH_NORTH]);
224
+ expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
225
+ expect(events[1]['locales']).toEqual([DWARFISH_NORTH]);
226
226
 
227
227
  // There should be no more events now that we added the last language.
228
228
  localization.insert(DWARFISH_NORTH, new Map());
229
229
  localization.changeLocale([ELVISH_WOODLAND, DWARFISH_NORTH]);
230
230
 
231
231
  expect(events.length).toBe(2);
232
- expect(events[0].locales).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
233
- expect(events[1].locales).toEqual([DWARFISH_NORTH]);
232
+ expect(events[0]['locales']).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
233
+ expect(events[1]['locales']).toEqual([DWARFISH_NORTH]);
234
234
  });
235
235
  });
236
236
 
@@ -373,8 +373,8 @@ describe('Localization', () => {
373
373
  // it was in the fallback (HALFLING) but not our preferred language
374
374
  // (EVLISH).
375
375
  expect(events.length).toBe(1);
376
- expect(events[0].locales).toEqual([ELVISH_WOODLAND]);
377
- expect(events[0].missing).toEqual(['hello']);
376
+ expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
377
+ expect(events[0]['missing']).toEqual(['hello']);
378
378
  });
379
379
 
380
380
  it('fires when key/value is missing from preferred but found in base',
@@ -400,8 +400,8 @@ describe('Localization', () => {
400
400
  // because it was in the base (ELVISH) but not our preferred language
401
401
  // (EVLISH_WOODLAND).
402
402
  expect(events.length).toBe(1);
403
- expect(events[0].locales).toEqual([ELVISH_WOODLAND]);
404
- expect(events[0].missing).toEqual(['Best Wishes']);
403
+ expect(events[0]['locales']).toEqual([ELVISH_WOODLAND]);
404
+ expect(events[0]['missing']).toEqual(['Best Wishes']);
405
405
  });
406
406
 
407
407
  it('fires when key/value is missing from preferred but found in sibling',
@@ -427,8 +427,8 @@ describe('Localization', () => {
427
427
  // understand" because it was in a sibling locale (HALFLING_SHIRE)
428
428
  // but not our preferred language (HALFLING_COMMON).
429
429
  expect(events.length).toBe(1);
430
- expect(events[0].locales).toEqual([HALFLING_COMMON]);
431
- expect(events[0].missing).toEqual(['Do you understand']);
430
+ expect(events[0]['locales']).toEqual([HALFLING_COMMON]);
431
+ expect(events[0]['missing']).toEqual(['Do you understand']);
432
432
  });
433
433
 
434
434
  it('fires when key/value is missing from some preferred languages',
@@ -454,8 +454,9 @@ describe('Localization', () => {
454
454
 
455
455
  // There should have no missing localization events.
456
456
  expect(events.length).toBe(1);
457
- expect(events[0].locales).toEqual([ELVISH_WOODLAND, DWARFISH_NORTH]);
458
- expect(events[0].missing).toEqual(['may your forge burn bright']);
457
+ expect(events[0]['locales']).toEqual(
458
+ [ELVISH_WOODLAND, DWARFISH_NORTH]);
459
+ expect(events[0]['missing']).toEqual(['may your forge burn bright']);
459
460
  });
460
461
  });
461
462
 
@@ -141,7 +141,9 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
141
141
  cue.region.id = '1';
142
142
  // Position the cue *explicitly* at the bottom of the screen.
143
143
  cue.region.viewportAnchorX = 0; // %
144
- cue.region.viewportAnchorY = 100; // %
144
+ cue.region.viewportAnchorY = 90; // %
145
+ cue.region.width = 100; // %
146
+ cue.region.height = 10; // %
145
147
  textDisplayer.append([cue]);
146
148
 
147
149
  await checkScreenshot('ui', 'cue-with-controls');
@@ -299,6 +301,31 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
299
301
  await checkScreenshot(prefix, 'two-nested-cues');
300
302
  });
301
303
 
304
+ // Distinct from "newline" test above, which has a literal \n character in
305
+ // the text payload. This uses a nested "lineBreak" cue, which is what you
306
+ // get with <br> in TTML.
307
+ it('nested cues with linebreak', async () => {
308
+ const cue = new shaka.text.Cue(0, 1, '');
309
+ cue.nestedCues = [
310
+ new shaka.text.Cue(0, 1, 'Captain\'s log,'),
311
+ shaka.text.Cue.lineBreak(0, 1),
312
+ new shaka.text.Cue(0, 1, 'stardate 41636.9'),
313
+ ];
314
+ textDisplayer.append([cue]);
315
+
316
+ await checkScreenshot(prefix, 'nested-cues-with-linebreak');
317
+ });
318
+
319
+ // Regression test for #3600
320
+ it('cue positioning', async () => {
321
+ const cue = new shaka.text.Cue(0, 1, 'Text');
322
+ cue.line = 10;
323
+ cue.lineInterpretation = shaka.text.Cue.lineInterpretation.PERCENTAGE;
324
+ textDisplayer.append([cue]);
325
+
326
+ await checkScreenshot(prefix, 'cue-position');
327
+ });
328
+
302
329
  // Regression test for #2157 and #2584
303
330
  it('region positioning', async () => {
304
331
  const nestedCue = new shaka.text.Cue(
@@ -316,6 +343,30 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
316
343
  await checkScreenshot(prefix, 'region-position');
317
344
  });
318
345
 
346
+ // Regression test for #3379, in which the displayAlign was not respected,
347
+ // placing text at the top of the region instead of the bottom.
348
+ it('region with display alignment', async () => {
349
+ const cue = new shaka.text.Cue(0, 1, '');
350
+
351
+ cue.region.id = '1';
352
+ cue.region.viewportAnchorX = 10;
353
+ cue.region.viewportAnchorY = 10;
354
+ cue.region.width = 80;
355
+ cue.region.height = 80;
356
+
357
+ cue.positionAlign = shaka.text.Cue.positionAlign.CENTER;
358
+ cue.lineAlign = shaka.text.Cue.lineAlign.CENTER;
359
+ cue.displayAlign = shaka.text.Cue.displayAlign.AFTER;
360
+
361
+ cue.nestedCues = [
362
+ // For those who don't speak Unicode, \xbf is an upside down "?".
363
+ new shaka.text.Cue(0, 1, '\xbfBien?'),
364
+ ];
365
+ textDisplayer.append([cue]);
366
+
367
+ await checkScreenshot(prefix, 'region-with-display-alignment');
368
+ });
369
+
319
370
  // Regression test for #2188
320
371
  it('bitmap-based cues', async () => {
321
372
  const cue = new shaka.text.Cue(0, 1, '');
@@ -196,6 +196,10 @@ describe('UI', () => {
196
196
  });
197
197
 
198
198
  it('goes into fullscreen on double click', async () => {
199
+ if (!document.fullscreenEnabled) {
200
+ pending('This test requires fullscreen support, which is unavailable.');
201
+ }
202
+
199
203
  const config = {
200
204
  controlPanelElements: [
201
205
  'overflow_menu',
@@ -149,4 +149,32 @@ describe('Mp4BoxParsers', () => {
149
149
  expect(defaultSampleDuration).toBe(expectedDefaultSampleDuration);
150
150
  expect(baseMediaDecodeTime).toBe(expectedBaseMediaDecodeTime);
151
151
  });
152
+
153
+ /**
154
+ * Test on parsing an incomplete TKHD V1 box, since the parser doesn't
155
+ * parse the other fields
156
+ *
157
+ * Explanation on the Uint8Array:
158
+ * [
159
+ * <creation_time, 8 bytes>,
160
+ * <modification_time, 8 bytes>,
161
+ * <track_id, 4 bytes>
162
+ * ]
163
+ *
164
+ * Time is a 32B integer expressed in seconds since Jan 1, 1904, 0000 UTC
165
+ *
166
+ */
167
+ it('parses TKHD v1 box', () => {
168
+ const tkhdBox = new Uint8Array([
169
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xBF, 0x0F, 0xD7, // Creation time
170
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xBF, 0x0F, 0xD7, // Modification time
171
+ 0x00, 0x00, 0x00, 0x01, // Track ID
172
+ // Remaining fields are not processed in parseTKHD()
173
+ ]);
174
+ const reader = new shaka.util.DataViewReader(
175
+ tkhdBox, shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
176
+ const parsedTkhd = shaka.util.Mp4BoxParsers
177
+ .parseTKHD(reader, /* version= */ 1);
178
+ expect(parsedTkhd.trackId).toBe(1);
179
+ });
152
180
  });