@pexip/media 19.1.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 +62 -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 +86 -44
- package/dist/previewController.js +3 -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 +81 -37
- package/dist/videoProcessor.d.ts +5 -3
- package/dist/videoProcessor.js +53 -60
- package/package.json +22 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# @pexip/media
|
|
2
2
|
|
|
3
|
+
## 20.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 2bfd9e1: Adds esm entry points.
|
|
8
|
+
- c975587: `dynamicProcessingDimensions` is a func.
|
|
9
|
+
- 7679b89: Removes `adjustResolution`.
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- dd5a4eb: Adds optional `gpuAPI` getter, sets the transformer backend when
|
|
14
|
+
opened.
|
|
15
|
+
- 34bf3dd: Add isVideoStreamTrackProcessorAPIs
|
|
16
|
+
- 5855473: Updates the segmenter processing width and height based on the track
|
|
17
|
+
settings if `dynamicProcessingDimensions` is `true` in `videoProcessor`.
|
|
18
|
+
- 3555d9d: Adds `build:sourcemap` script.
|
|
19
|
+
- 7d94f5c: Take unauthorized device into account
|
|
20
|
+
- ed53b05: Upgrade Typescript
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- 4146978: Bump logger proxy set to info level
|
|
25
|
+
- 0beec97: Fix mergeStatus to take requesting single device into consideration
|
|
26
|
+
|
|
27
|
+
When changing 1 device should not affect another type of device
|
|
28
|
+
|
|
29
|
+
- 67a18d0: Use recommended linter rules
|
|
30
|
+
- e1882e7: Request media only when there is no prompt permission
|
|
31
|
+
- 3882f98: No unused imports
|
|
32
|
+
- 50d0af0: Assert track and input
|
|
33
|
+
- 5adb1aa: Update according Logger changes
|
|
34
|
+
- 79c52c9: No signals reuse for clone
|
|
35
|
+
- 43d0b9c: Always use the merged constraints
|
|
36
|
+
- 6470495: Fix applyFeatures
|
|
37
|
+
- 6d68da2: Stop processing non-live track
|
|
38
|
+
- 58ecca1: Fix mergeStatus fallback case
|
|
39
|
+
- e76a15e: Migrate to biome linter
|
|
40
|
+
- Updated dependencies [2bfd9e1]
|
|
41
|
+
- Updated dependencies [4fe77de]
|
|
42
|
+
- Updated dependencies [b0d8af9]
|
|
43
|
+
- Updated dependencies [2bbff64]
|
|
44
|
+
- Updated dependencies [4baa8a4]
|
|
45
|
+
- Updated dependencies [db3a370]
|
|
46
|
+
- Updated dependencies [4ea0492]
|
|
47
|
+
- Updated dependencies [3555d9d]
|
|
48
|
+
- Updated dependencies [d3909ed]
|
|
49
|
+
- Updated dependencies [0416d6c]
|
|
50
|
+
- Updated dependencies [1601985]
|
|
51
|
+
- Updated dependencies [7d94f5c]
|
|
52
|
+
- Updated dependencies [3cb5aee]
|
|
53
|
+
- Updated dependencies [fa06b8b]
|
|
54
|
+
- Updated dependencies [84956b8]
|
|
55
|
+
- Updated dependencies [ed53b05]
|
|
56
|
+
- Updated dependencies [5855473]
|
|
57
|
+
- Updated dependencies [941363c]
|
|
58
|
+
- Updated dependencies [d681fd3]
|
|
59
|
+
- Updated dependencies [e76a15e]
|
|
60
|
+
- @pexip/media-processor@20.0.0
|
|
61
|
+
- @pexip/media-control@20.0.0
|
|
62
|
+
- @pexip/utils@17.0.0
|
|
63
|
+
- @pexip/signal@16.8.0
|
|
64
|
+
|
|
3
65
|
## 19.1.0
|
|
4
66
|
|
|
5
67
|
### Minor Changes
|
|
@@ -10,7 +10,7 @@ interface AudioStreamProcessorProps {
|
|
|
10
10
|
declare const FEATURE_KEYS: ['mixWithAdditionalMedia'];
|
|
11
11
|
type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
|
|
12
12
|
type FeatureProps = Pick<AudioStreamProcessorProps, FeaturePropKeys>;
|
|
13
|
-
export declare const updateFeatureProps: (constraints: MediaDeviceRequest[
|
|
13
|
+
export declare const updateFeatureProps: (constraints: MediaDeviceRequest["audio"], props: FeatureProps) => FeatureProps;
|
|
14
14
|
/**
|
|
15
15
|
* Create a Audio Mixing Processor and will own the stream passed-in
|
|
16
16
|
*/
|
|
@@ -12,7 +12,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
12
12
|
const [feature] = extracted[key];
|
|
13
13
|
if (feature !== undefined && props[key] !== feature) {
|
|
14
14
|
props[key] = feature;
|
|
15
|
-
|
|
15
|
+
accm[key] = feature;
|
|
16
|
+
return accm;
|
|
16
17
|
}
|
|
17
18
|
return accm;
|
|
18
19
|
}, {});
|
|
@@ -155,11 +156,10 @@ export const createAudioMixingProcess = (getCurrentMedia, label = PROCESSOR_LABE
|
|
|
155
156
|
overrideMute: true,
|
|
156
157
|
mute: toMute => {
|
|
157
158
|
prevMediaTrack.mute(toMute);
|
|
158
|
-
mainSource.node?.mediaStream
|
|
159
|
-
|
|
160
|
-
.forEach(track => {
|
|
159
|
+
for (const track of mainSource.node?.mediaStream.getAudioTracks() ??
|
|
160
|
+
[]) {
|
|
161
161
|
track.enabled = !toMute;
|
|
162
|
-
}
|
|
162
|
+
}
|
|
163
163
|
},
|
|
164
164
|
get muted() {
|
|
165
165
|
return (
|
package/dist/audioProcessor.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ interface AudioStreamProcessorProps {
|
|
|
78
78
|
declare const FEATURE_KEYS: ['denoise', 'vad', 'asd', 'contentHint'];
|
|
79
79
|
type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
|
|
80
80
|
type FeatureProps = Pick<AudioStreamProcessorProps, FeaturePropKeys>;
|
|
81
|
-
export declare const updateFeatureProps: (constraints: MediaDeviceRequest[
|
|
81
|
+
export declare const updateFeatureProps: (constraints: MediaDeviceRequest["audio"], props: FeatureProps) => FeatureProps;
|
|
82
82
|
/**
|
|
83
83
|
* Create a Audio Stream Processor and will own the stream passed-in
|
|
84
84
|
*/
|
package/dist/audioProcessor.js
CHANGED
|
@@ -32,7 +32,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
32
32
|
const [[feature] = []] = extracted[key];
|
|
33
33
|
if (isAudioContentHint(feature) && props[key] !== feature) {
|
|
34
34
|
props[key] = feature;
|
|
35
|
-
|
|
35
|
+
accm[key] = feature;
|
|
36
|
+
return accm;
|
|
36
37
|
}
|
|
37
38
|
return accm;
|
|
38
39
|
}
|
|
@@ -42,7 +43,8 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
42
43
|
const [feature] = extracted[key];
|
|
43
44
|
if (feature !== undefined && props[key] !== feature) {
|
|
44
45
|
props[key] = feature;
|
|
45
|
-
|
|
46
|
+
accm[key] = feature;
|
|
47
|
+
return accm;
|
|
46
48
|
}
|
|
47
49
|
return accm;
|
|
48
50
|
}
|
package/dist/baseLogger.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare enum LogLevels {
|
|
|
9
9
|
warn = 40,
|
|
10
10
|
error = 50,
|
|
11
11
|
fatal = 60,
|
|
12
|
-
silent
|
|
12
|
+
silent = 9007199254740991
|
|
13
13
|
}
|
|
14
14
|
export type LogLevelsString = keyof typeof LogLevels;
|
|
15
15
|
export type LogMethods = {
|
|
@@ -21,14 +21,23 @@ export type LogMethods = {
|
|
|
21
21
|
*/
|
|
22
22
|
export interface Logger extends LogMethods {
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Add a pattern to the redactor and an optional mask for the redaction.
|
|
25
25
|
*
|
|
26
|
-
* @
|
|
27
|
-
* The redaction set is applied globally, and only applies to the log file, not console logs.
|
|
26
|
+
* @see {@link Redactor}
|
|
28
27
|
*
|
|
29
|
-
* @param value -
|
|
28
|
+
* @param value - The pattern to add.
|
|
29
|
+
* @param replaceTo - The mask to use for redaction.
|
|
30
30
|
*/
|
|
31
|
-
|
|
31
|
+
addRedactPattern(value: string, redactTo?: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Redact the provided string by replacing all occurrences of the patterns
|
|
34
|
+
* and return a new string with redacted values.
|
|
35
|
+
*
|
|
36
|
+
* @see {@link Redactor}
|
|
37
|
+
*
|
|
38
|
+
* @param text - The string to redact.
|
|
39
|
+
*/
|
|
40
|
+
redact(text: string): string;
|
|
32
41
|
}
|
|
33
42
|
/**
|
|
34
43
|
* Create a logger with console API, and map fatal to error, skipping trace
|
package/dist/baseLogger.js
CHANGED
|
@@ -6,8 +6,7 @@ export var LogLevels;
|
|
|
6
6
|
LogLevels[LogLevels["warn"] = 40] = "warn";
|
|
7
7
|
LogLevels[LogLevels["error"] = 50] = "error";
|
|
8
8
|
LogLevels[LogLevels["fatal"] = 60] = "fatal";
|
|
9
|
-
|
|
10
|
-
LogLevels[LogLevels["silent"] = Number.MAX_SAFE_INTEGER] = "silent";
|
|
9
|
+
LogLevels[LogLevels["silent"] = 9007199254740991] = "silent";
|
|
11
10
|
})(LogLevels || (LogLevels = {}));
|
|
12
11
|
/**
|
|
13
12
|
* Create a logger with console API, and map fatal to error, skipping trace
|
|
@@ -15,15 +14,22 @@ export var LogLevels;
|
|
|
15
14
|
*/
|
|
16
15
|
export function createConsoleLogger() {
|
|
17
16
|
return Object.freeze({
|
|
18
|
-
/* eslint-disable no-console -- set logger to console */
|
|
19
17
|
fatal: (meta, message) => console.error(message, meta),
|
|
20
18
|
error: (meta, message) => console.error(message, meta),
|
|
21
19
|
warn: (meta, message) => console.warn(message, meta),
|
|
22
20
|
info: (meta, message) => console.info(message, meta),
|
|
23
21
|
debug: (meta, message) => console.debug(message, meta),
|
|
24
|
-
trace() {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
trace() {
|
|
23
|
+
// Noop
|
|
24
|
+
},
|
|
25
|
+
silent() {
|
|
26
|
+
// Noop
|
|
27
|
+
},
|
|
28
|
+
redact(text) {
|
|
29
|
+
return text;
|
|
30
|
+
},
|
|
31
|
+
addRedactPattern() {
|
|
32
|
+
// Noop
|
|
33
|
+
},
|
|
28
34
|
});
|
|
29
35
|
}
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
package/dist/displayMedia.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { DisplayMediaOptions } from '@pexip/media-control';
|
|
2
|
-
export declare const createGetDisplayMedia: (getDefaultConstraints: () => DisplayMediaOptions, getDisplayMedia?: MediaDevices[
|
|
2
|
+
export declare const createGetDisplayMedia: (getDefaultConstraints: () => DisplayMediaOptions, getDisplayMedia?: MediaDevices["getDisplayMedia"]) => (constraints?: DisplayMediaOptions) => Promise<MediaStream | undefined>;
|
|
3
3
|
export type GetDisplayMedia = ReturnType<typeof createGetDisplayMedia>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { createAudioStreamProcess } from './audioProcessor';
|
|
3
|
-
export { createVideoStreamProcess } from './videoProcessor';
|
|
4
|
-
export * from './types';
|
|
5
|
-
export { setLogger } from './logger';
|
|
1
|
+
export * from './displayMedia';
|
|
6
2
|
export * from './previewController';
|
|
7
|
-
export * from './status';
|
|
8
3
|
export * from './signals';
|
|
4
|
+
export * from './status';
|
|
5
|
+
export * from './typeGuard';
|
|
6
|
+
export * from './types';
|
|
9
7
|
export { applyContentHint } from './utils';
|
|
10
8
|
export { createAudioMixingProcess } from './audioMixingProcessor';
|
|
11
|
-
export
|
|
9
|
+
export { createAudioStreamProcess } from './audioProcessor';
|
|
10
|
+
export { createMedia } from './media';
|
|
11
|
+
export { createVideoStreamProcess } from './videoProcessor';
|
|
12
|
+
export { setLogger } from './logger';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { createAudioStreamProcess } from './audioProcessor';
|
|
3
|
-
export { createVideoStreamProcess } from './videoProcessor';
|
|
4
|
-
export * from './types';
|
|
5
|
-
export { setLogger } from './logger';
|
|
1
|
+
export * from './displayMedia';
|
|
6
2
|
export * from './previewController';
|
|
7
|
-
export * from './status';
|
|
8
3
|
export * from './signals';
|
|
4
|
+
export * from './status';
|
|
5
|
+
export * from './typeGuard';
|
|
6
|
+
export * from './types';
|
|
9
7
|
export { applyContentHint } from './utils';
|
|
10
8
|
export { createAudioMixingProcess } from './audioMixingProcessor';
|
|
11
|
-
export
|
|
9
|
+
export { createAudioStreamProcess } from './audioProcessor';
|
|
10
|
+
export { createMedia } from './media';
|
|
11
|
+
export { createVideoStreamProcess } from './videoProcessor';
|
|
12
|
+
export { setLogger } from './logger';
|
package/dist/logger.js
CHANGED
|
@@ -30,11 +30,8 @@ export const createLogProxyHandler = (logger, name, scope) => {
|
|
|
30
30
|
logger.debug({ scope, name, prop: p, value }, `called get ${name}[${String(p)}]`);
|
|
31
31
|
return value;
|
|
32
32
|
},
|
|
33
|
-
// eslint-disable-next-line max-params --- from lib.es2015 and log
|
|
34
33
|
set: (target, p, value, receiver) => {
|
|
35
|
-
logger.
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- it's a log call...
|
|
37
|
-
{ scope, name, prop: p, value }, `called set ${name}[${String(p)}]`);
|
|
34
|
+
logger.info({ scope, name, prop: p, value }, `called set ${name}[${String(p)}]`);
|
|
38
35
|
return Reflect.set(target, p, value, receiver);
|
|
39
36
|
},
|
|
40
37
|
};
|
package/dist/media.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IndexedDevices, MediaDeviceRequest, InputDevicePermission } from '@pexip/media-control';
|
|
1
|
+
import type { IndexedDevices, MediaDeviceRequest, InputDevicePermission, InputConstraintSet } from '@pexip/media-control';
|
|
2
2
|
import type { GetUserMediaProcess, Media, MediaController, MediaOptions, MediaSignals, MediaTrack } from './types';
|
|
3
3
|
export interface MediaUpdaterOptions {
|
|
4
4
|
getUserMedia: GetUserMediaProcess;
|
|
@@ -7,8 +7,12 @@ export interface MediaUpdaterOptions {
|
|
|
7
7
|
onMediaTracksChanged: (media: Media, tracks: MediaTrack[]) => void;
|
|
8
8
|
getInputDevicePermission?: () => Promise<InputDevicePermission>;
|
|
9
9
|
signals?: MediaSignals;
|
|
10
|
+
getDefaultConstraints?: () => {
|
|
11
|
+
audio?: InputConstraintSet | false;
|
|
12
|
+
video?: InputConstraintSet | false;
|
|
13
|
+
};
|
|
10
14
|
}
|
|
11
|
-
export declare const createMediaUpdater: ({ getUserMedia, getCurrentDevices, shouldDiscardMedia: shouldDisgardMedia, onMediaTracksChanged, getInputDevicePermission, signals, }: MediaUpdaterOptions) => (constraints: MediaDeviceRequest, currentMedia: Media | undefined) => Promise<void>;
|
|
15
|
+
export declare const createMediaUpdater: ({ getUserMedia, getCurrentDevices, shouldDiscardMedia: shouldDisgardMedia, onMediaTracksChanged, getInputDevicePermission, getDefaultConstraints, signals, }: MediaUpdaterOptions) => (constraints: MediaDeviceRequest, currentMedia: Media | undefined) => Promise<void>;
|
|
12
16
|
/**
|
|
13
17
|
* Create an object to interact with the media scream, which is usually used for
|
|
14
18
|
* our main stream.
|
package/dist/media.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createIndexedDevices, createStreamTrackEventSubscriptions, getDevices, getInputDevicePermissionState, isIndexedDevices, isRequestedResolution, mergeConstraints, } from '@pexip/media-control';
|
|
2
2
|
import { createAsyncQueue, isEmpty, assert } from '@pexip/utils';
|
|
3
3
|
import { internalSignals } from './signals';
|
|
4
4
|
import { UserMediaStatus } from './types';
|
|
5
5
|
import { createModuleLogger, logger } from './logger';
|
|
6
6
|
import { createGetUserMediaProcess, requestUserMediaWithRetry, } from './userMedia';
|
|
7
|
-
import { AUDIO_SETTINGS_KEYS, MIXING_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, buildMedia, createMediaProcessor, diffSettings, getDevicesChanges, hasPtzFeature, mergeSettings, refineMediaConstraints, } from './utils';
|
|
8
|
-
import { getPermissionStatus,
|
|
7
|
+
import { AUDIO_SETTINGS_KEYS, MIXING_SETTINGS_KEYS, PAN_TILT_ZOOM_SETTINGS_KEYS, VIDEO_SETTINGS_KEYS, buildMedia, createMediaProcessor, diffSettings, getDevicesChanges, getSettingsFromKeys, hasPtzFeature, isApplyingRenderEffect, mergeSettings, refineMediaConstraints, } from './utils';
|
|
8
|
+
import { getPermissionStatus, isRequestPermissionNeeded } from './status';
|
|
9
9
|
import { isMedia } from './typeGuard';
|
|
10
10
|
import { updateFeatureProps as getVideoFeatures } from './videoProcessor';
|
|
11
11
|
import { updateFeatureProps as getAudioFeatures } from './audioProcessor';
|
|
12
12
|
import { updateFeatureProps as getMixingFeatures } from './audioMixingProcessor';
|
|
13
|
-
import { GET_USER_MEDIA_TIMEOUT_MS } from './constants';
|
|
14
|
-
export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDiscardMedia: shouldDisgardMedia, onMediaTracksChanged, getInputDevicePermission = getInputDevicePermissionState, signals, }) => {
|
|
13
|
+
import { GET_USER_MEDIA_TIMEOUT_MS, DEVICE_CHANGE_DEBOUNCE_TIMEOUT_MS, } from './constants';
|
|
14
|
+
export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDiscardMedia: shouldDisgardMedia, onMediaTracksChanged, getInputDevicePermission = getInputDevicePermissionState, getDefaultConstraints, signals, }) => {
|
|
15
15
|
return async (constraints, currentMedia) => {
|
|
16
16
|
const currentDevices = await getCurrentDevices();
|
|
17
17
|
const permission = await getInputDevicePermission();
|
|
@@ -41,14 +41,26 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
|
|
|
41
41
|
const videoFeatures = getVideoFeatures(constraints.video, {});
|
|
42
42
|
const audioFeatures = getAudioFeatures(constraints.audio, {});
|
|
43
43
|
const mixingFeatures = getMixingFeatures(constraints.audio, {});
|
|
44
|
-
const hasRequestedResolution =
|
|
44
|
+
const hasRequestedResolution = isApplyingRenderEffect(videoFeatures.videoSegmentation ?? 'none') || // Skip when there is a render effect to modify the video
|
|
45
45
|
currentDevices.size('videoinput') === 0 || // Skip when no authorized video found
|
|
46
46
|
isRequestedResolution(constraints.video, currentMedia?.getVideoTracks().at(0)?.getSettings());
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
const defaultAudioSettings = getSettingsFromKeys(AUDIO_SETTINGS_KEYS, getDefaultConstraints?.().audio);
|
|
48
|
+
const audioFeaturesChanged = defaultAudioSettings === false
|
|
49
|
+
? undefined
|
|
50
|
+
: diffSettings(AUDIO_SETTINGS_KEYS)(prevAudioSettings ?? defaultAudioSettings, audioFeatures);
|
|
51
|
+
const defaultVideoSettings = getSettingsFromKeys(VIDEO_SETTINGS_KEYS, getDefaultConstraints?.().video);
|
|
52
|
+
const videoFeaturesChanged = defaultVideoSettings === false
|
|
53
|
+
? undefined
|
|
54
|
+
: diffSettings(VIDEO_SETTINGS_KEYS)(prevVideoSettings ?? defaultVideoSettings, videoFeatures);
|
|
55
|
+
const defaultMixingSettings = getSettingsFromKeys(MIXING_SETTINGS_KEYS, getDefaultConstraints?.().audio);
|
|
56
|
+
const mixingFeaturesChanged = defaultMixingSettings === false
|
|
57
|
+
? undefined
|
|
58
|
+
: diffSettings(MIXING_SETTINGS_KEYS)(prevAudioSettings ?? defaultMixingSettings, mixingFeatures);
|
|
59
|
+
const defaultPTZSettings = getSettingsFromKeys(PAN_TILT_ZOOM_SETTINGS_KEYS, getDefaultConstraints?.().video);
|
|
60
|
+
const ptzFeaturesChanges = defaultPTZSettings === false
|
|
61
|
+
? undefined
|
|
62
|
+
: hasPtzFeature() &&
|
|
63
|
+
diffSettings(PAN_TILT_ZOOM_SETTINGS_KEYS)(prevVideoSettings ?? defaultPTZSettings, videoFeatures);
|
|
52
64
|
const audioRequest = refineMediaConstraints({
|
|
53
65
|
kind: 'audioinput',
|
|
54
66
|
request: constraints.audio,
|
|
@@ -62,9 +74,9 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
|
|
|
62
74
|
currentDevices,
|
|
63
75
|
permission: permission.video,
|
|
64
76
|
currentMediaTracks: currentMedia?.getVideoTracks() ?? [],
|
|
65
|
-
force: !hasRequestedResolution ||
|
|
77
|
+
force: !hasRequestedResolution || !isEmpty(ptzFeaturesChanges),
|
|
66
78
|
});
|
|
67
|
-
if (audioRequest
|
|
79
|
+
if (audioRequest || videoRequest) {
|
|
68
80
|
const media = await getUserMedia({
|
|
69
81
|
constraints: { audio: audioRequest, video: videoRequest },
|
|
70
82
|
originalConstraints: constraints,
|
|
@@ -80,6 +92,48 @@ export const createMediaUpdater = ({ getUserMedia, getCurrentDevices, shouldDisc
|
|
|
80
92
|
...(videoRequest ? media.getVideoTracks() : []),
|
|
81
93
|
]);
|
|
82
94
|
}
|
|
95
|
+
else if (currentMedia?.status) {
|
|
96
|
+
// No media request, update status only
|
|
97
|
+
const anyAudioTrack = currentMedia
|
|
98
|
+
.getAudioTracks()
|
|
99
|
+
.some(track => track.track?.readyState === 'live');
|
|
100
|
+
const anyVideoTrack = currentMedia
|
|
101
|
+
.getVideoTracks()
|
|
102
|
+
.some(track => track.track?.readyState === 'live');
|
|
103
|
+
if (anyAudioTrack) {
|
|
104
|
+
if (anyVideoTrack) {
|
|
105
|
+
currentMedia.status = UserMediaStatus.PermissionsGranted;
|
|
106
|
+
}
|
|
107
|
+
else if (currentDevices.size('videoinput') === 0) {
|
|
108
|
+
currentMedia.status =
|
|
109
|
+
UserMediaStatus.PermissionsOnlyAudioinputNoVideoDevices;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
currentMedia.status =
|
|
113
|
+
UserMediaStatus.PermissionsOnlyAudioinput;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
if (anyVideoTrack) {
|
|
118
|
+
if (currentDevices.size('audioinput') === 0) {
|
|
119
|
+
currentMedia.status =
|
|
120
|
+
UserMediaStatus.PermissionsOnlyVideoinputNoAudioDevices;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
currentMedia.status =
|
|
124
|
+
UserMediaStatus.PermissionsOnlyVideoinput;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (currentDevices.size('audioinput') === 0 &&
|
|
128
|
+
currentDevices.size('videoinput') === 0) {
|
|
129
|
+
currentMedia.status = UserMediaStatus.NoDevicesFound;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
currentMedia.status = UserMediaStatus.PermissionsRejected;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
logger.debug({ status: currentMedia.status }, 'Update existing media status');
|
|
136
|
+
}
|
|
83
137
|
const audioDiff = constraints.audio &&
|
|
84
138
|
audioRequest === undefined &&
|
|
85
139
|
currentMedia?.getAudioTracks().at(0)
|
|
@@ -206,9 +260,6 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
|
|
|
206
260
|
},
|
|
207
261
|
});
|
|
208
262
|
const getCurrentDevices = async () => {
|
|
209
|
-
if (props.devices.size()) {
|
|
210
|
-
return props.devices;
|
|
211
|
-
}
|
|
212
263
|
props.devices = createIndexedDevices(await getDevices());
|
|
213
264
|
return props.devices;
|
|
214
265
|
};
|
|
@@ -277,10 +328,11 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
|
|
|
277
328
|
getUserMedia: getUserMediaProcess,
|
|
278
329
|
getCurrentDevices,
|
|
279
330
|
shouldDiscardMedia: () => props.discardMedia,
|
|
331
|
+
getDefaultConstraints,
|
|
280
332
|
onMediaTracksChanged: (media, tracks) => {
|
|
281
333
|
// Sync mute state to input tracks
|
|
282
334
|
syncMuteState(tracks);
|
|
283
|
-
|
|
335
|
+
for (const track of tracks) {
|
|
284
336
|
if (track.track) {
|
|
285
337
|
const unsubscribe = createStreamTrackEventSubscriptions(track.track, {
|
|
286
338
|
ended: track => {
|
|
@@ -291,7 +343,8 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
|
|
|
291
343
|
unmute: signals.onStreamTrackUnmuted?.emit,
|
|
292
344
|
});
|
|
293
345
|
}
|
|
294
|
-
}
|
|
346
|
+
}
|
|
347
|
+
logger.debug({ media, tracks }, 'Update media tracks');
|
|
295
348
|
props.media = media;
|
|
296
349
|
processAndUpdateMedia(media, tracks).catch((error) => {
|
|
297
350
|
if (error instanceof Error) {
|
|
@@ -337,13 +390,14 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
|
|
|
337
390
|
queue.enqueue(async () => {
|
|
338
391
|
const permission = await getInputDevicePermissionState();
|
|
339
392
|
const status = getPermissionStatus(permission);
|
|
340
|
-
|
|
393
|
+
logger.info({ permission, status }, 'tryAndGetUserMedia');
|
|
394
|
+
if (!isRequestPermissionNeeded(status)) {
|
|
341
395
|
await updateMedia(mergeMediaConstraints(constraints));
|
|
342
396
|
}
|
|
343
397
|
else {
|
|
344
398
|
const currentDevices = await getCurrentDevices();
|
|
345
399
|
props.media = buildMedia({
|
|
346
|
-
constraints,
|
|
400
|
+
constraints: mergeMediaConstraints(constraints),
|
|
347
401
|
permission,
|
|
348
402
|
devices: currentDevices,
|
|
349
403
|
status,
|
|
@@ -354,34 +408,22 @@ export const createMedia = ({ getMuteState, signals, audioProcessors, videoProce
|
|
|
354
408
|
}
|
|
355
409
|
});
|
|
356
410
|
};
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
if (
|
|
360
|
-
|
|
411
|
+
let deviceChangeTimeoutId = 0;
|
|
412
|
+
const clearDeviceChangeTimeout = () => {
|
|
413
|
+
if (deviceChangeTimeoutId) {
|
|
414
|
+
clearTimeout(deviceChangeTimeoutId);
|
|
415
|
+
deviceChangeTimeoutId = 0;
|
|
361
416
|
}
|
|
362
417
|
};
|
|
363
|
-
const handleDeviceChange = (
|
|
364
|
-
|
|
418
|
+
const handleDeviceChange = () => {
|
|
419
|
+
logger.debug('devicechange event triggered');
|
|
420
|
+
clearDeviceChangeTimeout();
|
|
421
|
+
deviceChangeTimeoutId = setTimeout(getCurrentDevices, DEVICE_CHANGE_DEBOUNCE_TIMEOUT_MS);
|
|
365
422
|
};
|
|
366
423
|
// Subscribe Media Events for devices
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
logger.debug(event.detail, 'NoInputDevices emitted');
|
|
371
|
-
handleNoInputs(event.detail.devices);
|
|
372
|
-
break;
|
|
373
|
-
case MediaEventType.DevicesFound:
|
|
374
|
-
case MediaEventType.DevicesLost:
|
|
375
|
-
logger.debug(event.detail, 'DevicesFound/Lost emitted');
|
|
376
|
-
handleDeviceChange([
|
|
377
|
-
...event.detail.authorizedDevices,
|
|
378
|
-
...event.detail.unauthorizedDevices,
|
|
379
|
-
]);
|
|
380
|
-
break;
|
|
381
|
-
default:
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
});
|
|
424
|
+
if ('mediaDevices' in navigator) {
|
|
425
|
+
navigator.mediaDevices.addEventListener('devicechange', handleDeviceChange);
|
|
426
|
+
}
|
|
385
427
|
return {
|
|
386
428
|
get updatingMedia() {
|
|
387
429
|
return props.updatingMedia;
|
|
@@ -21,7 +21,8 @@ const extractFeaturesToConstraints = (keysToLookFor, settings) => {
|
|
|
21
21
|
}
|
|
22
22
|
return keysToLookFor.reduce((set, key) => {
|
|
23
23
|
if (settings[key] !== undefined) {
|
|
24
|
-
|
|
24
|
+
// @ts-expect-error performance
|
|
25
|
+
set[key] = settings[key];
|
|
25
26
|
}
|
|
26
27
|
return set;
|
|
27
28
|
}, {});
|
|
@@ -289,11 +290,9 @@ export const createPreviewStreamController = ({ getCurrentDevices, getCurrentMed
|
|
|
289
290
|
const { audio: previewAudio, video: previewVideo } = props.media?.getSettings() ?? { audio: {}, video: {} };
|
|
290
291
|
if (force || hasChanges()) {
|
|
291
292
|
const audioSettings = extractFeaturesToConstraints(AUDIO_SETTINGS_KEYS, previewAudio);
|
|
292
|
-
const videoSettings = extractFeaturesToConstraints(VIDEO_SETTINGS_KEYS, previewVideo);
|
|
293
293
|
// Omit the width & height settings from the preview so that the one
|
|
294
294
|
// from the main can be applied
|
|
295
|
-
|
|
296
|
-
delete videoSettings.height;
|
|
295
|
+
const { width, height, ...videoSettings } = extractFeaturesToConstraints(VIDEO_SETTINGS_KEYS, previewVideo);
|
|
297
296
|
logger.info({ audioInput, videoInput, audioSettings, videoSettings }, 'Apply changes to main');
|
|
298
297
|
try {
|
|
299
298
|
await replaceMainStream({
|
package/dist/signals.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Signal, SignalVariant } from '@pexip/signal';
|
|
2
2
|
import type { IndexedDevices } from '@pexip/media-control';
|
|
3
|
-
import type { MediaSignals } from './types';
|
|
3
|
+
import type { MediaSignals, MediaSignalsOptional } from './types';
|
|
4
4
|
/**
|
|
5
5
|
* Create a general signal with consistent scoped name
|
|
6
6
|
*
|
|
@@ -8,7 +8,7 @@ import type { MediaSignals } from './types';
|
|
|
8
8
|
* @param crucial - Signify if the signal is unmissable. @defaultValue true
|
|
9
9
|
* @param variant - The variant of the signal @see Signal @defaultValue 'generic'
|
|
10
10
|
*/
|
|
11
|
-
export declare const createMediaSignal: <T = undefined>(name: string, crucial?: boolean, variant?: SignalVariant) => Signal<T>;
|
|
11
|
+
export declare const createMediaSignal: <T = undefined>(name: string, crucial?: boolean, variant?: Exclude<SignalVariant, "batched">) => Signal<T, T>;
|
|
12
12
|
export declare const REQUIRED_SIGNAL_KEYS: readonly ["onMediaChanged", "onVAD", "onSilentDetected"];
|
|
13
13
|
/**
|
|
14
14
|
* Create and return all required and optional (if specified with `more`),
|
|
@@ -23,12 +23,12 @@ export declare const REQUIRED_SIGNAL_KEYS: readonly ["onMediaChanged", "onVAD",
|
|
|
23
23
|
*
|
|
24
24
|
* @see REQUIRED_SIGNAL_KEYS
|
|
25
25
|
*/
|
|
26
|
-
export declare const createMediaSignals: <K extends
|
|
26
|
+
export declare const createMediaSignals: <K extends keyof MediaSignalsOptional>(more: K[], scope?: string) => Pick<Required<MediaSignals>, "onMediaChanged" | "onVAD" | "onSilentDetected" | K>;
|
|
27
27
|
export type GeneratedMediaSignals<K extends keyof MediaSignals> = Pick<Required<MediaSignals>, K>;
|
|
28
28
|
/**
|
|
29
29
|
* Internal signals
|
|
30
30
|
* @internal
|
|
31
31
|
*/
|
|
32
32
|
export declare const internalSignals: {
|
|
33
|
-
onDevicesChanged: Signal<IndexedDevices>;
|
|
33
|
+
onDevicesChanged: Signal<IndexedDevices, IndexedDevices>;
|
|
34
34
|
};
|
package/dist/signals.js
CHANGED
|
@@ -6,7 +6,9 @@ import { createSignal } from '@pexip/signal';
|
|
|
6
6
|
* @param crucial - Signify if the signal is unmissable. @defaultValue true
|
|
7
7
|
* @param variant - The variant of the signal @see Signal @defaultValue 'generic'
|
|
8
8
|
*/
|
|
9
|
-
export const createMediaSignal = (name, crucial = true,
|
|
9
|
+
export const createMediaSignal = (name, crucial = true,
|
|
10
|
+
// FIXME: need `schedule` function to support `batched` variant
|
|
11
|
+
variant = 'generic') => createSignal({
|
|
10
12
|
name: `media/${name}`,
|
|
11
13
|
allowEmittingWithoutObserver: !crucial,
|
|
12
14
|
variant,
|
|
@@ -31,10 +33,11 @@ export const REQUIRED_SIGNAL_KEYS = [
|
|
|
31
33
|
*/
|
|
32
34
|
export const createMediaSignals = (more, scope = '') => {
|
|
33
35
|
const signalScope = scope && [scope, ':'].join('');
|
|
34
|
-
return [...REQUIRED_SIGNAL_KEYS, ...more].reduce((signals, key) =>
|
|
35
|
-
|
|
36
|
-
[key]
|
|
37
|
-
|
|
36
|
+
return [...REQUIRED_SIGNAL_KEYS, ...more].reduce((signals, key) => {
|
|
37
|
+
// @ts-expect-error
|
|
38
|
+
signals[key] = createMediaSignal(`${signalScope}${key}`);
|
|
39
|
+
return signals;
|
|
40
|
+
}, {});
|
|
38
41
|
};
|
|
39
42
|
/**
|
|
40
43
|
* Internal signals
|
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"}
|