rx-player 3.29.0-dev.2022103101 → 3.29.0-dev.2022111000

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## v3.29.0-dev.2022103100 (2022-10-31)
3
+ ## v3.29.0 (XXXX-XX-XX)
4
4
 
5
5
  ### Features
6
6
 
@@ -22,7 +22,7 @@
22
22
  - Compat/DRM: Fix infinite loading on WebOS (LG TVs) 2021 and 2022 when loading more than once an encrypted content by resetting decryption capabilities each time [#1175]
23
23
  - Compat: To work around an issue on WebOS (LG TVs), also specify a request timeout manually through a `setTimeout` call when XMLHttpRequests are created for Manifest and segment requests [#1152]
24
24
  - Compat/Directfile: Fix an issue on Tizen (Samsung TVs) where playing directfile contents could randomly lead to not having audio [#1170]
25
- - Compat: Fix issue with Tizen (Samsung TVs) where starting playback on a discontinuity could lead to infinite rebuffering [#1140]
25
+ - Compat: Fix issue with Tizen (Samsung TVs) where starting playback on a discontinuity could lead to infinite rebuffering [#1140, #1176]
26
26
  - Compat/Directfile: For `"directfile"` contents, also consider `AudioTrack` with a `description` (without an "s") as audio-description audio tracks to work-around what seems to be a Safari typo [#1160]
27
27
  - DRM: When using persistent licenses, create new MediaKeySession when `load` resolves with `false`, instead of relying the same, to fix issues with such persistent sessions if the browser cleaned it up [#1139]
28
28
  - Only call "MediaSource.endOfStream" once, the most visible side-effect should have been repeated logs [#1163]
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.29.0-dev.2022103101
1
+ 3.29.0-dev.2022111000
@@ -37,9 +37,9 @@ var isTizen = !isNode &&
37
37
  var isWebOs = !isNode &&
38
38
  /Web0S/.test(navigator.userAgent);
39
39
  var isWebOs2021 = !isNode &&
40
- /WebOS.TV-2021/.test(navigator.userAgent);
40
+ /(W|w)eb(O|0)S.TV-2021/.test(navigator.userAgent);
41
41
  var isWebOs2022 = !isNode &&
42
- /WebOS.TV-2022/.test(navigator.userAgent);
42
+ /(W|w)eb(O|0)S.TV-2022/.test(navigator.userAgent);
43
43
  /** `true` on Safari on a PC platform (i.e. not iPhone / iPad etc.) */
44
44
  var isSafariDesktop = !isNode && (Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor") >= 0 ||
45
45
  ((_b = (_a = window.safari) === null || _a === void 0 ? void 0 : _a.pushNotification) === null || _b === void 0 ? void 0 : _b.toString()) ===
@@ -116,6 +116,11 @@ export default class PlaybackObserver {
116
116
  * @param {number} time
117
117
  */
118
118
  setCurrentTime(time: number): void;
119
+ /**
120
+ * Update the playback rate of the `HTMLMediaElement`.
121
+ * @param {number} playbackRate
122
+ */
123
+ setPlaybackRate(playbackRate: number): void;
119
124
  /**
120
125
  * Returns the current `readyState` advertised by the `HTMLMediaElement`.
121
126
  * @returns {number}
@@ -117,6 +117,13 @@ var PlaybackObserver = /** @class */ (function () {
117
117
  this._internalSeeksIncoming.push(time);
118
118
  this._mediaElement.currentTime = time;
119
119
  };
120
+ /**
121
+ * Update the playback rate of the `HTMLMediaElement`.
122
+ * @param {number} playbackRate
123
+ */
124
+ PlaybackObserver.prototype.setPlaybackRate = function (playbackRate) {
125
+ this._mediaElement.playbackRate = playbackRate;
126
+ };
120
127
  /**
121
128
  * Returns the current `readyState` advertised by the `HTMLMediaElement`.
122
129
  * @returns {number}
@@ -87,7 +87,7 @@ var Player = /** @class */ (function (_super) {
87
87
  // Workaround to support Firefox autoplay on FF 42.
88
88
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
89
89
  videoElement.preload = "auto";
90
- _this.version = /* PLAYER_VERSION */ "3.29.0-dev.2022103101";
90
+ _this.version = /* PLAYER_VERSION */ "3.29.0-dev.2022111000";
91
91
  _this.log = log;
92
92
  _this.state = "STOPPED";
93
93
  _this.videoElement = videoElement;
@@ -2312,5 +2312,5 @@ var Player = /** @class */ (function (_super) {
2312
2312
  };
2313
2313
  return Player;
2314
2314
  }(EventEmitter));
2315
- Player.version = /* PLAYER_VERSION */ "3.29.0-dev.2022103101";
2315
+ Player.version = /* PLAYER_VERSION */ "3.29.0-dev.2022111000";
2316
2316
  export default Player;
@@ -24,9 +24,8 @@ import deferSubscriptions from "../../utils/defer_subscriptions";
24
24
  import emitLoadedEvent from "./emit_loaded_event";
25
25
  import initialSeekAndPlay from "./initial_seek_and_play";
26
26
  import linkDrmAndContent from "./link_drm_and_content";
27
- import StallAvoider from "./stall_avoider";
27
+ import RebufferingController from "./rebuffering_controller";
28
28
  import throwOnMediaError from "./throw_on_media_error";
29
- import updatePlaybackRate from "./update_playback_rate";
30
29
  // NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
31
30
  // first type parameter as `any` instead of the perfectly fine `unknown`,
32
31
  // leading to linter issues, as it forbids the usage of `any`.
@@ -100,15 +99,11 @@ export default function initializeDirectfileContent(_a) {
100
99
  // through a throwing Observable.
101
100
  var mediaError$ = throwOnMediaError(mediaElement);
102
101
  var observation$ = playbackObserver.getReference().asObservable();
103
- // Set the speed set by the user on the media element while pausing a
104
- // little longer while the buffer is empty.
105
- var playbackRate$ = updatePlaybackRate(mediaElement, speed, observation$)
106
- .pipe(ignoreElements());
107
102
  /**
108
103
  * Observable trying to avoid various stalling situations, emitting "stalled"
109
104
  * events when it cannot, as well as "unstalled" events when it get out of one.
110
105
  */
111
- var stallAvoider$ = StallAvoider(playbackObserver, null, speed, EMPTY, EMPTY);
106
+ var rebuffer$ = RebufferingController(playbackObserver, null, speed, EMPTY, EMPTY);
112
107
  /**
113
108
  * Emit a "loaded" events once the initial play has been performed and the
114
109
  * media can begin playback.
@@ -121,5 +116,5 @@ export default function initializeDirectfileContent(_a) {
121
116
  }
122
117
  return emitLoadedEvent(observation$, mediaElement, null, true);
123
118
  }));
124
- return observableMerge(loadingEvts$, drmEvents$.pipe(ignoreElements()), mediaError$, playbackRate$, stallAvoider$);
119
+ return observableMerge(loadingEvts$, drmEvents$.pipe(ignoreElements()), mediaError$, rebuffer$);
125
120
  }
@@ -25,9 +25,8 @@ import emitLoadedEvent from "./emit_loaded_event";
25
25
  import { maintainEndOfStream } from "./end_of_stream";
26
26
  import initialSeekAndPlay from "./initial_seek_and_play";
27
27
  import MediaDurationUpdater from "./media_duration_updater";
28
- import StallAvoider from "./stall_avoider";
28
+ import RebufferingController from "./rebuffering_controller";
29
29
  import streamEventsEmitter from "./stream_events_emitter";
30
- import updatePlaybackRate from "./update_playback_rate";
31
30
  /**
32
31
  * Returns a function allowing to load or reload the content in arguments into
33
32
  * a single or multiple MediaSources.
@@ -101,18 +100,11 @@ export default function createMediaSourceLoader(_a) {
101
100
  return observableOf(evt);
102
101
  }
103
102
  }));
104
- /**
105
- * On subscription, keep the playback speed synchronized to the speed set by
106
- * the user on the media element and force a speed of `0` when the buffer is
107
- * empty, so it can build back buffer.
108
- */
109
- var playbackRate$ = updatePlaybackRate(mediaElement, speed, observation$)
110
- .pipe(ignoreElements());
111
103
  /**
112
104
  * Observable trying to avoid various stalling situations, emitting "stalled"
113
105
  * events when it cannot, as well as "unstalled" events when it get out of one.
114
106
  */
115
- var stallAvoider$ = StallAvoider(playbackObserver, manifest, speed, lockedStream$, discontinuityUpdate$);
107
+ var rebuffer$ = RebufferingController(playbackObserver, manifest, speed, lockedStream$, discontinuityUpdate$);
116
108
  /**
117
109
  * Emit a "loaded" events once the initial play has been performed and the
118
110
  * media can begin playback.
@@ -123,7 +115,7 @@ export default function createMediaSourceLoader(_a) {
123
115
  observableOf(evt) :
124
116
  emitLoadedEvent(observation$, mediaElement, segmentBuffersStore, false);
125
117
  }));
126
- return observableMerge(loadingEvts$, playbackRate$, stallAvoider$, streams$, contentTimeObserver, streamEvents$).pipe(finalize(function () {
118
+ return observableMerge(loadingEvts$, rebuffer$, streams$, contentTimeObserver, streamEvents$).pipe(finalize(function () {
127
119
  mediaDurationUpdater.stop();
128
120
  // clean-up every created SegmentBuffers
129
121
  segmentBuffersStore.disposeAll();
@@ -63,7 +63,10 @@ export interface IDiscontinuityTimeInfo {
63
63
  end: number | null;
64
64
  }
65
65
  /**
66
- * Monitor situations where playback is stalled and try to get out of those.
66
+ * Monitor playback, trying to avoid stalling situation.
67
+ * If stopping the player to build buffer is needed, temporarily set the
68
+ * playback rate (i.e. speed) at `0` until enough buffer is available again.
69
+ *
67
70
  * Emit "stalled" then "unstalled" respectively when an unavoidable stall is
68
71
  * encountered and exited.
69
72
  * @param {object} playbackObserver - emit the current playback conditions.
@@ -75,4 +78,4 @@ export interface IDiscontinuityTimeInfo {
75
78
  * discontinuities for loaded Period and buffer types.
76
79
  * @returns {Observable}
77
80
  */
78
- export default function StallAvoider(playbackObserver: PlaybackObserver, manifest: Manifest | null, speed: IReadOnlySharedReference<number>, lockedStream$: Observable<ILockedStreamEvent>, discontinuityUpdate$: Observable<IDiscontinuityEvent>): Observable<IStalledEvent | IUnstalledEvent | IWarningEvent>;
81
+ export default function RebufferingController(playbackObserver: PlaybackObserver, manifest: Manifest | null, speed: IReadOnlySharedReference<number>, lockedStream$: Observable<ILockedStreamEvent>, discontinuityUpdate$: Observable<IDiscontinuityEvent>): Observable<IStalledEvent | IUnstalledEvent | IWarningEvent>;
@@ -13,12 +13,13 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { ignoreElements, map, merge as observableMerge, scan, tap, withLatestFrom, } from "rxjs";
16
+ import { finalize, ignoreElements, map, merge as observableMerge, scan, tap, withLatestFrom, } from "rxjs";
17
17
  import isSeekingApproximate from "../../compat/is_seeking_approximate";
18
18
  import config from "../../config";
19
19
  import { MediaError } from "../../errors";
20
20
  import log from "../../log";
21
21
  import { getNextRangeGap } from "../../utils/ranges";
22
+ import TaskCanceller from "../../utils/task_canceller";
22
23
  import EVENTS from "../stream/events_generators";
23
24
  /**
24
25
  * Work-around rounding errors with floating points by setting an acceptable,
@@ -26,7 +27,10 @@ import EVENTS from "../stream/events_generators";
26
27
  */
27
28
  var EPSILON = 1 / 60;
28
29
  /**
29
- * Monitor situations where playback is stalled and try to get out of those.
30
+ * Monitor playback, trying to avoid stalling situation.
31
+ * If stopping the player to build buffer is needed, temporarily set the
32
+ * playback rate (i.e. speed) at `0` until enough buffer is available again.
33
+ *
30
34
  * Emit "stalled" then "unstalled" respectively when an unavoidable stall is
31
35
  * encountered and exited.
32
36
  * @param {object} playbackObserver - emit the current playback conditions.
@@ -38,7 +42,7 @@ var EPSILON = 1 / 60;
38
42
  * discontinuities for loaded Period and buffer types.
39
43
  * @returns {Observable}
40
44
  */
41
- export default function StallAvoider(playbackObserver, manifest, speed, lockedStream$, discontinuityUpdate$) {
45
+ export default function RebufferingController(playbackObserver, manifest, speed, lockedStream$, discontinuityUpdate$) {
42
46
  var initialDiscontinuitiesStore = [];
43
47
  /**
44
48
  * Emit every known audio and video buffer discontinuities in chronological
@@ -103,6 +107,7 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
103
107
  // This is why we're disabling the eslint rule.
104
108
  /* eslint-disable-next-line @typescript-eslint/no-unsafe-argument */
105
109
  ignoreElements());
110
+ var playbackRateUpdater = new PlaybackRateUpdater(playbackObserver, speed);
106
111
  var stall$ = playbackObserver.getReference().asObservable().pipe(withLatestFrom(discontinuitiesStore$), map(function (_a) {
107
112
  var _b;
108
113
  var observation = _a[0], discontinuitiesStore = _a[1];
@@ -131,6 +136,12 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
131
136
  prevFreezingState = { attemptTimestamp: now };
132
137
  }
133
138
  if (now - freezing.timestamp > FREEZING_STALLED_DELAY) {
139
+ if (rebuffering === null || ignoredStallTimeStamp !== null) {
140
+ playbackRateUpdater.stopRebuffering();
141
+ }
142
+ else {
143
+ playbackRateUpdater.startRebuffering();
144
+ }
134
145
  return { type: "stalled",
135
146
  value: "freezing" };
136
147
  }
@@ -139,6 +150,7 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
139
150
  prevFreezingState = null;
140
151
  }
141
152
  if (rebuffering === null) {
153
+ playbackRateUpdater.stopRebuffering();
142
154
  if (readyState === 1) {
143
155
  // With a readyState set to 1, we should still not be able to play:
144
156
  // Return that we're stalled
@@ -165,6 +177,7 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
165
177
  if (ignoredStallTimeStamp !== null) {
166
178
  var now = performance.now();
167
179
  if (now - ignoredStallTimeStamp < FORCE_DISCONTINUITY_SEEK_DELAY) {
180
+ playbackRateUpdater.stopRebuffering();
168
181
  log.debug("Init: letting the device get out of a stall by itself");
169
182
  return { type: "stalled",
170
183
  value: stalledReason };
@@ -174,6 +187,7 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
174
187
  }
175
188
  }
176
189
  ignoredStallTimeStamp = null;
190
+ playbackRateUpdater.startRebuffering();
177
191
  if (manifest === null) {
178
192
  return { type: "stalled",
179
193
  value: stalledReason };
@@ -231,7 +245,10 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
231
245
  return { type: "stalled",
232
246
  value: stalledReason };
233
247
  }));
234
- return observableMerge(unlock$, stall$);
248
+ return observableMerge(unlock$, stall$)
249
+ .pipe(finalize(function () {
250
+ playbackRateUpdater.dispose();
251
+ }));
235
252
  }
236
253
  /**
237
254
  * @param {Array.<Object>} discontinuitiesStore
@@ -348,3 +365,75 @@ function generateDiscontinuityError(stalledPosition, seekTo) {
348
365
  String(stalledPosition) + ", seeked at position " +
349
366
  String(seekTo));
350
367
  }
368
+ /**
369
+ * Manage playback speed, allowing to force a playback rate of `0` when
370
+ * rebuffering is wanted.
371
+ *
372
+ * Only one `PlaybackRateUpdater` should be created per HTMLMediaElement.
373
+ * Note that the `PlaybackRateUpdater` reacts to playback event and wanted
374
+ * speed change. You should call its `dispose` method once you don't need it
375
+ * anymore.
376
+ * @class PlaybackRateUpdater
377
+ */
378
+ var PlaybackRateUpdater = /** @class */ (function () {
379
+ /**
380
+ * Create a new `PlaybackRateUpdater`.
381
+ * @param {Object} playbackObserver
382
+ * @param {Object} speed
383
+ */
384
+ function PlaybackRateUpdater(playbackObserver, speed) {
385
+ this._speedUpdateCanceller = new TaskCanceller();
386
+ this._isRebuffering = false;
387
+ this._playbackObserver = playbackObserver;
388
+ this._isDisposed = false;
389
+ this._speed = speed;
390
+ this._updateSpeed();
391
+ }
392
+ /**
393
+ * Force the playback rate to `0`, to start a rebuffering phase.
394
+ *
395
+ * You can call `stopRebuffering` when you want the rebuffering phase to end.
396
+ */
397
+ PlaybackRateUpdater.prototype.startRebuffering = function () {
398
+ if (this._isRebuffering || this._isDisposed) {
399
+ return;
400
+ }
401
+ this._isRebuffering = true;
402
+ this._speedUpdateCanceller.cancel();
403
+ log.info("Init: Pause playback to build buffer");
404
+ this._playbackObserver.setPlaybackRate(0);
405
+ };
406
+ /**
407
+ * If in a rebuffering phase (during which the playback rate is forced to
408
+ * `0`), exit that phase to apply the wanted playback rate instead.
409
+ *
410
+ * Do nothing if not in a rebuffering phase.
411
+ */
412
+ PlaybackRateUpdater.prototype.stopRebuffering = function () {
413
+ if (!this._isRebuffering || this._isDisposed) {
414
+ return;
415
+ }
416
+ this._isRebuffering = false;
417
+ this._speedUpdateCanceller = new TaskCanceller();
418
+ this._updateSpeed();
419
+ };
420
+ /**
421
+ * The `PlaybackRateUpdater` allocate resources to for example listen to
422
+ * wanted speed changes and react to it.
423
+ *
424
+ * Consequently, you should call the `dispose` method, when you don't want the
425
+ * `PlaybackRateUpdater` to have an effect anymore.
426
+ */
427
+ PlaybackRateUpdater.prototype.dispose = function () {
428
+ this._speedUpdateCanceller.cancel();
429
+ this._isDisposed = true;
430
+ };
431
+ PlaybackRateUpdater.prototype._updateSpeed = function () {
432
+ var _this = this;
433
+ this._speed.onUpdate(function (lastSpeed) {
434
+ log.info("Init: Resume playback speed", lastSpeed);
435
+ _this._playbackObserver.setPlaybackRate(lastSpeed);
436
+ }, { clearSignal: this._speedUpdateCanceller.signal, emitCurrentValue: true });
437
+ };
438
+ return PlaybackRateUpdater;
439
+ }());
@@ -1,32 +0,0 @@
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
- import { Observable } from "rxjs";
17
- import { IReadOnlySharedReference } from "../../utils/reference";
18
- import { IPlaybackObservation } from "../api";
19
- export interface IPlaybackRateOptions {
20
- pauseWhenRebuffering?: boolean;
21
- }
22
- /**
23
- * Manage playback speed.
24
- * Set playback rate set by the user, pause playback when the player appear to
25
- * rebuffering and restore the speed once it appears to exit rebuffering status.
26
- *
27
- * @param {HTMLMediaElement} mediaElement
28
- * @param {Observable} speed - last speed set by the user
29
- * @param {Observable} observation$ - Current playback conditions
30
- * @returns {Observable}
31
- */
32
- export default function updatePlaybackRate(mediaElement: HTMLMediaElement, speed: IReadOnlySharedReference<number>, observation$: Observable<IPlaybackObservation>): Observable<number>;
@@ -1,46 +1 @@
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
- import { defer as observableDefer, distinctUntilChanged, map, of as observableOf, startWith, switchMap, tap, } from "rxjs";
17
- import log from "../../log";
18
- /**
19
- * Manage playback speed.
20
- * Set playback rate set by the user, pause playback when the player appear to
21
- * rebuffering and restore the speed once it appears to exit rebuffering status.
22
- *
23
- * @param {HTMLMediaElement} mediaElement
24
- * @param {Observable} speed - last speed set by the user
25
- * @param {Observable} observation$ - Current playback conditions
26
- * @returns {Observable}
27
- */
28
- export default function updatePlaybackRate(mediaElement, speed, observation$) {
29
- var forcePause$ = observation$
30
- .pipe(map(function (observation) { return observation.rebuffering !== null; }), startWith(false), distinctUntilChanged());
31
- return forcePause$
32
- .pipe(switchMap(function (shouldForcePause) {
33
- if (shouldForcePause) {
34
- return observableDefer(function () {
35
- log.info("Init: Pause playback to build buffer");
36
- mediaElement.playbackRate = 0;
37
- return observableOf(0);
38
- });
39
- }
40
- return speed.asObservable()
41
- .pipe(tap(function (lastSpeed) {
42
- log.info("Init: Resume playback speed", lastSpeed);
43
- mediaElement.playbackRate = lastSpeed;
44
- }));
45
- }));
46
- }
1
+ "use strict";
@@ -312,7 +312,7 @@ var DEFAULT_CONFIG = {
312
312
  * small enough so this (arguably rare) situation won't lead to too much
313
313
  * waiting time.
314
314
  */
315
- FORCE_DISCONTINUITY_SEEK_DELAY: 3000,
315
+ FORCE_DISCONTINUITY_SEEK_DELAY: 5000,
316
316
  /**
317
317
  * Ratio used to know if an already loaded segment should be re-buffered.
318
318
  * We re-load the given segment if the current one times that ratio is