@afosecure/meetingsdk 1.1.4 → 1.1.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.mjs CHANGED
@@ -6,7 +6,7 @@ import { createContext, useContext, useMemo, useRef } from "react";
6
6
 
7
7
  // src/config/ws.ts
8
8
  var SDK_CONFIG = {
9
- wsUrl: "wss://rust-video-server-sfyf.onrender.com/ws"
9
+ wsUrl: "ws://localhost:8080/ws"
10
10
  };
11
11
 
12
12
  // src/core/MeetingState.ts
@@ -168,8 +168,12 @@ var VideoSDKCore = class {
168
168
  this.peers = {};
169
169
  this.initiators = /* @__PURE__ */ new Set();
170
170
  this.lastPong = Date.now();
171
+ this.iceServers = [];
171
172
  this.intentionalDisconnect = false;
172
- this.roomId = null;
173
+ this.room = {
174
+ id: null,
175
+ name: null
176
+ };
173
177
  this.localStream = null;
174
178
  this.screenStream = null;
175
179
  this.isScreenSharing = false;
@@ -189,7 +193,7 @@ var VideoSDKCore = class {
189
193
  code,
190
194
  message,
191
195
  raw,
192
- roomId: this.roomId,
196
+ roomId: this.room.id,
193
197
  userId: this.myId,
194
198
  recoverable
195
199
  };
@@ -201,28 +205,43 @@ var VideoSDKCore = class {
201
205
  // ---------------- STREAM ----------------
202
206
  async initLocal(video, name) {
203
207
  this.participantName = name;
204
- if (!this.localStream) {
208
+ try {
205
209
  this.localStream = await navigator.mediaDevices.getUserMedia({
206
- video: true,
207
- audio: true
210
+ video: {
211
+ width: { ideal: 1280 },
212
+ height: { ideal: 720 }
213
+ },
214
+ audio: {
215
+ echoCancellation: true,
216
+ noiseSuppression: true,
217
+ autoGainControl: true
218
+ }
208
219
  });
209
- }
210
- video.srcObject = this.localStream;
211
- this.state.updateLocalParticipant({
212
- id: this.myId,
213
- name: this.participantName,
214
- media: {
215
- stream: this.localStream,
216
- micEnabled: true,
217
- camEnabled: true,
218
- isScreenSharing: false
220
+ const hasVideo = this.localStream.getVideoTracks().some((t) => t.readyState === "live");
221
+ const hasAudio = this.localStream.getAudioTracks().some((t) => t.readyState === "live");
222
+ if (!hasVideo || !hasAudio) {
223
+ throw new Error(`Missing tracks: video=${hasVideo}, audio=${hasAudio}`);
219
224
  }
220
- });
221
- this.state.localStream = this.localStream;
225
+ video.srcObject = this.localStream;
226
+ this.state.updateLocalParticipant({
227
+ id: this.myId,
228
+ name: this.participantName,
229
+ media: {
230
+ stream: this.localStream,
231
+ micEnabled: true,
232
+ camEnabled: true,
233
+ isScreenSharing: false
234
+ }
235
+ });
236
+ this.state.localStream = this.localStream;
237
+ } catch (err) {
238
+ this.emitError("GET_USER_MEDIA_FAILED", err?.message, err, false);
239
+ throw err;
240
+ }
222
241
  }
223
242
  // ---------------- CONNECT ----------------
224
243
  async connect(roomId, name) {
225
- this.roomId = roomId;
244
+ this.room.id = roomId;
226
245
  this.reset();
227
246
  return new Promise((resolve, reject) => {
228
247
  this.joinResolver = resolve;
@@ -291,8 +310,8 @@ var VideoSDKCore = class {
291
310
  await this.connect(roomId, name);
292
311
  }
293
312
  /** Expose the roomId without making it fully public */
294
- getMeetingId() {
295
- return this.roomId;
313
+ getMeeting() {
314
+ return this.room;
296
315
  }
297
316
  toggleMic() {
298
317
  const mediaState = this.state.localParticipant?.media;
@@ -333,12 +352,12 @@ var VideoSDKCore = class {
333
352
  });
334
353
  }
335
354
  scheduleReconnect() {
336
- if (!this.roomId) return;
355
+ if (!this.room.id) return;
337
356
  const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
338
357
  clearTimeout(this.reconnectTimer);
339
358
  this.reconnectTimer = window.setTimeout(async () => {
340
359
  try {
341
- await this.connect(this.roomId, this.participantName);
360
+ await this.connect(this.room.id, this.participantName);
342
361
  this.reconnectAttempts = 0;
343
362
  } catch {
344
363
  this.reconnectAttempts++;
@@ -379,10 +398,18 @@ var VideoSDKCore = class {
379
398
  this.lastPong = Date.now();
380
399
  break;
381
400
  case "OFFER":
401
+ console.log("[Offer] Received from", msg.sender, {
402
+ sdp: msg.payload.substring(0, 200)
403
+ });
382
404
  await this.handleOffer(msg.payload, msg.sender);
383
405
  break;
384
406
  case "ANSWER": {
385
407
  const pc = this.peers[msg.sender];
408
+ console.log("[Answer] Received from", msg.sender, {
409
+ signalingState: pc?.signalingState,
410
+ iceConnectionState: pc?.iceConnectionState,
411
+ connectionState: pc?.connectionState
412
+ });
386
413
  if (!pc) return;
387
414
  if (pc.signalingState !== "have-local-offer") {
388
415
  console.warn(
@@ -436,10 +463,16 @@ var VideoSDKCore = class {
436
463
  if (!p?.id || p.id === this.myId) continue;
437
464
  this.state.addParticipant(p);
438
465
  this.events.onUserJoined?.(p);
439
- await this.createOffer(p.id);
466
+ if (this.shouldInitiate(p.id)) {
467
+ await this.createOffer(p.id);
468
+ }
440
469
  }
441
470
  break;
442
471
  case "JOINED": {
472
+ if (msg.iceServers) {
473
+ this.iceServers = msg.iceServers;
474
+ }
475
+ this.room.name = msg.room_name;
443
476
  this.intentionalDisconnect = false;
444
477
  this.reconnectAttempts = 0;
445
478
  this.startHeartbeat();
@@ -453,7 +486,9 @@ var VideoSDKCore = class {
453
486
  if (!p?.id || p.id === this.myId) return;
454
487
  this.state.addParticipant(p);
455
488
  this.events.onUserJoined?.(p);
456
- await this.createOffer(p.id);
489
+ if (this.shouldInitiate(p.id)) {
490
+ await this.createOffer(p.id);
491
+ }
457
492
  break;
458
493
  }
459
494
  case "USER_LEFT":
@@ -528,6 +563,11 @@ var VideoSDKCore = class {
528
563
  // ---------------- PEER ----------------
529
564
  createPeer(id) {
530
565
  if (!this.localStream) throw new Error("No local stream");
566
+ if (!this.iceServers || this.iceServers.length === 0) {
567
+ throw new Error(
568
+ "ICE Servers not configured. Backend must provide iceServers on JOIN."
569
+ );
570
+ }
531
571
  console.log(
532
572
  "Adding tracks",
533
573
  this.localStream.getTracks().map((t) => ({
@@ -537,28 +577,15 @@ var VideoSDKCore = class {
537
577
  }))
538
578
  );
539
579
  const pc = new RTCPeerConnection({
540
- iceServers: [
541
- {
542
- urls: [
543
- "stun:stun1.1.google.com:19302",
544
- "stun:stun2.1.google.com:19302"
545
- ]
546
- }
547
- ],
548
- iceCandidatePoolSize: 3
580
+ iceServers: this.iceServers
549
581
  });
550
582
  pc.ontrack = (event) => {
551
- console.log(`Track received: ${event.track.kind}`, {
552
- trackId: event.track.id,
553
- streamCount: event.streams.length,
554
- streamTrackCount: event.streams[0]?.getTracks().length
555
- });
556
583
  const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
557
584
  const participant = this.state.getParticipant(id);
558
585
  const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
559
- if (event.track.kind === "video") {
586
+ if (event.track.muted) {
560
587
  event.track.onunmute = () => {
561
- console.log("Video track unmuted for", id);
588
+ console.log(`${event.track.kind} track unmuted for ${id}`);
562
589
  };
563
590
  }
564
591
  if (isScreenStream) {
@@ -615,18 +642,17 @@ var VideoSDKCore = class {
615
642
  }
616
643
  // ---------------- OFFER ----------------
617
644
  async createOffer(id, isRenegotiation = false) {
645
+ if (!isRenegotiation && !this.shouldInitiate(id)) {
646
+ console.debug(
647
+ `[Offer] ${id} should initiate (${id} > ${this.myId}), skipping`
648
+ );
649
+ return;
650
+ }
618
651
  if (!isRenegotiation && this.initiators.has(id)) {
619
652
  console.debug(
620
653
  `[Offer] Already initiating with ${id}, skipping duplicate`
621
654
  );
622
- }
623
- if (isRenegotiation && this.peers[id]) {
624
- const pc2 = this.peers[id];
625
- if (pc2.signalingState !== "stable") {
626
- console.warn(
627
- `[Offer] Cannot renegotiate: peer in state "${pc2.signalingState}"`
628
- );
629
- }
655
+ return;
630
656
  }
631
657
  if (!isRenegotiation) {
632
658
  this.initiators.add(id);
@@ -647,14 +673,11 @@ var VideoSDKCore = class {
647
673
  console.debug(`[Offer] Sent to ${id}`);
648
674
  } catch (err) {
649
675
  console.error(`[Offer] Failed for ${id}:`, err);
650
- this.emitError(
651
- "OFFER_FAILED",
652
- `Failed to create offer for ${id}`,
653
- err,
654
- true
655
- );
656
676
  }
657
677
  }
678
+ shouldInitiate(peerId) {
679
+ return this.myId < peerId;
680
+ }
658
681
  // ---------------- ANSWER ----------------
659
682
  async handleOffer(sdp, id) {
660
683
  if (!this.peers[id]) {
@@ -662,28 +685,44 @@ var VideoSDKCore = class {
662
685
  }
663
686
  const pc = this.peers[id];
664
687
  try {
665
- if (pc.signalingState !== "stable" && pc.signalingState !== "have-local-offer") {
688
+ if (pc.signalingState === "have-local-offer") {
689
+ if (this.shouldInitiate(id)) {
690
+ console.warn(
691
+ `[Glare] Both sent OFFERs, we win (${this.myId} < ${id}), keeping our OFFER`
692
+ );
693
+ return;
694
+ } else {
695
+ console.warn(
696
+ `[Glare] Both sent OFFERs, they win (${id} < ${this.myId}), rolling back`
697
+ );
698
+ pc.close();
699
+ delete this.peers[id];
700
+ this.initiators.delete(id);
701
+ this.peers[id] = this.createPeer(id);
702
+ }
703
+ }
704
+ if (this.peers[id].signalingState !== "stable" && this.peers[id].signalingState !== "have-local-offer") {
666
705
  console.warn(
667
- `[Signaling] Cannot accept OFFER in state "${pc.signalingState}"`
706
+ `[Signaling] Cannot accept OFFER in state "${this.peers[id].signalingState}"`
668
707
  );
669
708
  return;
670
709
  }
671
- await pc.setRemoteDescription({
710
+ await this.peers[id].setRemoteDescription({
672
711
  type: "offer",
673
712
  sdp
674
713
  });
675
714
  const pending = this.pendingIceCandidates[id] || [];
676
715
  for (const candidate of pending) {
677
716
  try {
678
- await pc.addIceCandidate(candidate);
717
+ await this.peers[id].addIceCandidate(candidate);
679
718
  } catch (err) {
680
719
  console.warn("[ICE] Failed to add candidate:", err);
681
720
  }
682
721
  }
683
722
  delete this.pendingIceCandidates[id];
684
- const answer = await pc.createAnswer();
685
- await pc.setLocalDescription(answer);
686
- await this.flushIce(id, pc);
723
+ const answer = await this.peers[id].createAnswer();
724
+ await this.peers[id].setLocalDescription(answer);
725
+ await this.flushIce(id, this.peers[id]);
687
726
  this.send({
688
727
  type: "ANSWER",
689
728
  payload: answer.sdp,
@@ -748,7 +787,7 @@ var VideoSDKCore = class {
748
787
  this.send({
749
788
  type: "SCREEN_SHARE_START",
750
789
  sender: this.myId,
751
- room_id: this.roomId,
790
+ room_id: this.room.id,
752
791
  stream_id: this.screenStream.id.replace(/[{}]/g, "")
753
792
  });
754
793
  return this.screenStream;
@@ -794,7 +833,7 @@ var VideoSDKCore = class {
794
833
  this.send({
795
834
  type: "SCREEN_SHARE_STOP",
796
835
  sender: this.myId,
797
- room_id: this.roomId
836
+ room_id: this.room.id
798
837
  });
799
838
  }
800
839
  sendChatMessage(payload) {
@@ -802,7 +841,7 @@ var VideoSDKCore = class {
802
841
  console.warn("WS not connected");
803
842
  return;
804
843
  }
805
- if (!this.roomId) {
844
+ if (!this.room.id) {
806
845
  console.warn("No roomId set");
807
846
  return;
808
847
  }
@@ -823,7 +862,7 @@ var VideoSDKCore = class {
823
862
  message: payload.message.trim(),
824
863
  user_id: this.myId,
825
864
  sender_name: senderName,
826
- room_id: this.roomId,
865
+ room_id: this.room.id,
827
866
  target: isPrivate ? payload.target ?? null : null,
828
867
  reply_to: payload.reply_to ?? null,
829
868
  client_ts: Date.now()
@@ -844,7 +883,7 @@ var VideoSDKCore = class {
844
883
  this.localStream.getTracks().forEach((track) => track.stop());
845
884
  this.localStream = null;
846
885
  }
847
- this.roomId = null;
886
+ this.room.id = null;
848
887
  this.state.localParticipant = null;
849
888
  this.state.notify("localParticipant");
850
889
  this.state.participants.clear();
@@ -939,7 +978,7 @@ var MeetingProvider = ({
939
978
  startScreenShare: sdk.startScreenShare.bind(sdk),
940
979
  stopScreenShare: sdk.stopScreenShare.bind(sdk),
941
980
  sendMessage: sdk.sendChatMessage.bind(sdk),
942
- meetingId: sdk.getMeetingId(),
981
+ room: sdk.getMeeting(),
943
982
  localParticipant,
944
983
  participants,
945
984
  messages,
@@ -1022,7 +1061,8 @@ var useMeeting = (handlers) => {
1022
1061
  const unsubscribe = ctx.onError(handlers.onError);
1023
1062
  return unsubscribe;
1024
1063
  }, [handlers?.onError]);
1025
- return ctx;
1064
+ const { sdk: _, ...publicApi } = ctx;
1065
+ return publicApi;
1026
1066
  };
1027
1067
 
1028
1068
  // src/react/useParticipants.ts