rx-player 3.29.0-dev.2022091600 → 3.29.0-dev.2022103100

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 (184) hide show
  1. package/CHANGELOG.md +22 -10
  2. package/MAINTAINERS.md +2 -1
  3. package/VERSION +1 -1
  4. package/dist/_esm5.processed/compat/browser_detection.d.ts +4 -1
  5. package/dist/_esm5.processed/compat/browser_detection.js +7 -1
  6. package/dist/_esm5.processed/compat/can_reuse_media_keys.d.ts +12 -0
  7. package/dist/_esm5.processed/compat/can_reuse_media_keys.js +15 -0
  8. package/dist/_esm5.processed/compat/enable_audio_track.d.ts +11 -0
  9. package/dist/_esm5.processed/compat/enable_audio_track.js +26 -0
  10. package/dist/_esm5.processed/compat/index.d.ts +4 -2
  11. package/dist/_esm5.processed/compat/index.js +4 -2
  12. package/dist/_esm5.processed/compat/{should_renew_media_keys.d.ts → should_renew_media_key_system_access.d.ts} +3 -3
  13. package/dist/_esm5.processed/compat/{should_renew_media_keys.js → should_renew_media_key_system_access.js} +3 -3
  14. package/dist/_esm5.processed/config.d.ts +1 -0
  15. package/dist/_esm5.processed/core/api/playback_observer.d.ts +14 -5
  16. package/dist/_esm5.processed/core/api/playback_observer.js +54 -22
  17. package/dist/_esm5.processed/core/api/public_api.d.ts +7 -4
  18. package/dist/_esm5.processed/core/api/public_api.js +79 -61
  19. package/dist/_esm5.processed/core/api/tracks_management/media_element_track_choice_manager.js +9 -10
  20. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +1 -1
  21. package/dist/_esm5.processed/core/decrypt/find_key_system.js +3 -3
  22. package/dist/_esm5.processed/core/decrypt/get_media_keys.js +4 -1
  23. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +98 -0
  24. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +173 -0
  25. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +4 -4
  26. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +2 -1
  27. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +15 -16
  28. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.d.ts +3 -1
  29. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.js +5 -2
  30. package/dist/_esm5.processed/core/fetchers/utils/schedule_request.d.ts +103 -0
  31. package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +414 -0
  32. package/dist/_esm5.processed/core/init/content_time_boundaries_observer.d.ts +4 -3
  33. package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +21 -22
  34. package/dist/_esm5.processed/core/init/initialize_media_source.d.ts +18 -4
  35. package/dist/_esm5.processed/core/init/initialize_media_source.js +10 -2
  36. package/dist/_esm5.processed/core/init/load_on_media_source.js +7 -1
  37. package/dist/_esm5.processed/core/init/stall_avoider.js +3 -1
  38. package/dist/_esm5.processed/default_config.d.ts +11 -0
  39. package/dist/_esm5.processed/default_config.js +11 -0
  40. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +1 -1
  41. package/dist/_esm5.processed/manifest/manifest.js +11 -2
  42. package/dist/_esm5.processed/manifest/representation.d.ts +12 -1
  43. package/dist/_esm5.processed/manifest/representation.js +1 -0
  44. package/dist/_esm5.processed/manifest/representation_index/static.d.ts +1 -1
  45. package/dist/_esm5.processed/manifest/representation_index/static.js +2 -2
  46. package/dist/_esm5.processed/manifest/representation_index/types.d.ts +32 -15
  47. package/dist/_esm5.processed/manifest/update_period_in_place.js +1 -0
  48. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.d.ts +10 -9
  49. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.js +11 -11
  50. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_init_segment.d.ts +1 -1
  51. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_init_segment.js +3 -3
  52. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_segments_from_timeline.d.ts +1 -1
  53. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_segments_from_timeline.js +4 -4
  54. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.d.ts +7 -7
  55. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.js +12 -8
  56. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.d.ts +5 -6
  57. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.js +16 -16
  58. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.d.ts +11 -10
  59. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.js +14 -13
  60. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/tokens.d.ts +3 -4
  61. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/tokens.js +4 -12
  62. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_mpd.js +2 -4
  63. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representation_index.js +1 -3
  64. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +6 -2
  65. package/dist/_esm5.processed/parsers/manifest/dash/common/resolve_base_urls.d.ts +1 -2
  66. package/dist/_esm5.processed/parsers/manifest/dash/common/resolve_base_urls.js +3 -8
  67. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/node_parsers/BaseURL.d.ts +1 -1
  68. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/node_parsers/BaseURL.js +2 -20
  69. package/dist/_esm5.processed/parsers/manifest/dash/node_parser_types.d.ts +0 -6
  70. package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/generators/BaseURL.js +0 -11
  71. package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/types.d.ts +5 -1
  72. package/dist/_esm5.processed/parsers/manifest/local/parse_local_manifest.js +2 -1
  73. package/dist/_esm5.processed/parsers/manifest/local/representation_index.js +2 -2
  74. package/dist/_esm5.processed/parsers/manifest/metaplaylist/metaplaylist_parser.js +7 -1
  75. package/dist/_esm5.processed/parsers/manifest/smooth/create_parser.js +17 -11
  76. package/dist/_esm5.processed/parsers/manifest/smooth/representation_index.js +2 -2
  77. package/dist/_esm5.processed/parsers/manifest/types.d.ts +24 -0
  78. package/dist/_esm5.processed/parsers/texttracks/ttml/html/apply_extent.js +8 -1
  79. package/dist/_esm5.processed/parsers/texttracks/ttml/html/apply_origin.js +9 -1
  80. package/dist/_esm5.processed/parsers/texttracks/ttml/html/create_element.js +2 -1
  81. package/dist/_esm5.processed/transports/dash/construct_segment_url.d.ts +18 -0
  82. package/dist/_esm5.processed/transports/dash/construct_segment_url.js +21 -0
  83. package/dist/_esm5.processed/transports/dash/image_pipelines.d.ts +3 -2
  84. package/dist/_esm5.processed/transports/dash/image_pipelines.js +5 -3
  85. package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -2
  86. package/dist/_esm5.processed/transports/dash/text_loader.js +4 -2
  87. package/dist/_esm5.processed/transports/dash/text_parser.js +3 -1
  88. package/dist/_esm5.processed/transports/local/segment_loader.d.ts +3 -2
  89. package/dist/_esm5.processed/transports/local/segment_loader.js +2 -2
  90. package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +8 -8
  91. package/dist/_esm5.processed/transports/smooth/pipelines.js +11 -7
  92. package/dist/_esm5.processed/transports/smooth/utils.d.ts +4 -2
  93. package/dist/_esm5.processed/transports/smooth/utils.js +7 -1
  94. package/dist/_esm5.processed/transports/types.d.ts +5 -3
  95. package/dist/_esm5.processed/utils/event_emitter.d.ts +1 -2
  96. package/dist/_esm5.processed/utils/resolve_url.d.ts +7 -4
  97. package/dist/_esm5.processed/utils/resolve_url.js +11 -8
  98. package/dist/mpd-parser.wasm +0 -0
  99. package/dist/rx-player.js +5656 -13236
  100. package/dist/rx-player.min.js +1 -1
  101. package/jest.config.js +28 -23
  102. package/package.json +29 -29
  103. package/sonar-project.properties +1 -1
  104. package/src/compat/__tests__/can_reuse_media_keys.test.ts +54 -0
  105. package/src/compat/__tests__/enable_audio_track.test.ts +341 -0
  106. package/src/compat/__tests__/{should_renew_media_keys.test.ts → should_renew_media_key_system_access.test.ts} +7 -5
  107. package/src/compat/browser_detection.ts +10 -0
  108. package/src/compat/can_reuse_media_keys.ts +19 -0
  109. package/src/compat/enable_audio_track.ts +33 -0
  110. package/src/compat/index.ts +6 -2
  111. package/src/compat/{should_renew_media_keys.ts → should_renew_media_key_system_access.ts} +3 -3
  112. package/src/core/api/playback_observer.ts +97 -33
  113. package/src/core/api/public_api.ts +126 -74
  114. package/src/core/api/tracks_management/media_element_track_choice_manager.ts +3 -11
  115. package/src/core/decrypt/__tests__/__global__/media_keys.test.ts +182 -0
  116. package/src/core/decrypt/__tests__/__global__/utils.ts +2 -0
  117. package/src/core/decrypt/find_key_system.ts +3 -3
  118. package/src/core/decrypt/get_media_keys.ts +5 -1
  119. package/src/core/fetchers/cdn_prioritizer.ts +198 -0
  120. package/src/core/fetchers/manifest/manifest_fetcher.ts +7 -11
  121. package/src/core/fetchers/segment/segment_fetcher.ts +12 -10
  122. package/src/core/fetchers/segment/segment_fetcher_creator.ts +10 -1
  123. package/src/core/fetchers/utils/schedule_request.ts +482 -0
  124. package/src/core/init/content_time_boundaries_observer.ts +12 -12
  125. package/src/core/init/initialize_media_source.ts +31 -4
  126. package/src/core/init/load_on_media_source.ts +11 -2
  127. package/src/core/init/stall_avoider.ts +4 -1
  128. package/src/core/stream/orchestrator/stream_orchestrator.ts +0 -1
  129. package/src/default_config.ts +12 -0
  130. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +1 -0
  131. package/src/manifest/manifest.ts +11 -2
  132. package/src/manifest/representation.ts +15 -0
  133. package/src/manifest/representation_index/__tests__/static.test.ts +1 -1
  134. package/src/manifest/representation_index/static.ts +3 -3
  135. package/src/manifest/representation_index/types.ts +33 -15
  136. package/src/manifest/update_period_in_place.ts +1 -0
  137. package/src/parsers/manifest/dash/common/indexes/base.ts +22 -23
  138. package/src/parsers/manifest/dash/common/indexes/get_init_segment.ts +6 -4
  139. package/src/parsers/manifest/dash/common/indexes/get_segments_from_timeline.ts +5 -5
  140. package/src/parsers/manifest/dash/common/indexes/list.ts +19 -20
  141. package/src/parsers/manifest/dash/common/indexes/template.ts +23 -27
  142. package/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +25 -23
  143. package/src/parsers/manifest/dash/common/indexes/tokens.ts +7 -18
  144. package/src/parsers/manifest/dash/common/parse_mpd.ts +2 -4
  145. package/src/parsers/manifest/dash/common/parse_representation_index.ts +1 -6
  146. package/src/parsers/manifest/dash/common/parse_representations.ts +9 -0
  147. package/src/parsers/manifest/dash/common/resolve_base_urls.ts +5 -9
  148. package/src/parsers/manifest/dash/js-parser/node_parsers/BaseURL.ts +2 -29
  149. package/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/AdaptationSet.test.ts +7 -11
  150. package/src/parsers/manifest/dash/node_parser_types.ts +0 -7
  151. package/src/parsers/manifest/dash/wasm-parser/rs/events.rs +3 -1
  152. package/src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs +1 -8
  153. package/src/parsers/manifest/dash/wasm-parser/ts/generators/BaseURL.ts +0 -13
  154. package/src/parsers/manifest/dash/wasm-parser/ts/types.ts +8 -0
  155. package/src/parsers/manifest/local/parse_local_manifest.ts +1 -0
  156. package/src/parsers/manifest/local/representation_index.ts +2 -2
  157. package/src/parsers/manifest/metaplaylist/metaplaylist_parser.ts +7 -1
  158. package/src/parsers/manifest/smooth/create_parser.ts +17 -13
  159. package/src/parsers/manifest/smooth/representation_index.ts +2 -2
  160. package/src/parsers/manifest/types.ts +26 -1
  161. package/src/parsers/manifest/utils/__tests__/get_first_time_from_adaptations.test.ts +15 -0
  162. package/src/parsers/manifest/utils/__tests__/get_last_time_from_adaptation.test.ts +15 -0
  163. package/src/parsers/texttracks/ttml/html/apply_extent.ts +8 -1
  164. package/src/parsers/texttracks/ttml/html/apply_origin.ts +9 -1
  165. package/src/parsers/texttracks/ttml/html/create_element.ts +2 -2
  166. package/src/transports/dash/construct_segment_url.ts +28 -0
  167. package/src/transports/dash/image_pipelines.ts +5 -2
  168. package/src/transports/dash/segment_loader.ts +5 -2
  169. package/src/transports/dash/text_loader.ts +5 -2
  170. package/src/transports/dash/text_parser.ts +3 -1
  171. package/src/transports/local/segment_loader.ts +3 -2
  172. package/src/transports/metaplaylist/pipelines.ts +28 -9
  173. package/src/transports/smooth/pipelines.ts +12 -6
  174. package/src/transports/smooth/utils.ts +13 -1
  175. package/src/transports/types.ts +6 -3
  176. package/src/utils/__tests__/event_emitter.test.ts +72 -67
  177. package/src/utils/__tests__/initialization_segment_cache.test.ts +7 -7
  178. package/src/utils/__tests__/resolve_url.test.ts +27 -23
  179. package/src/utils/event_emitter.ts +1 -3
  180. package/src/utils/resolve_url.ts +11 -8
  181. package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.d.ts +0 -85
  182. package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.js +0 -264
  183. package/dummy +0 -1
  184. package/src/core/fetchers/utils/try_urls_with_backoff.ts +0 -287
@@ -22,6 +22,7 @@ import {
22
22
  MediaSource_,
23
23
  } from "./browser_compatibility_types";
24
24
  import canPatchISOBMFFSegment from "./can_patch_isobmff";
25
+ import canReuseMediaKeys from "./can_reuse_media_keys";
25
26
  import tryToChangeSourceBufferType, {
26
27
  ICompatSourceBuffer,
27
28
  } from "./change_source_buffer_type";
@@ -38,6 +39,7 @@ import {
38
39
  requestMediaKeySystemAccess,
39
40
  setMediaKeys,
40
41
  } from "./eme";
42
+ import enableAudioTrack from "./enable_audio_track";
41
43
  import * as events from "./event_listeners";
42
44
  import {
43
45
  exitFullscreen,
@@ -57,7 +59,7 @@ import play from "./play";
57
59
  import setElementSrc$ from "./set_element_src";
58
60
  // eslint-disable-next-line max-len
59
61
  import shouldReloadMediaSourceOnDecipherabilityUpdate from "./should_reload_media_source_on_decipherability_update";
60
- import shouldRenewMediaKeys from "./should_renew_media_keys";
62
+ import shouldRenewMediaKeySystemAccess from "./should_renew_media_key_system_access";
61
63
  import shouldUnsetMediaKeys from "./should_unset_media_keys";
62
64
  import shouldValidateMetadata from "./should_validate_metadata";
63
65
  import shouldWaitForDataBeforeLoaded from "./should_wait_for_data_before_loaded";
@@ -72,9 +74,11 @@ export {
72
74
  addClassName,
73
75
  addTextTrack,
74
76
  canPatchISOBMFFSegment,
77
+ canReuseMediaKeys,
75
78
  clearElementSrc,
76
79
  closeSession,
77
80
  CustomMediaKeySystemAccess,
81
+ enableAudioTrack,
78
82
  events,
79
83
  exitFullscreen,
80
84
  generateKeyRequest,
@@ -102,7 +106,7 @@ export {
102
106
  setElementSrc$,
103
107
  setMediaKeys,
104
108
  shouldReloadMediaSourceOnDecipherabilityUpdate,
105
- shouldRenewMediaKeys,
109
+ shouldRenewMediaKeySystemAccess,
106
110
  shouldUnsetMediaKeys,
107
111
  shouldValidateMetadata,
108
112
  shouldWaitForDataBeforeLoaded,
@@ -17,10 +17,10 @@
17
17
  import { isIE11 } from "./browser_detection";
18
18
 
19
19
  /**
20
- * Returns true if the current target require the media keys to be renewed on
21
- * each content.
20
+ * Returns true if the current target require the MediaKeySystemAccess to be
21
+ * renewed on each content.
22
22
  * @returns {Boolean}
23
23
  */
24
- export default function shouldRenewMediaKeys() : boolean {
24
+ export default function shouldRenewMediaKeySystemAccess() : boolean {
25
25
  return isIE11;
26
26
  }
@@ -264,7 +264,9 @@ export default class PlaybackObserver {
264
264
  startedInternalSeekTime = this._internalSeeksIncoming.shift();
265
265
  }
266
266
  const _lastObservation = lastObservation ?? this._generateInitialObservation();
267
- const mediaTimings = getMediaInfos(this._mediaElement, tmpEvt);
267
+ const mediaTimings = getMediaInfos(this._mediaElement,
268
+ tmpEvt,
269
+ this._withMediaSource);
268
270
  let pendingInternalSeek : number | null = null;
269
271
  if (mediaTimings.seeking) {
270
272
  if (typeof startedInternalSeekTime === "number") {
@@ -350,7 +352,9 @@ export default class PlaybackObserver {
350
352
  }
351
353
 
352
354
  private _generateInitialObservation() : IPlaybackObservation {
353
- return objectAssign(getMediaInfos(this._mediaElement, "init"),
355
+ return objectAssign(getMediaInfos(this._mediaElement,
356
+ "init",
357
+ this._withMediaSource),
354
358
  { rebuffering: null,
355
359
  freezing: null,
356
360
  pendingInternalSeek: null });
@@ -388,14 +392,23 @@ export type IPlaybackObserverEventType =
388
392
 
389
393
  /** Information recuperated on the media element on each playback observation. */
390
394
  interface IMediaInfos {
391
- /** Gap between `currentTime` and the next position with un-buffered data. */
392
- bufferGap : number;
395
+ /**
396
+ * Gap between `currentTime` and the next position with un-buffered data.
397
+ * `Infinity` if we don't have buffered data right now.
398
+ * `undefined` if we cannot determine the buffer gap.
399
+ */
400
+ bufferGap : number | undefined;
393
401
  /** Value of `buffered` (buffered ranges) for the media element. */
394
402
  buffered : TimeRanges;
395
- /** The buffered range we are currently playing. */
403
+ /**
404
+ * The buffered range we are currently playing.
405
+ * `null` if no range is currently available.
406
+ * `undefined` if we cannot tell which range is currently available.
407
+ */
396
408
  currentRange : { start : number;
397
409
  end : number; } |
398
- null;
410
+ null |
411
+ undefined;
399
412
  /**
400
413
  * `currentTime` (position) set on the media element at the time of the
401
414
  * PlaybackObserver's measure.
@@ -435,8 +448,9 @@ export interface IRebufferingStatus {
435
448
  /**
436
449
  * Position, in seconds, at which data is awaited.
437
450
  * If `null` the player is rebuffering but not because it is awaiting future data.
451
+ * If `undefined`, that position is unknown.
438
452
  */
439
- position : number | null;
453
+ position : number | null | undefined;
440
454
  }
441
455
 
442
456
  /**
@@ -585,13 +599,18 @@ function getRebufferingEndGap(
585
599
  * @returns {Boolean}
586
600
  */
587
601
  function hasLoadedUntilTheEnd(
588
- currentRange : { start : number; end : number }|null,
602
+ currentTime : number,
603
+ currentRange : { start : number; end : number } | null | undefined,
604
+ ended : boolean,
589
605
  duration : number,
590
606
  lowLatencyMode : boolean
591
607
  ) : boolean {
592
608
  const { REBUFFERING_GAP } = config.getCurrent();
593
609
  const suffix : "LOW_LATENCY" | "DEFAULT" = lowLatencyMode ? "LOW_LATENCY" :
594
610
  "DEFAULT";
611
+ if (currentRange === undefined) {
612
+ return ended && Math.abs(duration - currentTime) <= REBUFFERING_GAP[suffix];
613
+ }
595
614
  return currentRange !== null &&
596
615
  (duration - currentRange.end) <= REBUFFERING_GAP[suffix];
597
616
  }
@@ -604,7 +623,8 @@ function hasLoadedUntilTheEnd(
604
623
  */
605
624
  function getMediaInfos(
606
625
  mediaElement : HTMLMediaElement,
607
- event : IPlaybackObserverEventType
626
+ event : IPlaybackObserverEventType,
627
+ withMediaSource : boolean
608
628
  ) : IMediaInfos {
609
629
  const { buffered,
610
630
  currentTime,
@@ -615,11 +635,23 @@ function getMediaInfos(
615
635
  readyState,
616
636
  seeking } = mediaElement;
617
637
 
618
- const currentRange = getRange(buffered, currentTime);
619
- return { bufferGap: currentRange !== null ? currentRange.end - currentTime :
620
- // TODO null/0 would probably be
621
- // more appropriate
622
- Infinity,
638
+ let currentRange;
639
+ let bufferGap;
640
+ if (!withMediaSource && buffered.length === 0 && readyState >= 3) {
641
+ // Sometimes `buffered` stay empty for directfile contents yet we are able
642
+ // to play. This seems to be linked to browser-side issues but has been
643
+ // encountered on enough platforms (Chrome desktop and PlayStation 4's
644
+ // WebKit for us to do something about it in the player.
645
+ currentRange = undefined;
646
+ bufferGap = undefined;
647
+ } else {
648
+ currentRange = getRange(buffered, currentTime);
649
+ bufferGap = currentRange !== null ? currentRange.end - currentTime :
650
+ // TODO null/0 would probably be
651
+ // more appropriate
652
+ Infinity;
653
+ }
654
+ return { bufferGap,
623
655
  buffered,
624
656
  currentRange,
625
657
  position: currentTime,
@@ -664,14 +696,18 @@ function getRebufferingStatus(
664
696
  event: prevEvt,
665
697
  position: prevTime } = prevObservation;
666
698
 
667
- const fullyLoaded = hasLoadedUntilTheEnd(currentRange, duration, lowLatencyMode);
699
+ const fullyLoaded = hasLoadedUntilTheEnd(currentTime,
700
+ currentRange,
701
+ ended,
702
+ duration,
703
+ lowLatencyMode);
668
704
 
669
705
  const canSwitchToRebuffering = (readyState >= 1 &&
670
706
  currentEvt !== "loadedmetadata" &&
671
707
  prevRebuffering === null &&
672
708
  !(fullyLoaded || ended));
673
709
 
674
- let rebufferEndPosition : number | null = null;
710
+ let rebufferEndPosition : number | null | undefined = null;
675
711
  let shouldRebuffer : boolean | undefined;
676
712
  let shouldStopRebuffer : boolean | undefined;
677
713
 
@@ -680,22 +716,32 @@ function getRebufferingStatus(
680
716
 
681
717
  if (withMediaSource) {
682
718
  if (canSwitchToRebuffering) {
683
- if (bufferGap <= rebufferGap) {
684
- shouldRebuffer = true;
685
- rebufferEndPosition = currentTime + bufferGap;
686
- } else if (bufferGap === Infinity) {
719
+ if (bufferGap === Infinity) {
687
720
  shouldRebuffer = true;
688
721
  rebufferEndPosition = currentTime;
722
+ } else if (bufferGap === undefined) {
723
+ if (readyState < 3) {
724
+ shouldRebuffer = true;
725
+ rebufferEndPosition = undefined;
726
+ }
727
+ } else if (bufferGap <= rebufferGap) {
728
+ shouldRebuffer = true;
729
+ rebufferEndPosition = currentTime + bufferGap;
689
730
  }
690
731
  } else if (prevRebuffering !== null) {
691
732
  const resumeGap = getRebufferingEndGap(prevRebuffering, lowLatencyMode);
692
733
  if (shouldRebuffer !== true && prevRebuffering !== null && readyState > 1 &&
693
- (fullyLoaded || ended || (bufferGap < Infinity && bufferGap > resumeGap)))
734
+ (fullyLoaded || ended ||
735
+ (bufferGap !== undefined && isFinite(bufferGap) && bufferGap > resumeGap)) ||
736
+ (bufferGap === undefined && readyState >= 3))
694
737
  {
695
738
  shouldStopRebuffer = true;
696
- } else if (bufferGap === Infinity || bufferGap <= resumeGap) {
697
- rebufferEndPosition = bufferGap === Infinity ? currentTime :
698
- currentTime + bufferGap;
739
+ } else if (bufferGap === undefined) {
740
+ rebufferEndPosition = undefined;
741
+ } else if (bufferGap === Infinity) {
742
+ rebufferEndPosition = currentTime;
743
+ } else if (bufferGap <= resumeGap) {
744
+ rebufferEndPosition = currentTime + bufferGap;
699
745
  }
700
746
  }
701
747
  }
@@ -705,17 +751,34 @@ function getRebufferingStatus(
705
751
  // between two consecutive timeupdates
706
752
  else {
707
753
  if (canSwitchToRebuffering &&
708
- (!paused && currentEvt === "timeupdate" &&
709
- prevEvt === "timeupdate" && currentTime === prevTime ||
710
- currentEvt === "seeking" && bufferGap === Infinity)
754
+ (
755
+ (
756
+ !paused &&
757
+ currentEvt === "timeupdate" && prevEvt === "timeupdate" &&
758
+ currentTime === prevTime
759
+ ) ||
760
+ (
761
+ currentEvt === "seeking" && (
762
+ bufferGap === Infinity || (bufferGap === undefined && readyState < 3)
763
+ )
764
+ )
765
+ )
711
766
  ) {
712
767
  shouldRebuffer = true;
713
- } else if (prevRebuffering !== null &&
714
- (currentEvt !== "seeking" && currentTime !== prevTime ||
715
- currentEvt === "canplay" ||
716
- bufferGap < Infinity &&
717
- (bufferGap > getRebufferingEndGap(prevRebuffering, lowLatencyMode) ||
718
- fullyLoaded || ended))
768
+ } else if (
769
+ prevRebuffering !== null &&
770
+ (
771
+ (currentEvt !== "seeking" && currentTime !== prevTime) ||
772
+ currentEvt === "canplay" ||
773
+ (bufferGap === undefined && readyState >= 3) ||
774
+ (
775
+ bufferGap !== undefined && bufferGap < Infinity &&
776
+ (
777
+ bufferGap > getRebufferingEndGap(prevRebuffering, lowLatencyMode) ||
778
+ fullyLoaded || ended
779
+ )
780
+ )
781
+ )
719
782
  ) {
720
783
  shouldStopRebuffer = true;
721
784
  }
@@ -775,6 +838,7 @@ function getFreezingStatus(
775
838
  }
776
839
 
777
840
  return currentInfo.event === "timeupdate" &&
841
+ currentInfo.bufferGap !== undefined &&
778
842
  currentInfo.bufferGap > MINIMUM_BUFFER_AMOUNT_BEFORE_FREEZING &&
779
843
  !currentInfo.ended &&
780
844
  !currentInfo.paused &&
@@ -41,6 +41,7 @@ import {
41
41
  switchMap,
42
42
  take,
43
43
  takeUntil,
44
+ tap,
44
45
  } from "rxjs";
45
46
  import {
46
47
  events,
@@ -119,7 +120,6 @@ import {
119
120
  IManifestFetcherParsedResult,
120
121
  IManifestFetcherWarningEvent,
121
122
  ManifestFetcher,
122
- SegmentFetcherCreator,
123
123
  } from "../fetchers";
124
124
  import initializeMediaSourcePlayback, {
125
125
  IInitEvent,
@@ -135,6 +135,7 @@ import { IInbandEvent } from "../stream";
135
135
  import {
136
136
  checkReloadOptions,
137
137
  IParsedLoadVideoOptions,
138
+ IParsedStartAtOption,
138
139
  parseConstructorOptions,
139
140
  parseLoadVideoOptions,
140
141
  } from "./option_utils";
@@ -372,10 +373,35 @@ class Player extends EventEmitter<IPublicAPIEvent> {
372
373
  /** Determines whether or not the player should stop at the end of video playback. */
373
374
  private readonly _priv_stopAtEnd : boolean;
374
375
 
375
- /** Information about last content being played. */
376
- private _priv_lastContentPlaybackInfos : { options?: IParsedLoadVideoOptions;
377
- manifest?: Manifest;
378
- lastPlaybackPosition?: number; };
376
+ /**
377
+ * Information that can be relied on once `reload` is called.
378
+ * It should refer to the last content being played.
379
+ */
380
+ private _priv_reloadingMetadata : {
381
+ /**
382
+ * `loadVideo` options communicated for the last content that will be re-used
383
+ * on reload.
384
+ */
385
+ options?: IParsedLoadVideoOptions;
386
+ /**
387
+ * Manifest loaded for the last content that should be used once `reload`
388
+ * is called.
389
+ */
390
+ manifest?: Manifest;
391
+ /**
392
+ * If `true`, the player should be paused after reloading.
393
+ * If `false`, the player should be playing after reloading.
394
+ * If `undefined`, `reload` should depend on other criteria (such as the
395
+ * `autoPlay` option, to know whether the content should play or not after
396
+ * reloading.
397
+ */
398
+ reloadInPause?: boolean;
399
+ /**
400
+ * If set this is the position that should be seeked to by default after
401
+ * reloading.
402
+ */
403
+ reloadPosition?: number;
404
+ };
379
405
 
380
406
  /** All possible Error types emitted by the RxPlayer. */
381
407
  static get ErrorTypes() : Record<IErrorType, IErrorType> {
@@ -434,7 +460,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
434
460
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
435
461
  videoElement.preload = "auto";
436
462
 
437
- this.version = /* PLAYER_VERSION */"3.29.0-dev.2022091600";
463
+ this.version = /* PLAYER_VERSION */"3.29.0-dev.2022103100";
438
464
  this.log = log;
439
465
  this.state = "STOPPED";
440
466
  this.videoElement = videoElement;
@@ -529,7 +555,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
529
555
  this._priv_preferredTextTracks = preferredTextTracks;
530
556
  this._priv_preferredVideoTracks = preferredVideoTracks;
531
557
 
532
- this._priv_lastContentPlaybackInfos = {};
558
+ this._priv_reloadingMetadata = {};
533
559
  }
534
560
 
535
561
  /**
@@ -600,7 +626,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
600
626
  this._priv_bitrateInfos.maxAutoBitrates.video.finish();
601
627
  this._priv_bitrateInfos.maxAutoBitrates.audio.finish();
602
628
 
603
- this._priv_lastContentPlaybackInfos = {};
629
+ this._priv_reloadingMetadata = {};
604
630
 
605
631
  // un-attach video element
606
632
  this.videoElement = null;
@@ -613,51 +639,56 @@ class Player extends EventEmitter<IPublicAPIEvent> {
613
639
  loadVideo(opts : ILoadVideoOptions) : void {
614
640
  const options = parseLoadVideoOptions(opts);
615
641
  log.info("API: Calling loadvideo", options.url, options.transport);
616
- this._priv_lastContentPlaybackInfos = { options };
642
+ this._priv_reloadingMetadata = { options };
617
643
  this._priv_initializeContentPlayback(options);
618
644
  }
619
645
 
620
646
  /**
621
- * Reload last content. Init media playback without fetching again
622
- * the manifest.
647
+ * Reload the last loaded content.
623
648
  * @param {Object} reloadOpts
624
649
  */
625
- reload(reloadOpts?: { reloadAt?: { position?: number; relative?: number } }): void {
650
+ reload(reloadOpts?: {
651
+ reloadAt?: { position?: number; relative?: number };
652
+ autoPlay?: boolean;
653
+ }): void {
626
654
  const { options,
627
655
  manifest,
628
- lastPlaybackPosition } = this._priv_lastContentPlaybackInfos;
629
- if (options === undefined ||
630
- manifest === undefined ||
631
- lastPlaybackPosition === undefined) {
656
+ reloadPosition,
657
+ reloadInPause } = this._priv_reloadingMetadata;
658
+ if (options === undefined) {
632
659
  throw new Error("API: Can't reload without having previously loaded a content.");
633
660
  }
634
661
  checkReloadOptions(reloadOpts);
635
- let startAtPositon: number;
636
- if (reloadOpts !== undefined &&
637
- reloadOpts.reloadAt !== undefined &&
638
- reloadOpts.reloadAt.position !== undefined) {
639
- startAtPositon = reloadOpts.reloadAt.position;
640
- } else {
641
- let playbackPosition: number;
642
- if (this.state === "STOPPED" || this.state === "ENDED") {
643
- playbackPosition = lastPlaybackPosition;
644
- } else {
645
- if (this.videoElement === null) {
646
- throw new Error("Can't reload when video element does not exist.");
647
- }
648
- playbackPosition = this.videoElement.currentTime;
649
- }
650
- if (reloadOpts !== undefined &&
651
- reloadOpts.reloadAt !== undefined &&
652
- reloadOpts.reloadAt.relative !== undefined) {
653
- startAtPositon = reloadOpts.reloadAt.relative + playbackPosition;
662
+ let startAt : IParsedStartAtOption | undefined;
663
+ if (reloadOpts?.reloadAt?.position !== undefined) {
664
+ startAt = { position: reloadOpts.reloadAt.position };
665
+ } else if (reloadOpts?.reloadAt?.relative !== undefined) {
666
+ if (reloadPosition === undefined) {
667
+ throw new Error(
668
+ "Can't reload to a relative position when previous content was not loaded."
669
+ );
654
670
  } else {
655
- startAtPositon = playbackPosition;
671
+ startAt = { position: reloadOpts.reloadAt.relative + reloadPosition };
656
672
  }
673
+ } else if (reloadPosition !== undefined) {
674
+ startAt = { position: reloadPosition };
675
+ }
676
+
677
+ let autoPlay : boolean | undefined;
678
+ if (reloadOpts?.autoPlay !== undefined) {
679
+ autoPlay = reloadOpts.autoPlay;
680
+ } else if (reloadInPause !== undefined) {
681
+ autoPlay = !reloadInPause;
657
682
  }
683
+
658
684
  const newOptions = { ...options,
659
685
  initialManifest: manifest };
660
- newOptions.startAt = { position: startAtPositon };
686
+ if (startAt !== undefined) {
687
+ newOptions.startAt = startAt;
688
+ }
689
+ if (autoPlay !== undefined) {
690
+ newOptions.autoPlay = autoPlay;
691
+ }
661
692
  this._priv_initializeContentPlayback(newOptions);
662
693
  }
663
694
 
@@ -758,14 +789,6 @@ class Player extends EventEmitter<IPublicAPIEvent> {
758
789
  maxRetryOffline: offlineRetry,
759
790
  requestTimeout: manifestRequestTimeout });
760
791
 
761
- /** Interface used to download segments. */
762
- const segmentFetcherCreator = new SegmentFetcherCreator(
763
- transportPipelines,
764
- { lowLatencyMode,
765
- maxRetryOffline: offlineRetry,
766
- maxRetryRegular: segmentRetry,
767
- requestTimeout: segmentRequestTimeout });
768
-
769
792
  /** Observable emitting the initial Manifest */
770
793
  let manifest$ : Observable<IManifestFetcherParsedResult |
771
794
  IManifestFetcherWarningEvent>;
@@ -865,6 +888,10 @@ class Player extends EventEmitter<IPublicAPIEvent> {
865
888
  onCodecSwitch },
866
889
  this._priv_bufferOptions);
867
890
 
891
+ const segmentRequestOptions = { regularError: segmentRetry,
892
+ requestTimeout: segmentRequestTimeout,
893
+ offlineError: offlineRetry };
894
+
868
895
  // We've every options set up. Start everything now
869
896
  const init$ = initializeMediaSourcePlayback({ adaptiveOptions,
870
897
  autoPlay,
@@ -876,9 +903,10 @@ class Player extends EventEmitter<IPublicAPIEvent> {
876
903
  manifestFetcher,
877
904
  mediaElement: videoElement,
878
905
  minimumManifestUpdateInterval,
879
- segmentFetcherCreator,
906
+ segmentRequestOptions,
880
907
  speed: this._priv_speed,
881
908
  startAt,
909
+ transport: transportPipelines,
882
910
  textTrackOptions })
883
911
  .pipe(takeUntil(stoppedContent$));
884
912
 
@@ -1038,10 +1066,51 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1038
1066
  }
1039
1067
  });
1040
1068
 
1041
- // Link "positionUpdate" events to the PlaybackObserver
1042
- observation$
1043
- .pipe(takeUntil(stoppedContent$))
1044
- .subscribe(o => this._priv_triggerPositionUpdate(o));
1069
+ /**
1070
+ * Function updating `this._priv_reloadingMetadata` in function of the
1071
+ * current state and playback conditions.
1072
+ * To call when either might change.
1073
+ * @param {string} state - The player state we're about to switch to.
1074
+ */
1075
+ const updateReloadingMetadata = (state : IPlayerState) => {
1076
+ switch (state) {
1077
+ case "STOPPED":
1078
+ case "RELOADING":
1079
+ case "LOADING":
1080
+ break; // keep previous metadata
1081
+ case "ENDED":
1082
+ this._priv_reloadingMetadata.reloadInPause = true;
1083
+ this._priv_reloadingMetadata.reloadPosition =
1084
+ playbackObserver.getReference().getValue().position;
1085
+ break;
1086
+ default:
1087
+ const o = playbackObserver.getReference().getValue();
1088
+ this._priv_reloadingMetadata.reloadInPause = o.paused;
1089
+ this._priv_reloadingMetadata.reloadPosition = o.position;
1090
+ break;
1091
+ }
1092
+ };
1093
+
1094
+ playerState$.pipe(
1095
+ tap(newState => {
1096
+ updateReloadingMetadata(newState);
1097
+ this._priv_setPlayerState(newState);
1098
+
1099
+ // Previous call could have performed all kind of side-effects, thus,
1100
+ // we re-check the current state associated to the RxPlayer
1101
+ if (this.state === "ENDED" && this._priv_stopAtEnd) {
1102
+ currentContentCanceller.cancel();
1103
+ }
1104
+ }),
1105
+ map(state => state !== "RELOADING" && state !== "STOPPED"),
1106
+ distinctUntilChanged(),
1107
+ switchMap(canSendObservation => canSendObservation ? observation$ :
1108
+ EMPTY),
1109
+ takeUntil(stoppedContent$)
1110
+ ).subscribe(o => {
1111
+ updateReloadingMetadata(this.state);
1112
+ this._priv_triggerPositionUpdate(o);
1113
+ });
1045
1114
 
1046
1115
  // Link "seeking" and "seeked" events (once the content is loaded)
1047
1116
  loaded$.pipe(
@@ -1052,19 +1121,6 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1052
1121
  this.trigger(evt, null);
1053
1122
  });
1054
1123
 
1055
- // Handle state updates
1056
- playerState$
1057
- .pipe(takeUntil(stoppedContent$))
1058
- .subscribe(newState => {
1059
- this._priv_setPlayerState(newState);
1060
-
1061
- // Previous call could have performed all kind of side-effects, thus,
1062
- // we re-check the current state associated to the RxPlayer
1063
- if (this.state === "ENDED" && this._priv_stopAtEnd) {
1064
- currentContentCanceller.cancel();
1065
- }
1066
- });
1067
-
1068
1124
  // Link playback events to the corresponding callbacks
1069
1125
  playback$.subscribe({
1070
1126
  next: (x) => this._priv_onPlaybackEvent(x),
@@ -2498,7 +2554,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2498
2554
  return;
2499
2555
  }
2500
2556
  contentInfos.manifest = manifest;
2501
- this._priv_lastContentPlaybackInfos.manifest = manifest;
2557
+ this._priv_reloadingMetadata.manifest = manifest;
2502
2558
 
2503
2559
  const { initialAudioTrack, initialTextTrack } = contentInfos;
2504
2560
  this._priv_trackChoiceManager = new TrackChoiceManager({
@@ -2859,17 +2915,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2859
2915
  return;
2860
2916
  }
2861
2917
 
2862
- if (this.state === PLAYER_STATES.RELOADING) {
2863
- return;
2864
- }
2865
-
2866
2918
  const { isDirectFile, manifest } = this._priv_contentInfos;
2867
2919
  if ((!isDirectFile && manifest === null) || isNullOrUndefined(observation)) {
2868
2920
  return;
2869
2921
  }
2870
2922
 
2871
- this._priv_lastContentPlaybackInfos.lastPlaybackPosition = observation.position;
2872
-
2873
2923
  const maximumPosition = manifest !== null ? manifest.getMaximumSafePosition() :
2874
2924
  undefined;
2875
2925
  const positionData : IPositionUpdate = {
@@ -2878,9 +2928,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2878
2928
  playbackRate: observation.playbackRate,
2879
2929
  maximumBufferTime: maximumPosition,
2880
2930
 
2881
- // TODO fix higher up?
2882
- bufferGap: isFinite(observation.bufferGap) ? observation.bufferGap :
2883
- 0,
2931
+ // TODO bufferGap may be undefined
2932
+ bufferGap: observation.bufferGap === undefined ||
2933
+ !isFinite(observation.bufferGap) ?
2934
+ 0 :
2935
+ observation.bufferGap,
2884
2936
  };
2885
2937
 
2886
2938
  if (manifest !== null &&
@@ -2950,7 +3002,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2950
3002
  return activeRepresentations[currentPeriod.id];
2951
3003
  }
2952
3004
  }
2953
- Player.version = /* PLAYER_VERSION */"3.29.0-dev.2022091600";
3005
+ Player.version = /* PLAYER_VERSION */"3.29.0-dev.2022103100";
2954
3006
 
2955
3007
  /** Every events sent by the RxPlayer's public API. */
2956
3008
  interface IPublicAPIEvent {
@@ -19,6 +19,7 @@
19
19
  * It always should be imported through the `features` object.
20
20
  */
21
21
 
22
+ import { enableAudioTrack } from "../../../compat";
22
23
  import {
23
24
  ICompatAudioTrack,
24
25
  ICompatAudioTrackList,
@@ -39,7 +40,6 @@ import {
39
40
  IAvailableAudioTrack,
40
41
  IAvailableTextTrack,
41
42
  } from "../../../public_types";
42
- import assert from "../../../utils/assert";
43
43
  import EventEmitter from "../../../utils/event_emitter";
44
44
  import normalizeLanguage from "../../../utils/languages";
45
45
 
@@ -913,16 +913,8 @@ export default class MediaElementTrackChoiceManager
913
913
  * @param {number} index}
914
914
  */
915
915
  private _enableAudioTrackFromIndex(index : number) : void {
916
- assert(index < this._audioTracks.length);
917
-
918
- // Seen on Safari MacOS only (2022-02-14), not disabling ALL audio tracks
919
- // first (even the wanted one), can lead to the media not playing.
920
- for (const audioTrack of this._audioTracks) {
921
- audioTrack.nativeTrack.enabled = false;
922
- }
923
-
924
- this._audioTracks[index].nativeTrack.enabled = true;
925
- return;
916
+ enableAudioTrack(this._audioTracks.map(({ nativeTrack }) => nativeTrack),
917
+ index);
926
918
  }
927
919
  }
928
920