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
@@ -14,13 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- combineLatest as observableCombineLatest,
19
- map,
20
- Observable,
21
- } from "rxjs";
22
17
  import Manifest from "../../manifest";
23
- import { IReadOnlySharedReference } from "../../utils/reference";
18
+ import createSharedReference, {
19
+ IReadOnlySharedReference,
20
+ } from "../../utils/reference";
21
+ import { CancellationSignal } from "../../utils/task_canceller";
24
22
  import {
25
23
  IPlaybackObservation,
26
24
  IReadOnlyPlaybackObserver,
@@ -47,7 +45,7 @@ export interface IStreamPlaybackObserverArguments {
47
45
  * @param {Object} manifest
48
46
  * @param {Object} playbackObserver
49
47
  * @param {Object} args
50
- * @returns {Observable}
48
+ * @returns {Object}
51
49
  */
52
50
  export default function createStreamPlaybackObserver(
53
51
  manifest : Manifest,
@@ -58,47 +56,71 @@ export default function createStreamPlaybackObserver(
58
56
  speed,
59
57
  startTime } : IStreamPlaybackObserverArguments
60
58
  ) : IReadOnlyPlaybackObserver<IStreamOrchestratorPlaybackObservation> {
61
- return playbackObserver.deriveReadOnlyObserver(function mapObservable(
62
- observation$ : Observable<IPlaybackObservation>
63
- ) : Observable<IStreamOrchestratorPlaybackObservation> {
64
- return observableCombineLatest([observation$, speed.asObservable()]).pipe(
65
- map(([observation, lastSpeed]) => {
66
- let pendingPosition : number | undefined;
67
- if (!initialSeekPerformed.getValue()) {
68
- pendingPosition = startTime;
69
- } else if (!manifest.isDynamic || manifest.isLastPeriodKnown) {
70
- // HACK: When the position is actually further than the maximum
71
- // position for a finished content, we actually want to be loading
72
- // the last segment before ending.
73
- // For now, this behavior is implicitely forced by making as if we
74
- // want to seek one second before the period's end (despite never
75
- // doing it).
76
- const lastPeriod = manifest.periods[manifest.periods.length - 1];
77
- if (lastPeriod !== undefined &&
78
- lastPeriod.end !== undefined &&
79
- observation.position > lastPeriod.end)
80
- {
81
- pendingPosition = lastPeriod.end - 1;
82
- }
59
+ return playbackObserver.deriveReadOnlyObserver(function transform(
60
+ observationRef : IReadOnlySharedReference<IPlaybackObservation>,
61
+ cancellationSignal : CancellationSignal
62
+ ) : IReadOnlySharedReference<IStreamOrchestratorPlaybackObservation> {
63
+ const newRef = createSharedReference(constructStreamPlaybackObservation());
64
+
65
+ speed.onUpdate(emitStreamPlaybackObservation, {
66
+ clearSignal: cancellationSignal,
67
+ emitCurrentValue: false,
68
+ });
69
+
70
+ observationRef.onUpdate(emitStreamPlaybackObservation, {
71
+ clearSignal: cancellationSignal,
72
+ emitCurrentValue: false,
73
+ });
74
+
75
+ cancellationSignal.register(() => {
76
+ newRef.finish();
77
+ });
78
+
79
+ return newRef;
80
+
81
+ function constructStreamPlaybackObservation() {
82
+ const observation = observationRef.getValue();
83
+ const lastSpeed = speed.getValue();
84
+ let pendingPosition : number | undefined;
85
+ if (!initialSeekPerformed.getValue()) {
86
+ pendingPosition = startTime;
87
+ } else if (!manifest.isDynamic || manifest.isLastPeriodKnown) {
88
+ // HACK: When the position is actually further than the maximum
89
+ // position for a finished content, we actually want to be loading
90
+ // the last segment before ending.
91
+ // For now, this behavior is implicitely forced by making as if we
92
+ // want to seek one second before the period's end (despite never
93
+ // doing it).
94
+ const lastPeriod = manifest.periods[manifest.periods.length - 1];
95
+ if (lastPeriod !== undefined &&
96
+ lastPeriod.end !== undefined &&
97
+ observation.position > lastPeriod.end)
98
+ {
99
+ pendingPosition = lastPeriod.end - 1;
83
100
  }
101
+ }
102
+
103
+ return {
104
+ // TODO more exact according to the current Adaptation chosen?
105
+ maximumPosition: manifest.getMaximumSafePosition(),
106
+ position: {
107
+ last: observation.position,
108
+ pending: pendingPosition,
109
+ },
110
+ duration: observation.duration,
111
+ paused: {
112
+ last: observation.paused,
113
+ pending: initialPlayPerformed.getValue() ? undefined :
114
+ !autoPlay === observation.paused ? undefined :
115
+ !autoPlay,
116
+ },
117
+ readyState: observation.readyState,
118
+ speed: lastSpeed,
119
+ };
120
+ }
84
121
 
85
- return {
86
- // TODO more exact according to the current Adaptation chosen?
87
- maximumPosition: manifest.getMaximumSafePosition(),
88
- position: {
89
- last: observation.position,
90
- pending: pendingPosition,
91
- },
92
- duration: observation.duration,
93
- paused: {
94
- last: observation.paused,
95
- pending: initialPlayPerformed.getValue() ? undefined :
96
- !autoPlay === observation.paused ? undefined :
97
- !autoPlay,
98
- },
99
- readyState: observation.readyState,
100
- speed: lastSpeed,
101
- };
102
- }));
122
+ function emitStreamPlaybackObservation() {
123
+ newRef.setValue(constructStreamPlaybackObservation());
124
+ }
103
125
  });
104
126
  }
@@ -83,7 +83,7 @@ export function waitUntilPlayable(
83
83
 
84
84
  /**
85
85
  * Try to play content then handle autoplay errors.
86
- * @param {HTMLMediaElement} - mediaElement
86
+ * @param {HTMLMediaElement} mediaElement
87
87
  * @returns {Observable}
88
88
  */
89
89
  function autoPlay(
@@ -161,12 +161,12 @@ export default function initialSeekAndPlay(
161
161
  const seekAndPlay$ = seek$.pipe(
162
162
  mergeMap(() : Observable<IWarningEvent | undefined> => {
163
163
  if (!shouldValidateMetadata() || mediaElement.duration > 0) {
164
- return waitUntilPlayable(playbackObserver.observe(true));
164
+ return waitUntilPlayable(playbackObserver.getReference().asObservable());
165
165
  } else {
166
166
  const error = new MediaError("MEDIA_ERR_NOT_LOADED_METADATA",
167
167
  "Cannot load automatically: your browser " +
168
168
  "falsely announced having loaded the content.");
169
- return waitUntilPlayable(playbackObserver.observe(true))
169
+ return waitUntilPlayable(playbackObserver.getReference().asObservable())
170
170
  .pipe(startWith(EVENTS.warning(error)));
171
171
  }
172
172
  }),
@@ -158,7 +158,7 @@ export default function initializeDirectfileContent({
158
158
  // through a throwing Observable.
159
159
  const mediaError$ = throwOnMediaError(mediaElement);
160
160
 
161
- const observation$ = playbackObserver.observe(true);
161
+ const observation$ = playbackObserver.getReference().asObservable();
162
162
 
163
163
  // Set the speed set by the user on the media element while pausing a
164
164
  // little longer while the buffer is empty.
@@ -125,7 +125,7 @@ export default function createMediaSourceLoader(
125
125
  startTime: initialTime,
126
126
  mustAutoPlay: autoPlay });
127
127
 
128
- const observation$ = playbackObserver.observe(true);
128
+ const observation$ = playbackObserver.getReference().asObservable();
129
129
  const streamEvents$ = initialPlayPerformed.asObservable().pipe(
130
130
  filter((hasPlayed) => hasPlayed),
131
131
  mergeMap(() => streamEventsEmitter(manifest, mediaElement, observation$)));
@@ -139,7 +139,7 @@ export default function StallAvoider(
139
139
  * order (first ordered by Period's start, then by bufferType in any order.
140
140
  */
141
141
  const discontinuitiesStore$ = discontinuityUpdate$.pipe(
142
- withLatestFrom(playbackObserver.observe(true)),
142
+ withLatestFrom(playbackObserver.getReference().asObservable()),
143
143
  scan(
144
144
  (discontinuitiesStore, [evt, observation]) =>
145
145
  updateDiscontinuitiesStore(discontinuitiesStore, evt, observation),
@@ -179,7 +179,7 @@ export default function StallAvoider(
179
179
  * Period handled by that stream to unlock the situation.
180
180
  */
181
181
  const unlock$ = lockedStream$.pipe(
182
- withLatestFrom(playbackObserver.observe(true)),
182
+ withLatestFrom(playbackObserver.getReference().asObservable()),
183
183
  tap(([lockedStreamEvt, observation]) => {
184
184
  // TODO(PaulB) also skip when the user's wanted speed is set to `0`, as we
185
185
  // might not want to seek in that case?
@@ -211,7 +211,7 @@ export default function StallAvoider(
211
211
  ignoreElements()
212
212
  );
213
213
 
214
- const stall$ = playbackObserver.observe(true).pipe(
214
+ const stall$ = playbackObserver.getReference().asObservable().pipe(
215
215
  withLatestFrom(discontinuitiesStore$),
216
216
  map(([observation, discontinuitiesStore]) => {
217
217
  const { buffered,
@@ -279,12 +279,15 @@ export default function StallAvoider(
279
279
  if (ignoredStallTimeStamp === null) {
280
280
  ignoredStallTimeStamp = now;
281
281
  }
282
- if (isSeekingApproximate &&
283
- observation.position < lastSeekingPosition &&
284
- now - ignoredStallTimeStamp < FORCE_DISCONTINUITY_SEEK_DELAY)
285
- {
286
- return { type: "stalled" as const,
287
- value: stalledReason };
282
+ if (isSeekingApproximate && observation.position < lastSeekingPosition) {
283
+ log.debug("Init: the device appeared to have seeked back by itself.");
284
+ if (now - ignoredStallTimeStamp < FORCE_DISCONTINUITY_SEEK_DELAY) {
285
+ return { type: "stalled" as const,
286
+ value: stalledReason };
287
+ } else {
288
+ log.warn("Init: ignored stall for too long, checking discontinuity",
289
+ now - ignoredStallTimeStamp);
290
+ }
288
291
  }
289
292
  lastSeekingPosition = null;
290
293
  }
@@ -142,7 +142,7 @@ export default function StreamOrchestrator(
142
142
  Infinity;
143
143
  return BufferGarbageCollector({
144
144
  segmentBuffer,
145
- currentTime$: playbackObserver.observe(true)
145
+ currentTime$: playbackObserver.getReference().asObservable()
146
146
  .pipe(map(o => o.position.pending ?? o.position.last)),
147
147
  maxBufferBehind$: maxBufferBehind.asObservable().pipe(
148
148
  map(val => Math.min(val, defaultMaxBehind))),
@@ -264,7 +264,8 @@ export default function StreamOrchestrator(
264
264
 
265
265
  // Restart the current Stream when the wanted time is in another period
266
266
  // than the ones already considered
267
- const restartStreamsWhenOutOfBounds$ = playbackObserver.observe(true).pipe(
267
+ const observation$ = playbackObserver.getReference().asObservable();
268
+ const restartStreamsWhenOutOfBounds$ = observation$.pipe(
268
269
  filterMap<
269
270
  IStreamOrchestratorPlaybackObservation,
270
271
  Period,
@@ -329,7 +330,7 @@ export default function StreamOrchestrator(
329
330
  // to reduce the risk of race conditions where the next observation
330
331
  // was going to be emitted synchronously.
331
332
  nextTickObs().pipe(ignoreElements()),
332
- playbackObserver.observe(true).pipe(
333
+ playbackObserver.getReference().asObservable().pipe(
333
334
  take(1),
334
335
  mergeMap((observation) => {
335
336
  const shouldAutoPlay = !(observation.paused.pending ??
@@ -399,7 +400,7 @@ export default function StreamOrchestrator(
399
400
  const destroyNextStreams$ = new Subject<void>();
400
401
 
401
402
  // Emits when the current position goes over the end of the current Stream.
402
- const endOfCurrentStream$ = playbackObserver.observe(true)
403
+ const endOfCurrentStream$ = playbackObserver.getReference().asObservable()
403
404
  .pipe(filter(({ position }) =>
404
405
  basePeriod.end != null &&
405
406
  (position.pending ?? position.last) >= basePeriod.end));
@@ -48,7 +48,7 @@ export default function createEmptyAdaptationStream(
48
48
  const { period } = content;
49
49
  let hasFinishedLoading = false;
50
50
  const wantedBufferAhead$ = wantedBufferAhead.asObservable();
51
- const observation$ = playbackObserver.observe(true);
51
+ const observation$ = playbackObserver.getReference().asObservable();
52
52
  return observableCombineLatest([observation$,
53
53
  wantedBufferAhead$]).pipe(
54
54
  mergeMap(([observation, wba]) => {
@@ -42,7 +42,10 @@ import Manifest, {
42
42
  import { IAudioTrackSwitchingMode } from "../../../public_types";
43
43
  import objectAssign from "../../../utils/object_assign";
44
44
  import { getLeftSizeOfRange } from "../../../utils/ranges";
45
- import { IReadOnlySharedReference } from "../../../utils/reference";
45
+ import createSharedReference, {
46
+ IReadOnlySharedReference,
47
+ } from "../../../utils/reference";
48
+ import { CancellationSignal } from "../../../utils/task_canceller";
46
49
  import WeakMapMemory from "../../../utils/weak_map_memory";
47
50
  import { IRepresentationEstimator } from "../../adaptive";
48
51
  import { IReadOnlyPlaybackObserver } from "../../api";
@@ -357,18 +360,34 @@ function createAdaptationStreamPlaybackObserver(
357
360
  initialPlaybackObserver : IReadOnlyPlaybackObserver<IPeriodStreamPlaybackObservation>,
358
361
  segmentBuffer : SegmentBuffer
359
362
  ) : IReadOnlyPlaybackObserver<IAdaptationStreamPlaybackObservation> {
360
- return initialPlaybackObserver.deriveReadOnlyObserver(
361
- (observation$) => observation$.pipe(map(mapObservation)),
362
- mapObservation
363
- );
363
+ return initialPlaybackObserver.deriveReadOnlyObserver(function transform(
364
+ observationRef : IReadOnlySharedReference<IPeriodStreamPlaybackObservation>,
365
+ cancellationSignal : CancellationSignal
366
+ ) : IReadOnlySharedReference<IAdaptationStreamPlaybackObservation> {
367
+ const newRef = createSharedReference(constructAdaptationStreamPlaybackObservation());
368
+
369
+ observationRef.onUpdate(emitAdaptationStreamPlaybackObservation, {
370
+ clearSignal: cancellationSignal,
371
+ emitCurrentValue: false,
372
+ });
373
+
374
+ cancellationSignal.register(() => {
375
+ newRef.finish();
376
+ });
377
+
378
+ return newRef;
379
+
380
+ function constructAdaptationStreamPlaybackObservation(
381
+ ) : IAdaptationStreamPlaybackObservation {
382
+ const baseObservation = observationRef.getValue();
383
+ const buffered = segmentBuffer.getBufferedRanges();
384
+ const bufferGap = getLeftSizeOfRange(buffered, baseObservation.position.last);
385
+ return objectAssign({}, baseObservation, { bufferGap });
386
+ }
387
+
388
+ function emitAdaptationStreamPlaybackObservation() {
389
+ newRef.setValue(constructAdaptationStreamPlaybackObservation());
390
+ }
391
+ });
364
392
 
365
- function mapObservation(
366
- baseObservation : IPeriodStreamPlaybackObservation
367
- ) : IAdaptationStreamPlaybackObservation {
368
- const buffered = segmentBuffer.getBufferedRanges();
369
- return objectAssign({},
370
- baseObservation,
371
- { bufferGap: getLeftSizeOfRange(buffered,
372
- baseObservation.position.last) });
373
- }
374
393
  }
@@ -62,7 +62,7 @@ export default function reloadAfterSwitch(
62
62
  // It can happen when `reloadAfterSwitch` is called as a side-effect of the
63
63
  // same event that triggers the playback observation to be emitted.
64
64
  return nextTickObs().pipe(
65
- mergeMap(() => playbackObserver.observe(true)),
65
+ mergeMap(() => playbackObserver.getReference().asObservable()),
66
66
  map((observation) => {
67
67
  const currentTime = playbackObserver.getCurrentTime();
68
68
  const pos = currentTime + deltaPos;
@@ -61,7 +61,7 @@ export default function appendSegmentToBuffer<T>(
61
61
  throw new MediaError("BUFFER_APPEND_ERROR", reason);
62
62
  }
63
63
 
64
- return playbackObserver.observe(true).pipe(
64
+ return playbackObserver.getReference().asObservable().pipe(
65
65
  take(1),
66
66
  mergeMap((observation) => {
67
67
  const currentPos = observation.position.pending ??
@@ -166,7 +166,7 @@ export default function RepresentationStream<TSegmentDataType>({
166
166
 
167
167
  /** Observable emitting the stream "status" and filling `lastSegmentQueue`. */
168
168
  const status$ = observableCombineLatest([
169
- playbackObserver.observe(true),
169
+ playbackObserver.getReference().asObservable(),
170
170
  bufferGoal$,
171
171
  maxBufferSize$,
172
172
  terminate$.pipe(take(1),
@@ -101,8 +101,8 @@ export interface ISharedReference<T> {
101
101
  onUpdate(
102
102
  cb : (val : T) => void,
103
103
  options? : {
104
- clearSignal?: CancellationSignal;
105
- emitCurrentValue?: boolean;
104
+ clearSignal?: CancellationSignal | undefined;
105
+ emitCurrentValue?: boolean | undefined;
106
106
  },
107
107
  ) : void;
108
108
  /**
@@ -217,10 +217,10 @@ export function createSharedReference<T>(initialValue : T) : ISharedReference<T>
217
217
  setValue(newVal : T) : void {
218
218
  if (isFinished) {
219
219
  if (__ENVIRONMENT__.CURRENT_ENV === __ENVIRONMENT__.DEV as number) {
220
- throw new Error("Finished shared references cannot be updated");
221
- } else {
222
- return;
220
+ /* eslint-disable-next-line no-console */
221
+ console.error("Finished shared references cannot be updated");
223
222
  }
223
+ return;
224
224
  }
225
225
  value = newVal;
226
226
 
@@ -344,10 +344,12 @@ export function createSharedReference<T>(initialValue : T) : ISharedReference<T>
344
344
  if (!cbObj.hasBeenCleared) {
345
345
  cbObj.complete();
346
346
  }
347
+ cbObj.hasBeenCleared = true;
347
348
  } catch (_) {
348
349
  /* nothing */
349
350
  }
350
351
  }
352
+ cbs.length = 0;
351
353
  },
352
354
  };
353
355
  }