rx-player 4.2.0-dev.2024100200 → 4.2.0
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/.vscode/settings.json +9 -0
- package/CHANGELOG.md +8 -1
- package/VERSION +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- package/dist/commonjs/core/segment_sinks/segment_buffers_store.d.ts.map +1 -1
- package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.d.ts.map +1 -1
- package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.js +85 -8
- package/dist/commonjs/main_thread/api/debug/modules/general_info.js +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +2 -2
- package/dist/commonjs/main_thread/decrypt/create_or_load_session.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/create_or_load_session.js +4 -1
- package/dist/commonjs/main_thread/decrypt/find_key_system.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/find_key_system.js +3 -0
- package/dist/commonjs/main_thread/decrypt/get_media_keys.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/get_media_keys.js +2 -1
- package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts +11 -1
- package/dist/commonjs/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/media_source_content_initializer.js +23 -11
- package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts +2 -1
- package/dist/commonjs/main_thread/init/multi_thread_content_initializer.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/multi_thread_content_initializer.js +9 -5
- package/dist/commonjs/main_thread/init/utils/initial_seek_and_play.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/utils/initial_seek_and_play.js +3 -7
- package/dist/commonjs/main_thread/text_displayer/native/native_text_displayer.d.ts +1 -0
- package/dist/commonjs/main_thread/text_displayer/native/native_text_displayer.d.ts.map +1 -1
- package/dist/commonjs/main_thread/text_displayer/native/native_text_displayer.js +19 -16
- package/dist/commonjs/public_types.d.ts +54 -2
- package/dist/commonjs/public_types.d.ts.map +1 -1
- package/dist/commonjs/utils/sync_or_async.d.ts.map +1 -1
- package/dist/commonjs/utils/sync_or_async.js +3 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.js +1 -1
- package/dist/es2017/core/segment_sinks/segment_buffers_store.d.ts.map +1 -1
- package/dist/es2017/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.d.ts.map +1 -1
- package/dist/es2017/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.js +36 -7
- package/dist/es2017/main_thread/api/debug/modules/general_info.js +1 -1
- package/dist/es2017/main_thread/api/public_api.js +2 -2
- package/dist/es2017/main_thread/decrypt/create_or_load_session.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/create_or_load_session.js +4 -1
- package/dist/es2017/main_thread/decrypt/find_key_system.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/find_key_system.js +3 -0
- package/dist/es2017/main_thread/decrypt/get_media_keys.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/get_media_keys.js +2 -1
- package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts +11 -1
- package/dist/es2017/main_thread/init/media_source_content_initializer.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/media_source_content_initializer.js +23 -11
- package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts +2 -1
- package/dist/es2017/main_thread/init/multi_thread_content_initializer.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/multi_thread_content_initializer.js +9 -5
- package/dist/es2017/main_thread/init/utils/initial_seek_and_play.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/utils/initial_seek_and_play.js +3 -7
- package/dist/es2017/main_thread/text_displayer/native/native_text_displayer.d.ts +1 -0
- package/dist/es2017/main_thread/text_displayer/native/native_text_displayer.d.ts.map +1 -1
- package/dist/es2017/main_thread/text_displayer/native/native_text_displayer.js +19 -16
- package/dist/es2017/public_types.d.ts +54 -2
- package/dist/es2017/public_types.d.ts.map +1 -1
- package/dist/es2017/utils/sync_or_async.d.ts.map +1 -1
- package/dist/es2017/utils/sync_or_async.js +3 -1
- package/dist/mpd-parser.wasm +0 -0
- package/dist/rx-player.js +60 -31
- package/dist/rx-player.min.js +17 -17
- package/package.json +1 -1
- package/src/__GENERATED_CODE/embedded_dash_wasm.ts +1 -1
- package/src/core/segment_sinks/segment_buffers_store.ts +6 -2
- package/src/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.ts +39 -7
- package/src/main_thread/api/debug/modules/general_info.ts +1 -1
- package/src/main_thread/api/public_api.ts +2 -2
- package/src/main_thread/decrypt/create_or_load_session.ts +6 -1
- package/src/main_thread/decrypt/find_key_system.ts +3 -0
- package/src/main_thread/decrypt/get_media_keys.ts +1 -0
- package/src/main_thread/init/media_source_content_initializer.ts +50 -17
- package/src/main_thread/init/multi_thread_content_initializer.ts +15 -7
- package/src/main_thread/init/utils/initial_seek_and_play.ts +3 -7
- package/src/main_thread/text_displayer/native/native_text_displayer.ts +22 -18
- package/src/public_types.ts +54 -3
- package/src/utils/sync_or_async.ts +5 -3
|
@@ -103,7 +103,11 @@ export default class SegmentSinksStore {
|
|
|
103
103
|
* disabled. This means that the corresponding type (e.g. audio, video etc.)
|
|
104
104
|
* won't be needed when playing the current content.
|
|
105
105
|
*/
|
|
106
|
-
private _initializedSegmentSinks:
|
|
106
|
+
private _initializedSegmentSinks: {
|
|
107
|
+
audio?: AudioVideoSegmentSink | undefined | null;
|
|
108
|
+
video?: AudioVideoSegmentSink | undefined | null;
|
|
109
|
+
text?: TextSegmentSink | null;
|
|
110
|
+
};
|
|
107
111
|
|
|
108
112
|
/**
|
|
109
113
|
* Callbacks called after a SourceBuffer is either created or disabled.
|
|
@@ -308,7 +312,7 @@ export default class SegmentSinksStore {
|
|
|
308
312
|
return memorizedSegmentSink;
|
|
309
313
|
}
|
|
310
314
|
|
|
311
|
-
let segmentSink:
|
|
315
|
+
let segmentSink: TextSegmentSink;
|
|
312
316
|
if (bufferType === "text") {
|
|
313
317
|
log.info("SB: Creating a new text SegmentSink");
|
|
314
318
|
if (this._textInterface === null) {
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { canRelyOnRequestMediaKeySystemAccess } from "../../../../compat/can_rely_on_request_media_key_system_access";
|
|
17
18
|
import eme from "../../../../compat/eme";
|
|
19
|
+
import {
|
|
20
|
+
DUMMY_PLAY_READY_HEADER,
|
|
21
|
+
generatePlayReadyInitData,
|
|
22
|
+
} from "../../../../compat/generate_init_data";
|
|
18
23
|
import isNullOrUndefined from "../../../../utils/is_null_or_undefined";
|
|
19
24
|
import log from "../log";
|
|
20
25
|
import type { ICompatibleKeySystem, IMediaConfiguration } from "../types";
|
|
@@ -57,13 +62,40 @@ export default function probeDRMInfos(
|
|
|
57
62
|
|
|
58
63
|
return eme
|
|
59
64
|
.requestMediaKeySystemAccess(type, [configuration])
|
|
60
|
-
.then((keySystemAccess) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
.then(async (keySystemAccess) => {
|
|
66
|
+
if (!canRelyOnRequestMediaKeySystemAccess(type)) {
|
|
67
|
+
try {
|
|
68
|
+
const mediaKeys = await keySystemAccess.createMediaKeys();
|
|
69
|
+
const session = mediaKeys.createSession();
|
|
70
|
+
const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
|
|
71
|
+
await session.generateRequest("cenc", initData);
|
|
72
|
+
session.close().catch(() => {
|
|
73
|
+
log.warn("DRM: Failed to close the dummy session");
|
|
74
|
+
});
|
|
75
|
+
result.compatibleConfiguration = keySystemAccess.getConfiguration();
|
|
76
|
+
const status: [ProberStatus, ICompatibleKeySystem?] = [
|
|
77
|
+
ProberStatus.Supported,
|
|
78
|
+
result,
|
|
79
|
+
];
|
|
80
|
+
return status;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
log.debug("DRM: KeySystemAccess was granted but it is not usable");
|
|
83
|
+
|
|
84
|
+
const statusError: [ProberStatus, ICompatibleKeySystem] = [
|
|
85
|
+
ProberStatus.NotSupported,
|
|
86
|
+
result,
|
|
87
|
+
];
|
|
88
|
+
return statusError;
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
result.compatibleConfiguration = keySystemAccess.getConfiguration();
|
|
92
|
+
|
|
93
|
+
const status: [ProberStatus, ICompatibleKeySystem?] = [
|
|
94
|
+
ProberStatus.Supported,
|
|
95
|
+
result,
|
|
96
|
+
];
|
|
97
|
+
return status;
|
|
98
|
+
}
|
|
67
99
|
})
|
|
68
100
|
.catch(() => {
|
|
69
101
|
return [ProberStatus.NotSupported, result];
|
|
@@ -65,7 +65,7 @@ export default function constructDebugGeneralInfo(
|
|
|
65
65
|
valuesLine1.push(["wo", "0"]);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const valuesLine2: Array<[string, string]> = [];
|
|
68
|
+
const valuesLine2: Array<[string, string]> = [["v", instance.version]];
|
|
69
69
|
const ks = instance.getKeySystemConfiguration();
|
|
70
70
|
if (ks !== null) {
|
|
71
71
|
valuesLine2.push(["ks", ks.keySystem]);
|
|
@@ -411,7 +411,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
411
411
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
|
|
412
412
|
videoElement.preload = "auto";
|
|
413
413
|
|
|
414
|
-
this.version = /* PLAYER_VERSION */ "4.2.0
|
|
414
|
+
this.version = /* PLAYER_VERSION */ "4.2.0";
|
|
415
415
|
this.log = log;
|
|
416
416
|
this.state = "STOPPED";
|
|
417
417
|
this.videoElement = videoElement;
|
|
@@ -3330,7 +3330,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
3330
3330
|
}
|
|
3331
3331
|
}
|
|
3332
3332
|
}
|
|
3333
|
-
Player.version = /* PLAYER_VERSION */ "4.2.0
|
|
3333
|
+
Player.version = /* PLAYER_VERSION */ "4.2.0";
|
|
3334
3334
|
|
|
3335
3335
|
/** Every events sent by the RxPlayer's public API. */
|
|
3336
3336
|
interface IPublicAPIEvent {
|
|
@@ -82,7 +82,12 @@ export default async function createOrLoadSession(
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
await cleanOldLoadedSessions(
|
|
85
|
+
await cleanOldLoadedSessions(
|
|
86
|
+
loadedSessionsStore,
|
|
87
|
+
// Account for the next session we will be creating
|
|
88
|
+
// Note that `maxSessionCacheSize < 0 has special semantic (no limit)`
|
|
89
|
+
maxSessionCacheSize <= 0 ? maxSessionCacheSize : maxSessionCacheSize - 1,
|
|
90
|
+
);
|
|
86
91
|
if (cancelSignal.cancellationError !== null) {
|
|
87
92
|
throw cancelSignal.cancellationError; // stop here if cancelled since
|
|
88
93
|
}
|
|
@@ -539,6 +539,9 @@ export async function testKeySystem(
|
|
|
539
539
|
const session = mediaKeys.createSession();
|
|
540
540
|
const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
|
|
541
541
|
await session.generateRequest("cenc", initData);
|
|
542
|
+
session.close().catch(() => {
|
|
543
|
+
log.warn("DRM: Failed to close the dummy session");
|
|
544
|
+
});
|
|
542
545
|
} catch (err) {
|
|
543
546
|
log.debug("DRM: KeySystemAccess was granted but it is not usable");
|
|
544
547
|
throw err;
|
|
@@ -98,6 +98,7 @@ export default async function getMediaKeysInfos(
|
|
|
98
98
|
const persistentSessionsStore = createPersistentSessionsStorage(options);
|
|
99
99
|
|
|
100
100
|
if (
|
|
101
|
+
evt.value.options.reuseMediaKeys !== false &&
|
|
101
102
|
canReuseMediaKeys() &&
|
|
102
103
|
currentState !== null &&
|
|
103
104
|
evt.type === "reuse-media-key-system-access"
|
|
@@ -92,7 +92,7 @@ import listenToMediaError from "./utils/throw_on_media_error";
|
|
|
92
92
|
*/
|
|
93
93
|
export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
94
94
|
/** Constructor settings associated to this `MediaSourceContentInitializer`. */
|
|
95
|
-
private
|
|
95
|
+
private _initSettings: IInitializeArguments;
|
|
96
96
|
/**
|
|
97
97
|
* `TaskCanceller` allowing to abort everything that the
|
|
98
98
|
* `MediaSourceContentInitializer` is doing.
|
|
@@ -145,7 +145,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
145
145
|
*/
|
|
146
146
|
constructor(settings: IInitializeArguments) {
|
|
147
147
|
super();
|
|
148
|
-
this.
|
|
148
|
+
this._initSettings = settings;
|
|
149
149
|
this._initCanceller = new TaskCanceller();
|
|
150
150
|
this._manifest = null;
|
|
151
151
|
this._decryptionCapabilities = { status: "uninitialized", value: null };
|
|
@@ -234,6 +234,10 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
234
234
|
this._initCanceller.cancel();
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Callback called when an error interrupting playback arised.
|
|
239
|
+
* @param {*} err
|
|
240
|
+
*/
|
|
237
241
|
private _onFatalError(err: unknown) {
|
|
238
242
|
if (this._initCanceller.isUsed()) {
|
|
239
243
|
return;
|
|
@@ -242,6 +246,12 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
242
246
|
this.trigger("error", err);
|
|
243
247
|
}
|
|
244
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Initialize decryption mechanisms if needed and begin creating and relying
|
|
251
|
+
* on the initial `MediaSourceInterface` for this content.
|
|
252
|
+
* @param {HTMLMediaElement|null} mediaElement
|
|
253
|
+
* @returns {Promise.<Object>}
|
|
254
|
+
*/
|
|
245
255
|
private _initializeMediaSourceAndDecryption(mediaElement: IMediaElement): Promise<{
|
|
246
256
|
mediaSource: MainMediaSourceInterface;
|
|
247
257
|
drmSystemId: string | undefined;
|
|
@@ -249,7 +259,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
249
259
|
}> {
|
|
250
260
|
const initCanceller = this._initCanceller;
|
|
251
261
|
return createCancellablePromise(initCanceller.signal, (resolve) => {
|
|
252
|
-
const { keySystems } = this.
|
|
262
|
+
const { keySystems } = this._initSettings;
|
|
253
263
|
|
|
254
264
|
/** Initialize decryption capabilities. */
|
|
255
265
|
const { statusRef: drmInitRef, contentDecryptor } = initializeContentDecryption(
|
|
@@ -383,7 +393,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
383
393
|
startAt,
|
|
384
394
|
textTrackOptions,
|
|
385
395
|
transport,
|
|
386
|
-
} = this.
|
|
396
|
+
} = this._initSettings;
|
|
387
397
|
const initCanceller = this._initCanceller;
|
|
388
398
|
assert(this._manifest !== null);
|
|
389
399
|
let manifest: IManifest;
|
|
@@ -526,7 +536,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
526
536
|
*/
|
|
527
537
|
private _startBufferingOnMediaSource(
|
|
528
538
|
args: IBufferingMediaSettings,
|
|
529
|
-
onReloadOrder:
|
|
539
|
+
onReloadOrder: IReloadMediaSourceCallback,
|
|
530
540
|
cancelSignal: CancellationSignal,
|
|
531
541
|
): void {
|
|
532
542
|
const {
|
|
@@ -553,18 +563,10 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
553
563
|
}
|
|
554
564
|
|
|
555
565
|
let textDisplayerInterface: ITextDisplayerInterface | null = null;
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
this.
|
|
559
|
-
|
|
560
|
-
) {
|
|
561
|
-
textDisplayer = new features.htmlTextDisplayer(
|
|
562
|
-
mediaElement,
|
|
563
|
-
this._settings.textTrackOptions.textTrackElement,
|
|
564
|
-
);
|
|
565
|
-
} else if (features.nativeTextDisplayer !== null) {
|
|
566
|
-
textDisplayer = new features.nativeTextDisplayer(mediaElement);
|
|
567
|
-
}
|
|
566
|
+
const textDisplayer = createTextDisplayer(
|
|
567
|
+
mediaElement,
|
|
568
|
+
this._initSettings.textTrackOptions,
|
|
569
|
+
);
|
|
568
570
|
if (textDisplayer !== null) {
|
|
569
571
|
const sender = new MainThreadTextDisplayerInterface(textDisplayer);
|
|
570
572
|
textDisplayerInterface = sender;
|
|
@@ -1123,6 +1125,21 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
|
1123
1125
|
}
|
|
1124
1126
|
}
|
|
1125
1127
|
|
|
1128
|
+
function createTextDisplayer(
|
|
1129
|
+
mediaElement: IMediaElement,
|
|
1130
|
+
textTrackOptions: ITextDisplayerOptions,
|
|
1131
|
+
): ITextDisplayer | null {
|
|
1132
|
+
if (textTrackOptions.textTrackMode === "html" && features.htmlTextDisplayer !== null) {
|
|
1133
|
+
return new features.htmlTextDisplayer(
|
|
1134
|
+
mediaElement,
|
|
1135
|
+
textTrackOptions.textTrackElement,
|
|
1136
|
+
);
|
|
1137
|
+
} else if (features.nativeTextDisplayer !== null) {
|
|
1138
|
+
return new features.nativeTextDisplayer(mediaElement);
|
|
1139
|
+
}
|
|
1140
|
+
return null;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1126
1143
|
/** Arguments to give to the `InitializeOnMediaSource` function. */
|
|
1127
1144
|
export interface IInitializeArguments {
|
|
1128
1145
|
/** Options concerning the ABR logic. */
|
|
@@ -1324,3 +1341,19 @@ function blackListProtectionDataOnManifest(
|
|
|
1324
1341
|
return rep.decipherable;
|
|
1325
1342
|
});
|
|
1326
1343
|
}
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Function to call when you want to "reload" the MediaSource: basically
|
|
1347
|
+
* restarting playback on a new MediaSource for the same content (it may
|
|
1348
|
+
* be for varied reasons, such as ensuring data buffers are empty, or
|
|
1349
|
+
* restarting after some kind of fatal error).
|
|
1350
|
+
* @param {Object} reloadOrder
|
|
1351
|
+
* @param {number} reloadOrder.position - Position in seconds at which we
|
|
1352
|
+
* should restart from when playback restarts.
|
|
1353
|
+
* @param {boolean} reloadOrder.autoPlay - If `true` we will directly play
|
|
1354
|
+
* once enough data is re-loaded.
|
|
1355
|
+
*/
|
|
1356
|
+
type IReloadMediaSourceCallback = (reloadOrder: {
|
|
1357
|
+
position: number;
|
|
1358
|
+
autoPlay: boolean;
|
|
1359
|
+
}) => void;
|
|
@@ -113,12 +113,13 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
|
|
|
113
113
|
private _currentMediaSourceCanceller: TaskCanceller;
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
* Stores the resolvers and the current messageId that is sent to the web worker to
|
|
116
|
+
* Stores the resolvers and the current messageId that is sent to the web worker to
|
|
117
|
+
* receive segment sink metrics.
|
|
117
118
|
* The purpose of collecting metrics is for monitoring and debugging.
|
|
118
119
|
*/
|
|
119
120
|
private _segmentMetrics: {
|
|
120
121
|
lastMessageId: number;
|
|
121
|
-
resolvers:
|
|
122
|
+
resolvers: Map<number, (value: ISegmentSinkMetrics | undefined) => void>;
|
|
122
123
|
};
|
|
123
124
|
|
|
124
125
|
/**
|
|
@@ -135,7 +136,7 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
|
|
|
135
136
|
this._currentContentInfo = null;
|
|
136
137
|
this._segmentMetrics = {
|
|
137
138
|
lastMessageId: 0,
|
|
138
|
-
resolvers:
|
|
139
|
+
resolvers: new Map(),
|
|
139
140
|
};
|
|
140
141
|
this._queuedWorkerMessages = null;
|
|
141
142
|
}
|
|
@@ -1121,10 +1122,9 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
|
|
|
1121
1122
|
if (this._currentContentInfo?.contentId !== msgData.contentId) {
|
|
1122
1123
|
return;
|
|
1123
1124
|
}
|
|
1124
|
-
const resolveFn = this._segmentMetrics.resolvers
|
|
1125
|
+
const resolveFn = this._segmentMetrics.resolvers.get(msgData.value.messageId);
|
|
1125
1126
|
if (resolveFn !== undefined) {
|
|
1126
1127
|
resolveFn(msgData.value.segmentSinkMetrics);
|
|
1127
|
-
delete this._segmentMetrics.resolvers[msgData.value.messageId];
|
|
1128
1128
|
} else {
|
|
1129
1129
|
log.error("MTCI: Failed to send segment sink store update");
|
|
1130
1130
|
}
|
|
@@ -1589,11 +1589,19 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
|
|
|
1589
1589
|
value: { messageId },
|
|
1590
1590
|
});
|
|
1591
1591
|
return new Promise((resolve, reject) => {
|
|
1592
|
-
this._segmentMetrics.resolvers[messageId] = resolve;
|
|
1593
1592
|
const rejectFn = (err: CancellationError) => {
|
|
1594
|
-
|
|
1593
|
+
cancelSignal.deregister(rejectFn);
|
|
1594
|
+
this._segmentMetrics.resolvers.delete(messageId);
|
|
1595
1595
|
return reject(err);
|
|
1596
1596
|
};
|
|
1597
|
+
this._segmentMetrics.resolvers.set(
|
|
1598
|
+
messageId,
|
|
1599
|
+
(value: ISegmentSinkMetrics | undefined) => {
|
|
1600
|
+
cancelSignal.deregister(rejectFn);
|
|
1601
|
+
this._segmentMetrics.resolvers.delete(messageId);
|
|
1602
|
+
resolve(value);
|
|
1603
|
+
},
|
|
1604
|
+
);
|
|
1597
1605
|
cancelSignal.register(rejectFn);
|
|
1598
1606
|
});
|
|
1599
1607
|
};
|
|
@@ -121,13 +121,9 @@ export default function performInitialSeekAndPlay(
|
|
|
121
121
|
obs.readyState < HTMLMediaElement.HAVE_CURRENT_DATA
|
|
122
122
|
) {
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* If so, the range in which it is possible to seek is not yet known.
|
|
128
|
-
* To solve this, the seek should be done after readyState HAVE_CURRENT_DATA (2),
|
|
129
|
-
* at that time the previously mentioned attributes are correctly initialized and
|
|
130
|
-
* the range in which it is possible to seek is correctly known.
|
|
124
|
+
* The starting position may not be known yet.
|
|
125
|
+
* Postpone the seek to a moment where the starting position should be known,
|
|
126
|
+
* assumely it's when readyState is greater or equal to HAVE_CURRENT_DATA (2).
|
|
131
127
|
* If the initiallySeekedTime is still `undefined` when the readyState is >= 2,
|
|
132
128
|
* let assume that the initiallySeekedTime will never be known and continue
|
|
133
129
|
* the logic without seeking.
|
|
@@ -158,24 +158,7 @@ export default class NativeTextDisplayer implements ITextDisplayer {
|
|
|
158
158
|
public reset(): void {
|
|
159
159
|
log.debug("NTD: Aborting NativeTextDisplayer");
|
|
160
160
|
this._removeData(0, Infinity);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (_trackElement !== undefined && _videoElement.hasChildNodes()) {
|
|
164
|
-
try {
|
|
165
|
-
_videoElement.removeChild(_trackElement);
|
|
166
|
-
} catch (_e) {
|
|
167
|
-
log.warn("NTD: Can't remove track element from the video");
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Ugly trick to work-around browser bugs by refreshing its mode
|
|
172
|
-
const oldMode = this._track.mode;
|
|
173
|
-
this._track.mode = "disabled";
|
|
174
|
-
this._track.mode = oldMode;
|
|
175
|
-
|
|
176
|
-
if (this._trackElement !== undefined) {
|
|
177
|
-
this._trackElement.innerHTML = "";
|
|
178
|
-
}
|
|
161
|
+
this._clearTrackElement();
|
|
179
162
|
}
|
|
180
163
|
|
|
181
164
|
public stop(): void {
|
|
@@ -213,6 +196,27 @@ export default class NativeTextDisplayer implements ITextDisplayer {
|
|
|
213
196
|
}
|
|
214
197
|
this._buffered.remove(start, end);
|
|
215
198
|
}
|
|
199
|
+
|
|
200
|
+
private _clearTrackElement(): void {
|
|
201
|
+
const { _trackElement, _videoElement } = this;
|
|
202
|
+
|
|
203
|
+
if (_trackElement !== undefined && _videoElement.hasChildNodes()) {
|
|
204
|
+
try {
|
|
205
|
+
_videoElement.removeChild(_trackElement);
|
|
206
|
+
} catch (_e) {
|
|
207
|
+
log.warn("NTD: Can't remove track element from the video");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Ugly trick to work-around browser bugs by refreshing its mode
|
|
212
|
+
const oldMode = this._track.mode;
|
|
213
|
+
this._track.mode = "disabled";
|
|
214
|
+
this._track.mode = oldMode;
|
|
215
|
+
|
|
216
|
+
if (this._trackElement !== undefined) {
|
|
217
|
+
this._trackElement.innerHTML = "";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
/** Data of chunks that should be pushed to the NativeTextDisplayer. */
|
package/src/public_types.ts
CHANGED
|
@@ -559,11 +559,62 @@ export interface IKeySystemOption {
|
|
|
559
559
|
*/
|
|
560
560
|
distinctiveIdentifier?: MediaKeysRequirement | undefined;
|
|
561
561
|
/**
|
|
562
|
-
* If true, all open MediaKeySession (
|
|
563
|
-
* closed when the current playback
|
|
562
|
+
* If true, all open `MediaKeySession` (JavaScript Objects linked to the keys
|
|
563
|
+
* used to decrypt the content) will be closed when the current playback
|
|
564
|
+
* stops.
|
|
565
|
+
*
|
|
566
|
+
* By default, we keep `MediaKeySession` from previous contents (up to
|
|
567
|
+
* `maxSessionCacheSize` `MediaKeySession`) to speed-up playback and avoid
|
|
568
|
+
* round-trips to the license server if the user ever decide to go back to
|
|
569
|
+
* those contents or to other contents relying to the same keys.
|
|
570
|
+
*
|
|
571
|
+
* However we found that some devices poorly handle that optimization.
|
|
572
|
+
*
|
|
573
|
+
* Note that if setting that property to `true` fixes your issue, it may be
|
|
574
|
+
* that it's just the `maxSessionCacheSize` which is for now too high.
|
|
575
|
+
*
|
|
576
|
+
* However if your problem doesn't disappear after setting
|
|
577
|
+
* `closeSessionsOnStop` to `true`, you may try `reuseMediaKeys` next.
|
|
564
578
|
*/
|
|
565
579
|
closeSessionsOnStop?: boolean;
|
|
566
|
-
|
|
580
|
+
/**
|
|
581
|
+
* If set to `true` or if not set, we might rely on the previous `MediaKeys`
|
|
582
|
+
* if a compatible one is already set on the media element, allowing to
|
|
583
|
+
* potentially speed-up content playback.
|
|
584
|
+
*
|
|
585
|
+
* If set to `false`, we will create a new `MediaKeys` instance (a
|
|
586
|
+
* JavaScript object needed to decrypt contents) if needed for that content.
|
|
587
|
+
*
|
|
588
|
+
* We noticed that reusing a previous MediaKeys had led to errors on a few
|
|
589
|
+
* devices. For example some smart TVs had shown errors after playing several
|
|
590
|
+
* encrypted contents, errors which disappeared if we renewed the
|
|
591
|
+
* `MediaKeys` for each content.
|
|
592
|
+
*
|
|
593
|
+
* We should already be able to detect most of those cases in the RxPlayer
|
|
594
|
+
* logic. However, it is still possible that we don't know yet of a device
|
|
595
|
+
* which also has problem with that optimization.
|
|
596
|
+
*
|
|
597
|
+
* If you have issues appearing only after playing multiple encrypted
|
|
598
|
+
* contents:
|
|
599
|
+
*
|
|
600
|
+
* - First, try setting the `closeSessionsOnStop` option which is less
|
|
601
|
+
* destructive.
|
|
602
|
+
*
|
|
603
|
+
* If it fixes your issue, it may be that it's just the number of
|
|
604
|
+
* `MediaKeySession` cached by the RxPlayer that is here too high.
|
|
605
|
+
*
|
|
606
|
+
* In that case you can instead update the `maxSessionCacheSize` option
|
|
607
|
+
* to still profit from a `MediaKeySession` cache (which avoid making
|
|
608
|
+
* license requests for already-played contents).
|
|
609
|
+
*
|
|
610
|
+
* If that second option doesn't seem to have an effect, you can just set
|
|
611
|
+
* `closeSessionsOnStop`.
|
|
612
|
+
*
|
|
613
|
+
* - If none of the precedent work-arounds work however, you can try setting
|
|
614
|
+
* `reuseMediaKeys` to `false`. If it fixes your problem, please open an
|
|
615
|
+
* RxPlayer issue so we can add your device to our list.
|
|
616
|
+
*/
|
|
617
|
+
reuseMediaKeys?: boolean | undefined;
|
|
567
618
|
singleLicensePer?: "content" | "periods" | "init-data";
|
|
568
619
|
/**
|
|
569
620
|
* Maximum number of `MediaKeySession` that should be created on the same
|
|
@@ -78,12 +78,14 @@ const SyncOrAsync = {
|
|
|
78
78
|
* @returns {Object}
|
|
79
79
|
*/
|
|
80
80
|
createAsync<T>(val: Promise<T>): ISyncOrAsyncValue<T> {
|
|
81
|
-
let ret = null;
|
|
82
|
-
val.then((resolved) => {
|
|
81
|
+
let ret: T | null = null;
|
|
82
|
+
val.then((resolved: T) => {
|
|
83
83
|
ret = resolved;
|
|
84
84
|
}, noop);
|
|
85
85
|
return {
|
|
86
|
-
syncValue:
|
|
86
|
+
get syncValue(): T | null {
|
|
87
|
+
return ret;
|
|
88
|
+
},
|
|
87
89
|
getValueAsAsync() {
|
|
88
90
|
return val;
|
|
89
91
|
},
|