@livedigital/client 2.42.0 → 2.44.0-audio-observer.1
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/dist/constants/events.d.ts +6 -0
- package/dist/engine/DefaultEngineDependenciesFactory.d.ts +2 -0
- package/dist/engine/handlers/ChannelAudioObserverEventHandler.d.ts +17 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/media/tracks/PeerTrack.d.ts +2 -0
- package/dist/engine/network/index.d.ts +5 -6
- package/dist/errors/InvalidPayloadError.d.ts +5 -0
- package/dist/errors/NeedJoinFirstError.d.ts +5 -0
- package/dist/index.es.js +11 -11
- package/dist/index.js +11 -11
- package/dist/types/channelAudioObserver.d.ts +28 -0
- package/dist/types/common.d.ts +14 -1
- package/dist/types/engine.d.ts +6 -0
- package/package.json +1 -1
- package/src/constants/events.ts +7 -0
- package/src/engine/DefaultEngineDependenciesFactory.ts +5 -0
- package/src/engine/handlers/ChannelAudioObserverEventHandler.ts +129 -0
- package/src/engine/handlers/ChannelEventHandler.ts +2 -1
- package/src/engine/index.ts +62 -0
- package/src/engine/media/tracks/PeerTrack.ts +16 -1
- package/src/engine/network/index.ts +69 -37
- package/src/errors/InvalidPayloadError.ts +9 -0
- package/src/errors/NeedJoinFirstError.ts +9 -0
- package/src/types/channelAudioObserver.ts +34 -0
- package/src/types/common.ts +17 -1
- package/src/types/engine.ts +7 -0
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
TransportOptions,
|
|
6
6
|
} from 'mediasoup-client/lib/Transport';
|
|
7
7
|
import { Device } from 'mediasoup-client';
|
|
8
|
-
import {
|
|
8
|
+
import { DataConsumer, DataConsumerOptions } from 'mediasoup-client/lib/DataConsumer';
|
|
9
9
|
import SocketIO from './Socket';
|
|
10
10
|
import {
|
|
11
11
|
CreateConsumerPayload,
|
|
12
|
+
CreateConsumerResponse,
|
|
12
13
|
LogLevel,
|
|
13
14
|
LogMessageHandler,
|
|
14
15
|
ProduceParams,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
TransportsStateInfo,
|
|
18
19
|
TransportStateInfo,
|
|
19
20
|
UpdatePeerAppDataPayload,
|
|
21
|
+
CreateDataConsumerPayload,
|
|
20
22
|
} from '../../types/common';
|
|
21
23
|
import LoadBalancerApiClient from './LoadBalancerClient';
|
|
22
24
|
import { CHANNEL_EVENTS, MEDIASOUP_EVENTS, MEDIASOUP_TRANSPORT_EVENTS } from '../../constants/events';
|
|
@@ -108,25 +110,24 @@ class Network {
|
|
|
108
110
|
localDirection: 'send',
|
|
109
111
|
}) as TransportOptions;
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
const transport = mediasoupDevice.createSendTransport(sendTransportOptions);
|
|
112
114
|
this.logger.debug('createSendTransport()', { transport: this.sendTransport });
|
|
113
115
|
|
|
114
|
-
|
|
115
|
-
this.sendTransport.on(MEDIASOUP_TRANSPORT_EVENTS.connect, async (
|
|
116
|
+
transport.on(MEDIASOUP_TRANSPORT_EVENTS.connect, async (
|
|
116
117
|
{ dtlsParameters }: { dtlsParameters: DtlsParameters },
|
|
117
118
|
callback: () => void,
|
|
118
119
|
errback: (error: Error) => void,
|
|
119
120
|
) => {
|
|
120
121
|
try {
|
|
121
122
|
await this.socket.request(MEDIASOUP_EVENTS.transportConnect, {
|
|
122
|
-
transportId:
|
|
123
|
+
transportId: transport.id,
|
|
123
124
|
dtlsParameters,
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
callback();
|
|
127
128
|
|
|
128
129
|
this.logger.debug('sendTransport.connect()', {
|
|
129
|
-
transportId:
|
|
130
|
+
transportId: transport.id,
|
|
130
131
|
dtlsRole: dtlsParameters.role,
|
|
131
132
|
});
|
|
132
133
|
} catch (error) {
|
|
@@ -135,14 +136,14 @@ class Network {
|
|
|
135
136
|
}
|
|
136
137
|
});
|
|
137
138
|
|
|
138
|
-
|
|
139
|
+
transport.on(MEDIASOUP_TRANSPORT_EVENTS.produce, async (
|
|
139
140
|
parameters: ProduceParams,
|
|
140
141
|
callback: (a: unknown) => void,
|
|
141
142
|
errback: (error: Error) => void,
|
|
142
143
|
) => {
|
|
143
144
|
try {
|
|
144
145
|
const { id } = await this.socket.request(MEDIASOUP_EVENTS.transportProduce, {
|
|
145
|
-
transportId:
|
|
146
|
+
transportId: transport.id,
|
|
146
147
|
kind: parameters.kind,
|
|
147
148
|
rtpParameters: parameters.rtpParameters,
|
|
148
149
|
appData: parameters.appData,
|
|
@@ -152,68 +153,67 @@ class Network {
|
|
|
152
153
|
|
|
153
154
|
this.logger.debug('sendTransport.produce()', {
|
|
154
155
|
producerId: id,
|
|
155
|
-
transportId:
|
|
156
|
+
transportId: transport.id,
|
|
156
157
|
kind: parameters.kind,
|
|
157
158
|
});
|
|
158
159
|
} catch (error) {
|
|
159
160
|
this.logger.error('sendTransport.produce()', {
|
|
160
161
|
error,
|
|
161
|
-
transportId:
|
|
162
|
+
transportId: transport.id,
|
|
162
163
|
kind: parameters.kind,
|
|
163
164
|
});
|
|
164
165
|
errback(error);
|
|
165
166
|
}
|
|
166
167
|
});
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
transport.on(MEDIASOUP_EVENTS.transportStateChange, (state: RTCPeerConnectionState) => {
|
|
169
170
|
this.logger.debug('sendTransport.state', { state });
|
|
170
171
|
if (state === 'failed') {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
}
|
|
172
|
+
this.restartIce(transport);
|
|
173
|
+
this.logger.warn('sendTransport.restartIce()', {
|
|
174
|
+
state,
|
|
175
|
+
sendTransportId: transport.id,
|
|
176
|
+
});
|
|
178
177
|
}
|
|
179
178
|
|
|
180
179
|
setTimeout(() => {
|
|
181
|
-
if (
|
|
180
|
+
if (transport.connectionState === 'disconnected' && this.socket.connection?.connected) {
|
|
182
181
|
this.logger.warn('sendTransport is closed before websocket is closed', {
|
|
183
|
-
sendTransportId:
|
|
182
|
+
sendTransportId: transport.id,
|
|
184
183
|
});
|
|
185
184
|
}
|
|
186
185
|
}, 3000);
|
|
187
186
|
});
|
|
187
|
+
|
|
188
|
+
this.sendTransport = transport;
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
async createRecvTransport(mediasoupDevice: Device): Promise<void> {
|
|
191
192
|
if (this.receiveTransport) {
|
|
192
|
-
this.logger.warn('
|
|
193
|
+
this.logger.warn('createReceiveTransport()', { message: 'Receive transport already exists' });
|
|
193
194
|
return;
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
const recvTransportOptions = await this.socket.request(MEDIASOUP_EVENTS.transportCreate, {
|
|
197
198
|
localDirection: 'receive',
|
|
198
199
|
}) as TransportOptions;
|
|
199
|
-
|
|
200
|
-
this.logger.debug('
|
|
200
|
+
const transport = mediasoupDevice.createRecvTransport(recvTransportOptions);
|
|
201
|
+
this.logger.debug('createReceiveTransport()', { transport: this.receiveTransport });
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
this.receiveTransport.on(MEDIASOUP_TRANSPORT_EVENTS.connect, async (
|
|
203
|
+
transport.on(MEDIASOUP_TRANSPORT_EVENTS.connect, async (
|
|
204
204
|
{ dtlsParameters }: { dtlsParameters: DtlsParameters },
|
|
205
205
|
callback: () => void,
|
|
206
206
|
errback: (error: Error) => void,
|
|
207
207
|
) => {
|
|
208
208
|
try {
|
|
209
209
|
await this.socket.request(MEDIASOUP_EVENTS.transportConnect, {
|
|
210
|
-
transportId:
|
|
210
|
+
transportId: transport.id,
|
|
211
211
|
dtlsParameters,
|
|
212
212
|
});
|
|
213
213
|
callback();
|
|
214
214
|
|
|
215
215
|
this.logger.debug('receiveTransport.connect()', {
|
|
216
|
-
transportId:
|
|
216
|
+
transportId: transport.id,
|
|
217
217
|
dtlsRole: dtlsParameters.role,
|
|
218
218
|
});
|
|
219
219
|
} catch (error) {
|
|
@@ -222,23 +222,23 @@ class Network {
|
|
|
222
222
|
}
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
transport.on(MEDIASOUP_EVENTS.transportStateChange, (state: RTCPeerConnectionState) => {
|
|
226
226
|
this.logger.debug('receiveTransport.state', { state });
|
|
227
227
|
if (state === 'failed') {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.logger.debug('receiveTransport.restartIce()', { recvTransportId: this.receiveTransport.id });
|
|
231
|
-
}
|
|
228
|
+
this.restartIce(transport);
|
|
229
|
+
this.logger.debug('receiveTransport.restartIce()', { recvTransportId: transport.id });
|
|
232
230
|
}
|
|
233
231
|
|
|
234
232
|
setTimeout(() => {
|
|
235
|
-
if (
|
|
233
|
+
if (transport.connectionState === 'disconnected' && this.socket.connection?.connected) {
|
|
236
234
|
this.logger.warn('receiveTransport is closed before websocket is closed', {
|
|
237
|
-
recvTransportId:
|
|
235
|
+
recvTransportId: transport.id,
|
|
238
236
|
});
|
|
239
237
|
}
|
|
240
238
|
}, 3000);
|
|
241
239
|
});
|
|
240
|
+
|
|
241
|
+
this.receiveTransport = transport;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
async getIceParameters(transport: Transport): Promise<IceParameters> {
|
|
@@ -271,11 +271,11 @@ class Network {
|
|
|
271
271
|
appId,
|
|
272
272
|
channelId,
|
|
273
273
|
producerPeerId,
|
|
274
|
-
}: CreateConsumerPayload): Promise<
|
|
274
|
+
}: CreateConsumerPayload): Promise<CreateConsumerResponse> {
|
|
275
275
|
const transport = this.receiveTransport;
|
|
276
276
|
if (!transport) {
|
|
277
|
-
this.logger.error('createConsumer()', { reason: 'missing
|
|
278
|
-
throw new Error('
|
|
277
|
+
this.logger.error('createConsumer()', { reason: 'missing receive transport' });
|
|
278
|
+
throw new Error('Failed to create consumer');
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
this.logger.debug('createConsumer()', { producerId });
|
|
@@ -301,6 +301,34 @@ class Network {
|
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
async createDataConsumer({
|
|
305
|
+
producerId,
|
|
306
|
+
appId,
|
|
307
|
+
channelId,
|
|
308
|
+
}: CreateDataConsumerPayload): Promise<DataConsumer> {
|
|
309
|
+
const transport = this.receiveTransport;
|
|
310
|
+
if (!transport) {
|
|
311
|
+
this.logger.error('createDataConsumer()', { reason: 'missing receive transport' });
|
|
312
|
+
throw new Error('Failed to create consumer');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this.logger.debug('createDataConsumer()', { producerId });
|
|
316
|
+
const options = await this.socket.request(MEDIASOUP_EVENTS.createDataConsumer, {
|
|
317
|
+
dataProducerId: producerId,
|
|
318
|
+
transportId: transport.id,
|
|
319
|
+
appData: {
|
|
320
|
+
peerId: this.socket.id,
|
|
321
|
+
producerId,
|
|
322
|
+
appId,
|
|
323
|
+
channelId,
|
|
324
|
+
},
|
|
325
|
+
}) as DataConsumerOptions;
|
|
326
|
+
|
|
327
|
+
const consumer = await transport.consumeData(options);
|
|
328
|
+
this.logger.debug('Data consumer created', { consumer });
|
|
329
|
+
return consumer;
|
|
330
|
+
}
|
|
331
|
+
|
|
304
332
|
async closeRemoteConsumer(consumerId: string): Promise<SocketResponse> {
|
|
305
333
|
return this.socket.request(MEDIASOUP_EVENTS.closeConsumer, { peerId: this.socket.id, consumerId });
|
|
306
334
|
}
|
|
@@ -317,6 +345,10 @@ class Network {
|
|
|
317
345
|
return this.socket.request(CHANNEL_EVENTS.updatePeerAppData, { peerId, appData });
|
|
318
346
|
}
|
|
319
347
|
|
|
348
|
+
async getAudioObserverProducer(): Promise<SocketResponse> {
|
|
349
|
+
return this.socket.request(CHANNEL_EVENTS.getAudioObserverProducer);
|
|
350
|
+
}
|
|
351
|
+
|
|
320
352
|
async getTransportsStateInfo(): Promise<TransportsStateInfo> {
|
|
321
353
|
const [receive, send] = await Promise.all([
|
|
322
354
|
this.getParsedTransportStats(this.receiveTransport),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { TrackLabel } from './common';
|
|
2
|
+
|
|
3
|
+
export enum ChannelAudioObserverEvents {
|
|
4
|
+
DominantSpeaker = 'dominant-speaker',
|
|
5
|
+
PeersVolumes = 'peers-volumes',
|
|
6
|
+
Silence = 'silence',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type DominantSpeakerEvent = {
|
|
10
|
+
event: ChannelAudioObserverEvents.DominantSpeaker;
|
|
11
|
+
data: {
|
|
12
|
+
peerId: string;
|
|
13
|
+
producerId: string;
|
|
14
|
+
trackLabel: TrackLabel.Microphone;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type SilenceEvent = {
|
|
19
|
+
event: ChannelAudioObserverEvents.Silence;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type PeerVolume = {
|
|
23
|
+
peerId: string;
|
|
24
|
+
producerId: string;
|
|
25
|
+
value: number;
|
|
26
|
+
trackLabel: TrackLabel.Microphone;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type PeersVolumesEvent = {
|
|
30
|
+
event: ChannelAudioObserverEvents.PeersVolumes;
|
|
31
|
+
volumes: PeerVolume[]
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type AudioObserverEvents = DominantSpeakerEvent | SilenceEvent | PeersVolumesEvent;
|
package/src/types/common.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
RtpEncodingParameters,
|
|
4
4
|
RtpParameters,
|
|
5
5
|
} from 'mediasoup-client/lib/types';
|
|
6
|
-
import { ConsumerOptions } from 'mediasoup-client/lib/Consumer';
|
|
6
|
+
import { Consumer, ConsumerOptions } from 'mediasoup-client/lib/Consumer';
|
|
7
7
|
import { RtpCapabilities } from 'mediasoup-client/src/RtpParameters';
|
|
8
8
|
import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
9
9
|
import { ConnectionState } from 'mediasoup-client/src/Transport';
|
|
@@ -287,6 +287,17 @@ export type CreateConsumerPayload = {
|
|
|
287
287
|
producerPeerId: string,
|
|
288
288
|
};
|
|
289
289
|
|
|
290
|
+
export type CreateDataConsumerPayload = {
|
|
291
|
+
producerId: string,
|
|
292
|
+
appId?: string,
|
|
293
|
+
channelId?: string,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
export type CreateConsumerResponse = {
|
|
297
|
+
consumer: Consumer,
|
|
298
|
+
isProducerPaused: boolean,
|
|
299
|
+
};
|
|
300
|
+
|
|
290
301
|
export enum DeviceErrors {
|
|
291
302
|
NotFoundError = 'NotFoundError',
|
|
292
303
|
NoDevices = 'NoDevices',
|
|
@@ -376,3 +387,8 @@ export interface TrackWithEncodings {
|
|
|
376
387
|
getCodecOptions(): ProducerCodecOptions;
|
|
377
388
|
getEncodings(): RtpEncodingParameters[];
|
|
378
389
|
}
|
|
390
|
+
|
|
391
|
+
export type ActivityConfirmationRequiredPayload = {
|
|
392
|
+
channelId: string;
|
|
393
|
+
time: number;
|
|
394
|
+
};
|
package/src/types/engine.ts
CHANGED
|
@@ -8,6 +8,7 @@ import ChannelEventHandler from '../engine/handlers/ChannelEventHandler';
|
|
|
8
8
|
import MediaSoupEventHandler from '../engine/handlers/MediaSoupEventHandler';
|
|
9
9
|
import { LoadBalancerApiClientParams } from '../engine/network/LoadBalancerClient';
|
|
10
10
|
import { Logger, LogLevel, LogMessageHandler } from './common';
|
|
11
|
+
import ChannelAudioObserverEventHandler from '../engine/handlers/ChannelAudioObserverEventHandler';
|
|
11
12
|
|
|
12
13
|
export type IssuesHandler = (issues: IssueDetectorResult) => void;
|
|
13
14
|
|
|
@@ -54,11 +55,17 @@ export interface CreateChannelEventHandlerParams {
|
|
|
54
55
|
onLogMessage?: LogMessageHandler;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
export interface CreateAudioObserverEventHandlerParams {
|
|
59
|
+
engine: Engine;
|
|
60
|
+
onLogMessage?: LogMessageHandler;
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
export interface EngineDependenciesFactory {
|
|
58
64
|
createSystem: (params: CreateSystemParams) => System;
|
|
59
65
|
createMedia: (params: CreateMediaParams) => Media;
|
|
60
66
|
createNetwork: (params: CreateNetworkParams) => Network;
|
|
61
67
|
createChannelEventHandler: (params: CreateChannelEventHandlerParams) => ChannelEventHandler;
|
|
62
68
|
createMediaSoupEventHandler: (params: CreateMediaSoupEventHandlerParams) => MediaSoupEventHandler;
|
|
69
|
+
createAudioObserverEventHandler: (params: CreateAudioObserverEventHandlerParams) => ChannelAudioObserverEventHandler;
|
|
63
70
|
createIssueDetector: (params: CreateIssueDetectorParams) => WebRTCIssueDetector;
|
|
64
71
|
}
|