@voicemaster/core 1.0.5 → 1.0.6
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/index.d.mts +1 -12
- package/dist/index.d.ts +1 -12
- package/dist/index.js +17 -3
- package/dist/index.mjs +17 -3
- package/package.json +37 -38
- package/src/VoiceClient.ts +213 -209
- package/src/index.ts +2 -2
- package/tsconfig.json +15 -15
package/dist/index.d.mts
CHANGED
|
@@ -34,16 +34,5 @@ declare class VoiceClient {
|
|
|
34
34
|
isMuted(): boolean;
|
|
35
35
|
getUserId(): string;
|
|
36
36
|
}
|
|
37
|
-
interface VoiceEvents {
|
|
38
|
-
connected: () => void;
|
|
39
|
-
disconnected: () => void;
|
|
40
|
-
remoteStream: (stream: MediaStream) => void;
|
|
41
|
-
localStream: (stream: MediaStream) => void;
|
|
42
|
-
error: (error: Error) => void;
|
|
43
|
-
userJoined: (userId: string) => void;
|
|
44
|
-
userLeft: (userId: string) => void;
|
|
45
|
-
speaking: (userId: string) => void;
|
|
46
|
-
stoppedSpeaking: (userId: string) => void;
|
|
47
|
-
}
|
|
48
37
|
|
|
49
|
-
export { VoiceClient, type VoiceClientConfig
|
|
38
|
+
export { VoiceClient, type VoiceClientConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -34,16 +34,5 @@ declare class VoiceClient {
|
|
|
34
34
|
isMuted(): boolean;
|
|
35
35
|
getUserId(): string;
|
|
36
36
|
}
|
|
37
|
-
interface VoiceEvents {
|
|
38
|
-
connected: () => void;
|
|
39
|
-
disconnected: () => void;
|
|
40
|
-
remoteStream: (stream: MediaStream) => void;
|
|
41
|
-
localStream: (stream: MediaStream) => void;
|
|
42
|
-
error: (error: Error) => void;
|
|
43
|
-
userJoined: (userId: string) => void;
|
|
44
|
-
userLeft: (userId: string) => void;
|
|
45
|
-
speaking: (userId: string) => void;
|
|
46
|
-
stoppedSpeaking: (userId: string) => void;
|
|
47
|
-
}
|
|
48
37
|
|
|
49
|
-
export { VoiceClient, type VoiceClientConfig
|
|
38
|
+
export { VoiceClient, type VoiceClientConfig };
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,16 @@ var VoiceClient = class {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
|
+
this.pc.onconnectionstatechange = () => {
|
|
99
|
+
if (this.pc?.connectionState === "connected") {
|
|
100
|
+
if (!this.isConnectedFlag) {
|
|
101
|
+
this.isConnectedFlag = true;
|
|
102
|
+
this.emit("connected");
|
|
103
|
+
}
|
|
104
|
+
} else if (this.pc?.connectionState === "disconnected") {
|
|
105
|
+
this.emit("disconnected");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
98
108
|
}
|
|
99
109
|
initWebSocket() {
|
|
100
110
|
const url = `${this.signalingUrl}?userId=${this.userId}&roomId=${this.roomId}`;
|
|
@@ -155,8 +165,10 @@ var VoiceClient = class {
|
|
|
155
165
|
await this.pc.setRemoteDescription(answer);
|
|
156
166
|
}
|
|
157
167
|
async handleCandidate(message) {
|
|
158
|
-
|
|
159
|
-
|
|
168
|
+
if (message.candidate) {
|
|
169
|
+
const candidate = new RTCIceCandidate(message.candidate);
|
|
170
|
+
await this.pc.addIceCandidate(candidate);
|
|
171
|
+
}
|
|
160
172
|
}
|
|
161
173
|
send(data) {
|
|
162
174
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
@@ -169,7 +181,9 @@ var VoiceClient = class {
|
|
|
169
181
|
this.ws.close();
|
|
170
182
|
}
|
|
171
183
|
this.pc?.close();
|
|
172
|
-
this.localStream
|
|
184
|
+
if (this.localStream) {
|
|
185
|
+
this.localStream.getTracks().forEach((track) => track.stop());
|
|
186
|
+
}
|
|
173
187
|
this.isConnectedFlag = false;
|
|
174
188
|
this.emit("disconnected");
|
|
175
189
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -69,6 +69,16 @@ var VoiceClient = class {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
|
+
this.pc.onconnectionstatechange = () => {
|
|
73
|
+
if (this.pc?.connectionState === "connected") {
|
|
74
|
+
if (!this.isConnectedFlag) {
|
|
75
|
+
this.isConnectedFlag = true;
|
|
76
|
+
this.emit("connected");
|
|
77
|
+
}
|
|
78
|
+
} else if (this.pc?.connectionState === "disconnected") {
|
|
79
|
+
this.emit("disconnected");
|
|
80
|
+
}
|
|
81
|
+
};
|
|
72
82
|
}
|
|
73
83
|
initWebSocket() {
|
|
74
84
|
const url = `${this.signalingUrl}?userId=${this.userId}&roomId=${this.roomId}`;
|
|
@@ -129,8 +139,10 @@ var VoiceClient = class {
|
|
|
129
139
|
await this.pc.setRemoteDescription(answer);
|
|
130
140
|
}
|
|
131
141
|
async handleCandidate(message) {
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
if (message.candidate) {
|
|
143
|
+
const candidate = new RTCIceCandidate(message.candidate);
|
|
144
|
+
await this.pc.addIceCandidate(candidate);
|
|
145
|
+
}
|
|
134
146
|
}
|
|
135
147
|
send(data) {
|
|
136
148
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
@@ -143,7 +155,9 @@ var VoiceClient = class {
|
|
|
143
155
|
this.ws.close();
|
|
144
156
|
}
|
|
145
157
|
this.pc?.close();
|
|
146
|
-
this.localStream
|
|
158
|
+
if (this.localStream) {
|
|
159
|
+
this.localStream.getTracks().forEach((track) => track.stop());
|
|
160
|
+
}
|
|
147
161
|
this.isConnectedFlag = false;
|
|
148
162
|
this.emit("disconnected");
|
|
149
163
|
}
|
package/package.json
CHANGED
|
@@ -1,38 +1,37 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@voicemaster/core",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "WebRTC voice communication core library",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.mjs",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.mjs",
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
17
|
-
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
18
|
-
"prepublishOnly": "npm run build"
|
|
19
|
-
},
|
|
20
|
-
"keywords": [
|
|
21
|
-
"webrtc",
|
|
22
|
-
"voice",
|
|
23
|
-
"audio",
|
|
24
|
-
"p2p",
|
|
25
|
-
"typescript"
|
|
26
|
-
],
|
|
27
|
-
"author": "Sergey Minasyan",
|
|
28
|
-
"license": "MIT",
|
|
29
|
-
"dependencies": {
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@voicemaster/core",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "WebRTC voice communication core library",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
17
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"webrtc",
|
|
22
|
+
"voice",
|
|
23
|
+
"audio",
|
|
24
|
+
"p2p",
|
|
25
|
+
"typescript"
|
|
26
|
+
],
|
|
27
|
+
"author": "Sergey Minasyan",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"simple-peer": "^9.11.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/simple-peer": "^9.11.8",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.3.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/VoiceClient.ts
CHANGED
|
@@ -1,210 +1,214 @@
|
|
|
1
|
-
export interface VoiceClientConfig {
|
|
2
|
-
signalingUrl: string;
|
|
3
|
-
roomId: string;
|
|
4
|
-
userId: string;
|
|
5
|
-
autoConnect?: boolean;
|
|
6
|
-
iceServers?: RTCIceServer[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class VoiceClient {
|
|
10
|
-
private ws: WebSocket | null = null;
|
|
11
|
-
private pc: RTCPeerConnection | null = null;
|
|
12
|
-
private localStream: MediaStream | null = null;
|
|
13
|
-
private remoteStream: MediaStream | null = null;
|
|
14
|
-
private eventHandlers: Map<string, Function[]> = new Map();
|
|
15
|
-
private signalingUrl: string;
|
|
16
|
-
private roomId: string;
|
|
17
|
-
private userId: string;
|
|
18
|
-
private iceServers: RTCIceServer[];
|
|
19
|
-
private isConnectedFlag = false;
|
|
20
|
-
|
|
21
|
-
constructor(config: VoiceClientConfig) {
|
|
22
|
-
this.signalingUrl = config.signalingUrl;
|
|
23
|
-
this.roomId = config.roomId;
|
|
24
|
-
this.userId = config.userId;
|
|
25
|
-
this.iceServers = config.iceServers || [
|
|
26
|
-
{ urls: 'stun:stun.l.google.com:19302' }
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
if (config.autoConnect !== false) {
|
|
30
|
-
this.connect();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
on(event: string, callback: Function): void {
|
|
35
|
-
if (!this.eventHandlers.has(event)) {
|
|
36
|
-
this.eventHandlers.set(event, []);
|
|
37
|
-
}
|
|
38
|
-
this.eventHandlers.get(event)!.push(callback);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private emit(event: string, ...args: any[]): void {
|
|
42
|
-
const handlers = this.eventHandlers.get(event);
|
|
43
|
-
if (handlers) {
|
|
44
|
-
handlers.forEach(handler => handler(...args));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async connect(): Promise<void> {
|
|
49
|
-
try {
|
|
50
|
-
await this.initMicrophone();
|
|
51
|
-
this.initPeerConnection();
|
|
52
|
-
this.initWebSocket();
|
|
53
|
-
} catch (error) {
|
|
54
|
-
this.emit('error', error);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private async initMicrophone(): Promise<void> {
|
|
59
|
-
this.localStream = await navigator.mediaDevices.getUserMedia({
|
|
60
|
-
audio: {
|
|
61
|
-
echoCancellation: true,
|
|
62
|
-
noiseSuppression: true,
|
|
63
|
-
autoGainControl: true
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
this.emit('localStream', this.localStream);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private initPeerConnection(): void {
|
|
70
|
-
this.pc = new RTCPeerConnection({ iceServers: this.iceServers });
|
|
71
|
-
|
|
72
|
-
this.localStream?.getTracks().forEach(track => {
|
|
73
|
-
this.pc!.addTrack(track, this.localStream!);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
this.pc.ontrack = (event) => {
|
|
77
|
-
this.remoteStream = event.streams[0];
|
|
78
|
-
this.emit('remoteStream', this.remoteStream);
|
|
79
|
-
if (!this.isConnectedFlag) {
|
|
80
|
-
this.isConnectedFlag = true;
|
|
81
|
-
this.emit('connected');
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
this.pc.onicecandidate = (event) => {
|
|
86
|
-
if (event.candidate) {
|
|
87
|
-
this.send({
|
|
88
|
-
type: 'candidate',
|
|
89
|
-
candidate: event.candidate
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.ws
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
await this.pc!.setLocalDescription(
|
|
150
|
-
this.send({
|
|
151
|
-
type: '
|
|
152
|
-
sdp: this.pc!.localDescription
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
private async
|
|
157
|
-
const
|
|
158
|
-
await this.pc!.setRemoteDescription(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
this.
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
1
|
+
export interface VoiceClientConfig {
|
|
2
|
+
signalingUrl: string;
|
|
3
|
+
roomId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
autoConnect?: boolean;
|
|
6
|
+
iceServers?: RTCIceServer[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class VoiceClient {
|
|
10
|
+
private ws: WebSocket | null = null;
|
|
11
|
+
private pc: RTCPeerConnection | null = null;
|
|
12
|
+
private localStream: MediaStream | null = null;
|
|
13
|
+
private remoteStream: MediaStream | null = null;
|
|
14
|
+
private eventHandlers: Map<string, Function[]> = new Map();
|
|
15
|
+
private signalingUrl: string;
|
|
16
|
+
private roomId: string;
|
|
17
|
+
private userId: string;
|
|
18
|
+
private iceServers: RTCIceServer[];
|
|
19
|
+
private isConnectedFlag = false;
|
|
20
|
+
|
|
21
|
+
constructor(config: VoiceClientConfig) {
|
|
22
|
+
this.signalingUrl = config.signalingUrl;
|
|
23
|
+
this.roomId = config.roomId;
|
|
24
|
+
this.userId = config.userId;
|
|
25
|
+
this.iceServers = config.iceServers || [
|
|
26
|
+
{ urls: 'stun:stun.l.google.com:19302' }
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
if (config.autoConnect !== false) {
|
|
30
|
+
this.connect();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
on(event: string, callback: Function): void {
|
|
35
|
+
if (!this.eventHandlers.has(event)) {
|
|
36
|
+
this.eventHandlers.set(event, []);
|
|
37
|
+
}
|
|
38
|
+
this.eventHandlers.get(event)!.push(callback);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private emit(event: string, ...args: any[]): void {
|
|
42
|
+
const handlers = this.eventHandlers.get(event);
|
|
43
|
+
if (handlers) {
|
|
44
|
+
handlers.forEach(handler => handler(...args));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async connect(): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
await this.initMicrophone();
|
|
51
|
+
this.initPeerConnection();
|
|
52
|
+
this.initWebSocket();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
this.emit('error', error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async initMicrophone(): Promise<void> {
|
|
59
|
+
this.localStream = await navigator.mediaDevices.getUserMedia({
|
|
60
|
+
audio: {
|
|
61
|
+
echoCancellation: true,
|
|
62
|
+
noiseSuppression: true,
|
|
63
|
+
autoGainControl: true
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.emit('localStream', this.localStream);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private initPeerConnection(): void {
|
|
70
|
+
this.pc = new RTCPeerConnection({ iceServers: this.iceServers });
|
|
71
|
+
|
|
72
|
+
this.localStream?.getTracks().forEach(track => {
|
|
73
|
+
this.pc!.addTrack(track, this.localStream!);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this.pc.ontrack = (event) => {
|
|
77
|
+
this.remoteStream = event.streams[0];
|
|
78
|
+
this.emit('remoteStream', this.remoteStream);
|
|
79
|
+
if (!this.isConnectedFlag) {
|
|
80
|
+
this.isConnectedFlag = true;
|
|
81
|
+
this.emit('connected');
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.pc.onicecandidate = (event) => {
|
|
86
|
+
if (event.candidate) {
|
|
87
|
+
this.send({
|
|
88
|
+
type: 'candidate',
|
|
89
|
+
candidate: event.candidate
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
this.pc.onconnectionstatechange = () => {
|
|
95
|
+
if (this.pc?.connectionState === 'connected') {
|
|
96
|
+
if (!this.isConnectedFlag) {
|
|
97
|
+
this.isConnectedFlag = true;
|
|
98
|
+
this.emit('connected');
|
|
99
|
+
}
|
|
100
|
+
} else if (this.pc?.connectionState === 'disconnected') {
|
|
101
|
+
this.emit('disconnected');
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private initWebSocket(): void {
|
|
107
|
+
const url = `${this.signalingUrl}?userId=${this.userId}&roomId=${this.roomId}`;
|
|
108
|
+
this.ws = new WebSocket(url);
|
|
109
|
+
|
|
110
|
+
this.ws.onopen = () => {
|
|
111
|
+
this.send({ type: 'join', roomId: this.roomId, userId: this.userId });
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
this.ws.onmessage = async (event) => {
|
|
115
|
+
const message = JSON.parse(event.data);
|
|
116
|
+
await this.handleSignalingMessage(message);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
this.ws.onclose = () => {
|
|
120
|
+
this.emit('disconnected');
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async handleSignalingMessage(message: any): Promise<void> {
|
|
125
|
+
switch (message.type) {
|
|
126
|
+
case 'user-joined':
|
|
127
|
+
if (message.userId !== this.userId) {
|
|
128
|
+
await this.createOffer();
|
|
129
|
+
this.emit('userJoined', message.userId);
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
case 'offer':
|
|
133
|
+
await this.handleOffer(message);
|
|
134
|
+
break;
|
|
135
|
+
case 'answer':
|
|
136
|
+
await this.handleAnswer(message);
|
|
137
|
+
break;
|
|
138
|
+
case 'candidate':
|
|
139
|
+
await this.handleCandidate(message);
|
|
140
|
+
break;
|
|
141
|
+
case 'user-left':
|
|
142
|
+
this.emit('userLeft', message.userId);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async createOffer(): Promise<void> {
|
|
148
|
+
const offer = await this.pc!.createOffer();
|
|
149
|
+
await this.pc!.setLocalDescription(offer);
|
|
150
|
+
this.send({
|
|
151
|
+
type: 'offer',
|
|
152
|
+
sdp: this.pc!.localDescription
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async handleOffer(message: any): Promise<void> {
|
|
157
|
+
const offer = new RTCSessionDescription(message.sdp);
|
|
158
|
+
await this.pc!.setRemoteDescription(offer);
|
|
159
|
+
const answer = await this.pc!.createAnswer();
|
|
160
|
+
await this.pc!.setLocalDescription(answer);
|
|
161
|
+
this.send({
|
|
162
|
+
type: 'answer',
|
|
163
|
+
sdp: this.pc!.localDescription
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private async handleAnswer(message: any): Promise<void> {
|
|
168
|
+
const answer = new RTCSessionDescription(message.sdp);
|
|
169
|
+
await this.pc!.setRemoteDescription(answer);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async handleCandidate(message: any): Promise<void> {
|
|
173
|
+
if (message.candidate) {
|
|
174
|
+
const candidate = new RTCIceCandidate(message.candidate);
|
|
175
|
+
await this.pc!.addIceCandidate(candidate);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private send(data: any): void {
|
|
180
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
181
|
+
this.ws.send(JSON.stringify(data));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
disconnect(): void {
|
|
186
|
+
if (this.ws) {
|
|
187
|
+
this.send({ type: 'leave', roomId: this.roomId, userId: this.userId });
|
|
188
|
+
this.ws.close();
|
|
189
|
+
}
|
|
190
|
+
this.pc?.close();
|
|
191
|
+
if (this.localStream) {
|
|
192
|
+
this.localStream.getTracks().forEach(track => track.stop());
|
|
193
|
+
}
|
|
194
|
+
this.isConnectedFlag = false;
|
|
195
|
+
this.emit('disconnected');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
toggleMute(): void {
|
|
199
|
+
if (this.localStream) {
|
|
200
|
+
const audioTrack = this.localStream.getAudioTracks()[0];
|
|
201
|
+
if (audioTrack) {
|
|
202
|
+
audioTrack.enabled = !audioTrack.enabled;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
isMuted(): boolean {
|
|
208
|
+
return this.localStream?.getAudioTracks()[0]?.enabled === false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getUserId(): string {
|
|
212
|
+
return this.userId;
|
|
213
|
+
}
|
|
210
214
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { VoiceClient } from './VoiceClient';
|
|
2
|
-
export type { VoiceClientConfig
|
|
1
|
+
export { VoiceClient } from './VoiceClient';
|
|
2
|
+
export type { VoiceClientConfig } from './VoiceClient';
|
package/tsconfig.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ES2020", "DOM"],
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"strict": true,
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"forceConsistentCasingInFileNames": true,
|
|
11
|
-
"resolveJsonModule": true,
|
|
12
|
-
"noEmit": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*"],
|
|
15
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2020", "DOM"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"noEmit": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
16
16
|
}
|