@pexip/media 18.4.0 → 19.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +130 -0
- package/README.md +9 -8
- package/dist/audioMixingProcessor.d.ts +2 -2
- package/dist/audioMixingProcessor.js +113 -100
- package/dist/audioProcessor.d.ts +4 -4
- package/dist/audioProcessor.js +117 -152
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +7 -0
- package/dist/media.d.ts +12 -2
- package/dist/media.js +242 -170
- package/dist/previewController.d.ts +10 -9
- package/dist/previewController.js +99 -68
- package/dist/signals.d.ts +10 -1
- package/dist/signals.js +9 -0
- package/dist/status.d.ts +5 -2
- package/dist/status.js +3 -3
- package/dist/types.d.ts +135 -40
- package/dist/userMedia.d.ts +10 -8
- package/dist/userMedia.js +122 -178
- package/dist/utils.d.ts +25 -47
- package/dist/utils.js +487 -186
- package/dist/videoProcessor.d.ts +3 -7
- package/dist/videoProcessor.js +103 -139
- package/package.json +12 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,135 @@
|
|
|
1
1
|
# @pexip/media
|
|
2
2
|
|
|
3
|
+
## 19.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 878fcca: Decouple Audio/Video request
|
|
8
|
+
|
|
9
|
+
The library is no longer assuming the request constraints both video and
|
|
10
|
+
audio, and there is no retry mechanism builtin. The media request and
|
|
11
|
+
processing are decoupled as well to minimize the time to get the media
|
|
12
|
+
flowing.
|
|
13
|
+
|
|
14
|
+
Changes of `Media` object:
|
|
15
|
+
|
|
16
|
+
- `MediaTrack`-based construct: An `Media` construct is changed to base on
|
|
17
|
+
`MediaTrack` which is a wrapper to `MediaStreamTrack` with a linked list to
|
|
18
|
+
reference on different `MediaStreamTrack` produced by the processor as well
|
|
19
|
+
as the original source track obtained from `getUserMedia` API.
|
|
20
|
+
- All track related functions are implemented by `MediaTrack`, e.g.
|
|
21
|
+
`applyConstraints`, `getSettings` and `clone`, etc.
|
|
22
|
+
- `Media['getSettings']` returns
|
|
23
|
+
`{audio?: ExtendedMediaTrackSettings, video?: ExtendedMediaTrackSettings}`
|
|
24
|
+
- The following attributes/functions are added to media object:
|
|
25
|
+
- `active: boolean`;
|
|
26
|
+
- `id: string`;
|
|
27
|
+
- `getConstraints(): MediaDeviceRequest`;
|
|
28
|
+
- `setOriginalConstraints(constraints: MediaDeviceRequest): void`;
|
|
29
|
+
- `getOriginalConstraints(): MediaDeviceRequest`;
|
|
30
|
+
- `isAudioInputUnavailable(): boolean`;
|
|
31
|
+
- `isVideoInputUnavailable(): boolean`;
|
|
32
|
+
- `requestedAudio(): boolean`;
|
|
33
|
+
- `requestedVideo(): boolean`;
|
|
34
|
+
- `getTracks(): MediaTrack[]`;
|
|
35
|
+
- `getAudioTracks(): MediaTrack[]`;
|
|
36
|
+
- `getVideoTracks(): MediaTrack[]`;
|
|
37
|
+
- `addTrack(track: MediaTrack): void`;
|
|
38
|
+
- `removeTrack(track: MediaTrack): void`;
|
|
39
|
+
- `clone(): Media`;
|
|
40
|
+
- BREAKING CHANGE:
|
|
41
|
+
- `Media['rawStream']` is removed, and we no longer keep a reference of the
|
|
42
|
+
original `MediaStream`, we keep the source track instead. See
|
|
43
|
+
`MediaTrack['source']`.
|
|
44
|
+
- `Media['constraints']` is removed, please use `Media['getConstraints']` or
|
|
45
|
+
`Media['getOriginalConstraints']` to get the constraints.
|
|
46
|
+
- The `processors` parameter for `createMedia({processors, ...})` has been
|
|
47
|
+
split into `audioProcessors` and `videoProcessors`, i.e.
|
|
48
|
+
`createMedia({videoProcessors, audioProcessors, ...})`
|
|
49
|
+
|
|
50
|
+
Changes of processing pipeline:
|
|
51
|
+
|
|
52
|
+
- The pipeline is changed to based on `MediaTrack`.
|
|
53
|
+
- Video processing pipeline and Audio processing pipeline are run in parallel
|
|
54
|
+
whereas the pipeline itself is run in series
|
|
55
|
+
- `getUserMedia` and processing pipeline are decoupled
|
|
56
|
+
- `MediaStream` is created as soon as the `Media` object is created which
|
|
57
|
+
could contains no track.
|
|
58
|
+
- Any subsequent `getUserMedia` API call will update the track in the
|
|
59
|
+
`MediaStream` object. i.e. `MediaStream` will remain the same through out
|
|
60
|
+
the whole life cycle of the App and the tracks are updated according the
|
|
61
|
+
`getUserMedia` with an assumption that a `MediaStream` contains at most 1
|
|
62
|
+
audio track and 1 video track. `addtrack` and `removetrack` events are
|
|
63
|
+
triggered whenever there is any changes to the `MediaStream`
|
|
64
|
+
|
|
65
|
+
Changes of media signals;
|
|
66
|
+
|
|
67
|
+
The following signals are added
|
|
68
|
+
|
|
69
|
+
- `onAddTrack`: Emit when a new track is added to the `MediaStream`
|
|
70
|
+
- `onRemoveTrack`: Emit when a track from the `MediaStream` is removed
|
|
71
|
+
- `onUpdatingMedia`: Emit whenever a track is added or removed from the
|
|
72
|
+
`Media` object
|
|
73
|
+
- `onTrackReleased`: Emit when a track is stopped
|
|
74
|
+
|
|
75
|
+
The behavior of the following signals are changed:
|
|
76
|
+
|
|
77
|
+
- `onAudioMuteStateChanged`: Emit whenever the mute state of the `MediaTrack`
|
|
78
|
+
is changed or the mute state of the source track. Here the mute state
|
|
79
|
+
includes the following: - `MediaStreamTrack.enabled` -
|
|
80
|
+
`MediaStreamTrack.muted` triggered by `mute` and `unmute` event -
|
|
81
|
+
`readyState` is `ended` triggered by `ended` event
|
|
82
|
+
- `onVideoMuteStateChanged`: Emit whenever the mute state of the `MediaTrack`
|
|
83
|
+
is changed or the mute state of the source track. Here the mute state
|
|
84
|
+
includes the following: - `MediaStreamTrack.enabled` -
|
|
85
|
+
`MediaStreamTrack.muted` triggered by `mute` and `unmute` event -
|
|
86
|
+
`readyState` is `ended` triggered by `ended` event
|
|
87
|
+
|
|
88
|
+
### Minor Changes
|
|
89
|
+
|
|
90
|
+
- 029198f: Fix device not found error mappinng
|
|
91
|
+
- b13010c: Handle timeout error
|
|
92
|
+
- 7d9eac2: Add processed track state signals
|
|
93
|
+
|
|
94
|
+
- onStreamTrackEndedFinal - Emitted when the final track is ended
|
|
95
|
+
- onStreamTrackMutedFinal - Emitted when the final track is muted
|
|
96
|
+
- onStreamTrackUnmutedFinal - Emitted when the final track is unmuted
|
|
97
|
+
|
|
98
|
+
### Patch Changes
|
|
99
|
+
|
|
100
|
+
- Updated dependencies [6c42e61]
|
|
101
|
+
- Updated dependencies [e362a65]
|
|
102
|
+
- Updated dependencies [22a807b]
|
|
103
|
+
- Updated dependencies [878fcca]
|
|
104
|
+
- Updated dependencies [5b9daa4]
|
|
105
|
+
- Updated dependencies [164a3e2]
|
|
106
|
+
- Updated dependencies [b13010c]
|
|
107
|
+
- Updated dependencies [66a8f72]
|
|
108
|
+
- Updated dependencies [4fd97b2]
|
|
109
|
+
- Updated dependencies [db258ca]
|
|
110
|
+
- Updated dependencies [dfd1900]
|
|
111
|
+
- @pexip/media-control@19.0.0
|
|
112
|
+
- @pexip/media-processor@19.0.0
|
|
113
|
+
- @pexip/utils@16.13.0
|
|
114
|
+
- @pexip/signal@16.7.1
|
|
115
|
+
|
|
116
|
+
## 18.5.0
|
|
117
|
+
|
|
118
|
+
### Minor Changes
|
|
119
|
+
|
|
120
|
+
- f146682: Make `delegate` prop to be a fn
|
|
121
|
+
|
|
122
|
+
### Patch Changes
|
|
123
|
+
|
|
124
|
+
- d98fe9d: Move to module type.
|
|
125
|
+
- Updated dependencies [d98fe9d]
|
|
126
|
+
- Updated dependencies [acd7aba]
|
|
127
|
+
- Updated dependencies [f146682]
|
|
128
|
+
- @pexip/media-processor@18.5.0
|
|
129
|
+
- @pexip/media-control@18.5.0
|
|
130
|
+
- @pexip/signal@16.7.1
|
|
131
|
+
- @pexip/utils@16.12.1
|
|
132
|
+
|
|
3
133
|
## 18.4.0
|
|
4
134
|
|
|
5
135
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -19,10 +19,11 @@ Its major features:
|
|
|
19
19
|
- Create a media pipeline to get and process the `MediaStream`, see
|
|
20
20
|
`createMediaPipeline`. It also provides some media features including:
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
- Video Processing - background blur
|
|
23
|
+
- Audio Processing - Noise Suppression
|
|
24
|
+
- Audio Signal Detection - To detect if the active audio input device is
|
|
25
|
+
capturing any audio
|
|
26
|
+
- Voice Activity Detection - To detect if there is any voice activity
|
|
26
27
|
|
|
27
28
|
- Provide media input related information
|
|
28
29
|
- Verify if we need to request a new `MediaStream`, see `updateMedia`
|
|
@@ -40,7 +41,7 @@ A processor to process video from the stream to be used together with
|
|
|
40
41
|
|
|
41
42
|
### `createPreviewController`
|
|
42
43
|
|
|
43
|
-
It
|
|
44
|
+
It creates an object to control the provided media stream, e.g. change the
|
|
44
45
|
audio/video input device and apply changes to the main stream when necessary.
|
|
45
46
|
|
|
46
47
|
## Usage
|
|
@@ -195,10 +196,9 @@ const transformer = createCanvasTransform(segmenter, {
|
|
|
195
196
|
backgroundImageUrl: states.backgroundImageUrl,
|
|
196
197
|
});
|
|
197
198
|
|
|
198
|
-
const delegate = isChromium() ? 'GPU' : 'CPU';
|
|
199
199
|
const selfie = createSegmenter(taskVisionBasePath, {
|
|
200
200
|
modelAsset,
|
|
201
|
-
delegate,
|
|
201
|
+
delegate: () => 'GPU', // Use GPU
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
export const segmenters: Partial<Segmenters> = {
|
|
@@ -218,7 +218,8 @@ const videoStreamProcessor = createVideoStreamProcess({
|
|
|
218
218
|
export const mediaController = createMedia({
|
|
219
219
|
getMuteState: () => states.muted,
|
|
220
220
|
signals,
|
|
221
|
-
|
|
221
|
+
audioProcessors: [], // No audio processor
|
|
222
|
+
videoProcessors: [videoStreamProcessor],
|
|
222
223
|
});
|
|
223
224
|
|
|
224
225
|
// Hook-up the signals to get the update
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MediaDeviceRequest } from '@pexip/media-control';
|
|
2
2
|
import type { AudioGraph, AudioNodeInit } from '@pexip/media-processor';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TrackProcessor } from './types';
|
|
4
4
|
interface AudioStreamProcessorProps {
|
|
5
5
|
mixWithAdditionalMedia?: boolean;
|
|
6
6
|
merger?: AudioNodeInit<ChannelMergerNode, ChannelMergerNode>;
|
|
@@ -14,5 +14,5 @@ export declare const updateFeatureProps: (constraints: MediaDeviceRequest['audio
|
|
|
14
14
|
/**
|
|
15
15
|
* Create a Audio Mixing Processor and will own the stream passed-in
|
|
16
16
|
*/
|
|
17
|
-
export declare const createAudioMixingProcess: (
|
|
17
|
+
export declare const createAudioMixingProcess: (getCurrentMedia: () => MediaStream | undefined, label?: "AudioMixingProcessor") => TrackProcessor;
|
|
18
18
|
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isEmpty } from '@pexip/utils';
|
|
1
|
+
import { extractConstraintsWithKeys } from '@pexip/media-control';
|
|
2
|
+
import { isEmpty, assert } from '@pexip/utils';
|
|
3
3
|
import { createAudioGraph, createAudioGraphProxy, createChannelMergerGraphNode, createStreamDestinationGraphNode, createStreamSourceGraphNode, resumeAudioOnUnmute, } from '@pexip/media-processor';
|
|
4
4
|
import { logger } from './logger';
|
|
5
|
-
import {
|
|
5
|
+
import { createMediaTrack, isTrackMuted } from './utils';
|
|
6
|
+
import { PROCESSOR_LABELS } from './constants';
|
|
6
7
|
const FEATURE_KEYS = ['mixWithAdditionalMedia'];
|
|
7
8
|
const getAudioConstraints = extractConstraintsWithKeys(FEATURE_KEYS);
|
|
8
9
|
export const updateFeatureProps = (constraints, props) => {
|
|
@@ -19,35 +20,31 @@ export const updateFeatureProps = (constraints, props) => {
|
|
|
19
20
|
/**
|
|
20
21
|
* Create a Audio Mixing Processor and will own the stream passed-in
|
|
21
22
|
*/
|
|
22
|
-
export const createAudioMixingProcess = (
|
|
23
|
+
export const createAudioMixingProcess = (getCurrentMedia, label = PROCESSOR_LABELS.AudioMixingProcessor) => {
|
|
23
24
|
const props = {};
|
|
24
|
-
return async (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
const applyConstraints = applyExtendedConstraints(media, async (constraints) => {
|
|
31
|
-
if (isEmpty(constraints.audio)) {
|
|
25
|
+
return async (prevMediaTrack) => {
|
|
26
|
+
updateFeatureProps(prevMediaTrack.getConstraints(), props);
|
|
27
|
+
const displayStream = getCurrentMedia();
|
|
28
|
+
const displayTrack = displayStream?.getAudioTracks?.().at(0);
|
|
29
|
+
const applyConstraints = async (constraints) => {
|
|
30
|
+
if (isEmpty(constraints)) {
|
|
32
31
|
return;
|
|
33
32
|
}
|
|
34
|
-
const features = updateFeatureProps(constraints
|
|
35
|
-
logger.debug({
|
|
33
|
+
const features = updateFeatureProps(constraints, props);
|
|
34
|
+
logger.debug({ label, constraints: constraints, features }, 'apply audio mixing constraints');
|
|
36
35
|
if (isEmpty(features) ||
|
|
37
36
|
(props.audioGraph &&
|
|
38
37
|
['closed', 'closing'].includes(props.audioGraph.state))) {
|
|
39
38
|
return;
|
|
40
39
|
}
|
|
41
40
|
if (features.mixWithAdditionalMedia) {
|
|
42
|
-
const newStream =
|
|
43
|
-
const [newTrack] =
|
|
41
|
+
const newStream = getCurrentMedia();
|
|
42
|
+
const [newTrack] = getCurrentMedia()?.getAudioTracks() ?? [];
|
|
44
43
|
if (!newTrack) {
|
|
45
44
|
return;
|
|
46
45
|
}
|
|
47
46
|
if (!props.audioGraph || !props.merger) {
|
|
48
|
-
|
|
49
|
-
// return replace(media.stream, newTrack);
|
|
50
|
-
logger.debug({ scope }, 'Should update the media stream directly');
|
|
47
|
+
logger.debug({ label }, 'Should update the media stream directly');
|
|
51
48
|
return;
|
|
52
49
|
}
|
|
53
50
|
if (props.displaySource && props.merger) {
|
|
@@ -78,96 +75,112 @@ export const createAudioMixingProcess = (getCurrrentMedia, scope = 'mixer') => {
|
|
|
78
75
|
}
|
|
79
76
|
}
|
|
80
77
|
return Promise.resolve();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
};
|
|
79
|
+
// No need to mix and use as is
|
|
80
|
+
if (!prevMediaTrack.track) {
|
|
81
|
+
if (!displayTrack) {
|
|
82
|
+
// Do nothing
|
|
83
|
+
return prevMediaTrack;
|
|
84
|
+
}
|
|
85
|
+
return createMediaTrack({
|
|
86
|
+
label,
|
|
87
|
+
kind: 'audioinput',
|
|
88
|
+
previousMediaTrack: prevMediaTrack,
|
|
89
|
+
input: prevMediaTrack.input,
|
|
90
|
+
expectedInput: prevMediaTrack.expectedInput,
|
|
91
|
+
track: displayTrack,
|
|
92
|
+
constraints: prevMediaTrack.getConstraints(),
|
|
93
|
+
overrideMute: true,
|
|
94
|
+
mute: () => {
|
|
89
95
|
/* Do Nothing */
|
|
90
96
|
},
|
|
91
97
|
applyConstraints,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
const mainSource = createStreamSourceGraphNode(media.stream);
|
|
96
|
-
props.displaySource =
|
|
97
|
-
displayStream && createStreamSourceGraphNode(displayStream);
|
|
98
|
-
props.merger = createChannelMergerGraphNode();
|
|
99
|
-
const destination = createStreamDestinationGraphNode({
|
|
100
|
-
channelCount: 1,
|
|
101
|
-
channelCountMode: 'explicit',
|
|
102
|
-
});
|
|
103
|
-
const initialAudioNodeConnection = [
|
|
104
|
-
[mainSource, props.merger],
|
|
105
|
-
[props.merger, destination],
|
|
106
|
-
];
|
|
107
|
-
logger.debug({ initialAudioNodeConnection, scope }, 'Initial AudioNodeConnection');
|
|
108
|
-
const audioGraph = createAudioGraphProxy(createAudioGraph(initialAudioNodeConnection), {
|
|
109
|
-
connect: (target, args) => {
|
|
110
|
-
logger.debug({ scope, target, args }, 'connect nodes');
|
|
111
|
-
},
|
|
112
|
-
disconnect: (target, args) => {
|
|
113
|
-
logger.debug({ scope, target, args }, 'disconnect nodes');
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
props.audioGraph = audioGraph;
|
|
117
|
-
const unsubscribes = media.rawStream
|
|
118
|
-
?.getAudioTracks()
|
|
119
|
-
.map(resumeAudioOnUnmute(audioGraph.context));
|
|
120
|
-
if (props.displaySource) {
|
|
121
|
-
props.audioGraph.connect([props.displaySource, props.merger]);
|
|
122
|
-
}
|
|
123
|
-
const tracks = [
|
|
124
|
-
...(destination?.node?.stream.getAudioTracks() ?? []),
|
|
125
|
-
...(media.stream?.getVideoTracks() ?? []),
|
|
126
|
-
];
|
|
127
|
-
const stream = new MediaStream(tracks);
|
|
128
|
-
const release = async () => {
|
|
129
|
-
logger.debug({ scope }, 'Release Media');
|
|
130
|
-
unsubscribes?.forEach(unsubscribe => unsubscribe());
|
|
131
|
-
stopMediaStream(stream);
|
|
132
|
-
// Release Props
|
|
133
|
-
await props.audioGraph?.release();
|
|
134
|
-
await media.release();
|
|
135
|
-
props.audioGraph = undefined;
|
|
136
|
-
props.merger = undefined;
|
|
137
|
-
props.displaySource = undefined;
|
|
138
|
-
};
|
|
139
|
-
const muteAudio = (mute) => {
|
|
140
|
-
media.muteAudio(mute);
|
|
141
|
-
// TODO: dbl check if this is safe
|
|
142
|
-
muteStreamTrack(mainSource.audioNode?.mediaStream)(mute, 'audio');
|
|
143
|
-
};
|
|
144
|
-
const prevGetSettings = media.getSettings;
|
|
145
|
-
return wrapToJSON(shallowCopy(media, {
|
|
146
|
-
stream,
|
|
147
|
-
applyConstraints,
|
|
148
|
-
muteAudio,
|
|
149
|
-
release,
|
|
150
98
|
getSettings: () => {
|
|
151
|
-
const { audio, video } = prevGetSettings();
|
|
152
99
|
const mixWithAdditionalMedia = !!props.mixWithAdditionalMedia;
|
|
153
|
-
const
|
|
100
|
+
const contentHint = displayTrack?.contentHint ?? '';
|
|
101
|
+
return {
|
|
154
102
|
mixWithAdditionalMedia,
|
|
103
|
+
contentHint,
|
|
155
104
|
};
|
|
156
|
-
const settings = {
|
|
157
|
-
audio: audio.map(settings => ({
|
|
158
|
-
...settings,
|
|
159
|
-
...audioSettings,
|
|
160
|
-
})),
|
|
161
|
-
video,
|
|
162
|
-
};
|
|
163
|
-
logger.debug({ scope, settings: audioSettings }, 'get audio mixing processor settings');
|
|
164
|
-
return settings;
|
|
165
105
|
},
|
|
166
|
-
})
|
|
106
|
+
});
|
|
167
107
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
108
|
+
assert(prevMediaTrack.track, 'Main track should be available');
|
|
109
|
+
const mainSource = createStreamSourceGraphNode(new MediaStream([prevMediaTrack.track]));
|
|
110
|
+
props.displaySource =
|
|
111
|
+
displayStream && createStreamSourceGraphNode(displayStream);
|
|
112
|
+
props.merger = createChannelMergerGraphNode();
|
|
113
|
+
const destination = createStreamDestinationGraphNode({
|
|
114
|
+
channelCount: 1,
|
|
115
|
+
channelCountMode: 'explicit',
|
|
116
|
+
});
|
|
117
|
+
const initialAudioNodeConnection = [
|
|
118
|
+
[mainSource, props.merger],
|
|
119
|
+
[props.merger, destination],
|
|
120
|
+
];
|
|
121
|
+
logger.debug({ initialAudioNodeConnection, label }, 'Initial AudioNodeConnection');
|
|
122
|
+
const audioGraph = createAudioGraphProxy(createAudioGraph(initialAudioNodeConnection), {
|
|
123
|
+
connect: (target, args) => {
|
|
124
|
+
logger.debug({ label, target, args }, 'connect nodes');
|
|
125
|
+
},
|
|
126
|
+
disconnect: (target, args) => {
|
|
127
|
+
logger.debug({ label, target, args }, 'disconnect nodes');
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
props.audioGraph = audioGraph;
|
|
131
|
+
const unsubscribe = resumeAudioOnUnmute(audioGraph.context)(prevMediaTrack.track);
|
|
132
|
+
if (props.displaySource) {
|
|
133
|
+
props.audioGraph.connect([props.displaySource, props.merger]);
|
|
171
134
|
}
|
|
135
|
+
const track = destination?.node?.stream.getAudioTracks().at(0);
|
|
136
|
+
assert(track, 'No audio track mixed');
|
|
137
|
+
// Inherit contentHint from previous track
|
|
138
|
+
track.contentHint = prevMediaTrack.track.contentHint;
|
|
139
|
+
const release = async () => {
|
|
140
|
+
logger.debug({ label }, 'Release Media');
|
|
141
|
+
unsubscribe();
|
|
142
|
+
// Release Props
|
|
143
|
+
await props.audioGraph?.release();
|
|
144
|
+
props.audioGraph = undefined;
|
|
145
|
+
props.merger = undefined;
|
|
146
|
+
props.displaySource = undefined;
|
|
147
|
+
};
|
|
148
|
+
return Promise.resolve(createMediaTrack({
|
|
149
|
+
label,
|
|
150
|
+
kind: 'audioinput',
|
|
151
|
+
previousMediaTrack: prevMediaTrack,
|
|
152
|
+
input: prevMediaTrack.input,
|
|
153
|
+
expectedInput: prevMediaTrack.expectedInput,
|
|
154
|
+
track,
|
|
155
|
+
overrideMute: true,
|
|
156
|
+
mute: toMute => {
|
|
157
|
+
prevMediaTrack.mute(toMute);
|
|
158
|
+
mainSource.node?.mediaStream
|
|
159
|
+
.getAudioTracks()
|
|
160
|
+
.forEach(track => {
|
|
161
|
+
track.enabled = !toMute;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
get muted() {
|
|
165
|
+
return (
|
|
166
|
+
// If the source track muted, the following processed track will
|
|
167
|
+
// be muted as well since there is no data to process
|
|
168
|
+
!!prevMediaTrack.muted ||
|
|
169
|
+
mainSource.node?.mediaStream
|
|
170
|
+
.getAudioTracks()
|
|
171
|
+
.some(isTrackMuted));
|
|
172
|
+
},
|
|
173
|
+
constraints: prevMediaTrack.getConstraints(),
|
|
174
|
+
applyConstraints,
|
|
175
|
+
release,
|
|
176
|
+
getSettings: () => {
|
|
177
|
+
const mixWithAdditionalMedia = !!props.mixWithAdditionalMedia;
|
|
178
|
+
const contentHint = track.contentHint ?? '';
|
|
179
|
+
return {
|
|
180
|
+
mixWithAdditionalMedia,
|
|
181
|
+
contentHint,
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
}));
|
|
172
185
|
};
|
|
173
186
|
};
|
package/dist/audioProcessor.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AudioGraphOptions, AudioNodeInit, ThrottleOptions, DenoiseWorkletNodeInit, AnalyzerNodeInit, AudioGraph } from '@pexip/media-processor';
|
|
2
2
|
import type { MediaDeviceRequest } from '@pexip/media-control';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TrackProcessor, DenoiseParams, AudioContentHint } from './types';
|
|
4
4
|
type AudioNodeInits = AudioNodeInit[];
|
|
5
5
|
/**
|
|
6
6
|
* A function to be called to create the AudioNodes needed for the graph
|
|
@@ -8,7 +8,7 @@ type AudioNodeInits = AudioNodeInit[];
|
|
|
8
8
|
*
|
|
9
9
|
* @param media - Media to be used for the AudioGraph creation
|
|
10
10
|
*/
|
|
11
|
-
type CreateNodes = (
|
|
11
|
+
type CreateNodes = (track: MediaStreamTrack) => AudioNodeInits;
|
|
12
12
|
interface AudioProcessOptions {
|
|
13
13
|
/**
|
|
14
14
|
* An option is being passed to AnalyserNode creation when used
|
|
@@ -59,7 +59,7 @@ interface AudioProcessOptions {
|
|
|
59
59
|
* silent in FFTed time domain
|
|
60
60
|
*/
|
|
61
61
|
silentThreshold?: number;
|
|
62
|
-
|
|
62
|
+
label?: string;
|
|
63
63
|
}
|
|
64
64
|
interface AudioStreamProcessorProps {
|
|
65
65
|
audioGraphOptions?: AudioGraphOptions;
|
|
@@ -82,5 +82,5 @@ export declare const updateFeatureProps: (constraints: MediaDeviceRequest['audio
|
|
|
82
82
|
/**
|
|
83
83
|
* Create a Audio Stream Processor and will own the stream passed-in
|
|
84
84
|
*/
|
|
85
|
-
export declare const createAudioStreamProcess: ({ analyzerUpdateFrequency, audioGraphOptions, audioSignalDetectionDuration, clock, createNodes, denoiseParams, fftSize, onAudioSignalDetected, onVoiceActivityDetected, shouldEnable, silentThreshold, throttleMs,
|
|
85
|
+
export declare const createAudioStreamProcess: ({ analyzerUpdateFrequency, audioGraphOptions, audioSignalDetectionDuration, clock, createNodes, denoiseParams, fftSize, onAudioSignalDetected, onVoiceActivityDetected, shouldEnable, silentThreshold, throttleMs, label, }: AudioProcessOptions & ThrottleOptions) => TrackProcessor;
|
|
86
86
|
export {};
|