rx-player 4.2.0-dev.2024100200 → 4.2.0-dev.2024101500
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +9 -0
- package/CHANGELOG.md +13 -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/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/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/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/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/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/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/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/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 +53 -29
- 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/find_key_system.ts +3 -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/text_displayer/native/native_text_displayer.ts +22 -18
- 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-dev.
|
|
414
|
+
this.version = /* PLAYER_VERSION */ "4.2.0-dev.2024101500";
|
|
415
415
|
this.log = log;
|
|
416
416
|
this.state = "STOPPED";
|
|
417
417
|
this.videoElement = videoElement;
|
|
@@ -3330,7 +3330,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
|
|
|
3330
3330
|
}
|
|
3331
3331
|
}
|
|
3332
3332
|
}
|
|
3333
|
-
Player.version = /* PLAYER_VERSION */ "4.2.0-dev.
|
|
3333
|
+
Player.version = /* PLAYER_VERSION */ "4.2.0-dev.2024101500";
|
|
3334
3334
|
|
|
3335
3335
|
/** Every events sent by the RxPlayer's public API. */
|
|
3336
3336
|
interface IPublicAPIEvent {
|
|
@@ -539,6 +539,9 @@ export async function testKeySystem(
|
|
|
539
539
|
const session = mediaKeys.createSession();
|
|
540
540
|
const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
|
|
541
541
|
await session.generateRequest("cenc", initData);
|
|
542
|
+
session.close().catch(() => {
|
|
543
|
+
log.warn("DRM: Failed to close the dummy session");
|
|
544
|
+
});
|
|
542
545
|
} catch (err) {
|
|
543
546
|
log.debug("DRM: KeySystemAccess was granted but it is not usable");
|
|
544
547
|
throw err;
|
|
@@ -92,7 +92,7 @@ import listenToMediaError from "./utils/throw_on_media_error";
|
|
|
92
92
|
*/
|
|
93
93
|
export default class MediaSourceContentInitializer extends ContentInitializer {
|
|
94
94
|
/** Constructor settings associated to this `MediaSourceContentInitializer`. */
|
|
95
|
-
private
|
|
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
|
};
|
|
@@ -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. */
|
|
@@ -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
|
},
|