@livedigital/client 2.45.0-test-speaker.1 → 3.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/dist/constants/common.d.ts +1 -0
- package/dist/engine/handlers/ChannelAudioObserverEventHandler.d.ts +0 -3
- package/dist/engine/index.d.ts +9 -4
- package/dist/engine/media/DefaultMediaTracksFactory.d.ts +6 -0
- package/dist/engine/media/index.d.ts +9 -8
- package/dist/engine/media/tracks/{AudioTrack.d.ts → DefaultAudioTrack.d.ts} +5 -7
- package/dist/engine/media/tracks/{BaseTrack.d.ts → DefaultBaseTrack.d.ts} +6 -14
- package/dist/engine/media/tracks/{VideoTrack.d.ts → DefaultVideoTrack.d.ts} +7 -6
- package/dist/engine/network/Socket.d.ts +2 -2
- package/dist/helpers/retry.d.ts +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.es.js +11 -11
- package/dist/index.js +11 -11
- package/dist/types/common.d.ts +2 -14
- package/dist/types/engine.d.ts +8 -1
- package/dist/types/media.d.ts +73 -0
- package/dist/types/network.d.ts +20 -0
- package/package.json +3 -1
- package/src/constants/common.ts +2 -0
- package/src/engine/handlers/ChannelAudioObserverEventHandler.ts +0 -23
- package/src/engine/handlers/MediaSoupEventHandler.ts +1 -1
- package/src/engine/index.ts +52 -33
- package/src/engine/media/DefaultMediaTracksFactory.ts +17 -0
- package/src/engine/media/index.ts +19 -16
- package/src/engine/media/tracks/{AudioTrack.ts → DefaultAudioTrack.ts} +5 -8
- package/src/engine/media/tracks/{BaseTrack.ts → DefaultBaseTrack.ts} +9 -13
- package/src/engine/media/tracks/{VideoTrack.ts → DefaultVideoTrack.ts} +7 -6
- package/src/engine/network/Socket.ts +6 -3
- package/src/helpers/media.ts +1 -1
- package/src/helpers/retry.ts +5 -0
- package/src/index.ts +8 -3
- package/src/types/common.ts +2 -17
- package/src/types/engine.ts +11 -1
- package/src/types/media.ts +96 -0
- package/src/types/network.ts +24 -0
package/dist/types/common.d.ts
CHANGED
|
@@ -5,11 +5,9 @@ import { RtpCapabilities } from 'mediasoup-client/src/RtpParameters';
|
|
|
5
5
|
import { ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
6
6
|
import { ConnectionState } from 'mediasoup-client/src/Transport';
|
|
7
7
|
import { DtlsRole } from 'mediasoup-client/lib/Transport';
|
|
8
|
-
import AudioTrack from '../engine/media/tracks/AudioTrack';
|
|
9
|
-
import VideoTrack from '../engine/media/tracks/VideoTrack';
|
|
10
8
|
import NodeJSTimeout = NodeJS.Timeout;
|
|
11
9
|
export declare type Timeout = NodeJSTimeout | number;
|
|
12
|
-
export declare type SocketResponse = {
|
|
10
|
+
export declare type SocketResponse<T = Record<string, any>> = T & {
|
|
13
11
|
success?: boolean;
|
|
14
12
|
error?: string;
|
|
15
13
|
errorCode?: string;
|
|
@@ -63,10 +61,8 @@ export declare type AvailableMediaDevices = {
|
|
|
63
61
|
};
|
|
64
62
|
export declare type JoinChannelParams = {
|
|
65
63
|
channelId: string;
|
|
66
|
-
|
|
67
|
-
sdkSecret: string;
|
|
64
|
+
token: string;
|
|
68
65
|
role: Role;
|
|
69
|
-
uid?: string;
|
|
70
66
|
appData?: Record<string, unknown>;
|
|
71
67
|
};
|
|
72
68
|
export declare type ChannelEvent = {
|
|
@@ -143,7 +139,6 @@ export declare type CreateScreenMediaOptions = BaseVideoTrackOptions & BaseAudio
|
|
|
143
139
|
videoEncoderConfig?: VideoEncoderConfig;
|
|
144
140
|
audioEncoderConfig?: AudioEncoderConfig;
|
|
145
141
|
};
|
|
146
|
-
export declare type Track = VideoTrack | AudioTrack;
|
|
147
142
|
export declare enum TrackLabel {
|
|
148
143
|
Camera = "camera",
|
|
149
144
|
Microphone = "microphone",
|
|
@@ -312,9 +307,6 @@ export declare type CreateTracksPayload = {
|
|
|
312
307
|
mediaStreamTracks: MediaStreamTrack[];
|
|
313
308
|
constraints: MediaStreamConstraints;
|
|
314
309
|
};
|
|
315
|
-
export declare type TrackPublishParams = {
|
|
316
|
-
keyFrameRequestDelay?: number;
|
|
317
|
-
};
|
|
318
310
|
export declare type ChangeProducerStatePayload = {
|
|
319
311
|
peerId: string;
|
|
320
312
|
producerId: string;
|
|
@@ -323,10 +315,6 @@ export declare type ForceCloseProducerPayload = {
|
|
|
323
315
|
peerId: string;
|
|
324
316
|
label: TrackLabel;
|
|
325
317
|
};
|
|
326
|
-
export interface TrackWithEncodings {
|
|
327
|
-
getCodecOptions(): ProducerCodecOptions;
|
|
328
|
-
getEncodings(): RtpEncodingParameters[];
|
|
329
|
-
}
|
|
330
318
|
export declare type ActivityConfirmationRequiredPayload = {
|
|
331
319
|
channelId: string;
|
|
332
320
|
time: number;
|
package/dist/types/engine.d.ts
CHANGED
|
@@ -7,8 +7,9 @@ import Engine from '../engine';
|
|
|
7
7
|
import ChannelEventHandler from '../engine/handlers/ChannelEventHandler';
|
|
8
8
|
import MediaSoupEventHandler from '../engine/handlers/MediaSoupEventHandler';
|
|
9
9
|
import { LoadBalancerApiClientParams } from '../engine/network/LoadBalancerClient';
|
|
10
|
-
import { Logger, LogLevel, LogMessageHandler } from './common';
|
|
10
|
+
import { Logger, LogLevel, LogMessageHandler, Role } from './common';
|
|
11
11
|
import ChannelAudioObserverEventHandler from '../engine/handlers/ChannelAudioObserverEventHandler';
|
|
12
|
+
import { MediaTracksFactory } from './media';
|
|
12
13
|
export declare type IssuesHandler = (issues: IssueDetectorResult) => void;
|
|
13
14
|
export declare type NetworkScoresUpdatedHandler = (networks: NetworkScores) => void;
|
|
14
15
|
export interface CreateIssueDetectorParams {
|
|
@@ -21,6 +22,7 @@ export interface CreateMediaParams {
|
|
|
21
22
|
logLevel: LogLevel;
|
|
22
23
|
engine: Engine;
|
|
23
24
|
clientEventEmitter: EnhancedEventEmitter;
|
|
25
|
+
mediaTracksFactory: MediaTracksFactory;
|
|
24
26
|
onLogMessage?: LogMessageHandler;
|
|
25
27
|
}
|
|
26
28
|
export interface CreateMediaStreamTrackManagerParams {
|
|
@@ -49,6 +51,11 @@ export interface CreateAudioObserverEventHandlerParams {
|
|
|
49
51
|
engine: Engine;
|
|
50
52
|
onLogMessage?: LogMessageHandler;
|
|
51
53
|
}
|
|
54
|
+
export interface ConnectParams {
|
|
55
|
+
channelId: string;
|
|
56
|
+
role: Role;
|
|
57
|
+
token: string;
|
|
58
|
+
}
|
|
52
59
|
export interface EngineDependenciesFactory {
|
|
53
60
|
createSystem: (params: CreateSystemParams) => System;
|
|
54
61
|
createMedia: (params: CreateMediaParams) => Media;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Producer, ProducerCodecOptions } from 'mediasoup-client/lib/Producer';
|
|
2
|
+
import { RtpEncodingParameters } from 'mediasoup-client/lib/types';
|
|
3
|
+
import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
|
|
4
|
+
import Engine from '../engine';
|
|
5
|
+
import { BaseTrackInfo, EncoderConfig, LogLevel, TrackLabel, TrackOutboundStats, TrackProduceParams } from './common';
|
|
6
|
+
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
7
|
+
import MediaStreamTrackManager from '../engine/media/tracks/MediaStreamTrackManager';
|
|
8
|
+
export interface BaseTrackParams {
|
|
9
|
+
mediaStreamTrack: MediaStreamTrack;
|
|
10
|
+
logLevel: LogLevel;
|
|
11
|
+
engine: Engine;
|
|
12
|
+
clientEventEmitter: EnhancedEventEmitter;
|
|
13
|
+
constraints: MediaStreamConstraints;
|
|
14
|
+
mediaStreamTrackManager: MediaStreamTrackManager;
|
|
15
|
+
}
|
|
16
|
+
export interface BaseTrack {
|
|
17
|
+
mediaStreamTrack: MediaStreamTrack;
|
|
18
|
+
id: string;
|
|
19
|
+
kind: MediaKind;
|
|
20
|
+
isPublished: boolean;
|
|
21
|
+
isPaused: boolean;
|
|
22
|
+
clientEventEmitter: EnhancedEventEmitter;
|
|
23
|
+
producerId?: string;
|
|
24
|
+
getLabel(): TrackLabel;
|
|
25
|
+
setLabel(label: TrackLabel): void;
|
|
26
|
+
setEncoderConfig(encoderConfig: EncoderConfig): void;
|
|
27
|
+
getEncoderConfig(): EncoderConfig;
|
|
28
|
+
setProducer(producer: Producer): void;
|
|
29
|
+
setStopTrackOnPause(value: boolean): void;
|
|
30
|
+
getProducer(): Producer | undefined;
|
|
31
|
+
stopMediaStreamTrack(): void;
|
|
32
|
+
closeProducer(stopTrack?: boolean): Promise<void>;
|
|
33
|
+
setPriority(priority: RTCPriorityType): Promise<void>;
|
|
34
|
+
produce({ encodings, codecOptions, preferredCodec, transformParams, keyFrameRequestDelay, }: TrackProduceParams): Promise<void>;
|
|
35
|
+
publish(): Promise<void>;
|
|
36
|
+
unpublish(): Promise<void>;
|
|
37
|
+
pause(): Promise<void>;
|
|
38
|
+
resume(): Promise<void>;
|
|
39
|
+
getInfo(): Promise<BaseTrackInfo>;
|
|
40
|
+
getStats(): Promise<TrackOutboundStats | undefined>;
|
|
41
|
+
replaceTrack(track: MediaStreamTrack): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export interface TrackWithEncodings extends BaseTrack {
|
|
44
|
+
getCodecOptions(): ProducerCodecOptions;
|
|
45
|
+
getEncodings(): RtpEncodingParameters[];
|
|
46
|
+
getPreferredCodec(): string;
|
|
47
|
+
}
|
|
48
|
+
export interface TrackWithEffects extends BaseTrack {
|
|
49
|
+
isEffectsProcessing: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface AudioTrack extends BaseTrack, TrackWithEncodings, TrackWithEffects {
|
|
52
|
+
enableNoiseSuppression(): Promise<void>;
|
|
53
|
+
disableNoiseSuppression(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export declare type VideoTrackPublishParams = {
|
|
56
|
+
keyFrameRequestDelay?: number;
|
|
57
|
+
};
|
|
58
|
+
export interface VideoTrack extends BaseTrack, TrackWithEncodings, TrackWithEffects {
|
|
59
|
+
publish(params?: VideoTrackPublishParams): Promise<void>;
|
|
60
|
+
setTransformParams(transformParams: {}): void;
|
|
61
|
+
setMaxSpatialLayer(spatialLayer: number): Promise<void>;
|
|
62
|
+
getMaxSpatialLayer(): number | undefined;
|
|
63
|
+
enableBlur(): Promise<void>;
|
|
64
|
+
disableBlur(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export declare type Track = AudioTrack | VideoTrack;
|
|
67
|
+
export interface AudioTrackParams extends BaseTrackParams {
|
|
68
|
+
noiseSuppressor?: WebAssembly.WebAssemblyInstantiatedSource;
|
|
69
|
+
}
|
|
70
|
+
export interface MediaTracksFactory {
|
|
71
|
+
createAudioTrack: (params: AudioTrackParams) => BaseTrack;
|
|
72
|
+
createVideoTrack: (params: BaseTrackParams) => BaseTrack;
|
|
73
|
+
}
|
package/dist/types/network.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SocketIOEvents } from '../constants/events';
|
|
1
2
|
import { Role } from './common';
|
|
2
3
|
export declare type GetNodeRequest = {
|
|
3
4
|
channelId: string;
|
|
@@ -6,3 +7,22 @@ export declare type GetNodeRequest = {
|
|
|
6
7
|
export declare type GetNodeResponse = {
|
|
7
8
|
webSocketUrl: string;
|
|
8
9
|
};
|
|
10
|
+
export declare type JoinChannelRequest = {
|
|
11
|
+
appData?: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
export declare type JoinChannelSuccessResponse = {
|
|
14
|
+
channelId: string;
|
|
15
|
+
appId: string;
|
|
16
|
+
role: string;
|
|
17
|
+
};
|
|
18
|
+
export declare type SocketConnectionError = {
|
|
19
|
+
message: string;
|
|
20
|
+
data?: {
|
|
21
|
+
errorCode: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export declare type SocketObserverEvent = {
|
|
25
|
+
state: SocketIOEvents;
|
|
26
|
+
code?: string;
|
|
27
|
+
error?: SocketConnectionError;
|
|
28
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@livedigital/client",
|
|
3
3
|
"author": "vlprojects",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "3.0.0",
|
|
6
6
|
"private": false,
|
|
7
7
|
"bugs": {
|
|
8
8
|
"url": "https://github.com/vlprojects/livedigital-sdk/issues"
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"@types/chai-subset": "^1.3.3",
|
|
69
69
|
"@types/emscripten": "^1.39.6",
|
|
70
70
|
"@types/faker": "^5.5.9",
|
|
71
|
+
"@types/jsonwebtoken": "^9.0.2",
|
|
71
72
|
"@types/mocha": "^10.0.1",
|
|
72
73
|
"@types/node": "^14.18.36",
|
|
73
74
|
"@types/qs": "^6.9.5",
|
|
@@ -88,6 +89,7 @@
|
|
|
88
89
|
"eslint-plugin-react": "^7.26.1",
|
|
89
90
|
"event-target-shim": "^6.0.2",
|
|
90
91
|
"faker": "^5.5.3",
|
|
92
|
+
"jsonwebtoken": "^9.0.1",
|
|
91
93
|
"mocha": "^10.2.0",
|
|
92
94
|
"nyc": "^15.1.0",
|
|
93
95
|
"rollup": "^2.57.0",
|
package/src/constants/common.ts
CHANGED
|
@@ -8,17 +8,12 @@ import {
|
|
|
8
8
|
ChannelAudioObserverEvents,
|
|
9
9
|
DominantSpeakerEvent, PeersVolumesEvent,
|
|
10
10
|
} from '../../types/channelAudioObserver';
|
|
11
|
-
import Peer from '../Peer';
|
|
12
11
|
|
|
13
12
|
class ChannelAudioObserverEventHandler {
|
|
14
13
|
private readonly engine: Engine;
|
|
15
14
|
|
|
16
15
|
private readonly logger: Logger;
|
|
17
16
|
|
|
18
|
-
private lastDominantSpeaker?: Peer;
|
|
19
|
-
|
|
20
|
-
private isSilence = true;
|
|
21
|
-
|
|
22
17
|
constructor(params: { engine: Engine, onLogMessage?: LogMessageHandler }) {
|
|
23
18
|
const { engine, onLogMessage } = params;
|
|
24
19
|
this.engine = engine;
|
|
@@ -61,12 +56,9 @@ class ChannelAudioObserverEventHandler {
|
|
|
61
56
|
const { peerId } = payload.data;
|
|
62
57
|
const peer = this.engine.getPeerById(peerId);
|
|
63
58
|
this.engine.setActiveSpeakerPeer(peer);
|
|
64
|
-
this.lastDominantSpeaker = peer;
|
|
65
59
|
}
|
|
66
60
|
|
|
67
61
|
private handlePeerVolumes(payload: PeersVolumesEvent): void {
|
|
68
|
-
this.checkLastDominantSpeakerContinueSpeak(payload);
|
|
69
|
-
this.isSilence = false;
|
|
70
62
|
payload.volumes.forEach((item) => {
|
|
71
63
|
const { peerId, trackLabel, value } = item;
|
|
72
64
|
const track = this.engine.getPeerById(peerId)?.tracks.get(trackLabel);
|
|
@@ -79,7 +71,6 @@ class ChannelAudioObserverEventHandler {
|
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
private handleSilence(): void {
|
|
82
|
-
this.isSilence = true;
|
|
83
74
|
const tracks = this.engine.peers.flatMap((peer) => Array.from(peer.tracks.values()));
|
|
84
75
|
tracks.forEach((track) => track.setVolume(0));
|
|
85
76
|
this.engine.setActiveSpeakerPeer(undefined);
|
|
@@ -130,20 +121,6 @@ class ChannelAudioObserverEventHandler {
|
|
|
130
121
|
}
|
|
131
122
|
}
|
|
132
123
|
|
|
133
|
-
private checkLastDominantSpeakerContinueSpeak(payload: PeersVolumesEvent) {
|
|
134
|
-
if (!this.isSilence || !this.lastDominantSpeaker) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const lastDominantSpeakerVolume = payload.volumes.find((item) => item.peerId === this.lastDominantSpeaker?.id);
|
|
139
|
-
if (!lastDominantSpeakerVolume) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const lastDominantSpeakerPeer = this.engine.getPeerById(lastDominantSpeakerVolume.peerId);
|
|
144
|
-
this.engine.setActiveSpeakerPeer(lastDominantSpeakerPeer);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
124
|
static throwInvalidPayload(): void {
|
|
148
125
|
throw new InvalidPayloadError('Invalid channel audio observer event handler payload');
|
|
149
126
|
}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, MEDIASOUP_EVENTS, PEER_EVENTS,
|
|
15
15
|
} from '../../constants/events';
|
|
16
16
|
import Logger from '../Logger';
|
|
17
|
-
import VideoTrack from '../media/tracks/
|
|
17
|
+
import VideoTrack from '../media/tracks/DefaultVideoTrack';
|
|
18
18
|
|
|
19
19
|
class MediaSoupEventHandler {
|
|
20
20
|
private readonly engine: Engine;
|
package/src/engine/index.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
PeerResponse,
|
|
11
11
|
Role,
|
|
12
12
|
SocketResponse,
|
|
13
|
-
Track,
|
|
14
13
|
TrackLabel,
|
|
15
14
|
LogMessageHandler,
|
|
16
15
|
LogLevel,
|
|
@@ -27,23 +26,32 @@ import Logger from './Logger';
|
|
|
27
26
|
import {
|
|
28
27
|
CHANNEL_EVENTS, CLIENT_EVENTS, INTERNAL_CLIENT_EVENTS, SocketIOEvents,
|
|
29
28
|
} from '../constants/events';
|
|
30
|
-
import {
|
|
31
|
-
|
|
29
|
+
import {
|
|
30
|
+
GetNodeRequest, JoinChannelRequest, JoinChannelSuccessResponse, SocketConnectionError, SocketObserverEvent,
|
|
31
|
+
} from '../types/network';
|
|
32
|
+
import VideoTrack from './media/tracks/DefaultVideoTrack';
|
|
32
33
|
import PeerTrack from './media/tracks/PeerTrack';
|
|
33
34
|
import { retryAsync } from '../helpers/retry';
|
|
34
|
-
import {
|
|
35
|
-
|
|
35
|
+
import {
|
|
36
|
+
ConnectParams, EngineDependenciesFactory, IssuesHandler, NetworkScoresUpdatedHandler,
|
|
37
|
+
} from '../types/engine';
|
|
38
|
+
import { LogLevels, SOCKET_ERROR_CODE_UNAUTHORIZED } from '../constants/common';
|
|
36
39
|
import { LoadBalancerApiClientParams } from './network/LoadBalancerClient';
|
|
37
40
|
import validateAppData from '../helpers/appDataValidator';
|
|
38
|
-
import AudioTrack from './media/tracks/AudioTrack';
|
|
39
41
|
import NeedJoinFirstError from '../errors/NeedJoinFirstError';
|
|
40
42
|
import ChannelAudioObserverEventHandler from './handlers/ChannelAudioObserverEventHandler';
|
|
43
|
+
import {
|
|
44
|
+
MediaTracksFactory, AudioTrack, Track, BaseTrack,
|
|
45
|
+
} from '../types/media';
|
|
41
46
|
|
|
42
47
|
type EngineParams = {
|
|
43
|
-
clientEventEmitter: EnhancedEventEmitter
|
|
48
|
+
clientEventEmitter: EnhancedEventEmitter;
|
|
44
49
|
network: {
|
|
45
50
|
loadbalancer: LoadBalancerApiClientParams;
|
|
46
|
-
}
|
|
51
|
+
};
|
|
52
|
+
media: {
|
|
53
|
+
mediaTracksFactory: MediaTracksFactory;
|
|
54
|
+
};
|
|
47
55
|
dependenciesFactory: EngineDependenciesFactory;
|
|
48
56
|
logLevel?: LogLevel;
|
|
49
57
|
onLogMessage?: LogMessageHandler;
|
|
@@ -93,6 +101,7 @@ class Engine {
|
|
|
93
101
|
const {
|
|
94
102
|
clientEventEmitter,
|
|
95
103
|
network,
|
|
104
|
+
media,
|
|
96
105
|
dependenciesFactory,
|
|
97
106
|
logLevel,
|
|
98
107
|
} = params;
|
|
@@ -106,6 +115,7 @@ class Engine {
|
|
|
106
115
|
logLevel: this.logLevel,
|
|
107
116
|
engine: this,
|
|
108
117
|
clientEventEmitter,
|
|
118
|
+
mediaTracksFactory: media.mediaTracksFactory,
|
|
109
119
|
onLogMessage: params.onLogMessage,
|
|
110
120
|
});
|
|
111
121
|
this.network = dependenciesFactory.createNetwork({
|
|
@@ -201,9 +211,16 @@ class Engine {
|
|
|
201
211
|
}
|
|
202
212
|
|
|
203
213
|
private watchSocketState(): void {
|
|
204
|
-
this.network.socket.observer.on('state', (evt:
|
|
205
|
-
const { state, code } = evt;
|
|
206
|
-
this.clientEventEmitter.emit(state, { code });
|
|
214
|
+
this.network.socket.observer.on('state', (evt: SocketObserverEvent) => {
|
|
215
|
+
const { state, code, error } = evt;
|
|
216
|
+
this.clientEventEmitter.emit(state, { code, error });
|
|
217
|
+
|
|
218
|
+
const isUnauthorizedError = error?.data?.errorCode === SOCKET_ERROR_CODE_UNAUTHORIZED;
|
|
219
|
+
if (state === SocketIOEvents.Error && this.isJoined && isUnauthorizedError) {
|
|
220
|
+
// reconnection failed due to expired token
|
|
221
|
+
this.clientEventEmitter.emit(CLIENT_EVENTS.channelRejoinRequired);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
207
224
|
|
|
208
225
|
if (state === SocketIOEvents.Reconnected) {
|
|
209
226
|
this.clientEventEmitter.emit(CLIENT_EVENTS.channelRejoinRequired);
|
|
@@ -252,7 +269,7 @@ class Engine {
|
|
|
252
269
|
this.logger.debug('join()', { params });
|
|
253
270
|
this.isChannelJoining = true;
|
|
254
271
|
await this.connectToSocketServerWithRetry(params);
|
|
255
|
-
await this.performJoin(params);
|
|
272
|
+
await this.performJoin({ appData: params.appData });
|
|
256
273
|
} catch (error) {
|
|
257
274
|
this.logger.error('join()', { error });
|
|
258
275
|
throw error;
|
|
@@ -310,7 +327,7 @@ class Engine {
|
|
|
310
327
|
}
|
|
311
328
|
}
|
|
312
329
|
|
|
313
|
-
async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<
|
|
330
|
+
async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<VideoTrack> {
|
|
314
331
|
const trackParams = Media.getCameraVideoTrackParams(options);
|
|
315
332
|
try {
|
|
316
333
|
const [track] = await this.media.createUserMediaTracks({
|
|
@@ -334,14 +351,14 @@ class Engine {
|
|
|
334
351
|
|
|
335
352
|
this.media.setTrack(track);
|
|
336
353
|
this.logger.debug('createCameraVideoTrack()', { trackParams, track });
|
|
337
|
-
return track;
|
|
354
|
+
return track as VideoTrack;
|
|
338
355
|
} catch (error) {
|
|
339
356
|
this.logger.error('createCameraVideoTrack()', { error, trackParams });
|
|
340
357
|
throw new Error(`Can not create camera track: ${error.message}`);
|
|
341
358
|
}
|
|
342
359
|
}
|
|
343
360
|
|
|
344
|
-
async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<
|
|
361
|
+
async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<AudioTrack> {
|
|
345
362
|
try {
|
|
346
363
|
const [track] = await this.media.createUserMediaTracks({
|
|
347
364
|
video: false,
|
|
@@ -354,9 +371,9 @@ class Engine {
|
|
|
354
371
|
track.setEncoderConfig(options.encoderConfig);
|
|
355
372
|
}
|
|
356
373
|
|
|
357
|
-
if (track
|
|
374
|
+
if (track.kind === 'audio' && options?.noiseSuppression) {
|
|
358
375
|
try {
|
|
359
|
-
await track.enableNoiseSuppression();
|
|
376
|
+
await (track as AudioTrack).enableNoiseSuppression();
|
|
360
377
|
} catch (error) {
|
|
361
378
|
this.logger.warn('Failed to start noise suppression, featured original track', { track: this });
|
|
362
379
|
}
|
|
@@ -364,7 +381,7 @@ class Engine {
|
|
|
364
381
|
|
|
365
382
|
this.media.setTrack(track);
|
|
366
383
|
this.logger.debug('createMicrophoneAudioTrack()', { options, track });
|
|
367
|
-
return track;
|
|
384
|
+
return track as AudioTrack;
|
|
368
385
|
} catch (error) {
|
|
369
386
|
this.logger.error('createMicrophoneAudioTrack()', { error, options });
|
|
370
387
|
throw new Error(`Can not create microphone track: ${error.message}`);
|
|
@@ -413,14 +430,14 @@ class Engine {
|
|
|
413
430
|
});
|
|
414
431
|
});
|
|
415
432
|
|
|
416
|
-
return tracks;
|
|
433
|
+
return tracks as Track[];
|
|
417
434
|
} catch (error) {
|
|
418
435
|
this.logger.error('createScreenMediaTrack()', { error, trackParams: videoTrackParams });
|
|
419
436
|
throw new Error(`Can not create screen media tracks: ${error.message}`);
|
|
420
437
|
}
|
|
421
438
|
}
|
|
422
439
|
|
|
423
|
-
deleteTrack(tracks:
|
|
440
|
+
deleteTrack(tracks: BaseTrack): Promise<void> {
|
|
424
441
|
return this.media.deleteTrack(tracks);
|
|
425
442
|
}
|
|
426
443
|
|
|
@@ -445,7 +462,7 @@ class Engine {
|
|
|
445
462
|
this.clientEventEmitter.safeEmit(CLIENT_EVENTS.activeSpeakerChanged, { peer });
|
|
446
463
|
}
|
|
447
464
|
|
|
448
|
-
private async connectToSocketServerWithRetry(params:
|
|
465
|
+
private async connectToSocketServerWithRetry(params: ConnectParams): Promise<void> {
|
|
449
466
|
const connectToSocketServerAction = async () => this.connectToSocketServer(params);
|
|
450
467
|
return retryAsync(connectToSocketServerAction, {
|
|
451
468
|
maxRetries: 15,
|
|
@@ -456,15 +473,17 @@ class Engine {
|
|
|
456
473
|
namespace: 'RetrySocketConnect',
|
|
457
474
|
logLevel: this.logLevel,
|
|
458
475
|
}),
|
|
476
|
+
abortOnError: (error: SocketConnectionError) => error?.data?.errorCode === SOCKET_ERROR_CODE_UNAUTHORIZED,
|
|
459
477
|
});
|
|
460
478
|
}
|
|
461
479
|
|
|
462
|
-
private async connectToSocketServer(params:
|
|
480
|
+
private async connectToSocketServer(params: ConnectParams): Promise<void> {
|
|
463
481
|
const { webSocketUrl } = await this.getAvailableNode({
|
|
464
482
|
channelId: params.channelId,
|
|
465
483
|
role: params.role,
|
|
466
484
|
});
|
|
467
|
-
|
|
485
|
+
|
|
486
|
+
this.network.socket.connect(webSocketUrl, params.token);
|
|
468
487
|
|
|
469
488
|
try {
|
|
470
489
|
await this.waitForSocketConnection();
|
|
@@ -493,7 +512,7 @@ class Engine {
|
|
|
493
512
|
|
|
494
513
|
private waitForSocketConnection(): Promise<void> {
|
|
495
514
|
return new Promise((resolve, reject) => {
|
|
496
|
-
const onSocketStateChange = async (data:
|
|
515
|
+
const onSocketStateChange = async (data: SocketObserverEvent) => {
|
|
497
516
|
const { error, state } = data;
|
|
498
517
|
const stopListening = () => this.network.socket.observer.removeListener('state', onSocketStateChange);
|
|
499
518
|
const isStateNotExpected = [SocketIOEvents.Disconnected, SocketIOEvents.Reconnecting].includes(state);
|
|
@@ -520,16 +539,16 @@ class Engine {
|
|
|
520
539
|
});
|
|
521
540
|
}
|
|
522
541
|
|
|
523
|
-
private async performJoin(params:
|
|
542
|
+
private async performJoin(params: JoinChannelRequest): Promise<void> {
|
|
524
543
|
try {
|
|
525
|
-
await this.sendJoinChannelRequestWithRetry(params);
|
|
526
|
-
this.app =
|
|
527
|
-
this.channel =
|
|
544
|
+
const { appId, channelId } = await this.sendJoinChannelRequestWithRetry<JoinChannelSuccessResponse>(params);
|
|
545
|
+
this.app = appId;
|
|
546
|
+
this.channel = channelId;
|
|
528
547
|
await this.initialize();
|
|
529
548
|
this.channelEventsHandler.subscribeToEvents();
|
|
530
549
|
this.mediaSoupEventsHandler.subscribeToEvents();
|
|
531
550
|
this.isJoined = true;
|
|
532
|
-
this.logger.debug('Successfully joined channel', { channelId
|
|
551
|
+
this.logger.debug('Successfully joined channel', { channelId });
|
|
533
552
|
await this.createAudioObserver();
|
|
534
553
|
} catch (error) {
|
|
535
554
|
this.logger.error('performJoin()', { error });
|
|
@@ -537,8 +556,8 @@ class Engine {
|
|
|
537
556
|
}
|
|
538
557
|
}
|
|
539
558
|
|
|
540
|
-
private async sendJoinChannelRequestWithRetry(params:
|
|
541
|
-
const joinChannelAction = async () => this.sendJoinChannelRequest(params);
|
|
559
|
+
private async sendJoinChannelRequestWithRetry<T>(params: JoinChannelRequest): Promise<SocketResponse<T>> {
|
|
560
|
+
const joinChannelAction = async () => this.sendJoinChannelRequest<T>(params);
|
|
542
561
|
return retryAsync(joinChannelAction, {
|
|
543
562
|
maxRetries: 3,
|
|
544
563
|
minBackoffDelayMs: 300,
|
|
@@ -550,8 +569,8 @@ class Engine {
|
|
|
550
569
|
});
|
|
551
570
|
}
|
|
552
571
|
|
|
553
|
-
private async sendJoinChannelRequest(params:
|
|
554
|
-
return this.network.socket.request(CHANNEL_EVENTS.channelJoin, params);
|
|
572
|
+
private async sendJoinChannelRequest<T>(params: JoinChannelRequest): Promise<SocketResponse<T>> {
|
|
573
|
+
return this.network.socket.request<T>(CHANNEL_EVENTS.channelJoin, params);
|
|
555
574
|
}
|
|
556
575
|
|
|
557
576
|
get cahPublish(): boolean {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AudioTrack, AudioTrackParams, BaseTrackParams, MediaTracksFactory, VideoTrack,
|
|
3
|
+
} from '../../types/media';
|
|
4
|
+
import DefaultVideoTrack from './tracks/DefaultVideoTrack';
|
|
5
|
+
import DefaultAudioTrack from './tracks/DefaultAudioTrack';
|
|
6
|
+
|
|
7
|
+
class DefaultMediaTracksFactory implements MediaTracksFactory {
|
|
8
|
+
createAudioTrack(params: AudioTrackParams): AudioTrack {
|
|
9
|
+
return new DefaultAudioTrack(params);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
createVideoTrack(params: BaseTrackParams): VideoTrack {
|
|
13
|
+
return new DefaultVideoTrack(params);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default DefaultMediaTracksFactory;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { Device } from 'mediasoup-client';
|
|
2
2
|
import { RtpCapabilities, RtpCodecCapability } from 'mediasoup-client/lib/RtpParameters';
|
|
3
|
-
import VideoTrack from './tracks/VideoTrack';
|
|
4
|
-
import AudioTrack from './tracks/AudioTrack';
|
|
5
3
|
import {
|
|
6
4
|
CreateCameraVideoTrackOptions,
|
|
7
5
|
CreateScreenMediaOptions,
|
|
8
6
|
CreateTracksPayload,
|
|
9
7
|
CreateVideoTrackParams,
|
|
10
8
|
LogLevel,
|
|
11
|
-
Track,
|
|
12
9
|
TrackLabel,
|
|
13
10
|
TransformParams,
|
|
14
11
|
} from '../../types/common';
|
|
@@ -21,13 +18,16 @@ import EnhancedEventEmitter from '../../EnhancedEventEmitter';
|
|
|
21
18
|
import MediaStreamTrackManager from './tracks/MediaStreamTrackManager';
|
|
22
19
|
import { detectHandlerName } from '../../helpers/media';
|
|
23
20
|
import WasmModuleCompiler from './streamEffects/audio/noiseSuppression/WasmModuleCompiler';
|
|
21
|
+
import {
|
|
22
|
+
AudioTrack, BaseTrack, MediaTracksFactory, TrackWithEncodings, VideoTrack,
|
|
23
|
+
} from '../../types/media';
|
|
24
24
|
|
|
25
25
|
class Media {
|
|
26
26
|
public isDeviceLoaded = false;
|
|
27
27
|
|
|
28
28
|
private device?: Device;
|
|
29
29
|
|
|
30
|
-
private tracks: Map<TrackLabel,
|
|
30
|
+
private tracks: Map<TrackLabel, BaseTrack> = new Map();
|
|
31
31
|
|
|
32
32
|
readonly #logger: Logger;
|
|
33
33
|
|
|
@@ -39,6 +39,8 @@ class Media {
|
|
|
39
39
|
|
|
40
40
|
readonly #mediaStreamTrackManager: MediaStreamTrackManager;
|
|
41
41
|
|
|
42
|
+
readonly #mediaTracksFactory: MediaTracksFactory;
|
|
43
|
+
|
|
42
44
|
#noiseSuppressor?: WebAssembly.WebAssemblyInstantiatedSource;
|
|
43
45
|
|
|
44
46
|
constructor(params: CreateMediaParams) {
|
|
@@ -50,6 +52,7 @@ class Media {
|
|
|
50
52
|
});
|
|
51
53
|
this.#engine = params.engine;
|
|
52
54
|
this.#clientEventEmitter = params.clientEventEmitter;
|
|
55
|
+
this.#mediaTracksFactory = params.mediaTracksFactory;
|
|
53
56
|
this.#mediaStreamTrackManager = new MediaStreamTrackManager({
|
|
54
57
|
logLevel: params.logLevel,
|
|
55
58
|
onLogMessage: params.onLogMessage,
|
|
@@ -68,7 +71,7 @@ class Media {
|
|
|
68
71
|
return !!this.#noiseSuppressor;
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
getTrack(label: TrackLabel):
|
|
74
|
+
getTrack(label: TrackLabel): BaseTrack | undefined {
|
|
72
75
|
return this.tracks.get(label);
|
|
73
76
|
}
|
|
74
77
|
|
|
@@ -92,7 +95,7 @@ class Media {
|
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
getTrackCodec(track:
|
|
98
|
+
getTrackCodec(track: TrackWithEncodings): RtpCodecCapability | undefined {
|
|
96
99
|
const { rtpCapabilities, rtpCapabilities: { codecs } } = this.mediasoupDevice;
|
|
97
100
|
if (!codecs) {
|
|
98
101
|
this.#logger.error('getTrackCodec()', { track, rtpCapabilities });
|
|
@@ -102,7 +105,7 @@ class Media {
|
|
|
102
105
|
return codecs.find((c) => c.mimeType.toLowerCase() === `${track.kind}/${track.getPreferredCodec()}`);
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
private createTracks({ constraints, mediaStreamTracks }: CreateTracksPayload):
|
|
108
|
+
private createTracks({ constraints, mediaStreamTracks }: CreateTracksPayload): BaseTrack[] {
|
|
106
109
|
return mediaStreamTracks.map((mediaStreamTrack) => {
|
|
107
110
|
const params = {
|
|
108
111
|
mediaStreamTrack,
|
|
@@ -114,8 +117,8 @@ class Media {
|
|
|
114
117
|
};
|
|
115
118
|
|
|
116
119
|
const track = mediaStreamTrack.kind === 'audio'
|
|
117
|
-
?
|
|
118
|
-
:
|
|
120
|
+
? this.#mediaTracksFactory.createAudioTrack({ ...params, noiseSuppressor: this.#noiseSuppressor })
|
|
121
|
+
: this.#mediaTracksFactory.createVideoTrack(params);
|
|
119
122
|
|
|
120
123
|
this.#logger.debug('createTrack() track created', {
|
|
121
124
|
trackId: track.id,
|
|
@@ -126,24 +129,24 @@ class Media {
|
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<
|
|
132
|
+
async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<BaseTrack[]> {
|
|
130
133
|
const mediaStreamTracks = await this.#mediaStreamTrackManager.createUserMediaTracks(constraints);
|
|
131
134
|
return this.createTracks({ constraints, mediaStreamTracks });
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<
|
|
137
|
+
async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<BaseTrack[]> {
|
|
135
138
|
const mediaStreamTracks = await this.#mediaStreamTrackManager.createDisplayMediaTracks(constraints);
|
|
136
139
|
return this.createTracks({ constraints, mediaStreamTracks });
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
async deleteTrack(track:
|
|
142
|
+
async deleteTrack(track: BaseTrack) {
|
|
140
143
|
await track.closeProducer();
|
|
141
144
|
if ('disableNoiseSuppression' in track) {
|
|
142
|
-
await track.disableNoiseSuppression();
|
|
145
|
+
await (track as AudioTrack).disableNoiseSuppression();
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
if ('disableBlur' in track) {
|
|
146
|
-
await track.disableBlur();
|
|
149
|
+
await (track as VideoTrack).disableBlur();
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
track.stopMediaStreamTrack();
|
|
@@ -151,7 +154,7 @@ class Media {
|
|
|
151
154
|
this.#logger.debug('deleteTrack() tack deleted', { trackId: track.id, kind: track.kind });
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
getAllTracks():
|
|
157
|
+
getAllTracks(): BaseTrack[] {
|
|
155
158
|
return Array.from(this.tracks.values());
|
|
156
159
|
}
|
|
157
160
|
|
|
@@ -165,7 +168,7 @@ class Media {
|
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
|
|
168
|
-
setTrack(track:
|
|
171
|
+
setTrack(track: BaseTrack): void {
|
|
169
172
|
this.tracks.set(track.getLabel(), track);
|
|
170
173
|
}
|
|
171
174
|
|