videocall-client-socket 0.1.7 → 0.1.8
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/README.md +26 -17
- package/package.json +1 -1
- package/src/index.d.ts +2 -2
- package/src/index.js +33 -23
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 📞 videocall-socket-client
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A WebRTC client library for multi-user video calls (mesh per room) using [simple-peer](https://www.npmjs.com/package/simple-peer) and [socket.io-client](https://www.npmjs.com/package/socket.io-client).
|
|
4
4
|
|
|
5
5
|
> ✅ Designed to work with [videocall-server](https://github.com/EmersonJaraG28/videocall-server)
|
|
6
6
|
|
|
@@ -30,7 +30,10 @@ This makes SimplePeer available globally in the browser.
|
|
|
30
30
|
import * as VideoClient from "videocall-client-socket";
|
|
31
31
|
import { v4 as uuidv4 } from "uuid";
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// Stable user identity (same user across devices/sessions)
|
|
34
|
+
const userId = "user-123";
|
|
35
|
+
// Unique device/session identity
|
|
36
|
+
const userUUID = uuidv4();
|
|
34
37
|
const channelName = "roomABC";
|
|
35
38
|
|
|
36
39
|
// Step 1: Subscribe to events before anything else
|
|
@@ -39,33 +42,30 @@ VideoClient.on("user-published", (data) => {
|
|
|
39
42
|
const video = document.createElement("video");
|
|
40
43
|
video.srcObject = user.videotrack;
|
|
41
44
|
video.autoplay = true;
|
|
42
|
-
video.id = user.
|
|
45
|
+
video.id = user.userUUID;
|
|
43
46
|
document.body.appendChild(video);
|
|
44
47
|
});
|
|
45
48
|
VideoClient.on("user-unpublished", (data) => {
|
|
46
49
|
const { user, mediaType } = data;
|
|
47
50
|
if (mediaType === "video") {
|
|
48
|
-
const video = document.getElementById(`${user.
|
|
51
|
+
const video = document.getElementById(`${user.userUUID}`);
|
|
49
52
|
if (video) {
|
|
50
53
|
video.remove();
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
});
|
|
54
57
|
VideoClient.on("user-media-toggled", (data) => {
|
|
55
|
-
console.log(data.user.
|
|
58
|
+
console.log(data.user.userId, data.user.userUUID, data.type, data.enabled);
|
|
56
59
|
});
|
|
57
60
|
|
|
58
61
|
// Step 2: Create media stream
|
|
59
|
-
|
|
62
|
+
VideoClient.createMediaStream()
|
|
60
63
|
.then(() => {
|
|
61
64
|
// Step 3: Play local stream in <video> tag
|
|
62
65
|
VideoClient.playVideoTrack("localVideo");
|
|
63
|
-
SocketClient.joinChannel(userId, channelName);
|
|
64
|
-
|
|
65
|
-
// Step 4: Join the signaling channel
|
|
66
|
-
//IMPORTANT: The service repository is located at the bottom.
|
|
67
66
|
VideoClient.setServerURL("http://localhost:3000");
|
|
68
|
-
|
|
67
|
+
// Step 4: Join the signaling channel
|
|
68
|
+
VideoClient.joinChannel(userId, userUUID, channelName);
|
|
69
69
|
})
|
|
70
70
|
.catch((err) => {
|
|
71
71
|
console.error(err);
|
|
@@ -83,7 +83,7 @@ SocketClient.createMediaStream()
|
|
|
83
83
|
| 1️⃣ | `on(...)` | You must subscribe to events **before** joining the channel. |
|
|
84
84
|
| 2️⃣ | `createMediaStream()` | This requests access to camera and microphone. |
|
|
85
85
|
| 3️⃣ | `playVideoTrack(videoElementId)` | This shows your local stream in a `<video>` tag. |
|
|
86
|
-
| 4️⃣ | `joinChannel(userId, room)`
|
|
86
|
+
| 4️⃣ | `joinChannel(userId, userUUID, room)` | Finally, join the signaling server and start peer connections. |
|
|
87
87
|
|
|
88
88
|
> 🧠 Skipping or reordering these steps may cause video/audio to not work properly or event listeners to be missed.
|
|
89
89
|
|
|
@@ -94,7 +94,7 @@ SocketClient.createMediaStream()
|
|
|
94
94
|
### 🔌 Connection
|
|
95
95
|
|
|
96
96
|
- `setServerURL(url: string): void`
|
|
97
|
-
- `joinChannel(userId: string, room: string): void`
|
|
97
|
+
- `joinChannel(userId: string, userUUID: string, room: string): void`
|
|
98
98
|
- `leaveChannel(): void`
|
|
99
99
|
|
|
100
100
|
### 🎥 Media Controls
|
|
@@ -110,9 +110,9 @@ SocketClient.createMediaStream()
|
|
|
110
110
|
|
|
111
111
|
Use `on(event, callback)` and `off(event, callback)`.
|
|
112
112
|
|
|
113
|
-
- `user-published`: `{ user: {
|
|
114
|
-
- `user-unpublished`: `{ user: {
|
|
115
|
-
- `user-media-toggled`: `{ user: {
|
|
113
|
+
- `user-published`: `{ user: { userId, userUUID, videotrack }, mediaType }`
|
|
114
|
+
- `user-unpublished`: `{ user: { userId, userUUID }, mediaType }`
|
|
115
|
+
- `user-media-toggled`: `{ user: { userId, userUUID }, type, enabled }`
|
|
116
116
|
|
|
117
117
|
#### Example:
|
|
118
118
|
|
|
@@ -121,13 +121,22 @@ VideoClient.on("user-published", ({ user }) => {
|
|
|
121
121
|
const video = document.createElement("video");
|
|
122
122
|
video.srcObject = user.videotrack;
|
|
123
123
|
video.autoplay = true;
|
|
124
|
-
video.id = user.
|
|
124
|
+
video.id = user.userUUID;
|
|
125
125
|
document.body.appendChild(video);
|
|
126
126
|
});
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
131
|
+
## 🕸 Multi-user Model
|
|
132
|
+
|
|
133
|
+
- Each room supports multiple participants.
|
|
134
|
+
- WebRTC connections are created in mesh mode (one peer connection per remote participant).
|
|
135
|
+
- `userId` identifies the person.
|
|
136
|
+
- `userUUID` identifies a specific device/session for that person.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
131
140
|
## 🧩 Backend Integration
|
|
132
141
|
|
|
133
142
|
This library is designed to work with the following signaling server:
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ declare module 'videocall-client-socket' {
|
|
|
14
14
|
|
|
15
15
|
export function playVideoTrack(localVideoId: string): void;
|
|
16
16
|
|
|
17
|
-
export function joinChannel(userId: string, channelName: string): void;
|
|
17
|
+
export function joinChannel(userId: string, userUUID: string, channelName: string): void;
|
|
18
18
|
export function leaveChannel(): void;
|
|
19
|
-
}
|
|
19
|
+
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var socket = null;
|
|
|
5
5
|
var mediastream = null;
|
|
6
6
|
var localVideoId = "";
|
|
7
7
|
var userId = "";
|
|
8
|
+
var userUUID = "";
|
|
8
9
|
var serverURL = 'http://localhost:3000';
|
|
9
10
|
let events = {};
|
|
10
11
|
|
|
@@ -90,51 +91,59 @@ export function playVideoTrack(_localVideoId) {
|
|
|
90
91
|
/**
|
|
91
92
|
* Connects to the signaling server and joins a room.
|
|
92
93
|
* @param {string} _userId - Unique identifier for the user.
|
|
94
|
+
* @param {string} _userUUID - Unique identifier for this device/session.
|
|
93
95
|
* @param {string} channelName - The name of the room to join.
|
|
94
96
|
*/
|
|
95
|
-
export function joinChannel(_userId, channelName) {
|
|
97
|
+
export function joinChannel(_userId, _userUUID, channelName) {
|
|
96
98
|
userId = _userId;
|
|
99
|
+
userUUID = _userUUID;
|
|
97
100
|
|
|
98
101
|
socket = io(serverURL, {
|
|
99
|
-
query: { room: channelName, userId }
|
|
102
|
+
query: { room: channelName, userId, userUUID }
|
|
100
103
|
});
|
|
101
104
|
|
|
102
105
|
socket.on('all-users', users => {
|
|
103
|
-
users.forEach(({ userId: remoteUserId, socketId, }) => {
|
|
104
|
-
const peer = createPeer(socket, mediastream, socketId, remoteUserId, true);
|
|
105
|
-
peers[
|
|
106
|
+
users.forEach(({ userId: remoteUserId, userUUID: remoteUserUUID, socketId, }) => {
|
|
107
|
+
const peer = createPeer(socket, mediastream, socketId, remoteUserId, remoteUserUUID, true);
|
|
108
|
+
peers[remoteUserUUID] = peer;
|
|
106
109
|
});
|
|
107
110
|
});
|
|
108
111
|
|
|
109
|
-
socket.on('user-joined', ({ userId: newUserId, socketId: newSocketId }) => {
|
|
110
|
-
console.log("User joined", newUserId, newSocketId);
|
|
112
|
+
socket.on('user-joined', ({ userId: newUserId, userUUID: newUserUUID, socketId: newSocketId }) => {
|
|
113
|
+
// console.log("User joined", newUserId, newUserUUID, newSocketId);
|
|
111
114
|
|
|
112
|
-
const peer = createPeer(socket, mediastream, newSocketId, newUserId, false);
|
|
113
|
-
peers[
|
|
115
|
+
const peer = createPeer(socket, mediastream, newSocketId, newUserId, newUserUUID, false);
|
|
116
|
+
peers[newUserUUID] = peer;
|
|
114
117
|
});
|
|
115
118
|
|
|
116
|
-
socket.on('signal', ({ fromUserId, signal }) => {
|
|
117
|
-
const peer = peers[fromUserId];
|
|
119
|
+
socket.on('signal', ({ fromUserId, fromUserUUID, signal }) => {
|
|
120
|
+
const peer = peers[fromUserUUID || fromUserId];
|
|
118
121
|
if (peer) {
|
|
119
122
|
peer.signal(signal);
|
|
120
123
|
}
|
|
121
124
|
});
|
|
122
125
|
|
|
123
|
-
socket.on('user-left', ({ userId: leftUserId }) => {
|
|
124
|
-
if (peers[
|
|
125
|
-
peers[
|
|
126
|
-
delete peers[
|
|
126
|
+
socket.on('user-left', ({ userId: leftUserId, userUUID: leftUserUUID }) => {
|
|
127
|
+
if (peers[leftUserUUID]) {
|
|
128
|
+
peers[leftUserUUID].destroy();
|
|
129
|
+
delete peers[leftUserUUID];
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
emit("user-unpublished", {
|
|
130
|
-
user: {
|
|
133
|
+
user: {
|
|
134
|
+
userId: leftUserId,
|
|
135
|
+
userUUID: leftUserUUID
|
|
136
|
+
},
|
|
131
137
|
mediaType: "video"
|
|
132
138
|
});
|
|
133
139
|
});
|
|
134
140
|
|
|
135
|
-
socket.on('user-media-toggled', ({ userId, type, enabled }) => {
|
|
141
|
+
socket.on('user-media-toggled', ({ userId, userUUID, type, enabled }) => {
|
|
136
142
|
emit('user-media-toggled', {
|
|
137
|
-
user: {
|
|
143
|
+
user: {
|
|
144
|
+
userId,
|
|
145
|
+
userUUID
|
|
146
|
+
},
|
|
138
147
|
type,
|
|
139
148
|
enabled
|
|
140
149
|
});
|
|
@@ -177,10 +186,11 @@ export function leaveChannel() {
|
|
|
177
186
|
* @param {MediaStream} localStream - The local media stream.
|
|
178
187
|
* @param {string} targetSocketId - The socket ID of the remote user.
|
|
179
188
|
* @param {string} remoteUserId - The ID of the remote user.
|
|
189
|
+
* @param {string} remoteUserUUID - The device/session ID of the remote user.
|
|
180
190
|
* @param {boolean} initiator - Whether this peer initiates the connection.
|
|
181
191
|
* @returns {SimplePeer.Instance} - The created peer.
|
|
182
192
|
*/
|
|
183
|
-
function createPeer(socket, localStream, targetSocketId, remoteUserId, initiator) {
|
|
193
|
+
function createPeer(socket, localStream, targetSocketId, remoteUserId, remoteUserUUID, initiator) {
|
|
184
194
|
const peer = new SimplePeer({
|
|
185
195
|
initiator,
|
|
186
196
|
trickle: false,
|
|
@@ -198,7 +208,8 @@ function createPeer(socket, localStream, targetSocketId, remoteUserId, initiator
|
|
|
198
208
|
|
|
199
209
|
emit("user-published", {
|
|
200
210
|
user: {
|
|
201
|
-
|
|
211
|
+
userId: remoteUserId,
|
|
212
|
+
userUUID: remoteUserUUID,
|
|
202
213
|
videotrack: stream
|
|
203
214
|
},
|
|
204
215
|
mediaType: "video"
|
|
@@ -207,7 +218,6 @@ function createPeer(socket, localStream, targetSocketId, remoteUserId, initiator
|
|
|
207
218
|
setTimeout(() => {
|
|
208
219
|
socket.emit('initial-media-status', {
|
|
209
220
|
targetId: targetSocketId,
|
|
210
|
-
userId: userId,
|
|
211
221
|
audio: isAudioOn(),
|
|
212
222
|
video: isCameraOn()
|
|
213
223
|
});
|
|
@@ -231,7 +241,7 @@ export function toggleCamera(on) {
|
|
|
231
241
|
track.enabled = on;
|
|
232
242
|
});
|
|
233
243
|
if (socket) {
|
|
234
|
-
socket.emit('media-toggle', { userId, type: 'video', enabled: on });
|
|
244
|
+
socket.emit('media-toggle', { userId, userUUID, type: 'video', enabled: on });
|
|
235
245
|
}
|
|
236
246
|
}
|
|
237
247
|
|
|
@@ -254,7 +264,7 @@ export function toggleAudio(on) {
|
|
|
254
264
|
track.enabled = on;
|
|
255
265
|
});
|
|
256
266
|
if (socket) {
|
|
257
|
-
socket.emit('media-toggle', { userId, type: 'audio', enabled: on });
|
|
267
|
+
socket.emit('media-toggle', { userId, userUUID, type: 'audio', enabled: on });
|
|
258
268
|
}
|
|
259
269
|
}
|
|
260
270
|
|