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

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rx-player",
3
3
  "author": "Canal+",
4
- "version": "3.28.0-dev.2022061700",
4
+ "version": "3.28.0-dev.2022062000",
5
5
  "description": "Canal+ HTML5 Video Player",
6
6
  "main": "./dist/rx-player.js",
7
7
  "keywords": [
@@ -1,7 +1,7 @@
1
1
  sonar.projectKey=rx-player
2
2
  sonar.organization=rx-player
3
3
  sonar.projectName=rx-player
4
- sonar.projectVersion=3.28.0-dev.2022061700
4
+ sonar.projectVersion=3.28.0-dev.2022062000
5
5
  sonar.sources=./src,./demo,./tests
6
6
  sonar.exclusions=demo/full/bundle.js,demo/standalone/lib.js,demo/bundle.js
7
7
  sonar.host.url=https://sonarcloud.io
@@ -52,8 +52,10 @@ import selectOptimalRepresentation from "./utils/select_optimal_representation";
52
52
  /**
53
53
  * Select the most adapted Representation according to the network and buffer
54
54
  * metrics it receives.
55
+ *
55
56
  * @param {Object} options - Initial configuration (see type definition)
56
- * @returns {Object} - Interface allowing to select a Representation
57
+ * @returns {Object} - Interface allowing to select a Representation.
58
+ * @see IRepresentationEstimator
57
59
  */
58
60
  export default function createAdaptiveRepresentationSelector(
59
61
  options : IAdaptiveRepresentationSelectorArguments
@@ -73,14 +75,13 @@ export default function createAdaptiveRepresentationSelector(
73
75
  /**
74
76
  * Returns Object emitting Representation estimates as well as callbacks
75
77
  * allowing to helping it produce them.
78
+ *
79
+ * @see IRepresentationEstimator
76
80
  * @param {Object} context
77
- * @param {Object} currentRepresentation - Reference emitting the
78
- * Representation currently loaded.
79
- * @param {Object} representations - Reference emitting the list of available
80
- * Representations to choose from.
81
- * @param {Object} playbackObserver - Emits regularly playback conditions
82
- * @param {Object} cancellationSignal - After this `CancellationSignal` emits,
83
- * estimates will stop to be emitted.
81
+ * @param {Object} currentRepresentation
82
+ * @param {Object} representations
83
+ * @param {Object} playbackObserver
84
+ * @param {Object} stopAllEstimates
84
85
  * @returns {Array.<Object>}
85
86
  */
86
87
  return function getEstimates(
@@ -92,9 +93,8 @@ export default function createAdaptiveRepresentationSelector(
92
93
  playbackObserver : IReadOnlyPlaybackObserver<
93
94
  IRepresentationEstimatorPlaybackObservation
94
95
  >,
95
- cancellationSignal : CancellationSignal
96
- ) : [ IReadOnlySharedReference<IABREstimate>,
97
- IRepresentationEstimatorCallbacks ] {
96
+ stopAllEstimates : CancellationSignal
97
+ ) : IRepresentationEstimatorResponse {
98
98
  const { type } = context.adaptation;
99
99
  const bandwidthEstimator = _getBandwidthEstimator(type);
100
100
  const manualBitrate = takeFirstSet<IReadOnlySharedReference<number>>(
@@ -127,7 +127,7 @@ export default function createAdaptiveRepresentationSelector(
127
127
  playbackObserver,
128
128
  representations,
129
129
  lowLatencyMode },
130
- cancellationSignal);
130
+ stopAllEstimates);
131
131
  };
132
132
 
133
133
  /**
@@ -163,7 +163,7 @@ export default function createAdaptiveRepresentationSelector(
163
163
  * events to help it doing those estimates.
164
164
  *
165
165
  * @param {Object} args
166
- * @param {Object} cancellationSignal
166
+ * @param {Object} stopAllEstimates
167
167
  * @returns {Array.<Object>}
168
168
  */
169
169
  function getEstimateReference(
@@ -178,10 +178,8 @@ function getEstimateReference(
178
178
  minAutoBitrate,
179
179
  playbackObserver,
180
180
  representations : representationsRef } : IRepresentationEstimatorArguments,
181
- cancellationSignal : CancellationSignal
182
- ) : [ IReadOnlySharedReference<IABREstimate>,
183
- IRepresentationEstimatorCallbacks ] {
184
-
181
+ stopAllEstimates : CancellationSignal
182
+ ) : IRepresentationEstimatorResponse {
185
183
  const scoreCalculator = new RepresentationScoreCalculator();
186
184
  const networkAnalyzer = new NetworkAnalyzer(initialBitrate ?? 0, lowLatencyMode);
187
185
  const requestsStore = new PendingRequestsStore();
@@ -200,10 +198,7 @@ function getEstimateReference(
200
198
  * This TaskCanceller is used both for restarting estimates with a new
201
199
  * configuration and to cancel them altogether.
202
200
  */
203
- let currentEstimatesCanceller = new TaskCanceller();
204
- cancellationSignal.register(() => {
205
- currentEstimatesCanceller.cancel();
206
- });
201
+ let currentEstimatesCanceller = new TaskCanceller({ cancelOn: stopAllEstimates });
207
202
 
208
203
  // Create `ISharedReference` on which estimates will be emitted.
209
204
  const estimateRef = createEstimateReference(manualBitrate.getValue(),
@@ -211,11 +206,11 @@ function getEstimateReference(
211
206
  currentEstimatesCanceller.signal);
212
207
 
213
208
  manualBitrate.onUpdate(restartEstimatesProductionFromCurrentConditions,
214
- { clearSignal: currentEstimatesCanceller.signal });
209
+ { clearSignal: stopAllEstimates });
215
210
  representationsRef.onUpdate(restartEstimatesProductionFromCurrentConditions,
216
- { clearSignal: currentEstimatesCanceller.signal });
211
+ { clearSignal: stopAllEstimates });
217
212
 
218
- return [estimateRef, callbacks];
213
+ return { estimates: estimateRef, callbacks };
219
214
 
220
215
  function createEstimateReference(
221
216
  manualBitrateVal : number,
@@ -475,12 +470,13 @@ function getEstimateReference(
475
470
  const manualBitrateVal = manualBitrate.getValue();
476
471
  const representations = representationsRef.getValue();
477
472
  currentEstimatesCanceller.cancel();
478
- currentEstimatesCanceller = new TaskCanceller();
473
+ currentEstimatesCanceller = new TaskCanceller({ cancelOn: stopAllEstimates });
479
474
  const newRef = createEstimateReference(
480
475
  manualBitrateVal,
481
476
  representations,
482
477
  currentEstimatesCanceller.signal
483
478
  );
479
+
484
480
  newRef.onUpdate(function onNewEstimate(newEstimate : IABREstimate) : void {
485
481
  estimateRef.setValue(newEstimate);
486
482
  }, { clearSignal: currentEstimatesCanceller.signal,
@@ -700,11 +696,7 @@ export interface IABRFiltersObject {
700
696
  width?: number;
701
697
  }
702
698
 
703
- /**
704
- * Callbacks returned by `getEstimateReference`.
705
- * Those needs to be called as soon as the corresponding events to obtain
706
- * coherent Representation estimates.
707
- */
699
+ /** Callbacks returned by `getEstimateReference`. */
708
700
  export interface IRepresentationEstimatorCallbacks {
709
701
  /** Callback to call when a segment has been completely pushed to the buffer. */
710
702
  addedSegment(val : IAddedSegmentCallbackPayload) : void;
@@ -832,17 +824,38 @@ export interface IRepresentationEstimatorArguments {
832
824
  * allowing to estimate the most adapted `Representation`.
833
825
  */
834
826
  export type IRepresentationEstimator = (
827
+ /** Information on the content for which a Representation will be chosen */
835
828
  context : { manifest : Manifest;
836
829
  period : Period;
837
830
  adaptation : Adaptation; },
831
+ /** Reference emitting the Representation currently loaded. */
838
832
  currentRepresentation : IReadOnlySharedReference<Representation | null>,
833
+ /** Reference emitting the list of available Representations to choose from. */
839
834
  representations : IReadOnlySharedReference<Representation[]>,
835
+ /** Regularly emits playback conditions */
840
836
  playbackObserver : IReadOnlyPlaybackObserver<
841
837
  IRepresentationEstimatorPlaybackObservation
842
838
  >,
843
- cancellationSignal : CancellationSignal
844
- ) => [ IReadOnlySharedReference<IABREstimate>,
845
- IRepresentationEstimatorCallbacks ];
839
+ /**
840
+ * After this `CancellationSignal` emits, resources will be disposed and
841
+ * estimates will stop to be emitted.
842
+ */
843
+ stopAllEstimates : CancellationSignal
844
+ ) => IRepresentationEstimatorResponse;
845
+
846
+ /** Value returned by an `IRepresentationEstimator` */
847
+ export interface IRepresentationEstimatorResponse {
848
+ /**
849
+ * Regularly produces estimates of the best Representation to play (from the
850
+ * list given).
851
+ */
852
+ estimates: IReadOnlySharedReference<IABREstimate>;
853
+ /**
854
+ * Callback which need to be called as soon as the corresponding events to
855
+ * obtain accurate Representation estimates.
856
+ */
857
+ callbacks: IRepresentationEstimatorCallbacks;
858
+ }
846
859
 
847
860
  /** Arguments received by `createAdaptiveRepresentationSelector`. */
848
861
  export interface IAdaptiveRepresentationSelectorArguments {
@@ -429,7 +429,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
429
429
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
430
430
  videoElement.preload = "auto";
431
431
 
432
- this.version = /* PLAYER_VERSION */"3.28.0-dev.2022061700";
432
+ this.version = /* PLAYER_VERSION */"3.28.0-dev.2022062000";
433
433
  this.log = log;
434
434
  this.state = "STOPPED";
435
435
  this.videoElement = videoElement;
@@ -2941,7 +2941,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2941
2941
  return activeRepresentations[currentPeriod.id];
2942
2942
  }
2943
2943
  }
2944
- Player.version = /* PLAYER_VERSION */"3.28.0-dev.2022061700";
2944
+ Player.version = /* PLAYER_VERSION */"3.28.0-dev.2022062000";
2945
2945
 
2946
2946
  /** Every events sent by the RxPlayer's public API. */
2947
2947
  interface IPublicAPIEvent {
@@ -491,20 +491,13 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
491
491
  return;
492
492
  }
493
493
 
494
- let linkedKeys;
495
- if (sessionInfo.source === MediaKeySessionLoadingType.Created) {
496
- // When the license has been fetched, there might be implicit key ids
497
- // linked to the session depending on the `singleLicensePer` option.
498
- linkedKeys = getFetchedLicenseKeysInfo(initializationData,
499
- options.singleLicensePer,
500
- evt.value.whitelistedKeyIds,
501
- evt.value.blacklistedKeyIDs);
502
- } else {
503
- // When the MediaKeySession is just a cached/persisted one, we don't
504
- // have any concept of "implicit key id".
505
- linkedKeys = { whitelisted: evt.value.whitelistedKeyIds,
506
- blacklisted: evt.value.blacklistedKeyIDs };
507
- }
494
+ const linkedKeys = getKeyIdsLinkedToSession(
495
+ initializationData,
496
+ sessionInfo.record,
497
+ options.singleLicensePer,
498
+ sessionInfo.source === MediaKeySessionLoadingType.Created,
499
+ evt.value.whitelistedKeyIds,
500
+ evt.value.blacklistedKeyIDs);
508
501
 
509
502
  sessionInfo.record.associateKeyIds(linkedKeys.whitelisted);
510
503
  sessionInfo.record.associateKeyIds(linkedKeys.blacklisted);
@@ -1053,22 +1046,46 @@ interface IAttachedMediaKeysData {
1053
1046
  }
1054
1047
 
1055
1048
  /**
1056
- * Returns information on all keys - explicit or implicit - that are linked to
1057
- * a loaded license.
1049
+ * Returns set of all usable and unusable keys - explicit or implicit - that are
1050
+ * linked to a `MediaKeySession`.
1058
1051
  *
1059
1052
  * In the RxPlayer, there is a concept of "explicit" key ids, which are key ids
1060
1053
  * found in a license whose status can be known through the `keyStatuses`
1061
1054
  * property from a `MediaKeySession`, and of "implicit" key ids, which are key
1062
1055
  * ids which were expected to be in a fetched license, but apparently weren't.
1063
- * @param {Object} initializationData
1064
- * @param {string|undefined} singleLicensePer
1065
- * @param {Array.<Uint8Array>} usableKeyIds
1066
- * @param {Array.<Uint8Array>} unusableKeyIds
1067
- * @returns {Object}
1056
+ *
1057
+ * @param {Object} initializationData - Initialization data object used to make
1058
+ * the request for the current license.
1059
+ * @param {Object} keySessionRecord - The `KeySessionRecord` associated with the
1060
+ * session that has been loaded. It might give supplementary information on
1061
+ * keys implicitly linked to the license.
1062
+ * @param {string|undefined} singleLicensePer - Setting allowing to indicate the
1063
+ * scope a given license should have.
1064
+ * @param {boolean} isCurrentLicense - If `true` the license has been fetched
1065
+ * especially for the current content.
1066
+ *
1067
+ * Knowing this allows to determine that if decryption keys that should have
1068
+ * been referenced in the fetched license (according to the `singleLicensePer`
1069
+ * setting) are missing, then the keys surely must have been voluntarly
1070
+ * removed from the license.
1071
+ *
1072
+ * If it is however set to `false`, it means that the license is an older
1073
+ * license that might have been linked to another content, thus we cannot make
1074
+ * that assumption.
1075
+ * @param {Array.<Uint8Array>} usableKeyIds - Key ids that are present in the
1076
+ * license and can be used.
1077
+ * @param {Array.<Uint8Array>} unusableKeyIds - Key ids that are present in the
1078
+ * license yet cannot be used.
1079
+ * @returns {Object} - Returns an object with the following properties:
1080
+ * - `whitelisted`: Array of key ids for keys that are known to be usable
1081
+ * - `blacklisted`: Array of key ids for keys that are considered unusable.
1082
+ * The qualities linked to those keys should not be played.
1068
1083
  */
1069
- function getFetchedLicenseKeysInfo(
1084
+ function getKeyIdsLinkedToSession(
1070
1085
  initializationData : IProcessedProtectionData,
1086
+ keySessionRecord : KeySessionRecord,
1071
1087
  singleLicensePer : undefined | "init-data" | "content" | "periods",
1088
+ isCurrentLicense : boolean,
1072
1089
  usableKeyIds : Uint8Array[],
1073
1090
  unusableKeyIds : Uint8Array[]
1074
1091
  ) : { whitelisted : Uint8Array[];
@@ -1081,6 +1098,19 @@ function getFetchedLicenseKeysInfo(
1081
1098
  const associatedKeyIds = [...usableKeyIds,
1082
1099
  ...unusableKeyIds];
1083
1100
 
1101
+ // Add all key ids associated to the `KeySessionRecord` yet not in
1102
+ // `usableKeyIds` nor in `unusableKeyIds`
1103
+ const allKnownKeyIds = keySessionRecord.getAssociatedKeyIds();
1104
+ for (const kid of allKnownKeyIds) {
1105
+ if (!associatedKeyIds.some(ak => areKeyIdsEqual(ak, kid))) {
1106
+ if (log.hasLevel("DEBUG")) {
1107
+ log.debug("DRM: KeySessionRecord's key missing in the license, blacklisting it",
1108
+ bytesToHex(kid));
1109
+ }
1110
+ associatedKeyIds.push(kid);
1111
+ }
1112
+ }
1113
+
1084
1114
  if (singleLicensePer !== undefined && singleLicensePer !== "init-data") {
1085
1115
  // We want to add the current key ids in the blacklist if it is
1086
1116
  // not already there.
@@ -1102,11 +1132,15 @@ function getFetchedLicenseKeysInfo(
1102
1132
  return !associatedKeyIds.some(k => areKeyIdsEqual(k, expected));
1103
1133
  });
1104
1134
  if (missingKeyIds.length > 0) {
1135
+ if (log.hasLevel("DEBUG")) {
1136
+ log.debug("DRM: init data keys missing in the license, blacklisting them",
1137
+ missingKeyIds.map(m => bytesToHex(m)).join(", "));
1138
+ }
1105
1139
  associatedKeyIds.push(...missingKeyIds) ;
1106
1140
  }
1107
1141
  }
1108
1142
 
1109
- if (content !== undefined) {
1143
+ if (isCurrentLicense && content !== undefined) {
1110
1144
  if (singleLicensePer === "content") {
1111
1145
  // Put it in a Set to automatically filter out duplicates (by ref)
1112
1146
  const contentKeys = new Set<Uint8Array>();
@@ -70,12 +70,12 @@ export default function getRepresentationEstimate(
70
70
  updateRepresentationsReference();
71
71
  manifest.addEventListener("decipherabilityUpdate", updateRepresentationsReference);
72
72
  const unregisterCleanUp = cancellationSignal.register(cleanUp);
73
- const [ estimateRef,
74
- abrCallbacks ] = representationEstimator(content,
75
- currentRepresentation,
76
- representations,
77
- playbackObserver,
78
- cancellationSignal);
73
+ const { estimates: estimateRef,
74
+ callbacks: abrCallbacks } = representationEstimator(content,
75
+ currentRepresentation,
76
+ representations,
77
+ playbackObserver,
78
+ cancellationSignal);
79
79
  return { abrCallbacks, estimateRef };
80
80
 
81
81
  function updateRepresentationsReference() : void {
@@ -57,11 +57,26 @@ import { CancellationSignal } from "./task_canceller";
57
57
  * those use cases makes the intent of the corresponding code clearer.
58
58
  */
59
59
  export interface ISharedReference<T> {
60
- /** Get the last set value for this shared reference. */
60
+ /**
61
+ * Get the last set value for this shared reference.
62
+ * @returns {*}
63
+ */
61
64
  getValue() : T;
62
65
 
63
- /** Update the value of this shared reference. */
66
+ /**
67
+ * Update the value of this shared reference.
68
+ * @param {*} newVal
69
+ */
64
70
  setValue(newVal : T) : void;
71
+
72
+ /**
73
+ * Update the value of this shared reference only if the value changed.
74
+ *
75
+ * Note that this function only performs a strict equality reference through
76
+ * the "===" operator. Different objects that are structurally the same will
77
+ * thus be considered different.
78
+ * @param {*} newVal
79
+ */
65
80
  setValueIfChanged(newVal : T) : void;
66
81
 
67
82
  /**
@@ -72,25 +87,6 @@ export interface ISharedReference<T> {
72
87
  * @returns {Observable}
73
88
  */
74
89
  asObservable(skipCurrentValue? : boolean) : Observable<T>;
75
- /**
76
- * Triggers a callback each time this reference's value is updated.
77
- *
78
- * Can be given several options as argument:
79
- * - clearSignal: When the attach `CancellationSignal` emits, the given
80
- * callback will not be called anymore on reference updates.
81
- * - emitCurrentValue: If `true`, the callback will be called directly and
82
- * synchronously on this call with its current value.
83
- * @param {Function} cb
84
- * @param {Object} [options]
85
- */
86
- onUpdate(
87
- cb : (val : T) => void,
88
- options? : {
89
- clearSignal?: CancellationSignal;
90
- emitCurrentValue?: boolean;
91
- },
92
- ) : void;
93
-
94
90
  /**
95
91
  * Allows to register a callback to be called each time the value inside the
96
92
  * reference is updated.
@@ -109,7 +105,6 @@ export interface ISharedReference<T> {
109
105
  emitCurrentValue?: boolean;
110
106
  },
111
107
  ) : void;
112
-
113
108
  /**
114
109
  * Indicate that no new values will be emitted.
115
110
  * Allows to automatically close all Observables generated from this shared
@@ -217,7 +212,7 @@ export function createSharedReference<T>(initialValue : T) : ISharedReference<T>
217
212
 
218
213
  /**
219
214
  * Update the value of this shared reference.
220
- * @param {*}
215
+ * @param {*} newVal
221
216
  */
222
217
  setValue(newVal : T) : void {
223
218
  if (isFinished) {
@@ -244,6 +239,10 @@ export function createSharedReference<T>(initialValue : T) : ISharedReference<T>
244
239
  }
245
240
  },
246
241
 
242
+ /**
243
+ * Update the value of this shared reference only if it changed.
244
+ * @param {*} newVal
245
+ */
247
246
  setValueIfChanged(newVal : T) : void {
248
247
  if (newVal !== value) {
249
248
  this.setValue(newVal);
@@ -353,6 +352,17 @@ export function createSharedReference<T>(initialValue : T) : ISharedReference<T>
353
352
  };
354
353
  }
355
354
 
355
+ /**
356
+ * Create a new `ISharedReference` based on another one by mapping over its
357
+ * referenced value each time it is updated.
358
+ * @param {Object} originalRef - The Original `ISharedReference` you wish to map
359
+ * over.
360
+ * @param {Function} mappingFn - The mapping function which will receives
361
+ * `originalRef`'s value and outputs this new reference's value.
362
+ * @param {Object | undefined} [cancellationSignal] - Optionally, a
363
+ * `CancellationSignal` which will finish that reference when it emits.
364
+ * @returns {Object} - The new, mapped, reference.
365
+ */
356
366
  export function createMappedReference<T, U>(
357
367
  originalRef : IReadOnlySharedReference<T>,
358
368
  mappingFn : (x : T) => U,