shaka-player 4.5.0 → 4.6.0

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 (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +98 -0
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTORS +2 -0
  6. package/README.md +38 -32
  7. package/demo/common/asset.js +11 -17
  8. package/demo/common/assets.js +117 -11
  9. package/demo/config.js +28 -1
  10. package/demo/custom.js +52 -38
  11. package/demo/main.js +11 -3
  12. package/demo/search.js +7 -0
  13. package/dist/controls.css +1 -1
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +133 -124
  16. package/dist/demo.compiled.debug.map +3 -3
  17. package/dist/demo.compiled.js +133 -124
  18. package/dist/demo.compiled.map +3 -3
  19. package/dist/deps.js +20 -18
  20. package/dist/locales.js +35 -31
  21. package/dist/receiver.compiled.debug.js +58 -50
  22. package/dist/receiver.compiled.debug.map +3 -3
  23. package/dist/receiver.compiled.js +58 -50
  24. package/dist/receiver.compiled.map +3 -3
  25. package/dist/shaka-player.compiled.d.ts +440 -390
  26. package/dist/shaka-player.compiled.debug.d.ts +440 -390
  27. package/dist/shaka-player.compiled.debug.externs.js +162 -77
  28. package/dist/shaka-player.compiled.debug.js +786 -736
  29. package/dist/shaka-player.compiled.debug.map +4 -4
  30. package/dist/shaka-player.compiled.externs.js +162 -77
  31. package/dist/shaka-player.compiled.js +1154 -1121
  32. package/dist/shaka-player.compiled.map +4 -4
  33. package/dist/shaka-player.ui.d.ts +491 -437
  34. package/dist/shaka-player.ui.debug.d.ts +491 -437
  35. package/dist/shaka-player.ui.debug.externs.js +167 -77
  36. package/dist/shaka-player.ui.debug.js +877 -811
  37. package/dist/shaka-player.ui.debug.map +4 -4
  38. package/dist/shaka-player.ui.externs.js +167 -77
  39. package/dist/shaka-player.ui.js +1362 -1318
  40. package/dist/shaka-player.ui.map +4 -4
  41. package/docs/tutorials/ad_monetization.md +54 -0
  42. package/docs/tutorials/basic-usage.md +2 -1
  43. package/docs/tutorials/errors.md +2 -1
  44. package/docs/tutorials/offline.md +10 -8
  45. package/docs/tutorials/plugins.md +5 -0
  46. package/docs/tutorials/ui-customization.md +13 -0
  47. package/docs/tutorials/ui.md +2 -1
  48. package/docs/tutorials/upgrade.md +3 -0
  49. package/externs/managedmediasource.js +24 -0
  50. package/externs/shaka/abr_manager.js +6 -2
  51. package/externs/shaka/cea.js +7 -0
  52. package/externs/shaka/codecs.js +5 -3
  53. package/externs/shaka/manifest.js +21 -5
  54. package/externs/shaka/manifest_parser.js +17 -0
  55. package/externs/shaka/net.js +2 -2
  56. package/externs/shaka/offline.js +9 -2
  57. package/externs/shaka/player.js +53 -7
  58. package/externs/shaka/text.js +3 -1
  59. package/externs/shaka/transmuxer.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +25 -8
  61. package/lib/ads/media_tailor_ad_manager.js +14 -3
  62. package/lib/cast/cast_receiver.js +1 -1
  63. package/lib/cea/cea_decoder.js +28 -0
  64. package/lib/cea/dummy_caption_decoder.js +5 -0
  65. package/lib/dash/dash_parser.js +249 -83
  66. package/lib/dash/mpd_utils.js +52 -31
  67. package/lib/dash/segment_base.js +4 -5
  68. package/lib/dash/segment_list.js +5 -4
  69. package/lib/dash/segment_template.js +83 -26
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +761 -649
  72. package/lib/hls/hls_utils.js +64 -5
  73. package/lib/hls/manifest_text_parser.js +7 -11
  74. package/lib/media/adaptation_set.js +20 -13
  75. package/lib/media/adaptation_set_criteria.js +70 -18
  76. package/lib/media/closed_caption_parser.js +13 -0
  77. package/lib/media/content_workarounds.js +6 -0
  78. package/lib/media/drm_engine.js +8 -13
  79. package/lib/media/manifest_parser.js +8 -113
  80. package/lib/media/media_source_capabilities.js +10 -3
  81. package/lib/media/media_source_engine.js +75 -21
  82. package/lib/media/play_rate_controller.js +8 -38
  83. package/lib/media/presentation_timeline.js +7 -1
  84. package/lib/media/quality_observer.js +1 -1
  85. package/lib/media/segment_index.js +5 -5
  86. package/lib/media/segment_prefetch.js +92 -24
  87. package/lib/media/segment_reference.js +67 -1
  88. package/lib/media/segment_utils.js +343 -0
  89. package/lib/media/stall_detector.js +0 -3
  90. package/lib/media/streaming_engine.js +41 -21
  91. package/lib/mss/mss_parser.js +18 -2
  92. package/lib/net/networking_engine.js +66 -12
  93. package/lib/net/networking_utils.js +95 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  96. package/lib/offline/manifest_converter.js +2 -0
  97. package/lib/offline/offline_manifest_parser.js +10 -0
  98. package/lib/offline/storage.js +11 -7
  99. package/lib/player.js +993 -1502
  100. package/lib/polyfill/mediasource.js +22 -1
  101. package/lib/text/mp4_ttml_parser.js +2 -2
  102. package/lib/text/srt_text_parser.js +2 -2
  103. package/lib/text/stub_text_displayer.js +51 -0
  104. package/lib/text/text_engine.js +3 -2
  105. package/lib/text/ttml_text_parser.js +28 -5
  106. package/lib/text/ui_text_displayer.js +4 -0
  107. package/lib/text/vtt_text_parser.js +7 -2
  108. package/lib/transmuxer/aac_transmuxer.js +1 -1
  109. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  110. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  111. package/lib/transmuxer/h264.js +5 -4
  112. package/lib/transmuxer/h265.js +5 -4
  113. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  114. package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
  115. package/lib/transmuxer/mss_transmuxer.js +1 -1
  116. package/lib/transmuxer/ts_transmuxer.js +33 -13
  117. package/lib/util/buffer_utils.js +26 -4
  118. package/lib/util/content_steering_manager.js +358 -0
  119. package/lib/util/error.js +2 -6
  120. package/lib/util/fairplay_utils.js +1 -1
  121. package/lib/util/fake_event.js +5 -1
  122. package/lib/util/id3_utils.js +50 -1
  123. package/lib/util/manifest_parser_utils.js +12 -8
  124. package/lib/util/mp4_generator.js +2 -4
  125. package/lib/util/mutex.js +59 -0
  126. package/lib/util/object_utils.js +1 -1
  127. package/lib/util/periods.js +154 -179
  128. package/lib/util/platform.js +4 -3
  129. package/lib/util/player_configuration.js +35 -6
  130. package/lib/util/stream_utils.js +105 -98
  131. package/lib/util/ts_parser.js +124 -73
  132. package/lib/util/uint8array_utils.js +5 -1
  133. package/lib/util/xml_utils.js +5 -0
  134. package/maintained-branches.md +2 -2
  135. package/package.json +1 -1
  136. package/roadmap.md +15 -5
  137. package/shaka-player.uncompiled.js +0 -1
  138. package/test/abr/simple_abr_manager_unit.js +33 -33
  139. package/test/ads/ad_manager_unit.js +3 -2
  140. package/test/cast/cast_receiver_integration.js +3 -4
  141. package/test/cast/cast_utils_unit.js +2 -0
  142. package/test/cea/cea_decoder_unit.js +7 -0
  143. package/test/codec_switching/codec_switching_integration.js +2 -1
  144. package/test/dash/dash_parser_integration.js +2 -1
  145. package/test/dash/dash_parser_live_unit.js +0 -19
  146. package/test/dash/dash_parser_manifest_unit.js +494 -2
  147. package/test/dash/mpd_utils_unit.js +61 -30
  148. package/test/hls/hls_live_unit.js +12 -3
  149. package/test/hls/hls_parser_unit.js +377 -27
  150. package/test/hls/manifest_text_parser_unit.js +32 -99
  151. package/test/media/adaptation_set_criteria_unit.js +172 -21
  152. package/test/media/adaptation_set_unit.js +1 -0
  153. package/test/media/content_workarounds_unit.js +1 -1
  154. package/test/media/drm_engine_unit.js +3 -3
  155. package/test/media/media_source_engine_unit.js +1 -1
  156. package/test/media/play_rate_controller_unit.js +0 -48
  157. package/test/media/segment_index_unit.js +2 -1
  158. package/test/mss/mss_player_integration.js +3 -2
  159. package/test/net/networking_engine_unit.js +3 -3
  160. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  161. package/test/offline/manifest_convert_unit.js +9 -0
  162. package/test/offline/offline_integration.js +2 -1
  163. package/test/offline/storage_integration.js +21 -9
  164. package/test/player_external.js +4 -2
  165. package/test/player_integration.js +15 -4
  166. package/test/player_load_graph_integration.js +47 -151
  167. package/test/player_src_equals_external.js +1 -0
  168. package/test/player_src_equals_integration.js +71 -4
  169. package/test/player_unit.js +80 -27
  170. package/test/test/util/fake_media_source_engine.js +5 -0
  171. package/test/test/util/fake_networking_engine.js +9 -1
  172. package/test/test/util/fake_segment_prefetch.js +3 -0
  173. package/test/test/util/manifest_generator.js +4 -0
  174. package/test/test/util/manifest_parser_util.js +14 -2
  175. package/test/test/util/offline_utils.js +1 -0
  176. package/test/test/util/simple_fakes.js +2 -2
  177. package/test/test/util/streaming_engine_util.js +3 -0
  178. package/test/test/util/test_scheme.js +6 -0
  179. package/test/test/util/ui_utils.js +4 -3
  180. package/test/text/mp4_ttml_parser_unit.js +4 -4
  181. package/test/text/srt_text_parser_unit.js +1 -1
  182. package/test/text/text_displayer_layout_unit.js +1 -1
  183. package/test/text/text_engine_unit.js +6 -0
  184. package/test/text/ttml_text_parser_unit.js +33 -2
  185. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  186. package/test/transmuxer/transmuxer_integration.js +33 -3
  187. package/test/ui/ad_ui_unit.js +2 -2
  188. package/test/ui/ui_customization_unit.js +17 -16
  189. package/test/ui/ui_integration.js +30 -20
  190. package/test/ui/ui_unit.js +25 -19
  191. package/test/util/buffer_utils_unit.js +1 -1
  192. package/test/util/content_steering_manager_unit.js +427 -0
  193. package/test/util/id3_utils_unit.js +52 -0
  194. package/test/util/stream_utils_unit.js +17 -113
  195. package/test/util/ts_parser_unit.js +3 -3
  196. package/third_party/closure-uri/uri.js +13 -1
  197. package/ui/audio_language_selection.js +9 -2
  198. package/ui/externs/ui.js +17 -2
  199. package/ui/language_utils.js +43 -8
  200. package/ui/less/range_elements.less +33 -0
  201. package/ui/locales/en.json +1 -0
  202. package/ui/locales/es-419.json +1 -0
  203. package/ui/locales/es.json +1 -0
  204. package/ui/locales/source.json +4 -0
  205. package/ui/localization.js +10 -0
  206. package/ui/remote_button.js +3 -1
  207. package/ui/resolution_selection.js +47 -10
  208. package/ui/seek_bar.js +296 -3
  209. package/ui/text_selection.js +2 -1
  210. package/ui/ui.js +6 -2
  211. package/lib/routing/node.js +0 -23
  212. package/lib/routing/payload.js +0 -47
  213. package/lib/routing/walker.js +0 -587
  214. package/test/routing/walker_unit.js +0 -505
@@ -15,8 +15,8 @@ describe('Player Load Graph', () => {
15
15
  /** @type {!jasmine.Spy} */
16
16
  let stateChangeSpy;
17
17
 
18
- /** @type {!jasmine.Spy} */
19
- let stateIdleSpy;
18
+ /** @type {?string} */
19
+ let lastStateChange = null;
20
20
 
21
21
  beforeAll(() => {
22
22
  video = shaka.test.UiUtils.createVideoElement();
@@ -29,57 +29,29 @@ describe('Player Load Graph', () => {
29
29
 
30
30
  beforeEach(() => {
31
31
  stateChangeSpy = jasmine.createSpy('stateChange');
32
- stateIdleSpy = jasmine.createSpy('stateIdle');
32
+ lastStateChange = null;
33
33
  });
34
34
 
35
- /**
36
- * @param {HTMLMediaElement} attachedTo
37
- */
38
- function createPlayer(attachedTo) {
39
- player = new shaka.Player(attachedTo);
35
+ function createPlayer() {
36
+ player = new shaka.Player();
40
37
  player.addEventListener(
41
38
  'onstatechange',
42
39
  shaka.test.Util.spyFunc(stateChangeSpy));
43
- player.addEventListener(
44
- 'onstateidle',
45
- shaka.test.Util.spyFunc(stateIdleSpy));
40
+ player.addEventListener('onstatechange', (event) => {
41
+ lastStateChange = event['state'];
42
+ });
46
43
  }
47
44
 
48
45
  // Even though some test will destroy the player, we want to make sure that
49
46
  // we don't allow the player to stay attached to the video element.
50
47
  afterEach(async () => {
51
48
  await player.destroy();
52
- });
53
-
54
- it('attach and initialize media source when constructed with media element',
55
- async () => {
56
- expect(video.src).toBeFalsy();
57
-
58
- createPlayer(/* attachedTo= */ video);
59
-
60
- // Wait until we enter the media source state.
61
- await new Promise((resolve) => {
62
- whenEnteringState('media-source', resolve);
63
- });
64
-
65
- expect(video.src).toBeTruthy();
66
- });
67
-
68
- it('does not set video.src when no video is provided', async () => {
69
- expect(video.src).toBeFalsy();
70
-
71
- createPlayer(/* attachedTo= */ null);
72
-
73
- // Wait until the player has hit an idle state (no more internal loading
74
- // actions).
75
- await spyIsCalled(stateIdleSpy);
76
-
77
- expect(video.src).toBeFalsy();
49
+ player.releaseAllMutexes();
78
50
  });
79
51
 
80
52
  it('attach + initializeMediaSource=true will initialize media source',
81
53
  async () => {
82
- createPlayer(/* attachedTo= */ null);
54
+ createPlayer();
83
55
 
84
56
  expect(video.src).toBeFalsy();
85
57
  await player.attach(video, /* initializeMediaSource= */ true);
@@ -88,7 +60,7 @@ describe('Player Load Graph', () => {
88
60
 
89
61
  it('attach + initializeMediaSource=false will not intialize media source',
90
62
  async () => {
91
- createPlayer(/* attachedTo= */ null);
63
+ createPlayer();
92
64
 
93
65
  expect(video.src).toBeFalsy();
94
66
  await player.attach(video, /* initializeMediaSource= */ false);
@@ -97,7 +69,7 @@ describe('Player Load Graph', () => {
97
69
 
98
70
  it('unload + initializeMediaSource=false does not initialize media source',
99
71
  async () => {
100
- createPlayer(/* attachedTo= */ null);
72
+ createPlayer();
101
73
 
102
74
  await player.attach(video);
103
75
  await player.load('test:sintel');
@@ -108,7 +80,7 @@ describe('Player Load Graph', () => {
108
80
 
109
81
  it('unload + initializeMediaSource=true initializes media source',
110
82
  async () => {
111
- createPlayer(/* attachedTo= */ null);
83
+ createPlayer();
112
84
 
113
85
  await player.attach(video);
114
86
  await player.load('test:sintel');
@@ -117,50 +89,8 @@ describe('Player Load Graph', () => {
117
89
  expect(video.src).toBeTruthy();
118
90
  });
119
91
 
120
- // There was a bug when calling unload before calling load would cause
121
- // the load to continue before the (first) unload was complete.
122
- // https://github.com/shaka-project/shaka-player/issues/612
123
- it('load will wait for unload to finish', async () => {
124
- createPlayer(/* attachedTo= */ null);
125
-
126
- await player.attach(video);
127
- await player.load('test:sintel');
128
-
129
- // We are going to call |unload| and |load| right after each other. What
130
- // we expect to see is that the player is fully unloaded before the load
131
- // occurs.
132
-
133
- const unload = player.unload();
134
- const load = player.load('test:sintel');
135
-
136
- await unload;
137
- await load;
138
-
139
- expect(getVisitedStates()).toEqual([
140
- 'attach',
141
-
142
- // First call to |load|.
143
- 'media-source',
144
- 'manifest-parser',
145
- 'manifest',
146
- 'drm-engine',
147
- 'load',
148
-
149
- // Our call to |unload| would have started the transition to
150
- // "unloaded", but since we called |load| right away, the transition
151
- // to "unloaded" was most likely done by the call to |load|.
152
- 'unload',
153
- 'attach',
154
- 'media-source',
155
- 'manifest-parser',
156
- 'manifest',
157
- 'drm-engine',
158
- 'load',
159
- ]);
160
- });
161
-
162
92
  it('load and unload can be called multiple times', async () => {
163
- createPlayer(/* attachedTo= */ null);
93
+ createPlayer();
164
94
 
165
95
  await player.attach(video);
166
96
 
@@ -180,7 +110,6 @@ describe('Player Load Graph', () => {
180
110
  'drm-engine',
181
111
  'load',
182
112
  'unload',
183
- 'attach',
184
113
  'media-source',
185
114
 
186
115
  // Load and unload 2
@@ -189,13 +118,12 @@ describe('Player Load Graph', () => {
189
118
  'drm-engine',
190
119
  'load',
191
120
  'unload',
192
- 'attach',
193
121
  'media-source',
194
122
  ]);
195
123
  });
196
124
 
197
125
  it('load can be called multiple times', async () => {
198
- createPlayer(/* attachedTo= */ null);
126
+ createPlayer();
199
127
 
200
128
  await player.attach(video);
201
129
 
@@ -215,7 +143,6 @@ describe('Player Load Graph', () => {
215
143
 
216
144
  // Load 2
217
145
  'unload',
218
- 'attach',
219
146
  'media-source',
220
147
  'manifest-parser',
221
148
  'manifest',
@@ -224,7 +151,6 @@ describe('Player Load Graph', () => {
224
151
 
225
152
  // Load 3
226
153
  'unload',
227
- 'attach',
228
154
  'media-source',
229
155
  'manifest-parser',
230
156
  'manifest',
@@ -234,7 +160,7 @@ describe('Player Load Graph', () => {
234
160
  });
235
161
 
236
162
  it('load will interrupt load', async () => {
237
- createPlayer(/* attachedTo= */ null);
163
+ createPlayer();
238
164
 
239
165
  await player.attach(video);
240
166
 
@@ -247,23 +173,8 @@ describe('Player Load Graph', () => {
247
173
  await load2;
248
174
  });
249
175
 
250
- it('unload will interrupt load', async () => {
251
- createPlayer(/* attachedTo= */ null);
252
-
253
- await player.attach(video);
254
-
255
- const load = player.load('test:sintel');
256
- const unload = player.unload();
257
-
258
- await expectAsync(load).toBeRejected();
259
- await unload;
260
-
261
- // We should never have gotten into the loaded state.
262
- expect(getVisitedStates()).not.toContain('load');
263
- });
264
-
265
176
  it('destroy will interrupt load', async () => {
266
- createPlayer(/* attachedTo= */ null);
177
+ createPlayer();
267
178
 
268
179
  await player.attach(video);
269
180
 
@@ -280,7 +191,7 @@ describe('Player Load Graph', () => {
280
191
  // When |destroy| is called, the player should move through the unload state
281
192
  // on its way to the detached state.
282
193
  it('destroy will unload and then detach', async () => {
283
- createPlayer(/* attachedTo= */ null);
194
+ createPlayer();
284
195
 
285
196
  await player.attach(video);
286
197
 
@@ -305,7 +216,7 @@ describe('Player Load Graph', () => {
305
216
  // |unload| after another |unload| call should just have the player re-enter
306
217
  // the state it was waiting in.
307
218
  it('unloading multiple times is okay', async () => {
308
- createPlayer(/* attachedTo= */ null);
219
+ createPlayer();
309
220
 
310
221
  await player.attach(video);
311
222
 
@@ -326,12 +237,10 @@ describe('Player Load Graph', () => {
326
237
 
327
238
  // |player.unload| (first call)
328
239
  'unload',
329
- 'attach',
330
240
  'media-source',
331
241
 
332
242
  // |player.unload| (second call)
333
243
  'unload',
334
- 'attach',
335
244
  'media-source',
336
245
  ]);
337
246
  });
@@ -339,7 +248,7 @@ describe('Player Load Graph', () => {
339
248
  // When we destroy, it will allow a current unload operation to occur even
340
249
  // though we are going to unload and detach as part of |destroy|.
341
250
  it('destroy will not interrupt unload', async () => {
342
- createPlayer(/* attachedTo= */ null);
251
+ createPlayer();
343
252
 
344
253
  await player.attach(video);
345
254
  await player.load('test:sintel');
@@ -355,7 +264,7 @@ describe('Player Load Graph', () => {
355
264
  // afterEach), this test will explicitly express our intentions to support
356
265
  // the use-case of calling |destroy| multiple times on a player instance.
357
266
  it('destroying multiple times is okay', async () => {
358
- createPlayer(/* attachedTo= */ null);
267
+ createPlayer();
359
268
 
360
269
  await player.attach(video);
361
270
  await player.load('test:sintel');
@@ -369,7 +278,7 @@ describe('Player Load Graph', () => {
369
278
  // instance when loading.
370
279
  it('pre-initialized media source is used when player continues loading',
371
280
  async () => {
372
- createPlayer(/* attachedTo= */ null);
281
+ createPlayer();
373
282
 
374
283
  // After we attach and initialize media source, we should just see
375
284
  // two states in our history.
@@ -409,7 +318,7 @@ describe('Player Load Graph', () => {
409
318
  * @return {!Promise}
410
319
  */
411
320
  async function testInterruptAfter(state) {
412
- createPlayer(/* attachedTo= */ null);
321
+ createPlayer();
413
322
 
414
323
  let pendingUnload;
415
324
  whenEnteringState(state, () => {
@@ -446,7 +355,7 @@ describe('Player Load Graph', () => {
446
355
 
447
356
  describe('error handling', () => {
448
357
  beforeEach(() => {
449
- createPlayer(/* attachedTo= */ null);
358
+ createPlayer();
450
359
  });
451
360
 
452
361
  it('returns to attach after load error', async () => {
@@ -472,12 +381,12 @@ describe('Player Load Graph', () => {
472
381
 
473
382
  // Wait a couple interrupter cycles to allow the player to enter idle
474
383
  // state.
475
- const event = await spyIsCalled(stateIdleSpy);
384
+ await shaka.test.Util.delay(/* seconds= */ 0.25);
476
385
 
477
386
  // Since attached and loaded in the same interrupter cycle, there won't be
478
387
  // any idle time until we finish failing to load. We expect to idle in
479
388
  // attach.
480
- expect(event.state).toBe('attach');
389
+ expect(lastStateChange).toBe('unload');
481
390
  });
482
391
  });
483
392
 
@@ -492,8 +401,8 @@ describe('Player Load Graph', () => {
492
401
  mediaSource = window.MediaSource;
493
402
  window['MediaSource'] = undefined;
494
403
 
495
- createPlayer(/* attachTo= */ null);
496
- await spyIsCalled(stateIdleSpy);
404
+ createPlayer();
405
+ await shaka.test.Util.delay(/* seconds= */ 0.25);
497
406
  });
498
407
 
499
408
  afterEach(() => {
@@ -508,8 +417,8 @@ describe('Player Load Graph', () => {
508
417
  // should stop at the attach state.
509
418
  player.attach(video, /* initMediaSource= */ true);
510
419
 
511
- const event = await spyIsCalled(stateIdleSpy);
512
- expect(event.state).toBe('attach');
420
+ await shaka.test.Util.delay(/* seconds= */ 0.25);
421
+ expect(lastStateChange).toBe('attach');
513
422
  });
514
423
 
515
424
  it('loading ignores media source path', async () => {
@@ -520,8 +429,8 @@ describe('Player Load Graph', () => {
520
429
  // src= path.
521
430
  player.load(SMALL_MP4_CONTENT_URI);
522
431
 
523
- const event = await spyIsCalled(stateIdleSpy);
524
- expect(event.state).toBe('src-equals');
432
+ await shaka.test.Util.delay(/* seconds= */ 0.25);
433
+ expect(lastStateChange).toBe('src-equals');
525
434
  });
526
435
 
527
436
  it('unloading ignores init media source flag', async () => {
@@ -533,8 +442,8 @@ describe('Player Load Graph', () => {
533
442
  // don't have media source, it should stop at the attach state.
534
443
  player.unload(/* initMediaSource= */ true);
535
444
 
536
- const event = await spyIsCalled(stateIdleSpy);
537
- expect(event.state).toBe('attach');
445
+ await shaka.test.Util.delay(/* seconds= */ 0.25);
446
+ expect(lastStateChange).toBe('unload');
538
447
  });
539
448
  });
540
449
 
@@ -543,9 +452,8 @@ describe('Player Load Graph', () => {
543
452
  // and then telling it to go to one of our destination states (e.g. attach,
544
453
  // load with media source, load with src=).
545
454
  describe('routing', () => {
546
- beforeEach(async () => {
547
- createPlayer(/* attachedTo= */ null);
548
- await spyIsCalled(stateIdleSpy);
455
+ beforeEach(() => {
456
+ createPlayer();
549
457
  });
550
458
 
551
459
  it('goes from detach to detach', async () => {
@@ -600,7 +508,7 @@ describe('Player Load Graph', () => {
600
508
 
601
509
  it('goes from media source to media source', async () => {
602
510
  await startIn('media-source');
603
- await goTo('media-source');
511
+ await goTo('media-source', 'attach'); // doesn't remake media source
604
512
  });
605
513
 
606
514
  it('goes from media source to load', async () => {
@@ -625,7 +533,7 @@ describe('Player Load Graph', () => {
625
533
 
626
534
  it('goes from load to media source', async () => {
627
535
  await startIn('load');
628
- await goTo('media-source');
536
+ await goTo('media-source', 'attach'); // doesn't remake media source
629
537
  });
630
538
 
631
539
  it('goes from load to load', async () => {
@@ -677,7 +585,7 @@ describe('Player Load Graph', () => {
677
585
 
678
586
  it('goes from manifest parser to media source', async () => {
679
587
  await passingThrough('manifest-parser', () => {
680
- return goTo('media-source');
588
+ return goTo('media-source', 'attach'); // doesn't remake media source
681
589
  });
682
590
  });
683
591
 
@@ -707,7 +615,7 @@ describe('Player Load Graph', () => {
707
615
 
708
616
  it('goes from manifest to media source', async () => {
709
617
  await passingThrough('manifest', () => {
710
- return goTo('media-source');
618
+ return goTo('media-source', 'attach'); // doesn't remake media source
711
619
  });
712
620
  });
713
621
 
@@ -737,7 +645,7 @@ describe('Player Load Graph', () => {
737
645
 
738
646
  it('goes from drm engine to media source', async () => {
739
647
  await passingThrough('drm-engine', () => {
740
- return goTo('media-source');
648
+ return goTo('media-source', 'attach'); // doesn't remake media source
741
649
  });
742
650
  });
743
651
 
@@ -818,15 +726,8 @@ describe('Player Load Graph', () => {
818
726
 
819
727
  const action = actions.get(state);
820
728
  expect(action).toBeTruthy();
821
-
822
- // Do not wait for the action to complete, our idle spy makes us wait. We
823
- // want to know where we stop, so using the idle spy is more accurate in
824
- // this situation.
825
- action();
826
-
827
- // Make sure that the player stops in the state that we asked it go to.
828
- const event = await spyIsCalled(stateIdleSpy);
829
- expect(event.state).toBe(state);
729
+ await action();
730
+ expect(lastStateChange).toBe(state);
830
731
  }
831
732
 
832
733
  /**
@@ -891,9 +792,10 @@ describe('Player Load Graph', () => {
891
792
  * starting the state change.
892
793
  *
893
794
  * @param {string} state
795
+ * @param {string=} expectedState
894
796
  * @return {!Promise}
895
797
  */
896
- async function goTo(state) {
798
+ async function goTo(state, expectedState) {
897
799
  /** @type {!Map.<string, function():!Promise>} */
898
800
  const actions = new Map()
899
801
  .set('detach', () => {
@@ -914,14 +816,8 @@ describe('Player Load Graph', () => {
914
816
 
915
817
  const action = actions.get(state);
916
818
  expect(action).toBeTruthy();
917
-
918
- // Do not wait for the action to complete, our idle spy make us wait. We
919
- // want to know where we stop, so using the idle spy is more accurate in
920
- // this situation.
921
- action();
922
-
923
- const event = await spyIsCalled(stateIdleSpy);
924
- expect(event.state).toBe(state);
819
+ await action();
820
+ expect(lastStateChange).toBe(expectedState || state);
925
821
  }
926
822
  });
927
823
 
@@ -29,6 +29,7 @@ describe('Player Src Equals', () => {
29
29
 
30
30
  afterEach(async () => {
31
31
  await player.destroy();
32
+ player.releaseAllMutexes();
32
33
  });
33
34
 
34
35
  afterAll(() => {
@@ -37,6 +37,7 @@ describe('Player Src Equals', () => {
37
37
 
38
38
  afterEach(async () => {
39
39
  await player.destroy();
40
+ player.releaseAllMutexes();
40
41
 
41
42
  eventManager.release();
42
43
  });
@@ -165,10 +166,6 @@ describe('Player Src Equals', () => {
165
166
  it('can control trick play rate', async () => {
166
167
  await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
167
168
 
168
- // Let playback run for a little.
169
- await video.play();
170
- await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
171
-
172
169
  let videoRateChange = false;
173
170
  let playerRateChange = false;
174
171
  eventManager.listen(video, 'ratechange', () => {
@@ -178,6 +175,10 @@ describe('Player Src Equals', () => {
178
175
  playerRateChange = true;
179
176
  });
180
177
 
178
+ // Let playback run for a little.
179
+ await video.play();
180
+ await waiter.waitForMovementOrFailOnTimeout(video, /* timeout= */10);
181
+
181
182
  // Enabling trick play should change our playback rate to the same rate.
182
183
  player.trickPlay(2);
183
184
  expect(video.playbackRate).toBe(2);
@@ -422,6 +423,72 @@ describe('Player Src Equals', () => {
422
423
  expect(player.getManifest()).toBeFalsy();
423
424
  });
424
425
 
426
+ describe('addThumbnailsTrack', () => {
427
+ it('appends thumbnails for external thumbnails with sprites',
428
+ async () => {
429
+ await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
430
+ const locationUri = new goog.Uri(location.href);
431
+ const partialUri =
432
+ new goog.Uri('/base/test/test/assets/thumbnails-sprites.vtt');
433
+ const absoluteUri = locationUri.resolve(partialUri);
434
+ const newTrack =
435
+ await player.addThumbnailsTrack(absoluteUri.toString());
436
+
437
+ expect(player.getImageTracks()).toEqual([newTrack]);
438
+
439
+ const thumbnail1 = await player.getThumbnails(newTrack.id, 0);
440
+ expect(thumbnail1.startTime).toBe(0);
441
+ expect(thumbnail1.duration).toBe(5);
442
+ expect(thumbnail1.height).toBe(90);
443
+ expect(thumbnail1.positionX).toBe(0);
444
+ expect(thumbnail1.positionY).toBe(0);
445
+ expect(thumbnail1.width).toBe(160);
446
+ const thumbnail2 = await player.getThumbnails(newTrack.id, 10);
447
+ expect(thumbnail2.startTime).toBe(5);
448
+ expect(thumbnail2.duration).toBe(25);
449
+ expect(thumbnail2.height).toBe(90);
450
+ expect(thumbnail2.positionX).toBe(160);
451
+ expect(thumbnail2.positionY).toBe(0);
452
+ expect(thumbnail2.width).toBe(160);
453
+ const thumbnail3 = await player.getThumbnails(newTrack.id, 40);
454
+ expect(thumbnail3.startTime).toBe(30);
455
+ expect(thumbnail3.duration).toBe(30);
456
+ expect(thumbnail3.height).toBe(90);
457
+ expect(thumbnail3.positionX).toBe(160);
458
+ expect(thumbnail3.positionY).toBe(90);
459
+ expect(thumbnail3.width).toBe(160);
460
+
461
+ const thumbnails = await player.getAllThumbnails(newTrack.id);
462
+ expect(thumbnails.length).toBe(3);
463
+ });
464
+
465
+ it('appends thumbnails for external thumbnails without sprites',
466
+ async () => {
467
+ await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
468
+ const locationUri = new goog.Uri(location.href);
469
+ const partialUri =
470
+ new goog.Uri('/base/test/test/assets/thumbnails.vtt');
471
+ const absoluteUri = locationUri.resolve(partialUri);
472
+ const newTrack =
473
+ await player.addThumbnailsTrack(absoluteUri.toString());
474
+
475
+ expect(player.getImageTracks()).toEqual([newTrack]);
476
+
477
+ const thumbnail1 = await player.getThumbnails(newTrack.id, 0);
478
+ expect(thumbnail1.startTime).toBe(0);
479
+ expect(thumbnail1.duration).toBe(5);
480
+ const thumbnail2 = await player.getThumbnails(newTrack.id, 10);
481
+ expect(thumbnail2.startTime).toBe(5);
482
+ expect(thumbnail2.duration).toBe(25);
483
+ const thumbnail3 = await player.getThumbnails(newTrack.id, 40);
484
+ expect(thumbnail3.startTime).toBe(30);
485
+ expect(thumbnail3.duration).toBe(30);
486
+
487
+ const thumbnails = await player.getAllThumbnails(newTrack.id);
488
+ expect(thumbnails.length).toBe(3);
489
+ });
490
+ }); // describe('addThumbnailsTrack')
491
+
425
492
  /**
426
493
  * @param {string} contentUri
427
494
  * @param {?number} startTime