shaka-player 4.7.13 → 4.8.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 (114) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +1 -0
  3. package/LICENSE +54 -0
  4. package/README.md +22 -1
  5. package/dist/controls.css +1 -1
  6. package/dist/controls.css.map +1 -1
  7. package/dist/deps.js +34 -23
  8. package/dist/locales.js +44 -34
  9. package/dist/shaka-player.compiled.d.ts +194 -44
  10. package/dist/shaka-player.compiled.debug.d.ts +194 -44
  11. package/dist/shaka-player.compiled.debug.externs.js +474 -337
  12. package/dist/shaka-player.compiled.debug.js +949 -832
  13. package/dist/shaka-player.compiled.debug.map +4 -4
  14. package/dist/shaka-player.compiled.externs.js +474 -337
  15. package/dist/shaka-player.compiled.js +1237 -1171
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +257 -47
  18. package/dist/shaka-player.ui.debug.d.ts +257 -47
  19. package/dist/shaka-player.ui.debug.externs.js +591 -376
  20. package/dist/shaka-player.ui.debug.js +1165 -965
  21. package/dist/shaka-player.ui.debug.map +4 -4
  22. package/dist/shaka-player.ui.externs.js +591 -376
  23. package/dist/shaka-player.ui.js +1521 -1404
  24. package/dist/shaka-player.ui.map +4 -4
  25. package/externs/device_sensor_event.js +18 -0
  26. package/externs/hisense.js +16 -0
  27. package/externs/playstation.js +31 -0
  28. package/externs/shaka/abr_manager.js +3 -1
  29. package/externs/shaka/ads.js +5 -0
  30. package/externs/shaka/codecs.js +13 -0
  31. package/externs/shaka/manifest.js +58 -6
  32. package/externs/shaka/net.js +13 -1
  33. package/externs/shaka/player.js +184 -13
  34. package/externs/shaka/text.js +19 -0
  35. package/externs/tizen.js +41 -0
  36. package/externs/webos.js +19 -0
  37. package/externs/xbox.js +43 -0
  38. package/lib/abr/simple_abr_manager.js +9 -1
  39. package/lib/ads/ad_manager.js +19 -2
  40. package/lib/ads/client_side_ad_manager.js +5 -3
  41. package/lib/ads/media_tailor_ad_manager.js +9 -0
  42. package/lib/cast/cast_receiver.js +0 -14
  43. package/lib/cast/cast_utils.js +1 -0
  44. package/lib/cea/mp4_cea_parser.js +6 -1
  45. package/lib/dash/content_protection.js +97 -53
  46. package/lib/dash/dash_parser.js +230 -176
  47. package/lib/dash/mpd_utils.js +72 -65
  48. package/lib/dash/segment_base.js +27 -21
  49. package/lib/dash/segment_list.js +18 -16
  50. package/lib/dash/segment_template.js +26 -21
  51. package/lib/hls/hls_parser.js +358 -164
  52. package/lib/media/buffering_observer.js +25 -1
  53. package/lib/media/drm_engine.js +91 -71
  54. package/lib/media/manifest_filterer.js +220 -0
  55. package/lib/media/media_source_engine.js +34 -5
  56. package/lib/media/preload_manager.js +827 -0
  57. package/lib/media/presentation_timeline.js +11 -0
  58. package/lib/media/segment_index.js +40 -30
  59. package/lib/media/segment_prefetch.js +140 -43
  60. package/lib/media/segment_reference.js +31 -8
  61. package/lib/media/segment_utils.js +208 -25
  62. package/lib/media/streaming_engine.js +319 -63
  63. package/lib/mss/content_protection.js +31 -25
  64. package/lib/mss/mss_parser.js +74 -55
  65. package/lib/net/networking_engine.js +49 -4
  66. package/lib/offline/storage.js +3 -3
  67. package/lib/player.js +882 -673
  68. package/lib/polyfill/media_capabilities.js +16 -1
  69. package/lib/text/simple_text_displayer.js +9 -0
  70. package/lib/text/stub_text_displayer.js +7 -0
  71. package/lib/text/text_engine.js +16 -3
  72. package/lib/text/ttml_text_parser.js +116 -114
  73. package/lib/text/ui_text_displayer.js +28 -11
  74. package/lib/text/vtt_text_parser.js +54 -115
  75. package/lib/transmuxer/opus.js +91 -0
  76. package/lib/transmuxer/ts_transmuxer.js +115 -0
  77. package/lib/util/cmcd_manager.js +152 -3
  78. package/lib/util/dom_utils.js +11 -0
  79. package/lib/util/error.js +5 -0
  80. package/lib/util/fake_event.js +2 -0
  81. package/lib/util/manifest_parser_utils.js +97 -3
  82. package/lib/util/mp4_box_parsers.js +346 -17
  83. package/lib/util/mp4_generator.js +23 -0
  84. package/lib/util/periods.js +19 -0
  85. package/lib/util/platform.js +161 -9
  86. package/lib/util/player_configuration.js +61 -3
  87. package/lib/util/stats.js +16 -0
  88. package/lib/util/stream_utils.js +16 -6
  89. package/lib/util/string_utils.js +36 -0
  90. package/lib/util/tXml.js +814 -0
  91. package/lib/util/ts_parser.js +50 -0
  92. package/lib/util/xml_utils.js +10 -338
  93. package/package.json +2 -2
  94. package/shaka-player.uncompiled.js +8 -0
  95. package/ui/controls.js +187 -5
  96. package/ui/enums.js +2 -0
  97. package/ui/externs/ui.js +30 -3
  98. package/ui/gl_matrix/matrix_4x4.js +568 -0
  99. package/ui/gl_matrix/matrix_quaternion.js +45 -0
  100. package/ui/less/containers.less +11 -0
  101. package/ui/locales/en-GB.json +2 -0
  102. package/ui/locales/en.json +2 -0
  103. package/ui/locales/es-419.json +2 -0
  104. package/ui/locales/es.json +2 -0
  105. package/ui/locales/source.json +8 -0
  106. package/ui/recenter_vr.js +126 -0
  107. package/ui/resolution_selection.js +25 -5
  108. package/ui/statistics_button.js +12 -0
  109. package/ui/text_selection.js +1 -1
  110. package/ui/toggle_stereoscopic.js +127 -0
  111. package/ui/ui.js +74 -38
  112. package/ui/vr_manager.js +557 -0
  113. package/ui/vr_utils.js +232 -0
  114. package/ui/vr_webgl.js +697 -0
@@ -0,0 +1,827 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.media.PreloadManager');
8
+
9
+ goog.require('goog.asserts');
10
+ goog.require('shaka.log');
11
+ goog.require('shaka.util.Error');
12
+ goog.require('shaka.media.ManifestFilterer');
13
+ goog.require('shaka.media.ManifestParser');
14
+ goog.require('shaka.util.PublicPromise');
15
+ goog.require('shaka.media.PreferenceBasedCriteria');
16
+ goog.require('shaka.util.Stats');
17
+ goog.require('shaka.media.SegmentPrefetch');
18
+ goog.require('shaka.util.IDestroyable');
19
+ goog.require('shaka.net.NetworkingEngine');
20
+ goog.require('shaka.media.AdaptationSetCriteria');
21
+ goog.require('shaka.media.DrmEngine');
22
+ goog.require('shaka.media.RegionTimeline');
23
+ goog.require('shaka.media.QualityObserver');
24
+ goog.require('shaka.util.StreamUtils');
25
+ goog.require('shaka.media.StreamingEngine');
26
+ goog.require('shaka.media.SegmentPrefetch');
27
+ goog.require('shaka.util.ConfigUtils');
28
+ goog.require('shaka.util.FakeEvent');
29
+ goog.require('shaka.util.FakeEventTarget');
30
+ goog.require('shaka.util.ObjectUtils');
31
+ goog.require('shaka.util.PlayerConfiguration');
32
+
33
+ /**
34
+ * @implements {shaka.util.IDestroyable}
35
+ * @export
36
+ */
37
+ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
38
+ /**
39
+ * @param {string} assetUri
40
+ * @param {?string} mimeType
41
+ * @param {number} startTimeOfLoad
42
+ * @param {?number} startTime
43
+ * @param {*} playerInterface
44
+ */
45
+ constructor(assetUri, mimeType, startTimeOfLoad, startTime, playerInterface) {
46
+ super();
47
+
48
+ // Making the playerInterface a * and casting it to the right type allows
49
+ // for the PlayerInterface for this class to not be exported.
50
+ // Unfortunately, the constructor is exported by default.
51
+ const typedPlayerInterface =
52
+ /** @type {!shaka.media.PreloadManager.PlayerInterface} */ (
53
+ playerInterface);
54
+
55
+ /** @private {string} */
56
+ this.assetUri_ = assetUri;
57
+
58
+ /** @private {?string} */
59
+ this.mimeType_ = mimeType;
60
+
61
+ /** @private {!shaka.net.NetworkingEngine} */
62
+ this.networkingEngine_ = typedPlayerInterface.networkingEngine;
63
+
64
+ /** @private {?number} */
65
+ this.startTime_ = startTime;
66
+
67
+ /** @private {?shaka.media.AdaptationSetCriteria} */
68
+ this.currentAdaptationSetCriteria_ = null;
69
+
70
+ /** @private {number} */
71
+ this.startTimeOfLoad_ = startTimeOfLoad;
72
+
73
+ /** @private {number} */
74
+ this.startTimeOfDrm_ = 0;
75
+
76
+ /** @private {function():!shaka.media.DrmEngine} */
77
+ this.createDrmEngine_ = typedPlayerInterface.createDrmEngine;
78
+
79
+ /** @private {!shaka.media.ManifestFilterer} */
80
+ this.manifestFilterer_ = typedPlayerInterface.manifestFilterer;
81
+
82
+ /** @private {!shaka.extern.ManifestParser.PlayerInterface} */
83
+ this.manifestPlayerInterface_ =
84
+ typedPlayerInterface.manifestPlayerInterface;
85
+
86
+ /** @private {!shaka.extern.PlayerConfiguration} */
87
+ this.config_ = typedPlayerInterface.config;
88
+
89
+ /** @private {?shaka.extern.Manifest} */
90
+ this.manifest_ = null;
91
+
92
+ /** @private {?shaka.extern.ManifestParser.Factory} */
93
+ this.parserFactory_ = null;
94
+
95
+ /** @private {?shaka.extern.ManifestParser} */
96
+ this.parser_ = null;
97
+
98
+ /** @private {boolean} */
99
+ this.parserEntrusted_ = false;
100
+
101
+ /** @private {!shaka.media.RegionTimeline} */
102
+ this.regionTimeline_ = typedPlayerInterface.regionTimeline;
103
+
104
+ /** @private {boolean} */
105
+ this.regionTimelineEntrusted_ = false;
106
+
107
+ /** @private {?shaka.media.DrmEngine} */
108
+ this.drmEngine_ = null;
109
+
110
+ /** @private {boolean} */
111
+ this.drmEngineEntrusted_ = false;
112
+
113
+ /** @private {?shaka.extern.AbrManager.Factory} */
114
+ this.abrManagerFactory_ = null;
115
+
116
+ /** @private {shaka.extern.AbrManager} */
117
+ this.abrManager_ = null;
118
+
119
+ /** @private {boolean} */
120
+ this.abrManagerEntrusted_ = false;
121
+
122
+ /** @private {!Map.<number, shaka.media.SegmentPrefetch>} */
123
+ this.segmentPrefetchById_ = new Map();
124
+
125
+ /** @private {boolean} */
126
+ this.segmentPrefetchEntrusted_ = false;
127
+
128
+ /** @private {?shaka.media.QualityObserver} */
129
+ this.qualityObserver_ = typedPlayerInterface.qualityObserver;
130
+
131
+ /** @private {!shaka.util.Stats} */
132
+ this.stats_ = new shaka.util.Stats();
133
+
134
+ /** @private {!shaka.util.PublicPromise} */
135
+ this.manifestPromise_ = new shaka.util.PublicPromise();
136
+
137
+ /** @private {!shaka.util.PublicPromise} */
138
+ this.drmPromise_ = new shaka.util.PublicPromise();
139
+
140
+ /** @private {!shaka.util.PublicPromise} */
141
+ this.chooseInitialVariantPromise_ = new shaka.util.PublicPromise();
142
+
143
+ /** @private {!shaka.util.PublicPromise} */
144
+ this.destroyPromise_ = new shaka.util.PublicPromise();
145
+
146
+ // Silence promise failures, in case nobody is catching this.destroyPromise_
147
+ this.destroyPromise_.catch((error) => {});
148
+
149
+ /** @private {?shaka.util.FakeEventTarget} */
150
+ this.eventHandoffTarget_ = null;
151
+
152
+ /** @private {boolean} */
153
+ this.destroyed_ = false;
154
+
155
+ /** @private {boolean} */
156
+ this.allowPrefetch_ = typedPlayerInterface.allowPrefetch;
157
+
158
+ /** @private {?shaka.extern.Variant} */
159
+ this.prefetchedVariant_ = null;
160
+
161
+ /** @private {boolean} */
162
+ this.allowMakeAbrManager_ = typedPlayerInterface.allowMakeAbrManager;
163
+
164
+ /** @private {boolean} */
165
+ this.hasBeenAttached_ = false;
166
+
167
+ /** @private {?Array.<function()>} */
168
+ this.queuedOperations_ = [];
169
+
170
+ /** @private {?Array.<function()>} */
171
+ this.latePhaseQueuedOperations_ = [];
172
+ }
173
+
174
+ /**
175
+ * @param {boolean} latePhase
176
+ * @param {function()} callback
177
+ */
178
+ addQueuedOperation(latePhase, callback) {
179
+ const queue =
180
+ latePhase ? this.latePhaseQueuedOperations_ : this.queuedOperations_;
181
+ if (queue) {
182
+ queue.push(callback);
183
+ } else {
184
+ callback();
185
+ }
186
+ }
187
+
188
+ /** Calls all late phase queued operations, and stops queueing them. */
189
+ stopQueuingLatePhaseQueuedOperations() {
190
+ if (this.latePhaseQueuedOperations_) {
191
+ for (const callback of this.latePhaseQueuedOperations_) {
192
+ callback();
193
+ }
194
+ }
195
+ this.latePhaseQueuedOperations_ = null;
196
+ }
197
+
198
+ /** @param {!shaka.util.FakeEventTarget} eventHandoffTarget */
199
+ setEventHandoffTarget(eventHandoffTarget) {
200
+ this.eventHandoffTarget_ = eventHandoffTarget;
201
+ this.hasBeenAttached_ = true;
202
+ // Also call all queued operations, and stop queuing them in the future.
203
+ if (this.queuedOperations_) {
204
+ for (const callback of this.queuedOperations_) {
205
+ callback();
206
+ }
207
+ }
208
+ this.queuedOperations_ = null;
209
+ }
210
+
211
+ /** @return {?number} */
212
+ getStartTime() {
213
+ return this.startTime_;
214
+ }
215
+
216
+ /** @return {number} */
217
+ getStartTimeOfLoad() {
218
+ return this.startTimeOfLoad_;
219
+ }
220
+
221
+ /** @return {number} */
222
+ getStartTimeOfDRM() {
223
+ return this.startTimeOfDrm_;
224
+ }
225
+
226
+ /** @return {?string} */
227
+ getMimeType() {
228
+ return this.mimeType_;
229
+ }
230
+
231
+ /** @return {string} */
232
+ getAssetUri() {
233
+ return this.assetUri_;
234
+ }
235
+
236
+ /** @return {?shaka.extern.Manifest} */
237
+ getManifest() {
238
+ return this.manifest_;
239
+ }
240
+
241
+ /** @return {?shaka.extern.ManifestParser.Factory} */
242
+ getParserFactory() {
243
+ return this.parserFactory_;
244
+ }
245
+
246
+ /** @return {?shaka.media.AdaptationSetCriteria} */
247
+ getCurrentAdaptationSetCriteria() {
248
+ return this.currentAdaptationSetCriteria_;
249
+ }
250
+
251
+ /** @return {?shaka.extern.AbrManager.Factory} */
252
+ getAbrManagerFactory() {
253
+ return this.abrManagerFactory_;
254
+ }
255
+
256
+ /**
257
+ * Gets the abr manager, if it exists. Also marks that the abr manager should
258
+ * not be stopped if this manager is destroyed.
259
+ * @return {?shaka.extern.AbrManager}
260
+ */
261
+ receiveAbrManager() {
262
+ this.abrManagerEntrusted_ = true;
263
+ return this.abrManager_;
264
+ }
265
+
266
+ /**
267
+ * @return {?shaka.extern.AbrManager}
268
+ */
269
+ getAbrManager() {
270
+ return this.abrManager_;
271
+ }
272
+
273
+ /**
274
+ * Gets the parser, if it exists. Also marks that the parser should not be
275
+ * stopped if this manager is destroyed.
276
+ * @return {?shaka.extern.ManifestParser}
277
+ */
278
+ receiveParser() {
279
+ this.parserEntrusted_ = true;
280
+ return this.parser_;
281
+ }
282
+
283
+ /**
284
+ * @return {?shaka.extern.ManifestParser}
285
+ */
286
+ getParser() {
287
+ return this.parser_;
288
+ }
289
+
290
+ /**
291
+ * Gets the region timeline, if it exists. Also marks that the timeline should
292
+ * not be released if this manager is destroyed.
293
+ * @return {?shaka.media.RegionTimeline}
294
+ */
295
+ receiveRegionTimeline() {
296
+ this.regionTimelineEntrusted_ = true;
297
+ return this.regionTimeline_;
298
+ }
299
+
300
+ /**
301
+ * @return {?shaka.media.RegionTimeline}
302
+ */
303
+ getRegionTimeline() {
304
+ return this.regionTimeline_;
305
+ }
306
+
307
+ /** @return {?shaka.media.QualityObserver} */
308
+ getQualityObserver() {
309
+ return this.qualityObserver_;
310
+ }
311
+
312
+ /** @return {!shaka.util.Stats} */
313
+ getStats() {
314
+ return this.stats_;
315
+ }
316
+
317
+ /** @return {!shaka.media.ManifestFilterer} */
318
+ getManifestFilterer() {
319
+ return this.manifestFilterer_;
320
+ }
321
+
322
+ /**
323
+ * Gets the drm engine, if it exists. Also marks that the drm engine should
324
+ * not be destroyed if this manager is destroyed.
325
+ * @return {?shaka.media.DrmEngine}
326
+ */
327
+ receiveDrmEngine() {
328
+ this.drmEngineEntrusted_ = true;
329
+ return this.drmEngine_;
330
+ }
331
+
332
+ /**
333
+ * @return {?shaka.media.DrmEngine}
334
+ */
335
+ getDrmEngine() {
336
+ return this.drmEngine_;
337
+ }
338
+
339
+ /**
340
+ * @return {?shaka.extern.Variant}
341
+ */
342
+ getPrefetchedVariant() {
343
+ return this.prefetchedVariant_;
344
+ }
345
+
346
+ /**
347
+ * Gets the SegmentPrefetch objects for the initial stream ids. Also marks
348
+ * that those objects should not be aborted if this manager is destroyed.
349
+ * @return {!Map.<number, shaka.media.SegmentPrefetch>}
350
+ */
351
+ receiveSegmentPrefetchesById() {
352
+ this.segmentPrefetchEntrusted_ = true;
353
+ return this.segmentPrefetchById_;
354
+ }
355
+
356
+ /** @return {!Promise} */
357
+ waitForManifest() {
358
+ return this.manifestPromise_;
359
+ }
360
+
361
+ /** @return {!Promise} */
362
+ waitForDrm() {
363
+ return this.drmPromise_;
364
+ }
365
+
366
+ /** @return {!Promise} */
367
+ waitForChooseInitialVariant() {
368
+ return this.chooseInitialVariantPromise_;
369
+ }
370
+
371
+ /**
372
+ * @param {?shaka.extern.AbrManager} abrManager
373
+ * @param {?shaka.extern.AbrManager.Factory} abrFactory
374
+ */
375
+ attachAbrManager(abrManager, abrFactory) {
376
+ this.abrManager_ = abrManager;
377
+ this.abrManagerFactory_ = abrFactory;
378
+ }
379
+
380
+ /**
381
+ * @param {?shaka.media.AdaptationSetCriteria} adaptationSetCriteria
382
+ */
383
+ attachAdaptationSetCriteria(adaptationSetCriteria) {
384
+ this.currentAdaptationSetCriteria_ = adaptationSetCriteria;
385
+ }
386
+
387
+ /**
388
+ * @param {!shaka.extern.Manifest} manifest
389
+ * @param {!shaka.extern.ManifestParser} parser
390
+ * @param {!shaka.extern.ManifestParser.Factory} parserFactory
391
+ */
392
+ attachManifest(manifest, parser, parserFactory) {
393
+ this.manifest_ = manifest;
394
+ this.parser_ = parser;
395
+ this.parserFactory_ = parserFactory;
396
+ }
397
+
398
+ /**
399
+ * Starts the process of loading the asset.
400
+ * @return {!Promise}
401
+ */
402
+ async start() {
403
+ // Force a context switch, to give the player a chance to hook up events
404
+ // immediately if desired.
405
+ await Promise.resolve();
406
+
407
+ // Perform the preloading process.
408
+ try {
409
+ await this.parseManifestInner_();
410
+ if (this.isDestroyed()) {
411
+ return;
412
+ }
413
+ this.manifestPromise_.resolve();
414
+ await this.initializeDrmInner_();
415
+ if (this.isDestroyed()) {
416
+ return;
417
+ }
418
+ this.drmPromise_.resolve();
419
+ await this.chooseInitialVariantInner_();
420
+ if (this.isDestroyed()) {
421
+ return;
422
+ }
423
+ this.chooseInitialVariantPromise_.resolve();
424
+ } catch (error) {
425
+ this.destroyPromise_.reject(error);
426
+ throw error;
427
+ }
428
+ }
429
+
430
+ /**
431
+ * @param {!Event} event
432
+ * @return {boolean}
433
+ * @override
434
+ */
435
+ dispatchEvent(event) {
436
+ if (this.eventHandoffTarget_) {
437
+ return this.eventHandoffTarget_.dispatchEvent(event);
438
+ } else {
439
+ return super.dispatchEvent(event);
440
+ }
441
+ }
442
+
443
+ /**
444
+ * @param {!shaka.util.Error} error
445
+ */
446
+ onError(error) {
447
+ if (error.severity === shaka.util.Error.Severity.CRITICAL) {
448
+ // Cancel the loading process.
449
+ this.destroyPromise_.reject(error);
450
+ this.destroy();
451
+ }
452
+
453
+ const eventName = shaka.util.FakeEvent.EventName.Error;
454
+ const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
455
+ this.dispatchEvent(event);
456
+ if (event.defaultPrevented) {
457
+ error.handled = true;
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Makes a fires an event corresponding to entering a state of the loading
463
+ * process.
464
+ * @param {string} nodeName
465
+ * @private
466
+ */
467
+ makeStateChangeEvent_(nodeName) {
468
+ this.dispatchEvent(new shaka.util.FakeEvent(
469
+ /* name= */ shaka.util.FakeEvent.EventName.OnStateChange,
470
+ /* data= */ (new Map()).set('state', nodeName)));
471
+ }
472
+
473
+ /**
474
+ * @param {!shaka.util.FakeEvent.EventName} name
475
+ * @param {Map.<string, Object>=} data
476
+ * @return {!shaka.util.FakeEvent}
477
+ * @private
478
+ */
479
+ makeEvent_(name, data) {
480
+ return new shaka.util.FakeEvent(name, data);
481
+ }
482
+
483
+ /**
484
+ * Pick and initialize a manifest parser, then have it download and parse the
485
+ * manifest.
486
+ *
487
+ * @return {!Promise}
488
+ * @private
489
+ */
490
+ async parseManifestInner_() {
491
+ this.makeStateChangeEvent_('manifest-parser');
492
+
493
+ if (!this.parser_) {
494
+ // Create the parser that we will use to parse the manifest.
495
+ this.parserFactory_ = shaka.media.ManifestParser.getFactory(
496
+ this.assetUri_, this.mimeType_);
497
+ goog.asserts.assert(this.parserFactory_, 'Must have manifest parser');
498
+ this.parser_ = this.parserFactory_();
499
+
500
+ this.parser_.configure(this.config_.manifest);
501
+ }
502
+
503
+ const startTime = Date.now() / 1000;
504
+
505
+ this.makeStateChangeEvent_('manifest');
506
+
507
+ if (!this.manifest_) {
508
+ this.manifest_ = await this.parser_.start(
509
+ this.assetUri_, this.manifestPlayerInterface_);
510
+ }
511
+
512
+ // This event is fired after the manifest is parsed, but before any
513
+ // filtering takes place.
514
+ const event =
515
+ this.makeEvent_(shaka.util.FakeEvent.EventName.ManifestParsed);
516
+ this.dispatchEvent(event);
517
+
518
+ // We require all manifests to have at least one variant.
519
+ if (this.manifest_.variants.length == 0) {
520
+ throw new shaka.util.Error(
521
+ shaka.util.Error.Severity.CRITICAL,
522
+ shaka.util.Error.Category.MANIFEST,
523
+ shaka.util.Error.Code.NO_VARIANTS);
524
+ }
525
+
526
+ // Make sure that all variants are either: audio-only, video-only, or
527
+ // audio-video.
528
+ shaka.media.PreloadManager.filterForAVVariants_(this.manifest_);
529
+
530
+ const now = Date.now() / 1000;
531
+ const delta = now - startTime;
532
+ this.stats_.setManifestTime(delta);
533
+ }
534
+
535
+ /**
536
+ * Initializes the DRM engine.
537
+ *
538
+ * @return {!Promise}
539
+ * @private
540
+ */
541
+ async initializeDrmInner_() {
542
+ goog.asserts.assert(
543
+ this.manifest_, 'The manifest should already be parsed.');
544
+
545
+ this.makeStateChangeEvent_('drm-engine');
546
+
547
+ this.startTimeOfDrm_ = Date.now() / 1000;
548
+
549
+ this.drmEngine_ = this.createDrmEngine_();
550
+ this.manifestFilterer_.setDrmEngine(this.drmEngine_);
551
+
552
+ this.drmEngine_.configure(this.config_.drm);
553
+
554
+ const tracksChangedInitial = this.manifestFilterer_.applyRestrictions(
555
+ this.manifest_);
556
+ if (tracksChangedInitial) {
557
+ const event = this.makeEvent_(
558
+ shaka.util.FakeEvent.EventName.TracksChanged);
559
+ await Promise.resolve();
560
+ this.dispatchEvent(event);
561
+ }
562
+
563
+ const playableVariants = shaka.util.StreamUtils.getPlayableVariants(
564
+ this.manifest_.variants);
565
+ await this.drmEngine_.initForPlayback(
566
+ playableVariants,
567
+ this.manifest_.offlineSessionIds);
568
+
569
+ // Now that we have drm information, filter the manifest (again) so that
570
+ // we can ensure we only use variants with the selected key system.
571
+ // const tracksChanged =
572
+ const tracksChangedAfter = await this.manifestFilterer_.filterManifest(
573
+ this.manifest_);
574
+ if (tracksChangedAfter) {
575
+ const event = this.makeEvent_(
576
+ shaka.util.FakeEvent.EventName.TracksChanged);
577
+ await Promise.resolve();
578
+ this.dispatchEvent(event);
579
+ }
580
+ }
581
+
582
+ /** @param {!shaka.extern.PlayerConfiguration} config */
583
+ reconfigure(config) {
584
+ this.config_ = config;
585
+ }
586
+
587
+ /**
588
+ * @param {string} name
589
+ * @param {*=} value
590
+ */
591
+ configure(name, value) {
592
+ const config = shaka.util.ConfigUtils.convertToConfigObject(name, value);
593
+ shaka.util.PlayerConfiguration.mergeConfigObjects(this.config_, config);
594
+ }
595
+
596
+ /**
597
+ * Return a copy of the current configuration.
598
+ *
599
+ * @return {shaka.extern.PlayerConfiguration}
600
+ */
601
+ getConfiguration() {
602
+ return shaka.util.ObjectUtils.cloneObject(this.config_);
603
+ }
604
+
605
+ /**
606
+ * Performs a final filtering of the manifest, and chooses the initial
607
+ * variant.
608
+ *
609
+ * @private
610
+ */
611
+ chooseInitialVariantInner_() {
612
+ goog.asserts.assert(
613
+ this.manifest_, 'The manifest should already be parsed.');
614
+
615
+ // This step does not have any associated events, as it is only part of the
616
+ // "load" state in the old state graph.
617
+
618
+ if (!this.currentAdaptationSetCriteria_) {
619
+ // Copy preferred languages from the config again, in case the config was
620
+ // changed between construction and playback.
621
+ this.currentAdaptationSetCriteria_ =
622
+ new shaka.media.PreferenceBasedCriteria(
623
+ this.config_.preferredAudioLanguage,
624
+ this.config_.preferredVariantRole,
625
+ this.config_.preferredAudioChannelCount,
626
+ this.config_.preferredVideoHdrLevel,
627
+ this.config_.preferSpatialAudio,
628
+ this.config_.preferredVideoLayout,
629
+ this.config_.preferredAudioLabel,
630
+ this.config_.preferredVideoLabel,
631
+ this.config_.mediaSource.codecSwitchingStrategy,
632
+ this.config_.manifest.dash.enableAudioGroups);
633
+
634
+ // If the content is multi-codec and the browser can play more than one of
635
+ // them, choose codecs now before we initialize streaming.
636
+ shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
637
+ this.manifest_,
638
+ this.config_.preferredVideoCodecs,
639
+ this.config_.preferredAudioCodecs,
640
+ this.config_.preferredDecodingAttributes);
641
+ }
642
+
643
+ // Make the ABR manager.
644
+ if (this.allowMakeAbrManager_) {
645
+ const abrFactory = this.config_.abrFactory;
646
+ this.abrManagerFactory_ = abrFactory;
647
+ this.abrManager_ = abrFactory();
648
+ this.abrManager_.configure(this.config_.abr);
649
+ }
650
+
651
+ if (this.allowPrefetch_) {
652
+ const isLive = this.manifest_.presentationTimeline.isLive();
653
+ // Prefetch segments for the predicted first variant.
654
+ // We start these here, but don't wait for them; it's okay to start the
655
+ // full load process while the segments are being prefetched.
656
+ const playableVariants = shaka.util.StreamUtils.getPlayableVariants(
657
+ this.manifest_.variants);
658
+ const adaptationSet = this.currentAdaptationSetCriteria_.create(
659
+ playableVariants);
660
+ // Guess what the first variant will be, based on a SimpleAbrManager.
661
+ this.abrManager_.configure(this.config_.abr);
662
+ this.abrManager_.setVariants(Array.from(adaptationSet.values()));
663
+ const variant =
664
+ this.abrManager_.chooseVariant(/* preferFastSwitching= */ true);
665
+ if (variant) {
666
+ this.prefetchedVariant_ = variant;
667
+ if (variant.video) {
668
+ this.makePrefetchForStream_(variant.video, isLive);
669
+ }
670
+ if (variant.audio) {
671
+ this.makePrefetchForStream_(variant.audio, isLive);
672
+ }
673
+ }
674
+ }
675
+ }
676
+
677
+ /**
678
+ * @param {!shaka.extern.Stream} stream
679
+ * @param {boolean} isLive
680
+ * @return {!Promise}
681
+ * @private
682
+ */
683
+ async makePrefetchForStream_(stream, isLive) {
684
+ // Use the prefetch limit from the config if this is set, otherwise use 2.
685
+ const prefetchLimit = this.config_.streaming.segmentPrefetchLimit || 2;
686
+ const prefetch = new shaka.media.SegmentPrefetch(
687
+ prefetchLimit, stream, (reference, stream, streamDataCallback) => {
688
+ return shaka.media.StreamingEngine.dispatchFetch(
689
+ reference, stream, streamDataCallback || null,
690
+ this.config_.streaming.retryParameters, this.networkingEngine_);
691
+ });
692
+ this.segmentPrefetchById_.set(stream.id, prefetch);
693
+
694
+ // Start prefetching a bit.
695
+ await stream.createSegmentIndex();
696
+ const startTime = this.startTime_ || 0;
697
+ const prefetchSegmentIterator =
698
+ stream.segmentIndex.getIteratorForTime(startTime);
699
+ let prefetchSegment =
700
+ prefetchSegmentIterator ? prefetchSegmentIterator.current() : null;
701
+ if (!prefetchSegment) {
702
+ // If we can't get a segment at the desired spot, at least get a segment,
703
+ // so we can get the init segment.
704
+ prefetchSegment = stream.segmentIndex.get(0);
705
+ }
706
+ if (prefetchSegment) {
707
+ if (isLive) {
708
+ // Preload only the init segment for Live
709
+ if (prefetchSegment.initSegmentReference) {
710
+ prefetch.prefetchInitSegment(prefetchSegment.initSegmentReference);
711
+ }
712
+ } else {
713
+ // Preload a segment, too... either the first segment, or the segment
714
+ // that corresponds with this.startTime_, as appropriate.
715
+ // Note: this method also preload the init segment
716
+ prefetch.prefetchSegmentsByTime(prefetchSegment.startTime);
717
+ }
718
+ }
719
+ }
720
+
721
+ /**
722
+ * Waits for the loading to be finished (or to fail with an error).
723
+ * @return {!Promise}
724
+ * @export
725
+ */
726
+ waitForFinish() {
727
+ return Promise.race([
728
+ this.chooseInitialVariantPromise_,
729
+ this.destroyPromise_,
730
+ ]);
731
+ }
732
+
733
+ /**
734
+ * Releases or stops all non-entrusted resources.
735
+ *
736
+ * @override
737
+ * @export
738
+ */
739
+ async destroy() {
740
+ this.destroyed_ = true;
741
+ if (this.parser_ && !this.parserEntrusted_) {
742
+ await this.parser_.stop();
743
+ }
744
+ if (this.abrManager_ && !this.abrManagerEntrusted_) {
745
+ await this.abrManager_.stop();
746
+ }
747
+ if (this.regionTimeline_ && !this.regionTimelineEntrusted_) {
748
+ this.regionTimeline_.release();
749
+ }
750
+ if (this.drmEngine_ && !this.drmEngineEntrusted_) {
751
+ await this.drmEngine_.destroy();
752
+ }
753
+ if (this.segmentPrefetchById_.size > 0 && !this.segmentPrefetchEntrusted_) {
754
+ for (const segmentPrefetch of this.segmentPrefetchById_.values()) {
755
+ segmentPrefetch.clearAll();
756
+ }
757
+ }
758
+ // this.eventHandoffTarget_ is not unset, so that events and errors fired
759
+ // after the preload manager is destroyed will still be routed to the
760
+ // player, if it was once linked up.
761
+ }
762
+
763
+ /** @return {boolean} */
764
+ isDestroyed() {
765
+ return this.destroyed_;
766
+ }
767
+
768
+ /** @return {boolean} */
769
+ hasBeenAttached() {
770
+ return this.hasBeenAttached_;
771
+ }
772
+
773
+ /**
774
+ * Take a series of variants and ensure that they only contain one type of
775
+ * variant. The different options are:
776
+ * 1. Audio-Video
777
+ * 2. Audio-Only
778
+ * 3. Video-Only
779
+ *
780
+ * A manifest can only contain a single type because once we initialize media
781
+ * source to expect specific streams, it must always have content for those
782
+ * streams. If we were to start with audio+video and switch to an audio-only
783
+ * variant, media source would block waiting for video content.
784
+ *
785
+ * @param {shaka.extern.Manifest} manifest
786
+ * @private
787
+ */
788
+ static filterForAVVariants_(manifest) {
789
+ const isAVVariant = (variant) => {
790
+ // Audio-video variants may include both streams separately or may be
791
+ // single multiplexed streams with multiple codecs.
792
+ return (variant.video && variant.audio) ||
793
+ (variant.video && variant.video.codecs.includes(','));
794
+ };
795
+ if (manifest.variants.some(isAVVariant)) {
796
+ shaka.log.debug('Found variant with audio and video content, ' +
797
+ 'so filtering out audio-only content.');
798
+ manifest.variants = manifest.variants.filter(isAVVariant);
799
+ }
800
+ }
801
+ };
802
+
803
+ /**
804
+ * @typedef {{
805
+ * config: !shaka.extern.PlayerConfiguration,
806
+ * manifestPlayerInterface: !shaka.extern.ManifestParser.PlayerInterface,
807
+ * regionTimeline: !shaka.media.RegionTimeline,
808
+ * qualityObserver: ?shaka.media.QualityObserver,
809
+ * createDrmEngine: function():!shaka.media.DrmEngine,
810
+ * networkingEngine: !shaka.net.NetworkingEngine,
811
+ * manifestFilterer: !shaka.media.ManifestFilterer,
812
+ * allowPrefetch: boolean,
813
+ * allowMakeAbrManager: boolean
814
+ * }}
815
+ *
816
+ * @property {!shaka.extern.PlayerConfiguration} config
817
+ * @property {!shaka.extern.ManifestParser.PlayerInterface}
818
+ * manifestPlayerInterface
819
+ * @property {!shaka.media.RegionTimeline} regionTimeline
820
+ * @property {?shaka.media.QualityObserver} qualityObserver
821
+ * @property {function():!shaka.media.DrmEngine} createDrmEngine
822
+ * @property {!shaka.net.NetworkingEngine} networkingEngine
823
+ * @property {!shaka.media.ManifestFilterer} manifestFilterer
824
+ * @property {boolean} allowPrefetch
825
+ * @property {boolean} allowMakeAbrManager
826
+ */
827
+ shaka.media.PreloadManager.PlayerInterface;