livekit-client 1.4.4 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +2478 -5368
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +3 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +25 -0
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/Checker.d.ts +59 -0
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/publishAudio.d.ts +6 -0
- package/dist/src/connectionHelper/checks/publishAudio.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/publishVideo.d.ts +6 -0
- package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/reconnect.d.ts +6 -0
- package/dist/src/connectionHelper/checks/reconnect.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/turn.d.ts +6 -0
- package/dist/src/connectionHelper/checks/turn.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/webrtc.d.ts +6 -0
- package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/websocket.d.ts +6 -0
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -0
- package/dist/src/index.d.ts +6 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +3 -3
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/options.d.ts +4 -1
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts +4 -4
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +4 -4
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +12 -4
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/DeviceManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +4 -3
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +27 -4
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +9 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +2 -0
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +2 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +1 -1
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +3 -3
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/types.d.ts +3 -3
- package/dist/src/room/track/types.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +13 -0
- package/dist/src/room/types.d.ts.map +1 -0
- package/dist/src/room/utils.d.ts +44 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +86 -0
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +25 -0
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +59 -0
- package/dist/ts4.2/src/connectionHelper/checks/publishAudio.d.ts +6 -0
- package/dist/ts4.2/src/connectionHelper/checks/publishVideo.d.ts +6 -0
- package/dist/ts4.2/src/connectionHelper/checks/reconnect.d.ts +6 -0
- package/dist/ts4.2/src/connectionHelper/checks/turn.d.ts +6 -0
- package/dist/ts4.2/src/connectionHelper/checks/webrtc.d.ts +6 -0
- package/dist/ts4.2/src/connectionHelper/checks/websocket.d.ts +6 -0
- package/dist/ts4.2/src/index.d.ts +31 -0
- package/dist/ts4.2/src/logger.d.ts +26 -0
- package/dist/ts4.2/src/options.d.ts +94 -0
- package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +141 -0
- package/dist/ts4.2/src/proto/livekit_models.d.ts +1421 -0
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +7122 -0
- package/dist/ts4.2/src/room/DefaultReconnectPolicy.d.ts +8 -0
- package/dist/ts4.2/src/room/DeviceManager.d.ts +9 -0
- package/dist/ts4.2/src/room/PCTransport.d.ts +33 -0
- package/dist/ts4.2/src/room/RTCEngine.d.ts +97 -0
- package/dist/ts4.2/src/room/ReconnectPolicy.d.ts +23 -0
- package/dist/ts4.2/src/room/Room.d.ts +220 -0
- package/dist/ts4.2/src/room/defaults.d.ts +8 -0
- package/dist/ts4.2/src/room/errors.d.ts +39 -0
- package/dist/ts4.2/src/room/events.d.ts +426 -0
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +141 -0
- package/dist/ts4.2/src/room/participant/Participant.d.ts +92 -0
- package/dist/ts4.2/src/room/participant/ParticipantTrackPermission.d.ts +26 -0
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +52 -0
- package/dist/ts4.2/src/room/participant/publishUtils.d.ts +19 -0
- package/dist/ts4.2/src/room/stats.d.ts +67 -0
- package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +25 -0
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +42 -0
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +38 -0
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +53 -0
- package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +53 -0
- package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +15 -0
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +61 -0
- package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +52 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +122 -0
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +68 -0
- package/dist/ts4.2/src/room/track/create.d.ts +24 -0
- package/dist/ts4.2/src/room/track/options.d.ts +241 -0
- package/dist/ts4.2/src/room/track/types.d.ts +23 -0
- package/dist/ts4.2/src/room/track/utils.d.ts +14 -0
- package/dist/ts4.2/src/room/types.d.ts +13 -0
- package/dist/ts4.2/src/room/utils.d.ts +79 -0
- package/dist/ts4.2/src/test/MockMediaStreamTrack.d.ts +26 -0
- package/dist/ts4.2/src/test/mocks.d.ts +11 -0
- package/dist/ts4.2/src/version.d.ts +3 -0
- package/package.json +32 -22
- package/src/api/SignalClient.ts +41 -17
- package/src/connectionHelper/ConnectionCheck.ts +90 -0
- package/src/connectionHelper/checks/Checker.ts +164 -0
- package/src/connectionHelper/checks/publishAudio.ts +33 -0
- package/src/connectionHelper/checks/publishVideo.ts +33 -0
- package/src/connectionHelper/checks/reconnect.ts +45 -0
- package/src/connectionHelper/checks/turn.ts +53 -0
- package/src/connectionHelper/checks/webrtc.ts +18 -0
- package/src/connectionHelper/checks/websocket.ts +22 -0
- package/src/index.ts +8 -1
- package/src/options.ts +5 -1
- package/src/proto/livekit_rtc.ts +12 -1
- package/src/room/DeviceManager.ts +0 -17
- package/src/room/RTCEngine.ts +35 -26
- package/src/room/Room.ts +231 -63
- package/src/room/events.ts +9 -0
- package/src/room/participant/LocalParticipant.ts +18 -11
- package/src/room/participant/publishUtils.ts +1 -1
- package/src/room/track/LocalAudioTrack.ts +1 -1
- package/src/room/track/LocalTrack.ts +4 -0
- package/src/room/track/LocalVideoTrack.ts +1 -1
- package/src/room/track/RemoteTrackPublication.ts +20 -0
- package/src/room/track/RemoteVideoTrack.ts +4 -0
- package/src/room/track/Track.ts +1 -0
- package/src/room/types.ts +12 -0
- package/src/room/utils.ts +150 -12
@@ -0,0 +1,90 @@
|
|
1
|
+
import EventEmitter from 'events';
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
3
|
+
|
4
|
+
import { Checker, CheckInfo, CheckStatus, InstantiableCheck } from './checks/Checker';
|
5
|
+
import { PublishAudioCheck } from './checks/publishAudio';
|
6
|
+
import { PublishVideoCheck } from './checks/publishVideo';
|
7
|
+
import { ReconnectCheck } from './checks/reconnect';
|
8
|
+
import { TURNCheck } from './checks/turn';
|
9
|
+
import { WebRTCCheck } from './checks/webrtc';
|
10
|
+
import { WebSocketCheck } from './checks/websocket';
|
11
|
+
|
12
|
+
export type { CheckInfo };
|
13
|
+
|
14
|
+
export class ConnectionCheck extends (EventEmitter as new () => TypedEmitter<ConnectionCheckCallbacks>) {
|
15
|
+
token: string;
|
16
|
+
|
17
|
+
url: string;
|
18
|
+
|
19
|
+
private checkResults: Map<number, CheckInfo> = new Map();
|
20
|
+
|
21
|
+
constructor(url: string, token: string) {
|
22
|
+
super();
|
23
|
+
this.url = url;
|
24
|
+
this.token = token;
|
25
|
+
}
|
26
|
+
|
27
|
+
private getNextCheckId() {
|
28
|
+
const nextId = this.checkResults.size;
|
29
|
+
this.checkResults.set(nextId, {
|
30
|
+
logs: [],
|
31
|
+
status: CheckStatus.IDLE,
|
32
|
+
name: '',
|
33
|
+
description: '',
|
34
|
+
});
|
35
|
+
return nextId;
|
36
|
+
}
|
37
|
+
|
38
|
+
private updateCheck(checkId: number, info: CheckInfo) {
|
39
|
+
this.checkResults.set(checkId, info);
|
40
|
+
this.emit('checkUpdate', checkId, info);
|
41
|
+
}
|
42
|
+
|
43
|
+
isSuccess() {
|
44
|
+
return Array.from(this.checkResults.values()).every((r) => r.status !== CheckStatus.FAILED);
|
45
|
+
}
|
46
|
+
|
47
|
+
getResults() {
|
48
|
+
return Array.from(this.checkResults.values());
|
49
|
+
}
|
50
|
+
|
51
|
+
async createAndRunCheck<T extends Checker>(check: InstantiableCheck<T>) {
|
52
|
+
const checkId = this.getNextCheckId();
|
53
|
+
const test = new check(this.url, this.token);
|
54
|
+
const handleUpdate = (info: CheckInfo) => {
|
55
|
+
this.updateCheck(checkId, info);
|
56
|
+
};
|
57
|
+
test.on('update', handleUpdate);
|
58
|
+
const result = await test.run();
|
59
|
+
test.off('update', handleUpdate);
|
60
|
+
return result;
|
61
|
+
}
|
62
|
+
|
63
|
+
async checkWebsocket() {
|
64
|
+
return this.createAndRunCheck(WebSocketCheck);
|
65
|
+
}
|
66
|
+
|
67
|
+
async checkWebRTC() {
|
68
|
+
return this.createAndRunCheck(WebRTCCheck);
|
69
|
+
}
|
70
|
+
|
71
|
+
async checkTURN() {
|
72
|
+
return this.createAndRunCheck(TURNCheck);
|
73
|
+
}
|
74
|
+
|
75
|
+
async checkReconnect() {
|
76
|
+
return this.createAndRunCheck(ReconnectCheck);
|
77
|
+
}
|
78
|
+
|
79
|
+
async checkPublishAudio() {
|
80
|
+
return this.createAndRunCheck(PublishAudioCheck);
|
81
|
+
}
|
82
|
+
|
83
|
+
async checkPublishVideo() {
|
84
|
+
return this.createAndRunCheck(PublishVideoCheck);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
type ConnectionCheckCallbacks = {
|
89
|
+
checkUpdate: (id: number, info: CheckInfo) => void;
|
90
|
+
};
|
@@ -0,0 +1,164 @@
|
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
3
|
+
import type { RoomConnectOptions, RoomOptions } from '../../options';
|
4
|
+
import Room, { ConnectionState } from '../../room/Room';
|
5
|
+
import type RTCEngine from '../../room/RTCEngine';
|
6
|
+
|
7
|
+
type LogMessage = {
|
8
|
+
level: 'info' | 'warning' | 'error';
|
9
|
+
message: string;
|
10
|
+
};
|
11
|
+
|
12
|
+
export enum CheckStatus {
|
13
|
+
IDLE,
|
14
|
+
RUNNING,
|
15
|
+
SKIPPED,
|
16
|
+
SUCCESS,
|
17
|
+
FAILED,
|
18
|
+
}
|
19
|
+
|
20
|
+
export type CheckInfo = {
|
21
|
+
name: string;
|
22
|
+
logs: Array<LogMessage>;
|
23
|
+
status: CheckStatus;
|
24
|
+
description: string;
|
25
|
+
};
|
26
|
+
|
27
|
+
export interface CheckerOptions {
|
28
|
+
errorsAsWarnings?: boolean;
|
29
|
+
roomOptions?: RoomOptions;
|
30
|
+
connectOptions?: RoomConnectOptions;
|
31
|
+
}
|
32
|
+
|
33
|
+
export abstract class Checker extends (EventEmitter as new () => TypedEmitter<CheckerCallbacks>) {
|
34
|
+
protected url: string;
|
35
|
+
|
36
|
+
protected token: string;
|
37
|
+
|
38
|
+
room: Room;
|
39
|
+
|
40
|
+
connectOptions?: RoomConnectOptions;
|
41
|
+
|
42
|
+
status: CheckStatus = CheckStatus.IDLE;
|
43
|
+
|
44
|
+
logs: Array<LogMessage> = [];
|
45
|
+
|
46
|
+
errorsAsWarnings: boolean = false;
|
47
|
+
|
48
|
+
name: string;
|
49
|
+
|
50
|
+
constructor(url: string, token: string, options: CheckerOptions = {}) {
|
51
|
+
super();
|
52
|
+
this.url = url;
|
53
|
+
this.token = token;
|
54
|
+
this.name = this.constructor.name;
|
55
|
+
this.room = new Room(options.roomOptions);
|
56
|
+
this.connectOptions = options.connectOptions;
|
57
|
+
if (options.errorsAsWarnings) {
|
58
|
+
this.errorsAsWarnings = options.errorsAsWarnings;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
abstract get description(): string;
|
63
|
+
|
64
|
+
protected abstract perform(): Promise<void>;
|
65
|
+
|
66
|
+
async run(onComplete?: () => void) {
|
67
|
+
if (this.status !== CheckStatus.IDLE) {
|
68
|
+
throw Error('check is running already');
|
69
|
+
}
|
70
|
+
this.setStatus(CheckStatus.RUNNING);
|
71
|
+
this.appendMessage(`${this.name} started.`);
|
72
|
+
|
73
|
+
try {
|
74
|
+
await this.perform();
|
75
|
+
} catch (err) {
|
76
|
+
if (err instanceof Error) {
|
77
|
+
if (this.errorsAsWarnings) {
|
78
|
+
this.appendWarning(err.message);
|
79
|
+
} else {
|
80
|
+
this.appendError(err.message);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
await this.disconnect();
|
86
|
+
|
87
|
+
// sleep for a bit to ensure disconnect
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
89
|
+
|
90
|
+
// @ts-ignore
|
91
|
+
if (this.status !== CheckStatus.SKIPPED) {
|
92
|
+
this.setStatus(this.isSuccess() ? CheckStatus.SUCCESS : CheckStatus.FAILED);
|
93
|
+
}
|
94
|
+
|
95
|
+
if (onComplete) {
|
96
|
+
onComplete();
|
97
|
+
}
|
98
|
+
return this.getInfo();
|
99
|
+
}
|
100
|
+
|
101
|
+
protected isSuccess(): boolean {
|
102
|
+
return !this.logs.some((l) => l.level === 'error');
|
103
|
+
}
|
104
|
+
|
105
|
+
protected async connect(): Promise<Room> {
|
106
|
+
if (this.room.state === ConnectionState.Connected) {
|
107
|
+
return this.room;
|
108
|
+
}
|
109
|
+
await this.room.connect(this.url, this.token);
|
110
|
+
return this.room;
|
111
|
+
}
|
112
|
+
|
113
|
+
protected async disconnect() {
|
114
|
+
if (this.room && this.room.state !== ConnectionState.Disconnected) {
|
115
|
+
await this.room.disconnect();
|
116
|
+
// wait for it to go through
|
117
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
protected skip() {
|
122
|
+
this.setStatus(CheckStatus.SKIPPED);
|
123
|
+
}
|
124
|
+
|
125
|
+
protected appendMessage(message: string) {
|
126
|
+
this.logs.push({ level: 'info', message });
|
127
|
+
this.emit('update', this.getInfo());
|
128
|
+
}
|
129
|
+
|
130
|
+
protected appendWarning(message: string) {
|
131
|
+
this.logs.push({ level: 'warning', message });
|
132
|
+
this.emit('update', this.getInfo());
|
133
|
+
}
|
134
|
+
|
135
|
+
protected appendError(message: string) {
|
136
|
+
this.logs.push({ level: 'error', message });
|
137
|
+
this.emit('update', this.getInfo());
|
138
|
+
}
|
139
|
+
|
140
|
+
protected setStatus(status: CheckStatus) {
|
141
|
+
this.status = status;
|
142
|
+
this.emit('update', this.getInfo());
|
143
|
+
}
|
144
|
+
|
145
|
+
protected get engine(): RTCEngine | undefined {
|
146
|
+
return this.room?.engine;
|
147
|
+
}
|
148
|
+
|
149
|
+
getInfo(): CheckInfo {
|
150
|
+
return {
|
151
|
+
logs: this.logs,
|
152
|
+
name: this.name,
|
153
|
+
status: this.status,
|
154
|
+
description: this.description,
|
155
|
+
};
|
156
|
+
}
|
157
|
+
}
|
158
|
+
export type InstantiableCheck<T extends Checker> = {
|
159
|
+
new (url: string, token: string, options?: CheckerOptions): T;
|
160
|
+
};
|
161
|
+
|
162
|
+
type CheckerCallbacks = {
|
163
|
+
update: (info: CheckInfo) => void;
|
164
|
+
};
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { createLocalAudioTrack } from '../../room/track/create';
|
2
|
+
import { Checker } from './Checker';
|
3
|
+
|
4
|
+
export class PublishAudioCheck extends Checker {
|
5
|
+
get description(): string {
|
6
|
+
return 'Can publish audio';
|
7
|
+
}
|
8
|
+
|
9
|
+
async perform(): Promise<void> {
|
10
|
+
const room = await this.connect();
|
11
|
+
|
12
|
+
const track = await createLocalAudioTrack();
|
13
|
+
room.localParticipant.publishTrack(track);
|
14
|
+
// wait for a few seconds to publish
|
15
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
16
|
+
|
17
|
+
// verify RTC stats that it's publishing
|
18
|
+
const stats = await track.sender?.getStats();
|
19
|
+
if (!stats) {
|
20
|
+
throw new Error('Could not get RTCStats');
|
21
|
+
}
|
22
|
+
let numPackets = 0;
|
23
|
+
stats.forEach((stat) => {
|
24
|
+
if (stat.type === 'outbound-rtp' && stat.mediaType === 'audio') {
|
25
|
+
numPackets = stat.packetsSent;
|
26
|
+
}
|
27
|
+
});
|
28
|
+
if (numPackets === 0) {
|
29
|
+
throw new Error('Could not determine packets are sent');
|
30
|
+
}
|
31
|
+
this.appendMessage(`published ${numPackets} audio packets`);
|
32
|
+
}
|
33
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { createLocalVideoTrack } from '../../room/track/create';
|
2
|
+
import { Checker } from './Checker';
|
3
|
+
|
4
|
+
export class PublishVideoCheck extends Checker {
|
5
|
+
get description(): string {
|
6
|
+
return 'Can publish video';
|
7
|
+
}
|
8
|
+
|
9
|
+
async perform(): Promise<void> {
|
10
|
+
const room = await this.connect();
|
11
|
+
|
12
|
+
const track = await createLocalVideoTrack();
|
13
|
+
room.localParticipant.publishTrack(track);
|
14
|
+
// wait for a few seconds to publish
|
15
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
16
|
+
|
17
|
+
// verify RTC stats that it's publishing
|
18
|
+
const stats = await track.sender?.getStats();
|
19
|
+
if (!stats) {
|
20
|
+
throw new Error('Could not get RTCStats');
|
21
|
+
}
|
22
|
+
let numPackets = 0;
|
23
|
+
stats.forEach((stat) => {
|
24
|
+
if (stat.type === 'outbound-rtp' && stat.mediaType === 'video') {
|
25
|
+
numPackets = stat.packetsSent;
|
26
|
+
}
|
27
|
+
});
|
28
|
+
if (numPackets === 0) {
|
29
|
+
throw new Error('Could not determine packets are sent');
|
30
|
+
}
|
31
|
+
this.appendMessage(`published ${numPackets} video packets`);
|
32
|
+
}
|
33
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { RoomEvent } from '../../room/events';
|
2
|
+
import { ConnectionState } from '../../room/Room';
|
3
|
+
import { Checker } from './Checker';
|
4
|
+
|
5
|
+
export class ReconnectCheck extends Checker {
|
6
|
+
get description(): string {
|
7
|
+
return 'Resuming connection after interruption';
|
8
|
+
}
|
9
|
+
|
10
|
+
async perform(): Promise<void> {
|
11
|
+
const room = await this.connect();
|
12
|
+
let reconnectingTriggered = false;
|
13
|
+
let reconnected = false;
|
14
|
+
|
15
|
+
let reconnectResolver: (value: unknown) => void;
|
16
|
+
const reconnectTimeout = new Promise((resolve) => {
|
17
|
+
setTimeout(resolve, 5000);
|
18
|
+
reconnectResolver = resolve;
|
19
|
+
});
|
20
|
+
|
21
|
+
room
|
22
|
+
.on(RoomEvent.Reconnecting, () => {
|
23
|
+
reconnectingTriggered = true;
|
24
|
+
})
|
25
|
+
.on(RoomEvent.Reconnected, () => {
|
26
|
+
reconnected = true;
|
27
|
+
reconnectResolver(true);
|
28
|
+
});
|
29
|
+
|
30
|
+
room.engine.client.ws?.close();
|
31
|
+
const onClose = room.engine.client.onClose;
|
32
|
+
if (onClose) {
|
33
|
+
onClose('');
|
34
|
+
}
|
35
|
+
|
36
|
+
await reconnectTimeout;
|
37
|
+
|
38
|
+
if (!reconnectingTriggered) {
|
39
|
+
throw new Error('Did not attempt to reconnect');
|
40
|
+
} else if (!reconnected || room.state !== ConnectionState.Connected) {
|
41
|
+
this.appendWarning('reconnection is only possible in Redis-based configurations');
|
42
|
+
throw new Error('Not able to reconnect');
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { SignalClient } from '../../api/SignalClient';
|
2
|
+
import { Checker } from './Checker';
|
3
|
+
|
4
|
+
export class TURNCheck extends Checker {
|
5
|
+
get description(): string {
|
6
|
+
return 'Can connect via TURN';
|
7
|
+
}
|
8
|
+
|
9
|
+
async perform(): Promise<void> {
|
10
|
+
const signalClient = new SignalClient();
|
11
|
+
const joinRes = await signalClient.join(this.url, this.token, {
|
12
|
+
autoSubscribe: true,
|
13
|
+
maxRetries: 0,
|
14
|
+
});
|
15
|
+
|
16
|
+
let hasTLS = false;
|
17
|
+
let hasTURN = false;
|
18
|
+
let hasSTUN = false;
|
19
|
+
|
20
|
+
for (let iceServer of joinRes.iceServers) {
|
21
|
+
for (let url of iceServer.urls) {
|
22
|
+
if (url.startsWith('turn:')) {
|
23
|
+
hasTURN = true;
|
24
|
+
hasSTUN = true;
|
25
|
+
} else if (url.startsWith('turns:')) {
|
26
|
+
hasTURN = true;
|
27
|
+
hasSTUN = true;
|
28
|
+
hasTLS = true;
|
29
|
+
}
|
30
|
+
if (url.startsWith('stun:')) {
|
31
|
+
hasSTUN = true;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
if (!hasSTUN) {
|
36
|
+
this.appendWarning('No STUN servers configured on server side.');
|
37
|
+
} else if (hasTURN && !hasTLS) {
|
38
|
+
this.appendWarning('TURN is configured server side, but TURN/TLS is unavailable.');
|
39
|
+
}
|
40
|
+
await signalClient.close();
|
41
|
+
if (this.connectOptions?.rtcConfig?.iceServers || hasTURN) {
|
42
|
+
await this.room!.connect(this.url, this.token, {
|
43
|
+
rtcConfig: {
|
44
|
+
iceTransportPolicy: 'relay',
|
45
|
+
},
|
46
|
+
});
|
47
|
+
} else {
|
48
|
+
this.appendWarning('No TURN servers configured.');
|
49
|
+
this.skip();
|
50
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { Checker } from './Checker';
|
2
|
+
|
3
|
+
export class WebRTCCheck extends Checker {
|
4
|
+
get description(): string {
|
5
|
+
return 'Establishing WebRTC connection';
|
6
|
+
}
|
7
|
+
|
8
|
+
protected async perform(): Promise<void> {
|
9
|
+
try {
|
10
|
+
console.log('initiating room connection');
|
11
|
+
this.room = await this.connect();
|
12
|
+
console.log('now the room is connected');
|
13
|
+
} catch (err) {
|
14
|
+
this.appendWarning('ports need to be open on firewall in order to connect.');
|
15
|
+
throw err;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { SignalClient } from '../../api/SignalClient';
|
2
|
+
import { Checker } from './Checker';
|
3
|
+
|
4
|
+
export class WebSocketCheck extends Checker {
|
5
|
+
get description(): string {
|
6
|
+
return 'Connecting to signal connection via WebSocket';
|
7
|
+
}
|
8
|
+
|
9
|
+
protected async perform(): Promise<void> {
|
10
|
+
if (this.url.startsWith('ws:') || this.url.startsWith('http:')) {
|
11
|
+
this.appendWarning('Server is insecure, clients may block connections to it');
|
12
|
+
}
|
13
|
+
|
14
|
+
let signalClient = new SignalClient();
|
15
|
+
const joinRes = await signalClient.join(this.url, this.token, {
|
16
|
+
autoSubscribe: true,
|
17
|
+
maxRetries: 0,
|
18
|
+
});
|
19
|
+
this.appendMessage(`Connected to server, version ${joinRes.serverVersion}.`);
|
20
|
+
await signalClient.close();
|
21
|
+
}
|
22
|
+
}
|
package/src/index.ts
CHANGED
@@ -13,7 +13,8 @@ import LocalVideoTrack from './room/track/LocalVideoTrack';
|
|
13
13
|
import RemoteAudioTrack from './room/track/RemoteAudioTrack';
|
14
14
|
import RemoteTrack from './room/track/RemoteTrack';
|
15
15
|
import RemoteTrackPublication from './room/track/RemoteTrackPublication';
|
16
|
-
import RemoteVideoTrack
|
16
|
+
import RemoteVideoTrack from './room/track/RemoteVideoTrack';
|
17
|
+
import type { ElementInfo } from './room/track/RemoteVideoTrack';
|
17
18
|
import { TrackPublication } from './room/track/TrackPublication';
|
18
19
|
import {
|
19
20
|
getEmptyAudioStreamTrack,
|
@@ -22,8 +23,11 @@ import {
|
|
22
23
|
supportsAdaptiveStream,
|
23
24
|
supportsAV1,
|
24
25
|
supportsDynacast,
|
26
|
+
createAudioAnalyser,
|
25
27
|
} from './room/utils';
|
26
28
|
|
29
|
+
import type { AudioAnalyserOptions } from './room/utils';
|
30
|
+
|
27
31
|
export * from './options';
|
28
32
|
export * from './room/errors';
|
29
33
|
export * from './room/events';
|
@@ -32,6 +36,7 @@ export * from './room/track/options';
|
|
32
36
|
export * from './room/track/Track';
|
33
37
|
export * from './room/track/types';
|
34
38
|
export * from './version';
|
39
|
+
export * from './connectionHelper/ConnectionCheck';
|
35
40
|
export {
|
36
41
|
setLogLevel,
|
37
42
|
setLogExtension,
|
@@ -41,6 +46,8 @@ export {
|
|
41
46
|
supportsAdaptiveStream,
|
42
47
|
supportsDynacast,
|
43
48
|
supportsAV1,
|
49
|
+
createAudioAnalyser,
|
50
|
+
AudioAnalyserOptions,
|
44
51
|
LogLevel,
|
45
52
|
Room,
|
46
53
|
ConnectionState,
|
package/src/options.ts
CHANGED
@@ -7,6 +7,10 @@ import type {
|
|
7
7
|
} from './room/track/options';
|
8
8
|
import type { AdaptiveStreamSettings } from './room/track/types';
|
9
9
|
|
10
|
+
export interface WebAudioSettings {
|
11
|
+
audioContext: AudioContext;
|
12
|
+
}
|
13
|
+
|
10
14
|
/**
|
11
15
|
* @internal
|
12
16
|
*/
|
@@ -72,7 +76,7 @@ export interface InternalRoomOptions {
|
|
72
76
|
* experimental flag, mix all audio tracks in web audio
|
73
77
|
*/
|
74
78
|
|
75
|
-
expWebAudioMix: boolean;
|
79
|
+
expWebAudioMix: boolean | WebAudioSettings;
|
76
80
|
}
|
77
81
|
|
78
82
|
/**
|
package/src/proto/livekit_rtc.ts
CHANGED
@@ -267,6 +267,8 @@ export interface UpdateTrackSettings {
|
|
267
267
|
width: number;
|
268
268
|
/** for video, height to receive */
|
269
269
|
height: number;
|
270
|
+
/** for video, frame rate to receive */
|
271
|
+
fps: number;
|
270
272
|
}
|
271
273
|
|
272
274
|
export interface LeaveRequest {
|
@@ -1843,7 +1845,7 @@ export const UpdateSubscription = {
|
|
1843
1845
|
};
|
1844
1846
|
|
1845
1847
|
function createBaseUpdateTrackSettings(): UpdateTrackSettings {
|
1846
|
-
return { trackSids: [], disabled: false, quality: 0, width: 0, height: 0 };
|
1848
|
+
return { trackSids: [], disabled: false, quality: 0, width: 0, height: 0, fps: 0 };
|
1847
1849
|
}
|
1848
1850
|
|
1849
1851
|
export const UpdateTrackSettings = {
|
@@ -1863,6 +1865,9 @@ export const UpdateTrackSettings = {
|
|
1863
1865
|
if (message.height !== 0) {
|
1864
1866
|
writer.uint32(48).uint32(message.height);
|
1865
1867
|
}
|
1868
|
+
if (message.fps !== 0) {
|
1869
|
+
writer.uint32(56).uint32(message.fps);
|
1870
|
+
}
|
1866
1871
|
return writer;
|
1867
1872
|
},
|
1868
1873
|
|
@@ -1888,6 +1893,9 @@ export const UpdateTrackSettings = {
|
|
1888
1893
|
case 6:
|
1889
1894
|
message.height = reader.uint32();
|
1890
1895
|
break;
|
1896
|
+
case 7:
|
1897
|
+
message.fps = reader.uint32();
|
1898
|
+
break;
|
1891
1899
|
default:
|
1892
1900
|
reader.skipType(tag & 7);
|
1893
1901
|
break;
|
@@ -1903,6 +1911,7 @@ export const UpdateTrackSettings = {
|
|
1903
1911
|
quality: isSet(object.quality) ? videoQualityFromJSON(object.quality) : 0,
|
1904
1912
|
width: isSet(object.width) ? Number(object.width) : 0,
|
1905
1913
|
height: isSet(object.height) ? Number(object.height) : 0,
|
1914
|
+
fps: isSet(object.fps) ? Number(object.fps) : 0,
|
1906
1915
|
};
|
1907
1916
|
},
|
1908
1917
|
|
@@ -1917,6 +1926,7 @@ export const UpdateTrackSettings = {
|
|
1917
1926
|
message.quality !== undefined && (obj.quality = videoQualityToJSON(message.quality));
|
1918
1927
|
message.width !== undefined && (obj.width = Math.round(message.width));
|
1919
1928
|
message.height !== undefined && (obj.height = Math.round(message.height));
|
1929
|
+
message.fps !== undefined && (obj.fps = Math.round(message.fps));
|
1920
1930
|
return obj;
|
1921
1931
|
},
|
1922
1932
|
|
@@ -1927,6 +1937,7 @@ export const UpdateTrackSettings = {
|
|
1927
1937
|
message.quality = object.quality ?? 0;
|
1928
1938
|
message.width = object.width ?? 0;
|
1929
1939
|
message.height = object.height ?? 0;
|
1940
|
+
message.fps = object.fps ?? 0;
|
1930
1941
|
return message;
|
1931
1942
|
},
|
1932
1943
|
};
|
@@ -65,23 +65,6 @@ export default class DeviceManager {
|
|
65
65
|
devices = devices.filter((device) => device.kind === kind);
|
66
66
|
}
|
67
67
|
|
68
|
-
// Chrome returns 'default' devices, we would filter them out, but put the default
|
69
|
-
// device at first
|
70
|
-
// we would only do this if there are more than 1 device though
|
71
|
-
if (devices.length > 1 && devices[0].deviceId === defaultId) {
|
72
|
-
// find another device with matching group id, and move that to 0
|
73
|
-
const defaultDevice = devices[0];
|
74
|
-
for (let i = 1; i < devices.length; i += 1) {
|
75
|
-
if (devices[i].groupId === defaultDevice.groupId) {
|
76
|
-
const temp = devices[0];
|
77
|
-
devices[0] = devices[i];
|
78
|
-
devices[i] = temp;
|
79
|
-
break;
|
80
|
-
}
|
81
|
-
}
|
82
|
-
return devices.filter((device) => device !== defaultDevice);
|
83
|
-
}
|
84
|
-
|
85
68
|
return devices;
|
86
69
|
}
|
87
70
|
|