rx-player 3.28.0-dev.2022062000 → 3.28.0-dev.2022063000

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 (45) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/VERSION +1 -1
  3. package/dist/_esm5.processed/compat/event_listeners.js +1 -1
  4. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +1 -6
  5. package/dist/_esm5.processed/core/api/playback_observer.d.ts +47 -35
  6. package/dist/_esm5.processed/core/api/playback_observer.js +120 -117
  7. package/dist/_esm5.processed/core/api/public_api.d.ts +4 -6
  8. package/dist/_esm5.processed/core/api/public_api.js +18 -18
  9. package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +1 -1
  10. package/dist/_esm5.processed/core/init/create_stream_playback_observer.d.ts +1 -1
  11. package/dist/_esm5.processed/core/init/create_stream_playback_observer.js +23 -6
  12. package/dist/_esm5.processed/core/init/initial_seek_and_play.js +3 -3
  13. package/dist/_esm5.processed/core/init/initialize_directfile.js +1 -1
  14. package/dist/_esm5.processed/core/init/load_on_media_source.js +1 -1
  15. package/dist/_esm5.processed/core/init/stall_avoider.js +12 -8
  16. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +5 -4
  17. package/dist/_esm5.processed/core/stream/period/create_empty_adaptation_stream.js +1 -1
  18. package/dist/_esm5.processed/core/stream/period/period_stream.js +21 -5
  19. package/dist/_esm5.processed/core/stream/reload_after_switch.js +1 -1
  20. package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +1 -1
  21. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +1 -1
  22. package/dist/_esm5.processed/utils/reference.d.ts +2 -2
  23. package/dist/_esm5.processed/utils/reference.js +5 -4
  24. package/dist/rx-player.js +250 -198
  25. package/dist/rx-player.min.js +1 -1
  26. package/package.json +2 -2
  27. package/sonar-project.properties +1 -1
  28. package/src/README.md +84 -68
  29. package/src/compat/event_listeners.ts +1 -1
  30. package/src/core/adaptive/adaptive_representation_selector.ts +1 -7
  31. package/src/core/api/playback_observer.ts +185 -173
  32. package/src/core/api/public_api.ts +23 -21
  33. package/src/core/init/content_time_boundaries_observer.ts +1 -1
  34. package/src/core/init/create_stream_playback_observer.ts +69 -47
  35. package/src/core/init/initial_seek_and_play.ts +3 -3
  36. package/src/core/init/initialize_directfile.ts +1 -1
  37. package/src/core/init/load_on_media_source.ts +1 -1
  38. package/src/core/init/stall_avoider.ts +12 -9
  39. package/src/core/stream/orchestrator/stream_orchestrator.ts +5 -4
  40. package/src/core/stream/period/create_empty_adaptation_stream.ts +1 -1
  41. package/src/core/stream/period/period_stream.ts +33 -14
  42. package/src/core/stream/reload_after_switch.ts +1 -1
  43. package/src/core/stream/representation/append_segment_to_buffer.ts +1 -1
  44. package/src/core/stream/representation/representation_stream.ts +1 -1
  45. package/src/utils/reference.ts +7 -5
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## v3.28.0
3
+ ## /v3.28.0-dev.2022063000
4
4
 
5
5
  ### Features
6
6
 
@@ -16,6 +16,7 @@
16
16
  - After a `RELOADING` state, stay in `PAUSED` if the media element was paused synchronously before the side-effect which triggered the reloading (usually coming from the API) was perform [#1132]
17
17
  - Fix issue with `maxVideoBufferSize` setting which could lead to too much data being buffered [#1125]
18
18
  - Prevent possibility of requests loops and infinite rebuffering when a pushed segment is always completely and immediately garbage collected by the browser [#1123]
19
+ - DASH: Fix potential rare memory leak when stopping the content after it has reloaded at least once [#1135]
19
20
  - DASH: Fix issues that could arise if a segment is calculated to start at a negative position [#1122]
20
21
  - DASH: Fix possibility of wrong segments being requested when a SegmentTimeline in a given Period (whose Period@end is set) had an S@r set to `-1` at its end [#1098]
21
22
  - DASH: If the first `<S>` has its S@t attribute not set, make as if it is set to `0` [#1118]
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.28.0-dev.2022062000
1
+ 3.28.0-dev.2022063000
@@ -132,7 +132,7 @@ function getDocumentVisibilityRef(stopListening) {
132
132
  var visibilityChangeEvent = isNonEmptyString(prefix) ? prefix + "visibilitychange" :
133
133
  "visibilitychange";
134
134
  var isHidden = document[hidden];
135
- var ref = createSharedReference(isHidden);
135
+ var ref = createSharedReference(!isHidden);
136
136
  addEventListener(document, visibilityChangeEvent, function () {
137
137
  var isVisible = !(document[hidden]);
138
138
  ref.setValueIfChanged(isVisible);
@@ -170,13 +170,8 @@ function getEstimateReference(_a, stopAllEstimates) {
170
170
  * Only used in very specific scenarios.
171
171
  */
172
172
  var guessBasedChooser = new GuessBasedChooser(scoreCalculator, prevEstimate);
173
- var lastPlaybackObservation;
174
173
  // get initial observation for initial estimate
175
- var unregisterInitial = playbackObserver.listen(function (obs) {
176
- lastPlaybackObservation = obs;
177
- }, { includeLastObservation: true });
178
- unregisterInitial(); // The initial is emitted synchronously, we can now remove it
179
- // TODO cleaner playbackObserver.getLast() or something?
174
+ var lastPlaybackObservation = playbackObserver.getReference().getValue();
180
175
  /** Reference through which estimates are emitted. */
181
176
  var innerEstimateRef = createSharedReference(getCurrentEstimate());
182
177
  // subscribe to subsequent playback observations
@@ -13,7 +13,8 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { Observable } from "rxjs";
16
+ import noop from "../../utils/noop";
17
+ import { IReadOnlySharedReference } from "../../utils/reference";
17
18
  import { CancellationSignal } from "../../utils/task_canceller";
18
19
  /**
19
20
  * Class allowing to "observe" current playback conditions so the RxPlayer is
@@ -55,21 +56,36 @@ export default class PlaybackObserver {
55
56
  */
56
57
  private _internalSeekingEventsIncomingCounter;
57
58
  /**
58
- * Last playback observation made by the `PlaybackObserver`.
59
- *
60
- * `null` if no observation has been made yet.
59
+ * Stores the last playback observation produced by the `PlaybackObserver`.:
61
60
  */
62
- private _lastObservation;
61
+ private _observationRef;
63
62
  /**
64
- * Lazily-created shared Observable that will emit playback observations.
65
- * Set to `null` until the first time it is generated.
63
+ * `TaskCanceller` allowing to free all resources and stop producing playback
64
+ * observations.
66
65
  */
67
- private _observation$;
66
+ private _canceller;
68
67
  /**
68
+ * Create a new `PlaybackObserver`, which allows to produce new "playback
69
+ * observations" on various media events and intervals.
70
+ *
71
+ * Note that creating a `PlaybackObserver` lead to the usage of resources,
72
+ * such as event listeners which will only be freed once the `stop` method is
73
+ * called.
69
74
  * @param {HTMLMediaElement} mediaElement
70
75
  * @param {Object} options
71
76
  */
72
77
  constructor(mediaElement: HTMLMediaElement, options: IPlaybackObserverOptions);
78
+ /**
79
+ * Stop the `PlaybackObserver` from emitting playback observations and free all
80
+ * resources reserved to emitting them such as event listeners, intervals and
81
+ * subscribing callbacks.
82
+ *
83
+ * Once `stop` is called, no new playback observation will ever be emitted.
84
+ *
85
+ * Note that it is important to call stop once the `PlaybackObserver` is no
86
+ * more needed to avoid unnecessarily leaking resources.
87
+ */
88
+ stop(): void;
73
89
  /**
74
90
  * Returns the current position advertised by the `HTMLMediaElement`, in
75
91
  * seconds.
@@ -101,18 +117,16 @@ export default class PlaybackObserver {
101
117
  */
102
118
  getReadyState(): number;
103
119
  /**
104
- * Returns an Observable regularly emitting playback observation, optionally
105
- * starting with the last one.
120
+ * Returns an `IReadOnlySharedReference` storing the last playback observation
121
+ * produced by the `PlaybackObserver` and updated each time a new one is
122
+ * produced.
106
123
  *
107
- * Note that this Observable is shared and unique, so that multiple `observe`
108
- * call will return the exact same Observable and multiple concurrent
109
- * `subscribe` will receive the same events at the same time.
110
- * This was done for performance and simplicity reasons.
124
+ * This value can then be for example subscribed to to be notified of future
125
+ * playback observations.
111
126
  *
112
- * @param {boolean} includeLastObservation
113
- * @returns {Observable}
127
+ * @returns {Object}
114
128
  */
115
- observe(includeLastObservation: boolean): Observable<IPlaybackObservation>;
129
+ getReference(): IReadOnlySharedReference<IPlaybackObservation>;
116
130
  /**
117
131
  * Register a callback so it regularly receives playback observations.
118
132
  * @param {Function} cb
@@ -121,12 +135,11 @@ export default class PlaybackObserver {
121
135
  * be first emitted synchronously.
122
136
  * - `clearSignal`: If set, the callback will be unregistered when this
123
137
  * CancellationSignal emits.
124
- * @returns {Function} - Allows to easily unregister the callback
125
138
  */
126
139
  listen(cb: (observation: IPlaybackObservation) => void, options?: {
127
140
  includeLastObservation?: boolean | undefined;
128
141
  clearSignal?: CancellationSignal | undefined;
129
- }): () => void;
142
+ }): typeof noop | undefined;
130
143
  /**
131
144
  * Generate a new playback observer which can listen to other
132
145
  * properties and which can only be accessed to read observations (e.g.
@@ -138,15 +151,16 @@ export default class PlaybackObserver {
138
151
  *
139
152
  * As argument, this method takes a function which will allow to produce
140
153
  * the new set of properties to be present on each observation.
141
- * @param {Function} mapObservable
154
+ * @param {Function} transform
142
155
  * @returns {Object}
143
156
  */
144
- deriveReadOnlyObserver<TDest>(mapObservable: (observation$: Observable<IPlaybackObservation>) => Observable<TDest>): IReadOnlyPlaybackObserver<TDest>;
157
+ deriveReadOnlyObserver<TDest>(transform: (observationRef: IReadOnlySharedReference<IPlaybackObservation>, cancellationSignal: CancellationSignal) => IReadOnlySharedReference<TDest>): IReadOnlyPlaybackObserver<TDest>;
145
158
  /**
146
- * Creates the observable that will generate playback observations.
159
+ * Creates the `IReadOnlySharedReference` that will generate playback
160
+ * observations.
147
161
  * @returns {Observable}
148
162
  */
149
- private _createInnerObservable;
163
+ private _createSharedReference;
150
164
  private _generateInitialObservation;
151
165
  }
152
166
  /** "Event" that triggered the playback observation. */
@@ -286,18 +300,16 @@ export interface IReadOnlyPlaybackObserver<TObservationType> {
286
300
  */
287
301
  getIsPaused(): boolean;
288
302
  /**
289
- * Returns an Observable regularly emitting playback observation, optionally
290
- * starting with the last one.
303
+ * Returns an `IReadOnlySharedReference` storing the last playback observation
304
+ * produced by the `IReadOnlyPlaybackObserver` and updated each time a new one
305
+ * is produced.
291
306
  *
292
- * Note that this Observable is shared and unique, so that multiple `observe`
293
- * call will return the exact same Observable and multiple concurrent
294
- * `subscribe` will receive the same events at the same time.
295
- * This was done for performance and simplicity reasons.
307
+ * This value can then be for example subscribed to to be notified of future
308
+ * playback observations.
296
309
  *
297
- * @param {boolean} includeLastObservation
298
- * @returns {Observable}
310
+ * @returns {Object}
299
311
  */
300
- observe(includeLastObservation: boolean): Observable<TObservationType>;
312
+ getReference(): IReadOnlySharedReference<TObservationType>;
301
313
  /**
302
314
  * Register a callback so it regularly receives playback observations.
303
315
  * @param {Function} cb
@@ -311,16 +323,16 @@ export interface IReadOnlyPlaybackObserver<TObservationType> {
311
323
  listen(cb: (observation: TObservationType) => void, options?: {
312
324
  includeLastObservation?: boolean | undefined;
313
325
  clearSignal?: CancellationSignal | undefined;
314
- }): () => void;
326
+ }): void;
315
327
  /**
316
328
  * Generate a new `IReadOnlyPlaybackObserver` from this one.
317
329
  *
318
330
  * As argument, this method takes a function which will allow to produce
319
331
  * the new set of properties to be present on each observation.
320
- * @param {Function} mapObservable
332
+ * @param {Function} transform
321
333
  * @returns {Object}
322
334
  */
323
- deriveReadOnlyObserver<TDest>(mapObservable: (observation$: Observable<TObservationType>) => Observable<TDest>, mapObservation: (observation: TObservationType) => TDest): IReadOnlyPlaybackObserver<TDest>;
335
+ deriveReadOnlyObserver<TDest>(transform: (observationRef: IReadOnlySharedReference<TObservationType>, cancellationSignal: CancellationSignal) => IReadOnlySharedReference<TDest>): IReadOnlyPlaybackObserver<TDest>;
324
336
  }
325
337
  export interface IPlaybackObserverOptions {
326
338
  withMediaSource: boolean;
@@ -13,21 +13,13 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
17
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
18
- if (ar || !(i in from)) {
19
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
20
- ar[i] = from[i];
21
- }
22
- }
23
- return to.concat(ar || Array.prototype.slice.call(from));
24
- };
25
- import { defer as observableDefer, fromEvent as observableFromEvent, interval as observableInterval, map, merge as observableMerge, share, shareReplay, skip, startWith, } from "rxjs";
26
16
  import config from "../../config";
27
17
  import log from "../../log";
28
18
  import noop from "../../utils/noop";
29
19
  import objectAssign from "../../utils/object_assign";
30
20
  import { getRange } from "../../utils/ranges";
21
+ import createSharedReference from "../../utils/reference";
22
+ import TaskCanceller from "../../utils/task_canceller";
31
23
  /**
32
24
  * HTMLMediaElement Events for which playback observations are calculated and
33
25
  * emitted.
@@ -56,6 +48,12 @@ var SCANNED_MEDIA_ELEMENTS_EVENTS = ["canplay",
56
48
  */
57
49
  var PlaybackObserver = /** @class */ (function () {
58
50
  /**
51
+ * Create a new `PlaybackObserver`, which allows to produce new "playback
52
+ * observations" on various media events and intervals.
53
+ *
54
+ * Note that creating a `PlaybackObserver` lead to the usage of resources,
55
+ * such as event listeners which will only be freed once the `stop` method is
56
+ * called.
59
57
  * @param {HTMLMediaElement} mediaElement
60
58
  * @param {Object} options
61
59
  */
@@ -64,9 +62,22 @@ var PlaybackObserver = /** @class */ (function () {
64
62
  this._mediaElement = mediaElement;
65
63
  this._withMediaSource = options.withMediaSource;
66
64
  this._lowLatencyMode = options.lowLatencyMode;
67
- this._lastObservation = null;
68
- this._observation$ = null;
65
+ this._canceller = new TaskCanceller();
66
+ this._observationRef = this._createSharedReference();
69
67
  }
68
+ /**
69
+ * Stop the `PlaybackObserver` from emitting playback observations and free all
70
+ * resources reserved to emitting them such as event listeners, intervals and
71
+ * subscribing callbacks.
72
+ *
73
+ * Once `stop` is called, no new playback observation will ever be emitted.
74
+ *
75
+ * Note that it is important to call stop once the `PlaybackObserver` is no
76
+ * more needed to avoid unnecessarily leaking resources.
77
+ */
78
+ PlaybackObserver.prototype.stop = function () {
79
+ this._canceller.cancel();
80
+ };
70
81
  /**
71
82
  * Returns the current position advertised by the `HTMLMediaElement`, in
72
83
  * seconds.
@@ -107,31 +118,17 @@ var PlaybackObserver = /** @class */ (function () {
107
118
  return this._mediaElement.readyState;
108
119
  };
109
120
  /**
110
- * Returns an Observable regularly emitting playback observation, optionally
111
- * starting with the last one.
121
+ * Returns an `IReadOnlySharedReference` storing the last playback observation
122
+ * produced by the `PlaybackObserver` and updated each time a new one is
123
+ * produced.
112
124
  *
113
- * Note that this Observable is shared and unique, so that multiple `observe`
114
- * call will return the exact same Observable and multiple concurrent
115
- * `subscribe` will receive the same events at the same time.
116
- * This was done for performance and simplicity reasons.
125
+ * This value can then be for example subscribed to to be notified of future
126
+ * playback observations.
117
127
  *
118
- * @param {boolean} includeLastObservation
119
- * @returns {Observable}
128
+ * @returns {Object}
120
129
  */
121
- PlaybackObserver.prototype.observe = function (includeLastObservation) {
122
- var _this = this;
123
- return observableDefer(function () {
124
- if (_this._observation$ === null || _this._lastObservation === null) {
125
- _this._lastObservation = _this._generateInitialObservation();
126
- _this._observation$ = _this._createInnerObservable().pipe(share());
127
- return _this.observe(includeLastObservation);
128
- }
129
- else {
130
- return includeLastObservation ?
131
- _this._observation$.pipe(startWith(_this._lastObservation)) :
132
- _this._observation$;
133
- }
134
- });
130
+ PlaybackObserver.prototype.getReference = function () {
131
+ return this._observationRef;
135
132
  };
136
133
  /**
137
134
  * Register a callback so it regularly receives playback observations.
@@ -141,22 +138,16 @@ var PlaybackObserver = /** @class */ (function () {
141
138
  * be first emitted synchronously.
142
139
  * - `clearSignal`: If set, the callback will be unregistered when this
143
140
  * CancellationSignal emits.
144
- * @returns {Function} - Allows to easily unregister the callback
145
141
  */
146
142
  PlaybackObserver.prototype.listen = function (cb, options) {
147
- var _a, _b, _c, _d;
148
- if (((_a = options === null || options === void 0 ? void 0 : options.clearSignal) === null || _a === void 0 ? void 0 : _a.isCancelled) === true) {
143
+ var _a;
144
+ if (this._canceller.isUsed || ((_a = options === null || options === void 0 ? void 0 : options.clearSignal) === null || _a === void 0 ? void 0 : _a.isCancelled) === true) {
149
145
  return noop;
150
146
  }
151
- var sub = this.observe((_b = options === null || options === void 0 ? void 0 : options.includeLastObservation) !== null && _b !== void 0 ? _b : false)
152
- .subscribe(cb);
153
- var unregister = (_d = (_c = options === null || options === void 0 ? void 0 : options.clearSignal) === null || _c === void 0 ? void 0 : _c.register(function () {
154
- sub.unsubscribe();
155
- })) !== null && _d !== void 0 ? _d : noop;
156
- return function () {
157
- unregister();
158
- sub.unsubscribe();
159
- };
147
+ this._observationRef.onUpdate(cb, {
148
+ clearSignal: options === null || options === void 0 ? void 0 : options.clearSignal,
149
+ emitCurrentValue: options === null || options === void 0 ? void 0 : options.includeLastObservation,
150
+ });
160
151
  };
161
152
  /**
162
153
  * Generate a new playback observer which can listen to other
@@ -169,63 +160,84 @@ var PlaybackObserver = /** @class */ (function () {
169
160
  *
170
161
  * As argument, this method takes a function which will allow to produce
171
162
  * the new set of properties to be present on each observation.
172
- * @param {Function} mapObservable
163
+ * @param {Function} transform
173
164
  * @returns {Object}
174
165
  */
175
- PlaybackObserver.prototype.deriveReadOnlyObserver = function (mapObservable) {
176
- return generateReadOnlyObserver(this, mapObservable);
166
+ PlaybackObserver.prototype.deriveReadOnlyObserver = function (transform) {
167
+ return generateReadOnlyObserver(this, transform, this._canceller.signal);
177
168
  };
178
169
  /**
179
- * Creates the observable that will generate playback observations.
170
+ * Creates the `IReadOnlySharedReference` that will generate playback
171
+ * observations.
180
172
  * @returns {Observable}
181
173
  */
182
- PlaybackObserver.prototype._createInnerObservable = function () {
174
+ PlaybackObserver.prototype._createSharedReference = function () {
183
175
  var _this = this;
184
- return observableDefer(function () {
185
- var _a = config.getCurrent(), SAMPLING_INTERVAL_MEDIASOURCE = _a.SAMPLING_INTERVAL_MEDIASOURCE, SAMPLING_INTERVAL_LOW_LATENCY = _a.SAMPLING_INTERVAL_LOW_LATENCY, SAMPLING_INTERVAL_NO_MEDIASOURCE = _a.SAMPLING_INTERVAL_NO_MEDIASOURCE;
186
- var getCurrentObservation = function (event) {
187
- var _a;
188
- var tmpEvt = event;
189
- if (tmpEvt === "seeking" && _this._internalSeekingEventsIncomingCounter > 0) {
190
- tmpEvt = "internal-seeking";
191
- _this._internalSeekingEventsIncomingCounter -= 1;
192
- }
193
- var lastObservation = (_a = _this._lastObservation) !== null && _a !== void 0 ? _a : _this._generateInitialObservation();
194
- var mediaTimings = getMediaInfos(_this._mediaElement, tmpEvt);
195
- var internalSeeking = mediaTimings.seeking &&
196
- // We've just received the event for internally seeking
197
- (tmpEvt === "internal-seeking" ||
198
- // or We're still waiting on the previous internal-seek
199
- (lastObservation.internalSeeking && tmpEvt !== "seeking"));
200
- var rebufferingStatus = getRebufferingStatus(lastObservation, mediaTimings, { lowLatencyMode: _this._lowLatencyMode,
201
- withMediaSource: _this._withMediaSource });
202
- var freezingStatus = getFreezingStatus(lastObservation, mediaTimings);
203
- var timings = objectAssign({}, { rebuffering: rebufferingStatus,
204
- freezing: freezingStatus, internalSeeking: internalSeeking }, mediaTimings);
205
- if (log.hasLevel("DEBUG")) {
206
- log.debug("API: current media element state tick", "event", timings.event, "position", timings.position, "seeking", timings.seeking, "internalSeeking", timings.internalSeeking, "rebuffering", timings.rebuffering !== null, "freezing", timings.freezing !== null, "ended", timings.ended, "paused", timings.paused, "playbackRate", timings.playbackRate, "readyState", timings.readyState);
207
- }
208
- return timings;
176
+ if (this._observationRef !== undefined) {
177
+ return this._observationRef;
178
+ }
179
+ var lastObservation;
180
+ var _a = config.getCurrent(), SAMPLING_INTERVAL_MEDIASOURCE = _a.SAMPLING_INTERVAL_MEDIASOURCE, SAMPLING_INTERVAL_LOW_LATENCY = _a.SAMPLING_INTERVAL_LOW_LATENCY, SAMPLING_INTERVAL_NO_MEDIASOURCE = _a.SAMPLING_INTERVAL_NO_MEDIASOURCE;
181
+ var getCurrentObservation = function (event) {
182
+ var tmpEvt = event;
183
+ if (tmpEvt === "seeking" && _this._internalSeekingEventsIncomingCounter > 0) {
184
+ tmpEvt = "internal-seeking";
185
+ _this._internalSeekingEventsIncomingCounter -= 1;
186
+ }
187
+ var _lastObservation = lastObservation !== null && lastObservation !== void 0 ? lastObservation : _this._generateInitialObservation();
188
+ var mediaTimings = getMediaInfos(_this._mediaElement, tmpEvt);
189
+ var internalSeeking = mediaTimings.seeking &&
190
+ // We've just received the event for internally seeking
191
+ (tmpEvt === "internal-seeking" ||
192
+ // or We're still waiting on the previous internal-seek
193
+ (_lastObservation.internalSeeking && tmpEvt !== "seeking"));
194
+ var rebufferingStatus = getRebufferingStatus(_lastObservation, mediaTimings, { lowLatencyMode: _this._lowLatencyMode,
195
+ withMediaSource: _this._withMediaSource });
196
+ var freezingStatus = getFreezingStatus(_lastObservation, mediaTimings);
197
+ var timings = objectAssign({}, { rebuffering: rebufferingStatus,
198
+ freezing: freezingStatus, internalSeeking: internalSeeking }, mediaTimings);
199
+ if (log.hasLevel("DEBUG")) {
200
+ log.debug("API: current media element state tick", "event", timings.event, "position", timings.position, "seeking", timings.seeking, "internalSeeking", timings.internalSeeking, "rebuffering", timings.rebuffering !== null, "freezing", timings.freezing !== null, "ended", timings.ended, "paused", timings.paused, "playbackRate", timings.playbackRate, "readyState", timings.readyState);
201
+ }
202
+ return timings;
203
+ };
204
+ var returnedSharedReference = createSharedReference(getCurrentObservation("init"));
205
+ var generateObservationForEvent = function (event) {
206
+ var newObservation = getCurrentObservation(event);
207
+ if (log.hasLevel("DEBUG")) {
208
+ log.debug("API: current playback timeline:\n" +
209
+ prettyPrintBuffered(newObservation.buffered, newObservation.position), "\n".concat(event));
210
+ }
211
+ lastObservation = newObservation;
212
+ returnedSharedReference.setValue(newObservation);
213
+ };
214
+ var interval = this._lowLatencyMode ? SAMPLING_INTERVAL_LOW_LATENCY :
215
+ this._withMediaSource ? SAMPLING_INTERVAL_MEDIASOURCE :
216
+ SAMPLING_INTERVAL_NO_MEDIASOURCE;
217
+ var intervalId = setInterval(onInterval, interval);
218
+ var removeEventListeners = SCANNED_MEDIA_ELEMENTS_EVENTS.map(function (eventName) {
219
+ _this._mediaElement.addEventListener(eventName, onMediaEvent);
220
+ function onMediaEvent() {
221
+ restartInterval();
222
+ generateObservationForEvent(eventName);
223
+ }
224
+ return function () {
225
+ _this._mediaElement.removeEventListener(eventName, onMediaEvent);
209
226
  };
210
- var eventObs = SCANNED_MEDIA_ELEMENTS_EVENTS.map(function (eventName) {
211
- return observableFromEvent(_this._mediaElement, eventName)
212
- .pipe(map(function () { return eventName; }));
213
- });
214
- var interval = _this._lowLatencyMode ? SAMPLING_INTERVAL_LOW_LATENCY :
215
- _this._withMediaSource ? SAMPLING_INTERVAL_MEDIASOURCE :
216
- SAMPLING_INTERVAL_NO_MEDIASOURCE;
217
- var interval$ = observableInterval(interval)
218
- .pipe(map(function () { return "timeupdate"; }));
219
- return observableMerge.apply(void 0, __spreadArray([interval$], eventObs, false)).pipe(map(function (event) {
220
- var newObservation = getCurrentObservation(event);
221
- if (log.hasLevel("DEBUG")) {
222
- log.debug("API: current playback timeline:\n" +
223
- prettyPrintBuffered(newObservation.buffered, newObservation.position), "\n".concat(event));
224
- }
225
- _this._lastObservation = newObservation;
226
- return newObservation;
227
- }));
228
227
  });
228
+ this._canceller.signal.register(function () {
229
+ clearInterval(intervalId);
230
+ removeEventListeners.forEach(function (cb) { return cb(); });
231
+ returnedSharedReference.finish();
232
+ });
233
+ return returnedSharedReference;
234
+ function onInterval() {
235
+ generateObservationForEvent("timeupdate");
236
+ }
237
+ function restartInterval() {
238
+ clearInterval(intervalId);
239
+ intervalId = setInterval(onInterval, interval);
240
+ }
229
241
  };
230
242
  PlaybackObserver.prototype._generateInitialObservation = function () {
231
243
  return objectAssign(getMediaInfos(this._mediaElement, "init"), { rebuffering: null,
@@ -480,13 +492,11 @@ function prettyPrintBuffered(buffered, currentTime) {
480
492
  * Create `IReadOnlyPlaybackObserver` from a source `IReadOnlyPlaybackObserver`
481
493
  * and a mapping function.
482
494
  * @param {Object} src
483
- * @param {Function} mapObservable
495
+ * @param {Function} transform
484
496
  * @returns {Object}
485
497
  */
486
- function generateReadOnlyObserver(src, mapObservable) {
487
- var newObs = observableDefer(function () {
488
- return mapObservable(src.observe(true));
489
- }).pipe(shareReplay({ bufferSize: 1, refCount: true }));
498
+ function generateReadOnlyObserver(src, transform, cancellationSignal) {
499
+ var mappedRef = transform(src.getReference(), cancellationSignal);
490
500
  return {
491
501
  getCurrentTime: function () {
492
502
  return src.getCurrentTime();
@@ -497,28 +507,21 @@ function generateReadOnlyObserver(src, mapObservable) {
497
507
  getIsPaused: function () {
498
508
  return src.getIsPaused();
499
509
  },
500
- observe: function (includeLastObservation) {
501
- return includeLastObservation ? newObs :
502
- newObs.pipe(skip(1));
510
+ getReference: function () {
511
+ return mappedRef;
503
512
  },
504
513
  listen: function (cb, options) {
505
- var _a, _b, _c;
506
- if (((_a = options === null || options === void 0 ? void 0 : options.clearSignal) === null || _a === void 0 ? void 0 : _a.isCancelled) === true) {
507
- return noop;
514
+ var _a;
515
+ if (cancellationSignal.isCancelled || ((_a = options === null || options === void 0 ? void 0 : options.clearSignal) === null || _a === void 0 ? void 0 : _a.isCancelled) === true) {
516
+ return;
508
517
  }
509
- var obs = (options === null || options === void 0 ? void 0 : options.includeLastObservation) === true ? newObs :
510
- newObs.pipe(skip(1));
511
- var sub = obs.subscribe(cb);
512
- var unregister = (_c = (_b = options === null || options === void 0 ? void 0 : options.clearSignal) === null || _b === void 0 ? void 0 : _b.register(function () {
513
- sub.unsubscribe();
514
- })) !== null && _c !== void 0 ? _c : noop;
515
- return function () {
516
- unregister();
517
- sub.unsubscribe();
518
- };
518
+ mappedRef.onUpdate(cb, {
519
+ clearSignal: options === null || options === void 0 ? void 0 : options.clearSignal,
520
+ emitCurrentValue: options === null || options === void 0 ? void 0 : options.includeLastObservation,
521
+ });
519
522
  },
520
- deriveReadOnlyObserver: function (newUdateObserver) {
521
- return generateReadOnlyObserver(this, newUdateObserver);
523
+ deriveReadOnlyObserver: function (newTransformFn) {
524
+ return generateReadOnlyObserver(this, newTransformFn, cancellationSignal);
522
525
  },
523
526
  };
524
527
  }
@@ -29,7 +29,10 @@ declare class Player extends EventEmitter<IPublicAPIEvent> {
29
29
  static version: string;
30
30
  /** Current version of the RxPlayer. */
31
31
  readonly version: string;
32
- /** Media element attached to the RxPlayer. */
32
+ /**
33
+ * Media element attached to the RxPlayer.
34
+ * Set to `null` when the RxPlayer is disposed.
35
+ */
33
36
  videoElement: HTMLMediaElement | null;
34
37
  /** Logger the RxPlayer uses. */
35
38
  readonly log: Logger;
@@ -693,11 +696,6 @@ declare class Player extends EventEmitter<IPublicAPIEvent> {
693
696
  * @param {Error} error
694
697
  */
695
698
  private _priv_onPlaybackError;
696
- /**
697
- * Triggered when the playback Observable completes.
698
- * Clean-up ressources and signal that the content has ended.
699
- */
700
- private _priv_onPlaybackFinished;
701
699
  /**
702
700
  * Triggered when we received a warning event during playback.
703
701
  * Trigger the right API event.
@@ -88,7 +88,7 @@ var Player = /** @class */ (function (_super) {
88
88
  // Workaround to support Firefox autoplay on FF 42.
89
89
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
90
90
  videoElement.preload = "auto";
91
- _this.version = /* PLAYER_VERSION */ "3.28.0-dev.2022062000";
91
+ _this.version = /* PLAYER_VERSION */ "3.28.0-dev.2022063000";
92
92
  _this.log = log;
93
93
  _this.state = "STOPPED";
94
94
  _this.videoElement = videoElement;
@@ -359,6 +359,9 @@ var Player = /** @class */ (function (_super) {
359
359
  withMediaSource: !isDirectFile,
360
360
  lowLatencyMode: lowLatencyMode,
361
361
  });
362
+ currentContentCanceller.signal.register(function () {
363
+ playbackObserver.stop();
364
+ });
362
365
  /** Emit playback events. */
363
366
  var playback$;
364
367
  if (!isDirectFile) {
@@ -534,7 +537,7 @@ var Player = /** @class */ (function (_super) {
534
537
  return evt.type === "reloading-media-source";
535
538
  }), share());
536
539
  /** Emit when the media element emits a "seeking" event. */
537
- var observation$ = playbackObserver.observe(true);
540
+ var observation$ = playbackObserver.getReference().asObservable();
538
541
  var stateChangingEvent$ = observation$.pipe(filter(function (o) {
539
542
  return o.event === "seeking" || o.event === "ended" ||
540
543
  o.event === "play" || o.event === "pause";
@@ -598,7 +601,14 @@ var Player = /** @class */ (function (_super) {
598
601
  playback$.subscribe({
599
602
  next: function (x) { return _this._priv_onPlaybackEvent(x); },
600
603
  error: function (err) { return _this._priv_onPlaybackError(err); },
601
- complete: function () { return _this._priv_onPlaybackFinished(); },
604
+ complete: function () {
605
+ if (!contentInfos.currentContentCanceller.isUsed) {
606
+ log.info("API: Previous playback finished. Stopping and cleaning-up...");
607
+ contentInfos.currentContentCanceller.cancel();
608
+ _this._priv_cleanUpCurrentContentState();
609
+ _this._priv_setPlayerState(PLAYER_STATES.STOPPED);
610
+ }
611
+ },
602
612
  });
603
613
  // initialize the content only when the lock is inactive
604
614
  this._priv_contentLock.asObservable()
@@ -1758,8 +1768,10 @@ var Player = /** @class */ (function (_super) {
1758
1768
  this._priv_contentEventsMemory = {};
1759
1769
  // DRM-related clean-up
1760
1770
  var freeUpContentLock = function () {
1761
- log.debug("Unlocking `contentLock`. Next content can begin.");
1762
- _this._priv_contentLock.setValue(false);
1771
+ if (_this.videoElement !== null) { // If not disposed
1772
+ log.debug("Unlocking `contentLock`. Next content can begin.");
1773
+ _this._priv_contentLock.setValue(false);
1774
+ }
1763
1775
  };
1764
1776
  if (!isNullOrUndefined(this.videoElement)) {
1765
1777
  clearOnStop(this.videoElement).then(function () {
@@ -1878,18 +1890,6 @@ var Player = /** @class */ (function (_super) {
1878
1890
  this.trigger("error", formattedError);
1879
1891
  }
1880
1892
  };
1881
- /**
1882
- * Triggered when the playback Observable completes.
1883
- * Clean-up ressources and signal that the content has ended.
1884
- */
1885
- Player.prototype._priv_onPlaybackFinished = function () {
1886
- log.info("API: Previous playback finished. Stopping and cleaning-up...");
1887
- if (this._priv_contentInfos !== null) {
1888
- this._priv_contentInfos.currentContentCanceller.cancel();
1889
- }
1890
- this._priv_cleanUpCurrentContentState();
1891
- this._priv_setPlayerState(PLAYER_STATES.ENDED);
1892
- };
1893
1893
  /**
1894
1894
  * Triggered when we received a warning event during playback.
1895
1895
  * Trigger the right API event.
@@ -2295,5 +2295,5 @@ var Player = /** @class */ (function (_super) {
2295
2295
  };
2296
2296
  return Player;
2297
2297
  }(EventEmitter));
2298
- Player.version = /* PLAYER_VERSION */ "3.28.0-dev.2022062000";
2298
+ Player.version = /* PLAYER_VERSION */ "3.28.0-dev.2022063000";
2299
2299
  export default Player;
@@ -45,7 +45,7 @@ export default function ContentTimeBoundariesObserver(manifest, streams, playbac
45
45
  var maximumPositionCalculator = new MaximumPositionCalculator(manifest);
46
46
  // trigger warnings when the wanted time is before or after the manifest's
47
47
  // segments
48
- var outOfManifest$ = playbackObserver.observe(true).pipe(filterMap(function (_a) {
48
+ var outOfManifest$ = playbackObserver.getReference().asObservable().pipe(filterMap(function (_a) {
49
49
  var _b;
50
50
  var position = _a.position;
51
51
  var wantedPosition = (_b = position.pending) !== null && _b !== void 0 ? _b : position.last;