Skip to content

Commit d725569

Browse files
committed
Refactor/Rename PlaybackObserver to MediaElementMonitor
**TL;DR: This is a code simplification proposal to set a single interface for all `HTMLMediaElement` interactions, under a class named `MediaElementMonitor`. This is just a merge of the previous `PlaybackObserver` module (which monitors playback) and of previously direct `HTMLMediaElement` access (which would now be avoided in profit of calling `MediaElementMonitor` methods).** --- I worked on multiple R&D subjects recently that needed a re-thinking of how we interact with the media element in the core logic: - The "preload" work (to preload a content in-memory before playback). Here the idea was to make most core RxPlayer modules work optionally "headlessly" during a preloading phase (without an actual media element to play on), and to then be able to "hot swap" the media element at any point if the content needed to be actually played - with the intent of speeding up loading time for future contents. - The "Core Dump" work (a planned API to output an extensive snapshot of the RxPlayer state and playback conditions on playback errors - for debugging, logging and advanced experimental mitigations purposes of platform issues). Here I needed to be able to access many media properties at once and both their current states but also their last pre-error status. On both of those subjects, I found that passing around both the media element and a `PlaybackObserver` everywhere was awkward and unnecessary: the `PlaybackObserver` already contains the media element and has to be used for some side-effects: e.g. seeking has to go through it. So I propose here to just define a single interface to the media element. The most difficult step has been to know what to name that thing. Initially I went with `MediaElementInterface`, but it didn't seem to be really indicative of what 99% of its use is, which is to monitor playback conditions. So in the end I just went with `MediaElementMonitor`. Though it also allows side-effect on the media element (seeking, setting `src`...) Not everything is simplified yet (there are still places when I rely on the two), but it's a good first step.
1 parent 1d19c5e commit d725569

49 files changed

Lines changed: 561 additions & 566 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ For the subdirectories and files in this directory not represented in that schem
182182
- `manifest` (_./manifest_): Defines a `Manifest` structure and its properties, a central
183183
structure of the player describing a content.
184184

185-
- `PlaybackObserver` (./playback_observer): Defines `PlaybackObserver` instances, used by
186-
many modules to obtain playback-related properties (such as the playing position, the
187-
current playback speed etc.).
185+
- `MediaElementMonitor` (./media_element_monitor): Defines the `MediaElementMonitor` ,
186+
used by many modules to poll the `HTMLMediaElement` for playback-related properties
187+
(such as the playing position, the current playback speed etc.).
188188

189189
- `parsers` (_./parsers_): Various parsers for several formats
190190

src/core/adaptive/adaptive_representation_selector.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import type {
2525
} from "../../manifest";
2626
import type {
2727
ObservationPosition,
28-
IReadOnlyPlaybackObserver,
29-
} from "../../playback_observer";
28+
IReadOnlyMediaElementMonitor,
29+
} from "../../media_element_monitor";
3030
import isNullOrUndefined from "../../utils/is_null_or_undefined";
3131
import noop from "../../utils/noop";
3232
import type { IRange } from "../../utils/ranges";
@@ -88,15 +88,15 @@ export default function createAdaptiveRepresentationSelector(
8888
* @param {Object} context
8989
* @param {Object} currentRepresentation
9090
* @param {Object} representations
91-
* @param {Object} playbackObserver
91+
* @param {Object} mediaElementMonitor
9292
* @param {Object} stopAllEstimates
9393
* @returns {Array.<Object>}
9494
*/
9595
return function getEstimates(
9696
context: { manifest: IManifest; period: IPeriod; adaptation: IAdaptation },
9797
currentRepresentation: IReadOnlySharedReference<IRepresentation | null>,
9898
representations: IReadOnlySharedReference<IRepresentation[]>,
99-
playbackObserver: IReadOnlyPlaybackObserver<IRepresentationEstimatorPlaybackObservation>,
99+
mediaElementMonitor: IReadOnlyMediaElementMonitor<IRepresentationEstimatorMediaObservation>,
100100
stopAllEstimates: CancellationSignal,
101101
): IRepresentationEstimatorResponse {
102102
const { type } = context.adaptation;
@@ -113,7 +113,7 @@ export default function createAdaptiveRepresentationSelector(
113113
currentRepresentation,
114114
filters,
115115
initialBitrate,
116-
playbackObserver,
116+
mediaElementMonitor,
117117
representations,
118118
lowLatencyMode,
119119
},
@@ -165,7 +165,7 @@ function getEstimateReference(
165165
filters,
166166
initialBitrate,
167167
lowLatencyMode,
168-
playbackObserver,
168+
mediaElementMonitor,
169169
representations: representationsRef,
170170
}: IRepresentationEstimatorArguments,
171171
stopAllEstimates: CancellationSignal,
@@ -263,25 +263,25 @@ function getEstimateReference(
263263
const guessBasedChooser = new GuessBasedChooser(scoreCalculator, prevEstimate);
264264

265265
// get initial observation for initial estimate
266-
let lastPlaybackObservation = playbackObserver.getReference().getValue();
266+
let lastMediaObservation = mediaElementMonitor.getReference().getValue();
267267

268268
/** Reference through which estimates are emitted. */
269269
const innerEstimateRef = new SharedReference<IABREstimate>(getCurrentEstimate());
270270

271-
// Listen to playback observations
272-
playbackObserver.listen(
271+
// Listen to media observations
272+
mediaElementMonitor.listen(
273273
(obs) => {
274-
lastPlaybackObservation = obs;
274+
lastMediaObservation = obs;
275275
updateEstimate();
276276
},
277277
{ includeLastObservation: false, clearSignal: innerCancellationSignal },
278278
);
279279

280280
onAddedSegment = function (val: IAddedSegmentCallbackPayload) {
281-
if (lastPlaybackObservation === null) {
281+
if (lastMediaObservation === null) {
282282
return;
283283
}
284-
const { position, speed } = lastPlaybackObservation;
284+
const { position, speed } = lastMediaObservation;
285285
const timeRanges = val.buffered;
286286
const bufferGap = getLeftSizeOfRange(timeRanges, position.getWanted());
287287
const { representation } = val.content;
@@ -310,7 +310,7 @@ function getEstimateReference(
310310

311311
/** Returns the actual estimate based on all methods and algorithm available. */
312312
function getCurrentEstimate(): IABREstimate {
313-
const { bufferGap, position, maximumPosition } = lastPlaybackObservation;
313+
const { bufferGap, position, maximumPosition } = lastMediaObservation;
314314
const resolutionLimit = filters.limitResolution.getValue();
315315
const bitrateThrottle = filters.throttleBitrate.getValue();
316316
const currentRepresentationVal = currentRepresentation.getValue();
@@ -322,7 +322,7 @@ function getEstimateReference(
322322
);
323323
const requests = requestsStore.getRequests();
324324
const { bandwidthEstimate, bitrateChosen } = networkAnalyzer.getBandwidthEstimate(
325-
lastPlaybackObservation,
325+
lastMediaObservation,
326326
bandwidthEstimator,
327327
currentRepresentationVal,
328328
requests,
@@ -334,7 +334,7 @@ function getEstimateReference(
334334
stableRepresentation === null
335335
? undefined
336336
: stableRepresentation.bitrate /
337-
(lastPlaybackObservation.speed > 0 ? lastPlaybackObservation.speed : 1);
337+
(lastMediaObservation.speed > 0 ? lastMediaObservation.speed : 1);
338338

339339
const { ABR_ENTER_BUFFER_BASED_ALGO, ABR_EXIT_BUFFER_BASED_ALGO } =
340340
config.getCurrent();
@@ -415,7 +415,7 @@ function getEstimateReference(
415415
) {
416416
chosenRepFromGuessMode = guessBasedChooser.getGuess(
417417
sortedRepresentations,
418-
lastPlaybackObservation,
418+
lastMediaObservation,
419419
currentRepresentationVal,
420420
currentBestBitrate,
421421
requests,
@@ -460,7 +460,7 @@ function getEstimateReference(
460460
chosenRepFromBufferSize.bitrate,
461461
currentRepresentationVal,
462462
requests,
463-
lastPlaybackObservation,
463+
lastMediaObservation,
464464
),
465465
knownStableBitrate,
466466
};
@@ -481,7 +481,7 @@ function getEstimateReference(
481481
chosenRepFromBandwidth.bitrate,
482482
currentRepresentationVal,
483483
requests,
484-
lastPlaybackObservation,
484+
lastMediaObservation,
485485
),
486486
knownStableBitrate,
487487
};
@@ -634,15 +634,15 @@ export interface IABREstimate {
634634
}
635635

636636
/** Media properties `getEstimateReference` will need to keep track of. */
637-
export interface IRepresentationEstimatorPlaybackObservation {
637+
export interface IRepresentationEstimatorMediaObservation {
638638
/**
639639
* For the concerned media buffer, difference in seconds between the next
640640
* position where no segment data is available and the current position.
641641
*/
642642
bufferGap: number;
643643
/**
644644
* Information on the current media position in seconds at the time of a
645-
* Playback Observation.
645+
* media observation.
646646
*/
647647
position: ObservationPosition;
648648
/**
@@ -744,7 +744,7 @@ export interface IRepresentationEstimatorArguments {
744744
/** Class allowing to estimate the current network bandwidth. */
745745
bandwidthEstimator: BandwidthEstimator;
746746
/** Emit regular playback information. */
747-
playbackObserver: IReadOnlyPlaybackObserver<IRepresentationEstimatorPlaybackObservation>;
747+
mediaElementMonitor: IReadOnlyMediaElementMonitor<IRepresentationEstimatorMediaObservation>;
748748
/**
749749
* The Representation currently loaded.
750750
* `null` if no Representation is currently loaded.
@@ -794,7 +794,7 @@ export type IRepresentationEstimator = (
794794
/** Reference emitting the list of available Representations to choose from. */
795795
representations: IReadOnlySharedReference<IRepresentation[]>,
796796
/** Regularly emits playback conditions */
797-
playbackObserver: IReadOnlyPlaybackObserver<IRepresentationEstimatorPlaybackObservation>,
797+
mediaElementMonitor: IReadOnlyMediaElementMonitor<IRepresentationEstimatorMediaObservation>,
798798
/**
799799
* After this `CancellationSignal` emits, resources will be disposed and
800800
* estimates will stop to be emitted.

src/core/adaptive/buffer_based_chooser.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,13 @@ export default class BufferBasedChooser {
130130
}
131131

132132
/**
133-
* @param {Object} playbackObservation
133+
* @param {Object} mediaObservation
134134
* @returns {number|undefined}
135135
*/
136-
public onAddedSegment(
137-
playbackObservation: IBufferBasedChooserPlaybackObservation,
138-
): void {
136+
public onAddedSegment(mediaObservation: IBufferBasedChooserMediaObservation): void {
139137
const bufferLevels = this._levelsMap;
140138
const bitrates = this._bitrates;
141-
const { bufferGap, currentBitrate, currentScore, speed } = playbackObservation;
139+
const { bufferGap, currentBitrate, currentScore, speed } = mediaObservation;
142140
if (isNullOrUndefined(currentBitrate)) {
143141
this._currentEstimate = bitrates[0];
144142
return;
@@ -256,8 +254,8 @@ export default class BufferBasedChooser {
256254
}
257255
}
258256

259-
/** Playback observation needed by the `BufferBasedChooser`. */
260-
export interface IBufferBasedChooserPlaybackObservation {
257+
/** media observation needed by the `BufferBasedChooser`. */
258+
export interface IBufferBasedChooserMediaObservation {
261259
/**
262260
* Difference in seconds between the current position and the next
263261
* non-buffered position in the buffer for the currently-considered

src/core/adaptive/guess_based_chooser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export default class GuessBasedChooser {
7070
* GuessBasedChooser can choose from, sorted by bitrate ascending.
7171
* /!\ It is very important that Representation in that Array are sorted by
7272
* bitrate ascending for this method to work as intented.
73-
* @param {Object} observation - Last playback observation performed.
73+
* @param {Object} observation - Last media observation performed.
7474
* @param {Object} currentRepresentation - The Representation currently
7575
* loading.
7676
* @param {number} incomingBestBitrate - The bitrate of the Representation

src/core/adaptive/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type {
2121
IMetricsCallbackPayload,
2222
IRepresentationEstimator,
2323
IRepresentationEstimatorCallbacks,
24-
IRepresentationEstimatorPlaybackObservation,
24+
IRepresentationEstimatorMediaObservation,
2525
IRepresentationEstimatorThrottlers as IABRThrottlers,
2626
IRequestBeginCallbackPayload,
2727
IRequestEndCallbackPayload,
@@ -39,7 +39,7 @@ export type {
3939
IABREstimate,
4040
IMetricsCallbackPayload,
4141
IRepresentationEstimatorCallbacks,
42-
IRepresentationEstimatorPlaybackObservation,
42+
IRepresentationEstimatorMediaObservation,
4343
IRequestBeginCallbackPayload,
4444
IRequestProgressCallbackPayload,
4545
IRequestEndCallbackPayload,

src/core/adaptive/network_analyzer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { IRepresentation } from "../../manifest";
2020
import arrayFind from "../../utils/array_find";
2121
import isNullOrUndefined from "../../utils/is_null_or_undefined";
2222
import getMonotonicTimeStamp from "../../utils/monotonic_timestamp";
23-
import type { IRepresentationEstimatorPlaybackObservation } from "./adaptive_representation_selector";
23+
import type { IRepresentationEstimatorMediaObservation } from "./adaptive_representation_selector";
2424
import type BandwidthEstimator from "./utils/bandwidth_estimator";
2525
import EWMA from "./utils/ewma";
2626
import type {
@@ -30,7 +30,7 @@ import type {
3030

3131
/** Object describing the current playback conditions. */
3232
type IPlaybackConditionsInfo = Pick<
33-
IRepresentationEstimatorPlaybackObservation,
33+
IRepresentationEstimatorMediaObservation,
3434
"bufferGap" | "position" | "speed" | "duration"
3535
>;
3636

src/core/cmcd/cmcd_data_builder.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import type {
77
ISegment,
88
} from "../../manifest";
99
import type {
10-
IReadOnlyPlaybackObserver,
10+
IReadOnlyMediaElementMonitor,
1111
IRebufferingStatus,
1212
ObservationPosition,
13-
} from "../../playback_observer";
13+
} from "../../media_element_monitor";
1414
import type { ICmcdOptions, ICmcdPayload, ITrackType } from "../../public_types";
1515
import createUuid from "../../utils/create_uuid";
1616
import isNullOrUndefined from "../../utils/is_null_or_undefined";
@@ -60,10 +60,10 @@ export interface ICmcdSegmentInfo {
6060
}
6161

6262
/**
63-
* Media playback observation's properties the `CmcdDataBuilder` wants to have
63+
* Media media observation's properties the `CmcdDataBuilder` wants to have
6464
* access to.
6565
*/
66-
export interface ICmcdDataBuilderPlaybackObservation {
66+
export interface ICmcdDataBuilderMediaObservation {
6767
/**
6868
* Ranges of buffered data per type of media.
6969
* `null` if no buffer exists for that type of media.
@@ -81,7 +81,7 @@ export interface ICmcdDataBuilderPlaybackObservation {
8181
* status.
8282
* "Rebuffering" is a status where the player has not enough buffer ahead to
8383
* play reliably.
84-
* The RxPlayer should pause playback when a playback observation indicates the
84+
* The RxPlayer should pause playback when a media observation indicates the
8585
* rebuffering status.
8686
*/
8787
rebuffering: IRebufferingStatus | null;
@@ -98,7 +98,7 @@ export default class CmcdDataBuilder {
9898
private _contentId: string;
9999
private _typePreference: TypePreference;
100100
private _lastThroughput: Partial<Record<ITrackType, number | undefined>>;
101-
private _playbackObserver: IReadOnlyPlaybackObserver<ICmcdDataBuilderPlaybackObservation> | null;
101+
private _mediaElementMonitor: IReadOnlyMediaElementMonitor<ICmcdDataBuilderMediaObservation> | null;
102102
private _bufferStarvationToggle: boolean;
103103
private _canceller: TaskCanceller | null;
104104

@@ -115,28 +115,28 @@ export default class CmcdDataBuilder {
115115
? TypePreference.Headers
116116
: TypePreference.QueryString;
117117
this._bufferStarvationToggle = false;
118-
this._playbackObserver = null;
118+
this._mediaElementMonitor = null;
119119
this._lastThroughput = {};
120120
this._canceller = null;
121121
}
122122

123123
/**
124-
* Start listening to the given `playbackObserver` so the `CmcdDataBuilder`
124+
* Start listening to the given `mediaElementMonitor` so the `CmcdDataBuilder`
125125
* can extract some playback-linked metadata that it needs.
126126
*
127127
* It will keep listening for media data until `stopMonitoringPlayback` is called.
128128
*
129129
* If `startMonitoringPlayback` is called again, the previous monitoring is
130130
* also cancelled.
131-
* @param {Object} playbackObserver
131+
* @param {Object} mediaElementMonitor
132132
*/
133133
public startMonitoringPlayback(
134-
playbackObserver: IReadOnlyPlaybackObserver<ICmcdDataBuilderPlaybackObservation>,
134+
mediaElementMonitor: IReadOnlyMediaElementMonitor<ICmcdDataBuilderMediaObservation>,
135135
): void {
136136
this._canceller?.cancel();
137137
this._canceller = new TaskCanceller();
138-
this._playbackObserver = playbackObserver;
139-
playbackObserver.listen(
138+
this._mediaElementMonitor = mediaElementMonitor;
139+
mediaElementMonitor.listen(
140140
(obs) => {
141141
if (obs.rebuffering !== null) {
142142
this._bufferStarvationToggle = true;
@@ -153,7 +153,7 @@ export default class CmcdDataBuilder {
153153
public stopMonitoringPlayback(): void {
154154
this._canceller?.cancel();
155155
this._canceller = null;
156-
this._playbackObserver = null;
156+
this._mediaElementMonitor = null;
157157
}
158158

159159
/**
@@ -184,7 +184,7 @@ export default class CmcdDataBuilder {
184184
: undefined;
185185
props.sid = this._sessionId;
186186

187-
const lastObservation = this._playbackObserver?.getReference().getValue();
187+
const lastObservation = this._mediaElementMonitor?.getReference().getValue();
188188
props.pr =
189189
lastObservation === undefined || lastObservation.speed === 1
190190
? undefined
@@ -229,7 +229,7 @@ export default class CmcdDataBuilder {
229229
* @returns {Object}
230230
*/
231231
public getCmcdDataForSegmentRequest(content: ICmcdSegmentInfo): ICmcdPayload {
232-
const lastObservation = this._playbackObserver?.getReference().getValue();
232+
const lastObservation = this._mediaElementMonitor?.getReference().getValue();
233233

234234
const props = this._getCommonCmcdData(this._lastThroughput[content.adaptation.type]);
235235
props.br = Math.round(content.representation.bitrate / 1000);
@@ -286,7 +286,7 @@ export default class CmcdDataBuilder {
286286
if (!isNullOrUndefined(bufferedForType)) {
287287
// TODO more precize position estimate?
288288
const position =
289-
this._playbackObserver?.getCurrentTime() ??
289+
this._mediaElementMonitor?.getCurrentTime() ??
290290
lastObservation.position.getWanted() ??
291291
lastObservation.position.getPolled();
292292
for (const range of bufferedForType) {

src/core/cmcd/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import CmcdDataBuilder from "./cmcd_data_builder";
22

33
export type {
44
ICmcdSegmentInfo,
5-
ICmcdDataBuilderPlaybackObservation,
5+
ICmcdDataBuilderMediaObservation,
66
} from "./cmcd_data_builder";
77
export default CmcdDataBuilder;

0 commit comments

Comments
 (0)