@pexip/media-processor 22.0.0 → 22.1.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/CHANGELOG.md +10 -0
- package/api-docs/README.mdx +3 -1
- package/api-docs/functions/avg.mdx +1 -1
- package/api-docs/functions/createAnalyzerGraphNode.mdx +3 -1
- package/api-docs/functions/createAnalyzerSubscribableGraphNode.mdx +3 -1
- package/api-docs/functions/createAsyncCallbackLoop.mdx +6 -6
- package/api-docs/functions/createAudioDestinationGraphNode.mdx +1 -1
- package/api-docs/functions/createAudioSignalDetector.mdx +4 -4
- package/api-docs/functions/createAudioStats.mdx +1 -1
- package/api-docs/functions/createChannelMergerGraphNode.mdx +1 -1
- package/api-docs/functions/createChannelSplitterGraphNode.mdx +1 -1
- package/api-docs/functions/createDelayGraphNode.mdx +1 -1
- package/api-docs/functions/createDenoiseWorkletGraphNode.mdx +2 -2
- package/api-docs/functions/createFrameCallbackRequest.mdx +3 -3
- package/api-docs/functions/createGainGraphNode.mdx +3 -1
- package/api-docs/functions/createMediaElementSourceGraphNode.mdx +1 -1
- package/api-docs/functions/createReadWritableStreamFallbackInputProcessAdaptor.mdx +1 -1
- package/api-docs/functions/createSegmenter.mdx +1 -1
- package/api-docs/functions/createStreamDestinationGraphNode.mdx +1 -1
- package/api-docs/functions/createStreamSourceGraphNode.mdx +1 -1
- package/api-docs/functions/createVADetector.mdx +3 -3
- package/api-docs/functions/createVoiceDetectorFromProbability.mdx +1 -1
- package/api-docs/functions/createVoiceDetectorFromTimeData.mdx +3 -1
- package/api-docs/functions/createWindowedStats.mdx +2 -2
- package/api-docs/functions/getBezierCurveControlPoints.mdx +3 -1
- package/api-docs/functions/labelComponents.mdx +3 -1
- package/api-docs/functions/loadScript.mdx +1 -1
- package/api-docs/functions/loadWasms.mdx +1 -1
- package/api-docs/functions/nearestPowerOfTwo.mdx +1 -1
- package/api-docs/functions/playVideo.mdx +6 -4
- package/api-docs/functions/processAverageVolume.mdx +1 -1
- package/api-docs/functions/rms.mdx +1 -1
- package/api-docs/functions/subscribeWorkletNode.mdx +1 -1
- package/api-docs/functions/sum.mdx +1 -1
- package/api-docs/interfaces/AnalyzerSubscribableOptions.mdx +5 -3
- package/api-docs/interfaces/AudioGraph.mdx +2 -2
- package/api-docs/interfaces/AudioNodeInit.mdx +10 -6
- package/api-docs/interfaces/AudioNodeProps.mdx +3 -3
- package/api-docs/interfaces/Benchmark.mdx +3 -1
- package/api-docs/interfaces/Clock.mdx +1 -1
- package/api-docs/interfaces/Denoise.mdx +2 -2
- package/api-docs/interfaces/InputProcessAdaptor.mdx +6 -2
- package/api-docs/interfaces/MPMask.mdx +1 -1
- package/api-docs/interfaces/Process.mdx +5 -5
- package/api-docs/interfaces/ProcessWorkerDebugMessage.mdx +1 -1
- package/api-docs/interfaces/ProcessWorkerEventClosed.mdx +5 -0
- package/api-docs/interfaces/ProcessorEvent.mdx +2 -1
- package/api-docs/interfaces/ProcessorProcessOptions.mdx +2 -2
- package/api-docs/interfaces/ProcessorUpdateOptions.mdx +3 -1
- package/api-docs/interfaces/ReadWritableStream.mdx +2 -2
- package/api-docs/interfaces/RenderEventHandlers.mdx +3 -3
- package/api-docs/interfaces/Renderer.mdx +3 -1
- package/api-docs/interfaces/RendererOptions.mdx +5 -5
- package/api-docs/interfaces/Runner.mdx +3 -3
- package/api-docs/interfaces/Segmentation.mdx +1 -1
- package/api-docs/interfaces/Segmenter.mdx +39 -19
- package/api-docs/interfaces/VideoFrameLike.mdx +1 -1
- package/api-docs/interfaces/VideoProcessor.mdx +7 -7
- package/api-docs/interfaces/WorkletMessagePortOptions.mdx +2 -2
- package/api-docs/type-aliases/AsyncCallback.mdx +1 -1
- package/api-docs/type-aliases/Callback.mdx +2 -2
- package/api-docs/type-aliases/ExtractMessageEventType.mdx +1 -1
- package/api-docs/type-aliases/IncludeMessageEventDataType.mdx +1 -1
- package/api-docs/type-aliases/Logger.mdx +12 -0
- package/api-docs/type-aliases/OptionalKeys.mdx +1 -1
- package/api-docs/type-aliases/ProcessWorkerEvents.mdx +1 -0
- package/api-docs/type-aliases/RunnerCreator.mdx +3 -3
- package/api-docs/type-aliases/TupleOf.mdx +1 -1
- package/api-docs/variables/cloneImageRecord.mdx +3 -1
- package/api-docs/variables/createLazyProps.mdx +1 -1
- package/api-docs/variables/createObjectUpdater.mdx +2 -2
- package/api-docs/variables/createRemoteImageBitmap.mdx +5 -5
- package/api-docs/variables/handleWebGLContextLoss.mdx +1 -1
- package/api-docs/variables/urls.mdx +2 -2
- package/dist/common/tsconfig.tsbuildinfo +1 -1
- package/dist/common/types/messageEvents.d.ts +4 -1
- package/dist/common/types/processor.d.ts +1 -0
- package/dist/main/tsconfig.tsbuildinfo +1 -1
- package/dist/main/video/segmenter.d.ts +8 -1
- package/dist/main/video/segmenter.js +44 -29
- package/dist/main/video/types.d.ts +1 -1
- package/dist/main/video/video.d.ts +1 -1
- package/dist/main/video/video.js +22 -11
- package/dist/workers/mediaWorker.js +23 -23
- package/dist/workers/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -21,17 +21,31 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
21
21
|
credentials,
|
|
22
22
|
});
|
|
23
23
|
mediaWorker.addEventListener('error', error => {
|
|
24
|
-
log?.(`${error.message}
|
|
24
|
+
log?.error({ event: error, error: error.error }, `${error.message}`);
|
|
25
25
|
error.preventDefault();
|
|
26
26
|
});
|
|
27
27
|
mediaWorker.addEventListener('message', (event) => {
|
|
28
28
|
switch (event.data.type) {
|
|
29
29
|
case 'debug': {
|
|
30
|
-
log?.(event.data.data.
|
|
30
|
+
log?.debug(event.data.data.context, event.data.data.message);
|
|
31
31
|
break;
|
|
32
32
|
}
|
|
33
33
|
case 'event': {
|
|
34
|
-
|
|
34
|
+
switch (event.data.data.type) {
|
|
35
|
+
case 'Error':
|
|
36
|
+
case 'ContextCreationError':
|
|
37
|
+
case 'SamplingError':
|
|
38
|
+
log?.error(event.data.data, event.data.data.message);
|
|
39
|
+
break;
|
|
40
|
+
case 'ContextLost':
|
|
41
|
+
case 'ContextRestored':
|
|
42
|
+
case 'ProcessorRestarted':
|
|
43
|
+
log?.info(event.data.data, event.data.data.message);
|
|
44
|
+
break;
|
|
45
|
+
default:
|
|
46
|
+
log?.debug(event.data.data, event.data.data.message);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
35
49
|
for (const cb of props.eventHandlers.get(event.data.data.type) ?? []) {
|
|
36
50
|
cb();
|
|
37
51
|
}
|
|
@@ -72,9 +86,7 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
72
86
|
}
|
|
73
87
|
}
|
|
74
88
|
catch (err) {
|
|
75
|
-
log?.('Failed to restart process'
|
|
76
|
-
error: err,
|
|
77
|
-
});
|
|
89
|
+
log?.error({ error: err }, 'Failed to restart process');
|
|
78
90
|
props.status = PROCESS_STATUS.Error;
|
|
79
91
|
}
|
|
80
92
|
};
|
|
@@ -100,20 +112,26 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
100
112
|
}
|
|
101
113
|
case 'event': {
|
|
102
114
|
removeListener();
|
|
103
|
-
reject(
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
reject(ev.data.data.error ??
|
|
116
|
+
new Error(ev.data.data.type, {
|
|
117
|
+
cause: ev.data.data.message,
|
|
118
|
+
}));
|
|
106
119
|
break;
|
|
107
120
|
}
|
|
108
121
|
default: {
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
// Ignore messages that don't match the expected
|
|
123
|
+
// response type. Other concurrent requests (e.g.
|
|
124
|
+
// a `close` arriving while a `processed` is in
|
|
125
|
+
// flight) post their own responses on the same
|
|
126
|
+
// worker; rejecting here would cross-poison
|
|
127
|
+
// listeners and surface false errors.
|
|
128
|
+
break;
|
|
111
129
|
}
|
|
112
130
|
}
|
|
113
131
|
};
|
|
114
132
|
const handleError = (error) => {
|
|
115
133
|
removeListener();
|
|
116
|
-
reject(error.message);
|
|
134
|
+
reject(error.error ?? new Error(error.message));
|
|
117
135
|
};
|
|
118
136
|
const removeListener = () => {
|
|
119
137
|
props.mediaWorker?.removeEventListener('message', handleMessage);
|
|
@@ -142,7 +160,9 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
142
160
|
},
|
|
143
161
|
backgroundImage,
|
|
144
162
|
};
|
|
145
|
-
log?.(
|
|
163
|
+
log?.debug({
|
|
164
|
+
config: processorSettings,
|
|
165
|
+
}, 'Init processor with config');
|
|
146
166
|
const result = await postMsg('opened', {
|
|
147
167
|
type: 'open',
|
|
148
168
|
data: processorSettings,
|
|
@@ -152,9 +172,7 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
152
172
|
}
|
|
153
173
|
catch (error) {
|
|
154
174
|
props.status = PROCESS_STATUS.Error;
|
|
155
|
-
log?.('Failed to open process'
|
|
156
|
-
error: error,
|
|
157
|
-
});
|
|
175
|
+
log?.warn({ error: error }, 'Failed to open process');
|
|
158
176
|
return {};
|
|
159
177
|
}
|
|
160
178
|
};
|
|
@@ -178,7 +196,7 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
178
196
|
}
|
|
179
197
|
catch (error) {
|
|
180
198
|
props.status = PROCESS_STATUS.Error;
|
|
181
|
-
log?.('Error during the processing, return original track instead.'
|
|
199
|
+
log?.warn({ error }, 'Error during the processing, return original track instead.');
|
|
182
200
|
return input;
|
|
183
201
|
}
|
|
184
202
|
}
|
|
@@ -196,9 +214,7 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
196
214
|
}
|
|
197
215
|
catch (error) {
|
|
198
216
|
props.status = PROCESS_STATUS.Error;
|
|
199
|
-
log?.('Error during the processing, return original ReadWritableStream instead.'
|
|
200
|
-
error,
|
|
201
|
-
});
|
|
217
|
+
log?.warn({ error }, 'Error during the processing, return original ReadWritableStream instead.');
|
|
202
218
|
return input;
|
|
203
219
|
}
|
|
204
220
|
}
|
|
@@ -212,7 +228,8 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
212
228
|
const handleMessage = (ev) => {
|
|
213
229
|
if (ev.data.type === 'processed' ||
|
|
214
230
|
ev.data.type === 'opened' ||
|
|
215
|
-
ev.data.type === 'updated'
|
|
231
|
+
ev.data.type === 'updated' ||
|
|
232
|
+
ev.data.type === 'closed') {
|
|
216
233
|
props.mediaWorker?.removeEventListener('message', handleMessage);
|
|
217
234
|
resolve(undefined);
|
|
218
235
|
}
|
|
@@ -228,7 +245,7 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
228
245
|
props.status = PROCESS_STATUS.Updating;
|
|
229
246
|
const backgroundImage = options.backgroundImage &&
|
|
230
247
|
(await cloneImageRecord(options.backgroundImage));
|
|
231
|
-
log?.(
|
|
248
|
+
log?.debug({ config: options }, 'Update processor config');
|
|
232
249
|
const transfer = backgroundImage ? [backgroundImage.image] : [];
|
|
233
250
|
const changed = await postMsg('updated', {
|
|
234
251
|
type: 'update',
|
|
@@ -242,24 +259,22 @@ basePath = '/', { workerScriptUrl = new URL('../../workers/mediaWorker.js', impo
|
|
|
242
259
|
}
|
|
243
260
|
catch (error) {
|
|
244
261
|
props.status = PROCESS_STATUS.Error;
|
|
245
|
-
log?.('Failed to update process'
|
|
246
|
-
error: error,
|
|
247
|
-
});
|
|
262
|
+
log?.warn({ error: error }, 'Failed to update process');
|
|
248
263
|
return {};
|
|
249
264
|
}
|
|
250
265
|
};
|
|
251
266
|
const mute = (muted) => {
|
|
252
267
|
postMsg(undefined, { type: 'mute', data: muted });
|
|
253
268
|
};
|
|
254
|
-
const close = () => {
|
|
269
|
+
const close = async () => {
|
|
255
270
|
props.status = PROCESS_STATUS.Closing;
|
|
256
|
-
log?.('Close processor');
|
|
257
|
-
postMsg(
|
|
271
|
+
log?.info('Close processor');
|
|
272
|
+
await postMsg('closed', { type: 'close' });
|
|
258
273
|
props.status = PROCESS_STATUS.Closed;
|
|
259
274
|
};
|
|
260
275
|
const destroy = async () => {
|
|
261
276
|
props.status = PROCESS_STATUS.Destroying;
|
|
262
|
-
log?.('Destroy processor');
|
|
277
|
+
log?.info('Destroy processor');
|
|
263
278
|
props.eventHandlers.clear();
|
|
264
279
|
postMsg(undefined, { type: 'destroy' });
|
|
265
280
|
props.mediaWorker?.terminate();
|
package/dist/main/video/video.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { assert, isEmpty } from '@pexip/utils';
|
|
2
|
-
import { AbortReason } from '../../common/constants';
|
|
3
2
|
import { BACKGROUND_BLUR_AMOUNT, BACKGROUND_THRESHOLD, DOWN_SAMPLE_FACTOR, EDGE_BLUR_AMOUNT, EXCLUDE_BYSTANDERS, FOREGROUND_THRESHOLD, LIGHT_WRAP_BLUR_AMOUNT, LIGHT_WRAP_EDGE_BAND, LIGHT_WRAP_INTENSITY, LIGHT_WRAP_TIGHTNESS, MASK_COMBINE_RATIO, MORPH_DILATE_RADIUS_PX, MORPH_ERODE_RADIUS_PX, MORPH_PASS, PERSON_CENTER, PROCESSING_HEIGHT, PROCESSING_WIDTH, SIGMA_RANGE, SIGMA_SPACE, } from '../../common/constants';
|
|
4
3
|
import { clamping, createObjectUpdater, compareImageRecords, } from '../../common/utils';
|
|
5
4
|
import { assertMediaStreamVideoTrack, isRenderEffects } from './typeGuards';
|
|
@@ -278,9 +277,26 @@ export const createVideoProcessor = (segmenter, inputProcessAdaptor) => {
|
|
|
278
277
|
const update = async (options) => {
|
|
279
278
|
await updateProcessorOptions(options);
|
|
280
279
|
};
|
|
281
|
-
|
|
280
|
+
// Serialization lock: `process()` and `close()` mutate the same worker
|
|
281
|
+
// pipeline (segmenter + transferred tracks). If they overlap, the worker
|
|
282
|
+
// can tear down a writable stream while the main thread is still feeding
|
|
283
|
+
// it, surfacing as `InvalidStateError: Stream closed`. Chain every
|
|
284
|
+
// public mutation through this promise so they always run end-to-end.
|
|
285
|
+
let serialized = Promise.resolve();
|
|
286
|
+
const runSerialized = (fn) => {
|
|
287
|
+
const next = serialized.then(fn, fn);
|
|
288
|
+
serialized = next.catch(() => undefined);
|
|
289
|
+
return next;
|
|
290
|
+
};
|
|
291
|
+
const closeImpl = async () => {
|
|
292
|
+
await segmenter.close();
|
|
293
|
+
props.outputTrack?.stop();
|
|
294
|
+
props.outputTrack = undefined;
|
|
295
|
+
props.processing = false;
|
|
296
|
+
};
|
|
297
|
+
const processImpl = async (source) => {
|
|
282
298
|
if (props.processing) {
|
|
283
|
-
|
|
299
|
+
await closeImpl();
|
|
284
300
|
}
|
|
285
301
|
assertMediaStreamVideoTrack(source);
|
|
286
302
|
if (segmenter.status === 'new') {
|
|
@@ -302,15 +318,10 @@ export const createVideoProcessor = (segmenter, inputProcessAdaptor) => {
|
|
|
302
318
|
props.processing = true;
|
|
303
319
|
return props.outputTrack;
|
|
304
320
|
};
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
props.abortController?.abort(AbortReason.Close);
|
|
308
|
-
props.outputTrack?.stop();
|
|
309
|
-
props.outputTrack = undefined;
|
|
310
|
-
props.processing = false;
|
|
311
|
-
};
|
|
321
|
+
const process = source => runSerialized(() => processImpl(source));
|
|
322
|
+
const close = () => runSerialized(closeImpl);
|
|
312
323
|
const destroy = async () => {
|
|
313
|
-
close();
|
|
324
|
+
await close();
|
|
314
325
|
processorOptions?.backgroundImage?.image?.close();
|
|
315
326
|
renderOptions = initRenderOptions();
|
|
316
327
|
changedRenderOptions = {};
|