@livedigital/client 2.17.0 → 2.18.0-webrtc-issue-events.2
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/common.d.ts +2 -0
- package/dist/constants/events.d.ts +8 -0
- package/dist/engine/Peer.d.ts +0 -1
- package/dist/engine/PeerConsumer.d.ts +2 -1
- package/dist/engine/index.d.ts +2 -6
- package/dist/engine/media/tracks/AudioTrack.d.ts +1 -0
- package/dist/engine/media/tracks/BaseTrack.d.ts +21 -3
- package/dist/engine/media/tracks/PeerTrack.d.ts +8 -5
- package/dist/engine/media/tracks/VideoTrack.d.ts +1 -0
- package/dist/engine/network/Socket.d.ts +1 -1
- package/dist/engine/network/index.d.ts +9 -1
- package/dist/engine/wid/types.d.ts +5 -0
- package/dist/index.d.ts +0 -4
- package/dist/index.es.js +1 -1
- package/dist/index.js +2 -2
- package/dist/types/common.d.ts +18 -0
- package/dist/types/engine.d.ts +3 -0
- package/package.json +1 -1
- package/src/constants/common.ts +3 -0
- package/src/constants/events.ts +9 -0
- package/src/engine/DefaultEngineDependenciesFactory.ts +1 -0
- package/src/engine/Peer.ts +24 -42
- package/src/engine/PeerConsumer.ts +33 -1
- package/src/engine/index.ts +46 -159
- package/src/engine/media/index.ts +18 -6
- package/src/engine/media/tracks/AudioTrack.ts +10 -0
- package/src/engine/media/tracks/BaseTrack.ts +287 -5
- package/src/engine/media/tracks/PeerTrack.ts +137 -24
- package/src/engine/media/tracks/VideoTrack.ts +12 -0
- package/src/engine/network/Socket.ts +4 -4
- package/src/engine/network/index.ts +51 -1
- package/src/engine/wid/WebRTCIssueDetector.ts +17 -7
- package/src/engine/wid/types.ts +6 -0
- package/src/index.ts +0 -16
- package/src/types/common.ts +21 -0
- package/src/types/engine.ts +3 -0
package/dist/types/common.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { MediaKind, RtpEncodingParameters, RtpParameters } from 'mediasoup-client/lib/types';
|
|
2
2
|
import { ConsumerOptions } from 'mediasoup-client/lib/Consumer';
|
|
3
|
+
import { RtpCapabilities } from 'mediasoup-client/src/RtpParameters';
|
|
4
|
+
import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
3
5
|
import AudioTrack from '../engine/media/tracks/AudioTrack';
|
|
4
6
|
import VideoTrack from '../engine/media/tracks/VideoTrack';
|
|
5
7
|
export declare type SocketResponse = {
|
|
@@ -13,6 +15,12 @@ export declare type ProduceParams = {
|
|
|
13
15
|
rtpParameters: RtpParameters;
|
|
14
16
|
appData: Record<string, unknown>;
|
|
15
17
|
};
|
|
18
|
+
export declare type TrackProduceParams = {
|
|
19
|
+
encodings?: RtpEncodingParameters[];
|
|
20
|
+
codecOptions?: ProducerCodecOptions;
|
|
21
|
+
preferredCodec?: string;
|
|
22
|
+
transformParams?: TrackTransformParams;
|
|
23
|
+
};
|
|
16
24
|
export declare type TrackTransformParams = {
|
|
17
25
|
width?: number;
|
|
18
26
|
height?: number;
|
|
@@ -212,3 +220,13 @@ export declare type TransportConnectionTimeoutPayload = {
|
|
|
212
220
|
direction: 'receive' | 'send';
|
|
213
221
|
timeout: number;
|
|
214
222
|
};
|
|
223
|
+
export declare type ExtendedRTCInboundRtpStreamStats = RTCInboundRtpStreamStats & {
|
|
224
|
+
decoderImplementation?: string;
|
|
225
|
+
};
|
|
226
|
+
export declare type CreateConsumerPayload = {
|
|
227
|
+
producerId: string;
|
|
228
|
+
rtpCapabilities: RtpCapabilities;
|
|
229
|
+
appId?: string;
|
|
230
|
+
channelId?: string;
|
|
231
|
+
producerPeerId: string;
|
|
232
|
+
};
|
package/dist/types/engine.d.ts
CHANGED
|
@@ -12,9 +12,12 @@ import { LogLevel } from './common';
|
|
|
12
12
|
export declare type IssuesHandler = (issues: IssueDetectorResult) => void;
|
|
13
13
|
export interface CreateIssueDetectorParams {
|
|
14
14
|
onIssues?: IssuesHandler;
|
|
15
|
+
ignoreSSRCList?: number[];
|
|
15
16
|
}
|
|
16
17
|
export interface CreateMediaParams {
|
|
17
18
|
logLevel: LogLevel;
|
|
19
|
+
engine: Engine;
|
|
20
|
+
clientEventEmitter: EnhancedEventEmitter;
|
|
18
21
|
}
|
|
19
22
|
export interface CreateNetworkParams {
|
|
20
23
|
logLevel: LogLevel;
|
package/package.json
CHANGED
package/src/constants/common.ts
CHANGED
package/src/constants/events.ts
CHANGED
|
@@ -12,6 +12,14 @@ export const CLIENT_EVENTS = {
|
|
|
12
12
|
channelRejoinRequired: 'channel-rejoin-required',
|
|
13
13
|
devicesListUpdated: 'devices-list-updated',
|
|
14
14
|
transportConnectionTimeout: 'transport-connection-timeout',
|
|
15
|
+
trackPublishingFailed: 'track-publishing-failed',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const INTERNAL_CLIENT_EVENTS = {
|
|
19
|
+
trackProduced: 'track-produced',
|
|
20
|
+
trackUnproduced: 'track-unproduced',
|
|
21
|
+
trackPaused: 'track-paused',
|
|
22
|
+
trackResumed: 'track-resumed',
|
|
15
23
|
};
|
|
16
24
|
|
|
17
25
|
export const PEER_EVENTS = {
|
|
@@ -22,6 +30,7 @@ export const PEER_EVENTS = {
|
|
|
22
30
|
trackEnd: 'track-end',
|
|
23
31
|
trackPaused: 'track-paused',
|
|
24
32
|
trackResumed: 'track-resumed',
|
|
33
|
+
trackFailed: 'track-failed',
|
|
25
34
|
};
|
|
26
35
|
|
|
27
36
|
export enum SocketIOEvents {
|
|
@@ -50,6 +50,7 @@ class DefaultEngineDependenciesFactory implements EngineDependenciesFactory {
|
|
|
50
50
|
createIssueDetector(params: CreateIssueDetectorParams): WebRTCIssueDetector {
|
|
51
51
|
return new WebRTCIssueDetector({
|
|
52
52
|
onIssues: params.onIssues,
|
|
53
|
+
ignoreSSRCList: params.ignoreSSRCList,
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
56
|
}
|
package/src/engine/Peer.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
ChangePreferredLayersPayload,
|
|
9
9
|
Role,
|
|
10
10
|
ProducerSetMaxSpatialLayer,
|
|
11
|
-
RemoteConsumerOptions,
|
|
12
11
|
} from '../types/common';
|
|
13
12
|
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
14
13
|
import Engine from './index';
|
|
@@ -114,61 +113,44 @@ class Peer {
|
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
public async subscribe(producerId: string): Promise<void> {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const consumer = this.getAllConsumers().find((item) => item.producerId === producerId);
|
|
124
|
-
if (consumer) {
|
|
125
|
-
this.logger.warn('subscribe()', { message: 'Already subscribed to producer', peer: this, producerId });
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
await this.createConsumer(producer);
|
|
130
|
-
}
|
|
116
|
+
try {
|
|
117
|
+
const producer = this.producers.get(producerId);
|
|
118
|
+
if (!producer) {
|
|
119
|
+
this.logger.warn('subscribe()', { message: 'Peer does not have a producer', peer: this, producerId });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
131
122
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
123
|
+
const peerConsumer = this.getAllConsumers().find((item) => item.producerId === producerId);
|
|
124
|
+
if (peerConsumer) {
|
|
125
|
+
this.logger.debug('subscribe()', { message: 'Already subscribed to producer', peer: this, producerId });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
138
128
|
|
|
139
|
-
|
|
140
|
-
const remoteConsumer = await this.engine.network.socket.request(MEDIASOUP_EVENTS.createConsumer, {
|
|
129
|
+
const { isProducerPaused, consumer } = await this.engine.network.createConsumer({
|
|
141
130
|
producerId: producer.id,
|
|
142
|
-
transportId: transport.id,
|
|
143
131
|
rtpCapabilities: this.engine.media.mediasoupDevice.rtpCapabilities,
|
|
144
|
-
appData: {
|
|
145
|
-
peerId: this.engine.mySocketId,
|
|
146
|
-
appId: this.engine.appId,
|
|
147
|
-
channelId: this.engine.channelId,
|
|
148
|
-
},
|
|
149
132
|
producerPeerId: this.id,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (!remoteConsumer.producerPaused) {
|
|
154
|
-
await this.engine.network.socket.request(MEDIASOUP_EVENTS.resumeConsumer, { consumerId: consumer.id });
|
|
155
|
-
}
|
|
133
|
+
appId: this.engine.appId,
|
|
134
|
+
channelId: this.engine.channelId,
|
|
135
|
+
});
|
|
156
136
|
|
|
157
|
-
this.logger.debug(`Subscribed for ${producer.kind}`, { peer: this });
|
|
158
137
|
const track = new PeerTrack({
|
|
159
138
|
mediaStreamTrack: consumer.track,
|
|
160
139
|
label: producer.label,
|
|
161
|
-
consumer: new PeerConsumer({
|
|
162
|
-
consumer,
|
|
163
|
-
logLevel: this.engine.logLevel,
|
|
164
|
-
}),
|
|
140
|
+
consumer: new PeerConsumer({ consumer, logLevel: this.engine.logLevel }),
|
|
165
141
|
engine: this.engine,
|
|
166
|
-
isPaused:
|
|
142
|
+
isPaused: true,
|
|
167
143
|
peerEventEmitter: this.observer,
|
|
168
144
|
});
|
|
145
|
+
|
|
146
|
+
if (!isProducerPaused) {
|
|
147
|
+
await track.resume();
|
|
148
|
+
}
|
|
149
|
+
|
|
169
150
|
this.tracks.set(track.label, track);
|
|
151
|
+
this.logger.debug(`Subscribed for ${producer.kind}`, { peer: this });
|
|
170
152
|
} catch (error) {
|
|
171
|
-
this.logger.error('
|
|
153
|
+
this.logger.error('subscribe()', { producerId, error });
|
|
172
154
|
throw new Error('Error subscribe media');
|
|
173
155
|
}
|
|
174
156
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { parseScalabilityMode } from 'mediasoup-client';
|
|
2
2
|
import { MediaKind, RtpEncodingParameters } from 'mediasoup-client/lib/RtpParameters';
|
|
3
3
|
import { Consumer as MediasoupConsumer } from 'mediasoup-client/lib/types';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ConsumerData,
|
|
6
|
+
ExtendedRTCInboundRtpStreamStats,
|
|
7
|
+
LogLevel,
|
|
8
|
+
SpatialLayerParams,
|
|
9
|
+
} from '../types/common';
|
|
5
10
|
import Logger from './Logger';
|
|
6
11
|
|
|
7
12
|
export type PeerConsumerConstructorParams = {
|
|
@@ -161,6 +166,33 @@ class PeerConsumer {
|
|
|
161
166
|
setCurrentMaxSpatialLayer(spatialLayer: number): void {
|
|
162
167
|
this.currentMaxSpatialLayer = spatialLayer;
|
|
163
168
|
}
|
|
169
|
+
|
|
170
|
+
async getInboundRTPStreamStats(): Promise<ExtendedRTCInboundRtpStreamStats> {
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const getRTCStatsReport = async (attempt = 0) => {
|
|
173
|
+
try {
|
|
174
|
+
const rtcStatsReport = await this.consumer.getStats();
|
|
175
|
+
const inboundRTPStreamStats: ExtendedRTCInboundRtpStreamStats = Array.from(rtcStatsReport.values())
|
|
176
|
+
.find((stat) => stat.type === 'inbound-rtp');
|
|
177
|
+
if (!inboundRTPStreamStats && attempt < 5) {
|
|
178
|
+
setTimeout(() => getRTCStatsReport(attempt + 1), 150);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (attempt >= 5 && !inboundRTPStreamStats) {
|
|
183
|
+
reject(new Error('InboundRTPStreamStat not exist'));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
resolve(inboundRTPStreamStats);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
reject(new Error('Can not get RTCStatsReport'));
|
|
189
|
+
this.logger.warn('getInboundRTPStreamStats()', { error });
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
getRTCStatsReport();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
164
196
|
}
|
|
165
197
|
|
|
166
198
|
export default PeerConsumer;
|
package/src/engine/index.ts
CHANGED
|
@@ -21,11 +21,10 @@ import ChannelEventHandler from './handlers/ChannelEventHandler';
|
|
|
21
21
|
import MediaSoupEventHandler from './handlers/MediaSoupEventHandler';
|
|
22
22
|
import Logger from './Logger';
|
|
23
23
|
import {
|
|
24
|
-
CHANNEL_EVENTS, CLIENT_EVENTS,
|
|
24
|
+
CHANNEL_EVENTS, CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, SocketIOEvents,
|
|
25
25
|
} from '../constants/events';
|
|
26
26
|
import { GetNodeRequest } from '../types/network';
|
|
27
27
|
import VideoTrack from './media/tracks/VideoTrack';
|
|
28
|
-
import AudioTrack from './media/tracks/AudioTrack';
|
|
29
28
|
import PeerTrack from './media/tracks/PeerTrack';
|
|
30
29
|
import WebRTCIssueDetector from './wid/WebRTCIssueDetector';
|
|
31
30
|
import { retryAsync } from '../helpers/retry';
|
|
@@ -89,6 +88,8 @@ class Engine {
|
|
|
89
88
|
});
|
|
90
89
|
this.media = dependenciesFactory.createMedia({
|
|
91
90
|
logLevel: this.logLevel,
|
|
91
|
+
engine: this,
|
|
92
|
+
clientEventEmitter,
|
|
92
93
|
});
|
|
93
94
|
this.network = dependenciesFactory.createNetwork({
|
|
94
95
|
logLevel: this.logLevel,
|
|
@@ -106,9 +107,11 @@ class Engine {
|
|
|
106
107
|
|
|
107
108
|
this.webRtcIssueDetector = dependenciesFactory.createIssueDetector({
|
|
108
109
|
onIssues: params.onIssues,
|
|
110
|
+
ignoreSSRCList: [1234], // mediasoup-client probator
|
|
109
111
|
});
|
|
110
112
|
|
|
111
113
|
this.watchSocketState();
|
|
114
|
+
this.watchClientEvents();
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
private async initialize(): Promise<void> {
|
|
@@ -137,7 +140,7 @@ class Engine {
|
|
|
137
140
|
this.webRtcIssueDetector.resetWatchedPeerConnectionsList();
|
|
138
141
|
this.network.socket.disconnect();
|
|
139
142
|
this.peersRepository.clear();
|
|
140
|
-
await this.
|
|
143
|
+
await this.unpublishAllTracks();
|
|
141
144
|
await this.network.closeSendTransport();
|
|
142
145
|
await this.network.closeReceiveTransport();
|
|
143
146
|
|
|
@@ -148,14 +151,6 @@ class Engine {
|
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
|
|
151
|
-
private async pauseRemoteProducer(producerId: string): Promise<SocketResponse> {
|
|
152
|
-
return this.network.socket.request(MEDIASOUP_EVENTS.pauseProducer, { producerId });
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private async resumeRemoteProducer(producerId: string): Promise<SocketResponse> {
|
|
156
|
-
return this.network.socket.request(MEDIASOUP_EVENTS.resumeProducer, { producerId });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
154
|
private watchSocketState(): void {
|
|
160
155
|
this.network.socket.observer.on('state', (evt: { state: SocketIOEvents, code?: string }) => {
|
|
161
156
|
const { state, code } = evt;
|
|
@@ -221,44 +216,6 @@ class Engine {
|
|
|
221
216
|
this.peersRepository.delete(peerId);
|
|
222
217
|
}
|
|
223
218
|
|
|
224
|
-
public async pause(track: Track): Promise<void> {
|
|
225
|
-
const producer = track.getProducer();
|
|
226
|
-
if (!producer) {
|
|
227
|
-
this.logger.warn('pause()', { message: 'Track is not published', kind: track.kind });
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
await this.pauseRemoteProducer(producer.id);
|
|
233
|
-
producer.pause();
|
|
234
|
-
const peerTrack = this.myPeer?.tracks.get(track.getLabel());
|
|
235
|
-
peerTrack?.pause();
|
|
236
|
-
this.logger.debug('pause()', { kind: track.kind });
|
|
237
|
-
} catch (error) {
|
|
238
|
-
this.logger.error('pause()', { error, kind: track.kind });
|
|
239
|
-
throw new Error('Can`t pause track');
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
public async resume(track: Track): Promise<void> {
|
|
244
|
-
const producer = track.getProducer();
|
|
245
|
-
if (!producer) {
|
|
246
|
-
this.logger.warn('resume()', { message: 'Track is not published' });
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
try {
|
|
251
|
-
await this.resumeRemoteProducer(producer.id);
|
|
252
|
-
producer.resume();
|
|
253
|
-
this.logger.debug('resume()', { kind: track.kind });
|
|
254
|
-
const peerTrack = this.myPeer?.tracks.get(track.getLabel());
|
|
255
|
-
peerTrack?.resume();
|
|
256
|
-
} catch (error) {
|
|
257
|
-
this.logger.error('resume()', { error, kind: track.kind });
|
|
258
|
-
throw new Error('Can`t resume track');
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
219
|
async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track> {
|
|
263
220
|
const trackParams = Media.getCameraVideoTrackParams(options);
|
|
264
221
|
try {
|
|
@@ -313,7 +270,7 @@ class Engine {
|
|
|
313
270
|
|
|
314
271
|
tracks.forEach((track) => {
|
|
315
272
|
track.mediaStreamTrack.addEventListener('ended', () => {
|
|
316
|
-
|
|
273
|
+
track.unpublish();
|
|
317
274
|
});
|
|
318
275
|
|
|
319
276
|
if (track instanceof VideoTrack) {
|
|
@@ -339,115 +296,6 @@ class Engine {
|
|
|
339
296
|
}
|
|
340
297
|
}
|
|
341
298
|
|
|
342
|
-
public async publish(tracks: Track | Track[]): Promise<void> {
|
|
343
|
-
if (!this.cahPublish) {
|
|
344
|
-
this.logger.error('publish()', { message: 'Not enough access to publish' });
|
|
345
|
-
throw new Error('Not enough access to publish');
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const tracksToPublish = Array.isArray(tracks) ? tracks : [tracks];
|
|
349
|
-
const actions = tracksToPublish.map(async (track) => {
|
|
350
|
-
const encodings = 'getEncodings' in track
|
|
351
|
-
? track.getEncodings()
|
|
352
|
-
: undefined;
|
|
353
|
-
|
|
354
|
-
const codecOptions = 'getCodecOptions' in track
|
|
355
|
-
? track.getCodecOptions()
|
|
356
|
-
: undefined;
|
|
357
|
-
|
|
358
|
-
const maxSpatialLayer = 0;
|
|
359
|
-
const appData = {
|
|
360
|
-
peerId: this.mySocketId,
|
|
361
|
-
label: track.getLabel(),
|
|
362
|
-
trackTransformParams: {},
|
|
363
|
-
maxSpatialLayer: 0,
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
if (track instanceof VideoTrack) {
|
|
367
|
-
appData.maxSpatialLayer = maxSpatialLayer;
|
|
368
|
-
appData.trackTransformParams = track.transformParams;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const producer = await this.network.sendTransport?.produce({
|
|
372
|
-
track: track.mediaStreamTrack,
|
|
373
|
-
encodings,
|
|
374
|
-
codecOptions,
|
|
375
|
-
codec: this.media.getTrackCodec(track),
|
|
376
|
-
stopTracks: true,
|
|
377
|
-
disableTrackOnPause: true,
|
|
378
|
-
zeroRtpOnPause: true,
|
|
379
|
-
appData,
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
if (!producer) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
track.setProducer(producer);
|
|
387
|
-
|
|
388
|
-
if (track instanceof VideoTrack) {
|
|
389
|
-
await track.setMaxSpatialLayer(maxSpatialLayer);
|
|
390
|
-
await this.network.socket.request(MEDIASOUP_EVENTS.producerSetMaxSpatialLayer, {
|
|
391
|
-
producerId: track.getProducer()?.id,
|
|
392
|
-
maxSpatialLayer,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (track instanceof AudioTrack) {
|
|
397
|
-
await track.setPriority('high');
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
401
|
-
if (!myPeer) {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const peerTrack = new PeerTrack({
|
|
406
|
-
mediaStreamTrack: track.mediaStreamTrack,
|
|
407
|
-
label: track.getLabel(),
|
|
408
|
-
engine: this,
|
|
409
|
-
isPaused: producer.paused,
|
|
410
|
-
peerEventEmitter: myPeer.observer,
|
|
411
|
-
});
|
|
412
|
-
myPeer.tracks.set(peerTrack.label, peerTrack);
|
|
413
|
-
this.logger.debug('publish()', { track, encodings, codecOptions });
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
try {
|
|
417
|
-
await Promise.all(actions);
|
|
418
|
-
} catch (error) {
|
|
419
|
-
this.logger.error('publish()', { error });
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
public async unpublish(tracks?: Track | Track[]): Promise<void> {
|
|
424
|
-
let tracksToUnpublish: Track[];
|
|
425
|
-
if (!tracks) {
|
|
426
|
-
tracksToUnpublish = this.media.getAllTracks();
|
|
427
|
-
} else {
|
|
428
|
-
tracksToUnpublish = Array.isArray(tracks) ? tracks : [tracks];
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const actions = tracksToUnpublish.map(async (track) => {
|
|
432
|
-
const producer = track.getProducer();
|
|
433
|
-
if (producer && this.network.socket.connection?.connected) {
|
|
434
|
-
await this.network.closeRemoteProducer(producer.id);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const peerTrack = this.myPeer?.tracks.get(track.getLabel());
|
|
438
|
-
peerTrack?.close();
|
|
439
|
-
|
|
440
|
-
await this.media.deleteTrack(track);
|
|
441
|
-
this.logger.debug('unpublish()', { track });
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
try {
|
|
445
|
-
await Promise.all(actions);
|
|
446
|
-
} catch (error) {
|
|
447
|
-
this.logger.error('unpublish()', { error });
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
299
|
deleteTrack(tracks: Track): void {
|
|
452
300
|
return this.media.deleteTrack(tracks);
|
|
453
301
|
}
|
|
@@ -568,6 +416,45 @@ class Engine {
|
|
|
568
416
|
private get myPeer(): Peer | undefined {
|
|
569
417
|
return this.peers.find((peer) => peer.isMe);
|
|
570
418
|
}
|
|
419
|
+
|
|
420
|
+
private async unpublishAllTracks(): Promise<void> {
|
|
421
|
+
await Promise.all(this.media.getAllTracks().map((track) => track.unpublish()));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private watchClientEvents(): void {
|
|
425
|
+
this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackProduced, (track: Track) => {
|
|
426
|
+
const myPeer = this.peersRepository.get(<string> this.mySocketId);
|
|
427
|
+
if (!myPeer) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const peerTrack = new PeerTrack({
|
|
432
|
+
mediaStreamTrack: track.mediaStreamTrack,
|
|
433
|
+
label: track.getLabel(),
|
|
434
|
+
engine: this,
|
|
435
|
+
isPaused: track.isPaused,
|
|
436
|
+
peerEventEmitter: myPeer.observer,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
myPeer.tracks.set(peerTrack.label, peerTrack);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackUnproduced, (track: Track) => {
|
|
443
|
+
const peerTrack = this.myPeer?.tracks.get(track.getLabel());
|
|
444
|
+
peerTrack?.close();
|
|
445
|
+
this.media.deleteTrack(track);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackPaused, (track: Track) => {
|
|
449
|
+
const peerTrack = this.myPeer?.tracks.get(track.getLabel());
|
|
450
|
+
peerTrack?.pause();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
this.clientEventEmitter.on(INTERNAL_CLIENT_EVENTS.trackResumed, (track: Track) => {
|
|
454
|
+
const peerTrack = this.myPeer?.tracks.get(track.getLabel());
|
|
455
|
+
peerTrack?.resume();
|
|
456
|
+
});
|
|
457
|
+
}
|
|
571
458
|
}
|
|
572
459
|
|
|
573
460
|
export default Engine;
|
|
@@ -9,6 +9,8 @@ import Logger from '../Logger';
|
|
|
9
9
|
import { VIDEO_CONSTRAINS } from '../../constants/videoConstrains';
|
|
10
10
|
import { SCREEN_SHARING_SIMULCAST_ENCODINGS, WEBCAM_SIMULCAST_ENCODINGS } from '../../constants/simulcastEncodings';
|
|
11
11
|
import { CreateMediaParams } from '../../types/engine';
|
|
12
|
+
import Engine from '../index';
|
|
13
|
+
import EnhancedEventEmitter from '../../EnhancedEventEmitter';
|
|
12
14
|
|
|
13
15
|
class Media {
|
|
14
16
|
public isDeviceLoaded = false;
|
|
@@ -17,9 +19,13 @@ class Media {
|
|
|
17
19
|
|
|
18
20
|
private tracks: Map<string, Track> = new Map();
|
|
19
21
|
|
|
20
|
-
#logger: Logger;
|
|
22
|
+
readonly #logger: Logger;
|
|
21
23
|
|
|
22
|
-
#logLevel: LogLevel;
|
|
24
|
+
readonly #logLevel: LogLevel;
|
|
25
|
+
|
|
26
|
+
readonly #engine: Engine;
|
|
27
|
+
|
|
28
|
+
readonly #clientEventEmitter: EnhancedEventEmitter;
|
|
23
29
|
|
|
24
30
|
constructor(params: CreateMediaParams) {
|
|
25
31
|
this.#logLevel = params.logLevel;
|
|
@@ -27,6 +33,8 @@ class Media {
|
|
|
27
33
|
namespace: 'Media',
|
|
28
34
|
logLevel: params.logLevel,
|
|
29
35
|
});
|
|
36
|
+
this.#engine = params.engine;
|
|
37
|
+
this.#clientEventEmitter = params.clientEventEmitter;
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
get mediasoupDevice(): Device {
|
|
@@ -57,10 +65,14 @@ class Media {
|
|
|
57
65
|
private createTracks(stream: MediaStream): Track[] {
|
|
58
66
|
const mediaStreamTracks = stream.getTracks();
|
|
59
67
|
return mediaStreamTracks.map((mediaStreamTrack) => {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
const params = {
|
|
69
|
+
mediaStreamTrack,
|
|
70
|
+
logLevel: this.#logLevel,
|
|
71
|
+
engine: this.#engine,
|
|
72
|
+
clientEventEmitter: this.#clientEventEmitter,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const track = mediaStreamTrack.kind === 'audio' ? new AudioTrack(params) : new VideoTrack(params);
|
|
64
76
|
this.tracks.set(track.id, track);
|
|
65
77
|
this.#logger.debug('createTrack() track created', {
|
|
66
78
|
streamId: stream.id,
|
|
@@ -2,6 +2,7 @@ import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
|
2
2
|
import { AudioCodec, AudioEncoderConfig } from '../../../types/common';
|
|
3
3
|
import BaseTrack from './BaseTrack';
|
|
4
4
|
import TrackWithCodecOptions from './TrackWithCodecOptions';
|
|
5
|
+
import { INTERNAL_CLIENT_EVENTS } from '../../../constants/events';
|
|
5
6
|
|
|
6
7
|
class AudioTrack extends BaseTrack implements TrackWithCodecOptions {
|
|
7
8
|
getEncoderConfig(): AudioEncoderConfig {
|
|
@@ -22,6 +23,15 @@ class AudioTrack extends BaseTrack implements TrackWithCodecOptions {
|
|
|
22
23
|
|
|
23
24
|
return 'opus';
|
|
24
25
|
}
|
|
26
|
+
|
|
27
|
+
async publish(): Promise<void> {
|
|
28
|
+
await this.produce({
|
|
29
|
+
preferredCodec: this.getPreferredCodec(),
|
|
30
|
+
codecOptions: this.getCodecOptions(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.clientEventEmitter.emit(INTERNAL_CLIENT_EVENTS.trackProduced, this);
|
|
34
|
+
}
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
export default AudioTrack;
|