@pexip/media 17.2.0 → 17.4.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 +39 -0
- package/dist/audioMixingProcessor.d.ts +18 -0
- package/dist/audioMixingProcessor.js +173 -0
- package/dist/audioProcessor.d.ts +86 -0
- package/dist/audioProcessor.js +310 -0
- package/dist/baseLogger.d.ts +37 -0
- package/dist/baseLogger.js +29 -0
- package/dist/displayMedia.d.ts +3 -0
- package/dist/displayMedia.js +31 -0
- package/dist/index.d.ts +11 -837
- package/dist/index.js +11 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +44 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.js +296 -0
- package/dist/previewController.d.ts +64 -0
- package/dist/previewController.js +375 -0
- package/dist/signals.d.ts +25 -0
- package/dist/signals.js +38 -0
- package/dist/status.d.ts +35 -0
- package/dist/status.js +199 -0
- package/dist/typeGuard.d.ts +9 -0
- package/dist/typeGuard.js +40 -0
- package/dist/types.d.ts +559 -0
- package/dist/types.js +278 -0
- package/dist/userMedia.d.ts +38 -0
- package/dist/userMedia.js +333 -0
- package/dist/utils.d.ts +108 -0
- package/dist/utils.js +360 -0
- package/dist/videoProcessor.d.ts +56 -0
- package/dist/videoProcessor.js +349 -0
- package/package.json +10 -10
- package/dist/index.mjs +0 -2900
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { createMedia } from './media';
|
|
2
|
+
export { createAudioStreamProcess } from './audioProcessor';
|
|
3
|
+
export { createVideoStreamProcess } from './videoProcessor';
|
|
4
|
+
export * from './types';
|
|
5
|
+
export { setLogger } from './logger';
|
|
6
|
+
export * from './previewController';
|
|
7
|
+
export * from './status';
|
|
8
|
+
export * from './signals';
|
|
9
|
+
export { applyContentHint } from './utils';
|
|
10
|
+
export { createAudioMixingProcess } from './audioMixingProcessor';
|
|
11
|
+
export * from './displayMedia';
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Logger } from './baseLogger';
|
|
2
|
+
export declare let logger: Readonly<Logger>;
|
|
3
|
+
export declare function setLogger(newLogger: Logger): void;
|
|
4
|
+
/**
|
|
5
|
+
* A logger wrapper to always include some meta base data to the log
|
|
6
|
+
*
|
|
7
|
+
* @param metaBase - The meta data which will always be included into the log
|
|
8
|
+
*/
|
|
9
|
+
export declare const createModuleLogger: (metaBase: unknown) => Logger;
|
|
10
|
+
export declare const createLogProxyHandler: <T extends object>(logger: Logger, name: string, scope: string) => ProxyHandler<T>;
|
|
11
|
+
export declare const proxyWithLog: (logger: Logger, scope: string) => <T extends object>(obj: T, name: string) => T;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createConsoleLogger } from './baseLogger';
|
|
2
|
+
export let logger = createConsoleLogger();
|
|
3
|
+
export function setLogger(newLogger) {
|
|
4
|
+
logger = newLogger;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A logger wrapper to always include some meta base data to the log
|
|
8
|
+
*
|
|
9
|
+
* @param metaBase - The meta data which will always be included into the log
|
|
10
|
+
*/
|
|
11
|
+
export const createModuleLogger = (metaBase) => {
|
|
12
|
+
return Object.keys(logger).reduce((log, key) => {
|
|
13
|
+
const k = key;
|
|
14
|
+
log[k] = (meta, msg) => {
|
|
15
|
+
if (typeof meta === 'string') {
|
|
16
|
+
return logger[k](metaBase, meta);
|
|
17
|
+
}
|
|
18
|
+
if (typeof meta === 'object') {
|
|
19
|
+
return logger[k]({ ...meta, meta: metaBase }, msg);
|
|
20
|
+
}
|
|
21
|
+
return logger[k]({ meta: metaBase, context: meta }, msg);
|
|
22
|
+
};
|
|
23
|
+
return log;
|
|
24
|
+
}, {});
|
|
25
|
+
};
|
|
26
|
+
export const createLogProxyHandler = (logger, name, scope) => {
|
|
27
|
+
return {
|
|
28
|
+
get: (target, p, receiver) => {
|
|
29
|
+
const value = Reflect.get(target, p, receiver);
|
|
30
|
+
logger.debug({ scope, name, prop: p, value }, `called get ${name}[${String(p)}]`);
|
|
31
|
+
return value;
|
|
32
|
+
},
|
|
33
|
+
// eslint-disable-next-line max-params --- from lib.es2015 and log
|
|
34
|
+
set: (target, p, value, receiver) => {
|
|
35
|
+
logger.debug(
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- it's a log call...
|
|
37
|
+
{ scope, name, prop: p, value }, `called set ${name}[${String(p)}]`);
|
|
38
|
+
return Reflect.set(target, p, value, receiver);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export const proxyWithLog = (logger, scope) => (obj, name) => {
|
|
43
|
+
return new Proxy(obj, createLogProxyHandler(logger, name, scope));
|
|
44
|
+
};
|
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MediaOptions, MediaController } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create an object to interact with the media scream, which is usually used for
|
|
4
|
+
* our main stream.
|
|
5
|
+
*
|
|
6
|
+
* @param options - @see MediaOptions
|
|
7
|
+
*/
|
|
8
|
+
export declare const createMedia: ({ getMuteState, signals, mediaProcessors, getDefaultConstraints, }: MediaOptions) => MediaController;
|
package/dist/media.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { subscribe, MediaEventType, MediaDeviceFailure, createStreamTrackEventSubscriptions, getDevices, isRequestedResolution, mergeConstraints, shouldRequestDevice, } from '@pexip/media-control';
|
|
2
|
+
import { createAsyncQueue, isEmpty } from '@pexip/utils';
|
|
3
|
+
import { getPermissionStatus, deriveInitialPermissionStatus, isInitialPermissions, isInitialPermissionsGranted, } from './status';
|
|
4
|
+
import { UserMediaStatus } from './types';
|
|
5
|
+
import { createModuleLogger, logger } from './logger';
|
|
6
|
+
import { createGetUserMediaProcess, requestUserMediaWithRetry, } from './userMedia';
|
|
7
|
+
import { createMediaPipeline, createMediaProcess, buildMedia, getDevicesChanges, shallowCopy, wrapToJSON, hasSettingsChanged, AUDIO_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, MIXING_SETTINGS_KEYS, applyContentHint, } from './utils';
|
|
8
|
+
import { isMedia } from './typeGuard';
|
|
9
|
+
import { updateFeatureProps as getVideoFeatures } from './videoProcessor';
|
|
10
|
+
import { updateFeatureProps as getAudioFeatures } from './audioProcessor';
|
|
11
|
+
import { updateFeatureProps as getMixingFeatures } from './audioMixingProcessor';
|
|
12
|
+
/**
|
|
13
|
+
* Proxy handler for Media Props
|
|
14
|
+
*/
|
|
15
|
+
const createMediaPropsHandler = (signals) => ({
|
|
16
|
+
get: (target, p) => {
|
|
17
|
+
switch (p) {
|
|
18
|
+
default:
|
|
19
|
+
return target[p];
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
set: (target, p, value) => {
|
|
23
|
+
if (target[p] === value) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (p === 'devices') {
|
|
27
|
+
const nextDevices = value;
|
|
28
|
+
const changes = getDevicesChanges(target[p].flatMap(device => (device.label ? [device] : [])), nextDevices);
|
|
29
|
+
if (isEmpty(changes.found) && isEmpty(changes.lost)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
logger.debug({
|
|
34
|
+
oldValue: target[p],
|
|
35
|
+
newValue: value,
|
|
36
|
+
meta: {
|
|
37
|
+
module: 'Media',
|
|
38
|
+
props: target,
|
|
39
|
+
},
|
|
40
|
+
}, `Update Props[${p}]`);
|
|
41
|
+
switch (p) {
|
|
42
|
+
case 'devices': {
|
|
43
|
+
if (!Array.isArray(value)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const devices = value;
|
|
47
|
+
target[p] = devices;
|
|
48
|
+
signals?.onDevicesChanged?.emit(devices);
|
|
49
|
+
target.media.devices = devices;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
case 'media': {
|
|
53
|
+
if (!isMedia(value)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const currentStatus = target[p].status;
|
|
57
|
+
target[p] = value;
|
|
58
|
+
signals?.onMediaChanged?.emit(value);
|
|
59
|
+
if (currentStatus !== value.status) {
|
|
60
|
+
signals?.onStatusChanged?.emit(value.status);
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
default: {
|
|
65
|
+
Reflect.set(target, p, value);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* Create an object to interact with the media scream, which is usually used for
|
|
73
|
+
* our main stream.
|
|
74
|
+
*
|
|
75
|
+
* @param options - @see MediaOptions
|
|
76
|
+
*/
|
|
77
|
+
export const createMedia = ({ getMuteState, signals, mediaProcessors, getDefaultConstraints = () => ({}), }) => {
|
|
78
|
+
const initMedia = (status = UserMediaStatus.Initial, devices = [], constraints) => buildMedia(() => ({ status, devices, constraints }), signals.onStatusChanged?.emit);
|
|
79
|
+
const _props = {
|
|
80
|
+
devices: [],
|
|
81
|
+
media: initMedia(),
|
|
82
|
+
discardMedia: false,
|
|
83
|
+
};
|
|
84
|
+
const props = new Proxy(_props, createMediaPropsHandler(signals));
|
|
85
|
+
const queue = createAsyncQueue();
|
|
86
|
+
const logger = createModuleLogger({
|
|
87
|
+
module: 'Media',
|
|
88
|
+
props: _props,
|
|
89
|
+
get mediaTracks() {
|
|
90
|
+
return _props.media?.stream?.getTracks();
|
|
91
|
+
},
|
|
92
|
+
get rawMediaTracks() {
|
|
93
|
+
return _props.media?.rawStream?.getTracks();
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const cleanup = async () => {
|
|
97
|
+
if (isInitialPermissions(props.media.status)) {
|
|
98
|
+
props.discardMedia = true;
|
|
99
|
+
}
|
|
100
|
+
// carry the most recent status over for the next time
|
|
101
|
+
const status = await deriveInitialPermissionStatus(props.media.status);
|
|
102
|
+
props.media = initMedia(status, props.devices, props.media.constraints);
|
|
103
|
+
};
|
|
104
|
+
// Media Pipeline
|
|
105
|
+
const mediaPipeline = createMediaPipeline([
|
|
106
|
+
createGetUserMediaProcess(requestUserMediaWithRetry(), () => props.devices, { initialMedia: props.media }),
|
|
107
|
+
...mediaProcessors,
|
|
108
|
+
createMediaProcess(media => {
|
|
109
|
+
// Subscribe the track event from raw stream
|
|
110
|
+
const trackSubscriptions = media.rawStream?.getTracks().map(track => createStreamTrackEventSubscriptions(track, {
|
|
111
|
+
ended: signals.onStreamTrackEnded?.emit,
|
|
112
|
+
mute: signals.onStreamTrackMuted?.emit,
|
|
113
|
+
unmute: signals.onStreamTrackUnmuted?.emit,
|
|
114
|
+
}));
|
|
115
|
+
const muteTrack = (tracks) => (muted) => {
|
|
116
|
+
const [track] = tracks;
|
|
117
|
+
const kind = track?.kind;
|
|
118
|
+
if (track && (kind === 'audio' || kind === 'video')) {
|
|
119
|
+
media[kind === 'audio' ? 'muteAudio' : 'muteVideo'](muted);
|
|
120
|
+
return tracks.forEach(track => {
|
|
121
|
+
logger.debug({ trackInResult: track, intendToMute: muted }, `mute ${track.kind}`);
|
|
122
|
+
signals.onStreamTrackEnabled?.emit(track);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
logger.warn({ tracks, kind }, 'trying to mute but no track');
|
|
126
|
+
};
|
|
127
|
+
const muteAudio = muteTrack(media.stream?.getAudioTracks() ?? []);
|
|
128
|
+
const muteVideo = muteTrack(media.stream?.getVideoTracks() ?? []);
|
|
129
|
+
// Sync mute
|
|
130
|
+
const { audio, video } = getMuteState();
|
|
131
|
+
media.audioMuted !== undefined && muteAudio(audio);
|
|
132
|
+
media.videoMuted !== undefined && muteVideo(video);
|
|
133
|
+
const release = async () => {
|
|
134
|
+
trackSubscriptions?.forEach(unsubscribe => unsubscribe());
|
|
135
|
+
await media.release();
|
|
136
|
+
await cleanup();
|
|
137
|
+
};
|
|
138
|
+
const applyConstraints = async (constraints) => {
|
|
139
|
+
await media.applyConstraints(constraints);
|
|
140
|
+
const videoFeatures = getVideoFeatures(constraints.video, {});
|
|
141
|
+
const audioFeatures = getAudioFeatures(constraints.audio, {});
|
|
142
|
+
media.stream
|
|
143
|
+
?.getAudioTracks()
|
|
144
|
+
.forEach(applyContentHint(audioFeatures.contentHint));
|
|
145
|
+
media.stream
|
|
146
|
+
?.getVideoTracks()
|
|
147
|
+
.forEach(applyContentHint(videoFeatures.contentHint));
|
|
148
|
+
};
|
|
149
|
+
logger.debug({ finalAudioMute: audio, finalVideoMute: video }, 'End of media pipeline');
|
|
150
|
+
return wrapToJSON(shallowCopy(media, {
|
|
151
|
+
release,
|
|
152
|
+
muteAudio,
|
|
153
|
+
muteVideo,
|
|
154
|
+
applyConstraints,
|
|
155
|
+
}));
|
|
156
|
+
}),
|
|
157
|
+
]);
|
|
158
|
+
/**
|
|
159
|
+
* A function to update the media only when necessary, aka the current setup
|
|
160
|
+
* for the stream is different from the new constraints
|
|
161
|
+
*
|
|
162
|
+
* @param constraints - @see MediaDeviceRequest
|
|
163
|
+
*/
|
|
164
|
+
const updateMedia = async (constraints) => {
|
|
165
|
+
if (!constraints.audio && !constraints.video) {
|
|
166
|
+
throw new Error(MediaDeviceFailure.MissingConstraintsError);
|
|
167
|
+
}
|
|
168
|
+
const shouldRequestAudio = shouldRequestDevice(constraints.audio, props.media?.rawStream?.getAudioTracks() ?? [], props.devices.filter(device => device.kind === 'audioinput'));
|
|
169
|
+
const shouldRequestVideo = shouldRequestDevice(constraints.video, props.media?.rawStream?.getVideoTracks() ?? [], props.devices.filter(device => device.kind === 'videoinput'));
|
|
170
|
+
const { audio: [prevAudioSettings], video: [prevVideoSettings], } = props.media.getSettings();
|
|
171
|
+
const videoFeatures = getVideoFeatures(constraints.video, {});
|
|
172
|
+
const audioFeatures = getAudioFeatures(constraints.audio, {});
|
|
173
|
+
const mixingFeatures = getMixingFeatures(constraints.audio, {});
|
|
174
|
+
const hasRequestedResolution = ['blur', 'overlay'].includes(videoFeatures.videoSegmentation ?? '') || // Skip when there is a render effect to modify the video
|
|
175
|
+
!props.devices.some(device => device.kind === 'videoinput' && device.label) || // Skip when no authorized video found
|
|
176
|
+
isRequestedResolution(constraints.video, props.media?.videoInput);
|
|
177
|
+
const audioFeaturesChanged = hasSettingsChanged(AUDIO_SETTINGS_KEYS)(prevAudioSettings, audioFeatures);
|
|
178
|
+
const videoFeaturesChanged = hasSettingsChanged(VIDEO_SETTINGS_KEYS)(prevVideoSettings, videoFeatures);
|
|
179
|
+
const mixingFeaturesChanged = hasSettingsChanged(MIXING_SETTINGS_KEYS)(prevAudioSettings, mixingFeatures);
|
|
180
|
+
const ptzFeaturesChanges = hasSettingsChanged(['pan', 'tilt', 'zoom'])(prevVideoSettings, videoFeatures);
|
|
181
|
+
logger.debug({
|
|
182
|
+
constraints,
|
|
183
|
+
stream: props.media.stream,
|
|
184
|
+
currentDevices: props.devices,
|
|
185
|
+
shouldRequestAudio,
|
|
186
|
+
shouldRequestVideo,
|
|
187
|
+
sameResolution: hasRequestedResolution,
|
|
188
|
+
audioFeaturesChanged,
|
|
189
|
+
videoFeaturesChanged,
|
|
190
|
+
mixingFeaturesChanged,
|
|
191
|
+
}, 'Is currently streaming requested stream');
|
|
192
|
+
if (shouldRequestAudio ||
|
|
193
|
+
shouldRequestVideo ||
|
|
194
|
+
ptzFeaturesChanges ||
|
|
195
|
+
!hasRequestedResolution ||
|
|
196
|
+
!props.media.stream) {
|
|
197
|
+
if (props.media) {
|
|
198
|
+
await props.media.release();
|
|
199
|
+
}
|
|
200
|
+
if (props.discardMedia) {
|
|
201
|
+
props.discardMedia = false;
|
|
202
|
+
}
|
|
203
|
+
const media = await mediaPipeline.execute(constraints);
|
|
204
|
+
media.stream
|
|
205
|
+
?.getAudioTracks()
|
|
206
|
+
.forEach(applyContentHint(audioFeatures.contentHint));
|
|
207
|
+
media.stream
|
|
208
|
+
?.getVideoTracks()
|
|
209
|
+
.forEach(applyContentHint(videoFeatures.contentHint));
|
|
210
|
+
if (props.discardMedia) {
|
|
211
|
+
logger.debug('Discard media');
|
|
212
|
+
return await media.release();
|
|
213
|
+
}
|
|
214
|
+
props.media = media;
|
|
215
|
+
}
|
|
216
|
+
else if (audioFeaturesChanged ||
|
|
217
|
+
videoFeaturesChanged ||
|
|
218
|
+
mixingFeaturesChanged) {
|
|
219
|
+
props.media.constraints = constraints;
|
|
220
|
+
await props.media.applyConstraints({
|
|
221
|
+
audio: audioFeatures,
|
|
222
|
+
video: videoFeatures,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const mergeMediaConstraints = (constraints) => {
|
|
227
|
+
const { audio, video } = getDefaultConstraints();
|
|
228
|
+
return {
|
|
229
|
+
audio: audio === false
|
|
230
|
+
? false
|
|
231
|
+
: mergeConstraints(audio)(constraints.audio),
|
|
232
|
+
video: video === false
|
|
233
|
+
? false
|
|
234
|
+
: mergeConstraints(video)(constraints.video),
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
const getUserMediaAsync = async (constraints) => {
|
|
238
|
+
queue.enqueue(async () => await updateMedia(mergeMediaConstraints(constraints)), false);
|
|
239
|
+
await queue.execute();
|
|
240
|
+
};
|
|
241
|
+
const getUserMedia = constraints => {
|
|
242
|
+
queue.enqueue(async () => await updateMedia(mergeMediaConstraints(constraints)));
|
|
243
|
+
};
|
|
244
|
+
const tryAndGetUserMedia = constraints => {
|
|
245
|
+
queue.enqueue(async () => {
|
|
246
|
+
props.media.status = await getPermissionStatus();
|
|
247
|
+
logger.debug({ status: props.media.status }, 'Current Permission State');
|
|
248
|
+
const mergedConstraints = mergeMediaConstraints(constraints);
|
|
249
|
+
props.media.constraints = mergedConstraints;
|
|
250
|
+
props.devices = await getDevices();
|
|
251
|
+
if (!mergedConstraints.audio && !mergedConstraints.video) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (isInitialPermissionsGranted(props.media.status)) {
|
|
255
|
+
return await updateMedia(mergedConstraints);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
// Media Control Event handlers
|
|
260
|
+
const handleNoInputs = (_availableDevices) => {
|
|
261
|
+
props.media.status = UserMediaStatus.NoDevicesFound;
|
|
262
|
+
};
|
|
263
|
+
const handleDeviceChange = (devices) => {
|
|
264
|
+
props.devices = devices;
|
|
265
|
+
};
|
|
266
|
+
// Subscribe Media Events for devices
|
|
267
|
+
subscribe(event => {
|
|
268
|
+
switch (event.detail.type) {
|
|
269
|
+
case MediaEventType.NoInputDevices:
|
|
270
|
+
logger.debug(event.detail, 'NoInputDevices emitted');
|
|
271
|
+
handleNoInputs(event.detail.devices);
|
|
272
|
+
break;
|
|
273
|
+
case MediaEventType.DevicesFound:
|
|
274
|
+
case MediaEventType.DevicesLost:
|
|
275
|
+
logger.debug(event.detail, 'DevicesFound/Lost emitted');
|
|
276
|
+
handleDeviceChange([
|
|
277
|
+
...event.detail.authorizedDevices,
|
|
278
|
+
...event.detail.unauthorizedDevices,
|
|
279
|
+
]);
|
|
280
|
+
break;
|
|
281
|
+
default:
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
get media() {
|
|
287
|
+
return props.media;
|
|
288
|
+
},
|
|
289
|
+
get devices() {
|
|
290
|
+
return props.devices;
|
|
291
|
+
},
|
|
292
|
+
getUserMedia,
|
|
293
|
+
getUserMediaAsync,
|
|
294
|
+
tryAndGetUserMedia,
|
|
295
|
+
};
|
|
296
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { MediaDeviceRequest, MediaDeviceInfoLike } from '@pexip/media-control';
|
|
2
|
+
import type { AsyncQueueOptions } from '@pexip/utils';
|
|
3
|
+
import type { Media, Unsubscribe, MediaSignals, MediaProcessor } from './types';
|
|
4
|
+
type EventCallback<T> = (event: T) => void;
|
|
5
|
+
type EventErrorCallback = (error: Error) => void;
|
|
6
|
+
export type PreviewInput = MediaDeviceInfoLike | undefined;
|
|
7
|
+
export interface PreviewEventHandler {
|
|
8
|
+
audioInput?: EventCallback<PreviewInput>;
|
|
9
|
+
videoInput?: EventCallback<PreviewInput>;
|
|
10
|
+
media?: EventCallback<Media>;
|
|
11
|
+
videoInputError?: EventErrorCallback;
|
|
12
|
+
audioInputError?: EventErrorCallback;
|
|
13
|
+
applyChangesError?: EventErrorCallback;
|
|
14
|
+
revertChangesError?: EventErrorCallback;
|
|
15
|
+
updatingPreview?: EventCallback<boolean>;
|
|
16
|
+
updatingMain?: EventCallback<boolean>;
|
|
17
|
+
unsubscribeMain?: Unsubscribe;
|
|
18
|
+
}
|
|
19
|
+
export interface PreviewStreamParams {
|
|
20
|
+
getCurrentDevices: () => MediaDeviceInfoLike[];
|
|
21
|
+
getCurrentMedia: () => Media | undefined;
|
|
22
|
+
updateMainStream: (request: MediaDeviceRequest) => Promise<void>;
|
|
23
|
+
mediaSignal: MediaSignals['onMediaChanged'];
|
|
24
|
+
onEnded?: () => void;
|
|
25
|
+
fftSize?: number;
|
|
26
|
+
queueOptions?: Partial<AsyncQueueOptions>;
|
|
27
|
+
processors: MediaProcessor[];
|
|
28
|
+
}
|
|
29
|
+
export interface PreviewControllerProps {
|
|
30
|
+
media: Media;
|
|
31
|
+
audioInput?: MediaDeviceInfoLike;
|
|
32
|
+
videoInput?: MediaDeviceInfoLike;
|
|
33
|
+
updatingPreview: boolean;
|
|
34
|
+
updatingMain: boolean;
|
|
35
|
+
originalMainAudioInput?: MediaDeviceInfoLike;
|
|
36
|
+
discardMedia: boolean;
|
|
37
|
+
initialized: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface PreviewStreamController {
|
|
40
|
+
media: Media;
|
|
41
|
+
audioInputChanged: boolean;
|
|
42
|
+
videoInputChanged: boolean;
|
|
43
|
+
inputChanged: boolean;
|
|
44
|
+
audioInput: PreviewInput;
|
|
45
|
+
videoInput: PreviewInput;
|
|
46
|
+
updatingPreview: boolean;
|
|
47
|
+
updatingMain: boolean;
|
|
48
|
+
updateAudioInput(input: PreviewInput): void;
|
|
49
|
+
updateVideoInput(input: PreviewInput): void;
|
|
50
|
+
applyChanges(force?: boolean): Promise<void>;
|
|
51
|
+
revertChanges(): Promise<void>;
|
|
52
|
+
onMediaChanged(callback: EventCallback<Media>): Unsubscribe;
|
|
53
|
+
onAudioInputChanged(callback: EventCallback<PreviewInput>): Unsubscribe;
|
|
54
|
+
onVideoInputChanged(callback: EventCallback<PreviewInput>): Unsubscribe;
|
|
55
|
+
onUpdatingPreview(callback: EventCallback<boolean>): Unsubscribe;
|
|
56
|
+
onUpdatingMain(callback: EventCallback<boolean>): Unsubscribe;
|
|
57
|
+
onAudioInputError(callback: EventErrorCallback): Unsubscribe;
|
|
58
|
+
onVideoInputError(callback: EventErrorCallback): Unsubscribe;
|
|
59
|
+
onApplyChangesError(callback: EventErrorCallback): Unsubscribe;
|
|
60
|
+
onRevertChangesError(callback: EventErrorCallback): Unsubscribe;
|
|
61
|
+
}
|
|
62
|
+
export declare const createPreviewStreamController: ({ getCurrentDevices, getCurrentMedia, updateMainStream, onEnded, mediaSignal, queueOptions, processors, }: PreviewStreamParams) => PreviewStreamController;
|
|
63
|
+
export type CreatePreviewStreamController = typeof createPreviewStreamController;
|
|
64
|
+
export {};
|