@pexip/media 18.3.0 → 18.5.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 +38 -0
- package/README.md +82 -151
- package/dist/media.js +22 -6
- package/dist/status.d.ts +1 -0
- package/dist/status.js +1 -0
- package/dist/utils.d.ts +6 -1
- package/dist/utils.js +11 -0
- package/package.json +12 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @pexip/media
|
|
2
2
|
|
|
3
|
+
## 18.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f146682: Make `delegate` prop to be a fn
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- d98fe9d: Move to module type.
|
|
12
|
+
- Updated dependencies [d98fe9d]
|
|
13
|
+
- Updated dependencies [acd7aba]
|
|
14
|
+
- Updated dependencies [f146682]
|
|
15
|
+
- @pexip/media-processor@18.5.0
|
|
16
|
+
- @pexip/media-control@18.5.0
|
|
17
|
+
- @pexip/signal@16.7.1
|
|
18
|
+
- @pexip/utils@16.12.1
|
|
19
|
+
|
|
20
|
+
## 18.4.0
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- 9179b97: Added hasPtzFeature function to utils.ts
|
|
25
|
+
|
|
26
|
+
Added isDevicesInUse function to status.ts
|
|
27
|
+
|
|
28
|
+
Added isTryingToRequestSameUsedAudioDevice /
|
|
29
|
+
isTryingToRequestSameUsedVideoDevice check to updateMedia function in
|
|
30
|
+
media.ts. This avoids infinity reloads in Firefox if a blocked (e.g. by
|
|
31
|
+
another application) device is selected.
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [36d5672]
|
|
34
|
+
- Updated dependencies [4bfce08]
|
|
35
|
+
- Updated dependencies [31e4c17]
|
|
36
|
+
- @pexip/media-processor@18.4.0
|
|
37
|
+
- @pexip/media-control@18.4.0
|
|
38
|
+
- @pexip/signal@16.7.0
|
|
39
|
+
- @pexip/utils@16.12.0
|
|
40
|
+
|
|
3
41
|
## 18.3.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -53,21 +53,6 @@ that's specified from the
|
|
|
53
53
|
[Media Capture and Streams](https://w3c.github.io/mediacapture-main) spec, we
|
|
54
54
|
have extended some additional media features as the following:
|
|
55
55
|
|
|
56
|
-
- Able to pass `MediaDeviceInfo` directly to audio/video
|
|
57
|
-
- Add `vad` to use our own implementation of Voice Activity Detection feature
|
|
58
|
-
- Add `asd` to use our own implementation of Audio Signal Detection feature
|
|
59
|
-
- Add `denoise` to use our own implementation of noise suppression
|
|
60
|
-
- Add `backgroundBlurAmount` to adjust the blur amount when using the background
|
|
61
|
-
blur feature
|
|
62
|
-
- Add `edgeBlurAmount` to adjust the blur amount for edge of the segmented
|
|
63
|
-
person
|
|
64
|
-
- Add `foregroundThreshold` to adjust the threshold to be considered as
|
|
65
|
-
foreground aka the segmented person
|
|
66
|
-
- Add `bgImageUrl` for the image to be used for background replacement
|
|
67
|
-
- Add `videoSegmentation` for specified effects of video segmentation
|
|
68
|
-
- Add `videoSegmentationModel` for specifying segmentation model to be used
|
|
69
|
-
- Add `flipHorizontal` to flip horizontally
|
|
70
|
-
|
|
71
56
|
```typescript
|
|
72
57
|
export interface InputConstraintSet extends MediaTrackConstraints {
|
|
73
58
|
/**
|
|
@@ -100,9 +85,10 @@ export interface InputConstraintSet extends MediaTrackConstraints {
|
|
|
100
85
|
*/
|
|
101
86
|
asd?: ConstrainBoolean;
|
|
102
87
|
/**
|
|
103
|
-
*
|
|
88
|
+
* Audio Signal Detection for the purpose of checking if the audio input is
|
|
89
|
+
* hardware muted or unusable
|
|
104
90
|
*/
|
|
105
|
-
|
|
91
|
+
mixWithAdditionalMedia?: ConstrainBoolean;
|
|
106
92
|
/**
|
|
107
93
|
* Blur size/level parameter when using video segmentation with `blur`
|
|
108
94
|
* effects
|
|
@@ -119,7 +105,26 @@ export interface InputConstraintSet extends MediaTrackConstraints {
|
|
|
119
105
|
/**
|
|
120
106
|
* Image Url that is being used for video overlay effects
|
|
121
107
|
*/
|
|
122
|
-
|
|
108
|
+
backgroundImageUrl?: ConstrainDOMString;
|
|
109
|
+
/**
|
|
110
|
+
* The ratio to be used for smoothing segmentation mask
|
|
111
|
+
*/
|
|
112
|
+
maskCombineRatio?: ConstrainDouble;
|
|
113
|
+
/**
|
|
114
|
+
* Image Url that is being used for video overlay effects
|
|
115
|
+
*/
|
|
116
|
+
resizeMode?: ConstrainDOMString;
|
|
117
|
+
/**
|
|
118
|
+
* Current pan, tilt and zoom level for a PTZ controllable camera on web
|
|
119
|
+
*/
|
|
120
|
+
pan?: boolean;
|
|
121
|
+
tilt?: boolean;
|
|
122
|
+
zoom?: boolean;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Content Hint for the track to apply
|
|
126
|
+
*/
|
|
127
|
+
contentHint?: ConstrainDOMString;
|
|
123
128
|
}
|
|
124
129
|
```
|
|
125
130
|
|
|
@@ -127,60 +132,35 @@ export interface InputConstraintSet extends MediaTrackConstraints {
|
|
|
127
132
|
|
|
128
133
|
```typescript
|
|
129
134
|
import {createMedia, createMediaSignals, UserMediaStatus} from '@pexip/media';
|
|
130
|
-
import type {
|
|
135
|
+
import type {RenderEffects, Segmenters, Media} from '@pexip/media';
|
|
131
136
|
import {
|
|
132
|
-
|
|
133
|
-
createMediapipeSegmenter,
|
|
134
|
-
createPsySegSegmenter,
|
|
137
|
+
createSegmenter,
|
|
135
138
|
createCanvasTransform,
|
|
136
|
-
createVideoTrackProcessor,
|
|
137
|
-
createVideoTrackProcessorWithFallback,
|
|
138
139
|
createVideoProcessor,
|
|
139
|
-
PROCESSING_WIDTH,
|
|
140
|
-
PROCESSING_HEIGHT,
|
|
141
|
-
FOREGROUND_THRESHOLD,
|
|
142
|
-
BACKGROUND_BLUR_AMOUNT,
|
|
143
|
-
EDGE_BLUR_AMOUNT,
|
|
144
|
-
FLIP_HORIZONTAL,
|
|
145
140
|
} from '@pexip/media-processor';
|
|
146
141
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
monitor: boolean;
|
|
160
|
-
channelCount: number;
|
|
161
|
-
}
|
|
162
|
-
export const mediaState: State = {
|
|
163
|
-
mute: {
|
|
142
|
+
// App states
|
|
143
|
+
const states: {
|
|
144
|
+
muted: {video: boolean; audio: boolean};
|
|
145
|
+
effects: RenderEffects;
|
|
146
|
+
foregroundThreshold: number;
|
|
147
|
+
backgroundBlurAmount: number;
|
|
148
|
+
maskCombineRatio: number;
|
|
149
|
+
backgroundImageUrl: string;
|
|
150
|
+
media?: Media;
|
|
151
|
+
status: UserMediaStatus;
|
|
152
|
+
} = {
|
|
153
|
+
muted: {
|
|
164
154
|
audio: false,
|
|
165
155
|
video: false,
|
|
166
156
|
},
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
width: PROCESSING_WIDTH,
|
|
175
|
-
height: PROCESSING_HEIGHT,
|
|
176
|
-
frameRate: 30,
|
|
177
|
-
bgImageUrl: '',
|
|
178
|
-
monitor: false,
|
|
179
|
-
channelCount: 1,
|
|
180
|
-
foregroundThreshold: FOREGROUND_THRESHOLD,
|
|
181
|
-
backgroundBlurAmount: BACKGROUND_BLUR_AMOUNT,
|
|
182
|
-
edgeBlurAmount: EDGE_BLUR_AMOUNT,
|
|
183
|
-
flipHorizontal: FLIP_HORIZONTAL,
|
|
157
|
+
// Could be `none`, `blur` or `overlay`
|
|
158
|
+
effects: 'none',
|
|
159
|
+
foregroundThreshold: 0.5, // 0.0 - 1.0
|
|
160
|
+
backgroundBlurAmount: 30, // 0 - 100
|
|
161
|
+
maskCombineRatio: 0.5, // 0.0 - 1.0
|
|
162
|
+
backgroundImageUrl: '/some/path/to/an/image/for/background/replacement.png',
|
|
163
|
+
status: UserMediaStatus.Initial,
|
|
184
164
|
};
|
|
185
165
|
|
|
186
166
|
// Create required signals with additional `onDevicesChanged`,
|
|
@@ -191,71 +171,61 @@ export const signals = createMediaSignals('demo', [
|
|
|
191
171
|
'onStreamTrackEnabled',
|
|
192
172
|
]);
|
|
193
173
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
174
|
+
// Setting the path to that `@mediapipe/tasks-vision` assets
|
|
175
|
+
// It will be passed direct to
|
|
176
|
+
// [FilesetResolver.forVisionTasks()](https://ai.google.dev/edge/api/mediapipe/js/tasks-vision.filesetresolver#filesetresolverforvisiontasks)
|
|
177
|
+
const tasksVisionBasePath =
|
|
178
|
+
'A base path to specify the directory the Wasm files should be loaded from';
|
|
179
|
+
|
|
180
|
+
const modelAsset = {
|
|
181
|
+
/**
|
|
182
|
+
* Path to mediapipe selfie segmentation model asset
|
|
183
|
+
*/
|
|
184
|
+
path: 'A path to selfie segmentation model',
|
|
185
|
+
modelName: 'selfie' as const,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const segmenter = createSegmenter(tasksVisionBasePath, {modelAssets});
|
|
189
|
+
// Create a processing transformer and set the effects to `blur`
|
|
190
|
+
const transformer = createCanvasTransform(segmenter, {
|
|
191
|
+
effects: states.effects,
|
|
192
|
+
foregroundThreshold: states.foregroundThreshold,
|
|
193
|
+
backgroundBlurAmount: states.backgroundBlurAmount,
|
|
194
|
+
maskCombineRatio: states.maskCombineRatio,
|
|
195
|
+
backgroundImageUrl: states.backgroundImageUrl,
|
|
205
196
|
});
|
|
206
197
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
document.baseURI,
|
|
212
|
-
);
|
|
213
|
-
const mediapipeSegmenter = createMediapipeSegmenter('selfie_segmentation', {
|
|
214
|
-
modelType: 'general',
|
|
215
|
-
processingWidth: PROCESSING_WIDTH,
|
|
216
|
-
processingHeight: PROCESSING_HEIGHT,
|
|
217
|
-
gluePath: selfieJs.href,
|
|
198
|
+
const delegate = () => (isChromium() ? 'GPU' : 'CPU');
|
|
199
|
+
const selfie = createSegmenter(taskVisionBasePath, {
|
|
200
|
+
modelAsset,
|
|
201
|
+
delegate,
|
|
218
202
|
});
|
|
219
203
|
|
|
220
|
-
const segmenters: Segmenters = {
|
|
221
|
-
|
|
204
|
+
export const segmenters: Partial<Segmenters> = {
|
|
205
|
+
selfie,
|
|
222
206
|
};
|
|
223
207
|
|
|
224
|
-
const transformer = createCanvasTransform(mediapipeSegmenter, {
|
|
225
|
-
effects: mediaState.videoSegmentation,
|
|
226
|
-
width: mediaState.width,
|
|
227
|
-
height: mediaState.height,
|
|
228
|
-
foregroundThreshold: mediaState.foregroundThreshold,
|
|
229
|
-
backgroundBlurAmount: mediaState.backgroundBlurAmount,
|
|
230
|
-
edgeBlurAmount: mediaState.edgeBlurAmount,
|
|
231
|
-
flipHorizontal: mediaState.flipHorizontal,
|
|
232
|
-
});
|
|
233
208
|
const videoProcessor = createVideoProcessor([transformer]);
|
|
234
209
|
|
|
235
210
|
const videoStreamProcessor = createVideoStreamProcess({
|
|
236
|
-
trackProcessorAPI: 'stream', // Use Stream API when available
|
|
237
211
|
shouldEnable: () => true, // Always Process Video Track
|
|
238
|
-
videoSegmentationModel: 'mediapipeSelfie', // Segmentation Modal
|
|
239
212
|
segmenters,
|
|
240
213
|
transformer,
|
|
241
214
|
videoProcessor,
|
|
242
|
-
processingWidth: mediaState.width,
|
|
243
|
-
processingHeight: mediaState.height,
|
|
244
|
-
onError: error => {
|
|
245
|
-
console.error(error);
|
|
246
|
-
},
|
|
247
215
|
});
|
|
248
216
|
|
|
249
217
|
// Instantiate the media object
|
|
250
218
|
export const mediaController = createMedia({
|
|
251
|
-
getMuteState: () =>
|
|
219
|
+
getMuteState: () => states.muted,
|
|
252
220
|
signals,
|
|
253
|
-
mediaProcessors: [videoStreamProcessor
|
|
221
|
+
mediaProcessors: [videoStreamProcessor],
|
|
254
222
|
});
|
|
255
223
|
|
|
256
224
|
// Hook-up the signals to get the update
|
|
257
225
|
// Subscribe the onMediaChanged signal to get the latest Media object change event
|
|
258
|
-
signals.onMediaChanged.add(media =>
|
|
226
|
+
signals.onMediaChanged.add(media => {
|
|
227
|
+
states.media = media;
|
|
228
|
+
}); // Assume there is a local function `setLocalMedia`
|
|
259
229
|
// Subscribe the onStatusChanged signal to get the latest Media status change event
|
|
260
230
|
signals.onStatusChanged.add(media => setMediaStatus(media)); // Assume there is a local function `setMediaStatus`
|
|
261
231
|
// Subscribe onStreamTrackEnabled signal to get the MediaStreamTrack['enabled'] change event
|
|
@@ -265,16 +235,13 @@ signals.onStreamTrackEnabled.add(track => {
|
|
|
265
235
|
|
|
266
236
|
// Later we can initialize a gUM to get a MediaStream
|
|
267
237
|
mediaController.getUserMedia({
|
|
268
|
-
audio:
|
|
269
|
-
sampleRate: 48000, // Ideal sample rate for our own noise suppression implementation
|
|
270
|
-
denoise: true, // Use our own implementation of noise suppression
|
|
271
|
-
noiseSuppression: false, // Disable the built-in noise suppression
|
|
272
|
-
vad: true, // Use Voice Activity Detection
|
|
273
|
-
asd: true, // Use Audio Signal Detection to check if the microphone is malfunctioned
|
|
274
|
-
},
|
|
238
|
+
audio: true,
|
|
275
239
|
video: {
|
|
276
240
|
videoSegmentation: 'blur', // Use background blur
|
|
277
|
-
|
|
241
|
+
foregroundThreshold: states.foregroundThreshold,
|
|
242
|
+
backgroundBlurAmount: states.backgroundBlurAmount,
|
|
243
|
+
maskCombineRatio: states.maskCombineRatio,
|
|
244
|
+
backgroundImageUrl: states.backgroundImageUrl,
|
|
278
245
|
},
|
|
279
246
|
});
|
|
280
247
|
|
|
@@ -314,39 +281,3 @@ await mediaController.media.applyConstraints({
|
|
|
314
281
|
audio: {denoise: false, noiseSuppression: true},
|
|
315
282
|
});
|
|
316
283
|
```
|
|
317
|
-
|
|
318
|
-
### `createPreviewController`
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
const {createPreviewController} = require('@pexip/media');
|
|
322
|
-
|
|
323
|
-
// Some states from the App
|
|
324
|
-
interface AppState {
|
|
325
|
-
media: Media;
|
|
326
|
-
devices: MediaDeviceInfo[];
|
|
327
|
-
}
|
|
328
|
-
// Some functions from the App
|
|
329
|
-
const updateStream = (constraints: MediaDeviceRequest) => Promise<void>;
|
|
330
|
-
|
|
331
|
-
const onMediaChangedSignal = createSignal<Media>();
|
|
332
|
-
|
|
333
|
-
// Instantiate the controller
|
|
334
|
-
const controller = createPreviewController({
|
|
335
|
-
getDesiredResolution: () => ({}),
|
|
336
|
-
getCurrentDevices: () => state.devices,
|
|
337
|
-
getCurrentMedia: () => state.media,
|
|
338
|
-
updateMainStream: updateStream,
|
|
339
|
-
mediaSignal: onMediaChangedSignal,
|
|
340
|
-
getSegmentationOptions: () => ({
|
|
341
|
-
effects: mediaState.videoSegmentation,
|
|
342
|
-
erode: mediaState.erode,
|
|
343
|
-
blurLevel: mediaState.blurLevel,
|
|
344
|
-
}),
|
|
345
|
-
videoProcessingEnabled: true,
|
|
346
|
-
updateMainStream: mc.getUserMediaAsync,
|
|
347
|
-
onEnded: () => {
|
|
348
|
-
close();
|
|
349
|
-
},
|
|
350
|
-
psySegScriptLoaded: true,
|
|
351
|
-
});
|
|
352
|
-
```
|
package/dist/media.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { subscribe, MediaEventType, MediaDeviceFailure, createStreamTrackEventSubscriptions, getDevices, isRequestedResolution, mergeConstraints, shouldRequestDevice, } from '@pexip/media-control';
|
|
1
|
+
import { subscribe, MediaEventType, MediaDeviceFailure, createStreamTrackEventSubscriptions, getDevices, isRequestedResolution, mergeConstraints, shouldRequestDevice, extractConstrainDevice, } from '@pexip/media-control';
|
|
2
2
|
import { createAsyncQueue, isEmpty } from '@pexip/utils';
|
|
3
|
-
import { getPermissionStatus, deriveInitialPermissionStatus, isInitialPermissions, isInitialPermissionsGranted, } from './status';
|
|
3
|
+
import { getPermissionStatus, deriveInitialPermissionStatus, isInitialPermissions, isInitialPermissionsGranted, isVideoDeviceInUse, isDevicesInUse, isAudioDeviceInUse, } from './status';
|
|
4
4
|
import { UserMediaStatus } from './types';
|
|
5
5
|
import { createModuleLogger, logger } from './logger';
|
|
6
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';
|
|
7
|
+
import { createMediaPipeline, createMediaProcess, buildMedia, getDevicesChanges, shallowCopy, wrapToJSON, hasSettingsChanged, AUDIO_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, MIXING_SETTINGS_KEYS, applyContentHint, hasPtzFeature, } from './utils';
|
|
8
8
|
import { isMedia } from './typeGuard';
|
|
9
9
|
import { updateFeatureProps as getVideoFeatures } from './videoProcessor';
|
|
10
10
|
import { updateFeatureProps as getAudioFeatures } from './audioProcessor';
|
|
@@ -171,19 +171,35 @@ export const createMedia = ({ getMuteState, signals, mediaProcessors, getDefault
|
|
|
171
171
|
if (!constraints.audio && !constraints.video) {
|
|
172
172
|
throw new Error(MediaDeviceFailure.MissingConstraintsError);
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
const
|
|
174
|
+
// Check if the audio device was requested before and is currently in use by another application
|
|
175
|
+
const isTryingToRequestSameUsedAudioDevice = (isAudioDeviceInUse(props.media.status) ||
|
|
176
|
+
isDevicesInUse(props.media.status)) &&
|
|
177
|
+
props.media.expectedAudioInput?.deviceId &&
|
|
178
|
+
props.media.expectedAudioInput?.deviceId ===
|
|
179
|
+
extractConstrainDevice(constraints.audio)?.[0]?.[0]?.deviceId;
|
|
180
|
+
// Check if the video device was requested before and is currently in use by another application
|
|
181
|
+
const isTryingToRequestSameUsedVideoDevice = (isVideoDeviceInUse(props.media.status) ||
|
|
182
|
+
isDevicesInUse(props.media.status)) &&
|
|
183
|
+
props.media.expectedVideoInput?.deviceId &&
|
|
184
|
+
props.media.expectedVideoInput?.deviceId ===
|
|
185
|
+
extractConstrainDevice(constraints?.video)?.[0]?.[0]?.deviceId;
|
|
186
|
+
const shouldRequestAudio = !isTryingToRequestSameUsedAudioDevice &&
|
|
187
|
+
shouldRequestDevice(constraints.audio, props.media?.rawStream?.getAudioTracks() ?? [], props.devices.filter(device => device.kind === 'audioinput'));
|
|
188
|
+
const shouldRequestVideo = !isTryingToRequestSameUsedVideoDevice &&
|
|
189
|
+
shouldRequestDevice(constraints.video, props.media?.rawStream?.getVideoTracks() ?? [], props.devices.filter(device => device.kind === 'videoinput'));
|
|
176
190
|
const { audio: [prevAudioSettings], video: [prevVideoSettings], } = props.media.getSettings();
|
|
177
191
|
const videoFeatures = getVideoFeatures(constraints.video, {});
|
|
178
192
|
const audioFeatures = getAudioFeatures(constraints.audio, {});
|
|
179
193
|
const mixingFeatures = getMixingFeatures(constraints.audio, {});
|
|
180
194
|
const hasRequestedResolution = ['blur', 'overlay'].includes(videoFeatures.videoSegmentation ?? '') || // Skip when there is a render effect to modify the video
|
|
181
195
|
!props.devices.some(device => device.kind === 'videoinput' && device.label) || // Skip when no authorized video found
|
|
196
|
+
isTryingToRequestSameUsedVideoDevice ||
|
|
182
197
|
isRequestedResolution(constraints.video, props.media?.videoInput);
|
|
183
198
|
const audioFeaturesChanged = hasSettingsChanged(AUDIO_SETTINGS_KEYS)(prevAudioSettings, audioFeatures);
|
|
184
199
|
const videoFeaturesChanged = hasSettingsChanged(VIDEO_SETTINGS_KEYS)(prevVideoSettings, videoFeatures);
|
|
185
200
|
const mixingFeaturesChanged = hasSettingsChanged(MIXING_SETTINGS_KEYS)(prevAudioSettings, mixingFeatures);
|
|
186
|
-
const ptzFeaturesChanges =
|
|
201
|
+
const ptzFeaturesChanges = hasPtzFeature() &&
|
|
202
|
+
hasSettingsChanged(['pan', 'tilt', 'zoom'])(prevVideoSettings, videoFeatures);
|
|
187
203
|
logger.debug({
|
|
188
204
|
constraints,
|
|
189
205
|
stream: props.media.stream,
|
package/dist/status.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare const isInitialPermissionsNotGranted: UserMediaValidator;
|
|
|
29
29
|
export declare const isInitialPermissionsGranted: UserMediaValidator;
|
|
30
30
|
export declare const isAudioDeviceInUse: UserMediaValidator;
|
|
31
31
|
export declare const isDeviceInUse: UserMediaValidator;
|
|
32
|
+
export declare const isDevicesInUse: UserMediaValidator;
|
|
32
33
|
export declare const isVideoDeviceInUse: UserMediaValidator;
|
|
33
34
|
export declare const toDeniedDevices: (status?: UserMediaStatus) => DeniedDevices | undefined;
|
|
34
35
|
export declare const getPermissionStatus: (getPermissionState?: (anyActiveStream?: boolean | undefined) => Promise<import("@pexip/media-control").InputDevicePermission>) => Promise<UserMediaStatus.Initial | UserMediaStatus.InitialPermissionsGranted | UserMediaStatus.InitialPermissionsNotGranted | UserMediaStatus.InitialPermissionsVideoInputDenied | UserMediaStatus.InitialPermissionsAudioInputDenied | UserMediaStatus.InitialPermissionsVideoInputGranted | UserMediaStatus.InitialPermissionsAudioInputGranted | UserMediaStatus.InitialPermissionsGrantedVideoInputDenied | UserMediaStatus.InitialPermissionsGrantedAudioInputDenied | UserMediaStatus.PermissionsRejected>;
|
package/dist/status.js
CHANGED
|
@@ -121,6 +121,7 @@ export const isDeviceInUse = status => [
|
|
|
121
121
|
UserMediaStatus.AudioDeviceInUse,
|
|
122
122
|
UserMediaStatus.VideoDeviceInUse,
|
|
123
123
|
].includes(status);
|
|
124
|
+
export const isDevicesInUse = status => status === UserMediaStatus.DevicesInUse;
|
|
124
125
|
export const isVideoDeviceInUse = status => status === UserMediaStatus.VideoDeviceInUse;
|
|
125
126
|
export const toDeniedDevices = (status) => {
|
|
126
127
|
if (!status) {
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
|
|
2
2
|
import { UserMediaStatus } from './types';
|
|
3
3
|
import type { Media, MediaAttributes, Pipeline, Process, ProcessMedia, ExtendedMediaTrackSettingsKey, ExtendedMediaTrackSettings } from './types';
|
|
4
4
|
export declare const makeDeriveDeviceStatus: (constraints: MediaDeviceRequest) => (audio: UserMediaStatus, video: UserMediaStatus, both: UserMediaStatus) => UserMediaStatus;
|
|
@@ -105,4 +105,9 @@ export declare const getBlurKernelSize: (percentage: number, height: number, max
|
|
|
105
105
|
* @param track - The track to be applied
|
|
106
106
|
*/
|
|
107
107
|
export declare const applyContentHint: <T extends "" | "speech" | "speech-recognition" | "music" | "motion" | "detail" | "text">(hint?: T | undefined) => (track: MediaStreamTrack) => void;
|
|
108
|
+
/**
|
|
109
|
+
* Check if the browser supports PTZ feature
|
|
110
|
+
* @returns true if the browser supports PTZ feature, otherwise false
|
|
111
|
+
*/
|
|
112
|
+
export declare const hasPtzFeature: () => boolean;
|
|
108
113
|
export {};
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyConstraints, createTrackDevicesChanges, extractConstraintsWithKeys, findDeviceFromConstraints, muteStreamTrack, relaxInputConstraint, stopMediaStream, isAudioInput, isVideoInput, } from '@pexip/media-control';
|
|
2
2
|
import { calculateMaxBlurPass } from '@pexip/media-processor';
|
|
3
|
+
import { hasOwn } from '@pexip/utils';
|
|
3
4
|
import { UserMediaStatus } from './types';
|
|
4
5
|
import { isOverConstrained } from './status';
|
|
5
6
|
import { isMedia } from './typeGuard';
|
|
@@ -359,3 +360,13 @@ export const applyContentHint = (hint) => (track) => {
|
|
|
359
360
|
track.contentHint = hint;
|
|
360
361
|
}
|
|
361
362
|
};
|
|
363
|
+
/**
|
|
364
|
+
* Check if the browser supports PTZ feature
|
|
365
|
+
* @returns true if the browser supports PTZ feature, otherwise false
|
|
366
|
+
*/
|
|
367
|
+
export const hasPtzFeature = () => {
|
|
368
|
+
const supports = navigator.mediaDevices.getSupportedConstraints();
|
|
369
|
+
return (hasOwn(supports, 'pan') &&
|
|
370
|
+
hasOwn(supports, 'tilt') &&
|
|
371
|
+
hasOwn(supports, 'zoom'));
|
|
372
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pexip/media",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.5.0",
|
|
4
4
|
"description": "Home for media related stuff",
|
|
5
5
|
"homepage": "https://gitlab.com/pexip/zoo",
|
|
6
6
|
"bugs": "https://gitlab.com/pexip/zoo/issues",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"url": "https://gitlab.com/pexip/zoo.git",
|
|
10
10
|
"directory": "src/aquila/packages/media"
|
|
11
11
|
},
|
|
12
|
+
"type": "module",
|
|
12
13
|
"license": "Apache-2.0",
|
|
13
14
|
"author": "Terrence Lam <terrence@tlam.dev>",
|
|
14
15
|
"module": "dist/index.js",
|
|
@@ -25,21 +26,24 @@
|
|
|
25
26
|
"clean": "rm -fr dist",
|
|
26
27
|
"dev": "pexip-bundler dev",
|
|
27
28
|
"prepack": "yarn clean && yarn build --no-sourcemap",
|
|
28
|
-
"test": "
|
|
29
|
+
"test": "jest",
|
|
29
30
|
"check-format": "prettier --ignore-path=../../.prettierignore --check .",
|
|
30
31
|
"format": "prettier --ignore-path=../../.prettierignore --write .",
|
|
31
32
|
"lint": "yarn run -T eslint --ext .js,.ts,.tsx --report-unused-disable-directives .",
|
|
32
33
|
"typecheck": "yarn tsc --noEmit -p ."
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"@pexip/media-control": "18.
|
|
36
|
-
"@pexip/media-processor": "18.
|
|
37
|
-
"@pexip/signal": "16.7.
|
|
38
|
-
"@pexip/utils": "16.12.
|
|
36
|
+
"@pexip/media-control": "18.5.0",
|
|
37
|
+
"@pexip/media-processor": "18.5.0",
|
|
38
|
+
"@pexip/signal": "16.7.1",
|
|
39
|
+
"@pexip/utils": "16.12.1"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@
|
|
42
|
-
"@
|
|
42
|
+
"@pexip/bundler": "17.0.0",
|
|
43
|
+
"@swc/core": "^1.4.6",
|
|
44
|
+
"@swc/jest": "^0.2.36",
|
|
45
|
+
"jest": "^29.5.12",
|
|
46
|
+
"jest-junit": "^16.0.0",
|
|
43
47
|
"prettier": "^3.2.5",
|
|
44
48
|
"typescript": "~5.3.0"
|
|
45
49
|
},
|