@afosecure/meetingsdk 1.4.5 → 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 +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +252 -75
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +252 -75
- 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,6 +166,9 @@ var VideoSDKCore = class {
|
|
|
166
166
|
this.ws = null;
|
|
167
167
|
this.pubPC = null;
|
|
168
168
|
this.subPC = null;
|
|
169
|
+
this.pendingTracks = /* @__PURE__ */ new Map();
|
|
170
|
+
this.subscriberNegotiating = false;
|
|
171
|
+
this.subscriberOfferQueue = [];
|
|
169
172
|
this.iceServers = [];
|
|
170
173
|
this.lastPong = Date.now();
|
|
171
174
|
this.intentionalDisconnect = false;
|
|
@@ -189,31 +192,84 @@ var VideoSDKCore = class {
|
|
|
189
192
|
this.myId = localStorage.getItem("vsdk_id") || crypto.randomUUID();
|
|
190
193
|
localStorage.setItem("vsdk_id", this.myId);
|
|
191
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
|
+
}
|
|
192
238
|
// ---------------- MEDIA SETUP ----------------
|
|
193
239
|
async initLocal(video, name) {
|
|
194
240
|
this.participantName = name;
|
|
195
241
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
242
|
+
const { stream, camEnabled, micEnabled } = await this.acquireLocalMedia({
|
|
243
|
+
videoConstraints: { width: { ideal: 1280 }, height: { ideal: 720 } },
|
|
244
|
+
audioConstraints: {
|
|
199
245
|
echoCancellation: true,
|
|
200
246
|
noiseSuppression: true,
|
|
201
247
|
autoGainControl: true
|
|
202
248
|
}
|
|
203
249
|
});
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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;
|
|
208
261
|
}
|
|
209
|
-
|
|
262
|
+
const cameraTrack = this.localStream.getVideoTracks()[0] || void 0;
|
|
263
|
+
const audioTrack = this.localStream.getAudioTracks()[0] || void 0;
|
|
210
264
|
this.state.updateLocalParticipant({
|
|
211
265
|
id: this.myId,
|
|
212
266
|
name: this.participantName,
|
|
213
267
|
media: {
|
|
214
268
|
stream: this.localStream,
|
|
215
|
-
|
|
216
|
-
|
|
269
|
+
cameraTrack,
|
|
270
|
+
audioTrack,
|
|
271
|
+
micEnabled,
|
|
272
|
+
camEnabled,
|
|
217
273
|
isScreenSharing: false
|
|
218
274
|
}
|
|
219
275
|
});
|
|
@@ -229,21 +285,38 @@ var VideoSDKCore = class {
|
|
|
229
285
|
throw new Error("roomId and name are required to join meeting");
|
|
230
286
|
}
|
|
231
287
|
this.participantName = name;
|
|
288
|
+
let camEnabled = !videoMuted;
|
|
289
|
+
let micEnabled = !audioMuted;
|
|
232
290
|
if (!this.localStream) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
291
|
+
const acquired = await this.acquireLocalMedia({
|
|
292
|
+
videoConstraints: !videoMuted,
|
|
293
|
+
audioConstraints: !audioMuted
|
|
236
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;
|
|
237
306
|
}
|
|
238
|
-
this.localStream.getAudioTracks().forEach((t) => t.enabled =
|
|
239
|
-
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;
|
|
240
311
|
this.state.updateLocalParticipant({
|
|
241
312
|
id: this.myId,
|
|
242
313
|
name: this.participantName,
|
|
243
314
|
media: {
|
|
244
315
|
stream: this.localStream,
|
|
245
|
-
|
|
246
|
-
|
|
316
|
+
cameraTrack,
|
|
317
|
+
audioTrack,
|
|
318
|
+
micEnabled,
|
|
319
|
+
camEnabled,
|
|
247
320
|
isScreenSharing: false
|
|
248
321
|
}
|
|
249
322
|
});
|
|
@@ -257,9 +330,39 @@ var VideoSDKCore = class {
|
|
|
257
330
|
iceServers: this.iceServers,
|
|
258
331
|
iceTransportPolicy: this.iceTransportPolicy
|
|
259
332
|
});
|
|
333
|
+
const tracksByKind = /* @__PURE__ */ new Map();
|
|
260
334
|
this.localStream.getTracks().forEach((track) => {
|
|
261
|
-
|
|
335
|
+
tracksByKind.set(track.kind, track);
|
|
262
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
|
+
}
|
|
263
366
|
this.pubPC.onicecandidate = (e) => {
|
|
264
367
|
if (e.candidate) {
|
|
265
368
|
this.send({
|
|
@@ -270,8 +373,13 @@ var VideoSDKCore = class {
|
|
|
270
373
|
}
|
|
271
374
|
};
|
|
272
375
|
this.pubPC.onconnectionstatechange = () => {
|
|
273
|
-
console.log(
|
|
376
|
+
console.log("[Publisher PC State]", {
|
|
377
|
+
connection: this.pubPC?.connectionState,
|
|
378
|
+
ice: this.pubPC?.iceConnectionState,
|
|
379
|
+
signaling: this.pubPC?.signalingState
|
|
380
|
+
});
|
|
274
381
|
if (this.pubPC?.connectionState === "failed") {
|
|
382
|
+
console.warn("[Publisher] Connection failed, restarting ICE");
|
|
275
383
|
this.restartPublisherIce();
|
|
276
384
|
}
|
|
277
385
|
};
|
|
@@ -291,44 +399,70 @@ var VideoSDKCore = class {
|
|
|
291
399
|
}
|
|
292
400
|
};
|
|
293
401
|
this.subPC.ontrack = (event) => {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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;
|
|
302
413
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
414
|
+
console.log("[Subscriber] Looking for descriptor with MID:", mid, {
|
|
415
|
+
available: [...this.pendingTracks.keys()]
|
|
416
|
+
});
|
|
417
|
+
const descriptor = this.pendingTracks.get(mid);
|
|
418
|
+
if (!descriptor) {
|
|
419
|
+
console.warn("[Subscriber] No descriptor found for MID:", mid);
|
|
420
|
+
console.log("[Subscriber] Pending tracks:", [
|
|
421
|
+
...this.pendingTracks.entries()
|
|
422
|
+
]);
|
|
307
423
|
return;
|
|
308
424
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
425
|
+
console.log("[Subscriber] Found descriptor:", descriptor);
|
|
426
|
+
this.pendingTracks.delete(mid);
|
|
427
|
+
switch (descriptor.source) {
|
|
428
|
+
case "camera":
|
|
429
|
+
console.log(
|
|
430
|
+
"[Subscriber] Updating camera for:",
|
|
431
|
+
descriptor.publisher_id
|
|
432
|
+
);
|
|
433
|
+
this.state.updateParticipantMedia(descriptor.publisher_id, {
|
|
434
|
+
stream,
|
|
435
|
+
cameraTrack: event.track
|
|
436
|
+
});
|
|
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;
|
|
448
|
+
case "screen":
|
|
449
|
+
console.log(
|
|
450
|
+
"[Subscriber] Updating screen for:",
|
|
451
|
+
descriptor.publisher_id
|
|
452
|
+
);
|
|
453
|
+
this.state.updateParticipantMedia(descriptor.publisher_id, {
|
|
454
|
+
screenStream: stream,
|
|
455
|
+
screenTrack: event.track,
|
|
456
|
+
isScreenSharing: true
|
|
457
|
+
});
|
|
458
|
+
break;
|
|
328
459
|
}
|
|
329
460
|
};
|
|
330
461
|
this.subPC.onconnectionstatechange = () => {
|
|
331
|
-
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
|
+
}
|
|
332
466
|
};
|
|
333
467
|
}
|
|
334
468
|
// ---------------- WEBSOCKET CONNECTION & SIGNALING ----------------
|
|
@@ -391,13 +525,26 @@ var VideoSDKCore = class {
|
|
|
391
525
|
this.setupPublisherPC();
|
|
392
526
|
this.setupSubscriberPC();
|
|
393
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
|
+
}
|
|
394
541
|
this.startHeartbeat();
|
|
395
542
|
this.joinResolver?.();
|
|
396
543
|
this.joinResolver = void 0;
|
|
397
544
|
this.joinRejecter = void 0;
|
|
398
545
|
break;
|
|
399
546
|
}
|
|
400
|
-
case "
|
|
547
|
+
case "PUB_ANSWER": {
|
|
401
548
|
if (this.pubPC) {
|
|
402
549
|
await this.pubPC.setRemoteDescription({
|
|
403
550
|
type: "answer",
|
|
@@ -406,20 +553,27 @@ var VideoSDKCore = class {
|
|
|
406
553
|
}
|
|
407
554
|
break;
|
|
408
555
|
}
|
|
409
|
-
case "
|
|
410
|
-
if (this.subPC) {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
556
|
+
case "SUB_OFFER": {
|
|
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;
|
|
422
575
|
}
|
|
576
|
+
await this.handleSubscriberOffer(msg);
|
|
423
577
|
break;
|
|
424
578
|
}
|
|
425
579
|
case "PUB_ICE": {
|
|
@@ -566,6 +720,31 @@ var VideoSDKCore = class {
|
|
|
566
720
|
}
|
|
567
721
|
}
|
|
568
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
|
+
}
|
|
569
748
|
// ---------------- PUBLISHER RENEGOTIATION ----------------
|
|
570
749
|
async createPublisherOffer() {
|
|
571
750
|
if (!this.pubPC) return;
|
|
@@ -618,6 +797,9 @@ var VideoSDKCore = class {
|
|
|
618
797
|
});
|
|
619
798
|
this.isScreenSharing = true;
|
|
620
799
|
const screenTrack = this.screenStream.getVideoTracks()[0];
|
|
800
|
+
Object.defineProperty(screenTrack, "contentHint", {
|
|
801
|
+
value: "detail"
|
|
802
|
+
});
|
|
621
803
|
if (this.pubPC) {
|
|
622
804
|
this.screenSender = this.pubPC.addTrack(screenTrack, this.screenStream);
|
|
623
805
|
await this.createPublisherOffer();
|
|
@@ -637,7 +819,6 @@ var VideoSDKCore = class {
|
|
|
637
819
|
type: "SCREEN_SHARE_START",
|
|
638
820
|
sender: this.myId,
|
|
639
821
|
room_id: this.room.id,
|
|
640
|
-
camera_id: this.localStream?.id.replace(/[{}]/g, ""),
|
|
641
822
|
stream_id: this.screenStream.id.replace(/[{}]/g, "")
|
|
642
823
|
});
|
|
643
824
|
return this.screenStream;
|
|
@@ -653,17 +834,13 @@ var VideoSDKCore = class {
|
|
|
653
834
|
throw err;
|
|
654
835
|
}
|
|
655
836
|
}
|
|
656
|
-
stopScreenShare() {
|
|
837
|
+
async stopScreenShare() {
|
|
657
838
|
if (!this.screenStream) return;
|
|
658
839
|
this.screenStream.getTracks().forEach((t) => t.stop());
|
|
659
840
|
if (this.pubPC && this.screenSender) {
|
|
660
|
-
|
|
661
|
-
this.pubPC.removeTrack(this.screenSender);
|
|
662
|
-
this.createPublisherOffer();
|
|
663
|
-
} catch (e) {
|
|
664
|
-
console.warn("Failed removing screen sender", e);
|
|
665
|
-
}
|
|
841
|
+
this.pubPC.removeTrack(this.screenSender);
|
|
666
842
|
this.screenSender = null;
|
|
843
|
+
await this.createPublisherOffer();
|
|
667
844
|
}
|
|
668
845
|
this.screenStream = null;
|
|
669
846
|
this.isScreenSharing = false;
|