shaka-player 4.10.13 → 4.11.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 (91) hide show
  1. package/README.md +5 -1
  2. package/dist/controls.css +3 -3
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +11 -11
  5. package/dist/locales.js +38 -34
  6. package/dist/shaka-player.compiled.d.ts +68 -33
  7. package/dist/shaka-player.compiled.debug.d.ts +68 -33
  8. package/dist/shaka-player.compiled.debug.externs.js +584 -544
  9. package/dist/shaka-player.compiled.debug.js +715 -676
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +584 -544
  12. package/dist/shaka-player.compiled.js +1394 -1371
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +76 -34
  15. package/dist/shaka-player.ui.debug.d.ts +76 -34
  16. package/dist/shaka-player.ui.debug.externs.js +77 -26
  17. package/dist/shaka-player.ui.debug.js +774 -720
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +77 -26
  20. package/dist/shaka-player.ui.js +1649 -1617
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/mediasession.js +10 -1
  23. package/externs/shaka/ads.js +79 -2
  24. package/externs/shaka/manifest.js +6 -1
  25. package/externs/shaka/manifest_parser.js +6 -2
  26. package/externs/shaka/net.js +23 -2
  27. package/externs/shaka/player.js +414 -103
  28. package/externs/shaka/text.js +2 -1
  29. package/lib/abr/simple_abr_manager.js +5 -0
  30. package/lib/ads/ad_manager.js +45 -1
  31. package/lib/ads/ad_utils.js +124 -1
  32. package/lib/ads/interstitial_ad.js +9 -2
  33. package/lib/ads/interstitial_ad_manager.js +216 -101
  34. package/lib/ads/media_tailor_ad.js +1 -39
  35. package/lib/ads/media_tailor_ad_manager.js +32 -16
  36. package/lib/cea/cea608_data_channel.js +40 -8
  37. package/lib/cea/cea608_memory.js +28 -0
  38. package/lib/dash/dash_parser.js +162 -25
  39. package/lib/dash/segment_base.js +3 -2
  40. package/lib/dash/segment_list.js +6 -3
  41. package/lib/dash/segment_template.js +104 -20
  42. package/lib/hls/hls_parser.js +319 -215
  43. package/lib/media/drm_engine.js +11 -3
  44. package/lib/media/media_source_engine.js +0 -8
  45. package/lib/media/preload_manager.js +16 -4
  46. package/lib/media/segment_reference.js +3 -0
  47. package/lib/media/segment_utils.js +8 -1
  48. package/lib/media/streaming_engine.js +10 -6
  49. package/lib/mss/mss_parser.js +14 -2
  50. package/lib/net/http_fetch_plugin.js +12 -5
  51. package/lib/net/http_xhr_plugin.js +7 -3
  52. package/lib/net/networking_engine.js +21 -1
  53. package/lib/offline/manifest_converter.js +1 -0
  54. package/lib/offline/storage.js +1 -0
  55. package/lib/player.js +336 -121
  56. package/lib/text/cue.js +373 -0
  57. package/lib/text/mp4_ttml_parser.js +51 -1
  58. package/lib/text/mp4_vtt_parser.js +2 -5
  59. package/lib/text/sbv_text_parser.js +2 -31
  60. package/lib/text/srt_text_parser.js +1 -1
  61. package/lib/text/text_engine.js +1 -1
  62. package/lib/text/text_utils.js +1 -0
  63. package/lib/text/ttml_text_parser.js +14 -4
  64. package/lib/text/ui_text_displayer.js +0 -4
  65. package/lib/text/vtt_text_parser.js +10 -415
  66. package/lib/util/abortable_operation.js +8 -0
  67. package/lib/util/cmcd_manager.js +38 -0
  68. package/lib/util/error.js +16 -0
  69. package/lib/util/fake_event.js +1 -0
  70. package/lib/util/manifest_parser_utils.js +12 -3
  71. package/lib/util/player_configuration.js +4 -2
  72. package/lib/util/stream_utils.js +57 -2
  73. package/lib/util/tXml.js +2 -1
  74. package/lib/util/text_parser.js +46 -0
  75. package/lib/util/ts_parser.js +32 -3
  76. package/package.json +1 -1
  77. package/shaka-player.uncompiled.js +1 -1
  78. package/ui/chapter_selection.js +179 -0
  79. package/ui/controls.js +151 -0
  80. package/ui/enums.js +1 -0
  81. package/ui/externs/ui.js +7 -1
  82. package/ui/less/containers.less +3 -4
  83. package/ui/less/overflow_menu.less +5 -0
  84. package/ui/less/tooltip.less +1 -1
  85. package/ui/locales/en.json +1 -0
  86. package/ui/locales/es.json +1 -0
  87. package/ui/locales/fr.json +1 -0
  88. package/ui/locales/source.json +4 -0
  89. package/ui/seek_bar.js +3 -2
  90. package/ui/ui.js +2 -0
  91. package/lib/polyfill/abort_controller.js +0 -158
@@ -24,6 +24,7 @@ goog.require('shaka.media.SegmentIndex');
24
24
  goog.require('shaka.media.SegmentReference');
25
25
  goog.require('shaka.net.DataUriPlugin');
26
26
  goog.require('shaka.net.NetworkingEngine');
27
+ goog.require('shaka.net.NetworkingEngine.PendingRequest');
27
28
  goog.require('shaka.util.ArrayUtils');
28
29
  goog.require('shaka.util.BufferUtils');
29
30
  goog.require('shaka.util.DrmUtils');
@@ -42,6 +43,7 @@ goog.require('shaka.util.Timer');
42
43
  goog.require('shaka.util.TsParser');
43
44
  goog.require('shaka.util.TXml');
44
45
  goog.require('shaka.util.Platform');
46
+ goog.require('shaka.util.StreamUtils');
45
47
  goog.require('shaka.util.Uint8ArrayUtils');
46
48
  goog.requireType('shaka.hls.Segment');
47
49
 
@@ -267,15 +269,26 @@ shaka.hls.HlsParser = class {
267
269
 
268
270
  /** @private {HTMLMediaElement} */
269
271
  this.mediaElement_ = null;
272
+
273
+ /** @private {?number} */
274
+ this.startTime_ = null;
275
+
276
+ /** @private {function():boolean} */
277
+ this.isPreloadFn_ = () => false;
270
278
  }
271
279
 
272
280
 
273
281
  /**
282
+ * @param {shaka.extern.ManifestConfiguration} config
283
+ * @param {(function():boolean)=} isPreloadFn
274
284
  * @override
275
285
  * @exportInterface
276
286
  */
277
- configure(config) {
287
+ configure(config, isPreloadFn) {
278
288
  this.config_ = config;
289
+ if (isPreloadFn) {
290
+ this.isPreloadFn_ = isPreloadFn;
291
+ }
279
292
 
280
293
  if (this.contentSteeringManager_) {
281
294
  this.contentSteeringManager_.configure(this.config_);
@@ -291,7 +304,7 @@ shaka.hls.HlsParser = class {
291
304
  this.playerInterface_ = playerInterface;
292
305
  this.lowLatencyMode_ = playerInterface.isLowLatencyMode();
293
306
 
294
- const response = await this.requestManifest_([uri]);
307
+ const response = await this.requestManifest_([uri]).promise;
295
308
 
296
309
  // Record the master playlist URI after redirects.
297
310
  this.masterPlaylistUri_ = response.uri;
@@ -456,8 +469,8 @@ shaka.hls.HlsParser = class {
456
469
  }
457
470
  let response;
458
471
  try {
459
- response =
460
- await this.requestManifest_(manifestUris, /* isPlaylist= */ true);
472
+ response = await this.requestManifest_(
473
+ manifestUris, /* isPlaylist= */ true).promise;
461
474
  } catch (e) {
462
475
  if (this.playerInterface_) {
463
476
  this.playerInterface_.disableStream(streamInfo.stream);
@@ -926,9 +939,10 @@ shaka.hls.HlsParser = class {
926
939
  this.parseCodecs_(variantTags);
927
940
 
928
941
  this.parseClosedCaptions_(mediaTags);
942
+ const iFrameStreams = this.parseIFrames_(iFrameTags);
929
943
  variants = await this.createVariantsForTags_(
930
944
  variantTags, sessionKeyTags, mediaTags, getUris,
931
- this.globalVariables_);
945
+ this.globalVariables_, iFrameStreams);
932
946
  textStreams = this.parseTexts_(mediaTags);
933
947
  imageStreams = await this.parseImages_(imageTags, iFrameTags);
934
948
  }
@@ -941,6 +955,8 @@ shaka.hls.HlsParser = class {
941
955
  shaka.util.Error.Code.OPERATION_ABORTED);
942
956
  }
943
957
 
958
+ this.determineStartTime_(playlist);
959
+
944
960
  // Single-variant streams aren't lazy-loaded, so for them we already have
945
961
  // enough info here to determine the presentation type and duration.
946
962
  if (playlist.type == shaka.hls.PlaylistType.MEDIA) {
@@ -975,6 +991,7 @@ shaka.hls.HlsParser = class {
975
991
  periodCount: 1,
976
992
  gapCount: 0,
977
993
  isLowLatency: false,
994
+ startTime: this.startTime_,
978
995
  };
979
996
 
980
997
  // If there is no 'CODECS' attribute in the manifest and codec guessing is
@@ -1045,7 +1062,7 @@ shaka.hls.HlsParser = class {
1045
1062
  const initType =
1046
1063
  shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT;
1047
1064
  const initResponse = await this.makeNetworkRequest_(
1048
- initSegmentRequest, requestType, {type: initType});
1065
+ initSegmentRequest, requestType, {type: initType}).promise;
1049
1066
  initData = initResponse.data;
1050
1067
  if (initSegmentRef.aesKey) {
1051
1068
  initData = await shaka.media.SegmentUtils.aesDecrypt(
@@ -1064,7 +1081,7 @@ shaka.hls.HlsParser = class {
1064
1081
  this.config_.retryParameters);
1065
1082
  const type = shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
1066
1083
  const response = await this.makeNetworkRequest_(
1067
- segmentRequest, requestType, {type});
1084
+ segmentRequest, requestType, {type}).promise;
1068
1085
  let data = response.data;
1069
1086
  if (segment.aesKey) {
1070
1087
  data = await shaka.media.SegmentUtils.aesDecrypt(
@@ -1137,14 +1154,7 @@ shaka.hls.HlsParser = class {
1137
1154
  const PresentationType = shaka.hls.HlsParser.PresentationType_;
1138
1155
 
1139
1156
  if (this.presentationType_ == PresentationType.LIVE) {
1140
- let segmentAvailabilityDuration = this.getLiveDuration_();
1141
-
1142
- // This defaults to the presentation delay, which has the effect of
1143
- // making the live stream unseekable. This is consistent with Apple's
1144
- // HLS implementation.
1145
- if (this.config_.hls.useSafariBehaviorForLive) {
1146
- segmentAvailabilityDuration = this.presentationTimeline_.getDelay();
1147
- }
1157
+ let segmentAvailabilityDuration = this.getLiveDuration_() || 0;
1148
1158
 
1149
1159
  // The app can override that with a longer duration, to allow seeking.
1150
1160
  if (!isNaN(this.config_.availabilityWindowOverride)) {
@@ -1167,7 +1177,8 @@ shaka.hls.HlsParser = class {
1167
1177
  if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
1168
1178
  continue;
1169
1179
  }
1170
- const firstReference = streamInfo.stream.segmentIndex.get(0);
1180
+ const firstReference =
1181
+ streamInfo.stream.segmentIndex.earliestReference();
1171
1182
  if (firstReference && firstReference.syncTime) {
1172
1183
  const syncTime = firstReference.syncTime;
1173
1184
  this.presentationTimeline_.setInitialProgramDateTime(syncTime);
@@ -1449,7 +1460,8 @@ shaka.hls.HlsParser = class {
1449
1460
  }
1450
1461
  try {
1451
1462
  const streamInfo = this.createStreamInfoFromIframeTag_(tag);
1452
- if (streamInfo.stream.codecs !== 'mjpg') {
1463
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
1464
+ if (streamInfo.stream.type !== ContentType.IMAGE) {
1453
1465
  return null;
1454
1466
  }
1455
1467
  return streamInfo.stream;
@@ -1494,6 +1506,26 @@ shaka.hls.HlsParser = class {
1494
1506
  }
1495
1507
  }
1496
1508
 
1509
+ /**
1510
+ * @param {!Array.<!shaka.hls.Tag>} iFrameTags from the playlist.
1511
+ * @return {!Array.<!shaka.extern.Stream>}
1512
+ * @private
1513
+ */
1514
+ parseIFrames_(iFrameTags) {
1515
+ // Create iFrame stream for each iFrame tag.
1516
+ const iFrameStreams = iFrameTags.map((tag) => {
1517
+ const streamInfo = this.createStreamInfoFromIframeTag_(tag);
1518
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
1519
+ if (streamInfo.stream.type !== ContentType.VIDEO) {
1520
+ return null;
1521
+ }
1522
+ return streamInfo.stream;
1523
+ });
1524
+
1525
+ // Filter mjpg iFrames
1526
+ return iFrameStreams.filter((s) => s);
1527
+ }
1528
+
1497
1529
  /**
1498
1530
  * @param {!Array.<!shaka.hls.Tag>} tags Variant tags from the playlist.
1499
1531
  * @param {!Array.<!shaka.hls.Tag>} sessionKeyTags EXT-X-SESSION-KEY tags
@@ -1501,12 +1533,13 @@ shaka.hls.HlsParser = class {
1501
1533
  * @param {!Array.<!shaka.hls.Tag>} mediaTags EXT-X-MEDIA tags from the
1502
1534
  * playlist.
1503
1535
  * @param {function():!Array.<string>} getUris
1504
- * @param {?Map.<string, string>=} variables
1536
+ * @param {?Map.<string, string>} variables
1537
+ * @param {!Array.<!shaka.extern.Stream>} iFrameStreams
1505
1538
  * @return {!Promise.<!Array.<!shaka.extern.Variant>>}
1506
1539
  * @private
1507
1540
  */
1508
1541
  async createVariantsForTags_(tags, sessionKeyTags, mediaTags, getUris,
1509
- variables) {
1542
+ variables, iFrameStreams) {
1510
1543
  // EXT-X-SESSION-KEY processing
1511
1544
  const drmInfos = [];
1512
1545
  const keyIds = new Set();
@@ -1529,7 +1562,8 @@ shaka.hls.HlsParser = class {
1529
1562
  const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
1530
1563
  const request = shaka.net.NetworkingEngine.makeRequest(
1531
1564
  keyUris, this.config_.retryParameters);
1532
- const keyResponse = this.makeNetworkRequest_(request, requestType);
1565
+ const keyResponse = this.makeNetworkRequest_(request, requestType)
1566
+ .promise;
1533
1567
  this.aesKeyMap_.set(keyMapKey, keyResponse);
1534
1568
  }
1535
1569
  continue;
@@ -1615,7 +1649,8 @@ shaka.hls.HlsParser = class {
1615
1649
  videoRange,
1616
1650
  videoLayout,
1617
1651
  drmInfos,
1618
- keyIds));
1652
+ keyIds,
1653
+ iFrameStreams));
1619
1654
  }
1620
1655
  return allVariants.filter((variant) => variant != null);
1621
1656
  }
@@ -1950,12 +1985,13 @@ shaka.hls.HlsParser = class {
1950
1985
  * @param {?string} videoLayout
1951
1986
  * @param {!Array.<shaka.extern.DrmInfo>} drmInfos
1952
1987
  * @param {!Set.<string>} keyIds
1988
+ * @param {!Array.<!shaka.extern.Stream>} iFrameStreams
1953
1989
  * @return {!Array.<!shaka.extern.Variant>}
1954
1990
  * @private
1955
1991
  */
1956
1992
  createVariants_(
1957
1993
  audioInfos, videoInfos, bandwidth, width, height, frameRate, videoRange,
1958
- videoLayout, drmInfos, keyIds) {
1994
+ videoLayout, drmInfos, keyIds, iFrameStreams) {
1959
1995
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1960
1996
  const DrmUtils = shaka.util.DrmUtils;
1961
1997
 
@@ -1990,6 +2026,8 @@ shaka.hls.HlsParser = class {
1990
2026
  if (videoStream) {
1991
2027
  videoStream.drmInfos = drmInfos;
1992
2028
  videoStream.keyIds = keyIds;
2029
+ shaka.util.StreamUtils.setBetterIFrameStream(
2030
+ videoStream, iFrameStreams);
1993
2031
  }
1994
2032
  if (videoStream && !audioStream) {
1995
2033
  videoStream.bandwidth = bandwidth;
@@ -2226,7 +2264,7 @@ shaka.hls.HlsParser = class {
2226
2264
  // lazy-load in this situation.
2227
2265
  await streamInfo.stream.createSegmentIndex();
2228
2266
 
2229
- const reference = streamInfo.stream.segmentIndex.get(0);
2267
+ const reference = streamInfo.stream.segmentIndex.earliestReference();
2230
2268
  const layout = reference.getTilesLayout();
2231
2269
  if (layout) {
2232
2270
  streamInfo.stream.width =
@@ -2257,11 +2295,15 @@ shaka.hls.HlsParser = class {
2257
2295
  goog.asserts.assert(tag.name == 'EXT-X-I-FRAME-STREAM-INF',
2258
2296
  'Should only be called on iframe tags!');
2259
2297
  /** @type {string} */
2260
- const type = shaka.util.ManifestParserUtils.ContentType.IMAGE;
2298
+ let type = shaka.util.ManifestParserUtils.ContentType.VIDEO;
2261
2299
 
2262
2300
  const verbatimIFramePlaylistUri = tag.getRequiredAttrValue('URI');
2263
2301
  const codecs = tag.getAttributeValue('CODECS') || '';
2264
2302
 
2303
+ if (codecs == 'mjpg') {
2304
+ type = shaka.util.ManifestParserUtils.ContentType.IMAGE;
2305
+ }
2306
+
2265
2307
  // Check if the stream has already been created as part of another Variant
2266
2308
  // and return it if it has.
2267
2309
  if (this.uriToStreamInfosMap_.has(verbatimIFramePlaylistUri)) {
@@ -2398,205 +2440,213 @@ shaka.hls.HlsParser = class {
2398
2440
  loadedOnce: false,
2399
2441
  };
2400
2442
 
2401
- /** @param {!AbortSignal} abortSignal */
2402
- const downloadSegmentIndex = async (abortSignal) => {
2443
+ /** @param {!shaka.net.NetworkingEngine.PendingRequest} pendingRequest */
2444
+ const downloadSegmentIndex = async (pendingRequest) => {
2403
2445
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
2404
2446
 
2405
- const uris = streamInfo.getUris();
2406
- // Download the actual manifest.
2407
- let response;
2408
2447
  try {
2409
- response = await this.requestManifest_(
2410
- streamInfo.getUris(), /* isPlaylist= */ true);
2411
- } catch (e) {
2412
- stream.closeSegmentIndex();
2413
- const handled = this.playerInterface_.disableStream(stream);
2414
- if (handled) {
2448
+ const uris = streamInfo.getUris();
2449
+ // Download the actual manifest.
2450
+ const response = await pendingRequest.promise;
2451
+ if (pendingRequest.aborted) {
2415
2452
  return;
2416
- } else {
2417
- throw e;
2418
2453
  }
2419
- }
2420
- if (abortSignal.aborted) {
2421
- return;
2422
- }
2423
2454
 
2424
- // Record the final URI after redirects.
2425
- const responseUri = response.uri;
2426
- if (responseUri != response.originalUri && !uris.includes(responseUri)) {
2427
- redirectUris.push(responseUri);
2428
- }
2455
+ // Record the final URI after redirects.
2456
+ const responseUri = response.uri;
2457
+ if (responseUri != response.originalUri &&
2458
+ !uris.includes(responseUri)) {
2459
+ redirectUris.push(responseUri);
2460
+ }
2429
2461
 
2430
- // Record the redirected, final URI of this media playlist when we parse
2431
- // it.
2432
- /** @type {!shaka.hls.Playlist} */
2433
- const playlist = this.manifestTextParser_.parsePlaylist(response.data);
2462
+ // Record the redirected, final URI of this media playlist when we parse
2463
+ // it.
2464
+ /** @type {!shaka.hls.Playlist} */
2465
+ const playlist = this.manifestTextParser_.parsePlaylist(response.data);
2434
2466
 
2435
- if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
2436
- throw new shaka.util.Error(
2437
- shaka.util.Error.Severity.CRITICAL,
2438
- shaka.util.Error.Category.MANIFEST,
2439
- shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
2440
- }
2467
+ if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
2468
+ throw new shaka.util.Error(
2469
+ shaka.util.Error.Severity.CRITICAL,
2470
+ shaka.util.Error.Category.MANIFEST,
2471
+ shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
2472
+ }
2441
2473
 
2442
- /** @type {!Array.<!shaka.hls.Tag>} */
2443
- const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
2444
- 'EXT-X-DEFINE');
2474
+ /** @type {!Array.<!shaka.hls.Tag>} */
2475
+ const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
2476
+ 'EXT-X-DEFINE');
2445
2477
 
2446
- const mediaVariables =
2447
- this.parseMediaVariables_(variablesTags, responseUri);
2478
+ const mediaVariables =
2479
+ this.parseMediaVariables_(variablesTags, responseUri);
2448
2480
 
2449
- const mimeType = undefined;
2481
+ const mimeType = undefined;
2450
2482
 
2451
- let requestBasicInfo = false;
2483
+ let requestBasicInfo = false;
2452
2484
 
2453
- // If no codec info was provided in the manifest and codec guessing is
2454
- // disabled we try to get necessary info from the media data.
2455
- if ((!this.codecInfoInManifest_ &&
2456
- this.config_.hls.disableCodecGuessing) ||
2457
- (this.needsClosedCaptionsDetection_ && type == ContentType.VIDEO &&
2458
- !this.config_.hls.disableClosedCaptionsDetection)) {
2459
- if (playlist.segments.length > 0) {
2460
- this.needsClosedCaptionsDetection_ = false;
2461
- requestBasicInfo = true;
2485
+ // If no codec info was provided in the manifest and codec guessing is
2486
+ // disabled we try to get necessary info from the media data.
2487
+ if ((!this.codecInfoInManifest_ &&
2488
+ this.config_.hls.disableCodecGuessing) ||
2489
+ (this.needsClosedCaptionsDetection_ && type == ContentType.VIDEO &&
2490
+ !this.config_.hls.disableClosedCaptionsDetection)) {
2491
+ if (playlist.segments.length > 0) {
2492
+ this.needsClosedCaptionsDetection_ = false;
2493
+ requestBasicInfo = true;
2494
+ }
2462
2495
  }
2463
- }
2464
2496
 
2465
- const allowOverrideMimeType = !this.codecInfoInManifest_ &&
2466
- this.config_.hls.disableCodecGuessing;
2497
+ const allowOverrideMimeType = !this.codecInfoInManifest_ &&
2498
+ this.config_.hls.disableCodecGuessing;
2467
2499
 
2468
- const wasLive = this.isLive_();
2469
- const realStreamInfo = await this.convertParsedPlaylistIntoStreamInfo_(
2470
- streamId, mediaVariables, playlist, getUris, responseUri, codecs,
2471
- type, languageValue, primary, name, channelsCount, closedCaptions,
2472
- characteristics, forced, sampleRate, spatialAudio, mimeType,
2473
- requestBasicInfo, allowOverrideMimeType);
2474
- if (abortSignal.aborted) {
2475
- return;
2476
- }
2500
+ const wasLive = this.isLive_();
2501
+ const realStreamInfo = await this.convertParsedPlaylistIntoStreamInfo_(
2502
+ streamId, mediaVariables, playlist, getUris, responseUri, codecs,
2503
+ type, languageValue, primary, name, channelsCount, closedCaptions,
2504
+ characteristics, forced, sampleRate, spatialAudio, mimeType,
2505
+ requestBasicInfo, allowOverrideMimeType);
2506
+ if (pendingRequest.aborted) {
2507
+ return;
2508
+ }
2477
2509
 
2478
- const realStream = realStreamInfo.stream;
2510
+ const realStream = realStreamInfo.stream;
2479
2511
 
2480
- if (this.isLive_() && !wasLive) {
2481
- // Now that we know that the presentation is live, convert the timeline
2482
- // to live.
2483
- this.changePresentationTimelineToLive_(playlist);
2484
- }
2512
+ this.determineStartTime_(playlist);
2485
2513
 
2486
- // Copy values from the real stream info to our initial one.
2487
- streamInfo.minTimestamp = realStreamInfo.minTimestamp;
2488
- streamInfo.maxTimestamp = realStreamInfo.maxTimestamp;
2489
- streamInfo.canSkipSegments = realStreamInfo.canSkipSegments;
2490
- streamInfo.canBlockReload = realStreamInfo.canBlockReload;
2491
- streamInfo.hasEndList = realStreamInfo.hasEndList;
2492
- streamInfo.mediaSequenceToStartTime =
2493
- realStreamInfo.mediaSequenceToStartTime;
2494
- streamInfo.nextMediaSequence = realStreamInfo.nextMediaSequence;
2495
- streamInfo.nextPart = realStreamInfo.nextPart;
2496
- streamInfo.loadedOnce = true;
2497
- stream.segmentIndex = realStream.segmentIndex;
2498
- stream.encrypted = realStream.encrypted;
2499
- stream.drmInfos = realStream.drmInfos;
2500
- stream.keyIds = realStream.keyIds;
2501
- stream.mimeType = realStream.mimeType;
2502
- stream.bandwidth = stream.bandwidth || realStream.bandwidth;
2503
- stream.codecs = stream.codecs || realStream.codecs;
2504
- stream.closedCaptions =
2505
- stream.closedCaptions || realStream.closedCaptions;
2506
- stream.width = stream.width || realStream.width;
2507
- stream.height = stream.height || realStream.height;
2508
- stream.hdr = stream.hdr || realStream.hdr;
2509
- stream.colorGamut = stream.colorGamut || realStream.colorGamut;
2510
- if (stream.language == 'und' && realStream.language != 'und') {
2511
- stream.language = realStream.language;
2512
- }
2513
- stream.language = stream.language || realStream.language;
2514
- stream.channelsCount = stream.channelsCount || realStream.channelsCount;
2515
- stream.audioSamplingRate =
2516
- stream.audioSamplingRate || realStream.audioSamplingRate;
2517
- this.setFullTypeForStream_(stream);
2518
-
2519
- // Since we lazy-loaded this content, the player may need to create new
2520
- // sessions for the DRM info in this stream.
2521
- if (stream.drmInfos.length) {
2522
- this.playerInterface_.newDrmInfo(stream);
2523
- }
2524
-
2525
- let closedCaptionsUpdated = false;
2526
- if ((!closedCaptions && stream.closedCaptions) ||
2527
- (closedCaptions && stream.closedCaptions &&
2528
- closedCaptions.size != stream.closedCaptions.size)) {
2529
- closedCaptionsUpdated = true;
2530
- }
2531
-
2532
- if (this.manifest_ && closedCaptionsUpdated) {
2533
- this.playerInterface_.makeTextStreamsForClosedCaptions(this.manifest_);
2534
- }
2535
-
2536
- if (type == ContentType.VIDEO || type == ContentType.AUDIO) {
2537
- for (const otherStreamInfo of this.uriToStreamInfosMap_.values()) {
2538
- if (!otherStreamInfo.loadedOnce && otherStreamInfo.type == type) {
2539
- // To aid manifest filtering, assume before loading that all video
2540
- // renditions have the same MIME type. (And likewise for audio.)
2541
- otherStreamInfo.stream.mimeType = realStream.mimeType;
2542
- this.setFullTypeForStream_(otherStreamInfo.stream);
2543
- }
2514
+ if (this.isLive_() && !wasLive) {
2515
+ // Now that we know that the presentation is live, convert the
2516
+ // timeline to live.
2517
+ this.changePresentationTimelineToLive_(playlist);
2544
2518
  }
2545
- }
2546
2519
 
2547
- if (type == ContentType.TEXT) {
2548
- const firstSegment = realStream.segmentIndex.get(0);
2549
- if (firstSegment && firstSegment.initSegmentReference) {
2550
- stream.mimeType = 'application/mp4';
2551
- this.setFullTypeForStream_(stream);
2520
+ // Copy values from the real stream info to our initial one.
2521
+ streamInfo.minTimestamp = realStreamInfo.minTimestamp;
2522
+ streamInfo.maxTimestamp = realStreamInfo.maxTimestamp;
2523
+ streamInfo.canSkipSegments = realStreamInfo.canSkipSegments;
2524
+ streamInfo.canBlockReload = realStreamInfo.canBlockReload;
2525
+ streamInfo.hasEndList = realStreamInfo.hasEndList;
2526
+ streamInfo.mediaSequenceToStartTime =
2527
+ realStreamInfo.mediaSequenceToStartTime;
2528
+ streamInfo.nextMediaSequence = realStreamInfo.nextMediaSequence;
2529
+ streamInfo.nextPart = realStreamInfo.nextPart;
2530
+ streamInfo.loadedOnce = true;
2531
+ stream.segmentIndex = realStream.segmentIndex;
2532
+ stream.encrypted = realStream.encrypted;
2533
+ stream.drmInfos = realStream.drmInfos;
2534
+ stream.keyIds = realStream.keyIds;
2535
+ stream.mimeType = realStream.mimeType;
2536
+ stream.bandwidth = stream.bandwidth || realStream.bandwidth;
2537
+ stream.codecs = stream.codecs || realStream.codecs;
2538
+ stream.closedCaptions =
2539
+ stream.closedCaptions || realStream.closedCaptions;
2540
+ stream.width = stream.width || realStream.width;
2541
+ stream.height = stream.height || realStream.height;
2542
+ stream.hdr = stream.hdr || realStream.hdr;
2543
+ stream.colorGamut = stream.colorGamut || realStream.colorGamut;
2544
+ stream.frameRate = stream.frameRate || realStream.frameRate;
2545
+ if (stream.language == 'und' && realStream.language != 'und') {
2546
+ stream.language = realStream.language;
2547
+ }
2548
+ stream.language = stream.language || realStream.language;
2549
+ stream.channelsCount = stream.channelsCount || realStream.channelsCount;
2550
+ stream.audioSamplingRate =
2551
+ stream.audioSamplingRate || realStream.audioSamplingRate;
2552
+ this.setFullTypeForStream_(stream);
2553
+
2554
+ // Since we lazy-loaded this content, the player may need to create new
2555
+ // sessions for the DRM info in this stream.
2556
+ if (stream.drmInfos.length) {
2557
+ this.playerInterface_.newDrmInfo(stream);
2552
2558
  }
2553
- }
2554
2559
 
2555
- const qualityInfo =
2556
- shaka.media.QualityObserver.createQualityInfo(stream);
2557
- stream.segmentIndex.forEachTopLevelReference((reference) => {
2558
- if (reference.initSegmentReference) {
2559
- reference.initSegmentReference.mediaQuality = qualityInfo;
2560
+ let closedCaptionsUpdated = false;
2561
+ if ((!closedCaptions && stream.closedCaptions) ||
2562
+ (closedCaptions && stream.closedCaptions &&
2563
+ closedCaptions.size != stream.closedCaptions.size)) {
2564
+ closedCaptionsUpdated = true;
2565
+ }
2566
+
2567
+ if (this.manifest_ && closedCaptionsUpdated) {
2568
+ this.playerInterface_.makeTextStreamsForClosedCaptions(
2569
+ this.manifest_);
2560
2570
  }
2561
- });
2562
2571
 
2563
- // Add finishing touches to the stream that can only be done once we have
2564
- // more full context on the media as a whole.
2565
- if (this.hasEnoughInfoToFinalizeStreams_()) {
2566
- if (!this.streamsFinalized_) {
2567
- // Mark this manifest as having been finalized, so we don't go through
2568
- // this whole process of finishing touches a second time.
2569
- this.streamsFinalized_ = true;
2570
- // Finalize all of the currently-loaded streams.
2571
- const streamInfos = Array.from(this.uriToStreamInfosMap_.values());
2572
- const activeStreamInfos =
2573
- streamInfos.filter((s) => s.stream.segmentIndex);
2574
- this.finalizeStreams_(activeStreamInfos);
2575
- // With the addition of this new stream, we now have enough info to
2576
- // figure out how long the streams should be. So process all streams
2577
- // we have downloaded up until this point.
2578
- this.determineDuration_();
2579
- // Finally, start the update timer, if this asset has been determined
2580
- // to be a livestream.
2581
- const delay = this.getUpdatePlaylistDelay_();
2582
- if (delay > 0) {
2583
- this.updatePlaylistTimer_.tickAfter(/* seconds= */ delay);
2572
+ if (type == ContentType.VIDEO || type == ContentType.AUDIO) {
2573
+ for (const otherStreamInfo of this.uriToStreamInfosMap_.values()) {
2574
+ if (!otherStreamInfo.loadedOnce && otherStreamInfo.type == type) {
2575
+ // To aid manifest filtering, assume before loading that all video
2576
+ // renditions have the same MIME type. (And likewise for audio.)
2577
+ otherStreamInfo.stream.mimeType = realStream.mimeType;
2578
+ this.setFullTypeForStream_(otherStreamInfo.stream);
2579
+ }
2584
2580
  }
2585
- } else {
2586
- // We don't need to go through the full process; just finalize this
2587
- // single stream.
2588
- this.finalizeStreams_([streamInfo]);
2589
2581
  }
2590
- }
2591
2582
 
2592
- this.processDateRangeTags_(
2593
- playlist.tags, stream.type, mediaVariables, getUris);
2583
+ if (type == ContentType.TEXT) {
2584
+ const firstSegment = realStream.segmentIndex.earliestReference();
2585
+ if (firstSegment && firstSegment.initSegmentReference) {
2586
+ stream.mimeType = 'application/mp4';
2587
+ this.setFullTypeForStream_(stream);
2588
+ }
2589
+ }
2590
+
2591
+ const qualityInfo =
2592
+ shaka.media.QualityObserver.createQualityInfo(stream);
2593
+ stream.segmentIndex.forEachTopLevelReference((reference) => {
2594
+ if (reference.initSegmentReference) {
2595
+ reference.initSegmentReference.mediaQuality = qualityInfo;
2596
+ }
2597
+ });
2598
+
2599
+ // Add finishing touches to the stream that can only be done once we
2600
+ // have more full context on the media as a whole.
2601
+ if (this.hasEnoughInfoToFinalizeStreams_()) {
2602
+ if (!this.streamsFinalized_) {
2603
+ // Mark this manifest as having been finalized, so we don't go
2604
+ // through this whole process of finishing touches a second time.
2605
+ this.streamsFinalized_ = true;
2606
+ // Finalize all of the currently-loaded streams.
2607
+ const streamInfos = Array.from(this.uriToStreamInfosMap_.values());
2608
+ const activeStreamInfos =
2609
+ streamInfos.filter((s) => s.stream.segmentIndex);
2610
+ this.finalizeStreams_(activeStreamInfos);
2611
+ // With the addition of this new stream, we now have enough info to
2612
+ // figure out how long the streams should be. So process all streams
2613
+ // we have downloaded up until this point.
2614
+ this.determineDuration_();
2615
+ // Finally, start the update timer, if this asset has been
2616
+ // determined to be a livestream.
2617
+ const delay = this.getUpdatePlaylistDelay_();
2618
+ if (delay > 0) {
2619
+ this.updatePlaylistTimer_.tickAfter(/* seconds= */ delay);
2620
+ }
2621
+ } else {
2622
+ // We don't need to go through the full process; just finalize this
2623
+ // single stream.
2624
+ this.finalizeStreams_([streamInfo]);
2625
+ }
2626
+ }
2627
+
2628
+ this.processDateRangeTags_(
2629
+ playlist.tags, stream.type, mediaVariables, getUris);
2630
+
2631
+ if (this.manifest_) {
2632
+ this.manifest_.startTime = this.startTime_;
2633
+ }
2634
+ } catch (e) {
2635
+ stream.closeSegmentIndex();
2636
+ if (e.code === shaka.util.Error.Code.OPERATION_ABORTED) {
2637
+ return;
2638
+ }
2639
+ const handled = this.playerInterface_.disableStream(stream);
2640
+ if (!handled) {
2641
+ throw e;
2642
+ }
2643
+ }
2594
2644
  };
2595
2645
 
2596
2646
  /** @type {Promise} */
2597
2647
  let creationPromise = null;
2598
- /** @type {!AbortController} */
2599
- let abortController = new AbortController();
2648
+ /** @type {!shaka.net.NetworkingEngine.PendingRequest} */
2649
+ let pendingRequest;
2600
2650
  const safeCreateSegmentIndex = () => {
2601
2651
  // An operation is already in progress. The second and subsequent
2602
2652
  // callers receive the same Promise as the first caller, and only one
@@ -2605,15 +2655,16 @@ shaka.hls.HlsParser = class {
2605
2655
  return creationPromise;
2606
2656
  }
2607
2657
 
2608
- // Create a new AbortController to be able to cancel this specific
2658
+ // Create a new PendingRequest to be able to cancel this specific
2609
2659
  // download.
2610
- abortController = new AbortController();
2660
+ pendingRequest = this.requestManifest_(streamInfo.getUris(),
2661
+ /* isPlaylist= */ true);
2611
2662
 
2612
2663
  // Create a Promise tied to the outcome of downloadSegmentIndex(). If
2613
2664
  // downloadSegmentIndex is rejected, creationPromise will also be
2614
2665
  // rejected.
2615
2666
  creationPromise = new Promise((resolve) => {
2616
- resolve(downloadSegmentIndex(abortController.signal));
2667
+ resolve(downloadSegmentIndex(pendingRequest));
2617
2668
  });
2618
2669
  return creationPromise;
2619
2670
  };
@@ -2623,7 +2674,7 @@ shaka.hls.HlsParser = class {
2623
2674
  stream.closeSegmentIndex = () => {
2624
2675
  // If we're mid-creation, cancel it.
2625
2676
  if (creationPromise && !stream.segmentIndex) {
2626
- abortController.abort();
2677
+ pendingRequest.abort();
2627
2678
  }
2628
2679
  // If we have a segment index, release it.
2629
2680
  if (stream.segmentIndex) {
@@ -2781,8 +2832,20 @@ shaka.hls.HlsParser = class {
2781
2832
  channelsCount, closedCaptions, characteristics, forced, sampleRate,
2782
2833
  spatialAudio, mimeType = undefined, requestBasicInfo = true,
2783
2834
  allowOverrideMimeType = true) {
2784
- goog.asserts.assert(playlist.segments != null,
2785
- 'Media playlist should have segments!');
2835
+ const playlistSegments = playlist.segments || [];
2836
+ const allAreMissing = playlistSegments.every((seg) => {
2837
+ if (shaka.hls.Utils.getFirstTagWithName(seg.tags, 'EXT-X-GAP')) {
2838
+ return true;
2839
+ }
2840
+ return false;
2841
+ });
2842
+
2843
+ if (!playlistSegments.length || allAreMissing) {
2844
+ throw new shaka.util.Error(
2845
+ shaka.util.Error.Severity.CRITICAL,
2846
+ shaka.util.Error.Category.MANIFEST,
2847
+ shaka.util.Error.Code.HLS_EMPTY_MEDIA_PLAYLIST);
2848
+ }
2786
2849
 
2787
2850
  this.determinePresentationType_(playlist);
2788
2851
 
@@ -2800,6 +2863,7 @@ shaka.hls.HlsParser = class {
2800
2863
  let height = null;
2801
2864
  let videoRange = null;
2802
2865
  let colorGamut = null;
2866
+ let frameRate = null;
2803
2867
  if (segments.length > 0 && requestBasicInfo) {
2804
2868
  const basicInfo = await this.getBasicInfoFromSegments_(segments);
2805
2869
 
@@ -2815,6 +2879,7 @@ shaka.hls.HlsParser = class {
2815
2879
  width = basicInfo.width;
2816
2880
  videoRange = basicInfo.videoRange;
2817
2881
  colorGamut = basicInfo.colorGamut;
2882
+ frameRate = basicInfo.frameRate;
2818
2883
 
2819
2884
  if (allowOverrideMimeType) {
2820
2885
  mimeType = basicInfo.mimeType;
@@ -2851,8 +2916,7 @@ shaka.hls.HlsParser = class {
2851
2916
  if (type == shaka.util.ManifestParserUtils.ContentType.VIDEO &&
2852
2917
  (width || height || videoRange || colorGamut)) {
2853
2918
  this.addVideoAttributes_(stream, width, height,
2854
- /* frameRate= */ null, videoRange, /* videoLayout= */ null,
2855
- colorGamut);
2919
+ frameRate, videoRange, /* videoLayout= */ null, colorGamut);
2856
2920
  }
2857
2921
 
2858
2922
  // This new calculation is necessary for Low Latency streams.
@@ -3202,7 +3266,8 @@ shaka.hls.HlsParser = class {
3202
3266
  const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
3203
3267
  const request = shaka.net.NetworkingEngine.makeRequest(
3204
3268
  keyUris, this.config_.retryParameters);
3205
- const keyResponse = this.makeNetworkRequest_(request, requestType);
3269
+ const keyResponse = this.makeNetworkRequest_(request, requestType)
3270
+ .promise;
3206
3271
  this.aesKeyMap_.set(keyMapKey, keyResponse);
3207
3272
  }
3208
3273
  const keyResponse = await this.aesKeyMap_.get(keyMapKey);
@@ -3230,6 +3295,24 @@ shaka.hls.HlsParser = class {
3230
3295
  }
3231
3296
 
3232
3297
 
3298
+ /**
3299
+ * @param {!shaka.hls.Playlist} playlist
3300
+ * @private
3301
+ */
3302
+ determineStartTime_(playlist) {
3303
+ // If we already have a starttime we avoid processing this again.
3304
+ if (this.startTime_ != null) {
3305
+ return;
3306
+ }
3307
+ const startTimeTag =
3308
+ shaka.hls.Utils.getFirstTagWithName(playlist.tags, 'EXT-X-START');
3309
+ if (startTimeTag) {
3310
+ this.startTime_ =
3311
+ Number(startTimeTag.getRequiredAttrValue('TIME-OFFSET'));
3312
+ }
3313
+ }
3314
+
3315
+
3233
3316
  /**
3234
3317
  * @param {!shaka.hls.Playlist} playlist
3235
3318
  * @private
@@ -3359,7 +3442,7 @@ shaka.hls.HlsParser = class {
3359
3442
  // We accomplish this in our DASH-y model by setting a presentation
3360
3443
  // delay of configured value, or 3 segments duration if not configured.
3361
3444
  // This will be the "live edge" of the presentation.
3362
- let presentationDelay;
3445
+ let presentationDelay = 0;
3363
3446
  if (this.config_.defaultPresentationDelay) {
3364
3447
  presentationDelay = this.config_.defaultPresentationDelay;
3365
3448
  } else if (this.lowLatencyPresentationDelay_) {
@@ -3367,12 +3450,27 @@ shaka.hls.HlsParser = class {
3367
3450
  } else if (this.presentationDelay_) {
3368
3451
  presentationDelay = this.presentationDelay_;
3369
3452
  } else {
3370
- const playlistSegments = playlist.segments.length;
3453
+ const totalSegments = playlist.segments.length;
3371
3454
  let delaySegments = this.config_.hls.liveSegmentsDelay;
3372
- if (delaySegments > (playlistSegments - 2)) {
3373
- delaySegments = Math.max(1, playlistSegments - 2);
3455
+ if (delaySegments > (totalSegments - 2)) {
3456
+ delaySegments = Math.max(1, totalSegments - 2);
3457
+ }
3458
+ for (let i = totalSegments - delaySegments; i < totalSegments; i++) {
3459
+ const extinfTag = shaka.hls.Utils.getFirstTagWithName(
3460
+ playlist.segments[i].tags, 'EXTINF');
3461
+ if (extinfTag) {
3462
+ const extinfValues = extinfTag.value.split(',');
3463
+ const duration = Number(extinfValues[0]);
3464
+ presentationDelay += Math.ceil(duration);
3465
+ } else {
3466
+ presentationDelay += this.maxTargetDuration_;
3467
+ }
3374
3468
  }
3375
- presentationDelay = this.maxTargetDuration_ * delaySegments;
3469
+ }
3470
+
3471
+ if (this.startTime_ && this.startTime_ < 0) {
3472
+ presentationDelay = Math.min(-this.startTime_, presentationDelay);
3473
+ this.startTime_ += presentationDelay;
3376
3474
  }
3377
3475
 
3378
3476
  this.presentationTimeline_.setPresentationStartTime(0);
@@ -3937,7 +4035,6 @@ shaka.hls.HlsParser = class {
3937
4035
  // Exclude these attributes from the metadata since they already go into
3938
4036
  // other fields (eg: startTime or endTime) or are not necessary..
3939
4037
  const excludedAttributes = [
3940
- 'ID',
3941
4038
  'CLASS',
3942
4039
  'START-DATE',
3943
4040
  'END-DATE',
@@ -3967,7 +4064,8 @@ shaka.hls.HlsParser = class {
3967
4064
  values.push(metadataFrame);
3968
4065
  }
3969
4066
 
3970
- if (values.length) {
4067
+ // ID is always required. So we need more than 1 value.
4068
+ if (values.length > 1) {
3971
4069
  this.playerInterface_.onMetadata(type, startTime, endTime, values);
3972
4070
  }
3973
4071
 
@@ -4021,7 +4119,8 @@ shaka.hls.HlsParser = class {
4021
4119
  if (firstStartTime === 0 &&
4022
4120
  this.presentationType_ == shaka.hls.HlsParser.PresentationType_.LIVE &&
4023
4121
  mediaSequenceToStartTime.size > 0 &&
4024
- !mediaSequenceToStartTime.has(position)) {
4122
+ !mediaSequenceToStartTime.has(position) &&
4123
+ this.presentationTimeline_.getPresentationStartTime() != null) {
4025
4124
  firstStartTime = this.presentationTimeline_.getSegmentAvailabilityStart();
4026
4125
  }
4027
4126
 
@@ -4351,7 +4450,7 @@ shaka.hls.HlsParser = class {
4351
4450
  try {
4352
4451
  headRequest.method = 'HEAD';
4353
4452
  const response = await this.makeNetworkRequest_(
4354
- headRequest, requestType, {type});
4453
+ headRequest, requestType, {type}).promise;
4355
4454
 
4356
4455
  contentMimeType = response.headers['content-type'];
4357
4456
  } catch (error) {
@@ -4360,7 +4459,7 @@ shaka.hls.HlsParser = class {
4360
4459
  error.code == shaka.util.Error.Code.BAD_HTTP_STATUS)) {
4361
4460
  headRequest.method = 'GET';
4362
4461
  const response = await this.makeNetworkRequest_(
4363
- headRequest, requestType, {type});
4462
+ headRequest, requestType, {type}).promise;
4364
4463
 
4365
4464
  contentMimeType = response.headers['content-type'];
4366
4465
  }
@@ -4423,7 +4522,7 @@ shaka.hls.HlsParser = class {
4423
4522
  *
4424
4523
  * @param {!Array.<string>} uris
4425
4524
  * @param {boolean=} isPlaylist
4426
- * @return {!Promise.<!shaka.extern.Response>}
4525
+ * @return {!shaka.net.NetworkingEngine.PendingRequest}
4427
4526
  * @private
4428
4527
  */
4429
4528
  requestManifest_(uris, isPlaylist) {
@@ -4556,7 +4655,7 @@ shaka.hls.HlsParser = class {
4556
4655
  * @param {shaka.extern.Request} request
4557
4656
  * @param {shaka.net.NetworkingEngine.RequestType} type
4558
4657
  * @param {shaka.extern.RequestContext=} context
4559
- * @return {!Promise.<shaka.extern.Response>}
4658
+ * @return {!shaka.net.NetworkingEngine.PendingRequest}
4560
4659
  * @private
4561
4660
  */
4562
4661
  makeNetworkRequest_(request, type, context) {
@@ -4567,11 +4666,15 @@ shaka.hls.HlsParser = class {
4567
4666
  shaka.util.Error.Code.OPERATION_ABORTED);
4568
4667
  }
4569
4668
 
4669
+ if (!context) {
4670
+ context = {};
4671
+ }
4672
+ context.isPreload = this.isPreloadFn_();
4570
4673
  const op = this.playerInterface_.networkingEngine.request(
4571
4674
  type, request, context);
4572
4675
  this.operationManager_.manage(op);
4573
4676
 
4574
- return op.promise;
4677
+ return op;
4575
4678
  }
4576
4679
 
4577
4680
  /**
@@ -4769,7 +4872,8 @@ shaka.hls.HlsParser = class {
4769
4872
  const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
4770
4873
  const request = shaka.net.NetworkingEngine.makeRequest(
4771
4874
  keyUris, this.config_.retryParameters);
4772
- const keyResponse = this.makeNetworkRequest_(request, requestType);
4875
+ const keyResponse = this.makeNetworkRequest_(request, requestType)
4876
+ .promise;
4773
4877
  this.identityKeyMap_.set(keyMapKey, keyResponse);
4774
4878
  }
4775
4879
  const keyResponse = await this.identityKeyMap_.get(keyMapKey);
@@ -4801,7 +4905,7 @@ shaka.hls.HlsParser = class {
4801
4905
  const initType =
4802
4906
  shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT;
4803
4907
  const initResponse = await this.makeNetworkRequest_(
4804
- initSegmentRequest, requestType, {type: initType});
4908
+ initSegmentRequest, requestType, {type: initType}).promise;
4805
4909
 
4806
4910
  defaultKID = shaka.media.SegmentUtils.getDefaultKID(
4807
4911
  initResponse.data);