@afosecure/meetingsdk 1.4.6 → 1.4.7
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 +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +230 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +230 -49
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -6,8 +6,8 @@ import { createContext, useContext, useMemo, useRef } from "react";
|
|
|
6
6
|
|
|
7
7
|
// src/config/ws.ts
|
|
8
8
|
var SDK_CONFIG = {
|
|
9
|
-
wsUrl: "wss://
|
|
10
|
-
baseUrl: "https://
|
|
9
|
+
wsUrl: "wss://sfuserver-production.up.railway.app/ws",
|
|
10
|
+
baseUrl: "https://sfuserver-production.up.railway.app"
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/core/MeetingState.ts
|
|
@@ -166,7 +166,9 @@ var VideoSDKCore = class {
|
|
|
166
166
|
this.ws = null;
|
|
167
167
|
this.pubPC = null;
|
|
168
168
|
this.subPC = null;
|
|
169
|
-
this.pendingTracks =
|
|
169
|
+
this.pendingTracks = /* @__PURE__ */ new Map();
|
|
170
|
+
this.subscriberNegotiating = false;
|
|
171
|
+
this.subscriberOfferQueue = [];
|
|
170
172
|
this.iceServers = [];
|
|
171
173
|
this.lastPong = Date.now();
|
|
172
174
|
this.intentionalDisconnect = false;
|
|
@@ -190,33 +192,84 @@ var VideoSDKCore = class {
|
|
|
190
192
|
this.myId = localStorage.getItem("vsdk_id") || crypto.randomUUID();
|
|
191
193
|
localStorage.setItem("vsdk_id", this.myId);
|
|
192
194
|
}
|
|
195
|
+
async acquireLocalMedia(options) {
|
|
196
|
+
const { videoConstraints = true, audioConstraints = true } = options;
|
|
197
|
+
try {
|
|
198
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
199
|
+
video: videoConstraints,
|
|
200
|
+
audio: audioConstraints
|
|
201
|
+
});
|
|
202
|
+
const hasVideo = stream.getVideoTracks().some((t) => t.readyState === "live");
|
|
203
|
+
const hasAudio = stream.getAudioTracks().some((t) => t.readyState === "live");
|
|
204
|
+
return { stream, camEnabled: hasVideo, micEnabled: hasAudio };
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.warn(
|
|
207
|
+
"[VideoSDKCore] Primary getUserMedia failed:",
|
|
208
|
+
err?.name || err?.message
|
|
209
|
+
);
|
|
210
|
+
const isDeviceLocked = err?.name === "NotReadableError" || err?.name === "TrackStartError" || err?.message?.toLowerCase().includes("allocate videosource") || err?.message?.toLowerCase().includes("could not start video source");
|
|
211
|
+
if (isDeviceLocked) {
|
|
212
|
+
console.warn(
|
|
213
|
+
"[VideoSDKCore] Camera is locked by another browser/app. Falling back to Audio-Only."
|
|
214
|
+
);
|
|
215
|
+
try {
|
|
216
|
+
const audioOnlyStream = await navigator.mediaDevices.getUserMedia({
|
|
217
|
+
video: false,
|
|
218
|
+
audio: audioConstraints
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
stream: audioOnlyStream,
|
|
222
|
+
camEnabled: false,
|
|
223
|
+
micEnabled: audioOnlyStream.getAudioTracks().some((t) => t.readyState === "live")
|
|
224
|
+
};
|
|
225
|
+
} catch (audioErr) {
|
|
226
|
+
console.warn(
|
|
227
|
+
"[VideoSDKCore] Audio acquisition also failed. Falling back to View-Only stream."
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
stream: new MediaStream(),
|
|
233
|
+
camEnabled: false,
|
|
234
|
+
micEnabled: false
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
193
238
|
// ---------------- MEDIA SETUP ----------------
|
|
194
239
|
async initLocal(video, name) {
|
|
195
240
|
this.participantName = name;
|
|
196
241
|
try {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
242
|
+
const { stream, camEnabled, micEnabled } = await this.acquireLocalMedia({
|
|
243
|
+
videoConstraints: { width: { ideal: 1280 }, height: { ideal: 720 } },
|
|
244
|
+
audioConstraints: {
|
|
200
245
|
echoCancellation: true,
|
|
201
246
|
noiseSuppression: true,
|
|
202
247
|
autoGainControl: true
|
|
203
248
|
}
|
|
204
249
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
250
|
+
this.localStream = stream;
|
|
251
|
+
console.log(
|
|
252
|
+
"[LOCAL MEDIA]",
|
|
253
|
+
this.localStream.getTracks().map((t) => ({
|
|
254
|
+
kind: t.kind,
|
|
255
|
+
enabled: t.enabled,
|
|
256
|
+
ready: t.readyState
|
|
257
|
+
}))
|
|
258
|
+
);
|
|
259
|
+
if (video && this.localStream.getVideoTracks().length > 0) {
|
|
260
|
+
video.srcObject = this.localStream;
|
|
209
261
|
}
|
|
210
|
-
|
|
262
|
+
const cameraTrack = this.localStream.getVideoTracks()[0] || void 0;
|
|
263
|
+
const audioTrack = this.localStream.getAudioTracks()[0] || void 0;
|
|
211
264
|
this.state.updateLocalParticipant({
|
|
212
265
|
id: this.myId,
|
|
213
266
|
name: this.participantName,
|
|
214
267
|
media: {
|
|
215
268
|
stream: this.localStream,
|
|
216
|
-
cameraTrack
|
|
217
|
-
audioTrack
|
|
218
|
-
micEnabled
|
|
219
|
-
camEnabled
|
|
269
|
+
cameraTrack,
|
|
270
|
+
audioTrack,
|
|
271
|
+
micEnabled,
|
|
272
|
+
camEnabled,
|
|
220
273
|
isScreenSharing: false
|
|
221
274
|
}
|
|
222
275
|
});
|
|
@@ -232,23 +285,38 @@ var VideoSDKCore = class {
|
|
|
232
285
|
throw new Error("roomId and name are required to join meeting");
|
|
233
286
|
}
|
|
234
287
|
this.participantName = name;
|
|
288
|
+
let camEnabled = !videoMuted;
|
|
289
|
+
let micEnabled = !audioMuted;
|
|
235
290
|
if (!this.localStream) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
291
|
+
const acquired = await this.acquireLocalMedia({
|
|
292
|
+
videoConstraints: !videoMuted,
|
|
293
|
+
audioConstraints: !audioMuted
|
|
239
294
|
});
|
|
295
|
+
this.localStream = acquired.stream;
|
|
296
|
+
console.log(
|
|
297
|
+
"[LOCAL MEDIA]",
|
|
298
|
+
this.localStream.getTracks().map((t) => ({
|
|
299
|
+
kind: t.kind,
|
|
300
|
+
enabled: t.enabled,
|
|
301
|
+
ready: t.readyState
|
|
302
|
+
}))
|
|
303
|
+
);
|
|
304
|
+
camEnabled = acquired.camEnabled && !videoMuted;
|
|
305
|
+
micEnabled = acquired.micEnabled && !audioMuted;
|
|
240
306
|
}
|
|
241
|
-
this.localStream.getAudioTracks().forEach((t) => t.enabled =
|
|
242
|
-
this.localStream.getVideoTracks().forEach((t) => t.enabled =
|
|
307
|
+
this.localStream.getAudioTracks().forEach((t) => t.enabled = micEnabled);
|
|
308
|
+
this.localStream.getVideoTracks().forEach((t) => t.enabled = camEnabled);
|
|
309
|
+
const cameraTrack = this.localStream.getVideoTracks()[0] || void 0;
|
|
310
|
+
const audioTrack = this.localStream.getAudioTracks()[0] || void 0;
|
|
243
311
|
this.state.updateLocalParticipant({
|
|
244
312
|
id: this.myId,
|
|
245
313
|
name: this.participantName,
|
|
246
314
|
media: {
|
|
247
315
|
stream: this.localStream,
|
|
248
|
-
cameraTrack
|
|
249
|
-
audioTrack
|
|
250
|
-
micEnabled
|
|
251
|
-
camEnabled
|
|
316
|
+
cameraTrack,
|
|
317
|
+
audioTrack,
|
|
318
|
+
micEnabled,
|
|
319
|
+
camEnabled,
|
|
252
320
|
isScreenSharing: false
|
|
253
321
|
}
|
|
254
322
|
});
|
|
@@ -262,9 +330,39 @@ var VideoSDKCore = class {
|
|
|
262
330
|
iceServers: this.iceServers,
|
|
263
331
|
iceTransportPolicy: this.iceTransportPolicy
|
|
264
332
|
});
|
|
333
|
+
const tracksByKind = /* @__PURE__ */ new Map();
|
|
265
334
|
this.localStream.getTracks().forEach((track) => {
|
|
266
|
-
|
|
335
|
+
tracksByKind.set(track.kind, track);
|
|
267
336
|
});
|
|
337
|
+
const audioTrack = this.localStream.getAudioTracks()[0];
|
|
338
|
+
if (audioTrack) {
|
|
339
|
+
console.log("[Publisher] Adding audio track:", {
|
|
340
|
+
kind: audioTrack.kind,
|
|
341
|
+
id: audioTrack.id,
|
|
342
|
+
enabled: audioTrack.enabled,
|
|
343
|
+
state: audioTrack.readyState
|
|
344
|
+
});
|
|
345
|
+
try {
|
|
346
|
+
this.pubPC?.addTrack(audioTrack, this.localStream);
|
|
347
|
+
} catch (e) {
|
|
348
|
+
console.error("[Publisher] Failed to add audio track:", e);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const videoTrack = this.localStream.getVideoTracks()[0];
|
|
352
|
+
if (videoTrack) {
|
|
353
|
+
console.log("[Publisher] Adding video track:", {
|
|
354
|
+
kind: videoTrack.kind,
|
|
355
|
+
id: videoTrack.id,
|
|
356
|
+
enabled: videoTrack.enabled,
|
|
357
|
+
// Can be false, that's OK
|
|
358
|
+
state: videoTrack.readyState
|
|
359
|
+
});
|
|
360
|
+
try {
|
|
361
|
+
this.pubPC?.addTrack(videoTrack, this.localStream);
|
|
362
|
+
} catch (e) {
|
|
363
|
+
console.error("[Publisher] Failed to add video track:", e);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
268
366
|
this.pubPC.onicecandidate = (e) => {
|
|
269
367
|
if (e.candidate) {
|
|
270
368
|
this.send({
|
|
@@ -275,8 +373,13 @@ var VideoSDKCore = class {
|
|
|
275
373
|
}
|
|
276
374
|
};
|
|
277
375
|
this.pubPC.onconnectionstatechange = () => {
|
|
278
|
-
console.log(
|
|
376
|
+
console.log("[Publisher PC State]", {
|
|
377
|
+
connection: this.pubPC?.connectionState,
|
|
378
|
+
ice: this.pubPC?.iceConnectionState,
|
|
379
|
+
signaling: this.pubPC?.signalingState
|
|
380
|
+
});
|
|
279
381
|
if (this.pubPC?.connectionState === "failed") {
|
|
382
|
+
console.warn("[Publisher] Connection failed, restarting ICE");
|
|
280
383
|
this.restartPublisherIce();
|
|
281
384
|
}
|
|
282
385
|
};
|
|
@@ -296,36 +399,70 @@ var VideoSDKCore = class {
|
|
|
296
399
|
}
|
|
297
400
|
};
|
|
298
401
|
this.subPC.ontrack = (event) => {
|
|
299
|
-
|
|
402
|
+
console.log("[SFU ontrack Event]", {
|
|
403
|
+
kind: event.track.kind,
|
|
404
|
+
id: event.track.id,
|
|
405
|
+
mid: event.transceiver.mid,
|
|
406
|
+
streams: event.streams.length
|
|
407
|
+
});
|
|
408
|
+
const stream = event.streams[0] || new MediaStream([event.track]);
|
|
409
|
+
const mid = event.transceiver.mid;
|
|
410
|
+
if (!mid) {
|
|
411
|
+
console.warn("[Subscriber] Track received without MID", event.track);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
console.log("[Subscriber] Looking for descriptor with MID:", mid, {
|
|
415
|
+
available: [...this.pendingTracks.keys()]
|
|
416
|
+
});
|
|
417
|
+
const descriptor = this.pendingTracks.get(mid);
|
|
300
418
|
if (!descriptor) {
|
|
301
|
-
console.warn("
|
|
419
|
+
console.warn("[Subscriber] No descriptor found for MID:", mid);
|
|
420
|
+
console.log("[Subscriber] Pending tracks:", [
|
|
421
|
+
...this.pendingTracks.entries()
|
|
422
|
+
]);
|
|
302
423
|
return;
|
|
303
424
|
}
|
|
304
|
-
|
|
425
|
+
console.log("[Subscriber] Found descriptor:", descriptor);
|
|
426
|
+
this.pendingTracks.delete(mid);
|
|
305
427
|
switch (descriptor.source) {
|
|
306
428
|
case "camera":
|
|
429
|
+
console.log(
|
|
430
|
+
"[Subscriber] Updating camera for:",
|
|
431
|
+
descriptor.publisher_id
|
|
432
|
+
);
|
|
307
433
|
this.state.updateParticipantMedia(descriptor.publisher_id, {
|
|
308
434
|
stream,
|
|
309
435
|
cameraTrack: event.track
|
|
310
436
|
});
|
|
311
437
|
break;
|
|
438
|
+
case "audio":
|
|
439
|
+
console.log(
|
|
440
|
+
"[Subscriber] Updating audio for:",
|
|
441
|
+
descriptor.publisher_id
|
|
442
|
+
);
|
|
443
|
+
this.state.updateParticipantMedia(descriptor.publisher_id, {
|
|
444
|
+
stream,
|
|
445
|
+
audioTrack: event.track
|
|
446
|
+
});
|
|
447
|
+
break;
|
|
312
448
|
case "screen":
|
|
449
|
+
console.log(
|
|
450
|
+
"[Subscriber] Updating screen for:",
|
|
451
|
+
descriptor.publisher_id
|
|
452
|
+
);
|
|
313
453
|
this.state.updateParticipantMedia(descriptor.publisher_id, {
|
|
314
454
|
screenStream: stream,
|
|
315
455
|
screenTrack: event.track,
|
|
316
456
|
isScreenSharing: true
|
|
317
457
|
});
|
|
318
458
|
break;
|
|
319
|
-
case "audio":
|
|
320
|
-
this.state.updateParticipantMedia(descriptor.publisher_id, {
|
|
321
|
-
stream,
|
|
322
|
-
audioTrack: event.track
|
|
323
|
-
});
|
|
324
|
-
break;
|
|
325
459
|
}
|
|
326
460
|
};
|
|
327
461
|
this.subPC.onconnectionstatechange = () => {
|
|
328
|
-
console.log(
|
|
462
|
+
console.log("[SFU Subscriber PC State]", this.subPC?.connectionState);
|
|
463
|
+
if (this.subPC?.connectionState === "failed") {
|
|
464
|
+
console.warn("Subscriber connection failed");
|
|
465
|
+
}
|
|
329
466
|
};
|
|
330
467
|
}
|
|
331
468
|
// ---------------- WEBSOCKET CONNECTION & SIGNALING ----------------
|
|
@@ -388,6 +525,19 @@ var VideoSDKCore = class {
|
|
|
388
525
|
this.setupPublisherPC();
|
|
389
526
|
this.setupSubscriberPC();
|
|
390
527
|
await this.createPublisherOffer();
|
|
528
|
+
const media = this.state.localParticipant?.media;
|
|
529
|
+
if (media) {
|
|
530
|
+
this.send({
|
|
531
|
+
type: "MEDIA_STATE",
|
|
532
|
+
kind: "audio",
|
|
533
|
+
enabled: !!media.micEnabled
|
|
534
|
+
});
|
|
535
|
+
this.send({
|
|
536
|
+
type: "MEDIA_STATE",
|
|
537
|
+
kind: "video",
|
|
538
|
+
enabled: !!media.camEnabled
|
|
539
|
+
});
|
|
540
|
+
}
|
|
391
541
|
this.startHeartbeat();
|
|
392
542
|
this.joinResolver?.();
|
|
393
543
|
this.joinResolver = void 0;
|
|
@@ -404,20 +554,26 @@ var VideoSDKCore = class {
|
|
|
404
554
|
break;
|
|
405
555
|
}
|
|
406
556
|
case "SUB_OFFER": {
|
|
407
|
-
this.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
557
|
+
if (!this.subPC) {
|
|
558
|
+
console.warn("Subscriber PC not ready");
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const descriptor = msg.track;
|
|
562
|
+
const mid = descriptor.mid;
|
|
563
|
+
console.log(
|
|
564
|
+
"[Signaling] Received SUB_OFFER with descriptor:",
|
|
565
|
+
descriptor
|
|
566
|
+
);
|
|
567
|
+
if (mid) {
|
|
568
|
+
this.pendingTracks.set(mid, descriptor);
|
|
569
|
+
console.log("[Signaling] Stored pending track for MID:", mid);
|
|
570
|
+
}
|
|
571
|
+
if (this.subscriberNegotiating) {
|
|
572
|
+
console.warn("Subscriber negotiating, queueing offer");
|
|
573
|
+
this.subscriberOfferQueue.push(msg);
|
|
574
|
+
return;
|
|
420
575
|
}
|
|
576
|
+
await this.handleSubscriberOffer(msg);
|
|
421
577
|
break;
|
|
422
578
|
}
|
|
423
579
|
case "PUB_ICE": {
|
|
@@ -564,6 +720,31 @@ var VideoSDKCore = class {
|
|
|
564
720
|
}
|
|
565
721
|
}
|
|
566
722
|
}
|
|
723
|
+
async handleSubscriberOffer(msg) {
|
|
724
|
+
if (!this.subPC) return;
|
|
725
|
+
try {
|
|
726
|
+
this.subscriberNegotiating = true;
|
|
727
|
+
await this.subPC.setRemoteDescription({
|
|
728
|
+
type: "offer",
|
|
729
|
+
sdp: msg.payload
|
|
730
|
+
});
|
|
731
|
+
const answer = await this.subPC.createAnswer();
|
|
732
|
+
await this.subPC.setLocalDescription(answer);
|
|
733
|
+
this.send({
|
|
734
|
+
type: "SUB_ANSWER",
|
|
735
|
+
payload: answer.sdp,
|
|
736
|
+
user_id: this.myId
|
|
737
|
+
});
|
|
738
|
+
} catch (err) {
|
|
739
|
+
console.error("[SUB OFFER ERROR]", err);
|
|
740
|
+
} finally {
|
|
741
|
+
this.subscriberNegotiating = false;
|
|
742
|
+
const next = this.subscriberOfferQueue.shift();
|
|
743
|
+
if (next) {
|
|
744
|
+
await this.handleSubscriberOffer(next);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
567
748
|
// ---------------- PUBLISHER RENEGOTIATION ----------------
|
|
568
749
|
async createPublisherOffer() {
|
|
569
750
|
if (!this.pubPC) return;
|