rx-player 4.2.0-dev.2024100200 → 4.2.0-dev.2024101500

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 (57) hide show
  1. package/.vscode/settings.json +9 -0
  2. package/CHANGELOG.md +13 -1
  3. package/VERSION +1 -1
  4. package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  5. package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  6. package/dist/commonjs/core/segment_sinks/segment_buffers_store.d.ts.map +1 -1
  7. package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.d.ts.map +1 -1
  8. package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.js +85 -8
  9. package/dist/commonjs/main_thread/api/debug/modules/general_info.js +1 -1
  10. package/dist/commonjs/main_thread/api/public_api.js +2 -2
  11. package/dist/commonjs/main_thread/decrypt/find_key_system.d.ts.map +1 -1
  12. package/dist/commonjs/main_thread/decrypt/find_key_system.js +3 -0
  13. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +11 -1
  14. package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  15. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +23 -11
  16. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts +2 -1
  17. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts.map +1 -1
  18. package/dist/commonjs/main_thread/init/multi_thread_content_initializer.js +9 -5
  19. package/dist/commonjs/main_thread/text_displayer/native/native_text_displayer.d.ts +1 -0
  20. package/dist/commonjs/main_thread/text_displayer/native/native_text_displayer.d.ts.map +1 -1
  21. package/dist/commonjs/main_thread/text_displayer/native/native_text_displayer.js +19 -16
  22. package/dist/commonjs/utils/sync_or_async.d.ts.map +1 -1
  23. package/dist/commonjs/utils/sync_or_async.js +3 -1
  24. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  25. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  26. package/dist/es2017/core/segment_sinks/segment_buffers_store.d.ts.map +1 -1
  27. package/dist/es2017/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.d.ts.map +1 -1
  28. package/dist/es2017/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.js +36 -7
  29. package/dist/es2017/main_thread/api/debug/modules/general_info.js +1 -1
  30. package/dist/es2017/main_thread/api/public_api.js +2 -2
  31. package/dist/es2017/main_thread/decrypt/find_key_system.d.ts.map +1 -1
  32. package/dist/es2017/main_thread/decrypt/find_key_system.js +3 -0
  33. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +11 -1
  34. package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
  35. package/dist/es2017/main_thread/init/media_source_content_initializer.js +23 -11
  36. package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts +2 -1
  37. package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts.map +1 -1
  38. package/dist/es2017/main_thread/init/multi_thread_content_initializer.js +9 -5
  39. package/dist/es2017/main_thread/text_displayer/native/native_text_displayer.d.ts +1 -0
  40. package/dist/es2017/main_thread/text_displayer/native/native_text_displayer.d.ts.map +1 -1
  41. package/dist/es2017/main_thread/text_displayer/native/native_text_displayer.js +19 -16
  42. package/dist/es2017/utils/sync_or_async.d.ts.map +1 -1
  43. package/dist/es2017/utils/sync_or_async.js +3 -1
  44. package/dist/mpd-parser.wasm +0 -0
  45. package/dist/rx-player.js +53 -29
  46. package/dist/rx-player.min.js +17 -17
  47. package/package.json +1 -1
  48. package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
  49. package/src/core/segment_sinks/segment_buffers_store.ts +6 -2
  50. package/src/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.ts +39 -7
  51. package/src/main_thread/api/debug/modules/general_info.ts +1 -1
  52. package/src/main_thread/api/public_api.ts +2 -2
  53. package/src/main_thread/decrypt/find_key_system.ts +3 -0
  54. package/src/main_thread/init/media_source_content_initializer.ts +50 -17
  55. package/src/main_thread/init/multi_thread_content_initializer.ts +15 -7
  56. package/src/main_thread/text_displayer/native/native_text_displayer.ts +22 -18
  57. package/src/utils/sync_or_async.ts +5 -3
@@ -103,7 +103,11 @@ export default class SegmentSinksStore {
103
103
  * disabled. This means that the corresponding type (e.g. audio, video etc.)
104
104
  * won't be needed when playing the current content.
105
105
  */
106
- private _initializedSegmentSinks: Partial<Record<IBufferType, SegmentSink | null>>;
106
+ private _initializedSegmentSinks: {
107
+ audio?: AudioVideoSegmentSink | undefined | null;
108
+ video?: AudioVideoSegmentSink | undefined | null;
109
+ text?: TextSegmentSink | null;
110
+ };
107
111
 
108
112
  /**
109
113
  * Callbacks called after a SourceBuffer is either created or disabled.
@@ -308,7 +312,7 @@ export default class SegmentSinksStore {
308
312
  return memorizedSegmentSink;
309
313
  }
310
314
 
311
- let segmentSink: SegmentSink;
315
+ let segmentSink: TextSegmentSink;
312
316
  if (bufferType === "text") {
313
317
  log.info("SB: Creating a new text SegmentSink");
314
318
  if (this._textInterface === null) {
@@ -14,7 +14,12 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ import { canRelyOnRequestMediaKeySystemAccess } from "../../../../compat/can_rely_on_request_media_key_system_access";
17
18
  import eme from "../../../../compat/eme";
19
+ import {
20
+ DUMMY_PLAY_READY_HEADER,
21
+ generatePlayReadyInitData,
22
+ } from "../../../../compat/generate_init_data";
18
23
  import isNullOrUndefined from "../../../../utils/is_null_or_undefined";
19
24
  import log from "../log";
20
25
  import type { ICompatibleKeySystem, IMediaConfiguration } from "../types";
@@ -57,13 +62,40 @@ export default function probeDRMInfos(
57
62
 
58
63
  return eme
59
64
  .requestMediaKeySystemAccess(type, [configuration])
60
- .then((keySystemAccess) => {
61
- result.compatibleConfiguration = keySystemAccess.getConfiguration();
62
- const status: [ProberStatus, ICompatibleKeySystem?] = [
63
- ProberStatus.Supported,
64
- result,
65
- ];
66
- return status;
65
+ .then(async (keySystemAccess) => {
66
+ if (!canRelyOnRequestMediaKeySystemAccess(type)) {
67
+ try {
68
+ const mediaKeys = await keySystemAccess.createMediaKeys();
69
+ const session = mediaKeys.createSession();
70
+ const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
71
+ await session.generateRequest("cenc", initData);
72
+ session.close().catch(() => {
73
+ log.warn("DRM: Failed to close the dummy session");
74
+ });
75
+ result.compatibleConfiguration = keySystemAccess.getConfiguration();
76
+ const status: [ProberStatus, ICompatibleKeySystem?] = [
77
+ ProberStatus.Supported,
78
+ result,
79
+ ];
80
+ return status;
81
+ } catch (err) {
82
+ log.debug("DRM: KeySystemAccess was granted but it is not usable");
83
+
84
+ const statusError: [ProberStatus, ICompatibleKeySystem] = [
85
+ ProberStatus.NotSupported,
86
+ result,
87
+ ];
88
+ return statusError;
89
+ }
90
+ } else {
91
+ result.compatibleConfiguration = keySystemAccess.getConfiguration();
92
+
93
+ const status: [ProberStatus, ICompatibleKeySystem?] = [
94
+ ProberStatus.Supported,
95
+ result,
96
+ ];
97
+ return status;
98
+ }
67
99
  })
68
100
  .catch(() => {
69
101
  return [ProberStatus.NotSupported, result];
@@ -65,7 +65,7 @@ export default function constructDebugGeneralInfo(
65
65
  valuesLine1.push(["wo", "0"]);
66
66
  }
67
67
 
68
- const valuesLine2: Array<[string, string]> = [];
68
+ const valuesLine2: Array<[string, string]> = [["v", instance.version]];
69
69
  const ks = instance.getKeySystemConfiguration();
70
70
  if (ks !== null) {
71
71
  valuesLine2.push(["ks", ks.keySystem]);
@@ -411,7 +411,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
411
411
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
412
412
  videoElement.preload = "auto";
413
413
 
414
- this.version = /* PLAYER_VERSION */ "4.2.0-dev.2024100200";
414
+ this.version = /* PLAYER_VERSION */ "4.2.0-dev.2024101500";
415
415
  this.log = log;
416
416
  this.state = "STOPPED";
417
417
  this.videoElement = videoElement;
@@ -3330,7 +3330,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3330
3330
  }
3331
3331
  }
3332
3332
  }
3333
- Player.version = /* PLAYER_VERSION */ "4.2.0-dev.2024100200";
3333
+ Player.version = /* PLAYER_VERSION */ "4.2.0-dev.2024101500";
3334
3334
 
3335
3335
  /** Every events sent by the RxPlayer's public API. */
3336
3336
  interface IPublicAPIEvent {
@@ -539,6 +539,9 @@ export async function testKeySystem(
539
539
  const session = mediaKeys.createSession();
540
540
  const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
541
541
  await session.generateRequest("cenc", initData);
542
+ session.close().catch(() => {
543
+ log.warn("DRM: Failed to close the dummy session");
544
+ });
542
545
  } catch (err) {
543
546
  log.debug("DRM: KeySystemAccess was granted but it is not usable");
544
547
  throw err;
@@ -92,7 +92,7 @@ import listenToMediaError from "./utils/throw_on_media_error";
92
92
  */
93
93
  export default class MediaSourceContentInitializer extends ContentInitializer {
94
94
  /** Constructor settings associated to this `MediaSourceContentInitializer`. */
95
- private _settings: IInitializeArguments;
95
+ private _initSettings: IInitializeArguments;
96
96
  /**
97
97
  * `TaskCanceller` allowing to abort everything that the
98
98
  * `MediaSourceContentInitializer` is doing.
@@ -145,7 +145,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
145
145
  */
146
146
  constructor(settings: IInitializeArguments) {
147
147
  super();
148
- this._settings = settings;
148
+ this._initSettings = settings;
149
149
  this._initCanceller = new TaskCanceller();
150
150
  this._manifest = null;
151
151
  this._decryptionCapabilities = { status: "uninitialized", value: null };
@@ -234,6 +234,10 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
234
234
  this._initCanceller.cancel();
235
235
  }
236
236
 
237
+ /**
238
+ * Callback called when an error interrupting playback arised.
239
+ * @param {*} err
240
+ */
237
241
  private _onFatalError(err: unknown) {
238
242
  if (this._initCanceller.isUsed()) {
239
243
  return;
@@ -242,6 +246,12 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
242
246
  this.trigger("error", err);
243
247
  }
244
248
 
249
+ /**
250
+ * Initialize decryption mechanisms if needed and begin creating and relying
251
+ * on the initial `MediaSourceInterface` for this content.
252
+ * @param {HTMLMediaElement|null} mediaElement
253
+ * @returns {Promise.<Object>}
254
+ */
245
255
  private _initializeMediaSourceAndDecryption(mediaElement: IMediaElement): Promise<{
246
256
  mediaSource: MainMediaSourceInterface;
247
257
  drmSystemId: string | undefined;
@@ -249,7 +259,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
249
259
  }> {
250
260
  const initCanceller = this._initCanceller;
251
261
  return createCancellablePromise(initCanceller.signal, (resolve) => {
252
- const { keySystems } = this._settings;
262
+ const { keySystems } = this._initSettings;
253
263
 
254
264
  /** Initialize decryption capabilities. */
255
265
  const { statusRef: drmInitRef, contentDecryptor } = initializeContentDecryption(
@@ -383,7 +393,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
383
393
  startAt,
384
394
  textTrackOptions,
385
395
  transport,
386
- } = this._settings;
396
+ } = this._initSettings;
387
397
  const initCanceller = this._initCanceller;
388
398
  assert(this._manifest !== null);
389
399
  let manifest: IManifest;
@@ -526,7 +536,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
526
536
  */
527
537
  private _startBufferingOnMediaSource(
528
538
  args: IBufferingMediaSettings,
529
- onReloadOrder: (reloadOrder: { position: number; autoPlay: boolean }) => void,
539
+ onReloadOrder: IReloadMediaSourceCallback,
530
540
  cancelSignal: CancellationSignal,
531
541
  ): void {
532
542
  const {
@@ -553,18 +563,10 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
553
563
  }
554
564
 
555
565
  let textDisplayerInterface: ITextDisplayerInterface | null = null;
556
- let textDisplayer: ITextDisplayer | null = null;
557
- if (
558
- this._settings.textTrackOptions.textTrackMode === "html" &&
559
- features.htmlTextDisplayer !== null
560
- ) {
561
- textDisplayer = new features.htmlTextDisplayer(
562
- mediaElement,
563
- this._settings.textTrackOptions.textTrackElement,
564
- );
565
- } else if (features.nativeTextDisplayer !== null) {
566
- textDisplayer = new features.nativeTextDisplayer(mediaElement);
567
- }
566
+ const textDisplayer = createTextDisplayer(
567
+ mediaElement,
568
+ this._initSettings.textTrackOptions,
569
+ );
568
570
  if (textDisplayer !== null) {
569
571
  const sender = new MainThreadTextDisplayerInterface(textDisplayer);
570
572
  textDisplayerInterface = sender;
@@ -1123,6 +1125,21 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
1123
1125
  }
1124
1126
  }
1125
1127
 
1128
+ function createTextDisplayer(
1129
+ mediaElement: IMediaElement,
1130
+ textTrackOptions: ITextDisplayerOptions,
1131
+ ): ITextDisplayer | null {
1132
+ if (textTrackOptions.textTrackMode === "html" && features.htmlTextDisplayer !== null) {
1133
+ return new features.htmlTextDisplayer(
1134
+ mediaElement,
1135
+ textTrackOptions.textTrackElement,
1136
+ );
1137
+ } else if (features.nativeTextDisplayer !== null) {
1138
+ return new features.nativeTextDisplayer(mediaElement);
1139
+ }
1140
+ return null;
1141
+ }
1142
+
1126
1143
  /** Arguments to give to the `InitializeOnMediaSource` function. */
1127
1144
  export interface IInitializeArguments {
1128
1145
  /** Options concerning the ABR logic. */
@@ -1324,3 +1341,19 @@ function blackListProtectionDataOnManifest(
1324
1341
  return rep.decipherable;
1325
1342
  });
1326
1343
  }
1344
+
1345
+ /**
1346
+ * Function to call when you want to "reload" the MediaSource: basically
1347
+ * restarting playback on a new MediaSource for the same content (it may
1348
+ * be for varied reasons, such as ensuring data buffers are empty, or
1349
+ * restarting after some kind of fatal error).
1350
+ * @param {Object} reloadOrder
1351
+ * @param {number} reloadOrder.position - Position in seconds at which we
1352
+ * should restart from when playback restarts.
1353
+ * @param {boolean} reloadOrder.autoPlay - If `true` we will directly play
1354
+ * once enough data is re-loaded.
1355
+ */
1356
+ type IReloadMediaSourceCallback = (reloadOrder: {
1357
+ position: number;
1358
+ autoPlay: boolean;
1359
+ }) => void;
@@ -113,12 +113,13 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
113
113
  private _currentMediaSourceCanceller: TaskCanceller;
114
114
 
115
115
  /**
116
- * Stores the resolvers and the current messageId that is sent to the web worker to receive segment sink metrics.
116
+ * Stores the resolvers and the current messageId that is sent to the web worker to
117
+ * receive segment sink metrics.
117
118
  * The purpose of collecting metrics is for monitoring and debugging.
118
119
  */
119
120
  private _segmentMetrics: {
120
121
  lastMessageId: number;
121
- resolvers: Record<number, (value: ISegmentSinkMetrics | undefined) => void>;
122
+ resolvers: Map<number, (value: ISegmentSinkMetrics | undefined) => void>;
122
123
  };
123
124
 
124
125
  /**
@@ -135,7 +136,7 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
135
136
  this._currentContentInfo = null;
136
137
  this._segmentMetrics = {
137
138
  lastMessageId: 0,
138
- resolvers: {},
139
+ resolvers: new Map(),
139
140
  };
140
141
  this._queuedWorkerMessages = null;
141
142
  }
@@ -1121,10 +1122,9 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
1121
1122
  if (this._currentContentInfo?.contentId !== msgData.contentId) {
1122
1123
  return;
1123
1124
  }
1124
- const resolveFn = this._segmentMetrics.resolvers[msgData.value.messageId];
1125
+ const resolveFn = this._segmentMetrics.resolvers.get(msgData.value.messageId);
1125
1126
  if (resolveFn !== undefined) {
1126
1127
  resolveFn(msgData.value.segmentSinkMetrics);
1127
- delete this._segmentMetrics.resolvers[msgData.value.messageId];
1128
1128
  } else {
1129
1129
  log.error("MTCI: Failed to send segment sink store update");
1130
1130
  }
@@ -1589,11 +1589,19 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
1589
1589
  value: { messageId },
1590
1590
  });
1591
1591
  return new Promise((resolve, reject) => {
1592
- this._segmentMetrics.resolvers[messageId] = resolve;
1593
1592
  const rejectFn = (err: CancellationError) => {
1594
- delete this._segmentMetrics.resolvers[messageId];
1593
+ cancelSignal.deregister(rejectFn);
1594
+ this._segmentMetrics.resolvers.delete(messageId);
1595
1595
  return reject(err);
1596
1596
  };
1597
+ this._segmentMetrics.resolvers.set(
1598
+ messageId,
1599
+ (value: ISegmentSinkMetrics | undefined) => {
1600
+ cancelSignal.deregister(rejectFn);
1601
+ this._segmentMetrics.resolvers.delete(messageId);
1602
+ resolve(value);
1603
+ },
1604
+ );
1597
1605
  cancelSignal.register(rejectFn);
1598
1606
  });
1599
1607
  };
@@ -158,24 +158,7 @@ export default class NativeTextDisplayer implements ITextDisplayer {
158
158
  public reset(): void {
159
159
  log.debug("NTD: Aborting NativeTextDisplayer");
160
160
  this._removeData(0, Infinity);
161
- const { _trackElement, _videoElement } = this;
162
-
163
- if (_trackElement !== undefined && _videoElement.hasChildNodes()) {
164
- try {
165
- _videoElement.removeChild(_trackElement);
166
- } catch (_e) {
167
- log.warn("NTD: Can't remove track element from the video");
168
- }
169
- }
170
-
171
- // Ugly trick to work-around browser bugs by refreshing its mode
172
- const oldMode = this._track.mode;
173
- this._track.mode = "disabled";
174
- this._track.mode = oldMode;
175
-
176
- if (this._trackElement !== undefined) {
177
- this._trackElement.innerHTML = "";
178
- }
161
+ this._clearTrackElement();
179
162
  }
180
163
 
181
164
  public stop(): void {
@@ -213,6 +196,27 @@ export default class NativeTextDisplayer implements ITextDisplayer {
213
196
  }
214
197
  this._buffered.remove(start, end);
215
198
  }
199
+
200
+ private _clearTrackElement(): void {
201
+ const { _trackElement, _videoElement } = this;
202
+
203
+ if (_trackElement !== undefined && _videoElement.hasChildNodes()) {
204
+ try {
205
+ _videoElement.removeChild(_trackElement);
206
+ } catch (_e) {
207
+ log.warn("NTD: Can't remove track element from the video");
208
+ }
209
+ }
210
+
211
+ // Ugly trick to work-around browser bugs by refreshing its mode
212
+ const oldMode = this._track.mode;
213
+ this._track.mode = "disabled";
214
+ this._track.mode = oldMode;
215
+
216
+ if (this._trackElement !== undefined) {
217
+ this._trackElement.innerHTML = "";
218
+ }
219
+ }
216
220
  }
217
221
 
218
222
  /** Data of chunks that should be pushed to the NativeTextDisplayer. */
@@ -78,12 +78,14 @@ const SyncOrAsync = {
78
78
  * @returns {Object}
79
79
  */
80
80
  createAsync<T>(val: Promise<T>): ISyncOrAsyncValue<T> {
81
- let ret = null;
82
- val.then((resolved) => {
81
+ let ret: T | null = null;
82
+ val.then((resolved: T) => {
83
83
  ret = resolved;
84
84
  }, noop);
85
85
  return {
86
- syncValue: ret,
86
+ get syncValue(): T | null {
87
+ return ret;
88
+ },
87
89
  getValueAsAsync() {
88
90
  return val;
89
91
  },