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.
- package/CHANGELOG.md +38 -0
- package/VERSION +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/commonjs/compat/browser_detection.d.ts +3 -1
- package/dist/commonjs/compat/browser_detection.d.ts.map +1 -1
- package/dist/commonjs/compat/browser_detection.js +8 -1
- package/dist/commonjs/compat/can_reuse_media_keys.d.ts +2 -0
- package/dist/commonjs/compat/can_reuse_media_keys.d.ts.map +1 -1
- package/dist/commonjs/compat/can_reuse_media_keys.js +3 -1
- package/dist/commonjs/compat/eme/custom_media_keys/types.d.ts +3 -0
- package/dist/commonjs/compat/eme/custom_media_keys/types.d.ts.map +1 -1
- package/dist/commonjs/compat/eme/custom_media_keys/webkit_media_keys.d.ts +1 -1
- package/dist/commonjs/compat/eme/custom_media_keys/webkit_media_keys.d.ts.map +1 -1
- package/dist/commonjs/compat/eme/eme-api-implementation.d.ts +2 -2
- package/dist/commonjs/compat/eme/eme-api-implementation.d.ts.map +1 -1
- package/dist/commonjs/compat/eme/eme-api-implementation.js +19 -2
- package/dist/commonjs/compat/eme/get_init_data.d.ts +3 -1
- package/dist/commonjs/compat/eme/get_init_data.d.ts.map +1 -1
- package/dist/commonjs/compat/eme/get_init_data.js +2 -2
- package/dist/commonjs/compat/event_listeners.d.ts +21 -19
- package/dist/commonjs/compat/event_listeners.d.ts.map +1 -1
- package/dist/commonjs/compat/event_listeners.js +0 -11
- package/dist/commonjs/core/cmcd/cmcd_data_builder.js +1 -1
- package/dist/commonjs/core/main/worker/worker_main.js +1 -0
- package/dist/commonjs/core/segment_sinks/inventory/segment_inventory.js +8 -5
- package/dist/commonjs/main_thread/api/public_api.d.ts +22 -10
- package/dist/commonjs/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +68 -28
- package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts +8 -0
- package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/content_decryptor.js +51 -2
- package/dist/commonjs/main_thread/decrypt/types.d.ts +5 -0
- package/dist/commonjs/main_thread/decrypt/types.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/media_source_content_initializer.js +3 -3
- package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts +12 -4
- package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
- package/dist/commonjs/main_thread/tracks_store/tracks_store.js +18 -8
- package/dist/commonjs/manifest/classes/adaptation.d.ts +13 -13
- package/dist/commonjs/manifest/classes/adaptation.d.ts.map +1 -1
- package/dist/commonjs/manifest/classes/adaptation.js +1 -4
- package/dist/commonjs/manifest/classes/representation.d.ts +20 -12
- package/dist/commonjs/manifest/classes/representation.d.ts.map +1 -1
- package/dist/commonjs/manifest/classes/representation.js +8 -8
- package/dist/commonjs/manifest/utils.d.ts +4 -3
- package/dist/commonjs/manifest/utils.d.ts.map +1 -1
- package/dist/commonjs/manifest/utils.js +13 -7
- package/dist/commonjs/parsers/manifest/dash/common/content_protection_parser.js +4 -4
- package/dist/commonjs/parsers/manifest/local/parse_local_manifest.js +1 -1
- package/dist/commonjs/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
- package/dist/commonjs/parsers/manifest/smooth/create_parser.js +1 -4
- package/dist/commonjs/parsers/manifest/types.d.ts +1 -6
- package/dist/commonjs/parsers/manifest/types.d.ts.map +1 -1
- package/dist/commonjs/public_types.d.ts +69 -0
- package/dist/commonjs/public_types.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/es2017/compat/browser_detection.d.ts +3 -1
- package/dist/es2017/compat/browser_detection.d.ts.map +1 -1
- package/dist/es2017/compat/browser_detection.js +7 -1
- package/dist/es2017/compat/can_reuse_media_keys.d.ts +2 -0
- package/dist/es2017/compat/can_reuse_media_keys.d.ts.map +1 -1
- package/dist/es2017/compat/can_reuse_media_keys.js +4 -2
- package/dist/es2017/compat/eme/custom_media_keys/types.d.ts +3 -0
- package/dist/es2017/compat/eme/custom_media_keys/types.d.ts.map +1 -1
- package/dist/es2017/compat/eme/custom_media_keys/webkit_media_keys.d.ts +1 -1
- package/dist/es2017/compat/eme/custom_media_keys/webkit_media_keys.d.ts.map +1 -1
- package/dist/es2017/compat/eme/eme-api-implementation.d.ts +2 -2
- package/dist/es2017/compat/eme/eme-api-implementation.d.ts.map +1 -1
- package/dist/es2017/compat/eme/eme-api-implementation.js +19 -2
- package/dist/es2017/compat/eme/get_init_data.d.ts +3 -1
- package/dist/es2017/compat/eme/get_init_data.d.ts.map +1 -1
- package/dist/es2017/compat/eme/get_init_data.js +2 -2
- package/dist/es2017/compat/event_listeners.d.ts +21 -19
- package/dist/es2017/compat/event_listeners.d.ts.map +1 -1
- package/dist/es2017/compat/event_listeners.js +0 -11
- package/dist/es2017/core/cmcd/cmcd_data_builder.js +1 -1
- package/dist/es2017/core/main/worker/worker_main.js +1 -0
- package/dist/es2017/core/segment_sinks/inventory/segment_inventory.js +8 -5
- package/dist/es2017/main_thread/api/public_api.d.ts +22 -10
- package/dist/es2017/main_thread/api/public_api.d.ts.map +1 -1
- package/dist/es2017/main_thread/api/public_api.js +64 -24
- package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts +8 -0
- package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/content_decryptor.js +47 -2
- package/dist/es2017/main_thread/decrypt/types.d.ts +5 -0
- package/dist/es2017/main_thread/decrypt/types.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/media_source_content_initializer.js +3 -3
- package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts +12 -4
- package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
- package/dist/es2017/main_thread/tracks_store/tracks_store.js +18 -8
- package/dist/es2017/manifest/classes/adaptation.d.ts +13 -13
- package/dist/es2017/manifest/classes/adaptation.d.ts.map +1 -1
- package/dist/es2017/manifest/classes/adaptation.js +1 -1
- package/dist/es2017/manifest/classes/representation.d.ts +20 -12
- package/dist/es2017/manifest/classes/representation.d.ts.map +1 -1
- package/dist/es2017/manifest/classes/representation.js +8 -8
- package/dist/es2017/manifest/utils.d.ts +4 -3
- package/dist/es2017/manifest/utils.d.ts.map +1 -1
- package/dist/es2017/manifest/utils.js +13 -7
- package/dist/es2017/parsers/manifest/dash/common/content_protection_parser.js +4 -4
- package/dist/es2017/parsers/manifest/local/parse_local_manifest.js +1 -1
- package/dist/es2017/parsers/manifest/smooth/create_parser.d.ts.map +1 -1
- package/dist/es2017/parsers/manifest/smooth/create_parser.js +1 -4
- package/dist/es2017/parsers/manifest/types.d.ts +1 -6
- package/dist/es2017/parsers/manifest/types.d.ts.map +1 -1
- package/dist/es2017/public_types.d.ts +69 -0
- package/dist/es2017/public_types.d.ts.map +1 -1
- package/dist/rx-player.js +18 -18
- package/dist/worker.js +5 -5
- package/package.json +2 -2
- package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
- package/src/compat/__tests__/can_reuse_media_keys.test.ts +33 -2
- package/src/compat/browser_detection.ts +9 -0
- package/src/compat/can_reuse_media_keys.ts +4 -2
- package/src/compat/eme/custom_media_keys/types.ts +4 -0
- package/src/compat/eme/custom_media_keys/webkit_media_keys.ts +1 -1
- package/src/compat/eme/eme-api-implementation.ts +35 -4
- package/src/compat/eme/get_init_data.ts +5 -3
- package/src/compat/event_listeners.ts +30 -5
- package/src/core/cmcd/cmcd_data_builder.ts +1 -1
- package/src/core/main/worker/worker_main.ts +1 -0
- package/src/core/segment_sinks/inventory/segment_inventory.ts +7 -4
- package/src/main_thread/api/public_api.ts +96 -24
- package/src/main_thread/decrypt/content_decryptor.ts +60 -3
- package/src/main_thread/decrypt/types.ts +5 -0
- package/src/main_thread/init/media_source_content_initializer.ts +3 -3
- package/src/main_thread/tracks_store/tracks_store.ts +30 -6
- package/src/manifest/classes/adaptation.ts +14 -16
- package/src/manifest/classes/representation.ts +26 -18
- package/src/manifest/utils.ts +14 -6
- package/src/parsers/manifest/dash/common/content_protection_parser.ts +4 -4
- package/src/parsers/manifest/local/parse_local_manifest.ts +1 -1
- package/src/parsers/manifest/smooth/create_parser.ts +2 -6
- package/src/parsers/manifest/types.ts +1 -7
- package/src/public_types.ts +73 -0
- 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
|
|
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
|
|
1151
|
-
set.add(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
913
|
+
public getChosenAudioTrack(
|
|
914
|
+
periodObj: ITSPeriodObject,
|
|
915
|
+
filterPlayableRepresentations: boolean,
|
|
916
|
+
): IAudioTrack | null {
|
|
914
917
|
return isNullOrUndefined(periodObj.audio.storedSettings)
|
|
915
918
|
? null
|
|
916
|
-
: toAudioTrack(
|
|
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(
|
|
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(
|
|
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,
|
|
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,
|
|
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
|
|
52
|
+
* @see IAdaptationMetadata.representations
|
|
53
53
|
*/
|
|
54
54
|
public readonly representations: Representation[];
|
|
55
55
|
/**
|
|
56
|
-
* @see
|
|
56
|
+
* @see ITrackType
|
|
57
57
|
*/
|
|
58
58
|
public readonly type: ITrackType;
|
|
59
59
|
/**
|
|
60
|
-
* @see
|
|
60
|
+
* @see IAdaptationMetadata.isAudioDescription
|
|
61
61
|
*/
|
|
62
62
|
public isAudioDescription?: boolean;
|
|
63
63
|
/**
|
|
64
|
-
* @see
|
|
64
|
+
* @see IAdaptationMetadata.isClosedCaption
|
|
65
65
|
*/
|
|
66
66
|
public isClosedCaption?: boolean;
|
|
67
67
|
/**
|
|
68
|
-
* @see
|
|
68
|
+
* @see IAdaptationMetadata.isForcedSubtitles
|
|
69
69
|
*/
|
|
70
70
|
public isForcedSubtitles?: boolean;
|
|
71
71
|
/**
|
|
72
|
-
* @see
|
|
72
|
+
* @see IAdaptationMetadata.isSignInterpreted
|
|
73
73
|
*/
|
|
74
74
|
public isSignInterpreted?: boolean;
|
|
75
75
|
/**
|
|
76
|
-
* @see
|
|
76
|
+
* @see IAdaptationMetadata.isDub
|
|
77
77
|
*/
|
|
78
78
|
public isDub?: boolean;
|
|
79
79
|
/**
|
|
80
|
-
* @see
|
|
80
|
+
* @see IAdaptationMetadata.language
|
|
81
81
|
*/
|
|
82
82
|
public language?: string;
|
|
83
83
|
/**
|
|
84
|
-
* @see
|
|
84
|
+
* @see IAdaptationMetadata.normalizedLanguage
|
|
85
85
|
*/
|
|
86
86
|
public normalizedLanguage?: string;
|
|
87
87
|
/**
|
|
88
|
-
* @see
|
|
88
|
+
* @see IAdaptationSupportStatus
|
|
89
89
|
*/
|
|
90
90
|
public supportStatus: IAdaptationSupportStatus;
|
|
91
91
|
/**
|
|
92
|
-
* @see
|
|
92
|
+
* @see IAdaptationMetadata.isTrickModeTrack
|
|
93
93
|
*/
|
|
94
94
|
public isTrickModeTrack?: boolean;
|
|
95
95
|
/**
|
|
96
|
-
* @see
|
|
96
|
+
* @see IAdaptationMetadata.label
|
|
97
97
|
*/
|
|
98
98
|
public label?: string;
|
|
99
99
|
/**
|
|
100
|
-
* @see
|
|
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
|
|
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
|
|
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
|
|
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 ? [
|
|
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 = [
|
|
383
|
+
this.contentProtections.keyIds = [keyId];
|
|
376
384
|
} else {
|
|
377
385
|
let foundKeyId = false;
|
|
378
386
|
for (const knownKeyId of keyIds) {
|
|
379
|
-
if (areArraysOfNumbersEqual(knownKeyId
|
|
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(
|
|
393
|
+
keyIds.push(keyId);
|
|
386
394
|
}
|
|
387
395
|
}
|
|
388
396
|
}
|
package/src/manifest/utils.ts
CHANGED
|
@@ -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 {
|
|
411
|
-
* @param {Array.<Uint8Array>}
|
|
412
|
-
* @param {Array.<Uint8Array>}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
217
|
+
const kid = contentProtectionIr.attributes.keyId;
|
|
218
218
|
if (representation.contentProtections === undefined) {
|
|
219
|
-
representation.contentProtections = { keyIds: [
|
|
219
|
+
representation.contentProtections = { keyIds: [kid], initData: [] };
|
|
220
220
|
} else if (representation.contentProtections.keyIds === undefined) {
|
|
221
|
-
representation.contentProtections.keyIds = [
|
|
221
|
+
representation.contentProtections.keyIds = [kid];
|
|
222
222
|
} else {
|
|
223
|
-
representation.contentProtections.keyIds.push(
|
|
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:
|
|
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
|
-
|
|
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:
|
|
82
|
+
keyIds: Uint8Array[] | undefined;
|
|
89
83
|
/** The different encryption initialization data associated with that content. */
|
|
90
84
|
initData: IContentProtectionInitData[];
|
|
91
85
|
}
|
package/src/public_types.ts
CHANGED
|
@@ -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
|
},
|