rx-player 4.2.0-dev.2024081400 → 4.2.0-dev.2024090300

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 (137) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/VERSION +1 -1
  3. package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  4. package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
  5. package/dist/commonjs/compat/browser_detection.d.ts +3 -1
  6. package/dist/commonjs/compat/browser_detection.d.ts.map +1 -1
  7. package/dist/commonjs/compat/browser_detection.js +8 -1
  8. package/dist/commonjs/compat/can_reuse_media_keys.d.ts +2 -0
  9. package/dist/commonjs/compat/can_reuse_media_keys.d.ts.map +1 -1
  10. package/dist/commonjs/compat/can_reuse_media_keys.js +3 -1
  11. package/dist/commonjs/compat/eme/custom_media_keys/types.d.ts +3 -0
  12. package/dist/commonjs/compat/eme/custom_media_keys/types.d.ts.map +1 -1
  13. package/dist/commonjs/compat/eme/custom_media_keys/webkit_media_keys.d.ts +1 -1
  14. package/dist/commonjs/compat/eme/custom_media_keys/webkit_media_keys.d.ts.map +1 -1
  15. package/dist/commonjs/compat/eme/eme-api-implementation.d.ts +2 -2
  16. package/dist/commonjs/compat/eme/eme-api-implementation.d.ts.map +1 -1
  17. package/dist/commonjs/compat/eme/eme-api-implementation.js +19 -2
  18. package/dist/commonjs/compat/eme/get_init_data.d.ts +3 -1
  19. package/dist/commonjs/compat/eme/get_init_data.d.ts.map +1 -1
  20. package/dist/commonjs/compat/eme/get_init_data.js +2 -2
  21. package/dist/commonjs/compat/event_listeners.d.ts +21 -19
  22. package/dist/commonjs/compat/event_listeners.d.ts.map +1 -1
  23. package/dist/commonjs/compat/event_listeners.js +0 -11
  24. package/dist/commonjs/core/cmcd/cmcd_data_builder.js +1 -1
  25. package/dist/commonjs/core/main/worker/worker_main.js +1 -0
  26. package/dist/commonjs/core/segment_sinks/inventory/segment_inventory.js +8 -5
  27. package/dist/commonjs/main_thread/api/public_api.d.ts +22 -10
  28. package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
  29. package/dist/commonjs/main_thread/api/public_api.js +68 -28
  30. package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts +8 -0
  31. package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
  32. package/dist/commonjs/main_thread/decrypt/content_decryptor.js +51 -2
  33. package/dist/commonjs/main_thread/decrypt/types.d.ts +5 -0
  34. package/dist/commonjs/main_thread/decrypt/types.d.ts.map +1 -1
  35. package/dist/commonjs/main_thread/init/media_source_content_initializer.js +3 -3
  36. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +12 -4
  37. package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
  38. package/dist/commonjs/main_thread/tracks_store/tracks_store.js +18 -8
  39. package/dist/commonjs/manifest/classes/adaptation.d.ts +13 -13
  40. package/dist/commonjs/manifest/classes/adaptation.d.ts.map +1 -1
  41. package/dist/commonjs/manifest/classes/adaptation.js +1 -4
  42. package/dist/commonjs/manifest/classes/representation.d.ts +20 -12
  43. package/dist/commonjs/manifest/classes/representation.d.ts.map +1 -1
  44. package/dist/commonjs/manifest/classes/representation.js +8 -8
  45. package/dist/commonjs/manifest/utils.d.ts +4 -3
  46. package/dist/commonjs/manifest/utils.d.ts.map +1 -1
  47. package/dist/commonjs/manifest/utils.js +13 -7
  48. package/dist/commonjs/parsers/manifest/dash/common/content_protection_parser.js +4 -4
  49. package/dist/commonjs/parsers/manifest/local/parse_local_manifest.js +1 -1
  50. package/dist/commonjs/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  51. package/dist/commonjs/parsers/manifest/smooth/create_parser.js +1 -4
  52. package/dist/commonjs/parsers/manifest/types.d.ts +1 -6
  53. package/dist/commonjs/parsers/manifest/types.d.ts.map +1 -1
  54. package/dist/commonjs/public_types.d.ts +69 -0
  55. package/dist/commonjs/public_types.d.ts.map +1 -1
  56. package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
  57. package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
  58. package/dist/es2017/compat/browser_detection.d.ts +3 -1
  59. package/dist/es2017/compat/browser_detection.d.ts.map +1 -1
  60. package/dist/es2017/compat/browser_detection.js +7 -1
  61. package/dist/es2017/compat/can_reuse_media_keys.d.ts +2 -0
  62. package/dist/es2017/compat/can_reuse_media_keys.d.ts.map +1 -1
  63. package/dist/es2017/compat/can_reuse_media_keys.js +4 -2
  64. package/dist/es2017/compat/eme/custom_media_keys/types.d.ts +3 -0
  65. package/dist/es2017/compat/eme/custom_media_keys/types.d.ts.map +1 -1
  66. package/dist/es2017/compat/eme/custom_media_keys/webkit_media_keys.d.ts +1 -1
  67. package/dist/es2017/compat/eme/custom_media_keys/webkit_media_keys.d.ts.map +1 -1
  68. package/dist/es2017/compat/eme/eme-api-implementation.d.ts +2 -2
  69. package/dist/es2017/compat/eme/eme-api-implementation.d.ts.map +1 -1
  70. package/dist/es2017/compat/eme/eme-api-implementation.js +19 -2
  71. package/dist/es2017/compat/eme/get_init_data.d.ts +3 -1
  72. package/dist/es2017/compat/eme/get_init_data.d.ts.map +1 -1
  73. package/dist/es2017/compat/eme/get_init_data.js +2 -2
  74. package/dist/es2017/compat/event_listeners.d.ts +21 -19
  75. package/dist/es2017/compat/event_listeners.d.ts.map +1 -1
  76. package/dist/es2017/compat/event_listeners.js +0 -11
  77. package/dist/es2017/core/cmcd/cmcd_data_builder.js +1 -1
  78. package/dist/es2017/core/main/worker/worker_main.js +1 -0
  79. package/dist/es2017/core/segment_sinks/inventory/segment_inventory.js +8 -5
  80. package/dist/es2017/main_thread/api/public_api.d.ts +22 -10
  81. package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
  82. package/dist/es2017/main_thread/api/public_api.js +64 -24
  83. package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts +8 -0
  84. package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
  85. package/dist/es2017/main_thread/decrypt/content_decryptor.js +47 -2
  86. package/dist/es2017/main_thread/decrypt/types.d.ts +5 -0
  87. package/dist/es2017/main_thread/decrypt/types.d.ts.map +1 -1
  88. package/dist/es2017/main_thread/init/media_source_content_initializer.js +3 -3
  89. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +12 -4
  90. package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
  91. package/dist/es2017/main_thread/tracks_store/tracks_store.js +18 -8
  92. package/dist/es2017/manifest/classes/adaptation.d.ts +13 -13
  93. package/dist/es2017/manifest/classes/adaptation.d.ts.map +1 -1
  94. package/dist/es2017/manifest/classes/adaptation.js +1 -1
  95. package/dist/es2017/manifest/classes/representation.d.ts +20 -12
  96. package/dist/es2017/manifest/classes/representation.d.ts.map +1 -1
  97. package/dist/es2017/manifest/classes/representation.js +8 -8
  98. package/dist/es2017/manifest/utils.d.ts +4 -3
  99. package/dist/es2017/manifest/utils.d.ts.map +1 -1
  100. package/dist/es2017/manifest/utils.js +13 -7
  101. package/dist/es2017/parsers/manifest/dash/common/content_protection_parser.js +4 -4
  102. package/dist/es2017/parsers/manifest/local/parse_local_manifest.js +1 -1
  103. package/dist/es2017/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
  104. package/dist/es2017/parsers/manifest/smooth/create_parser.js +1 -4
  105. package/dist/es2017/parsers/manifest/types.d.ts +1 -6
  106. package/dist/es2017/parsers/manifest/types.d.ts.map +1 -1
  107. package/dist/es2017/public_types.d.ts +69 -0
  108. package/dist/es2017/public_types.d.ts.map +1 -1
  109. package/dist/rx-player.js +18 -18
  110. package/dist/worker.js +5 -5
  111. package/package.json +2 -2
  112. package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
  113. package/src/compat/__tests__/can_reuse_media_keys.test.ts +33 -2
  114. package/src/compat/browser_detection.ts +9 -0
  115. package/src/compat/can_reuse_media_keys.ts +4 -2
  116. package/src/compat/eme/custom_media_keys/types.ts +4 -0
  117. package/src/compat/eme/custom_media_keys/webkit_media_keys.ts +1 -1
  118. package/src/compat/eme/eme-api-implementation.ts +35 -4
  119. package/src/compat/eme/get_init_data.ts +5 -3
  120. package/src/compat/event_listeners.ts +30 -5
  121. package/src/core/cmcd/cmcd_data_builder.ts +1 -1
  122. package/src/core/main/worker/worker_main.ts +1 -0
  123. package/src/core/segment_sinks/inventory/segment_inventory.ts +7 -4
  124. package/src/main_thread/api/public_api.ts +96 -24
  125. package/src/main_thread/decrypt/content_decryptor.ts +60 -3
  126. package/src/main_thread/decrypt/types.ts +5 -0
  127. package/src/main_thread/init/media_source_content_initializer.ts +3 -3
  128. package/src/main_thread/tracks_store/tracks_store.ts +30 -6
  129. package/src/manifest/classes/adaptation.ts +14 -16
  130. package/src/manifest/classes/representation.ts +26 -18
  131. package/src/manifest/utils.ts +14 -6
  132. package/src/parsers/manifest/dash/common/content_protection_parser.ts +4 -4
  133. package/src/parsers/manifest/local/parse_local_manifest.ts +1 -1
  134. package/src/parsers/manifest/smooth/create_parser.ts +2 -6
  135. package/src/parsers/manifest/types.ts +1 -7
  136. package/src/public_types.ts +73 -0
  137. package/vitest.config.mjs +1 -0
@@ -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
  }
@@ -910,10 +910,16 @@ export default class TracksStore extends EventEmitter<ITracksStoreEvents> {
910
910
  * `null` if audio tracks were disabled and `undefined` if the Period is not
911
911
  * known.
912
912
  */
913
- public getChosenAudioTrack(periodObj: ITSPeriodObject): IAudioTrack | null {
913
+ public getChosenAudioTrack(
914
+ periodObj: ITSPeriodObject,
915
+ filterPlayableRepresentations: boolean,
916
+ ): IAudioTrack | null {
914
917
  return isNullOrUndefined(periodObj.audio.storedSettings)
915
918
  ? null
916
- : toAudioTrack(periodObj.audio.storedSettings.adaptation, true);
919
+ : toAudioTrack(
920
+ periodObj.audio.storedSettings.adaptation,
921
+ filterPlayableRepresentations,
922
+ );
917
923
  }
918
924
 
919
925
  /**
@@ -942,12 +948,18 @@ export default class TracksStore extends EventEmitter<ITracksStoreEvents> {
942
948
  * @param {Object} periodObj - The concerned Period's object
943
949
  * @returns {Object|null} - The video track chosen for this Period
944
950
  */
945
- public getChosenVideoTrack(periodObj: ITSPeriodObject): IVideoTrack | null {
951
+ public getChosenVideoTrack(
952
+ periodObj: ITSPeriodObject,
953
+ filterPlayableRepresentations: boolean,
954
+ ): IVideoTrack | null {
946
955
  if (isNullOrUndefined(periodObj.video.storedSettings)) {
947
956
  return null;
948
957
  }
949
958
 
950
- return toVideoTrack(periodObj.video.storedSettings.adaptation, true);
959
+ return toVideoTrack(
960
+ periodObj.video.storedSettings.adaptation,
961
+ filterPlayableRepresentations,
962
+ );
951
963
  }
952
964
 
953
965
  /**
@@ -957,10 +969,15 @@ export default class TracksStore extends EventEmitter<ITracksStoreEvents> {
957
969
  * Returns `undefined` if the given Period's id is not known.
958
970
  *
959
971
  * @param {Object} periodObj - The concerned Period's object
972
+ * @param {boolean} filterPlayableRepresentations - If `true`, only
973
+ * representations considered to be "playable" will be included in the
974
+ * returned response.
975
+ * If `false`, the response should contain all linked representations.
960
976
  * @returns {Array.<Object>}
961
977
  */
962
978
  public getAvailableAudioTracks(
963
979
  periodObj: ITSPeriodObject,
980
+ filterPlayableRepresentations: boolean,
964
981
  ): IAvailableAudioTrack[] | undefined {
965
982
  const storedSettings = periodObj.audio.storedSettings;
966
983
  const currentId = !isNullOrUndefined(storedSettings)
@@ -969,7 +986,9 @@ export default class TracksStore extends EventEmitter<ITracksStoreEvents> {
969
986
  const adaptations = getSupportedAdaptations(periodObj.period, "audio");
970
987
  return adaptations.map((adaptation: IAdaptationMetadata) => {
971
988
  const active = currentId === null ? false : currentId === adaptation.id;
972
- return objectAssign(toAudioTrack(adaptation, true), { active });
989
+ return objectAssign(toAudioTrack(adaptation, filterPlayableRepresentations), {
990
+ active,
991
+ });
973
992
  });
974
993
  }
975
994
 
@@ -1004,10 +1023,15 @@ export default class TracksStore extends EventEmitter<ITracksStoreEvents> {
1004
1023
  * Returns `undefined` if the given Period's id is not known.
1005
1024
  *
1006
1025
  * @param {Object} periodObj - The concerned Period's object
1026
+ * @param {boolean} filterPlayableRepresentations - If `true`, only
1027
+ * representations considered to be "playable" will be included in the
1028
+ * returned response.
1029
+ * If `false`, the response should contain all linked representations.
1007
1030
  * @returns {Array.<Object>}
1008
1031
  */
1009
1032
  public getAvailableVideoTracks(
1010
1033
  periodObj: ITSPeriodObject,
1034
+ filterPlayableRepresentations: boolean,
1011
1035
  ): IAvailableVideoTrack[] | undefined {
1012
1036
  const storedSettings = periodObj.video.storedSettings;
1013
1037
  const currentId = isNullOrUndefined(storedSettings)
@@ -1017,7 +1041,7 @@ export default class TracksStore extends EventEmitter<ITracksStoreEvents> {
1017
1041
  const adaptations = getSupportedAdaptations(periodObj.period, "video");
1018
1042
  return adaptations.map((adaptation) => {
1019
1043
  const active = currentId === null ? false : currentId === adaptation.id;
1020
- const track = toVideoTrack(adaptation, true);
1044
+ const track = toVideoTrack(adaptation, filterPlayableRepresentations);
1021
1045
  const trickModeTracks =
1022
1046
  track.trickModeTracks !== undefined
1023
1047
  ? track.trickModeTracks.map((trickModeAdaptation) => {
@@ -49,55 +49,55 @@ export default class Adaptation implements IAdaptationMetadata {
49
49
  */
50
50
  public manuallyAdded?: boolean;
51
51
  /**
52
- * @see IRepresentationMetadata
52
+ * @see IAdaptationMetadata.representations
53
53
  */
54
54
  public readonly representations: Representation[];
55
55
  /**
56
- * @see IRepresentationMetadata
56
+ * @see ITrackType
57
57
  */
58
58
  public readonly type: ITrackType;
59
59
  /**
60
- * @see IRepresentationMetadata
60
+ * @see IAdaptationMetadata.isAudioDescription
61
61
  */
62
62
  public isAudioDescription?: boolean;
63
63
  /**
64
- * @see IRepresentationMetadata
64
+ * @see IAdaptationMetadata.isClosedCaption
65
65
  */
66
66
  public isClosedCaption?: boolean;
67
67
  /**
68
- * @see IRepresentationMetadata
68
+ * @see IAdaptationMetadata.isForcedSubtitles
69
69
  */
70
70
  public isForcedSubtitles?: boolean;
71
71
  /**
72
- * @see IRepresentationMetadata
72
+ * @see IAdaptationMetadata.isSignInterpreted
73
73
  */
74
74
  public isSignInterpreted?: boolean;
75
75
  /**
76
- * @see IRepresentationMetadata
76
+ * @see IAdaptationMetadata.isDub
77
77
  */
78
78
  public isDub?: boolean;
79
79
  /**
80
- * @see IRepresentationMetadata
80
+ * @see IAdaptationMetadata.language
81
81
  */
82
82
  public language?: string;
83
83
  /**
84
- * @see IRepresentationMetadata
84
+ * @see IAdaptationMetadata.normalizedLanguage
85
85
  */
86
86
  public normalizedLanguage?: string;
87
87
  /**
88
- * @see IRepresentationMetadata
88
+ * @see IAdaptationSupportStatus
89
89
  */
90
90
  public supportStatus: IAdaptationSupportStatus;
91
91
  /**
92
- * @see IRepresentationMetadata
92
+ * @see IAdaptationMetadata.isTrickModeTrack
93
93
  */
94
94
  public isTrickModeTrack?: boolean;
95
95
  /**
96
- * @see IRepresentationMetadata
96
+ * @see IAdaptationMetadata.label
97
97
  */
98
98
  public label?: string;
99
99
  /**
100
- * @see IRepresentationMetadata
100
+ * @see IAdaptationMetadata.trickModeTracks
101
101
  */
102
102
  public readonly trickModeTracks?: Adaptation[];
103
103
 
@@ -180,9 +180,7 @@ export default class Adaptation implements IAdaptationMetadata {
180
180
  if (representation.contentProtections !== undefined) {
181
181
  reprObject.contentProtections = {};
182
182
  if (representation.contentProtections.keyIds !== undefined) {
183
- const keyIds = representation.contentProtections.keyIds.map(
184
- ({ keyId }) => keyId,
185
- );
183
+ const keyIds = representation.contentProtections.keyIds;
186
184
  reprObject.contentProtections.keyIds = keyIds;
187
185
  }
188
186
  }
@@ -37,15 +37,15 @@ class Representation implements IRepresentationMetadata {
37
37
  /** ID uniquely identifying the Representation in its parent Adaptation. */
38
38
  public readonly id: string;
39
39
  /**
40
- * @see IRepresentationMetadata
40
+ * @see IRepresentationMetadata.uniqueId
41
41
  */
42
42
  public readonly uniqueId: string;
43
43
  /**
44
- * @see IRepresentationMetadata
44
+ * @see IRepresentationMetadata.bitrate
45
45
  */
46
46
  public bitrate: number;
47
47
  /**
48
- * @see IRepresentationMetadata
48
+ * @see IRepresentationMetadata.frameRate
49
49
  */
50
50
  public frameRate?: number;
51
51
  /**
@@ -65,39 +65,47 @@ class Representation implements IRepresentationMetadata {
65
65
  */
66
66
  public cdnMetadata: ICdnMetadata[] | null;
67
67
  /**
68
- * @see IRepresentationMetadata
68
+ * @see IRepresentationMetadata.isSpatialAudio
69
69
  */
70
70
  public isSpatialAudio?: boolean | undefined;
71
71
  /**
72
- * @see IRepresentationMetadata
72
+ * @see IRepresentationMetadata.codecs
73
73
  */
74
74
  public codecs: string[];
75
75
  /**
76
- * @see IRepresentationMetadata
76
+ * @see IRepresentationMetadata.mimeType
77
77
  */
78
78
  public mimeType?: string;
79
79
  /**
80
- * @see IRepresentationMetadata
80
+ * @see IRepresentationMetadata.width
81
81
  */
82
82
  public width?: number;
83
83
  /**
84
- * @see IRepresentationMetadata
84
+ * @see IRepresentationMetadata.height
85
85
  */
86
86
  public height?: number;
87
87
  /**
88
- * @see IRepresentationMetadata
88
+ * @see IRepresentationMetadata.contentProtections
89
89
  */
90
90
  public contentProtections?: IContentProtections;
91
91
  /**
92
- * @see IRepresentationMetadata
92
+ * @see IRepresentationMetadata.hdrInfo
93
93
  */
94
94
  public hdrInfo?: IHDRInformation;
95
95
  /**
96
- * @see IRepresentationMetadata
96
+ * @see IRepresentationMetadata.decipherable
97
+ *
98
+ * Note that this property should __NEVER__ be updated directly on an
99
+ * instanciated `Representation`, you are supposed to rely on
100
+ * `Manifest` methods for this.
97
101
  */
98
102
  public decipherable?: boolean | undefined;
99
103
  /**
100
- * @see IRepresentationMetadata
104
+ * @see IRepresentationMetadata.isSupported
105
+ *
106
+ * Note that this property should __NEVER__ be updated directly on an
107
+ * instanciated `Representation`, you are supposed to rely on
108
+ * `Manifest` methods for this.
101
109
  */
102
110
  public isSupported: boolean | undefined;
103
111
  /**
@@ -281,7 +289,7 @@ class Representation implements IRepresentationMetadata {
281
289
  for (let j = 0; j < initData.values.length; j++) {
282
290
  if (initData.values[j].systemId.toLowerCase() === drmSystemId.toLowerCase()) {
283
291
  if (!createdObjForType) {
284
- const keyIds = this.contentProtections?.keyIds?.map((val) => val.keyId);
292
+ const keyIds = this.contentProtections?.keyIds;
285
293
  filtered.push({
286
294
  type: initData.type,
287
295
  keyIds,
@@ -330,7 +338,7 @@ class Representation implements IRepresentationMetadata {
330
338
  ) {
331
339
  return [];
332
340
  }
333
- const keyIds = this.contentProtections?.keyIds?.map((val) => val.keyId);
341
+ const keyIds = this.contentProtections?.keyIds;
334
342
  return this.contentProtections.initData.map((x) => {
335
343
  return { type: x.type, keyIds, values: x.values };
336
344
  });
@@ -363,7 +371,7 @@ class Representation implements IRepresentationMetadata {
363
371
  let hasUpdatedProtectionData = false;
364
372
  if (this.contentProtections === undefined) {
365
373
  this.contentProtections = {
366
- keyIds: keyId !== undefined ? [{ keyId }] : [],
374
+ keyIds: keyId !== undefined ? [keyId] : [],
367
375
  initData: [{ type: initDataType, values: data }],
368
376
  };
369
377
  return true;
@@ -372,17 +380,17 @@ class Representation implements IRepresentationMetadata {
372
380
  if (keyId !== undefined) {
373
381
  const keyIds = this.contentProtections.keyIds;
374
382
  if (keyIds === undefined) {
375
- this.contentProtections.keyIds = [{ keyId }];
383
+ this.contentProtections.keyIds = [keyId];
376
384
  } else {
377
385
  let foundKeyId = false;
378
386
  for (const knownKeyId of keyIds) {
379
- if (areArraysOfNumbersEqual(knownKeyId.keyId, keyId)) {
387
+ if (areArraysOfNumbersEqual(knownKeyId, keyId)) {
380
388
  foundKeyId = true;
381
389
  }
382
390
  }
383
391
  if (!foundKeyId) {
384
392
  log.warn("Manifest: found unanounced key id.");
385
- keyIds.push({ keyId });
393
+ keyIds.push(keyId);
386
394
  }
387
395
  }
388
396
  }
@@ -358,6 +358,7 @@ function toVideoRepresentation(
358
358
  hdrInfo,
359
359
  isSupported,
360
360
  decipherable,
361
+ contentProtections,
361
362
  } = representation;
362
363
  return {
363
364
  id,
@@ -369,6 +370,12 @@ function toVideoRepresentation(
369
370
  hdrInfo,
370
371
  isCodecSupported: isSupported,
371
372
  decipherable,
373
+ contentProtections:
374
+ contentProtections !== undefined
375
+ ? {
376
+ keyIds: contentProtections.keyIds,
377
+ }
378
+ : undefined,
372
379
  };
373
380
  }
374
381
 
@@ -407,9 +414,10 @@ export interface IDecipherabilityStatusChangedElement {
407
414
  * decipherability updated to `undefined`.
408
415
  *
409
416
  * @param {Object} manifest
410
- * @param {Array.<Uint8Array>} whitelistedKeyIds
411
- * @param {Array.<Uint8Array>} blacklistedKeyIds
412
- * @param {Array.<Uint8Array>} delistedKeyIds
417
+ * @param {Object} updates
418
+ * @param {Array.<Uint8Array>} updates.whitelistedKeyIds
419
+ * @param {Array.<Uint8Array>} updates.blacklistedKeyIds
420
+ * @param {Array.<Uint8Array>} updates.delistedKeyIds
413
421
  */
414
422
  export function updateDecipherabilityFromKeyIds(
415
423
  manifest: IManifestMetadata,
@@ -428,17 +436,17 @@ export function updateDecipherabilityFromKeyIds(
428
436
  if (contentKIDs !== undefined) {
429
437
  for (const elt of contentKIDs) {
430
438
  for (const blacklistedKeyId of blacklistedKeyIds) {
431
- if (areArraysOfNumbersEqual(blacklistedKeyId, elt.keyId)) {
439
+ if (areArraysOfNumbersEqual(blacklistedKeyId, elt)) {
432
440
  return false;
433
441
  }
434
442
  }
435
443
  for (const whitelistedKeyId of whitelistedKeyIds) {
436
- if (areArraysOfNumbersEqual(whitelistedKeyId, elt.keyId)) {
444
+ if (areArraysOfNumbersEqual(whitelistedKeyId, elt)) {
437
445
  return true;
438
446
  }
439
447
  }
440
448
  for (const delistedKeyId of delistedKeyIds) {
441
- if (areArraysOfNumbersEqual(delistedKeyId, elt.keyId)) {
449
+ if (areArraysOfNumbersEqual(delistedKeyId, elt)) {
442
450
  return undefined;
443
451
  }
444
452
  }
@@ -214,13 +214,13 @@ function parseContentProtection(
214
214
  contentProtectionIr.attributes.keyId !== undefined &&
215
215
  contentProtectionIr.attributes.keyId.length > 0
216
216
  ) {
217
- const kidObj = { keyId: contentProtectionIr.attributes.keyId, systemId };
217
+ const kid = contentProtectionIr.attributes.keyId;
218
218
  if (representation.contentProtections === undefined) {
219
- representation.contentProtections = { keyIds: [kidObj], initData: [] };
219
+ representation.contentProtections = { keyIds: [kid], initData: [] };
220
220
  } else if (representation.contentProtections.keyIds === undefined) {
221
- representation.contentProtections.keyIds = [kidObj];
221
+ representation.contentProtections.keyIds = [kid];
222
222
  } else {
223
- representation.contentProtections.keyIds.push(kidObj);
223
+ representation.contentProtections.keyIds.push(kid);
224
224
  }
225
225
  }
226
226
 
@@ -169,7 +169,7 @@ function parseRepresentation(
169
169
  function formatContentProtections(
170
170
  localContentProtections: ILocalContentProtections,
171
171
  ): IContentProtections {
172
- const keyIds = localContentProtections.keyIds;
172
+ const keyIds = localContentProtections.keyIds.map((k) => k.keyId);
173
173
  const initData: IContentProtectionInitData[] = Object.keys(
174
174
  localContentProtections.initData,
175
175
  ).map((currType) => {
@@ -27,7 +27,6 @@ import { getFilenameIndexInUrl } from "../../../utils/resolve_url";
27
27
  import { hexToBytes } from "../../../utils/string_parsing";
28
28
  import { createBox } from "../../containers/isobmff";
29
29
  import type {
30
- IContentProtectionKID,
31
30
  IParsedAdaptation,
32
31
  IParsedAdaptations,
33
32
  IParsedManifest,
@@ -371,15 +370,12 @@ function createSmoothStreamingParser(
371
370
  (!isNullOrUndefined(codecs) ? codecs + "-" : "") +
372
371
  String(qualityLevel.bitrate);
373
372
 
374
- const keyIDs: IContentProtectionKID[] = [];
373
+ const keyIDs: Uint8Array[] = [];
375
374
  let firstProtection: IContentProtectionSmooth | undefined;
376
375
  if (protections.length > 0) {
377
376
  firstProtection = protections[0];
378
377
  protections.forEach((protection) => {
379
- const keyId = protection.keyId;
380
- protection.keySystems.forEach((keySystem) => {
381
- keyIDs.push({ keyId, systemId: keySystem.systemId });
382
- });
378
+ keyIDs.push(protection.keyId);
383
379
  });
384
380
  }
385
381
 
@@ -39,12 +39,6 @@ export interface IParsedStreamEventData {
39
39
  };
40
40
  }
41
41
 
42
- /** Describes information about an encryption Key ID of a given media. */
43
- export interface IContentProtectionKID {
44
- keyId: Uint8Array;
45
- systemId?: string | undefined;
46
- }
47
-
48
42
  /**
49
43
  * Encryption initialization data.
50
44
  * This is the data used to initialize a license request.
@@ -85,7 +79,7 @@ export interface IContentProtections {
85
79
  * `undefined` if the key id(s) associated with that content may exist but are
86
80
  * not known.
87
81
  */
88
- keyIds: IContentProtectionKID[] | undefined;
82
+ keyIds: Uint8Array[] | undefined;
89
83
  /** The different encryption initialization data associated with that content. */
90
84
  initData: IContentProtectionInitData[];
91
85
  }
@@ -803,12 +803,50 @@ export interface IRepresentationContext {
803
803
  * RxPlayer.
804
804
  */
805
805
  export interface IAudioRepresentation {
806
+ /**
807
+ * Identifier for that Representation.
808
+ * Might e.g. be used with the `lockVideoRepresentation` API.
809
+ */
806
810
  id: string | number;
811
+ /** Optional maximum bitrate, in bits per seconds, for this Representation. */
807
812
  bitrate?: number | undefined;
813
+ /** Codec(s) relied on by the media segments of that Representation. */
808
814
  codec?: string | undefined;
815
+ /**
816
+ * If `true`, this Representation is linked to "spatial audio" technology, such as
817
+ * Dolby Atmos.
818
+ * If `false`, it is not linked to such technology.
819
+ *
820
+ * If `undefined`, we don't if it is linked to a spatial audio technology or not.
821
+ */
809
822
  isSpatialAudio?: boolean | undefined;
823
+ /**
824
+ * If `true`, the codec is known to be supported on the current device.
825
+ * If `false`, it is known to be unsupported.
826
+ *
827
+ * If `undefined`, we don't know yet if it is supported or not.
828
+ */
810
829
  isCodecSupported?: boolean | undefined;
830
+ /**
831
+ * If `true`, this Representation is known to be decipherable.
832
+ * If `false`, it is known to be encrypted and not decipherable.
833
+ *
834
+ * If `undefined`, we don't know yet if it is decipherable or not (or if it is
835
+ * encrypted or not in some cases).
836
+ */
811
837
  decipherable?: boolean | undefined;
838
+ /**
839
+ * Encryption information linked to this content.
840
+ * If set to an Object, the Representation is known to be encrypted.
841
+ * If unset or set to `undefined` the Representation is either unencrypted or
842
+ * we don't know if it is.
843
+ */
844
+ contentProtections?:
845
+ | {
846
+ /** Known key ids linked to that Representation. */
847
+ keyIds?: Uint8Array[] | undefined;
848
+ }
849
+ | undefined;
812
850
  }
813
851
 
814
852
  /** Audio track returned by the RxPlayer. */
@@ -847,15 +885,50 @@ export interface ITextTrack {
847
885
  * RxPlayer.
848
886
  */
849
887
  export interface IVideoRepresentation {
888
+ /**
889
+ * Identifier for that Representation.
890
+ * Might e.g. be used with the `lockVideoRepresentation` API.
891
+ */
850
892
  id: string;
893
+ /** Optional maximum bitrate, in bits per seconds, for this Representation. */
851
894
  bitrate?: number | undefined;
895
+ /** Defines the width of the Representation in pixels. */
852
896
  width?: number | undefined;
897
+ /** Defines the height of the Representation in pixels. */
853
898
  height?: number | undefined;
899
+ /** Codec(s) relied on by the media segments of that Representation. */
854
900
  codec?: string | undefined;
901
+ /** The frame rate for this Representation, in frame per seconds. */
855
902
  frameRate?: number | undefined;
903
+ /** If the track is HDR, gives the HDR characteristics of the content */
856
904
  hdrInfo?: IHDRInformation | undefined;
905
+ /**
906
+ * If `true`, the codec is known to be supported on the current device.
907
+ * If `false`, it is known to be unsupported.
908
+ *
909
+ * If `undefined`, we don't know yet if it is supported or not.
910
+ */
857
911
  isCodecSupported?: boolean | undefined;
912
+ /**
913
+ * If `true`, this Representation is known to be decipherable.
914
+ * If `false`, it is known to be encrypted and not decipherable.
915
+ *
916
+ * If `undefined`, we don't know yet if it is decipherable or not (or if it is
917
+ * encrypted or not in some cases).
918
+ */
858
919
  decipherable?: boolean | undefined;
920
+ /**
921
+ * Encryption information linked to this content.
922
+ * If set to an Object, the Representation is known to be encrypted.
923
+ * If unset or set to `undefined` the Representation is either unencrypted or
924
+ * we don't know if it is.
925
+ */
926
+ contentProtections?:
927
+ | {
928
+ /** Known key ids linked to that Representation. */
929
+ keyIds?: Uint8Array[] | undefined;
930
+ }
931
+ | undefined;
859
932
  }
860
933
 
861
934
  /** Video track returned by the RxPlayer. */
package/vitest.config.mjs CHANGED
@@ -91,6 +91,7 @@ export default defineConfig({
91
91
  globals: false,
92
92
  reporters: "dot",
93
93
  include: ["tests/**/*.[jt]s?(x)"],
94
+ exclude: ["tests/performance/**/*.[jt]s?(x)"],
94
95
  globalSetup: "tests/contents/server.mjs",
95
96
  browser: getBrowserConfig(process.env.BROWSER_CONFIG),
96
97
  },