@afosecure/meetingsdk 1.0.8 → 1.1.1

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.js CHANGED
@@ -430,6 +430,7 @@ var VideoSDKCore = class {
430
430
  if (!p?.id || p.id === this.myId) return;
431
431
  this.state.addParticipant(p);
432
432
  this.events.onUserJoined?.(p);
433
+ await this.createOffer(p.id);
433
434
  break;
434
435
  }
435
436
  case "OFFER":
@@ -439,14 +440,26 @@ var VideoSDKCore = class {
439
440
  const pc = this.peers[msg.sender];
440
441
  if (!pc) return;
441
442
  if (pc.signalingState !== "have-local-offer") {
442
- console.warn("Ignoring invalid answer:", pc.signalingState);
443
+ console.warn(
444
+ `[Signaling] Unexpected ANSWER in state "${pc.signalingState}", ignoring`
445
+ );
443
446
  return;
444
447
  }
445
- await pc.setRemoteDescription({
446
- type: "answer",
447
- sdp: msg.payload
448
- });
449
- await this.flushIce(msg.sender, pc);
448
+ try {
449
+ await pc.setRemoteDescription({
450
+ type: "answer",
451
+ sdp: msg.payload
452
+ });
453
+ await this.flushIce(msg.sender, pc);
454
+ } catch (err) {
455
+ console.error("[Signaling] Failed to apply answer:", err);
456
+ this.emitError(
457
+ "ANSWER_FAILED",
458
+ `Failed to apply answer from ${msg.sender}`,
459
+ err,
460
+ true
461
+ );
462
+ }
450
463
  break;
451
464
  }
452
465
  case "ICE": {
@@ -541,17 +554,47 @@ var VideoSDKCore = class {
541
554
  // ---------------- PEER ----------------
542
555
  createPeer(id) {
543
556
  if (!this.localStream) throw new Error("No local stream");
557
+ console.log(
558
+ "Adding tracks",
559
+ this.localStream.getTracks().map((t) => ({
560
+ kind: t.kind,
561
+ enabled: t.enabled,
562
+ state: t.readyState
563
+ }))
564
+ );
544
565
  const pc = new RTCPeerConnection({
545
566
  iceServers: [
546
567
  {
547
- urls: [
548
- "stun:stun.l.google.com:19302",
549
- "stun:stun1.l.google.com:19302"
550
- ]
568
+ urls: "stun:stun.relay.metered.ca:80"
569
+ },
570
+ {
571
+ urls: "turn:global.relay.metered.ca:80",
572
+ username: "25aed888d2d360e9fae0e812",
573
+ credential: "WPYstojO9Wf3+HsQ"
574
+ },
575
+ {
576
+ urls: "turn:global.relay.metered.ca:80?transport=tcp",
577
+ username: "25aed888d2d360e9fae0e812",
578
+ credential: "WPYstojO9Wf3+HsQ"
579
+ },
580
+ {
581
+ urls: "turn:global.relay.metered.ca:443",
582
+ username: "25aed888d2d360e9fae0e812",
583
+ credential: "WPYstojO9Wf3+HsQ"
584
+ },
585
+ {
586
+ urls: "turns:global.relay.metered.ca:443?transport=tcp",
587
+ username: "25aed888d2d360e9fae0e812",
588
+ credential: "WPYstojO9Wf3+HsQ"
551
589
  }
552
590
  ]
553
591
  });
554
592
  pc.ontrack = (event) => {
593
+ console.log(`Track received: ${event.track.kind}`, {
594
+ trackId: event.track.id,
595
+ streamCount: event.streams.length,
596
+ streamTrackCount: event.streams[0]?.getTracks().length
597
+ });
555
598
  const incomingStream = event.streams[0];
556
599
  const participant = this.state.getParticipant(id);
557
600
  const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
@@ -584,6 +627,15 @@ var VideoSDKCore = class {
584
627
  target: id
585
628
  });
586
629
  };
630
+ pc.oniceconnectionstatechange = () => {
631
+ console.log(`ICE Connection State: ${pc.iceConnectionState}`);
632
+ if (pc.iceConnectionState === "failed" || pc.iceConnectionState === "disconnected") {
633
+ this.emitError(
634
+ "ICE_FAILED",
635
+ "Connection failed. Please check your network or refresh."
636
+ );
637
+ }
638
+ };
587
639
  pc.onconnectionstatechange = () => {
588
640
  if (pc.connectionState === "failed") {
589
641
  try {
@@ -606,7 +658,21 @@ var VideoSDKCore = class {
606
658
  }
607
659
  // ---------------- OFFER ----------------
608
660
  async createOffer(id, isRenegotiation = false) {
609
- if (!isRenegotiation && this.initiators.has(id)) return;
661
+ if (!isRenegotiation && this.initiators.has(id)) {
662
+ console.debug(
663
+ `[Offer] Already initiating with ${id}, skipping duplicate`
664
+ );
665
+ return;
666
+ }
667
+ if (isRenegotiation && this.peers[id]) {
668
+ const pc2 = this.peers[id];
669
+ if (pc2.signalingState !== "stable") {
670
+ console.warn(
671
+ `[Offer] Cannot renegotiate: peer in state "${pc2.signalingState}"`
672
+ );
673
+ return;
674
+ }
675
+ }
610
676
  if (!isRenegotiation) {
611
677
  this.initiators.add(id);
612
678
  }
@@ -614,46 +680,80 @@ var VideoSDKCore = class {
614
680
  this.peers[id] = this.createPeer(id);
615
681
  }
616
682
  const pc = this.peers[id];
617
- if (pc.signalingState !== "stable") {
618
- return;
683
+ try {
684
+ const offer = await pc.createOffer();
685
+ await pc.setLocalDescription(offer);
686
+ this.send({
687
+ type: "OFFER",
688
+ payload: offer.sdp,
689
+ sender: this.myId,
690
+ target: id
691
+ });
692
+ console.debug(`[Offer] Sent to ${id}`);
693
+ } catch (err) {
694
+ console.error(`[Offer] Failed for ${id}:`, err);
695
+ this.emitError(
696
+ "OFFER_FAILED",
697
+ `Failed to create offer for ${id}`,
698
+ err,
699
+ true
700
+ );
619
701
  }
620
- const offer = await pc.createOffer();
621
- await pc.setLocalDescription(offer);
622
- this.send({
623
- type: "OFFER",
624
- payload: offer.sdp,
625
- sender: this.myId,
626
- target: id
627
- });
628
702
  }
629
703
  // ---------------- ANSWER ----------------
630
704
  async handleOffer(sdp, id) {
705
+ if (this.initiators.has(id) && this.peers[id]) {
706
+ const pc2 = this.peers[id];
707
+ if (pc2.signalingState !== "stable") {
708
+ console.warn(
709
+ `[Signaling] Offer collision with ${id}. We initiated, ignoring their offer.`
710
+ );
711
+ return;
712
+ }
713
+ }
631
714
  if (!this.peers[id]) {
632
715
  this.peers[id] = this.createPeer(id);
633
716
  }
634
717
  const pc = this.peers[id];
635
- await pc.setRemoteDescription({
636
- type: "offer",
637
- sdp
638
- });
639
- const pending = this.pendingIceCandidates[id] || [];
640
- for (const candidate of pending) {
641
- try {
642
- await pc.addIceCandidate(candidate);
643
- } catch (err) {
644
- console.warn(err);
718
+ try {
719
+ if (pc.signalingState !== "stable" && pc.signalingState !== "have-local-offer") {
720
+ console.warn(
721
+ `[Signaling] Cannot accept OFFER in state "${pc.signalingState}"`
722
+ );
723
+ return;
724
+ }
725
+ await pc.setRemoteDescription({
726
+ type: "offer",
727
+ sdp
728
+ });
729
+ const pending = this.pendingIceCandidates[id] || [];
730
+ for (const candidate of pending) {
731
+ try {
732
+ await pc.addIceCandidate(candidate);
733
+ } catch (err) {
734
+ console.warn("[ICE] Failed to add candidate:", err);
735
+ }
645
736
  }
737
+ delete this.pendingIceCandidates[id];
738
+ const answer = await pc.createAnswer();
739
+ await pc.setLocalDescription(answer);
740
+ await this.flushIce(id, pc);
741
+ this.send({
742
+ type: "ANSWER",
743
+ payload: answer.sdp,
744
+ sender: this.myId,
745
+ target: id
746
+ });
747
+ console.debug(`[Answer] Sent to ${id}`);
748
+ } catch (err) {
749
+ console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);
750
+ this.emitError(
751
+ "OFFER_HANDLING_FAILED",
752
+ `Failed to handle offer from ${id}`,
753
+ err,
754
+ true
755
+ );
646
756
  }
647
- delete this.pendingIceCandidates[id];
648
- const answer = await pc.createAnswer();
649
- await pc.setLocalDescription(answer);
650
- await this.flushIce(id, pc);
651
- this.send({
652
- type: "ANSWER",
653
- payload: answer.sdp,
654
- sender: this.myId,
655
- target: id
656
- });
657
757
  }
658
758
  // ---------------- CLEANUP ----------------
659
759
  closePeer(id) {
@@ -668,39 +768,55 @@ var VideoSDKCore = class {
668
768
  this.state.removeParticipant(id);
669
769
  }
670
770
  async startScreenShare() {
671
- if (this.state.presenterId && this.state.presenterId !== this.myId) {
672
- throw new Error("Another user is already sharing their screen.");
673
- }
674
- this.screenStream = await navigator.mediaDevices.getDisplayMedia({
675
- video: true,
676
- audio: true
677
- });
678
- this.isScreenSharing = true;
679
- this.state.updateLocalParticipant({
680
- media: {
681
- isScreenSharing: true,
682
- screenStream: this.screenStream
771
+ try {
772
+ if (this.state.presenterId && this.state.presenterId !== this.myId) {
773
+ throw new Error("Another user is already sharing their screen.");
683
774
  }
684
- });
685
- this.state.setPresenterId(this.myId);
686
- this.screenStream.getVideoTracks()[0].onended = () => {
687
- this.stopScreenShare();
688
- };
689
- Object.entries(this.peers).forEach(([peerId, pc]) => {
690
- this.screenSenders[peerId] = [];
691
- this.screenStream.getTracks().forEach((track) => {
692
- const sender = pc.addTrack(track, this.screenStream);
693
- this.screenSenders[peerId].push(sender);
775
+ if (!navigator.mediaDevices?.getDisplayMedia) {
776
+ throw new Error("Screen sharing not supported on this device");
777
+ }
778
+ this.screenStream = await navigator.mediaDevices.getDisplayMedia({
779
+ video: true
780
+ // audio: true,
694
781
  });
695
- this.createOffer(peerId, true);
696
- });
697
- this.send({
698
- type: "SCREEN_SHARE_START",
699
- sender: this.myId,
700
- room_id: this.roomId,
701
- stream_id: this.screenStream.id.replace(/[{}]/g, "")
702
- });
703
- return this.screenStream;
782
+ this.isScreenSharing = true;
783
+ this.state.updateLocalParticipant({
784
+ media: {
785
+ isScreenSharing: true,
786
+ screenStream: this.screenStream,
787
+ screenTrack: this.screenStream.getVideoTracks()[0]
788
+ }
789
+ });
790
+ this.state.setPresenterId(this.myId);
791
+ this.screenStream.getVideoTracks()[0].onended = () => {
792
+ this.stopScreenShare();
793
+ };
794
+ Object.entries(this.peers).forEach(([peerId, pc]) => {
795
+ this.screenSenders[peerId] = [];
796
+ this.screenStream.getTracks().forEach((track) => {
797
+ const sender = pc.addTrack(track, this.screenStream);
798
+ this.screenSenders[peerId].push(sender);
799
+ });
800
+ this.createOffer(peerId, true);
801
+ });
802
+ this.send({
803
+ type: "SCREEN_SHARE_START",
804
+ sender: this.myId,
805
+ room_id: this.roomId,
806
+ stream_id: this.screenStream.id.replace(/[{}]/g, "")
807
+ });
808
+ return this.screenStream;
809
+ } catch (err) {
810
+ this.emitError(
811
+ "SCREEN_SHARE_FAILED",
812
+ err?.message || "Failed to start screen sharing",
813
+ err,
814
+ true
815
+ );
816
+ this.isScreenSharing = false;
817
+ this.screenStream = null;
818
+ throw err;
819
+ }
704
820
  }
705
821
  stopScreenShare() {
706
822
  if (!this.screenStream) return;
@@ -938,7 +1054,6 @@ var useLocalParticipant = () => {
938
1054
  video.srcObject = stream;
939
1055
  video.autoplay = true;
940
1056
  video.playsInline = true;
941
- video.muted = true;
942
1057
  video.play().catch((err) => {
943
1058
  console.warn(`Autoplay failed for local view:`, err);
944
1059
  });
@@ -988,6 +1103,15 @@ var useRemoteMedia = (participantId) => {
988
1103
  const [participant, setParticipant] = (0, import_react6.useState)(
989
1104
  () => sdk.state.getParticipant(participantId) || null
990
1105
  );
1106
+ const buildMediaStream = (participant2) => {
1107
+ if (!participant2?.media) return null;
1108
+ const stream = new MediaStream();
1109
+ const videoTrack = participant2.media.stream?.getVideoTracks?.()?.[0];
1110
+ const audioTrack = participant2.media.stream?.getAudioTracks?.()?.[0];
1111
+ if (videoTrack) stream.addTrack(videoTrack);
1112
+ if (audioTrack) stream.addTrack(audioTrack);
1113
+ return stream;
1114
+ };
991
1115
  (0, import_react6.useEffect)(() => {
992
1116
  const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {
993
1117
  const p = sdk.state.getParticipant(participantId);
@@ -1001,13 +1125,14 @@ var useRemoteMedia = (participantId) => {
1001
1125
  const stream = participant?.media?.stream;
1002
1126
  if (!stream) return;
1003
1127
  node.srcObject = stream;
1004
- node.autoplay = true;
1005
- node.playsInline = true;
1006
1128
  node.muted = true;
1007
- node.play().catch((e) => console.warn("Video playback failed:", e));
1129
+ node.playsInline = true;
1130
+ node.autoplay = true;
1131
+ node.play().catch((err) => {
1132
+ console.log("can't play video:", err);
1133
+ });
1008
1134
  },
1009
- // FIX: Added cameraTrack so React knows to re-run this when the video track arrives
1010
- [participant?.media?.stream, participant?.media?.cameraTrack]
1135
+ [participant?.media?.stream]
1011
1136
  );
1012
1137
  const audioRef = (0, import_react6.useCallback)(
1013
1138
  (node) => {
@@ -1015,12 +1140,12 @@ var useRemoteMedia = (participantId) => {
1015
1140
  const stream = participant?.media?.stream;
1016
1141
  if (!stream) return;
1017
1142
  node.srcObject = stream;
1018
- node.autoplay = true;
1019
- node.muted = !participant?.media?.micEnabled;
1020
- node.play().catch((e) => console.warn("Audio playback failed:", e));
1143
+ node.muted = false;
1144
+ node.play().catch((err) => {
1145
+ console.log("can't play Audio:", err);
1146
+ });
1021
1147
  },
1022
- // FIX: Added audioTrack to dependency array
1023
- [participant?.media?.stream, participant?.media?.micEnabled]
1148
+ [participant?.media?.stream]
1024
1149
  );
1025
1150
  return {
1026
1151
  videoRef,