@pexip/media 18.4.0 → 19.0.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 +130 -0
- package/README.md +9 -8
- package/dist/audioMixingProcessor.d.ts +2 -2
- package/dist/audioMixingProcessor.js +113 -100
- package/dist/audioProcessor.d.ts +4 -4
- package/dist/audioProcessor.js +117 -152
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +7 -0
- package/dist/media.d.ts +12 -2
- package/dist/media.js +242 -170
- package/dist/previewController.d.ts +10 -9
- package/dist/previewController.js +99 -68
- package/dist/signals.d.ts +10 -1
- package/dist/signals.js +9 -0
- package/dist/status.d.ts +5 -2
- package/dist/status.js +3 -3
- package/dist/types.d.ts +135 -40
- package/dist/userMedia.d.ts +10 -8
- package/dist/userMedia.js +122 -178
- package/dist/utils.d.ts +25 -47
- package/dist/utils.js +487 -186
- package/dist/videoProcessor.d.ts +3 -7
- package/dist/videoProcessor.js +103 -139
- package/package.json +12 -7
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createAsyncQueue } from '@pexip/utils';
|
|
3
|
-
import {
|
|
4
|
-
import { buildMedia, cloneMedia, createMediaPipeline, hasSettingsChanged, AUDIO_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, } from './utils';
|
|
1
|
+
import { hasChangedInput, isMediaDeviceInfo, reverseDeviceId, } from '@pexip/media-control';
|
|
2
|
+
import { createAsyncQueue, assert } from '@pexip/utils';
|
|
3
|
+
import { AUDIO_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, createMediaProcessor, hasSettingsChanged, } from './utils';
|
|
5
4
|
import { isMedia } from './typeGuard';
|
|
6
5
|
import { createModuleLogger } from './logger';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
const DEFAULT_QUEUE_SIZE = 1;
|
|
10
|
-
const DEFAULT_QUEUE_THROTTLE_MS = 500;
|
|
6
|
+
import { createGetUserMediaProcess, requestUserMediaWithRetry, } from './userMedia';
|
|
7
|
+
import { createMediaUpdater } from './media';
|
|
11
8
|
const DEFAULT_QUEUE_DELAY_MS = 100;
|
|
12
9
|
const DEFAULT_QUEUE_DROP_LAST = false;
|
|
10
|
+
const DEFAULT_QUEUE_SIZE = 1;
|
|
11
|
+
const DEFAULT_QUEUE_THROTTLE_MS = 500;
|
|
13
12
|
const isPreviewInput = (value) => {
|
|
14
13
|
if (isMediaDeviceInfo(value) || value === undefined) {
|
|
15
14
|
return true;
|
|
@@ -33,18 +32,14 @@ const createEventHandler = (eventHandlers) => (key) => (callback) => {
|
|
|
33
32
|
eventHandlers[key] = undefined;
|
|
34
33
|
};
|
|
35
34
|
};
|
|
36
|
-
const getUM = async (constraints) => [
|
|
37
|
-
await getUserMedia(constraints),
|
|
38
|
-
UserMediaStatus.PermissionsGranted,
|
|
39
|
-
];
|
|
40
35
|
const createAudioVideoProcessingSettingsChangeDetector = (getCurrentMedia, getPreviewMedia) => {
|
|
41
36
|
const hasAudioSettingsChanged = hasSettingsChanged(AUDIO_SETTINGS_KEYS);
|
|
42
37
|
const hasVideoSettingsChanged = hasSettingsChanged(VIDEO_SETTINGS_KEYS);
|
|
43
38
|
return () => {
|
|
44
39
|
const currentMedia = getCurrentMedia?.();
|
|
45
40
|
const previewMedia = getPreviewMedia();
|
|
46
|
-
const { audio:
|
|
47
|
-
const { audio:
|
|
41
|
+
const { audio: mainAudio, video: mainVideo } = currentMedia?.getSettings() ?? { audio: {}, video: {} };
|
|
42
|
+
const { audio: previewAudio, video: previewVideo } = previewMedia?.getSettings() ?? { audio: {}, video: {} };
|
|
48
43
|
const changed = hasAudioSettingsChanged(mainAudio, previewAudio) ||
|
|
49
44
|
hasVideoSettingsChanged(mainVideo, previewVideo);
|
|
50
45
|
return changed;
|
|
@@ -55,18 +50,11 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
55
50
|
throttleInMS: DEFAULT_QUEUE_THROTTLE_MS,
|
|
56
51
|
delayInMS: DEFAULT_QUEUE_DELAY_MS,
|
|
57
52
|
dropLast: DEFAULT_QUEUE_DROP_LAST,
|
|
58
|
-
},
|
|
53
|
+
}, audioProcessors, videoProcessors, }) => {
|
|
59
54
|
const queue = createAsyncQueue(queueOptions);
|
|
60
55
|
const eventHandlers = {};
|
|
61
|
-
const release = () => {
|
|
62
|
-
if (isInitial(props.media.status)) {
|
|
63
|
-
props.discardMedia = true;
|
|
64
|
-
}
|
|
65
|
-
props.media = buildMedia(() => ({ release }));
|
|
66
|
-
return Promise.resolve();
|
|
67
|
-
};
|
|
68
56
|
const internalProps = {
|
|
69
|
-
media:
|
|
57
|
+
media: undefined,
|
|
70
58
|
updatingPreview: false,
|
|
71
59
|
updatingMain: false,
|
|
72
60
|
discardMedia: false,
|
|
@@ -127,30 +115,68 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
127
115
|
}
|
|
128
116
|
},
|
|
129
117
|
});
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
118
|
+
const getUserMedia = createGetUserMediaProcess({
|
|
119
|
+
getUserMedia: requestUserMediaWithRetry(() => Promise.resolve(getCurrentDevices())),
|
|
120
|
+
getCurrentDevices: () => Promise.resolve(getCurrentDevices()),
|
|
121
|
+
});
|
|
122
|
+
const processMedia = createMediaProcessor({
|
|
123
|
+
audioProcessors,
|
|
124
|
+
videoProcessors,
|
|
125
|
+
onProcessingError(error, track) {
|
|
126
|
+
logger.error({ error, track }, 'Failed to process media track');
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
const processAndUpdateMedia = async (media, tracks) => {
|
|
130
|
+
try {
|
|
131
|
+
const processedTracks = await processMedia(tracks);
|
|
132
|
+
for (const [idx, track] of processedTracks.entries()) {
|
|
133
|
+
const originTrack = tracks.at(idx);
|
|
134
|
+
assert(originTrack, 'Processed track should always has the original track in the same order');
|
|
135
|
+
// Only replace track when they are not the same
|
|
136
|
+
if (originTrack.id !== track.id) {
|
|
137
|
+
media.removeTrack(originTrack);
|
|
138
|
+
media.addTrack(track);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logger.error({ error, tracks }, 'Failed to process media tracks');
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const updateMediaProcess = createMediaUpdater({
|
|
147
|
+
getUserMedia,
|
|
148
|
+
getCurrentDevices: () => Promise.resolve(getCurrentDevices()),
|
|
149
|
+
shouldDiscardMedia: () => props.discardMedia,
|
|
150
|
+
onMediaTracksChanged: (media, tracks) => {
|
|
151
|
+
props.media = media;
|
|
152
|
+
queue.enqueue(async () => {
|
|
153
|
+
await processAndUpdateMedia(media, tracks);
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
});
|
|
133
157
|
const initFromMain = (mainMedia) => {
|
|
134
|
-
if (!mainMedia
|
|
158
|
+
if (!mainMedia?.stream) {
|
|
135
159
|
return;
|
|
136
160
|
}
|
|
137
|
-
|
|
138
|
-
.
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
try {
|
|
162
|
+
const clonedMedia = mainMedia.clone();
|
|
163
|
+
props.media = clonedMedia;
|
|
164
|
+
queue.enqueue(async () => {
|
|
165
|
+
await processAndUpdateMedia(clonedMedia, clonedMedia.getTracks());
|
|
166
|
+
});
|
|
167
|
+
props.audioInput = mainMedia.audioInput;
|
|
168
|
+
props.videoInput = mainMedia.videoInput;
|
|
169
|
+
props.originalMainAudioInput = mainMedia.audioInput;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
143
172
|
logger.error({ error }, 'Failed to post process media');
|
|
144
|
-
}
|
|
145
|
-
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
146
175
|
props.initialized = true;
|
|
147
176
|
if (props.discardMedia) {
|
|
148
|
-
void props.media
|
|
177
|
+
void props.media?.release();
|
|
149
178
|
}
|
|
150
|
-
}
|
|
151
|
-
props.audioInput = mainMedia.audioInput;
|
|
152
|
-
props.videoInput = mainMedia.videoInput;
|
|
153
|
-
props.originalMainAudioInput = mainMedia.audioInput;
|
|
179
|
+
}
|
|
154
180
|
};
|
|
155
181
|
const mainMedia = getCurrentMedia();
|
|
156
182
|
if (mainMedia?.stream) {
|
|
@@ -165,31 +191,29 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
165
191
|
await updateMainStream(constraints);
|
|
166
192
|
props.updatingMain = false;
|
|
167
193
|
};
|
|
168
|
-
const mediaPipeline = createMediaPipeline(() => [
|
|
169
|
-
createGetUserMediaProcess(getUM, getCurrentDevices, {
|
|
170
|
-
initialMedia: props.media,
|
|
171
|
-
scope: 'PreviewStreamController',
|
|
172
|
-
}),
|
|
173
|
-
...processors,
|
|
174
|
-
]);
|
|
175
194
|
const updatePreviewMedia = async (constraints) => {
|
|
176
195
|
logger.debug({ constraints }, 'Requesting a new preview stream');
|
|
177
|
-
await props.media
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
196
|
+
await updateMediaProcess(constraints, props.media);
|
|
197
|
+
logger.debug({ media: props.media }, 'Preview media updated');
|
|
198
|
+
};
|
|
199
|
+
const releaseAudio = async () => {
|
|
200
|
+
for (const track of props.media?.getAudioTracks() ?? []) {
|
|
201
|
+
await track.release();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const releaseVideo = async () => {
|
|
205
|
+
for (const track of props.media?.getVideoTracks() ?? []) {
|
|
206
|
+
await track.release();
|
|
182
207
|
}
|
|
183
|
-
props.media = media;
|
|
184
208
|
};
|
|
185
209
|
const updateAudioInput = async (input) => {
|
|
186
|
-
|
|
187
|
-
const request = {
|
|
188
|
-
audio: { device: { exact: input } },
|
|
189
|
-
video: props.videoInput,
|
|
190
|
-
};
|
|
210
|
+
const request = { audio: { device: { exact: input } } };
|
|
191
211
|
try {
|
|
212
|
+
props.audioInput = input;
|
|
192
213
|
props.updatingPreview = true;
|
|
214
|
+
if (props.audioInput === undefined) {
|
|
215
|
+
return await releaseAudio();
|
|
216
|
+
}
|
|
193
217
|
await updatePreviewMedia(request);
|
|
194
218
|
}
|
|
195
219
|
catch (error) {
|
|
@@ -227,13 +251,13 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
227
251
|
}
|
|
228
252
|
};
|
|
229
253
|
const updateVideoInput = async (input) => {
|
|
230
|
-
props.videoInput = input;
|
|
231
254
|
try {
|
|
255
|
+
props.videoInput = input;
|
|
232
256
|
props.updatingPreview = true;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
257
|
+
if (input === undefined) {
|
|
258
|
+
return await releaseVideo();
|
|
259
|
+
}
|
|
260
|
+
return await updatePreviewMedia({ video: { device: { exact: input } } });
|
|
237
261
|
}
|
|
238
262
|
catch (error) {
|
|
239
263
|
if (error instanceof Error) {
|
|
@@ -248,7 +272,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
248
272
|
};
|
|
249
273
|
const cleanup = async () => {
|
|
250
274
|
logger.debug('Cleanup preview controller');
|
|
251
|
-
await props.media
|
|
275
|
+
await props.media?.release();
|
|
252
276
|
cleanUpMainSubscription();
|
|
253
277
|
props.audioInput = undefined;
|
|
254
278
|
props.videoInput = undefined;
|
|
@@ -262,7 +286,7 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
262
286
|
hasAudioVideoProcessingSettingsChanged();
|
|
263
287
|
const applyChanges = async (force = false) => {
|
|
264
288
|
const { audioInput, videoInput } = props;
|
|
265
|
-
const { audio:
|
|
289
|
+
const { audio: previewAudio, video: previewVideo } = props.media?.getSettings() ?? { audio: {}, video: {} };
|
|
266
290
|
if (force || hasChanges()) {
|
|
267
291
|
const audioSettings = extractFeaturesToConstraints(AUDIO_SETTINGS_KEYS, previewAudio);
|
|
268
292
|
const videoSettings = extractFeaturesToConstraints(VIDEO_SETTINGS_KEYS, previewVideo);
|
|
@@ -273,8 +297,12 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
273
297
|
logger.info({ audioInput, videoInput, audioSettings, videoSettings }, 'Apply changes to main');
|
|
274
298
|
try {
|
|
275
299
|
await replaceMainStream({
|
|
276
|
-
audio:
|
|
277
|
-
|
|
300
|
+
audio: audioInput
|
|
301
|
+
? { device: audioInput, ...audioSettings }
|
|
302
|
+
: false,
|
|
303
|
+
video: videoInput
|
|
304
|
+
? { device: videoInput, ...videoSettings }
|
|
305
|
+
: false,
|
|
278
306
|
});
|
|
279
307
|
props.originalMainAudioInput = props.audioInput;
|
|
280
308
|
}
|
|
@@ -321,7 +349,10 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
321
349
|
}
|
|
322
350
|
}
|
|
323
351
|
};
|
|
324
|
-
const updateInput = (hasChanged, updater) => (
|
|
352
|
+
const updateInput = (hasChanged, updater) => (id) => {
|
|
353
|
+
const [input] = id
|
|
354
|
+
? getCurrentDevices().get(reverseDeviceId(id))
|
|
355
|
+
: [];
|
|
325
356
|
if (hasChanged(input)) {
|
|
326
357
|
queue.enqueue(async () => {
|
|
327
358
|
await updater(input);
|
package/dist/signals.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Signal, SignalVariant } from '@pexip/signal';
|
|
2
|
+
import type { IndexedDevices } from '@pexip/media-control';
|
|
2
3
|
import type { MediaSignals } from './types';
|
|
3
4
|
/**
|
|
4
5
|
* Create a general signal with consistent scoped name
|
|
@@ -22,4 +23,12 @@ export declare const REQUIRED_SIGNAL_KEYS: readonly ["onMediaChanged", "onVAD",
|
|
|
22
23
|
*
|
|
23
24
|
* @see REQUIRED_SIGNAL_KEYS
|
|
24
25
|
*/
|
|
25
|
-
export declare const createMediaSignals: <K extends "onDevicesChanged" | "
|
|
26
|
+
export declare const createMediaSignals: <K extends "onAddTrack" | "onAudioMuteStateChanged" | "onTrackReleased" | "onDevicesChanged" | "onRemoveTrack" | "onStatusChanged" | "onVideoMuteStateChanged" | "onUpdatingMedia" | keyof import("./types").StreamTrackSignals | "onStreamTrackMutedFinal" | "onStreamTrackUnmutedFinal" | "onStreamTrackEndedFinal" | "onStreamTrackEnabledFinal">(more: K[], scope?: string) => Pick<Required<MediaSignals>, "onMediaChanged" | "onVAD" | "onSilentDetected" | K>;
|
|
27
|
+
export type GeneratedMediaSignals<K extends keyof MediaSignals> = Pick<Required<MediaSignals>, K>;
|
|
28
|
+
/**
|
|
29
|
+
* Internal signals
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
export declare const internalSignals: {
|
|
33
|
+
onDevicesChanged: Signal<IndexedDevices>;
|
|
34
|
+
};
|
package/dist/signals.js
CHANGED
|
@@ -36,3 +36,12 @@ export const createMediaSignals = (more, scope = '') => {
|
|
|
36
36
|
[key]: createMediaSignal(`${signalScope}${key}`),
|
|
37
37
|
}), {});
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Internal signals
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
export const internalSignals = {
|
|
44
|
+
onDevicesChanged: createSignal({
|
|
45
|
+
name: 'media/devicesChanged',
|
|
46
|
+
}),
|
|
47
|
+
};
|
package/dist/status.d.ts
CHANGED
|
@@ -32,5 +32,8 @@ export declare const isDeviceInUse: UserMediaValidator;
|
|
|
32
32
|
export declare const isDevicesInUse: UserMediaValidator;
|
|
33
33
|
export declare const isVideoDeviceInUse: UserMediaValidator;
|
|
34
34
|
export declare const toDeniedDevices: (status?: UserMediaStatus) => DeniedDevices | undefined;
|
|
35
|
-
export declare const getPermissionStatus: (
|
|
36
|
-
|
|
35
|
+
export declare const getPermissionStatus: ({ audio, video, }: {
|
|
36
|
+
audio: PermissionState;
|
|
37
|
+
video: PermissionState;
|
|
38
|
+
}) => UserMediaStatus.Initial | UserMediaStatus.InitialPermissionsGranted | UserMediaStatus.InitialPermissionsNotGranted | UserMediaStatus.InitialPermissionsVideoInputDenied | UserMediaStatus.InitialPermissionsAudioInputDenied | UserMediaStatus.InitialPermissionsVideoInputGranted | UserMediaStatus.InitialPermissionsAudioInputGranted | UserMediaStatus.InitialPermissionsGrantedVideoInputDenied | UserMediaStatus.InitialPermissionsGrantedAudioInputDenied | UserMediaStatus.PermissionsRejected;
|
|
39
|
+
export declare const deriveInitialPermissionStatus: (prevStatus: UserMediaStatus, getPermissionState?: (anyActiveAudioTrack?: boolean | undefined, anyActiveVideoTrack?: boolean | undefined, getDevices?: (() => Promise<MediaDeviceInfo[]>) | undefined) => Promise<import("@pexip/media-control").InputDevicePermission>) => Promise<UserMediaStatus>;
|
package/dist/status.js
CHANGED
|
@@ -137,8 +137,7 @@ export const toDeniedDevices = (status) => {
|
|
|
137
137
|
return DeniedDevices.Both;
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
-
export const getPermissionStatus =
|
|
141
|
-
const { audio, video } = await getPermissionState();
|
|
140
|
+
export const getPermissionStatus = ({ audio, video, }) => {
|
|
142
141
|
if (audio === 'denied') {
|
|
143
142
|
if (video === 'denied') {
|
|
144
143
|
return UserMediaStatus.PermissionsRejected;
|
|
@@ -196,5 +195,6 @@ export const deriveInitialPermissionStatus = async (prevStatus, getPermissionSta
|
|
|
196
195
|
if (prevStatus === UserMediaStatus.DevicesInUse) {
|
|
197
196
|
return UserMediaStatus.InitialPermissionsGranted;
|
|
198
197
|
}
|
|
199
|
-
|
|
198
|
+
const permission = await getPermissionState();
|
|
199
|
+
return getPermissionStatus(permission);
|
|
200
200
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InputConstraintSet, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
|
|
1
|
+
import type { IndexedDevices, InputConstraintSet, InputDeviceConstraint, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
|
|
2
2
|
import type { RendererOptions, RenderEffects, Segmenter, SegmentationModel } from '@pexip/media-processor';
|
|
3
3
|
import type { Signal } from '@pexip/signal';
|
|
4
4
|
export type Unsubscribe = () => void;
|
|
@@ -22,8 +22,8 @@ export interface ExtendedMediaTrackSettings extends MediaTrackSettings {
|
|
|
22
22
|
}
|
|
23
23
|
export type ExtendedMediaTrackSettingsKey = keyof ExtendedMediaTrackSettings;
|
|
24
24
|
export interface MediaSettings {
|
|
25
|
-
audio
|
|
26
|
-
video
|
|
25
|
+
audio?: ExtendedMediaTrackSettings;
|
|
26
|
+
video?: ExtendedMediaTrackSettings;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* URL links needed for creating a denoise node for audio processing
|
|
@@ -44,29 +44,26 @@ export interface DenoiseParams {
|
|
|
44
44
|
}
|
|
45
45
|
export interface MediaAttributes {
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
47
|
+
* Indicating whether the media is active
|
|
48
|
+
*
|
|
49
|
+
* A stream is considered active if at least one of its MediaStreamTracks
|
|
50
|
+
* does not have its property MediaStreamTrack.readyState set to ended.
|
|
51
|
+
* Once every track has ended, the stream's active property becomes false.
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
readonly active: boolean;
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* A string containing a 36-character universally unique identifier (UUID)
|
|
56
|
+
* for the object.
|
|
56
57
|
*/
|
|
57
|
-
|
|
58
|
+
readonly id: string;
|
|
58
59
|
/**
|
|
59
|
-
* The
|
|
60
|
+
* The devices used for the media
|
|
60
61
|
*/
|
|
61
|
-
|
|
62
|
+
devices: IndexedDevices;
|
|
62
63
|
/**
|
|
63
|
-
*
|
|
64
|
+
* Media stream for the media
|
|
64
65
|
*/
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Video input device is used for the video track from the current stream
|
|
68
|
-
*/
|
|
69
|
-
videoInput?: MediaDeviceInfoLike;
|
|
66
|
+
stream: MediaStream;
|
|
70
67
|
/**
|
|
71
68
|
* The audio input device which is expected to be used for the current
|
|
72
69
|
* stream based on the provided constraints
|
|
@@ -91,6 +88,14 @@ export interface MediaAttributes {
|
|
|
91
88
|
* `undefined` means there is no such track from the stream
|
|
92
89
|
*/
|
|
93
90
|
videoMuted: boolean | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Audio input device is used for the audio track from the current stream
|
|
93
|
+
*/
|
|
94
|
+
audioInput?: MediaDeviceInfoLike;
|
|
95
|
+
/**
|
|
96
|
+
* Video input device is used for the video track from the current stream
|
|
97
|
+
*/
|
|
98
|
+
videoInput?: MediaDeviceInfoLike;
|
|
94
99
|
}
|
|
95
100
|
export interface Media extends MediaAttributes {
|
|
96
101
|
/**
|
|
@@ -109,19 +114,95 @@ export interface Media extends MediaAttributes {
|
|
|
109
114
|
* Release the media resources, e.g. camera/microphone
|
|
110
115
|
*/
|
|
111
116
|
release(): Promise<void>;
|
|
117
|
+
getConstraints(): MediaDeviceRequest;
|
|
118
|
+
setOriginalConstraints(constraints: MediaDeviceRequest): void;
|
|
119
|
+
getOriginalConstraints(): MediaDeviceRequest;
|
|
120
|
+
isAudioInputUnavailable(): boolean;
|
|
121
|
+
isVideoInputUnavailable(): boolean;
|
|
122
|
+
requestedAudio(): boolean;
|
|
123
|
+
requestedVideo(): boolean;
|
|
112
124
|
getSettings(): MediaSettings;
|
|
113
|
-
toJSON
|
|
125
|
+
toJSON(): unknown;
|
|
126
|
+
getTracks(): MediaTrack[];
|
|
127
|
+
getAudioTracks(): MediaTrack[];
|
|
128
|
+
getVideoTracks(): MediaTrack[];
|
|
129
|
+
addTrack(track: MediaTrack): void;
|
|
130
|
+
removeTrack(track: MediaTrack): void;
|
|
131
|
+
clone(): Media;
|
|
114
132
|
}
|
|
115
|
-
export
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
export interface MediaInit {
|
|
134
|
+
constraints: MediaDeviceRequest;
|
|
135
|
+
permission: {
|
|
136
|
+
audio: PermissionState;
|
|
137
|
+
video: PermissionState;
|
|
138
|
+
};
|
|
139
|
+
originalConstraints?: MediaDeviceRequest;
|
|
140
|
+
tracks: MediaTrack[];
|
|
141
|
+
devices: IndexedDevices;
|
|
142
|
+
status: UserMediaStatus;
|
|
143
|
+
stream: MediaStream | undefined;
|
|
144
|
+
signals?: MediaSignals;
|
|
145
|
+
}
|
|
146
|
+
export interface MediaTrack {
|
|
147
|
+
/**
|
|
148
|
+
* A unique identifier for the track same as `MediaStreamTrack.id` if
|
|
149
|
+
* `track` is available otherwise it uses the `id` from the construction or the
|
|
150
|
+
* `kind`. @see MediaTrackInit
|
|
151
|
+
*/
|
|
152
|
+
id: string;
|
|
153
|
+
kind: 'audioinput' | 'videoinput';
|
|
154
|
+
label?: string;
|
|
155
|
+
previousMediaTrack?: MediaTrack;
|
|
156
|
+
/**
|
|
157
|
+
* The MediaStreamTrack object representing the track
|
|
158
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack
|
|
159
|
+
*/
|
|
160
|
+
track?: MediaStreamTrack;
|
|
161
|
+
/**
|
|
162
|
+
* The `MediaDeviceInfo` object representing the device that the track is
|
|
163
|
+
* connected to. @see https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo
|
|
164
|
+
*/
|
|
165
|
+
input?: MediaDeviceInfoLike;
|
|
166
|
+
/**
|
|
167
|
+
* The `MediaDeviceInfo` object representing the device that the track is
|
|
168
|
+
* expected to be connected to. @see https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo
|
|
169
|
+
*/
|
|
170
|
+
expectedInput?: MediaDeviceInfoLike;
|
|
171
|
+
/**
|
|
172
|
+
* The current mute state of the track. `undefined` means the track is not available
|
|
173
|
+
*/
|
|
174
|
+
muted?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* The source track that it is originated from
|
|
177
|
+
*/
|
|
178
|
+
source: MediaTrack;
|
|
179
|
+
mute(toMute: boolean): void;
|
|
180
|
+
getSettings(): ExtendedMediaTrackSettings;
|
|
181
|
+
getConstraints(): InputDeviceConstraint;
|
|
182
|
+
applyConstraints(constraints: InputDeviceConstraint): Promise<void>;
|
|
183
|
+
release(): Promise<void>;
|
|
184
|
+
clone(): MediaTrack;
|
|
185
|
+
toJSON(): unknown;
|
|
186
|
+
}
|
|
187
|
+
export interface MediaTrackInit extends Partial<MediaTrack> {
|
|
188
|
+
kind: 'audioinput' | 'videoinput';
|
|
189
|
+
input: MediaDeviceInfoLike | undefined;
|
|
190
|
+
expectedInput?: MediaDeviceInfoLike | undefined;
|
|
191
|
+
constraints: InputDeviceConstraint;
|
|
192
|
+
overrideMute?: boolean;
|
|
193
|
+
signals?: MediaSignals;
|
|
194
|
+
}
|
|
195
|
+
export type Process<T> = (a: T) => Promise<MediaTrack>;
|
|
196
|
+
export type TrackProcessor = Process<MediaTrack>;
|
|
197
|
+
export type GetUserMediaProcess = (request: {
|
|
198
|
+
constraints: MediaDeviceRequest;
|
|
199
|
+
originalConstraints: MediaDeviceRequest;
|
|
200
|
+
permission: {
|
|
201
|
+
audio: PermissionState;
|
|
202
|
+
video: PermissionState;
|
|
203
|
+
};
|
|
204
|
+
currentMedia: Media | undefined;
|
|
205
|
+
}) => Promise<Media>;
|
|
125
206
|
/**
|
|
126
207
|
* Use which processor API to process the stream track
|
|
127
208
|
* `stream` - Use MediaStreamTrackProcessor, when available
|
|
@@ -361,7 +442,8 @@ export interface MediaOptions {
|
|
|
361
442
|
/**
|
|
362
443
|
* Media Processors
|
|
363
444
|
*/
|
|
364
|
-
|
|
445
|
+
audioProcessors: TrackProcessor[];
|
|
446
|
+
videoProcessors: TrackProcessor[];
|
|
365
447
|
/**
|
|
366
448
|
* A function to get the devices' mute state
|
|
367
449
|
*/
|
|
@@ -378,25 +460,27 @@ export interface MediaProps {
|
|
|
378
460
|
/**
|
|
379
461
|
* Current media
|
|
380
462
|
*/
|
|
381
|
-
media
|
|
463
|
+
media?: Media;
|
|
382
464
|
/**
|
|
383
465
|
* Current device
|
|
384
466
|
*/
|
|
385
|
-
devices:
|
|
467
|
+
devices: IndexedDevices;
|
|
386
468
|
/**
|
|
387
469
|
* When should we discard the requested MediaStream
|
|
388
470
|
*/
|
|
389
471
|
discardMedia: boolean;
|
|
472
|
+
updatingMedia: boolean;
|
|
390
473
|
}
|
|
391
474
|
export interface MediaController {
|
|
475
|
+
readonly updatingMedia: boolean;
|
|
392
476
|
/**
|
|
393
477
|
* Current Media
|
|
394
478
|
*/
|
|
395
|
-
media
|
|
479
|
+
readonly media?: Media;
|
|
396
480
|
/**
|
|
397
481
|
* Current device list
|
|
398
482
|
*/
|
|
399
|
-
devices:
|
|
483
|
+
readonly devices: IndexedDevices;
|
|
400
484
|
/**
|
|
401
485
|
* Execute the media pipeline immediately with provided constraints
|
|
402
486
|
*
|
|
@@ -454,18 +538,29 @@ export interface StreamTrackSignals {
|
|
|
454
538
|
*/
|
|
455
539
|
onStreamTrackEnabled: Signal<MediaStreamTrack>;
|
|
456
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Track state signals that used for the final track that the media pipeline
|
|
543
|
+
* produced
|
|
544
|
+
*/
|
|
545
|
+
export type StreamTrackSignalFinals = {
|
|
546
|
+
[key in keyof StreamTrackSignals as `${key}Final`]: Signal<MediaStreamTrack>;
|
|
547
|
+
};
|
|
457
548
|
export interface MediaChangesSignals {
|
|
458
|
-
|
|
549
|
+
onAddTrack: Signal<MediaStreamTrack>;
|
|
550
|
+
onAudioMuteStateChanged: Signal<boolean | undefined>;
|
|
551
|
+
onTrackReleased: Signal<MediaStreamTrack>;
|
|
552
|
+
onDevicesChanged: Signal<IndexedDevices>;
|
|
553
|
+
onMediaChanged: Signal<Media | undefined>;
|
|
554
|
+
onRemoveTrack: Signal<MediaStreamTrack>;
|
|
459
555
|
onStatusChanged: Signal<UserMediaStatus>;
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
onVideoMuteStateChanged: Signal<boolean>;
|
|
556
|
+
onVideoMuteStateChanged: Signal<boolean | undefined>;
|
|
557
|
+
onUpdatingMedia: Signal<boolean>;
|
|
463
558
|
}
|
|
464
559
|
export interface AudioDetectionSignals {
|
|
465
560
|
onVAD: Signal<undefined>;
|
|
466
561
|
onSilentDetected: Signal<boolean>;
|
|
467
562
|
}
|
|
468
|
-
export type MediaSignalsOptional =
|
|
563
|
+
export type MediaSignalsOptional = Omit<Partial<MediaChangesSignals>, 'onMediaChanged'> & Partial<StreamTrackSignals> & Partial<StreamTrackSignalFinals>;
|
|
469
564
|
export type MediaSignalsRequired = Pick<MediaChangesSignals, 'onMediaChanged'> & AudioDetectionSignals;
|
|
470
565
|
export type MediaSignals = MediaSignalsRequired & MediaSignalsOptional;
|
|
471
566
|
export declare enum DeniedDevices {
|
package/dist/userMedia.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import type { MediaDeviceRequest,
|
|
2
|
-
import type {
|
|
1
|
+
import type { MediaDeviceRequest, IndexedDevices } from '@pexip/media-control';
|
|
2
|
+
import type { GetUserMediaProcess, MediaSignals } from './types';
|
|
3
3
|
import { UserMediaStatus } from './types';
|
|
4
4
|
import { makeDeriveDeviceStatus } from './utils';
|
|
5
5
|
export type UserMedia = [MediaStream | undefined, UserMediaStatus];
|
|
6
6
|
export type GetUserMedia<T extends Promise<Iterable<unknown>> = Promise<UserMedia>> = (constraints: MediaDeviceRequest) => T;
|
|
7
|
+
export type GetCurrentDevices = () => Promise<IndexedDevices>;
|
|
7
8
|
export declare const toErrorDeviceStatus: (error: Error, deriveDeviceStatus: ReturnType<typeof makeDeriveDeviceStatus>) => UserMediaStatus;
|
|
8
9
|
export declare const mapErrorStatus: (status: UserMediaStatus) => (constraints: MediaDeviceRequest) => UserMediaStatus;
|
|
9
10
|
export declare const toSameDeviceStatus: ({ audio, video, }: {
|
|
10
11
|
audio: boolean;
|
|
11
12
|
video: boolean;
|
|
12
13
|
}) => UserMediaStatus.PermissionsGranted | UserMediaStatus.PermissionsGrantedFallback | UserMediaStatus.PermissionsGrantedFallbackAudioinput | UserMediaStatus.PermissionsGrantedFallbackVideoinput;
|
|
13
|
-
export declare const toOnlyDeviceStatus: (kind:
|
|
14
|
+
export declare const toOnlyDeviceStatus: (kind: 'audioinput' | 'videoinput', matched: boolean, devices: IndexedDevices) => UserMediaStatus.PermissionsOnlyAudioinput | UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices | UserMediaStatus.PermissionsOnlyAudioinputFallback | UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices | UserMediaStatus.PermissionsOnlyVideoinput | UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices | UserMediaStatus.PermissionsOnlyVideoinputFallback | UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
|
|
14
15
|
/**
|
|
15
16
|
* Try to come up with an error level according to provided UserMediaStatus and
|
|
16
17
|
* MediaDeviceRequest
|
|
@@ -20,19 +21,20 @@ export declare const toOnlyDeviceStatus: (kind: Extract<MediaDeviceKind, 'audioi
|
|
|
20
21
|
* @param constraints - The constraints used for the request
|
|
21
22
|
*/
|
|
22
23
|
export declare const deriveErrorLevel: (error: Error, status: UserMediaStatus, constraints: MediaDeviceRequest) => "error" | "warn";
|
|
23
|
-
export declare const requestUserMedia: (
|
|
24
|
+
export declare const requestUserMedia: (getCurrentDevices: GetCurrentDevices, getMedia?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia;
|
|
24
25
|
export declare const mergeNoDeviceStatus: (constraints: MediaDeviceRequest, anyDevices: {
|
|
25
26
|
audio: boolean;
|
|
26
27
|
video: boolean;
|
|
27
28
|
}, status: UserMediaStatus) => UserMediaStatus;
|
|
28
|
-
export declare const requestUserMediaWithRetry: (
|
|
29
|
+
export declare const requestUserMediaWithRetry: (getCurrentDevices: GetCurrentDevices, createRequestUserMedia?: (getCurrentDevices: GetCurrentDevices, getMedia?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia, gUM?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia;
|
|
29
30
|
interface Options {
|
|
30
|
-
|
|
31
|
+
getUserMedia: GetUserMedia;
|
|
32
|
+
getCurrentDevices: GetCurrentDevices;
|
|
33
|
+
signals?: MediaSignals;
|
|
31
34
|
scope?: string;
|
|
32
35
|
}
|
|
33
|
-
type CreateGetUserMediaProcess = (getUserMedia: GetUserMedia, getCurrentDevices: () => MediaDeviceInfoLike[], options: Options) => Process<MediaDeviceRequest>;
|
|
34
36
|
/**
|
|
35
37
|
* A process to get user media
|
|
36
38
|
*/
|
|
37
|
-
export declare const createGetUserMediaProcess:
|
|
39
|
+
export declare const createGetUserMediaProcess: ({ getUserMedia, getCurrentDevices, signals, scope, }: Options) => GetUserMediaProcess;
|
|
38
40
|
export {};
|