@livedigital/client 2.8.0 → 2.10.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/engine/DefaultEngineDependenciesFactory.d.ts +18 -0
- package/dist/engine/index.d.ts +5 -3
- package/dist/engine/network/index.d.ts +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/dist/types/engine.d.ts +24 -0
- package/package.json +1 -1
- package/src/engine/DefaultEngineDependenciesFactory.ts +53 -0
- package/src/engine/index.ts +12 -15
- package/src/engine/network/index.ts +6 -5
- package/src/engine/wid/WebRTCIssueDetector.ts +1 -0
- package/src/engine/wid/detectors/FramesDroppedIssueDetector.ts +2 -1
- package/src/engine/wid/detectors/NetworkIssueDetector.ts +32 -9
- package/src/index.ts +9 -4
- package/src/types/engine.ts +27 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
2
|
+
import System from '../engine/system';
|
|
3
|
+
import Media from '../engine/media';
|
|
4
|
+
import Network from '../engine/network';
|
|
5
|
+
import Engine from '../engine';
|
|
6
|
+
import ChannelEventHandler from '../engine/handlers/ChannelEventHandler';
|
|
7
|
+
import MediaSoupEventHandler from '../engine/handlers/MediaSoupEventHandler';
|
|
8
|
+
import WebRTCIssueDetector from '../engine/wid/WebRTCIssueDetector';
|
|
9
|
+
import { LoadBalancerApiClientParams } from '../engine/network/LoadBalancerClient';
|
|
10
|
+
import Logger from '../engine/Logger';
|
|
11
|
+
export interface CreateIssueDetectorParams {
|
|
12
|
+
logger: Logger;
|
|
13
|
+
}
|
|
14
|
+
export interface CreateNetworkParams {
|
|
15
|
+
loadbalancer: LoadBalancerApiClientParams;
|
|
16
|
+
}
|
|
17
|
+
export interface EngineDependenciesFactory {
|
|
18
|
+
createSystem: (clientEventEmitter: EnhancedEventEmitter) => System;
|
|
19
|
+
createMedia: () => Media;
|
|
20
|
+
createNetwork: (networkParams: CreateNetworkParams) => Network;
|
|
21
|
+
createChannelEventHandler: (engine: Engine) => ChannelEventHandler;
|
|
22
|
+
createMediaSoupEventHandler: (engine: Engine) => MediaSoupEventHandler;
|
|
23
|
+
createIssueDetector: (params: CreateIssueDetectorParams) => WebRTCIssueDetector;
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
2
|
+
import Network from './network';
|
|
3
|
+
import System from './system';
|
|
4
|
+
import Media from './media';
|
|
5
|
+
import ChannelEventHandler from './handlers/ChannelEventHandler';
|
|
6
|
+
import Engine from './index';
|
|
7
|
+
import MediaSoupEventHandler from './handlers/MediaSoupEventHandler';
|
|
8
|
+
import WebRTCIssueDetector from './wid/WebRTCIssueDetector';
|
|
9
|
+
import { CreateIssueDetectorParams, CreateNetworkParams, EngineDependenciesFactory } from '../types/engine';
|
|
10
|
+
import SocketIO from './network/Socket';
|
|
11
|
+
import LoadBalancerApiClient from './network/LoadBalancerClient';
|
|
12
|
+
|
|
13
|
+
/* eslint-disable class-methods-use-this */
|
|
14
|
+
class DefaultEngineDependenciesFactory implements EngineDependenciesFactory {
|
|
15
|
+
createSystem(clientEventEmitter: EnhancedEventEmitter): System {
|
|
16
|
+
return new System({ clientEventEmitter });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
createMedia(): Media {
|
|
20
|
+
return new Media();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
createNetwork(params: CreateNetworkParams): Network {
|
|
24
|
+
const { loadbalancer } = params;
|
|
25
|
+
const socketClient = new SocketIO();
|
|
26
|
+
const loadBalancerApiClient = new LoadBalancerApiClient(loadbalancer);
|
|
27
|
+
return new Network({
|
|
28
|
+
socketClient,
|
|
29
|
+
loadBalancerApiClient,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
createChannelEventHandler(engine: Engine): ChannelEventHandler {
|
|
34
|
+
return new ChannelEventHandler(engine);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
createMediaSoupEventHandler(engine: Engine): MediaSoupEventHandler {
|
|
38
|
+
return new MediaSoupEventHandler(engine);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
createIssueDetector(params: CreateIssueDetectorParams): WebRTCIssueDetector {
|
|
42
|
+
return new WebRTCIssueDetector({
|
|
43
|
+
onIssue: (issues: any[]) => {
|
|
44
|
+
issues.forEach((issue) => {
|
|
45
|
+
params.logger.warn('webRtcIssueDetector', issue);
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/* eslint-enable class-methods-use-this */
|
|
52
|
+
|
|
53
|
+
export default DefaultEngineDependenciesFactory;
|
package/src/engine/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
16
16
|
import System from './system';
|
|
17
17
|
import Peer from './Peer';
|
|
18
|
-
import Network
|
|
18
|
+
import Network from './network';
|
|
19
19
|
import Media from './media';
|
|
20
20
|
import ChannelEventHandler from './handlers/ChannelEventHandler';
|
|
21
21
|
import MediaSoupEventHandler from './handlers/MediaSoupEventHandler';
|
|
@@ -29,11 +29,13 @@ import AudioTrack from './media/tracks/AudioTrack';
|
|
|
29
29
|
import PeerTrack from './media/tracks/PeerTrack';
|
|
30
30
|
import WebRTCIssueDetector from './wid/WebRTCIssueDetector';
|
|
31
31
|
import { retryAsync } from '../helpers/retry';
|
|
32
|
+
import { CreateNetworkParams, EngineDependenciesFactory } from '../types/engine';
|
|
32
33
|
|
|
33
34
|
type EngineParams = {
|
|
34
35
|
clientEventEmitter: EnhancedEventEmitter,
|
|
35
|
-
network:
|
|
36
|
+
network: CreateNetworkParams,
|
|
36
37
|
onLogMessage?: LogMessageHandler;
|
|
38
|
+
dependenciesFactory: EngineDependenciesFactory;
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
class Engine {
|
|
@@ -61,7 +63,7 @@ class Engine {
|
|
|
61
63
|
|
|
62
64
|
private isRoomJoining = false;
|
|
63
65
|
|
|
64
|
-
private logger: Logger;
|
|
66
|
+
private readonly logger: Logger;
|
|
65
67
|
|
|
66
68
|
private webRtcIssueDetector: WebRTCIssueDetector;
|
|
67
69
|
|
|
@@ -69,22 +71,17 @@ class Engine {
|
|
|
69
71
|
const {
|
|
70
72
|
clientEventEmitter,
|
|
71
73
|
network,
|
|
74
|
+
dependenciesFactory,
|
|
72
75
|
} = params;
|
|
73
|
-
this.system =
|
|
74
|
-
this.media =
|
|
75
|
-
this.network =
|
|
76
|
+
this.system = dependenciesFactory.createSystem(clientEventEmitter);
|
|
77
|
+
this.media = dependenciesFactory.createMedia();
|
|
78
|
+
this.network = dependenciesFactory.createNetwork(network);
|
|
76
79
|
this.peersRepository = new Map<string, Peer>();
|
|
77
80
|
this.clientEventEmitter = clientEventEmitter;
|
|
78
|
-
this.channelEventsHandler =
|
|
79
|
-
this.mediaSoupEventsHandler =
|
|
81
|
+
this.channelEventsHandler = dependenciesFactory.createChannelEventHandler(this);
|
|
82
|
+
this.mediaSoupEventsHandler = dependenciesFactory.createMediaSoupEventHandler(this);
|
|
80
83
|
this.logger = new Logger('Engine', params.onLogMessage);
|
|
81
|
-
this.webRtcIssueDetector =
|
|
82
|
-
onIssue: (issues: any[]) => {
|
|
83
|
-
issues.forEach((issue) => {
|
|
84
|
-
this.logger.warn('webRtcIssueDetector', issue);
|
|
85
|
-
});
|
|
86
|
-
},
|
|
87
|
-
});
|
|
84
|
+
this.webRtcIssueDetector = dependenciesFactory.createIssueDetector({ logger: this.logger });
|
|
88
85
|
|
|
89
86
|
this.watchSocketState();
|
|
90
87
|
}
|
|
@@ -7,12 +7,13 @@ import {
|
|
|
7
7
|
import { Device } from 'mediasoup-client';
|
|
8
8
|
import SocketIO from './Socket';
|
|
9
9
|
import { ProduceParams } from '../../types/common';
|
|
10
|
-
import LoadBalancerApiClient
|
|
10
|
+
import LoadBalancerApiClient from './LoadBalancerClient';
|
|
11
11
|
import { MEDIASOUP_EVENTS, MEDIASOUP_TRANSPORT_EVENTS } from '../../constants/events';
|
|
12
12
|
import Logger from '../Logger';
|
|
13
13
|
|
|
14
14
|
export type NetworkParams = {
|
|
15
|
-
|
|
15
|
+
socketClient: SocketIO;
|
|
16
|
+
loadBalancerApiClient: LoadBalancerApiClient;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
class Network {
|
|
@@ -27,9 +28,9 @@ class Network {
|
|
|
27
28
|
private readonly logger: Logger;
|
|
28
29
|
|
|
29
30
|
constructor(params: NetworkParams) {
|
|
30
|
-
const {
|
|
31
|
-
this.socket =
|
|
32
|
-
this.loadBalancerClient =
|
|
31
|
+
const { socketClient, loadBalancerApiClient } = params;
|
|
32
|
+
this.socket = socketClient;
|
|
33
|
+
this.loadBalancerClient = loadBalancerApiClient;
|
|
33
34
|
this.logger = new Logger('Network');
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -52,7 +52,8 @@ class FramesDroppedIssueDetector implements IssueDetector {
|
|
|
52
52
|
type: IssueType.CPU,
|
|
53
53
|
reason: IssueReason.DecoderCPUThrottling,
|
|
54
54
|
ssrc: streamStats.ssrc,
|
|
55
|
-
debug: `framesDropped: ${Math.round(framesDropped * 100)}
|
|
55
|
+
debug: `framesDropped: ${Math.round(framesDropped * 100)}`
|
|
56
|
+
+ ` , deltaFramesDropped: ${deltaFramesDropped}, deltaFramesReceived: ${deltaFramesReceived}`,
|
|
56
57
|
});
|
|
57
58
|
}
|
|
58
59
|
});
|
|
@@ -33,21 +33,30 @@ class NetworkIssueDetector implements IssueDetector {
|
|
|
33
33
|
|
|
34
34
|
const rtpNetworkStats = inboundRTPStreamsStats.reduce((stats, currentStreamStats) => {
|
|
35
35
|
const previousStreamStats = previousInboundStreamStats.find((stream) => stream.ssrc === currentStreamStats.ssrc);
|
|
36
|
+
|
|
36
37
|
const lastJitterBufferDelay = previousStreamStats?.jitterBufferDelay || 0;
|
|
37
38
|
const lastJitterBufferEmittedCount = previousStreamStats?.jitterBufferEmittedCount || 0;
|
|
38
39
|
const delay = currentStreamStats.jitterBufferDelay - lastJitterBufferDelay;
|
|
39
40
|
const emitted = currentStreamStats.jitterBufferEmittedCount - lastJitterBufferEmittedCount;
|
|
40
|
-
const
|
|
41
|
+
const jitterBufferDelayMs = delay && emitted ? (1e3 * delay) / emitted : 0;
|
|
41
42
|
|
|
42
43
|
return {
|
|
43
|
-
|
|
44
|
+
sumJitter: stats.sumJitter + currentStreamStats.jitter,
|
|
45
|
+
sumJitterBufferDelayMs: stats.sumJitterBufferDelayMs + jitterBufferDelayMs,
|
|
44
46
|
packetsLost: stats.packetsLost + currentStreamStats.packetsLost,
|
|
45
47
|
lastPacketsLost: stats.lastPacketsLost + (previousStreamStats?.packetsLost || 0),
|
|
46
48
|
};
|
|
47
|
-
}, {
|
|
49
|
+
}, {
|
|
50
|
+
sumJitter: 0,
|
|
51
|
+
sumJitterBufferDelayMs: 0,
|
|
52
|
+
packetsLost: 0,
|
|
53
|
+
lastPacketsLost: 0,
|
|
54
|
+
});
|
|
48
55
|
|
|
49
56
|
const rtt = (1e3 * data.connection.currentRoundTripTime) || 0;
|
|
50
|
-
const
|
|
57
|
+
const { sumJitter, sumJitterBufferDelayMs } = rtpNetworkStats;
|
|
58
|
+
const avgJitter = sumJitter / inboundRTPStreamsStats.length;
|
|
59
|
+
const avgJitterBufferDelay = sumJitterBufferDelayMs / inboundRTPStreamsStats.length;
|
|
51
60
|
|
|
52
61
|
const deltaPacketReceived = packetsReceived - lastPacketsReceived;
|
|
53
62
|
const deltaPacketLost = rtpNetworkStats.packetsLost - rtpNetworkStats.lastPacketsLost;
|
|
@@ -57,19 +66,24 @@ class NetworkIssueDetector implements IssueDetector {
|
|
|
57
66
|
: 0;
|
|
58
67
|
|
|
59
68
|
const isHighPacketsLoss = packetsLoss > 5;
|
|
60
|
-
const isHighJitter =
|
|
61
|
-
const isHighRTT = rtt >=
|
|
69
|
+
const isHighJitter = avgJitter >= 200;
|
|
70
|
+
const isHighRTT = rtt >= 250;
|
|
71
|
+
const isHighJitterBufferDelay = avgJitterBufferDelay > 500;
|
|
62
72
|
|
|
63
73
|
const isNetworkIssue = (!isHighPacketsLoss && isHighJitter) || isHighJitter || isHighPacketsLoss;
|
|
64
74
|
const isServerIssue = isHighRTT && !isHighJitter && !isHighPacketsLoss;
|
|
65
75
|
const isNetworkMediaLatencyIssue = isHighPacketsLoss && isHighJitter;
|
|
76
|
+
const isNetworkMediaSyncIssue = isHighJitter && isHighJitterBufferDelay;
|
|
77
|
+
|
|
78
|
+
const debug = `packetLoss: ${packetsLoss}%, jitter: ${avgJitter}, rtt: ${rtt},`
|
|
79
|
+
+ ` jitterBuffer: ${avgJitterBufferDelay}ms`;
|
|
66
80
|
|
|
67
81
|
if (isNetworkIssue) {
|
|
68
82
|
issues.push({
|
|
69
83
|
type: IssueType.Network,
|
|
70
84
|
reason: IssueReason.NetworkQuality,
|
|
71
85
|
iceCandidate: data.connection.local.id,
|
|
72
|
-
debug
|
|
86
|
+
debug,
|
|
73
87
|
});
|
|
74
88
|
}
|
|
75
89
|
|
|
@@ -78,7 +92,7 @@ class NetworkIssueDetector implements IssueDetector {
|
|
|
78
92
|
type: IssueType.Server,
|
|
79
93
|
reason: IssueReason.ServerIssue,
|
|
80
94
|
iceCandidate: data.connection.remote.id,
|
|
81
|
-
debug
|
|
95
|
+
debug,
|
|
82
96
|
});
|
|
83
97
|
}
|
|
84
98
|
|
|
@@ -87,7 +101,16 @@ class NetworkIssueDetector implements IssueDetector {
|
|
|
87
101
|
type: IssueType.Network,
|
|
88
102
|
reason: IssueReason.NetworkMediaLatency,
|
|
89
103
|
iceCandidate: data.connection.local.id,
|
|
90
|
-
debug
|
|
104
|
+
debug,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isNetworkMediaSyncIssue) {
|
|
109
|
+
issues.push({
|
|
110
|
+
type: IssueType.Network,
|
|
111
|
+
reason: IssueReason.NetworkMediaSyncFailure,
|
|
112
|
+
iceCandidate: data.connection.local.id,
|
|
113
|
+
debug,
|
|
91
114
|
});
|
|
92
115
|
}
|
|
93
116
|
|
package/src/index.ts
CHANGED
|
@@ -4,18 +4,20 @@ import {
|
|
|
4
4
|
CreateMicrophoneAudioTrackOptions,
|
|
5
5
|
CreateScreenMediaOptions,
|
|
6
6
|
JoinChannelParams,
|
|
7
|
+
LogMessageHandler,
|
|
7
8
|
Role,
|
|
8
9
|
Track,
|
|
9
|
-
LogMessageHandler,
|
|
10
10
|
} from './types/common';
|
|
11
11
|
import EnhancedEventEmitter from './EnhancedEventEmitter';
|
|
12
12
|
import Engine from './engine';
|
|
13
13
|
import Peer from './engine/Peer';
|
|
14
14
|
import { LoadBalancerApiClientParams } from './engine/network/LoadBalancerClient';
|
|
15
|
+
import DefaultEngineDependenciesFactory from './engine/DefaultEngineDependenciesFactory';
|
|
15
16
|
|
|
16
17
|
type ClientParams = {
|
|
18
|
+
observer?: EnhancedEventEmitter;
|
|
17
19
|
network?: {
|
|
18
|
-
loadbalancer?: LoadBalancerApiClientParams
|
|
20
|
+
loadbalancer?: LoadBalancerApiClientParams;
|
|
19
21
|
},
|
|
20
22
|
onLogMessage?: LogMessageHandler;
|
|
21
23
|
};
|
|
@@ -23,11 +25,14 @@ type ClientParams = {
|
|
|
23
25
|
class Client {
|
|
24
26
|
private readonly engine: Engine;
|
|
25
27
|
|
|
26
|
-
private readonly _observer
|
|
28
|
+
private readonly _observer: EnhancedEventEmitter;
|
|
27
29
|
|
|
28
30
|
constructor(params: ClientParams) {
|
|
29
|
-
const { network, onLogMessage } = params;
|
|
31
|
+
const { observer, network, onLogMessage } = params;
|
|
32
|
+
this._observer = observer ?? new EnhancedEventEmitter();
|
|
33
|
+
const dependenciesFactory = new DefaultEngineDependenciesFactory();
|
|
30
34
|
this.engine = new Engine({
|
|
35
|
+
dependenciesFactory,
|
|
31
36
|
clientEventEmitter: this.observer,
|
|
32
37
|
network: {
|
|
33
38
|
loadbalancer: {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import EnhancedEventEmitter from '../EnhancedEventEmitter';
|
|
2
|
+
import System from '../engine/system';
|
|
3
|
+
import Media from '../engine/media';
|
|
4
|
+
import Network from '../engine/network';
|
|
5
|
+
import Engine from '../engine';
|
|
6
|
+
import ChannelEventHandler from '../engine/handlers/ChannelEventHandler';
|
|
7
|
+
import MediaSoupEventHandler from '../engine/handlers/MediaSoupEventHandler';
|
|
8
|
+
import WebRTCIssueDetector from '../engine/wid/WebRTCIssueDetector';
|
|
9
|
+
import { LoadBalancerApiClientParams } from '../engine/network/LoadBalancerClient';
|
|
10
|
+
import Logger from '../engine/Logger';
|
|
11
|
+
|
|
12
|
+
export interface CreateIssueDetectorParams {
|
|
13
|
+
logger: Logger;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CreateNetworkParams {
|
|
17
|
+
loadbalancer: LoadBalancerApiClientParams;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface EngineDependenciesFactory {
|
|
21
|
+
createSystem: (clientEventEmitter: EnhancedEventEmitter) => System;
|
|
22
|
+
createMedia: () => Media;
|
|
23
|
+
createNetwork: (networkParams: CreateNetworkParams) => Network;
|
|
24
|
+
createChannelEventHandler: (engine: Engine) => ChannelEventHandler;
|
|
25
|
+
createMediaSoupEventHandler: (engine: Engine) => MediaSoupEventHandler;
|
|
26
|
+
createIssueDetector: (params: CreateIssueDetectorParams) => WebRTCIssueDetector;
|
|
27
|
+
}
|