rx-player 3.28.0-dev.2022062300 → 3.28.0-dev.2022062700

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 (42) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/VERSION +1 -1
  3. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +1 -6
  4. package/dist/_esm5.processed/core/api/playback_observer.d.ts +47 -35
  5. package/dist/_esm5.processed/core/api/playback_observer.js +120 -117
  6. package/dist/_esm5.processed/core/api/public_api.d.ts +4 -1
  7. package/dist/_esm5.processed/core/api/public_api.js +10 -5
  8. package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +1 -1
  9. package/dist/_esm5.processed/core/init/create_stream_playback_observer.d.ts +1 -1
  10. package/dist/_esm5.processed/core/init/create_stream_playback_observer.js +23 -6
  11. package/dist/_esm5.processed/core/init/initial_seek_and_play.js +3 -3
  12. package/dist/_esm5.processed/core/init/initialize_directfile.js +1 -1
  13. package/dist/_esm5.processed/core/init/load_on_media_source.js +1 -1
  14. package/dist/_esm5.processed/core/init/stall_avoider.js +12 -8
  15. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +5 -4
  16. package/dist/_esm5.processed/core/stream/period/create_empty_adaptation_stream.js +1 -1
  17. package/dist/_esm5.processed/core/stream/period/period_stream.js +21 -5
  18. package/dist/_esm5.processed/core/stream/reload_after_switch.js +1 -1
  19. package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +1 -1
  20. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +1 -1
  21. package/dist/_esm5.processed/utils/reference.d.ts +2 -2
  22. package/dist/_esm5.processed/utils/reference.js +5 -4
  23. package/dist/rx-player.js +241 -179
  24. package/dist/rx-player.min.js +1 -1
  25. package/package.json +2 -2
  26. package/sonar-project.properties +1 -1
  27. package/src/core/adaptive/adaptive_representation_selector.ts +1 -7
  28. package/src/core/api/playback_observer.ts +185 -173
  29. package/src/core/api/public_api.ts +15 -6
  30. package/src/core/init/content_time_boundaries_observer.ts +1 -1
  31. package/src/core/init/create_stream_playback_observer.ts +69 -47
  32. package/src/core/init/initial_seek_and_play.ts +3 -3
  33. package/src/core/init/initialize_directfile.ts +1 -1
  34. package/src/core/init/load_on_media_source.ts +1 -1
  35. package/src/core/init/stall_avoider.ts +12 -9
  36. package/src/core/stream/orchestrator/stream_orchestrator.ts +5 -4
  37. package/src/core/stream/period/create_empty_adaptation_stream.ts +1 -1
  38. package/src/core/stream/period/period_stream.ts +33 -14
  39. package/src/core/stream/reload_after_switch.ts +1 -1
  40. package/src/core/stream/representation/append_segment_to_buffer.ts +1 -1
  41. package/src/core/stream/representation/representation_stream.ts +1 -1
  42. 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.2022062700
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.2022062300
1
+ 3.28.0-dev.2022062700
@@ -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;
@@ -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.2022062300";
91
+ _this.version = /* PLAYER_VERSION */ "3.28.0-dev.2022062700";
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";
@@ -1765,8 +1768,10 @@ var Player = /** @class */ (function (_super) {
1765
1768
  this._priv_contentEventsMemory = {};
1766
1769
  // DRM-related clean-up
1767
1770
  var freeUpContentLock = function () {
1768
- log.debug("Unlocking `contentLock`. Next content can begin.");
1769
- _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
+ }
1770
1775
  };
1771
1776
  if (!isNullOrUndefined(this.videoElement)) {
1772
1777
  clearOnStop(this.videoElement).then(function () {
@@ -2290,5 +2295,5 @@ var Player = /** @class */ (function (_super) {
2290
2295
  };
2291
2296
  return Player;
2292
2297
  }(EventEmitter));
2293
- Player.version = /* PLAYER_VERSION */ "3.28.0-dev.2022062300";
2298
+ Player.version = /* PLAYER_VERSION */ "3.28.0-dev.2022062700";
2294
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;
@@ -35,6 +35,6 @@ export interface IStreamPlaybackObserverArguments {
35
35
  * @param {Object} manifest
36
36
  * @param {Object} playbackObserver
37
37
  * @param {Object} args
38
- * @returns {Observable}
38
+ * @returns {Object}
39
39
  */
40
40
  export default function createStreamPlaybackObserver(manifest: Manifest, playbackObserver: PlaybackObserver, { autoPlay, initialPlayPerformed, initialSeekPerformed, speed, startTime }: IStreamPlaybackObserverArguments): IReadOnlyPlaybackObserver<IStreamOrchestratorPlaybackObservation>;