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
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Copyright 2015 CANAL+ Group
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
+ return new (P || (P = Promise))(function (resolve, reject) {
19
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
20
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
23
+ });
24
+ };
25
+ var __generator = (this && this.__generator) || function (thisArg, body) {
26
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
27
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
28
+ function verb(n) { return function (v) { return step([n, v]); }; }
29
+ function step(op) {
30
+ if (f) throw new TypeError("Generator is already executing.");
31
+ while (_) try {
32
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
33
+ if (y = 0, t) op = [op[0] & 2, t.value];
34
+ switch (op[0]) {
35
+ case 0: case 1: t = op; break;
36
+ case 4: _.label++; return { value: op[1], done: false };
37
+ case 5: _.label++; y = op[1]; op = [0]; continue;
38
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
39
+ default:
40
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
41
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
42
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
43
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
44
+ if (t[2]) _.ops.pop();
45
+ _.trys.pop(); continue;
46
+ }
47
+ op = body.call(thisArg, _);
48
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
49
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
50
+ }
51
+ };
52
+ import { isOffline } from "../../../compat";
53
+ import { CustomLoaderError, isKnownError, NetworkErrorTypes, RequestError, } from "../../../errors";
54
+ import log from "../../../log";
55
+ import cancellableSleep from "../../../utils/cancellable_sleep";
56
+ import getFuzzedDelay from "../../../utils/get_fuzzed_delay";
57
+ import noop from "../../../utils/noop";
58
+ import TaskCanceller from "../../../utils/task_canceller";
59
+ /**
60
+ * Called on a loader error.
61
+ * Returns whether the loader request should be retried.
62
+ *
63
+ * TODO the notion of retrying or not could be transport-specific (e.g. 412 are
64
+ * mainly used for Smooth contents) and thus as part of the transport code (e.g.
65
+ * by rejecting with an error always having a `canRetry` property?).
66
+ * Or not, to ponder.
67
+ *
68
+ * @param {Error} error
69
+ * @returns {Boolean} - If true, the request can be retried.
70
+ */
71
+ function shouldRetry(error) {
72
+ if (error instanceof RequestError) {
73
+ if (error.type === NetworkErrorTypes.ERROR_HTTP_CODE) {
74
+ return error.status >= 500 ||
75
+ error.status === 404 ||
76
+ error.status === 415 || // some CDN seems to use that code when
77
+ // requesting low-latency segments too much
78
+ // in advance
79
+ error.status === 412;
80
+ }
81
+ return error.type === NetworkErrorTypes.TIMEOUT ||
82
+ error.type === NetworkErrorTypes.ERROR_EVENT;
83
+ }
84
+ else if (error instanceof CustomLoaderError) {
85
+ if (typeof error.canRetry === "boolean") {
86
+ return error.canRetry;
87
+ }
88
+ if (error.xhr !== undefined) {
89
+ return error.xhr.status >= 500 ||
90
+ error.xhr.status === 404 ||
91
+ error.xhr.status === 415 || // some CDN seems to use that code when
92
+ // requesting low-latency segments too much
93
+ // in advance
94
+ error.xhr.status === 412;
95
+ }
96
+ return false;
97
+ }
98
+ return isKnownError(error) && error.code === "INTEGRITY_ERROR";
99
+ }
100
+ /**
101
+ * Returns true if we're pretty sure that the current error is due to the
102
+ * user being offline.
103
+ * @param {Error} error
104
+ * @returns {Boolean}
105
+ */
106
+ function isOfflineRequestError(error) {
107
+ if (error instanceof RequestError) {
108
+ return error.type === NetworkErrorTypes.ERROR_EVENT &&
109
+ isOffline();
110
+ }
111
+ else if (error instanceof CustomLoaderError) {
112
+ return error.isOfflineError;
113
+ }
114
+ return false; // under doubt, return false
115
+ }
116
+ /**
117
+ * Guess the type of error obtained.
118
+ * @param {*} error
119
+ * @returns {number}
120
+ */
121
+ function getRequestErrorType(error) {
122
+ return isOfflineRequestError(error) ? 2 /* REQUEST_ERROR_TYPES.Offline */ :
123
+ 1 /* REQUEST_ERROR_TYPES.Regular */;
124
+ }
125
+ /**
126
+ * Specific algorithm used to perform segment and manifest requests.
127
+ *
128
+ * Here how it works:
129
+ *
130
+ * 1. You give it one or multiple of the CDN available for the resource you
131
+ * want to request (from the most important one to the least important),
132
+ * a callback doing the request with the chosen CDN in argument, and some
133
+ * options.
134
+ *
135
+ * 2. it tries to call the request callback with the most prioritized CDN
136
+ * first:
137
+ * - if it works as expected, it resolves the returned Promise with that
138
+ * request's response.
139
+ * - if it fails, it calls ther `onRetry` callback given with the
140
+ * corresponding error, un-prioritize that CDN and try with the new
141
+ * most prioritized CDN.
142
+ *
143
+ * Each CDN might be retried multiple times, depending on the nature of the
144
+ * error and the Configuration given.
145
+ *
146
+ * Multiple retries of the same CDN are done after a delay to avoid
147
+ * overwhelming it, this is what we call a "backoff". That delay raises
148
+ * exponentially as multiple consecutive errors are encountered on this
149
+ * CDN.
150
+ *
151
+ * @param {Array.<string>|null} cdns - The different CDN on which the
152
+ * wanted resource is available. `scheduleRequestWithCdns` will call the
153
+ * `performRequest` callback with the right element from that array if different
154
+ * from `null`.
155
+ *
156
+ * Can be set to `null` when that resource is not reachable through a CDN, in
157
+ * which case the `performRequest` callback may be called with `null`.
158
+ * @param {Object|null} cdnPrioritizer - Interface allowing to give the priority
159
+ * between multiple CDNs.
160
+ * @param {Function} performRequest - Callback implementing the request in
161
+ * itself. Resolving when the resource request succeed and rejecting with the
162
+ * corresponding error when the request failed.
163
+ * @param {Object} options - Configuration allowing to tweak the number on which
164
+ * the algorithm behind `scheduleRequestWithCdns` bases itself.
165
+ * @param {Object} cancellationSignal - CancellationSignal allowing to cancel
166
+ * the logic of `scheduleRequestWithCdns`.
167
+ * To trigger if the resource is not needed anymore.
168
+ * @returns {Promise} - Promise resolving, with the corresponding
169
+ * `performRequest`'s data, when the resource request succeed and rejecting in
170
+ * the following scenarios:
171
+ * - `scheduleRequestWithCdns` has been cancelled due to `cancellationSignal`
172
+ * being triggered. In that case a `CancellationError` is thrown.
173
+ *
174
+ * - The resource request(s) failed and will not be retried anymore.
175
+ */
176
+ export function scheduleRequestWithCdns(cdns, cdnPrioritizer, performRequest, options, cancellationSignal) {
177
+ return __awaiter(this, void 0, void 0, function () {
178
+ /**
179
+ * Returns what is now the most prioritary CDN to request the wanted resource.
180
+ *
181
+ * A return value of `null` indicates that the resource can be requested
182
+ * through another mean than by doing an HTTP request.
183
+ *
184
+ * A return value of `undefined` indicates that there's no CDN left to request
185
+ * the resource.
186
+ * @returns {Object|null|undefined}
187
+ */
188
+ function getCdnToRequest() {
189
+ if (cdns === null) {
190
+ var nullAttemptObject = missedAttempts.get(null);
191
+ if (nullAttemptObject !== undefined && nullAttemptObject.isBlacklisted) {
192
+ return undefined;
193
+ }
194
+ return null;
195
+ }
196
+ else if (cdnPrioritizer === null) {
197
+ return getPrioritaryRequestableCdnFromSortedList(cdns);
198
+ }
199
+ else {
200
+ var prioritized = cdnPrioritizer.getCdnPreferenceForResource(cdns);
201
+ return getPrioritaryRequestableCdnFromSortedList(prioritized);
202
+ }
203
+ }
204
+ /**
205
+ * Perform immediately the request for the given CDN.
206
+ *
207
+ * If it fails, forbid the CDN from being used - optionally and in some
208
+ * conditions, only temporarily, then try the next CDN according to
209
+ * previously-set delays (with a potential sleep before to respect them).
210
+ *
211
+ * Reject if both the request fails and there's no CDN left to use.
212
+ * @param {string|null} cdn
213
+ * @returns {Promise}
214
+ */
215
+ function requestCdn(cdn) {
216
+ return __awaiter(this, void 0, void 0, function () {
217
+ var res, error_1, currentErrorType, missedAttemptsObj, maxRetry, errorCounter, delay, fuzzedDelay;
218
+ return __generator(this, function (_a) {
219
+ switch (_a.label) {
220
+ case 0:
221
+ _a.trys.push([0, 2, , 3]);
222
+ return [4 /*yield*/, performRequest(cdn, cancellationSignal)];
223
+ case 1:
224
+ res = _a.sent();
225
+ return [2 /*return*/, res];
226
+ case 2:
227
+ error_1 = _a.sent();
228
+ if (TaskCanceller.isCancellationError(error_1)) {
229
+ throw error_1;
230
+ }
231
+ if (cdn !== null && cdnPrioritizer !== null) {
232
+ // We failed requesting the resource on this CDN.
233
+ // Globally give priority to the next CDN through the CdnPrioritizer.
234
+ cdnPrioritizer.downgradeCdn(cdn);
235
+ }
236
+ currentErrorType = getRequestErrorType(error_1);
237
+ missedAttemptsObj = missedAttempts.get(cdn);
238
+ if (missedAttemptsObj === undefined) {
239
+ missedAttemptsObj = { errorCounter: 1,
240
+ lastErrorType: currentErrorType,
241
+ blockedUntil: undefined,
242
+ isBlacklisted: false };
243
+ missedAttempts.set(cdn, missedAttemptsObj);
244
+ }
245
+ else {
246
+ if (currentErrorType !== missedAttemptsObj.lastErrorType) {
247
+ missedAttemptsObj.errorCounter = 1;
248
+ missedAttemptsObj.lastErrorType = currentErrorType;
249
+ }
250
+ else {
251
+ missedAttemptsObj.errorCounter++;
252
+ }
253
+ }
254
+ if (!shouldRetry(error_1)) {
255
+ missedAttemptsObj.blockedUntil = undefined;
256
+ missedAttemptsObj.isBlacklisted = true;
257
+ return [2 /*return*/, retryWithNextCdn(error_1)];
258
+ }
259
+ maxRetry = currentErrorType === 2 /* REQUEST_ERROR_TYPES.Offline */ ?
260
+ maxRetryOffline :
261
+ maxRetryRegular;
262
+ if (missedAttemptsObj.errorCounter > maxRetry) {
263
+ missedAttemptsObj.blockedUntil = undefined;
264
+ missedAttemptsObj.isBlacklisted = true;
265
+ }
266
+ else {
267
+ errorCounter = missedAttemptsObj.errorCounter;
268
+ delay = Math.min(baseDelay * Math.pow(2, errorCounter - 1), maxDelay);
269
+ fuzzedDelay = getFuzzedDelay(delay);
270
+ missedAttemptsObj.blockedUntil = performance.now() + fuzzedDelay;
271
+ }
272
+ return [2 /*return*/, retryWithNextCdn(error_1)];
273
+ case 3: return [2 /*return*/];
274
+ }
275
+ });
276
+ });
277
+ }
278
+ /**
279
+ * After a request error, find the new most prioritary CDN and perform the
280
+ * request with it, optionally after a delay.
281
+ *
282
+ * If there's no CDN left to test, reject the original request error.
283
+ * @param {*} prevRequestError
284
+ * @returns {Promise}
285
+ */
286
+ function retryWithNextCdn(prevRequestError) {
287
+ return __awaiter(this, void 0, void 0, function () {
288
+ var nextCdn;
289
+ return __generator(this, function (_a) {
290
+ nextCdn = getCdnToRequest();
291
+ if (cancellationSignal.isCancelled) {
292
+ throw cancellationSignal.cancellationError;
293
+ }
294
+ if (nextCdn === undefined) {
295
+ throw prevRequestError;
296
+ }
297
+ onRetry(prevRequestError);
298
+ if (cancellationSignal.isCancelled) {
299
+ throw cancellationSignal.cancellationError;
300
+ }
301
+ return [2 /*return*/, waitPotentialBackoffAndRequest(nextCdn, prevRequestError)];
302
+ });
303
+ });
304
+ }
305
+ /**
306
+ * Request the corresponding CDN after the optional backoff needed before
307
+ * requesting it.
308
+ *
309
+ * If a new CDN become prioritary in the meantime, request it instead, again
310
+ * awaiting its optional backoff delay if it exists.
311
+ * @param {string|null} nextWantedCdn
312
+ * @param {*} prevRequestError
313
+ * @returns {Promise}
314
+ */
315
+ function waitPotentialBackoffAndRequest(nextWantedCdn, prevRequestError) {
316
+ var nextCdnAttemptObj = missedAttempts.get(nextWantedCdn);
317
+ if (nextCdnAttemptObj === undefined ||
318
+ nextCdnAttemptObj.blockedUntil === undefined) {
319
+ return requestCdn(nextWantedCdn);
320
+ }
321
+ var now = performance.now();
322
+ var blockedFor = nextCdnAttemptObj.blockedUntil - now;
323
+ if (blockedFor <= 0) {
324
+ return requestCdn(nextWantedCdn);
325
+ }
326
+ var canceller = new TaskCanceller({ cancelOn: cancellationSignal });
327
+ return new Promise(function (res, rej) {
328
+ /* eslint-disable-next-line @typescript-eslint/no-misused-promises */
329
+ cdnPrioritizer === null || cdnPrioritizer === void 0 ? void 0 : cdnPrioritizer.addEventListener("priorityChange", function () {
330
+ var updatedPrioritaryCdn = getCdnToRequest();
331
+ if (cancellationSignal.isCancelled) {
332
+ throw cancellationSignal.cancellationError;
333
+ }
334
+ if (updatedPrioritaryCdn === undefined) {
335
+ return rej(prevRequestError);
336
+ }
337
+ if (updatedPrioritaryCdn !== nextWantedCdn) {
338
+ canceller.cancel();
339
+ waitPotentialBackoffAndRequest(updatedPrioritaryCdn, prevRequestError)
340
+ .then(res, rej);
341
+ }
342
+ }, canceller.signal);
343
+ cancellableSleep(blockedFor, canceller.signal)
344
+ .then(function () { return requestCdn(nextWantedCdn).then(res, rej); }, noop);
345
+ });
346
+ }
347
+ /**
348
+ * Takes in input the list of CDN that can be used to request the resource, in
349
+ * a general preference order.
350
+ *
351
+ * Returns the actual most prioritary Cdn to request, based on the current
352
+ * attempts already done for that resource.
353
+ *
354
+ * Returns `undefined` if there's no Cdn left to request the resource.
355
+ * @param {Array.<Object>}
356
+ * @returns {Object|undefined}
357
+ */
358
+ function getPrioritaryRequestableCdnFromSortedList(sortedCdns) {
359
+ var _a;
360
+ if (missedAttempts.size === 0) {
361
+ return sortedCdns[0];
362
+ }
363
+ var now = performance.now();
364
+ return (_a = sortedCdns
365
+ .filter(function (c) { var _a; return ((_a = missedAttempts.get(c)) === null || _a === void 0 ? void 0 : _a.isBlacklisted) !== true; })
366
+ .reduce(function (acc, x) {
367
+ var _a;
368
+ var blockedUntil = (_a = missedAttempts.get(x)) === null || _a === void 0 ? void 0 : _a.blockedUntil;
369
+ if (blockedUntil !== undefined && blockedUntil <= now) {
370
+ blockedUntil = undefined;
371
+ }
372
+ if (acc === undefined) {
373
+ return [x, blockedUntil];
374
+ }
375
+ if (blockedUntil === undefined) {
376
+ if (acc[1] === undefined) {
377
+ return acc;
378
+ }
379
+ return [x, undefined];
380
+ }
381
+ return acc[1] === undefined ? acc :
382
+ blockedUntil < acc[1] ? [x, blockedUntil] :
383
+ acc;
384
+ }, undefined)) === null || _a === void 0 ? void 0 : _a[0];
385
+ }
386
+ var baseDelay, maxDelay, maxRetryRegular, maxRetryOffline, onRetry, missedAttempts, initialCdnToRequest;
387
+ return __generator(this, function (_a) {
388
+ if (cancellationSignal.cancellationError !== null) {
389
+ return [2 /*return*/, Promise.reject(cancellationSignal.cancellationError)];
390
+ }
391
+ baseDelay = options.baseDelay, maxDelay = options.maxDelay, maxRetryRegular = options.maxRetryRegular, maxRetryOffline = options.maxRetryOffline, onRetry = options.onRetry;
392
+ if (cdns !== null && cdns.length === 0) {
393
+ log.warn("Fetchers: no CDN given to `scheduleRequestWithCdns`.");
394
+ }
395
+ missedAttempts = new Map();
396
+ initialCdnToRequest = getCdnToRequest();
397
+ if (initialCdnToRequest === undefined) {
398
+ throw new Error("No CDN to request");
399
+ }
400
+ return [2 /*return*/, requestCdn(initialCdnToRequest)];
401
+ });
402
+ });
403
+ }
404
+ /**
405
+ * Lightweight version of the request algorithm, this time with only a simple
406
+ * Promise given.
407
+ * @param {Function} performRequest
408
+ * @param {Object} options
409
+ * @returns {Promise}
410
+ */
411
+ export function scheduleRequestPromise(performRequest, options, cancellationSignal) {
412
+ // same than for a single unknown CDN
413
+ return scheduleRequestWithCdns(null, null, performRequest, options, cancellationSignal);
414
+ }
@@ -15,8 +15,9 @@
15
15
  */
16
16
  import { Observable } from "rxjs";
17
17
  import Manifest from "../../manifest";
18
+ import { IReadOnlySharedReference } from "../../utils/reference";
18
19
  import { IReadOnlyPlaybackObserver } from "../api";
19
- import { IStreamOrchestratorEvent, IStreamOrchestratorPlaybackObservation } from "../stream";
20
+ import { IAdaptationChangeEvent, IStreamOrchestratorPlaybackObservation } from "../stream";
20
21
  import { IWarningEvent } from "./types";
21
22
  /**
22
23
  * Observes the position and Adaptations being played and deduce various events
@@ -27,11 +28,11 @@ import { IWarningEvent } from "./types";
27
28
  * theoretically playable.
28
29
  *
29
30
  * @param {Object} manifest
30
- * @param {Observable} streams
31
+ * @param {Object} lastAdaptationChange
31
32
  * @param {Object} playbackObserver
32
33
  * @returns {Observable}
33
34
  */
34
- export default function ContentTimeBoundariesObserver(manifest: Manifest, streams: Observable<IStreamOrchestratorEvent>, playbackObserver: IReadOnlyPlaybackObserver<IContentTimeObserverPlaybackObservation>): Observable<IContentDurationUpdateEvent | IWarningEvent>;
35
+ export default function ContentTimeBoundariesObserver(manifest: Manifest, lastAdaptationChange: IReadOnlySharedReference<IAdaptationChangeEvent | null>, playbackObserver: IReadOnlyPlaybackObserver<IContentTimeObserverPlaybackObservation>): Observable<IContentDurationUpdateEvent | IWarningEvent>;
35
36
  /**
36
37
  * Emitted when the duration of the full content (== the last playable position)
37
38
  * has changed.
@@ -34,11 +34,11 @@ import EVENTS from "./events_generators";
34
34
  * theoretically playable.
35
35
  *
36
36
  * @param {Object} manifest
37
- * @param {Observable} streams
37
+ * @param {Object} lastAdaptationChange
38
38
  * @param {Object} playbackObserver
39
39
  * @returns {Observable}
40
40
  */
41
- export default function ContentTimeBoundariesObserver(manifest, streams, playbackObserver) {
41
+ export default function ContentTimeBoundariesObserver(manifest, lastAdaptationChange, playbackObserver) {
42
42
  /**
43
43
  * Allows to calculate the minimum and maximum playable position on the
44
44
  * whole content.
@@ -74,27 +74,26 @@ export default function ContentTimeBoundariesObserver(manifest, streams, playbac
74
74
  maximumPositionCalculator.getMaximumAvailablePosition();
75
75
  contentDuration.setValue(duration);
76
76
  }), ignoreElements());
77
- var updateDurationAndTimeBoundsOnTrackChange$ = streams.pipe(tap(function (message) {
78
- if (message.type === "adaptationChange") {
79
- if (!manifest.isLastPeriodKnown) {
80
- return;
81
- }
82
- var lastPeriod = manifest.periods[manifest.periods.length - 1];
83
- if (message.value.period.id === (lastPeriod === null || lastPeriod === void 0 ? void 0 : lastPeriod.id)) {
84
- if (message.value.type === "audio" || message.value.type === "video") {
85
- if (message.value.type === "audio") {
86
- maximumPositionCalculator
87
- .updateLastAudioAdaptation(message.value.adaptation);
88
- }
89
- else {
90
- maximumPositionCalculator
91
- .updateLastVideoAdaptation(message.value.adaptation);
92
- }
93
- var newDuration = manifest.isDynamic ?
94
- maximumPositionCalculator.getMaximumAvailablePosition() :
95
- maximumPositionCalculator.getEndingPosition();
96
- contentDuration.setValue(newDuration);
77
+ var updateDurationAndTimeBoundsOnTrackChange$ = lastAdaptationChange
78
+ .asObservable().pipe(tap(function (message) {
79
+ if (message === null || !manifest.isLastPeriodKnown) {
80
+ return;
81
+ }
82
+ var lastPeriod = manifest.periods[manifest.periods.length - 1];
83
+ if (message.value.period.id === (lastPeriod === null || lastPeriod === void 0 ? void 0 : lastPeriod.id)) {
84
+ if (message.value.type === "audio" || message.value.type === "video") {
85
+ if (message.value.type === "audio") {
86
+ maximumPositionCalculator
87
+ .updateLastAudioAdaptation(message.value.adaptation);
88
+ }
89
+ else {
90
+ maximumPositionCalculator
91
+ .updateLastVideoAdaptation(message.value.adaptation);
97
92
  }
93
+ var newDuration = manifest.isDynamic ?
94
+ maximumPositionCalculator.getMaximumAvailablePosition() :
95
+ maximumPositionCalculator.getEndingPosition();
96
+ contentDuration.setValue(newDuration);
98
97
  }
99
98
  }
100
99
  }), ignoreElements());
@@ -15,10 +15,11 @@
15
15
  */
16
16
  import { Observable } from "rxjs";
17
17
  import { IKeySystemOption } from "../../public_types";
18
+ import { ITransportPipelines } from "../../transports";
18
19
  import { IReadOnlySharedReference } from "../../utils/reference";
19
20
  import { IAdaptiveRepresentationSelectorArguments } from "../adaptive";
20
21
  import { PlaybackObserver } from "../api";
21
- import { IManifestFetcherParsedResult, IManifestFetcherWarningEvent, ManifestFetcher, SegmentFetcherCreator } from "../fetchers";
22
+ import { IManifestFetcherParsedResult, IManifestFetcherWarningEvent, ManifestFetcher } from "../fetchers";
22
23
  import { ITextTrackSegmentBufferOptions } from "../segment_buffers";
23
24
  import { IAudioTrackSwitchingMode } from "../stream";
24
25
  import { IInitialTimeOptions } from "./get_initial_time";
@@ -65,8 +66,21 @@ export interface IInitializeArguments {
65
66
  mediaElement: HTMLMediaElement;
66
67
  /** Limit the frequency of Manifest updates. */
67
68
  minimumManifestUpdateInterval: number;
68
- /** Interface allowing to load segments */
69
- segmentFetcherCreator: SegmentFetcherCreator;
69
+ /** Interface allowing to interact with the transport protocol */
70
+ transport: ITransportPipelines;
71
+ /** Configuration for the segment requesting logic. */
72
+ segmentRequestOptions: {
73
+ /** Maximum number of time a request on error will be retried. */
74
+ regularError: number | undefined;
75
+ /** Maximum number of time a request be retried when the user is offline. */
76
+ offlineError: number | undefined;
77
+ /**
78
+ * Amount of time after which a request should be aborted.
79
+ * `undefined` indicates that a default value is wanted.
80
+ * `-1` indicates no timeout.
81
+ */
82
+ requestTimeout: number | undefined;
83
+ };
70
84
  /** Emit the playback rate (speed) set by the user. */
71
85
  speed: IReadOnlySharedReference<number>;
72
86
  /** The configured starting position. */
@@ -105,4 +119,4 @@ export interface IInitializeArguments {
105
119
  * @param {Object} args
106
120
  * @returns {Observable}
107
121
  */
108
- export default function InitializeOnMediaSource({ adaptiveOptions, autoPlay, bufferOptions, keySystems, lowLatencyMode, manifest$, manifestFetcher, mediaElement, minimumManifestUpdateInterval, playbackObserver, segmentFetcherCreator, speed, startAt, textTrackOptions }: IInitializeArguments): Observable<IInitEvent>;
122
+ export default function InitializeOnMediaSource({ adaptiveOptions, autoPlay, bufferOptions, keySystems, lowLatencyMode, manifest$, manifestFetcher, mediaElement, minimumManifestUpdateInterval, playbackObserver, segmentRequestOptions, speed, startAt, transport, textTrackOptions }: IInitializeArguments): Observable<IInitEvent>;
@@ -21,8 +21,10 @@ import deferSubscriptions from "../../utils/defer_subscriptions";
21
21
  import { fromEvent } from "../../utils/event_emitter";
22
22
  import filterMap from "../../utils/filter_map";
23
23
  import objectAssign from "../../utils/object_assign";
24
+ import TaskCanceller from "../../utils/task_canceller";
24
25
  import AdaptiveRepresentationSelector from "../adaptive";
25
26
  import { getCurrentKeySystem, } from "../decrypt";
27
+ import { SegmentFetcherCreator, } from "../fetchers";
26
28
  import openMediaSource from "./create_media_source";
27
29
  import EVENTS from "./events_generators";
28
30
  import getInitialTime from "./get_initial_time";
@@ -62,9 +64,10 @@ import throwOnMediaError from "./throw_on_media_error";
62
64
  * @returns {Observable}
63
65
  */
64
66
  export default function InitializeOnMediaSource(_a) {
65
- var adaptiveOptions = _a.adaptiveOptions, autoPlay = _a.autoPlay, bufferOptions = _a.bufferOptions, keySystems = _a.keySystems, lowLatencyMode = _a.lowLatencyMode, manifest$ = _a.manifest$, manifestFetcher = _a.manifestFetcher, mediaElement = _a.mediaElement, minimumManifestUpdateInterval = _a.minimumManifestUpdateInterval, playbackObserver = _a.playbackObserver, segmentFetcherCreator = _a.segmentFetcherCreator, speed = _a.speed, startAt = _a.startAt, textTrackOptions = _a.textTrackOptions;
67
+ var adaptiveOptions = _a.adaptiveOptions, autoPlay = _a.autoPlay, bufferOptions = _a.bufferOptions, keySystems = _a.keySystems, lowLatencyMode = _a.lowLatencyMode, manifest$ = _a.manifest$, manifestFetcher = _a.manifestFetcher, mediaElement = _a.mediaElement, minimumManifestUpdateInterval = _a.minimumManifestUpdateInterval, playbackObserver = _a.playbackObserver, segmentRequestOptions = _a.segmentRequestOptions, speed = _a.speed, startAt = _a.startAt, transport = _a.transport, textTrackOptions = _a.textTrackOptions;
66
68
  /** Choose the right "Representation" for a given "Adaptation". */
67
69
  var representationEstimator = AdaptiveRepresentationSelector(adaptiveOptions);
70
+ var playbackCanceller = new TaskCanceller();
68
71
  /**
69
72
  * Create and open a new MediaSource object on the given media element on
70
73
  * subscription.
@@ -108,6 +111,10 @@ export default function InitializeOnMediaSource(_a) {
108
111
  log.debug("Init: Calculating initial time");
109
112
  var initialTime = getInitialTime(manifest, lowLatencyMode, startAt);
110
113
  log.debug("Init: Initial time calculated:", initialTime);
114
+ var requestOptions = { lowLatencyMode: lowLatencyMode, requestTimeout: segmentRequestOptions.requestTimeout,
115
+ maxRetryRegular: segmentRequestOptions.regularError,
116
+ maxRetryOffline: segmentRequestOptions.offlineError };
117
+ var segmentFetcherCreator = new SegmentFetcherCreator(transport, requestOptions, playbackCanceller.signal);
111
118
  var mediaSourceLoader = createMediaSourceLoader({
112
119
  bufferOptions: objectAssign({ textTrackOptions: textTrackOptions, drmSystemId: drmSystemId }, bufferOptions),
113
120
  manifest: manifest,
@@ -189,5 +196,6 @@ export default function InitializeOnMediaSource(_a) {
189
196
  return observableMerge(handleReloads$, currentLoad$);
190
197
  }
191
198
  }));
192
- return observableMerge(loadContent$, mediaError$, drmEvents$.pipe(ignoreElements()));
199
+ return observableMerge(loadContent$, mediaError$, drmEvents$.pipe(ignoreElements()))
200
+ .pipe(finalize(function () { playbackCanceller.cancel(); }));
193
201
  }
@@ -16,6 +16,7 @@
16
16
  import { EMPTY, filter, finalize, ignoreElements, merge as observableMerge, mergeMap, of as observableOf, Subject, switchMap, takeUntil, throwError, } from "rxjs";
17
17
  import { MediaError } from "../../errors";
18
18
  import log from "../../log";
19
+ import createSharedReference from "../../utils/reference";
19
20
  import SegmentBuffersStore from "../segment_buffers";
20
21
  import StreamOrchestrator from "../stream";
21
22
  import ContentTimeBoundariesObserver from "./content_time_boundaries_observer";
@@ -63,6 +64,8 @@ export default function createMediaSourceLoader(_a) {
63
64
  var discontinuityUpdate$ = new Subject();
64
65
  /** Emits event when streams are "locked", meaning they cannot load segments. */
65
66
  var lockedStream$ = new Subject();
67
+ /** Emit each time a new Adaptation is considered by the `StreamOrchestrator`. */
68
+ var lastAdaptationChange = createSharedReference(null);
66
69
  // Creates Observable which will manage every Stream for the given Content.
67
70
  var streams$ = StreamOrchestrator({ manifest: manifest, initialPeriod: initialPeriod }, streamObserver, representationEstimator, segmentBuffersStore, segmentFetcherCreator, bufferOptions).pipe(mergeMap(function (evt) {
68
71
  switch (evt.type) {
@@ -80,11 +83,14 @@ export default function createMediaSourceLoader(_a) {
80
83
  case "locked-stream":
81
84
  lockedStream$.next(evt.value);
82
85
  return EMPTY;
86
+ case "adaptationChange":
87
+ lastAdaptationChange.setValue(evt);
88
+ return observableOf(evt);
83
89
  default:
84
90
  return observableOf(evt);
85
91
  }
86
92
  }));
87
- var contentTimeObserver = ContentTimeBoundariesObserver(manifest, streams$, streamObserver)
93
+ var contentTimeObserver = ContentTimeBoundariesObserver(manifest, lastAdaptationChange, streamObserver)
88
94
  .pipe(mergeMap(function (evt) {
89
95
  switch (evt.type) {
90
96
  case "contentDurationUpdate":
@@ -180,7 +180,9 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
180
180
  }
181
181
  /** Position at which data is awaited. */
182
182
  var stalledPosition = rebuffering.position;
183
- if (stalledPosition !== null && speed.getValue() > 0) {
183
+ if (stalledPosition !== null &&
184
+ stalledPosition !== undefined &&
185
+ speed.getValue() > 0) {
184
186
  var skippableDiscontinuity = findSeekableDiscontinuity(discontinuitiesStore, manifest, stalledPosition);
185
187
  if (skippableDiscontinuity !== null) {
186
188
  var realSeekTime = skippableDiscontinuity + 0.001;
@@ -336,6 +336,17 @@ declare const DEFAULT_CONFIG: {
336
336
  * @type Number
337
337
  */
338
338
  DEFAULT_MAX_MANIFEST_REQUEST_RETRY: number;
339
+ /**
340
+ * Default delay, in seconds, during which a CDN will be "downgraded".
341
+ *
342
+ * For example in case of media content being available on multiple CDNs, the
343
+ * RxPlayer may decide that a CDN is less reliable (for example, it returned a
344
+ * server error) and should thus be avoided, at least for some time
345
+ *
346
+ * This value is the amount of time this CDN will be "less considered" than the
347
+ * alternatives.
348
+ */
349
+ DEFAULT_CDN_DOWNGRADE_TIME: number;
339
350
  /**
340
351
  * The default number of times a segment request will be re-performed when
341
352
  * on error which justify a retry.