rx-player 4.2.0-dev.2024081301 → 4.2.0-dev.2024082600

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 (127) hide show
  1. package/.vscode/settings.json +7 -0
  2. package/CHANGELOG.md +29 -0
  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/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  7. package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
  8. package/dist/commonjs/compat/eme/custom_media_keys/types.d.ts +3 -0
  9. package/dist/commonjs/compat/eme/custom_media_keys/types.d.ts.map +1 -1
  10. package/dist/commonjs/compat/eme/custom_media_keys/webkit_media_keys.d.ts +1 -1
  11. package/dist/commonjs/compat/eme/custom_media_keys/webkit_media_keys.d.ts.map +1 -1
  12. package/dist/commonjs/compat/eme/eme-api-implementation.d.ts +2 -2
  13. package/dist/commonjs/compat/eme/eme-api-implementation.d.ts.map +1 -1
  14. package/dist/commonjs/compat/eme/eme-api-implementation.js +19 -2
  15. package/dist/commonjs/compat/eme/get_init_data.d.ts +3 -1
  16. package/dist/commonjs/compat/eme/get_init_data.d.ts.map +1 -1
  17. package/dist/commonjs/compat/eme/get_init_data.js +2 -2
  18. package/dist/commonjs/compat/event_listeners.d.ts +21 -19
  19. package/dist/commonjs/compat/event_listeners.d.ts.map +1 -1
  20. package/dist/commonjs/compat/event_listeners.js +0 -11
  21. package/dist/commonjs/core/segment_sinks/inventory/segment_inventory.js +8 -5
  22. package/dist/commonjs/core/segment_sinks/segment_buffers_store.d.ts.map +1 -1
  23. package/dist/commonjs/core/segment_sinks/segment_buffers_store.js +5 -2
  24. package/dist/commonjs/main_thread/api/public_api.d.ts +22 -10
  25. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  26. package/dist/commonjs/main_thread/api/public_api.js +68 -28
  27. package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts +8 -0
  28. package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
  29. package/dist/commonjs/main_thread/decrypt/content_decryptor.js +51 -2
  30. package/dist/commonjs/main_thread/decrypt/types.d.ts +5 -0
  31. package/dist/commonjs/main_thread/decrypt/types.d.ts.map +1 -1
  32. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +3 -3
  33. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +12 -4
  34. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
  35. package/dist/commonjs/main_thread/tracks_store/tracks_store.js +18 -8
  36. package/dist/commonjs/manifest/classes/adaptation.d.ts.map +1 -1
  37. package/dist/commonjs/manifest/classes/adaptation.js +1 -4
  38. package/dist/commonjs/manifest/classes/representation.js +8 -8
  39. package/dist/commonjs/manifest/utils.d.ts +4 -3
  40. package/dist/commonjs/manifest/utils.d.ts.map +1 -1
  41. package/dist/commonjs/manifest/utils.js +13 -7
  42. package/dist/commonjs/parsers/manifest/dash/common/content_protection_parser.js +4 -4
  43. package/dist/commonjs/parsers/manifest/local/parse_local_manifest.js +1 -1
  44. package/dist/commonjs/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  45. package/dist/commonjs/parsers/manifest/smooth/create_parser.js +1 -4
  46. package/dist/commonjs/parsers/manifest/types.d.ts +1 -6
  47. package/dist/commonjs/parsers/manifest/types.d.ts.map +1 -1
  48. package/dist/commonjs/public_types.d.ts +69 -0
  49. package/dist/commonjs/public_types.d.ts.map +1 -1
  50. package/dist/commonjs/utils/are_codecs_compatible.d.ts.map +1 -1
  51. package/dist/commonjs/utils/are_codecs_compatible.js +5 -1
  52. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
  53. package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
  54. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  55. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  56. package/dist/es2017/compat/eme/custom_media_keys/types.d.ts +3 -0
  57. package/dist/es2017/compat/eme/custom_media_keys/types.d.ts.map +1 -1
  58. package/dist/es2017/compat/eme/custom_media_keys/webkit_media_keys.d.ts +1 -1
  59. package/dist/es2017/compat/eme/custom_media_keys/webkit_media_keys.d.ts.map +1 -1
  60. package/dist/es2017/compat/eme/eme-api-implementation.d.ts +2 -2
  61. package/dist/es2017/compat/eme/eme-api-implementation.d.ts.map +1 -1
  62. package/dist/es2017/compat/eme/eme-api-implementation.js +19 -2
  63. package/dist/es2017/compat/eme/get_init_data.d.ts +3 -1
  64. package/dist/es2017/compat/eme/get_init_data.d.ts.map +1 -1
  65. package/dist/es2017/compat/eme/get_init_data.js +2 -2
  66. package/dist/es2017/compat/event_listeners.d.ts +21 -19
  67. package/dist/es2017/compat/event_listeners.d.ts.map +1 -1
  68. package/dist/es2017/compat/event_listeners.js +0 -11
  69. package/dist/es2017/core/segment_sinks/inventory/segment_inventory.js +8 -5
  70. package/dist/es2017/core/segment_sinks/segment_buffers_store.d.ts.map +1 -1
  71. package/dist/es2017/core/segment_sinks/segment_buffers_store.js +5 -2
  72. package/dist/es2017/main_thread/api/public_api.d.ts +22 -10
  73. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  74. package/dist/es2017/main_thread/api/public_api.js +64 -24
  75. package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts +8 -0
  76. package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
  77. package/dist/es2017/main_thread/decrypt/content_decryptor.js +47 -2
  78. package/dist/es2017/main_thread/decrypt/types.d.ts +5 -0
  79. package/dist/es2017/main_thread/decrypt/types.d.ts.map +1 -1
  80. package/dist/es2017/main_thread/init/media_source_content_initializer.js +3 -3
  81. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +12 -4
  82. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
  83. package/dist/es2017/main_thread/tracks_store/tracks_store.js +18 -8
  84. package/dist/es2017/manifest/classes/adaptation.d.ts.map +1 -1
  85. package/dist/es2017/manifest/classes/adaptation.js +1 -1
  86. package/dist/es2017/manifest/classes/representation.js +8 -8
  87. package/dist/es2017/manifest/utils.d.ts +4 -3
  88. package/dist/es2017/manifest/utils.d.ts.map +1 -1
  89. package/dist/es2017/manifest/utils.js +13 -7
  90. package/dist/es2017/parsers/manifest/dash/common/content_protection_parser.js +4 -4
  91. package/dist/es2017/parsers/manifest/local/parse_local_manifest.js +1 -1
  92. package/dist/es2017/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  93. package/dist/es2017/parsers/manifest/smooth/create_parser.js +1 -4
  94. package/dist/es2017/parsers/manifest/types.d.ts +1 -6
  95. package/dist/es2017/parsers/manifest/types.d.ts.map +1 -1
  96. package/dist/es2017/public_types.d.ts +69 -0
  97. package/dist/es2017/public_types.d.ts.map +1 -1
  98. package/dist/es2017/utils/are_codecs_compatible.d.ts.map +1 -1
  99. package/dist/es2017/utils/are_codecs_compatible.js +5 -1
  100. package/dist/mpd-parser.wasm +0 -0
  101. package/dist/rx-player.js +18 -18
  102. package/dist/worker.js +5 -5
  103. package/package.json +2 -2
  104. package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
  105. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  106. package/src/compat/eme/custom_media_keys/types.ts +4 -0
  107. package/src/compat/eme/custom_media_keys/webkit_media_keys.ts +1 -1
  108. package/src/compat/eme/eme-api-implementation.ts +35 -4
  109. package/src/compat/eme/get_init_data.ts +5 -3
  110. package/src/compat/event_listeners.ts +30 -5
  111. package/src/core/segment_sinks/inventory/segment_inventory.ts +7 -4
  112. package/src/core/segment_sinks/segment_buffers_store.ts +5 -2
  113. package/src/main_thread/api/public_api.ts +96 -24
  114. package/src/main_thread/decrypt/content_decryptor.ts +60 -3
  115. package/src/main_thread/decrypt/types.ts +5 -0
  116. package/src/main_thread/init/media_source_content_initializer.ts +3 -3
  117. package/src/main_thread/tracks_store/tracks_store.ts +30 -6
  118. package/src/manifest/classes/adaptation.ts +1 -3
  119. package/src/manifest/classes/representation.ts +6 -6
  120. package/src/manifest/utils.ts +14 -6
  121. package/src/parsers/manifest/dash/common/content_protection_parser.ts +4 -4
  122. package/src/parsers/manifest/local/parse_local_manifest.ts +1 -1
  123. package/src/parsers/manifest/smooth/create_parser.ts +2 -6
  124. package/src/parsers/manifest/types.ts +1 -7
  125. package/src/public_types.ts +73 -0
  126. package/src/utils/are_codecs_compatible.ts +5 -1
  127. package/vitest.config.mjs +1 -0
@@ -60,3 +60,7 @@ export interface IMediaKeySessionEvents {
60
60
  // "keyerror"
61
61
  /* "error" */
62
62
  }
63
+
64
+ export interface ICustomMediaEncryptedEvent extends MediaEncryptedEvent {
65
+ forceSessionRecreation?: boolean | undefined;
66
+ }
@@ -30,7 +30,7 @@ import type { IWebKitMediaKeys } from "./webkit_media_keys_constructor";
30
30
  import { WebKitMediaKeysConstructor } from "./webkit_media_keys_constructor";
31
31
 
32
32
  export interface ICustomWebKitMediaKeys {
33
- _setVideo: (videoElement: IMediaElement) => void;
33
+ _setVideo: (videoElement: IMediaElement) => Promise<unknown>;
34
34
  createSession(mimeType: string, initData: Uint8Array): ICustomMediaKeySession;
35
35
  setServerCertificate(setServerCertificate: BufferSource): Promise<void>;
36
36
  }
@@ -3,6 +3,7 @@ import assert from "../../utils/assert";
3
3
  import globalScope from "../../utils/global_scope";
4
4
  import isNode from "../../utils/is_node";
5
5
  import isNullOrUndefined from "../../utils/is_null_or_undefined";
6
+ import objectAssign from "../../utils/object_assign";
6
7
  import type { CancellationSignal } from "../../utils/task_canceller";
7
8
  import type { IMediaElement } from "../browser_compatibility_types";
8
9
  import { isIE11 } from "../browser_detection";
@@ -19,7 +20,10 @@ import getMozMediaKeysCallbacks, {
19
20
  import getOldKitWebKitMediaKeyCallbacks, {
20
21
  isOldWebkitMediaElement,
21
22
  } from "./custom_media_keys/old_webkit_media_keys";
22
- import type { ICustomMediaKeys } from "./custom_media_keys/types";
23
+ import type {
24
+ ICustomMediaEncryptedEvent,
25
+ ICustomMediaKeys,
26
+ } from "./custom_media_keys/types";
23
27
  import getWebKitMediaKeysCallbacks from "./custom_media_keys/webkit_media_keys";
24
28
  import { WebKitMediaKeysConstructor } from "./custom_media_keys/webkit_media_keys_constructor";
25
29
 
@@ -63,7 +67,7 @@ export interface IEmeApiImplementation {
63
67
  */
64
68
  onEncrypted: (
65
69
  target: IEventTargetLike,
66
- listener: (evt: unknown) => void,
70
+ listener: (evt: ICustomMediaEncryptedEvent) => void,
67
71
  cancelSignal: CancellationSignal,
68
72
  ) => void;
69
73
 
@@ -143,8 +147,8 @@ function getEmeApiImplementation(
143
147
  let createCustomMediaKeys: (keyType: string) => ICustomMediaKeys;
144
148
 
145
149
  if (preferredApiType === "webkit" && WebKitMediaKeysConstructor !== undefined) {
146
- onEncrypted = createCompatibleEventListener(["needkey"]);
147
150
  const callbacks = getWebKitMediaKeysCallbacks();
151
+ onEncrypted = createOnEncryptedForWebkit();
148
152
  isTypeSupported = callbacks.isTypeSupported;
149
153
  createCustomMediaKeys = callbacks.createCustomMediaKeys;
150
154
  setMediaKeys = callbacks.setMediaKeys;
@@ -160,7 +164,7 @@ function getEmeApiImplementation(
160
164
  implementation = "older-webkit";
161
165
  // This is for WebKit with prefixed EME api
162
166
  } else if (WebKitMediaKeysConstructor !== undefined) {
163
- onEncrypted = createCompatibleEventListener(["needkey"]);
167
+ onEncrypted = createOnEncryptedForWebkit();
164
168
  const callbacks = getWebKitMediaKeysCallbacks();
165
169
  isTypeSupported = callbacks.isTypeSupported;
166
170
  createCustomMediaKeys = callbacks.createCustomMediaKeys;
@@ -274,6 +278,33 @@ function getEmeApiImplementation(
274
278
  implementation,
275
279
  };
276
280
  }
281
+ /**
282
+ * Create an event listener for the "webkitneedkey" event
283
+ * @returns
284
+ */
285
+ function createOnEncryptedForWebkit(): IEmeApiImplementation["onEncrypted"] {
286
+ const compatibleEventListener = createCompatibleEventListener(
287
+ ["needkey"],
288
+ undefined /* prefixes */,
289
+ );
290
+ const onEncrypted = (
291
+ target: IEventTargetLike,
292
+ listener: (event: ICustomMediaEncryptedEvent) => void,
293
+ cancelSignal: CancellationSignal,
294
+ ) => {
295
+ compatibleEventListener(
296
+ target,
297
+ (event?: Event) => {
298
+ const patchedEvent = objectAssign(event as MediaEncryptedEvent, {
299
+ forceSessionRecreation: true,
300
+ });
301
+ listener(patchedEvent);
302
+ },
303
+ cancelSignal,
304
+ );
305
+ };
306
+ return onEncrypted;
307
+ }
277
308
 
278
309
  /**
279
310
  * Set the given MediaKeys on the given HTMLMediaElement.
@@ -20,6 +20,7 @@ import areArraysOfNumbersEqual from "../../utils/are_arrays_of_numbers_equal";
20
20
  import { be4toi } from "../../utils/byte_parsing";
21
21
  import isNullOrUndefined from "../../utils/is_null_or_undefined";
22
22
  import { PSSH_TO_INTEGER } from "./constants";
23
+ import type { ICustomMediaEncryptedEvent } from "./custom_media_keys/types";
23
24
 
24
25
  /** Data recuperated from parsing the payload of an `encrypted` event. */
25
26
  export interface IEncryptedEventData {
@@ -50,6 +51,7 @@ export interface IEncryptedEventData {
50
51
  */
51
52
  data: Uint8Array;
52
53
  }>;
54
+ forceSessionRecreation?: boolean | undefined;
53
55
  }
54
56
 
55
57
  /**
@@ -145,9 +147,9 @@ function isPSSHAlreadyEncountered(
145
147
  * encountered in the given event.
146
148
  */
147
149
  export default function getInitData(
148
- encryptedEvent: MediaEncryptedEvent,
150
+ encryptedEvent: ICustomMediaEncryptedEvent,
149
151
  ): IEncryptedEventData | null {
150
- const { initData, initDataType } = encryptedEvent;
152
+ const { initData, initDataType, forceSessionRecreation } = encryptedEvent;
151
153
  if (isNullOrUndefined(initData)) {
152
154
  log.warn("Compat: No init data found on media encrypted event.");
153
155
  return null;
@@ -155,5 +157,5 @@ export default function getInitData(
155
157
 
156
158
  const initDataBytes = new Uint8Array(initData);
157
159
  const values = getInitializationDataValues(initDataBytes);
158
- return { type: initDataType, values };
160
+ return { type: initDataType, values, forceSessionRecreation };
159
161
  }
@@ -30,6 +30,7 @@ import type {
30
30
  IEventTarget,
31
31
  IMediaElement,
32
32
  } from "./browser_compatibility_types";
33
+ import type { ICustomMediaEncryptedEvent } from "./eme/custom_media_keys/types";
33
34
 
34
35
  const BROWSER_PREFIXES = ["", "webkit", "moz", "ms"];
35
36
 
@@ -82,8 +83,14 @@ function eventPrefixed(eventNames: string[], prefixes?: string[]): string[] {
82
83
  }
83
84
 
84
85
  export interface IEventEmitterLike {
85
- addEventListener: (eventName: string, handler: () => void) => void;
86
- removeEventListener: (eventName: string, handler: () => void) => void;
86
+ addEventListener: (
87
+ eventName: string,
88
+ handler: EventListenerOrEventListenerObject,
89
+ ) => void;
90
+ removeEventListener: (
91
+ eventName: string,
92
+ handler: EventListenerOrEventListenerObject,
93
+ ) => void;
87
94
  }
88
95
 
89
96
  export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter<unknown>;
@@ -99,12 +106,31 @@ export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter<u
99
106
  * @returns {Function} - Returns function allowing to easily add a callback to
100
107
  * be triggered when that event is emitted on a given event target.
101
108
  */
109
+
110
+ function createCompatibleEventListener(
111
+ eventNames: Array<"needkey" | "encrypted">,
112
+ prefixes?: string[],
113
+ ): (
114
+ element: IEventTargetLike,
115
+ listener: (event: ICustomMediaEncryptedEvent) => void,
116
+ cancelSignal: CancellationSignal,
117
+ ) => void;
118
+
102
119
  function createCompatibleEventListener(
103
120
  eventNames: string[],
104
121
  prefixes?: string[],
105
122
  ): (
106
123
  element: IEventTargetLike,
107
- listener: (event?: unknown) => void,
124
+ listener: (event?: Event) => void,
125
+ cancelSignal: CancellationSignal,
126
+ ) => void;
127
+
128
+ function createCompatibleEventListener(
129
+ eventNames: string[] | Array<"needkey" | "encrypted">,
130
+ prefixes?: string[],
131
+ ): (
132
+ element: IEventTargetLike,
133
+ listener: (event?: Event | MediaEncryptedEvent) => void,
108
134
  cancelSignal: CancellationSignal,
109
135
  ) => void {
110
136
  let mem: string | undefined;
@@ -112,13 +138,12 @@ function createCompatibleEventListener(
112
138
 
113
139
  return (
114
140
  element: IEventTargetLike,
115
- listener: (event?: unknown) => void,
141
+ listener: (event?: Event) => void,
116
142
  cancelSignal: CancellationSignal,
117
143
  ) => {
118
144
  if (cancelSignal.isCancelled()) {
119
145
  return;
120
146
  }
121
-
122
147
  // if the element is a HTMLElement we can detect
123
148
  // the supported event, and memoize it in `mem`
124
149
  if (typeof HTMLElement !== "undefined" && element instanceof HTMLElement) {
@@ -1349,11 +1349,14 @@ function prettyPrintInventory(inventory: IBufferedChunk[]): string {
1349
1349
  if (encounteredPeriod === undefined) {
1350
1350
  currentLetter = generateNewLetter(chunk.infos);
1351
1351
  encounteredReps[periodId] = { [representationId]: currentLetter };
1352
- } else if (encounteredPeriod[representationId] === undefined) {
1353
- currentLetter = generateNewLetter(chunk.infos);
1354
- encounteredPeriod[representationId] = currentLetter;
1355
1352
  } else {
1356
- currentLetter = encounteredPeriod[representationId];
1353
+ const previousLetter = encounteredPeriod[representationId];
1354
+ if (previousLetter === undefined) {
1355
+ currentLetter = generateNewLetter(chunk.infos);
1356
+ encounteredPeriod[representationId] = currentLetter;
1357
+ } else {
1358
+ currentLetter = previousLetter;
1359
+ }
1357
1360
  }
1358
1361
 
1359
1362
  if (lastChunk === null) {
@@ -18,6 +18,7 @@ import { MediaError } from "../../errors";
18
18
  import log from "../../log";
19
19
  import type { IMediaSourceInterface } from "../../mse";
20
20
  import { SourceBufferType } from "../../mse";
21
+ import assert from "../../utils/assert";
21
22
  import createCancellablePromise from "../../utils/create_cancellable_promise";
22
23
  import isNullOrUndefined from "../../utils/is_null_or_undefined";
23
24
  import type { CancellationSignal } from "../../utils/task_canceller";
@@ -251,7 +252,8 @@ export default class SegmentSinksStore {
251
252
  }
252
253
  this._initializedSegmentSinks[bufferType] = null;
253
254
  if (SegmentSinksStore.isNative(bufferType)) {
254
- this._onNativeBufferAddedOrDisabled.forEach((cb) => cb());
255
+ this._onNativeBufferAddedOrDisabled.slice().forEach((cb) => cb());
256
+ assert(this._onNativeBufferAddedOrDisabled.length === 0);
255
257
  }
256
258
  }
257
259
 
@@ -295,7 +297,8 @@ export default class SegmentSinksStore {
295
297
  this._mediaSource,
296
298
  );
297
299
  this._initializedSegmentSinks[bufferType] = nativeSegmentSink;
298
- this._onNativeBufferAddedOrDisabled.forEach((cb) => cb());
300
+ this._onNativeBufferAddedOrDisabled.slice().forEach((cb) => cb());
301
+ assert(this._onNativeBufferAddedOrDisabled.length === 0);
299
302
  return nativeSegmentSink;
300
303
  }
301
304
 
@@ -409,7 +409,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
409
409
  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
410
410
  videoElement.preload = "auto";
411
411
 
412
- this.version = /* PLAYER_VERSION */ "4.2.0-dev.2024081301";
412
+ this.version = /* PLAYER_VERSION */ "4.2.0-dev.2024082600";
413
413
  this.log = log;
414
414
  this.state = "STOPPED";
415
415
  this.videoElement = videoElement;
@@ -1889,10 +1889,18 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1889
1889
  /**
1890
1890
  * Returns every available audio tracks for a given Period - or the current
1891
1891
  * one if no `periodId` is given.
1892
- * @param {string|undefined} [periodId]
1892
+ * @param {string|Object|undefined} [arg]
1893
1893
  * @returns {Array.<Object>}
1894
1894
  */
1895
- getAvailableAudioTracks(periodId?: string | undefined): IAvailableAudioTrack[] {
1895
+ getAvailableAudioTracks(
1896
+ arg?:
1897
+ | string
1898
+ | undefined
1899
+ | {
1900
+ periodId: string;
1901
+ filterPlayableRepresentations: boolean;
1902
+ },
1903
+ ): IAvailableAudioTrack[] {
1896
1904
  if (this._priv_contentInfos === null) {
1897
1905
  return [];
1898
1906
  }
@@ -1900,10 +1908,20 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1900
1908
  if (isDirectFile) {
1901
1909
  return mediaElementTracksStore?.getAvailableAudioTracks() ?? [];
1902
1910
  }
1911
+
1912
+ let periodId: string | undefined;
1913
+ let filterPlayableRepresentations: boolean;
1914
+ if (typeof arg === "string") {
1915
+ periodId = arg;
1916
+ } else {
1917
+ periodId = arg?.periodId;
1918
+ filterPlayableRepresentations = arg?.filterPlayableRepresentations ?? true;
1919
+ }
1903
1920
  return this._priv_callTracksStoreGetterSetter(
1904
1921
  periodId,
1905
1922
  [],
1906
- (tcm, periodRef) => tcm.getAvailableAudioTracks(periodRef) ?? [],
1923
+ (tcm, periodRef) =>
1924
+ tcm.getAvailableAudioTracks(periodRef, filterPlayableRepresentations) ?? [],
1907
1925
  );
1908
1926
  }
1909
1927
 
@@ -1930,10 +1948,18 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1930
1948
 
1931
1949
  /**
1932
1950
  * Returns every available video tracks for the current Period.
1933
- * @param {string|undefined} [periodId]
1951
+ * @param {string|Object|undefined} [arg]
1934
1952
  * @returns {Array.<Object>}
1935
1953
  */
1936
- getAvailableVideoTracks(periodId?: string | undefined): IAvailableVideoTrack[] {
1954
+ getAvailableVideoTracks(
1955
+ arg?:
1956
+ | string
1957
+ | undefined
1958
+ | {
1959
+ periodId: string;
1960
+ filterPlayableRepresentations: boolean;
1961
+ },
1962
+ ): IAvailableVideoTrack[] {
1937
1963
  if (this._priv_contentInfos === null) {
1938
1964
  return [];
1939
1965
  }
@@ -1941,19 +1967,37 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1941
1967
  if (isDirectFile) {
1942
1968
  return mediaElementTracksStore?.getAvailableVideoTracks() ?? [];
1943
1969
  }
1970
+
1971
+ let periodId: string | undefined;
1972
+ let filterPlayableRepresentations: boolean;
1973
+ if (typeof arg === "string") {
1974
+ periodId = arg;
1975
+ } else {
1976
+ periodId = arg?.periodId;
1977
+ filterPlayableRepresentations = arg?.filterPlayableRepresentations ?? true;
1978
+ }
1944
1979
  return this._priv_callTracksStoreGetterSetter(
1945
1980
  periodId,
1946
1981
  [],
1947
- (tcm, periodRef) => tcm.getAvailableVideoTracks(periodRef) ?? [],
1982
+ (tcm, periodRef) =>
1983
+ tcm.getAvailableVideoTracks(periodRef, filterPlayableRepresentations) ?? [],
1948
1984
  );
1949
1985
  }
1950
1986
 
1951
1987
  /**
1952
1988
  * Returns currently chosen audio language for the current Period.
1953
- * @param {string|undefined} [periodId]
1989
+ * @param {string|Object|undefined} [arg]
1954
1990
  * @returns {Object|null|undefined}
1955
1991
  */
1956
- getAudioTrack(periodId?: string | undefined): IAudioTrack | null | undefined {
1992
+ getAudioTrack(
1993
+ arg?:
1994
+ | string
1995
+ | undefined
1996
+ | {
1997
+ periodId: string;
1998
+ filterPlayableRepresentations: boolean;
1999
+ },
2000
+ ): IAudioTrack | null | undefined {
1957
2001
  if (this._priv_contentInfos === null) {
1958
2002
  return undefined;
1959
2003
  }
@@ -1964,8 +2008,17 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1964
2008
  }
1965
2009
  return mediaElementTracksStore.getChosenAudioTrack();
1966
2010
  }
2011
+
2012
+ let periodId: string | undefined;
2013
+ let filterPlayableRepresentations: boolean;
2014
+ if (typeof arg === "string") {
2015
+ periodId = arg;
2016
+ } else {
2017
+ periodId = arg?.periodId;
2018
+ filterPlayableRepresentations = arg?.filterPlayableRepresentations ?? true;
2019
+ }
1967
2020
  return this._priv_callTracksStoreGetterSetter(periodId, undefined, (tcm, periodRef) =>
1968
- tcm.getChosenAudioTrack(periodRef),
2021
+ tcm.getChosenAudioTrack(periodRef, filterPlayableRepresentations),
1969
2022
  );
1970
2023
  }
1971
2024
 
@@ -1992,10 +2045,18 @@ class Player extends EventEmitter<IPublicAPIEvent> {
1992
2045
 
1993
2046
  /**
1994
2047
  * Returns currently chosen video track for the current Period.
1995
- * @param {string|undefined} [periodId]
2048
+ * @param {string|Object|undefined} [arg]
1996
2049
  * @returns {Object|null|undefined}
1997
2050
  */
1998
- getVideoTrack(periodId?: string | undefined): IVideoTrack | null | undefined {
2051
+ getVideoTrack(
2052
+ arg?:
2053
+ | string
2054
+ | undefined
2055
+ | {
2056
+ periodId: string;
2057
+ filterPlayableRepresentations: boolean;
2058
+ },
2059
+ ): IVideoTrack | null | undefined {
1999
2060
  if (this._priv_contentInfos === null) {
2000
2061
  return undefined;
2001
2062
  }
@@ -2006,8 +2067,17 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2006
2067
  }
2007
2068
  return mediaElementTracksStore.getChosenVideoTrack();
2008
2069
  }
2070
+
2071
+ let periodId: string | undefined;
2072
+ let filterPlayableRepresentations: boolean;
2073
+ if (typeof arg === "string") {
2074
+ periodId = arg;
2075
+ } else {
2076
+ periodId = arg?.periodId;
2077
+ filterPlayableRepresentations = arg?.filterPlayableRepresentations ?? true;
2078
+ }
2009
2079
  return this._priv_callTracksStoreGetterSetter(periodId, undefined, (tcm, periodRef) =>
2010
- tcm.getChosenVideoTrack(periodRef),
2080
+ tcm.getChosenVideoTrack(periodRef, filterPlayableRepresentations),
2011
2081
  );
2012
2082
  }
2013
2083
 
@@ -2612,10 +2682,12 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2612
2682
  }
2613
2683
  switch (elt.adaptation.type) {
2614
2684
  case "audio":
2615
- isCurrent = tStore.getChosenAudioTrack(periodRef)?.id === elt.adaptation.id;
2685
+ isCurrent =
2686
+ tStore.getChosenAudioTrack(periodRef, false)?.id === elt.adaptation.id;
2616
2687
  break;
2617
2688
  case "video":
2618
- isCurrent = tStore.getChosenVideoTrack(periodRef)?.id === elt.adaptation.id;
2689
+ isCurrent =
2690
+ tStore.getChosenVideoTrack(periodRef, false)?.id === elt.adaptation.id;
2619
2691
  break;
2620
2692
  case "text":
2621
2693
  isCurrent = tStore.getChosenTextTrack(periodRef)?.id === elt.adaptation.id;
@@ -2690,11 +2762,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2690
2762
  if (!isNullOrUndefined(tracksStore)) {
2691
2763
  const periodRef = tracksStore.getPeriodObjectFromPeriod(period);
2692
2764
  if (periodRef) {
2693
- const audioTrack = tracksStore.getChosenAudioTrack(periodRef);
2765
+ const audioTrack = tracksStore.getChosenAudioTrack(periodRef, true);
2694
2766
  this._priv_triggerEventIfNotStopped("audioTrackChange", audioTrack, cancelSignal);
2695
2767
  const textTrack = tracksStore.getChosenTextTrack(periodRef);
2696
2768
  this._priv_triggerEventIfNotStopped("textTrackChange", textTrack, cancelSignal);
2697
- const videoTrack = tracksStore.getChosenVideoTrack(periodRef);
2769
+ const videoTrack = tracksStore.getChosenVideoTrack(periodRef, true);
2698
2770
  this._priv_triggerEventIfNotStopped("videoTrackChange", videoTrack, cancelSignal);
2699
2771
  }
2700
2772
  } else {
@@ -2854,7 +2926,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2854
2926
  }
2855
2927
  switch (type) {
2856
2928
  case "audio":
2857
- const audioTrack = tracksStore.getChosenAudioTrack(periodRef);
2929
+ const audioTrack = tracksStore.getChosenAudioTrack(periodRef, true);
2858
2930
  this._priv_triggerEventIfNotStopped(
2859
2931
  "audioTrackChange",
2860
2932
  audioTrack,
@@ -2866,7 +2938,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
2866
2938
  this._priv_triggerEventIfNotStopped("textTrackChange", textTrack, cancelSignal);
2867
2939
  break;
2868
2940
  case "video":
2869
- const videoTrack = tracksStore.getChosenVideoTrack(periodRef);
2941
+ const videoTrack = tracksStore.getChosenVideoTrack(periodRef, true);
2870
2942
  this._priv_triggerEventIfNotStopped(
2871
2943
  "videoTrackChange",
2872
2944
  videoTrack,
@@ -3178,7 +3250,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3178
3250
  }
3179
3251
  switch (trackType) {
3180
3252
  case "video":
3181
- const videoTracks = tracksStore.getAvailableVideoTracks(periodRef);
3253
+ const videoTracks = tracksStore.getAvailableVideoTracks(periodRef, true);
3182
3254
  this._priv_triggerEventIfNotStopped(
3183
3255
  "availableVideoTracksChange",
3184
3256
  videoTracks ?? [],
@@ -3186,7 +3258,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3186
3258
  );
3187
3259
  break;
3188
3260
  case "audio":
3189
- const audioTracks = tracksStore.getAvailableAudioTracks(periodRef);
3261
+ const audioTracks = tracksStore.getAvailableAudioTracks(periodRef, true);
3190
3262
  this._priv_triggerEventIfNotStopped(
3191
3263
  "availableAudioTracksChange",
3192
3264
  audioTracks ?? [],
@@ -3235,7 +3307,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
3235
3307
  }
3236
3308
  }
3237
3309
  }
3238
- Player.version = /* PLAYER_VERSION */ "4.2.0-dev.2024081301";
3310
+ Player.version = /* PLAYER_VERSION */ "4.2.0-dev.2024082600";
3239
3311
 
3240
3312
  /** Every events sent by the RxPlayer's public API. */
3241
3313
  interface IPublicAPIEvent {
@@ -3244,8 +3316,8 @@ interface IPublicAPIEvent {
3244
3316
  audioTrackChange: IAudioTrack | null;
3245
3317
  textTrackChange: ITextTrack | null;
3246
3318
  videoTrackChange: IVideoTrack | null;
3247
- audioRepresentationChange: IVideoRepresentation | null;
3248
- videoRepresentationChange: IAudioRepresentation | null;
3319
+ audioRepresentationChange: IAudioRepresentation | null;
3320
+ videoRepresentationChange: IVideoRepresentation | null;
3249
3321
  volumeChange: {
3250
3322
  volume: number;
3251
3323
  muted: boolean;
@@ -177,7 +177,7 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
177
177
  mediaElement,
178
178
  (evt) => {
179
179
  log.debug("DRM: Encrypted event received from media element.");
180
- const initData = getInitData(evt as MediaEncryptedEvent);
180
+ const initData = getInitData(evt);
181
181
  if (initData !== null) {
182
182
  this.onInitializationData(initData);
183
183
  }
@@ -730,6 +730,21 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
730
730
  return false;
731
731
  }
732
732
 
733
+ /**
734
+ * On Safari using Directfile, the old EME implementation triggers
735
+ * the "webkitneedkey" event instead of "encrypted". There's an issue in Safari
736
+ * where "webkitneedkey" fires too early before all tracks are added from an HLS playlist.
737
+ * Safari incorrectly assumes some keys are missing for these tracks,
738
+ * leading to repeated "webkitneedkey" events. Because RxPlayer recognizes
739
+ * it already has a session for these keys and ignores the events,
740
+ * the content remains frozen. To resolve this, the session is re-created.
741
+ */
742
+ const forceSessionRecreation = initializationData.forceSessionRecreation;
743
+ if (forceSessionRecreation === true) {
744
+ this.removeSessionForInitData(initializationData, mediaKeysData);
745
+ return false;
746
+ }
747
+
733
748
  // Check if the compatible session is blacklisted
734
749
  const blacklistedSessionError = compatibleSessionInfo.blacklistedSessionError;
735
750
  if (!isNullOrUndefined(blacklistedSessionError)) {
@@ -834,6 +849,48 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
834
849
  return false;
835
850
  }
836
851
 
852
+ /**
853
+ * Remove the session corresponding to the initData provided, and close it.
854
+ * It does nothing if no session was found for this initData.
855
+ * @param {Object} initData : The initialization data corresponding to the session
856
+ * that need to be removed
857
+ * @param {Object} mediaKeysData : The media keys data
858
+ */
859
+ private removeSessionForInitData(
860
+ initData: IProcessedProtectionData,
861
+ mediaKeysData: IAttachedMediaKeysData,
862
+ ) {
863
+ const { stores } = mediaKeysData;
864
+ /** Remove the session and close it from the loadedSessionStore */
865
+ const entry = stores.loadedSessionsStore.reuse(initData);
866
+ if (entry !== null) {
867
+ stores.loadedSessionsStore
868
+ .closeSession(entry.mediaKeySession)
869
+ .catch(() =>
870
+ log.error("DRM: Cannot close the session from the loaded session store"),
871
+ );
872
+ }
873
+
874
+ /**
875
+ * If set, a currently-used key session is already compatible to this
876
+ * initialization data.
877
+ */
878
+ const compatibleSessionInfo = arrayFind(this._currentSessions, (x) =>
879
+ x.record.isCompatibleWith(initData),
880
+ );
881
+ if (compatibleSessionInfo === undefined) {
882
+ return;
883
+ }
884
+ /** Remove the session from the currentSessions */
885
+ const indexOf = this._currentSessions.indexOf(compatibleSessionInfo);
886
+ if (indexOf !== -1) {
887
+ log.debug(
888
+ "DRM: A session from a processed init is removed due to forceSessionRecreation policy.",
889
+ );
890
+ this._currentSessions.splice(indexOf, 1);
891
+ }
892
+ }
893
+
837
894
  /**
838
895
  * Callback that should be called if an error that made the current
839
896
  * `ContentDecryptor` instance unusable arised.
@@ -1147,8 +1204,8 @@ function addKeyIdsFromPeriod(set: Set<Uint8Array>, period: IPeriodMetadata) {
1147
1204
  representation.contentProtections !== undefined &&
1148
1205
  representation.contentProtections.keyIds !== undefined
1149
1206
  ) {
1150
- for (const kidInf of representation.contentProtections.keyIds) {
1151
- set.add(kidInf.keyId);
1207
+ for (const kid of representation.contentProtections.keyIds) {
1208
+ set.add(kid);
1152
1209
  }
1153
1210
  }
1154
1211
  }
@@ -127,6 +127,11 @@ export interface IProtectionData {
127
127
  /** Protection initialization data actually processed by the `ContentDecryptor`. */
128
128
  export interface IProcessedProtectionData extends Omit<IProtectionData, "values"> {
129
129
  values: InitDataValuesContainer;
130
+ /**
131
+ * Enforce to recreate the media key session if there is already a session created
132
+ * with this init data
133
+ */
134
+ forceSessionRecreation?: boolean | undefined;
130
135
  }
131
136
 
132
137
  /**
@@ -1267,17 +1267,17 @@ function updateKeyIdsDecipherabilityOnManifest(
1267
1267
  if (contentKIDs !== undefined) {
1268
1268
  for (const elt of contentKIDs) {
1269
1269
  for (const blacklistedKeyId of blacklistedKeyIds) {
1270
- if (areArraysOfNumbersEqual(blacklistedKeyId, elt.keyId)) {
1270
+ if (areArraysOfNumbersEqual(blacklistedKeyId, elt)) {
1271
1271
  return false;
1272
1272
  }
1273
1273
  }
1274
1274
  for (const whitelistedKeyId of whitelistedKeyIds) {
1275
- if (areArraysOfNumbersEqual(whitelistedKeyId, elt.keyId)) {
1275
+ if (areArraysOfNumbersEqual(whitelistedKeyId, elt)) {
1276
1276
  return true;
1277
1277
  }
1278
1278
  }
1279
1279
  for (const delistedKeyId of delistedKeyIds) {
1280
- if (areArraysOfNumbersEqual(delistedKeyId, elt.keyId)) {
1280
+ if (areArraysOfNumbersEqual(delistedKeyId, elt)) {
1281
1281
  return undefined;
1282
1282
  }
1283
1283
  }