@pexip/media 19.0.0 → 20.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 +75 -0
- package/dist/audioMixingProcessor.d.ts +1 -1
- package/dist/audioMixingProcessor.js +5 -5
- package/dist/audioProcessor.d.ts +1 -1
- package/dist/audioProcessor.js +4 -2
- package/dist/baseLogger.d.ts +15 -6
- package/dist/baseLogger.js +13 -7
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/displayMedia.d.ts +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -7
- package/dist/logger.js +1 -4
- package/dist/media.d.ts +6 -2
- package/dist/media.js +96 -46
- package/dist/previewController.js +4 -4
- package/dist/signals.d.ts +4 -4
- package/dist/signals.js +8 -5
- package/dist/status.d.ts +2 -1
- package/dist/status.js +8 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/typeGuard.d.ts +2 -1
- package/dist/typeGuard.js +3 -0
- package/dist/types.d.ts +3 -3
- package/dist/userMedia.d.ts +8 -1
- package/dist/userMedia.js +357 -20
- package/dist/utils.d.ts +10 -8
- package/dist/utils.js +84 -39
- package/dist/videoProcessor.d.ts +5 -3
- package/dist/videoProcessor.js +53 -60
- package/package.json +22 -11
package/dist/status.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare const isUnknownError: UserMediaValidator;
|
|
|
26
26
|
export declare const isInitial: UserMediaValidator;
|
|
27
27
|
export declare const isInitialPermissions: UserMediaValidator;
|
|
28
28
|
export declare const isInitialPermissionsNotGranted: UserMediaValidator;
|
|
29
|
+
export declare const isRequestPermissionNeeded: UserMediaValidator;
|
|
29
30
|
export declare const isInitialPermissionsGranted: UserMediaValidator;
|
|
30
31
|
export declare const isAudioDeviceInUse: UserMediaValidator;
|
|
31
32
|
export declare const isDeviceInUse: UserMediaValidator;
|
|
@@ -36,4 +37,4 @@ export declare const getPermissionStatus: ({ audio, video, }: {
|
|
|
36
37
|
audio: PermissionState;
|
|
37
38
|
video: PermissionState;
|
|
38
39
|
}) => 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
|
|
40
|
+
export declare const deriveInitialPermissionStatus: (prevStatus: UserMediaStatus, getPermissionState?: (anyActiveAudioTrack?: boolean, anyActiveVideoTrack?: boolean, getDevices?: () => Promise<MediaDeviceInfo[]>) => Promise<import("@pexip/media-control").InputDevicePermission>) => Promise<UserMediaStatus>;
|
package/dist/status.js
CHANGED
|
@@ -110,10 +110,18 @@ export const isInitialPermissionsNotGranted = status => [
|
|
|
110
110
|
UserMediaStatus.InitialPermissionsNotGranted,
|
|
111
111
|
UserMediaStatus.InitialPermissionsVideoInputDenied,
|
|
112
112
|
].includes(status);
|
|
113
|
+
const PermissionPromptStatusSet = new Set([
|
|
114
|
+
UserMediaStatus.InitialPermissionsNotGranted,
|
|
115
|
+
UserMediaStatus.InitialPermissionsAudioInputGranted,
|
|
116
|
+
UserMediaStatus.InitialPermissionsVideoInputGranted,
|
|
117
|
+
]);
|
|
118
|
+
export const isRequestPermissionNeeded = status => PermissionPromptStatusSet.has(status);
|
|
113
119
|
export const isInitialPermissionsGranted = status => [
|
|
114
120
|
UserMediaStatus.InitialPermissionsGranted,
|
|
115
121
|
UserMediaStatus.InitialPermissionsGrantedAudioInputDenied,
|
|
122
|
+
UserMediaStatus.InitialPermissionsAudioInputGranted,
|
|
116
123
|
UserMediaStatus.InitialPermissionsGrantedVideoInputDenied,
|
|
124
|
+
UserMediaStatus.InitialPermissionsVideoInputGranted,
|
|
117
125
|
].includes(status);
|
|
118
126
|
export const isAudioDeviceInUse = status => status === UserMediaStatus.AudioDeviceInUse;
|
|
119
127
|
export const isDeviceInUse = status => [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/audioMixingProcessor.ts","../src/audioProcessor.ts","../src/baseLogger.ts","../src/constants.ts","../src/displayMedia.ts","../src/index.ts","../src/logger.ts","../src/media.ts","../src/previewController.ts","../src/signals.ts","../src/status.ts","../src/typeGuard.ts","../src/types.ts","../src/userMedia.ts","../src/utils.ts","../src/videoProcessor.ts","../../../@types/globals.d.ts"],"version":"5.7.3"}
|
package/dist/typeGuard.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnalyzerNodeInit } from '@pexip/media-processor';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AudioContentHint, Media, VideoContentHint, VideoStreamTrackProcessorAPIs } from './types';
|
|
3
3
|
import { UserMediaStatus } from './types';
|
|
4
4
|
export declare const isNonNullObject: (value: unknown) => value is Record<string, unknown>;
|
|
5
5
|
export declare const isUserMediaStatus: (value: unknown) => value is UserMediaStatus;
|
|
@@ -7,3 +7,4 @@ export declare const isMedia: (value: unknown) => value is Media;
|
|
|
7
7
|
export declare const isAnalyzerNodeInitProp: (value: unknown) => value is AnalyzerNodeInit | undefined;
|
|
8
8
|
export declare const isAudioContentHint: (value: unknown) => value is AudioContentHint;
|
|
9
9
|
export declare const isVideoContentHint: (value: unknown) => value is VideoContentHint;
|
|
10
|
+
export declare const isVideoStreamTrackProcessorAPIs: (value: unknown) => value is VideoStreamTrackProcessorAPIs;
|
package/dist/typeGuard.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -128,7 +128,7 @@ export interface Media extends MediaAttributes {
|
|
|
128
128
|
getVideoTracks(): MediaTrack[];
|
|
129
129
|
addTrack(track: MediaTrack): void;
|
|
130
130
|
removeTrack(track: MediaTrack): void;
|
|
131
|
-
clone(): Media;
|
|
131
|
+
clone(signals?: MediaSignals): Media;
|
|
132
132
|
}
|
|
133
133
|
export interface MediaInit {
|
|
134
134
|
constraints: MediaDeviceRequest;
|
|
@@ -181,7 +181,7 @@ export interface MediaTrack {
|
|
|
181
181
|
getConstraints(): InputDeviceConstraint;
|
|
182
182
|
applyConstraints(constraints: InputDeviceConstraint): Promise<void>;
|
|
183
183
|
release(): Promise<void>;
|
|
184
|
-
clone(): MediaTrack;
|
|
184
|
+
clone(signals?: MediaSignals): MediaTrack;
|
|
185
185
|
toJSON(): unknown;
|
|
186
186
|
}
|
|
187
187
|
export interface MediaTrackInit extends Partial<MediaTrack> {
|
|
@@ -205,7 +205,7 @@ export type GetUserMediaProcess = (request: {
|
|
|
205
205
|
}) => Promise<Media>;
|
|
206
206
|
/**
|
|
207
207
|
* Use which processor API to process the stream track
|
|
208
|
-
* `stream` - Use MediaStreamTrackProcessor
|
|
208
|
+
* `stream` - Use `MediaStreamTrackProcessor`, when available
|
|
209
209
|
* `canvas` - Use Canvas
|
|
210
210
|
*/
|
|
211
211
|
export type VideoStreamTrackProcessorAPIs = 'stream' | 'canvas';
|
package/dist/userMedia.d.ts
CHANGED
|
@@ -11,7 +11,13 @@ export declare const toSameDeviceStatus: ({ audio, video, }: {
|
|
|
11
11
|
audio: boolean;
|
|
12
12
|
video: boolean;
|
|
13
13
|
}) => UserMediaStatus.PermissionsGranted | UserMediaStatus.PermissionsGrantedFallback | UserMediaStatus.PermissionsGrantedFallbackAudioinput | UserMediaStatus.PermissionsGrantedFallbackVideoinput;
|
|
14
|
-
export declare const toOnlyDeviceStatus: (kind:
|
|
14
|
+
export declare const toOnlyDeviceStatus: (kind: "audioinput" | "videoinput", matched: boolean, devices: IndexedDevices) => UserMediaStatus.PermissionsRejectedAudioInput | UserMediaStatus.PermissionsRejectedVideoInput | UserMediaStatus.PermissionsOnlyAudioinput | UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices | UserMediaStatus.PermissionsOnlyAudioinputFallback | UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices | UserMediaStatus.PermissionsOnlyVideoinput | UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices | UserMediaStatus.PermissionsOnlyVideoinputFallback | UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
|
|
15
|
+
export declare const hasLiveTrack: (kind: "audio" | "video", tracks: MediaStreamTrack[]) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Merge the previous status with the next status and assuming requesting both
|
|
18
|
+
* video and audio inputs.
|
|
19
|
+
*/
|
|
20
|
+
export declare const mergeStatus: (prevStatus: UserMediaStatus, nextStatus: UserMediaStatus, prevTracks?: MediaStreamTrack[], nextTracks?: MediaStreamTrack[]) => UserMediaStatus;
|
|
15
21
|
/**
|
|
16
22
|
* Try to come up with an error level according to provided UserMediaStatus and
|
|
17
23
|
* MediaDeviceRequest
|
|
@@ -26,6 +32,7 @@ export declare const mergeNoDeviceStatus: (constraints: MediaDeviceRequest, anyD
|
|
|
26
32
|
audio: boolean;
|
|
27
33
|
video: boolean;
|
|
28
34
|
}, status: UserMediaStatus) => UserMediaStatus;
|
|
35
|
+
export declare const deriveUserMediaStatus: (devices: IndexedDevices, constraints: MediaDeviceRequest, prevStatus: UserMediaStatus) => UserMediaStatus;
|
|
29
36
|
export declare const requestUserMediaWithRetry: (getCurrentDevices: GetCurrentDevices, createRequestUserMedia?: (getCurrentDevices: GetCurrentDevices, getMedia?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia, gUM?: ({ audio, video, }: MediaDeviceRequest) => Promise<MediaStream>) => GetUserMedia;
|
|
30
37
|
interface Options {
|
|
31
38
|
getUserMedia: GetUserMedia;
|
package/dist/userMedia.js
CHANGED
|
@@ -72,26 +72,317 @@ export const toSameDeviceStatus = ({ audio, video, }) => {
|
|
|
72
72
|
return UserMediaStatus.PermissionsGrantedFallback;
|
|
73
73
|
};
|
|
74
74
|
export const toOnlyDeviceStatus = (kind, matched, devices) => {
|
|
75
|
-
const
|
|
75
|
+
const hasAnotherTypeOfAuthorizedDevice = devices.anyAuthorizedDevice(kind === 'audioinput' ? 'videoinput' : 'audioinput');
|
|
76
|
+
const hasAnotherTypeOfDevice = devices.size(kind === 'audioinput' ? 'videoinput' : 'audioinput') > 0;
|
|
76
77
|
if (matched) {
|
|
77
|
-
if (
|
|
78
|
+
if (hasAnotherTypeOfDevice) {
|
|
79
|
+
if (hasAnotherTypeOfAuthorizedDevice) {
|
|
80
|
+
return kind === 'audioinput'
|
|
81
|
+
? UserMediaStatus.PermissionsOnlyAudioinput
|
|
82
|
+
: UserMediaStatus.PermissionsOnlyVideoinput;
|
|
83
|
+
}
|
|
78
84
|
return kind === 'audioinput'
|
|
79
|
-
? UserMediaStatus.
|
|
80
|
-
: UserMediaStatus.
|
|
85
|
+
? UserMediaStatus.PermissionsRejectedVideoInput
|
|
86
|
+
: UserMediaStatus.PermissionsRejectedAudioInput;
|
|
81
87
|
}
|
|
82
88
|
return kind === 'audioinput'
|
|
83
89
|
? UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices
|
|
84
90
|
: UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices;
|
|
85
91
|
}
|
|
86
|
-
if (
|
|
92
|
+
if (hasAnotherTypeOfDevice) {
|
|
93
|
+
if (hasAnotherTypeOfAuthorizedDevice) {
|
|
94
|
+
return kind === 'audioinput'
|
|
95
|
+
? UserMediaStatus.PermissionsOnlyAudioinputFallback
|
|
96
|
+
: UserMediaStatus.PermissionsOnlyVideoinputFallback;
|
|
97
|
+
}
|
|
87
98
|
return kind === 'audioinput'
|
|
88
|
-
? UserMediaStatus.
|
|
89
|
-
: UserMediaStatus.
|
|
99
|
+
? UserMediaStatus.PermissionsRejectedVideoInput
|
|
100
|
+
: UserMediaStatus.PermissionsRejectedAudioInput;
|
|
90
101
|
}
|
|
91
102
|
return kind === 'audioinput'
|
|
92
103
|
? UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices
|
|
93
104
|
: UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
|
|
94
105
|
};
|
|
106
|
+
export const hasLiveTrack = (kind, tracks) => tracks.some(track => track.kind === kind && track.readyState === 'live');
|
|
107
|
+
/**
|
|
108
|
+
* Merge the previous status with the next status and assuming requesting both
|
|
109
|
+
* video and audio inputs.
|
|
110
|
+
*/
|
|
111
|
+
export const mergeStatus = (prevStatus, nextStatus, prevTracks = [], nextTracks = []) => {
|
|
112
|
+
const hasPrevLiveAudio = hasLiveTrack('audio', prevTracks);
|
|
113
|
+
const hasPrevLiveVideo = hasLiveTrack('video', prevTracks);
|
|
114
|
+
const hasNextLiveAudio = hasLiveTrack('audio', nextTracks);
|
|
115
|
+
const hasNextLiveVideo = hasLiveTrack('video', nextTracks);
|
|
116
|
+
// Replace Audio Track
|
|
117
|
+
if (hasNextLiveAudio) {
|
|
118
|
+
assert(hasPrevLiveAudio === false, 'Only 1 live audio');
|
|
119
|
+
// Replace Audio and Video tracks
|
|
120
|
+
if (hasNextLiveVideo) {
|
|
121
|
+
assert(hasPrevLiveVideo === false, 'Only 1 live video');
|
|
122
|
+
// No need to consider the previous status
|
|
123
|
+
switch (nextStatus) {
|
|
124
|
+
case UserMediaStatus.PermissionsOnlyVideoinput:
|
|
125
|
+
case UserMediaStatus.PermissionsOnlyAudioinput:
|
|
126
|
+
return UserMediaStatus.PermissionsGranted;
|
|
127
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
128
|
+
return UserMediaStatus.PermissionsGrantedFallbackAudioinput;
|
|
129
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallback:
|
|
130
|
+
return UserMediaStatus.PermissionsGrantedFallbackVideoinput;
|
|
131
|
+
default:
|
|
132
|
+
return nextStatus;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (hasPrevLiveVideo) {
|
|
136
|
+
// Merge previous video status with next audio status
|
|
137
|
+
switch (prevStatus) {
|
|
138
|
+
case UserMediaStatus.PermissionsGranted:
|
|
139
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
140
|
+
case UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices:
|
|
141
|
+
case UserMediaStatus.PermissionsOnlyVideoinput: {
|
|
142
|
+
switch (nextStatus) {
|
|
143
|
+
case UserMediaStatus.PermissionsOnlyAudioinput:
|
|
144
|
+
return UserMediaStatus.PermissionsGranted;
|
|
145
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
146
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
147
|
+
return UserMediaStatus.PermissionsGrantedFallbackAudioinput;
|
|
148
|
+
default:
|
|
149
|
+
return nextStatus;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
|
|
153
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
154
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices:
|
|
155
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallback: {
|
|
156
|
+
switch (nextStatus) {
|
|
157
|
+
case UserMediaStatus.PermissionsGranted:
|
|
158
|
+
case UserMediaStatus.PermissionsOnlyAudioinput:
|
|
159
|
+
return UserMediaStatus.PermissionsGrantedFallbackVideoinput;
|
|
160
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
161
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
162
|
+
return UserMediaStatus.PermissionsGrantedFallback;
|
|
163
|
+
default:
|
|
164
|
+
return nextStatus;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
return nextStatus;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// No previous Live Video
|
|
172
|
+
switch (nextStatus) {
|
|
173
|
+
case UserMediaStatus.PermissionsRejected:
|
|
174
|
+
case UserMediaStatus.PermissionsRejectedVideoInput: {
|
|
175
|
+
switch (prevStatus) {
|
|
176
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
177
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
178
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
179
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
|
|
180
|
+
return UserMediaStatus.PermissionsOnlyAudioinputFallback;
|
|
181
|
+
default:
|
|
182
|
+
return UserMediaStatus.PermissionsOnlyAudioinput;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
case UserMediaStatus.NoDevicesFound:
|
|
186
|
+
case UserMediaStatus.AudioVideoDevicesNotFound:
|
|
187
|
+
case UserMediaStatus.VideoDeviceNotFound:
|
|
188
|
+
case UserMediaStatus.NoVideoDevicesFound:
|
|
189
|
+
switch (prevStatus) {
|
|
190
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
191
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
192
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
193
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
|
|
194
|
+
return UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices;
|
|
195
|
+
default:
|
|
196
|
+
return UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices;
|
|
197
|
+
}
|
|
198
|
+
case UserMediaStatus.DevicesInUse:
|
|
199
|
+
case UserMediaStatus.VideoDeviceInUse:
|
|
200
|
+
return UserMediaStatus.VideoDeviceInUse;
|
|
201
|
+
case UserMediaStatus.Overconstrained:
|
|
202
|
+
case UserMediaStatus.VideoOverconstrained:
|
|
203
|
+
return UserMediaStatus.VideoOverconstrained;
|
|
204
|
+
case UserMediaStatus.NotSupportedError:
|
|
205
|
+
case UserMediaStatus.NotSupportedErrorOnlyVideoInput:
|
|
206
|
+
return UserMediaStatus.NotSupportedErrorOnlyVideoInput;
|
|
207
|
+
case UserMediaStatus.InvalidConstraints:
|
|
208
|
+
case UserMediaStatus.InvalidVideoConstraints:
|
|
209
|
+
return UserMediaStatus.InvalidVideoConstraints;
|
|
210
|
+
case UserMediaStatus.UnknownError:
|
|
211
|
+
case UserMediaStatus.UnknownErrorOnlyVideoinput:
|
|
212
|
+
return UserMediaStatus.UnknownErrorOnlyVideoinput;
|
|
213
|
+
default:
|
|
214
|
+
return nextStatus;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Replace Video Track Only
|
|
218
|
+
if (hasNextLiveVideo) {
|
|
219
|
+
assert(hasPrevLiveVideo === false, 'Only 1 live video');
|
|
220
|
+
if (hasPrevLiveAudio) {
|
|
221
|
+
switch (prevStatus) {
|
|
222
|
+
case UserMediaStatus.PermissionsGranted:
|
|
223
|
+
case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
|
|
224
|
+
case UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices:
|
|
225
|
+
case UserMediaStatus.PermissionsOnlyAudioinput: {
|
|
226
|
+
switch (nextStatus) {
|
|
227
|
+
case UserMediaStatus.PermissionsOnlyVideoinput:
|
|
228
|
+
return UserMediaStatus.PermissionsGranted;
|
|
229
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
230
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallback:
|
|
231
|
+
return UserMediaStatus.PermissionsGrantedFallbackVideoinput;
|
|
232
|
+
default:
|
|
233
|
+
return nextStatus;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
237
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
238
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
|
|
239
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback: {
|
|
240
|
+
switch (nextStatus) {
|
|
241
|
+
case UserMediaStatus.PermissionsGranted:
|
|
242
|
+
case UserMediaStatus.PermissionsOnlyVideoinput:
|
|
243
|
+
return UserMediaStatus.PermissionsGrantedFallbackAudioinput;
|
|
244
|
+
case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
|
|
245
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallback:
|
|
246
|
+
return UserMediaStatus.PermissionsGrantedFallback;
|
|
247
|
+
default:
|
|
248
|
+
return nextStatus;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
default:
|
|
252
|
+
return nextStatus;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Failed to get a new track
|
|
257
|
+
if (hasPrevLiveAudio) {
|
|
258
|
+
switch (nextStatus) {
|
|
259
|
+
case UserMediaStatus.PermissionsRejected:
|
|
260
|
+
case UserMediaStatus.PermissionsRejectedVideoInput: {
|
|
261
|
+
switch (prevStatus) {
|
|
262
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
263
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
264
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
265
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
|
|
266
|
+
return UserMediaStatus.PermissionsOnlyAudioinputFallback;
|
|
267
|
+
default:
|
|
268
|
+
return UserMediaStatus.PermissionsOnlyAudioinput;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
case UserMediaStatus.NoDevicesFound:
|
|
272
|
+
case UserMediaStatus.AudioVideoDevicesNotFound:
|
|
273
|
+
case UserMediaStatus.VideoDeviceNotFound:
|
|
274
|
+
case UserMediaStatus.NoVideoDevicesFound:
|
|
275
|
+
switch (prevStatus) {
|
|
276
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
277
|
+
case UserMediaStatus.PermissionsGrantedFallbackAudioinput:
|
|
278
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallback:
|
|
279
|
+
case UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices:
|
|
280
|
+
return UserMediaStatus.PermissionsOnlyAudioinputFallbackNoVideoDevices;
|
|
281
|
+
default:
|
|
282
|
+
return UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices;
|
|
283
|
+
}
|
|
284
|
+
case UserMediaStatus.DevicesInUse:
|
|
285
|
+
case UserMediaStatus.VideoDeviceInUse:
|
|
286
|
+
return UserMediaStatus.VideoDeviceInUse;
|
|
287
|
+
case UserMediaStatus.Overconstrained:
|
|
288
|
+
case UserMediaStatus.VideoOverconstrained:
|
|
289
|
+
return UserMediaStatus.VideoOverconstrained;
|
|
290
|
+
case UserMediaStatus.NotSupportedError:
|
|
291
|
+
case UserMediaStatus.NotSupportedErrorOnlyVideoInput:
|
|
292
|
+
return UserMediaStatus.NotSupportedErrorOnlyVideoInput;
|
|
293
|
+
case UserMediaStatus.InvalidConstraints:
|
|
294
|
+
case UserMediaStatus.InvalidVideoConstraints:
|
|
295
|
+
return UserMediaStatus.InvalidVideoConstraints;
|
|
296
|
+
case UserMediaStatus.UnknownError:
|
|
297
|
+
case UserMediaStatus.UnknownErrorOnlyVideoinput:
|
|
298
|
+
return UserMediaStatus.UnknownErrorOnlyVideoinput;
|
|
299
|
+
default:
|
|
300
|
+
return prevStatus;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (hasPrevLiveVideo) {
|
|
304
|
+
switch (nextStatus) {
|
|
305
|
+
case UserMediaStatus.PermissionsRejected:
|
|
306
|
+
case UserMediaStatus.PermissionsRejectedAudioInput:
|
|
307
|
+
switch (prevStatus) {
|
|
308
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
309
|
+
case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
|
|
310
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallback:
|
|
311
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices:
|
|
312
|
+
return UserMediaStatus.PermissionsOnlyVideoinputFallback;
|
|
313
|
+
default:
|
|
314
|
+
return UserMediaStatus.PermissionsOnlyVideoinput;
|
|
315
|
+
}
|
|
316
|
+
case UserMediaStatus.NoDevicesFound:
|
|
317
|
+
case UserMediaStatus.AudioVideoDevicesNotFound:
|
|
318
|
+
case UserMediaStatus.AudioDeviceNotFound:
|
|
319
|
+
case UserMediaStatus.NoAudioDevicesFound: {
|
|
320
|
+
switch (prevStatus) {
|
|
321
|
+
case UserMediaStatus.PermissionsGrantedFallback:
|
|
322
|
+
case UserMediaStatus.PermissionsGrantedFallbackVideoinput:
|
|
323
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallback:
|
|
324
|
+
case UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices:
|
|
325
|
+
return UserMediaStatus.PermissionsOnlyVideoinputFallbackNoAudioDevices;
|
|
326
|
+
default:
|
|
327
|
+
return UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
case UserMediaStatus.DevicesInUse:
|
|
331
|
+
case UserMediaStatus.AudioDeviceInUse:
|
|
332
|
+
return UserMediaStatus.AudioDeviceInUse;
|
|
333
|
+
case UserMediaStatus.Overconstrained:
|
|
334
|
+
case UserMediaStatus.AudioOverconstrained:
|
|
335
|
+
return UserMediaStatus.AudioOverconstrained;
|
|
336
|
+
case UserMediaStatus.NotSupportedError:
|
|
337
|
+
case UserMediaStatus.NotSupportedErrorOnlyAudioInput:
|
|
338
|
+
return UserMediaStatus.NotSupportedErrorOnlyAudioInput;
|
|
339
|
+
case UserMediaStatus.InvalidConstraints:
|
|
340
|
+
case UserMediaStatus.InvalidAudioConstraints:
|
|
341
|
+
return UserMediaStatus.InvalidAudioConstraints;
|
|
342
|
+
case UserMediaStatus.UnknownError:
|
|
343
|
+
case UserMediaStatus.UnknownErrorOnlyAudioinput:
|
|
344
|
+
return UserMediaStatus.UnknownErrorOnlyAudioinput;
|
|
345
|
+
default:
|
|
346
|
+
return prevStatus;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
switch (nextStatus) {
|
|
350
|
+
case UserMediaStatus.PermissionsRejected:
|
|
351
|
+
case UserMediaStatus.PermissionsRejectedAudioInput:
|
|
352
|
+
case UserMediaStatus.PermissionsRejectedVideoInput:
|
|
353
|
+
return UserMediaStatus.PermissionsRejected;
|
|
354
|
+
case UserMediaStatus.NoDevicesFound:
|
|
355
|
+
case UserMediaStatus.AudioVideoDevicesNotFound:
|
|
356
|
+
case UserMediaStatus.AudioDeviceNotFound:
|
|
357
|
+
case UserMediaStatus.VideoDeviceNotFound:
|
|
358
|
+
case UserMediaStatus.NoVideoDevicesFound:
|
|
359
|
+
case UserMediaStatus.NoAudioDevicesFound: {
|
|
360
|
+
return UserMediaStatus.NoDevicesFound;
|
|
361
|
+
}
|
|
362
|
+
case UserMediaStatus.DevicesInUse:
|
|
363
|
+
case UserMediaStatus.AudioDeviceInUse:
|
|
364
|
+
case UserMediaStatus.VideoDeviceInUse:
|
|
365
|
+
return UserMediaStatus.DevicesInUse;
|
|
366
|
+
case UserMediaStatus.Overconstrained:
|
|
367
|
+
case UserMediaStatus.AudioOverconstrained:
|
|
368
|
+
case UserMediaStatus.VideoOverconstrained:
|
|
369
|
+
return UserMediaStatus.Overconstrained;
|
|
370
|
+
case UserMediaStatus.InvalidConstraints:
|
|
371
|
+
case UserMediaStatus.InvalidVideoConstraints:
|
|
372
|
+
case UserMediaStatus.InvalidAudioConstraints:
|
|
373
|
+
return UserMediaStatus.InvalidConstraints;
|
|
374
|
+
case UserMediaStatus.NotSupportedError:
|
|
375
|
+
case UserMediaStatus.NotSupportedErrorOnlyAudioInput:
|
|
376
|
+
case UserMediaStatus.NotSupportedErrorOnlyVideoInput:
|
|
377
|
+
return UserMediaStatus.NotSupportedError;
|
|
378
|
+
case UserMediaStatus.UnknownError:
|
|
379
|
+
case UserMediaStatus.UnknownErrorOnlyAudioinput:
|
|
380
|
+
case UserMediaStatus.UnknownErrorOnlyVideoinput:
|
|
381
|
+
return UserMediaStatus.UnknownError;
|
|
382
|
+
default:
|
|
383
|
+
return nextStatus;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
95
386
|
/**
|
|
96
387
|
* Try to come up with an error level according to provided UserMediaStatus and
|
|
97
388
|
* MediaDeviceRequest
|
|
@@ -116,18 +407,26 @@ export const deriveErrorLevel = (error, status, constraints) => {
|
|
|
116
407
|
: 'warn';
|
|
117
408
|
};
|
|
118
409
|
export const requestUserMedia = (getCurrentDevices, getMedia = getUserMedia) => async (constraints) => {
|
|
119
|
-
const deriveDeviceStatus = makeDeriveDeviceStatus(constraints);
|
|
120
410
|
try {
|
|
121
411
|
const stream = await getMedia(constraints);
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
412
|
+
const devices = await getCurrentDevices();
|
|
413
|
+
const deriveDeviceStatus = makeDeriveDeviceStatus({
|
|
414
|
+
audio: devices.anyAuthorizedDevice('audioinput')
|
|
415
|
+
? constraints.audio
|
|
416
|
+
: false,
|
|
417
|
+
video: devices.anyAuthorizedDevice('videoinput')
|
|
418
|
+
? constraints.video
|
|
419
|
+
: false,
|
|
420
|
+
});
|
|
421
|
+
const { audio, video } = isStreamingRequestedDevices(constraints, stream, devices);
|
|
422
|
+
const onlyAudioStatus = toOnlyDeviceStatus('audioinput', audio, devices);
|
|
423
|
+
const onlyVideoStatus = toOnlyDeviceStatus('videoinput', video, devices);
|
|
126
424
|
const status = deriveDeviceStatus(onlyAudioStatus, onlyVideoStatus, toSameDeviceStatus({ audio, video }));
|
|
127
425
|
return [stream, status];
|
|
128
426
|
}
|
|
129
427
|
catch (error) {
|
|
130
428
|
if (error instanceof Error) {
|
|
429
|
+
const deriveDeviceStatus = makeDeriveDeviceStatus(constraints);
|
|
131
430
|
const status = toErrorDeviceStatus(error, deriveDeviceStatus);
|
|
132
431
|
return [undefined, status];
|
|
133
432
|
}
|
|
@@ -150,17 +449,18 @@ export const mergeNoDeviceStatus = (constraints, anyDevices, status) => {
|
|
|
150
449
|
}
|
|
151
450
|
return status;
|
|
152
451
|
};
|
|
452
|
+
export const deriveUserMediaStatus = (devices, constraints, prevStatus) => {
|
|
453
|
+
const anyAudioDevices = devices.size('audioinput') > 0;
|
|
454
|
+
const anyVideoDevices = devices.size('videoinput') > 0;
|
|
455
|
+
return mergeNoDeviceStatus(constraints, { audio: anyAudioDevices, video: anyVideoDevices }, prevStatus);
|
|
456
|
+
};
|
|
153
457
|
export const requestUserMediaWithRetry = (getCurrentDevices, createRequestUserMedia = requestUserMedia, gUM = getUserMedia) => {
|
|
154
458
|
const request = createRequestUserMedia(getCurrentDevices, gUM);
|
|
155
459
|
return async (constraints) => {
|
|
156
460
|
const [stream, status] = await request(constraints);
|
|
157
461
|
const devices = await getCurrentDevices();
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
return [
|
|
161
|
-
stream,
|
|
162
|
-
mergeNoDeviceStatus(constraints, { audio: anyAudioDevices, video: anyVideoDevices }, status),
|
|
163
|
-
];
|
|
462
|
+
const finalStatus = deriveUserMediaStatus(devices, constraints, status);
|
|
463
|
+
return [stream, finalStatus];
|
|
164
464
|
};
|
|
165
465
|
};
|
|
166
466
|
/**
|
|
@@ -216,8 +516,32 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
|
|
|
216
516
|
}));
|
|
217
517
|
}
|
|
218
518
|
const [stream, status] = await getUserMedia(constraints);
|
|
219
|
-
|
|
519
|
+
let finalStatus = status;
|
|
520
|
+
if (currentMedia &&
|
|
521
|
+
constraints.audio !== false &&
|
|
522
|
+
constraints.video !== false) {
|
|
523
|
+
finalStatus = mergeStatus(currentMedia.status, status,
|
|
524
|
+
// When the constraint is undefined, it means the track will be kept as what it is.
|
|
525
|
+
// i.e. It belongs to the next track for this mergeStatus logic.
|
|
526
|
+
[
|
|
527
|
+
...(constraints.audio === undefined
|
|
528
|
+
? []
|
|
529
|
+
: currentMedia.stream.getAudioTracks()),
|
|
530
|
+
...(constraints.video === undefined
|
|
531
|
+
? []
|
|
532
|
+
: currentMedia.stream.getVideoTracks()),
|
|
533
|
+
], [
|
|
534
|
+
...(stream?.getTracks() ?? []),
|
|
535
|
+
...(constraints.audio === undefined
|
|
536
|
+
? currentMedia.stream.getAudioTracks()
|
|
537
|
+
: []),
|
|
538
|
+
...(constraints.video === undefined
|
|
539
|
+
? currentMedia.stream.getVideoTracks()
|
|
540
|
+
: []),
|
|
541
|
+
]);
|
|
542
|
+
}
|
|
220
543
|
const devices = await getCurrentDevices();
|
|
544
|
+
logger.debug({ scope, stream, devices, status: finalStatus }, 'post getUserMedia');
|
|
221
545
|
const findInput = findMediaInputFromMediaStreamTrack(devices);
|
|
222
546
|
let audioMediaTrack;
|
|
223
547
|
let videoMediaTrack;
|
|
@@ -225,6 +549,7 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
|
|
|
225
549
|
if (constraints.audio !== undefined) {
|
|
226
550
|
const audioTrack = stream?.getAudioTracks().at(0);
|
|
227
551
|
const audioInput = findInput(audioTrack);
|
|
552
|
+
assert(Boolean(audioTrack) === Boolean(audioInput), 'audioTrack <=> audioInput');
|
|
228
553
|
const { contentHint: [[contentHint] = []], } = extractContentHint(constraints.audio);
|
|
229
554
|
if (audioTrack) {
|
|
230
555
|
audioTrack.contentHint = contentHint ?? '';
|
|
@@ -243,6 +568,7 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
|
|
|
243
568
|
if (constraints.video !== undefined) {
|
|
244
569
|
const videoTrack = stream?.getVideoTracks().at(0);
|
|
245
570
|
const videoInput = findInput(videoTrack);
|
|
571
|
+
assert(Boolean(videoTrack) === Boolean(videoInput), 'videoTrack <=> videoInput');
|
|
246
572
|
const { contentHint: [[contentHint] = []], } = extractContentHint(constraints.video);
|
|
247
573
|
if (videoTrack) {
|
|
248
574
|
videoTrack.contentHint = contentHint ?? '';
|
|
@@ -258,8 +584,19 @@ export const createGetUserMediaProcess = ({ getUserMedia, getCurrentDevices, sig
|
|
|
258
584
|
});
|
|
259
585
|
currentMedia?.addTrack(videoMediaTrack);
|
|
260
586
|
}
|
|
587
|
+
logger.info({
|
|
588
|
+
permission,
|
|
589
|
+
devices,
|
|
590
|
+
stream,
|
|
591
|
+
status: finalStatus,
|
|
592
|
+
originalConstraints,
|
|
593
|
+
currentMedia,
|
|
594
|
+
audioMediaTrack,
|
|
595
|
+
videoMediaTrack,
|
|
596
|
+
constraints,
|
|
597
|
+
}, 'GetUserMedia');
|
|
261
598
|
if (currentMedia) {
|
|
262
|
-
currentMedia.status =
|
|
599
|
+
currentMedia.status = finalStatus;
|
|
263
600
|
currentMedia.setOriginalConstraints(originalConstraints);
|
|
264
601
|
return currentMedia;
|
|
265
602
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { IndexedDevices, InputDeviceConstraint, MediaDeviceInfoLike, MediaDeviceRequest } from '@pexip/media-control';
|
|
2
|
-
import
|
|
1
|
+
import type { IndexedDevices, InputDeviceConstraint, MediaDeviceInfoLike, MediaDeviceRequest, InputConstraintSet } from '@pexip/media-control';
|
|
2
|
+
import { type RenderEffects } from '@pexip/media-processor';
|
|
3
|
+
import type { AudioContentHint, ExtendedMediaTrackSettings, ExtendedMediaTrackSettingsKey, Media, MediaInit, MediaTrack, MediaTrackInit, TrackProcessor, UserMediaStatus, VideoContentHint } from './types';
|
|
3
4
|
export declare const makeDeriveDeviceStatus: (constraints: MediaDeviceRequest) => (audio: UserMediaStatus, video: UserMediaStatus, both: UserMediaStatus) => UserMediaStatus;
|
|
4
5
|
export declare const createMediaTrack: (trackInit: MediaTrackInit) => MediaTrack;
|
|
5
6
|
export declare const createTrackProcessingPipeline: (processors: TrackProcessor[]) => (track: MediaTrack) => Promise<MediaTrack>;
|
|
@@ -8,12 +9,12 @@ export declare const createTrackProcessingPipeline: (processors: TrackProcessor[
|
|
|
8
9
|
* otherwise `undefined`
|
|
9
10
|
*/
|
|
10
11
|
export declare const interpretInput: (input: boolean | MediaDeviceInfoLike | undefined, getCurrentInput: () => MediaDeviceInfoLike | undefined) => MediaDeviceInfoLike | undefined;
|
|
11
|
-
export declare const findExpectedInput: (devices: IndexedDevices, constraints: InputDeviceConstraint | undefined, input: MediaDeviceInfoLike | undefined, kind:
|
|
12
|
+
export declare const findExpectedInput: (devices: IndexedDevices, constraints: InputDeviceConstraint | undefined, input: MediaDeviceInfoLike | undefined, kind: "audioinput" | "videoinput") => MediaDeviceInfoLike | undefined;
|
|
12
13
|
/**
|
|
13
14
|
* A utility function to check if the provided track is muted. There are
|
|
14
15
|
* 2 factors to be considered: `MediaStreamTrack['muted']` and `MediaStreamTrack['enabled']`.
|
|
15
16
|
*
|
|
16
|
-
* ```
|
|
17
|
+
* ```md
|
|
17
18
|
* | muted \ enabled | true | false |
|
|
18
19
|
* |-----------------| ----- | ----- |
|
|
19
20
|
* | true | true | true |
|
|
@@ -27,7 +28,7 @@ export declare const findExpectedInput: (devices: IndexedDevices, constraints: I
|
|
|
27
28
|
* there is no track to check
|
|
28
29
|
*/
|
|
29
30
|
export declare const isTrackMuted: (track: MediaStreamTrack | undefined) => boolean | undefined;
|
|
30
|
-
export declare const buildMedia: (mediaInit: MediaInit, onDevicesChanged?: import("@pexip/signal").Signal<IndexedDevices>) => Media;
|
|
31
|
+
export declare const buildMedia: (mediaInit: MediaInit, onDevicesChanged?: import("@pexip/signal").Signal<IndexedDevices, IndexedDevices>) => Media;
|
|
31
32
|
/**
|
|
32
33
|
* Shallow copy the provided object and override with provided overriding
|
|
33
34
|
*
|
|
@@ -38,8 +39,6 @@ export declare const buildMedia: (mediaInit: MediaInit, onDevicesChanged?: impor
|
|
|
38
39
|
*/
|
|
39
40
|
export declare const shallowCopy: <T>(original: T, overriding: Partial<T>) => T;
|
|
40
41
|
export declare const getDevicesChanges: (prev: MediaDeviceInfoLike[], next: MediaDeviceInfoLike[]) => {
|
|
41
|
-
unauthorized: MediaDeviceInfoLike[];
|
|
42
|
-
authorized: MediaDeviceInfoLike[];
|
|
43
42
|
found: MediaDeviceInfoLike[];
|
|
44
43
|
lost: MediaDeviceInfoLike[];
|
|
45
44
|
devices: MediaDeviceInfoLike[];
|
|
@@ -47,7 +46,10 @@ export declare const getDevicesChanges: (prev: MediaDeviceInfoLike[], next: Medi
|
|
|
47
46
|
export declare const AUDIO_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
|
|
48
47
|
export declare const VIDEO_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
|
|
49
48
|
export declare const MIXING_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
|
|
49
|
+
export declare const PAN_TILT_ZOOM_SETTINGS_KEYS: ExtendedMediaTrackSettingsKey[];
|
|
50
50
|
export declare const hasSettingsChanged: (keysToLookFor: ExtendedMediaTrackSettingsKey[]) => (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => boolean;
|
|
51
|
+
export declare const isApplyingRenderEffect: (effect: RenderEffects) => boolean;
|
|
52
|
+
export declare const getSettingsFromKeys: (keys: ExtendedMediaTrackSettingsKey[], settings: InputConstraintSet | undefined | false) => false | ExtendedMediaTrackSettings | undefined;
|
|
51
53
|
export declare const diffSettings: (keysToLookFor: ExtendedMediaTrackSettingsKey[]) => (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => ExtendedMediaTrackSettings | undefined;
|
|
52
54
|
export declare const mergeSettings: (settingsA: ExtendedMediaTrackSettings | undefined, settingsB: ExtendedMediaTrackSettings | undefined) => ExtendedMediaTrackSettings | undefined;
|
|
53
55
|
/**
|
|
@@ -67,7 +69,7 @@ export declare const getBlurKernelSize: (percentage: number, height: number, max
|
|
|
67
69
|
* @param hint - Content hint
|
|
68
70
|
* @param track - The track to be applied
|
|
69
71
|
*/
|
|
70
|
-
export declare const applyContentHint: <T extends
|
|
72
|
+
export declare const applyContentHint: <T extends AudioContentHint | VideoContentHint>(hint?: T) => (track: MediaStreamTrack) => void;
|
|
71
73
|
/**
|
|
72
74
|
* Check if the browser supports PTZ feature
|
|
73
75
|
* @returns true if the browser supports PTZ feature, otherwise false
|