mitmi 1.1.7 → 1.1.9
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/LICENSE +661 -0
- package/README.md +7 -1
- package/dist/Conference.d.ts +54 -0
- package/dist/Contact.d.ts +20 -1
- package/dist/Session.d.ts +17 -0
- package/dist/Stream.d.ts +46 -19
- package/dist/core/SocketInteraction.d.ts +97 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/mitmi.d.ts +145 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils.d.ts +5 -5
- package/docs/.nojekyll +1 -0
- package/docs/assets/hierarchy.js +1 -0
- package/docs/assets/highlight.css +36 -0
- package/docs/assets/icons.js +18 -0
- package/docs/assets/icons.svg +1 -0
- package/docs/assets/main.js +60 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1633 -0
- package/docs/classes/Conference.html +33 -0
- package/docs/classes/Contact.html +10 -0
- package/docs/classes/DeviceManager.html +9 -0
- package/docs/classes/HelloWorld.html +3 -0
- package/docs/classes/Session.html +12 -0
- package/docs/classes/Stream.html +46 -0
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +35 -0
- package/docs/interfaces/ContactInfo.html +4 -0
- package/docs/interfaces/StreamParams.html +4 -0
- package/docs/modules.html +1 -0
- package/package.json +7 -5
- package/src/Conference.ts +57 -2
- package/src/Contact.ts +20 -1
- package/src/Session.ts +17 -0
- package/src/Stream.ts +72 -38
- package/src/core/SocketInteraction.ts +207 -22
- package/src/index.ts +4 -0
- package/src/utils.ts +12 -6
package/src/Stream.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Conference } from "./Conference";
|
|
2
2
|
import { setLocalStream } from "./utils";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Stream constraints
|
|
6
|
+
*/
|
|
4
7
|
export interface StreamParams {
|
|
5
8
|
audio: boolean;
|
|
6
9
|
video: boolean;
|
|
7
10
|
}
|
|
8
11
|
|
|
12
|
+
/**
|
|
13
|
+
* This class represent a video, camera or screenshare.
|
|
14
|
+
*/
|
|
9
15
|
class Stream {
|
|
10
16
|
mediastream: MediaStream;
|
|
11
17
|
domElement: undefined | HTMLVideoElement;
|
|
@@ -17,10 +23,14 @@ class Stream {
|
|
|
17
23
|
params: StreamParams;
|
|
18
24
|
|
|
19
25
|
/**
|
|
26
|
+
* Create a Stream
|
|
27
|
+
*
|
|
28
|
+
* @param mediastream - The video input to attach to the stream
|
|
29
|
+
* @param ownerId - The ownerId of the mediastream
|
|
30
|
+
* @param ownerName - The owner of the mediastream
|
|
20
31
|
*
|
|
21
|
-
* @param mediastream
|
|
22
|
-
* @param owner "" => ourself, id instead
|
|
23
32
|
*/
|
|
33
|
+
//TODO Enlever le fait que ownerid est une string vide si l'owner est soit meme
|
|
24
34
|
constructor(mediastream: MediaStream, ownerId: string, ownerName: string) {
|
|
25
35
|
this.mediastream = mediastream;
|
|
26
36
|
this.ownerId = ownerId;
|
|
@@ -29,15 +39,24 @@ class Stream {
|
|
|
29
39
|
this.params = { audio: true, video: false };
|
|
30
40
|
}
|
|
31
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Check if the stream is local
|
|
44
|
+
*
|
|
45
|
+
* @returns - True if it's a localstream, false instead
|
|
46
|
+
*/
|
|
32
47
|
isLocal(): boolean {
|
|
33
48
|
return this.ownerId === "";
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @param
|
|
40
|
-
* @
|
|
52
|
+
* Get camera input
|
|
53
|
+
*
|
|
54
|
+
* @param video - Is video enable ?
|
|
55
|
+
* @param audio - Is audio enable ?
|
|
56
|
+
* @param audioDeviceId optional - Use a specific audio device
|
|
57
|
+
* @param videoDeviceId optional - Use a specific video device
|
|
58
|
+
*
|
|
59
|
+
* @returns A stream with your camera.
|
|
41
60
|
*/
|
|
42
61
|
static async getCamera(
|
|
43
62
|
video: boolean,
|
|
@@ -65,82 +84,97 @@ class Stream {
|
|
|
65
84
|
}
|
|
66
85
|
static getScreen() {}
|
|
67
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Attach the stream to your DOM
|
|
89
|
+
*
|
|
90
|
+
* @param domElement - The HTML element to attach the stream
|
|
91
|
+
*/
|
|
68
92
|
attachToElement(domElement: HTMLVideoElement): void {
|
|
69
93
|
this.domElement = domElement;
|
|
70
94
|
domElement.srcObject = this.mediastream;
|
|
71
95
|
|
|
72
96
|
if (this.isLocal()) {
|
|
73
|
-
this.
|
|
97
|
+
this.localMuteAudio();
|
|
74
98
|
}
|
|
75
99
|
}
|
|
76
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Detach the stream to your DOM
|
|
103
|
+
*/
|
|
77
104
|
detachToElement(): void {
|
|
78
105
|
if (!this.domElement) return;
|
|
79
106
|
this.domElement.srcObject = null;
|
|
80
107
|
}
|
|
81
108
|
|
|
82
|
-
|
|
83
|
-
* If the stream is published (so its yours) :
|
|
84
|
-
* - video will be disabled for everyone
|
|
85
|
-
* If the stream is not published (its yours but not publish, or other ppl stream):
|
|
86
|
-
* - video will be disabled for you only
|
|
87
|
-
*/
|
|
88
|
-
muteVideo() {
|
|
89
|
-
this.params.video = false;
|
|
109
|
+
globalMuteVideo() {
|
|
90
110
|
if (this.conferencePublish) {
|
|
91
|
-
|
|
92
|
-
this.conferencePublish.session.socketInteraction.setConstraint(this);
|
|
111
|
+
this.params.video = false;
|
|
112
|
+
//this.conferencePublish.session.socketInteraction.setConstraint(this);
|
|
113
|
+
this.mediastream.getVideoTracks()[0].enabled = false;
|
|
93
114
|
}
|
|
94
|
-
this.mediastream.getVideoTracks()[0].enabled = false;
|
|
95
115
|
}
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
this.params.video = true;
|
|
117
|
+
globalUnmuteVideo() {
|
|
99
118
|
if (this.conferencePublish) {
|
|
100
|
-
|
|
101
|
-
this.conferencePublish.session.socketInteraction.setConstraint(this);
|
|
119
|
+
this.params.video = true;
|
|
120
|
+
//this.conferencePublish.session.socketInteraction.setConstraint(this);
|
|
121
|
+
this.mediastream.getVideoTracks()[0].enabled = true;
|
|
102
122
|
}
|
|
103
|
-
this.mediastream.getVideoTracks()[0].enabled = true;
|
|
104
123
|
}
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
this.params.audio = false;
|
|
125
|
+
globalMuteAudio(): void {
|
|
108
126
|
if (this.conferencePublish) {
|
|
109
|
-
this.
|
|
127
|
+
this.params.audio = false;
|
|
128
|
+
//this.conferencePublish.session.socketInteraction.setConstraint(this);
|
|
129
|
+
this.mediastream.getAudioTracks()[0].enabled = false;
|
|
110
130
|
}
|
|
111
|
-
this.mediastream.getAudioTracks()[0].enabled = false;
|
|
112
131
|
}
|
|
113
132
|
|
|
114
|
-
|
|
115
|
-
this.params.audio = true;
|
|
133
|
+
globalUnmuteAudio(): void {
|
|
116
134
|
if (this.conferencePublish) {
|
|
117
|
-
this.
|
|
135
|
+
this.params.audio = true;
|
|
136
|
+
//this.conferencePublish.session.socketInteraction.setConstraint(this);
|
|
137
|
+
this.mediastream.getAudioTracks()[0].enabled = true;
|
|
118
138
|
}
|
|
119
|
-
this.mediastream.getAudioTracks()[0].enabled = true;
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
/**
|
|
123
|
-
*
|
|
124
|
-
* You need to call here when a localstream is started.
|
|
142
|
+
* Disable local audio
|
|
125
143
|
*/
|
|
126
|
-
|
|
144
|
+
localMuteAudio(): void {
|
|
127
145
|
if (!this.domElement) return;
|
|
128
146
|
|
|
129
|
-
// Important: cette ligne n'affecte pas l'envoi audio
|
|
130
147
|
this.domElement.muted = true;
|
|
131
148
|
this.domElement.volume = 0;
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
/**
|
|
135
|
-
*
|
|
152
|
+
* Enable local audio
|
|
136
153
|
*/
|
|
137
|
-
|
|
154
|
+
localUnmuteAudio(): void {
|
|
138
155
|
if (!this.domElement) return;
|
|
139
156
|
|
|
140
|
-
// Important: cette ligne n'affecte pas l'envoi audio
|
|
141
157
|
this.domElement.muted = false;
|
|
142
158
|
this.domElement.volume = 1;
|
|
143
159
|
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Disable local video
|
|
163
|
+
*/
|
|
164
|
+
localMuteVideo(): void {
|
|
165
|
+
if (!this.domElement) return;
|
|
166
|
+
|
|
167
|
+
this.domElement.pause();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Enable local video
|
|
172
|
+
*/
|
|
173
|
+
async localUnmuteVideo(): Promise<void> {
|
|
174
|
+
if (!this.domElement) return;
|
|
175
|
+
|
|
176
|
+
await this.domElement.play();
|
|
177
|
+
}
|
|
144
178
|
}
|
|
145
179
|
|
|
146
180
|
export { Stream };
|
|
@@ -2,8 +2,11 @@ import { io, Socket } from "socket.io-client";
|
|
|
2
2
|
import { serverUrl } from "../constants";
|
|
3
3
|
import { getCurrentSession } from "../utils";
|
|
4
4
|
import { Stream, StreamParams } from "../Stream";
|
|
5
|
-
import { ContactInfo } from "../
|
|
5
|
+
import { ContactInfo } from "../Contact";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Type of message between server and client
|
|
9
|
+
*/
|
|
7
10
|
interface SocketMessage {
|
|
8
11
|
from: ContactInfo;
|
|
9
12
|
target?: string;
|
|
@@ -16,6 +19,12 @@ interface SocketMessage {
|
|
|
16
19
|
};
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Main interaction between socket and server
|
|
24
|
+
*
|
|
25
|
+
* @private
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
19
28
|
export class SocketInteraction extends EventTarget {
|
|
20
29
|
private socket!: Socket;
|
|
21
30
|
private _userId?: string;
|
|
@@ -24,6 +33,10 @@ export class SocketInteraction extends EventTarget {
|
|
|
24
33
|
private senders: Array<RTCRtpSender> = [];
|
|
25
34
|
|
|
26
35
|
private peerConnections: Record<string, RTCPeerConnection> = {};
|
|
36
|
+
private pendingCandidates: Record<
|
|
37
|
+
string,
|
|
38
|
+
Array<RTCIceCandidate | RTCIceCandidateInit>
|
|
39
|
+
> = {};
|
|
27
40
|
|
|
28
41
|
async init(): Promise<string> {
|
|
29
42
|
this.socket = io(serverUrl, {});
|
|
@@ -45,6 +58,12 @@ export class SocketInteraction extends EventTarget {
|
|
|
45
58
|
return this._userId;
|
|
46
59
|
}
|
|
47
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Publish a Stream into all peers
|
|
63
|
+
*
|
|
64
|
+
* @param stream - Stream to publish
|
|
65
|
+
*/
|
|
66
|
+
//TODO Check to publish multiple stream, ATM => one at the moment
|
|
48
67
|
publish(stream: Stream) {
|
|
49
68
|
this.localStream = stream;
|
|
50
69
|
|
|
@@ -55,6 +74,11 @@ export class SocketInteraction extends EventTarget {
|
|
|
55
74
|
console.log("[RTC] Stream published to all peers");
|
|
56
75
|
}
|
|
57
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Unbuplish stream into all peers
|
|
79
|
+
*
|
|
80
|
+
* @param stream - Stream to unpublish
|
|
81
|
+
*/
|
|
58
82
|
unpublish(stream: Stream) {
|
|
59
83
|
if (this.localStream != stream) throw new Error("this is not your stream");
|
|
60
84
|
this.localStream = undefined;
|
|
@@ -67,8 +91,10 @@ export class SocketInteraction extends EventTarget {
|
|
|
67
91
|
}
|
|
68
92
|
|
|
69
93
|
/**
|
|
94
|
+
* Set constraint to you stream ex : muteAudio
|
|
95
|
+
* Stream have stream.params, use to set constraint on the peer
|
|
70
96
|
*
|
|
71
|
-
* @param stream
|
|
97
|
+
* @param stream - Affected Stream with constraints
|
|
72
98
|
*/
|
|
73
99
|
setConstraint(stream: Stream) {
|
|
74
100
|
if (this.localStream != stream) throw new Error("this is not your stream");
|
|
@@ -91,6 +117,13 @@ export class SocketInteraction extends EventTarget {
|
|
|
91
117
|
console.log("[RTC] Set constraint");
|
|
92
118
|
}
|
|
93
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Attach a stream to a peer
|
|
122
|
+
*
|
|
123
|
+
* @param pc - PeerConnection to attach this.localstream
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
126
|
+
//TODO don't use this.localstream, but a parameter instead ?
|
|
94
127
|
private attachStreamToPeer(pc: RTCPeerConnection) {
|
|
95
128
|
if (!this.localStream) return;
|
|
96
129
|
|
|
@@ -100,6 +133,13 @@ export class SocketInteraction extends EventTarget {
|
|
|
100
133
|
});
|
|
101
134
|
}
|
|
102
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Detach a stream to a peer
|
|
138
|
+
*
|
|
139
|
+
* @param pc - PeerConnection to detach this.localstream
|
|
140
|
+
* @returns
|
|
141
|
+
*/
|
|
142
|
+
//TODO don't use this.localstream, but a parameter instead ?
|
|
103
143
|
private removeStreamToPeer(pc: RTCPeerConnection) {
|
|
104
144
|
if (!this.localStream) return;
|
|
105
145
|
|
|
@@ -108,6 +148,13 @@ export class SocketInteraction extends EventTarget {
|
|
|
108
148
|
});
|
|
109
149
|
}
|
|
110
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Stop sending a specific track
|
|
153
|
+
*
|
|
154
|
+
* @param pc - Affected Peerconnection
|
|
155
|
+
* @param track - Track to disable
|
|
156
|
+
* @returns
|
|
157
|
+
*/
|
|
111
158
|
private disableTrackToPeer(pc: RTCPeerConnection, track: MediaStreamTrack) {
|
|
112
159
|
if (!this.localStream) return;
|
|
113
160
|
|
|
@@ -120,6 +167,13 @@ export class SocketInteraction extends EventTarget {
|
|
|
120
167
|
});
|
|
121
168
|
}
|
|
122
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Activate a track into a peer
|
|
172
|
+
*
|
|
173
|
+
* @param pc - Affected Peerconnection
|
|
174
|
+
* @param track - Track to enable
|
|
175
|
+
* @returns
|
|
176
|
+
*/
|
|
123
177
|
private enableTrackToPeer(pc: RTCPeerConnection, track: MediaStreamTrack) {
|
|
124
178
|
if (!this.localStream) return;
|
|
125
179
|
|
|
@@ -132,6 +186,11 @@ export class SocketInteraction extends EventTarget {
|
|
|
132
186
|
});
|
|
133
187
|
}
|
|
134
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Send a join message to the server
|
|
191
|
+
*
|
|
192
|
+
* @param confId - ID conference
|
|
193
|
+
*/
|
|
135
194
|
register(confId: number) {
|
|
136
195
|
/*if (!this.publishStream) {
|
|
137
196
|
throw new Error("Call publish() before register()");
|
|
@@ -147,7 +206,24 @@ export class SocketInteraction extends EventTarget {
|
|
|
147
206
|
console.log(`[CONF] Join request sent for room ${confId}`);
|
|
148
207
|
}
|
|
149
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Disconnect
|
|
211
|
+
*/
|
|
150
212
|
unregister() {
|
|
213
|
+
//Stop all the track before (release camera and microphone)
|
|
214
|
+
if (this.localStream) {
|
|
215
|
+
this.localStream.mediastream.getTracks().forEach((track) => track.stop());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const sender = getCurrentSession()?.contact!;
|
|
219
|
+
this.sendMessage({
|
|
220
|
+
from: sender.toString(),
|
|
221
|
+
payload: {
|
|
222
|
+
action: "close",
|
|
223
|
+
disconnect: sender.toString().id,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
151
227
|
Object.values(this.peerConnections).forEach((pc) => pc.close());
|
|
152
228
|
this.peerConnections = {};
|
|
153
229
|
|
|
@@ -157,6 +233,11 @@ export class SocketInteraction extends EventTarget {
|
|
|
157
233
|
console.log("[CONF] Unregistered and socket closed");
|
|
158
234
|
}
|
|
159
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Set Listeners
|
|
238
|
+
*
|
|
239
|
+
* @private
|
|
240
|
+
*/
|
|
160
241
|
private setupSocketListeners() {
|
|
161
242
|
this.socket.on("message", async (message: SocketMessage) => {
|
|
162
243
|
if (!this._confId) return;
|
|
@@ -165,44 +246,70 @@ export class SocketInteraction extends EventTarget {
|
|
|
165
246
|
|
|
166
247
|
switch (payload.action) {
|
|
167
248
|
case "join":
|
|
168
|
-
console.log(`[RTC] Join received from ${from}`);
|
|
249
|
+
console.log(`[RTC] Join received from ${from.name}`);
|
|
169
250
|
await this.createPeerConnection(from, true);
|
|
170
251
|
break;
|
|
171
252
|
|
|
172
253
|
case "offer":
|
|
173
|
-
console.log(`[RTC] Offer received from ${from}`);
|
|
254
|
+
console.log(`[RTC] Offer received from ${from.name}`);
|
|
174
255
|
await this.handleOffer(from, payload.sdp!);
|
|
175
256
|
break;
|
|
176
257
|
|
|
177
258
|
case "answer":
|
|
178
|
-
console.log(`[RTC] Answer received from ${from}`);
|
|
259
|
+
console.log(`[RTC] Answer received from ${from.name}`);
|
|
179
260
|
await this.handleAnswer(from, payload.sdp!);
|
|
180
261
|
break;
|
|
181
262
|
|
|
182
263
|
case "ice":
|
|
183
|
-
console.log(`[RTC] ICE received from ${from}`);
|
|
264
|
+
console.log(`[RTC] ICE received from ${from.name}`);
|
|
184
265
|
await this.handleIce(from, payload.candidate!);
|
|
185
266
|
break;
|
|
186
267
|
|
|
187
268
|
case "close":
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
269
|
+
if (from) {
|
|
270
|
+
//Normal way
|
|
271
|
+
console.log(`[RTC] Peer ${from.name} disconnected`);
|
|
272
|
+
this.removePeer(payload.disconnect!);
|
|
273
|
+
this.dispatchEvent(
|
|
274
|
+
new CustomEvent("peopleLeave", {
|
|
275
|
+
detail: { leaveId: payload.disconnect, name: from.name },
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
} else {
|
|
279
|
+
//Message send by server
|
|
280
|
+
//Normal way
|
|
281
|
+
console.log(`[RTC] Someone disconnected`);
|
|
282
|
+
this.removePeer(payload.disconnect!);
|
|
283
|
+
this.dispatchEvent(
|
|
284
|
+
new CustomEvent("peopleLeave", {
|
|
285
|
+
detail: {
|
|
286
|
+
leaveId: payload.disconnect,
|
|
287
|
+
name: "Call conference.leave()",
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
);
|
|
291
|
+
}
|
|
197
292
|
break;
|
|
198
293
|
}
|
|
199
294
|
});
|
|
200
295
|
}
|
|
201
296
|
|
|
202
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Create a Peerconnection with the remote contact
|
|
299
|
+
*
|
|
300
|
+
* @param from - Peerconnection with this contact
|
|
301
|
+
* @param initiator - Are you the applicant ? IF yes send an Offer
|
|
302
|
+
* @returns
|
|
303
|
+
*/
|
|
304
|
+
private async createPeerConnection(
|
|
305
|
+
from: ContactInfo,
|
|
306
|
+
initiator: boolean
|
|
307
|
+
): Promise<RTCPeerConnection> {
|
|
203
308
|
const remoteUserId = from.id;
|
|
204
309
|
const remoteUserName = from.name;
|
|
205
|
-
if (this.peerConnections[remoteUserId])
|
|
310
|
+
if (this.peerConnections[remoteUserId]) {
|
|
311
|
+
return this.peerConnections[remoteUserId]; // Already exist
|
|
312
|
+
}
|
|
206
313
|
|
|
207
314
|
const pc = new RTCPeerConnection();
|
|
208
315
|
this.peerConnections[remoteUserId] = pc;
|
|
@@ -247,14 +354,27 @@ export class SocketInteraction extends EventTarget {
|
|
|
247
354
|
},
|
|
248
355
|
});
|
|
249
356
|
}
|
|
357
|
+
|
|
358
|
+
return pc;
|
|
250
359
|
}
|
|
251
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Event handle the offer
|
|
363
|
+
*
|
|
364
|
+
* @param from - Contact who send the offer
|
|
365
|
+
* @param sdp - Offer
|
|
366
|
+
*/
|
|
252
367
|
private async handleOffer(from: ContactInfo, sdp: RTCSessionDescriptionInit) {
|
|
253
368
|
const remoteUserId = from.id;
|
|
254
|
-
await this.createPeerConnection(from, false);
|
|
369
|
+
const pc = await this.createPeerConnection(from, false);
|
|
255
370
|
|
|
256
|
-
const pc = this.peerConnections[remoteUserId];
|
|
257
|
-
|
|
371
|
+
//const pc = this.peerConnections[remoteUserId];
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
await pc.setRemoteDescription(new RTCSessionDescription(sdp));
|
|
375
|
+
} catch (error) {
|
|
376
|
+
console.error("Remote decription error : ", error);
|
|
377
|
+
}
|
|
258
378
|
|
|
259
379
|
const answer = await pc.createAnswer();
|
|
260
380
|
await pc.setLocalDescription(answer);
|
|
@@ -269,6 +389,9 @@ export class SocketInteraction extends EventTarget {
|
|
|
269
389
|
},
|
|
270
390
|
});
|
|
271
391
|
|
|
392
|
+
// flush pending ICE candidates received before remoteDescription was set
|
|
393
|
+
await this.flushPendingCandidates(remoteUserId);
|
|
394
|
+
|
|
272
395
|
const event: CustomEvent = new CustomEvent("newPeople", {
|
|
273
396
|
detail: {
|
|
274
397
|
contact: from,
|
|
@@ -277,6 +400,13 @@ export class SocketInteraction extends EventTarget {
|
|
|
277
400
|
this.dispatchEvent(event);
|
|
278
401
|
}
|
|
279
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Event handle the enswer
|
|
405
|
+
*
|
|
406
|
+
* @param from - Contact who send tjhe answer
|
|
407
|
+
* @param sdp - The answer
|
|
408
|
+
* @returns
|
|
409
|
+
*/
|
|
280
410
|
private async handleAnswer(
|
|
281
411
|
from: ContactInfo,
|
|
282
412
|
sdp: RTCSessionDescriptionInit
|
|
@@ -286,6 +416,10 @@ export class SocketInteraction extends EventTarget {
|
|
|
286
416
|
if (!pc) return;
|
|
287
417
|
|
|
288
418
|
await pc.setRemoteDescription(new RTCSessionDescription(sdp));
|
|
419
|
+
|
|
420
|
+
// flush pending ICE candidates received before remoteDescription was set
|
|
421
|
+
await this.flushPendingCandidates(remoteUserId);
|
|
422
|
+
|
|
289
423
|
const event: CustomEvent = new CustomEvent("newPeople", {
|
|
290
424
|
detail: {
|
|
291
425
|
contact: from,
|
|
@@ -294,19 +428,70 @@ export class SocketInteraction extends EventTarget {
|
|
|
294
428
|
this.dispatchEvent(event);
|
|
295
429
|
}
|
|
296
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Event when receive ice candidates
|
|
433
|
+
*
|
|
434
|
+
* @param from - Contact
|
|
435
|
+
* @param candidate - Icecandidate
|
|
436
|
+
* @returns
|
|
437
|
+
*/
|
|
297
438
|
private async handleIce(from: ContactInfo, candidate: RTCIceCandidate) {
|
|
298
439
|
const remoteUserId = from.id;
|
|
440
|
+
/*if (!pc) return;
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
await pc.addIceCandidate(candidate);
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error("Ice error : ", error);
|
|
446
|
+
console.error(pc);
|
|
447
|
+
}*/
|
|
299
448
|
const pc = this.peerConnections[remoteUserId];
|
|
300
|
-
if (!pc)
|
|
449
|
+
if (!pc || !pc.remoteDescription || !pc.remoteDescription.type) {
|
|
450
|
+
this.pendingCandidates[remoteUserId] =
|
|
451
|
+
this.pendingCandidates[remoteUserId] || [];
|
|
452
|
+
this.pendingCandidates[remoteUserId].push(candidate);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
301
455
|
|
|
302
|
-
|
|
456
|
+
try {
|
|
457
|
+
await pc.addIceCandidate(candidate);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
console.error("Ice error : ", error);
|
|
460
|
+
}
|
|
303
461
|
}
|
|
304
462
|
|
|
463
|
+
private async flushPendingCandidates(remoteUserId: string): Promise<void> {
|
|
464
|
+
const pc = this.peerConnections[remoteUserId];
|
|
465
|
+
if (!pc) return; // No remote description attach to this user
|
|
466
|
+
|
|
467
|
+
const pendingCandidates = this.pendingCandidates[remoteUserId];
|
|
468
|
+
if (!pendingCandidates || !pendingCandidates.length) return; // No pending candidates
|
|
469
|
+
|
|
470
|
+
for (const pendingCandidate of pendingCandidates) {
|
|
471
|
+
try {
|
|
472
|
+
await pc.addIceCandidate(pendingCandidate);
|
|
473
|
+
} catch (e) {
|
|
474
|
+
console.error("Error adding pending ICE candidate", e);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
delete this.pendingCandidates[remoteUserId];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Event when receive a disconnection message
|
|
482
|
+
*
|
|
483
|
+
* @param remoteId - ID that leave
|
|
484
|
+
*/
|
|
305
485
|
private removePeer(remoteId: string) {
|
|
306
486
|
this.peerConnections[remoteId]?.close();
|
|
307
487
|
delete this.peerConnections[remoteId];
|
|
308
488
|
}
|
|
309
489
|
|
|
490
|
+
/**
|
|
491
|
+
* Send a message on the socket
|
|
492
|
+
*
|
|
493
|
+
* @param msg - Message to send on the socket
|
|
494
|
+
*/
|
|
310
495
|
private sendMessage(msg: SocketMessage) {
|
|
311
496
|
this.socket.emit("message", msg);
|
|
312
497
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,3 +4,7 @@ export { HelloWorld } from "./HelloWorld";
|
|
|
4
4
|
export { Stream } from "./Stream";
|
|
5
5
|
export { Conference } from "./Conference";
|
|
6
6
|
export { DeviceManager } from "./DeviceManager";
|
|
7
|
+
|
|
8
|
+
//Interfaces
|
|
9
|
+
export type { StreamParams } from "./Stream";
|
|
10
|
+
export type { ContactInfo } from "./Contact";
|
package/src/utils.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Session } from "./Session";
|
|
2
2
|
import { Stream } from "./Stream";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Generate a random string
|
|
6
|
+
*
|
|
7
|
+
* @returns - A random string format, ex: de0b1c44-b42e-c617-bcc2-14586c5fffe2
|
|
8
|
+
*/
|
|
4
9
|
function uidGenerator(): String {
|
|
5
10
|
var S4 = function () {
|
|
6
11
|
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
|
@@ -21,6 +26,9 @@ function uidGenerator(): String {
|
|
|
21
26
|
);
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Function for global session
|
|
31
|
+
*/
|
|
24
32
|
let currentSession: Session | undefined = undefined;
|
|
25
33
|
function setCurrentSession(newSession: undefined | Session) {
|
|
26
34
|
currentSession = newSession;
|
|
@@ -29,6 +37,10 @@ function getCurrentSession() {
|
|
|
29
37
|
return currentSession;
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
//TODO Useless ?
|
|
41
|
+
/**
|
|
42
|
+
* Function for localstream
|
|
43
|
+
*/
|
|
32
44
|
let localStream: Stream | undefined = undefined;
|
|
33
45
|
function setLocalStream(newStream: undefined | Stream) {
|
|
34
46
|
localStream = newStream;
|
|
@@ -37,12 +49,6 @@ function getLocalStream() {
|
|
|
37
49
|
return localStream;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
interface ContactInfo {
|
|
41
|
-
id: string;
|
|
42
|
-
name: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export { ContactInfo };
|
|
46
52
|
export { setCurrentSession };
|
|
47
53
|
export { getCurrentSession };
|
|
48
54
|
export { setLocalStream };
|