@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.mjs CHANGED
@@ -397,6 +397,7 @@ var VideoSDKCore = class {
397
397
  if (!p?.id || p.id === this.myId) return;
398
398
  this.state.addParticipant(p);
399
399
  this.events.onUserJoined?.(p);
400
+ await this.createOffer(p.id);
400
401
  break;
401
402
  }
402
403
  case "OFFER":
@@ -406,14 +407,26 @@ var VideoSDKCore = class {
406
407
  const pc = this.peers[msg.sender];
407
408
  if (!pc) return;
408
409
  if (pc.signalingState !== "have-local-offer") {
409
- console.warn("Ignoring invalid answer:", pc.signalingState);
410
+ console.warn(
411
+ `[Signaling] Unexpected ANSWER in state "${pc.signalingState}", ignoring`
412
+ );
410
413
  return;
411
414
  }
412
- await pc.setRemoteDescription({
413
- type: "answer",
414
- sdp: msg.payload
415
- });
416
- await this.flushIce(msg.sender, pc);
415
+ try {
416
+ await pc.setRemoteDescription({
417
+ type: "answer",
418
+ sdp: msg.payload
419
+ });
420
+ await this.flushIce(msg.sender, pc);
421
+ } catch (err) {
422
+ console.error("[Signaling] Failed to apply answer:", err);
423
+ this.emitError(
424
+ "ANSWER_FAILED",
425
+ `Failed to apply answer from ${msg.sender}`,
426
+ err,
427
+ true
428
+ );
429
+ }
417
430
  break;
418
431
  }
419
432
  case "ICE": {
@@ -508,17 +521,47 @@ var VideoSDKCore = class {
508
521
  // ---------------- PEER ----------------
509
522
  createPeer(id) {
510
523
  if (!this.localStream) throw new Error("No local stream");
524
+ console.log(
525
+ "Adding tracks",
526
+ this.localStream.getTracks().map((t) => ({
527
+ kind: t.kind,
528
+ enabled: t.enabled,
529
+ state: t.readyState
530
+ }))
531
+ );
511
532
  const pc = new RTCPeerConnection({
512
533
  iceServers: [
513
534
  {
514
- urls: [
515
- "stun:stun.l.google.com:19302",
516
- "stun:stun1.l.google.com:19302"
517
- ]
535
+ urls: "stun:stun.relay.metered.ca:80"
536
+ },
537
+ {
538
+ urls: "turn:global.relay.metered.ca:80",
539
+ username: "25aed888d2d360e9fae0e812",
540
+ credential: "WPYstojO9Wf3+HsQ"
541
+ },
542
+ {
543
+ urls: "turn:global.relay.metered.ca:80?transport=tcp",
544
+ username: "25aed888d2d360e9fae0e812",
545
+ credential: "WPYstojO9Wf3+HsQ"
546
+ },
547
+ {
548
+ urls: "turn:global.relay.metered.ca:443",
549
+ username: "25aed888d2d360e9fae0e812",
550
+ credential: "WPYstojO9Wf3+HsQ"
551
+ },
552
+ {
553
+ urls: "turns:global.relay.metered.ca:443?transport=tcp",
554
+ username: "25aed888d2d360e9fae0e812",
555
+ credential: "WPYstojO9Wf3+HsQ"
518
556
  }
519
557
  ]
520
558
  });
521
559
  pc.ontrack = (event) => {
560
+ console.log(`Track received: ${event.track.kind}`, {
561
+ trackId: event.track.id,
562
+ streamCount: event.streams.length,
563
+ streamTrackCount: event.streams[0]?.getTracks().length
564
+ });
522
565
  const incomingStream = event.streams[0];
523
566
  const participant = this.state.getParticipant(id);
524
567
  const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
@@ -551,6 +594,15 @@ var VideoSDKCore = class {
551
594
  target: id
552
595
  });
553
596
  };
597
+ pc.oniceconnectionstatechange = () => {
598
+ console.log(`ICE Connection State: ${pc.iceConnectionState}`);
599
+ if (pc.iceConnectionState === "failed" || pc.iceConnectionState === "disconnected") {
600
+ this.emitError(
601
+ "ICE_FAILED",
602
+ "Connection failed. Please check your network or refresh."
603
+ );
604
+ }
605
+ };
554
606
  pc.onconnectionstatechange = () => {
555
607
  if (pc.connectionState === "failed") {
556
608
  try {
@@ -573,7 +625,21 @@ var VideoSDKCore = class {
573
625
  }
574
626
  // ---------------- OFFER ----------------
575
627
  async createOffer(id, isRenegotiation = false) {
576
- if (!isRenegotiation && this.initiators.has(id)) return;
628
+ if (!isRenegotiation && this.initiators.has(id)) {
629
+ console.debug(
630
+ `[Offer] Already initiating with ${id}, skipping duplicate`
631
+ );
632
+ return;
633
+ }
634
+ if (isRenegotiation && this.peers[id]) {
635
+ const pc2 = this.peers[id];
636
+ if (pc2.signalingState !== "stable") {
637
+ console.warn(
638
+ `[Offer] Cannot renegotiate: peer in state "${pc2.signalingState}"`
639
+ );
640
+ return;
641
+ }
642
+ }
577
643
  if (!isRenegotiation) {
578
644
  this.initiators.add(id);
579
645
  }
@@ -581,46 +647,80 @@ var VideoSDKCore = class {
581
647
  this.peers[id] = this.createPeer(id);
582
648
  }
583
649
  const pc = this.peers[id];
584
- if (pc.signalingState !== "stable") {
585
- return;
650
+ try {
651
+ const offer = await pc.createOffer();
652
+ await pc.setLocalDescription(offer);
653
+ this.send({
654
+ type: "OFFER",
655
+ payload: offer.sdp,
656
+ sender: this.myId,
657
+ target: id
658
+ });
659
+ console.debug(`[Offer] Sent to ${id}`);
660
+ } catch (err) {
661
+ console.error(`[Offer] Failed for ${id}:`, err);
662
+ this.emitError(
663
+ "OFFER_FAILED",
664
+ `Failed to create offer for ${id}`,
665
+ err,
666
+ true
667
+ );
586
668
  }
587
- const offer = await pc.createOffer();
588
- await pc.setLocalDescription(offer);
589
- this.send({
590
- type: "OFFER",
591
- payload: offer.sdp,
592
- sender: this.myId,
593
- target: id
594
- });
595
669
  }
596
670
  // ---------------- ANSWER ----------------
597
671
  async handleOffer(sdp, id) {
672
+ if (this.initiators.has(id) && this.peers[id]) {
673
+ const pc2 = this.peers[id];
674
+ if (pc2.signalingState !== "stable") {
675
+ console.warn(
676
+ `[Signaling] Offer collision with ${id}. We initiated, ignoring their offer.`
677
+ );
678
+ return;
679
+ }
680
+ }
598
681
  if (!this.peers[id]) {
599
682
  this.peers[id] = this.createPeer(id);
600
683
  }
601
684
  const pc = this.peers[id];
602
- await pc.setRemoteDescription({
603
- type: "offer",
604
- sdp
605
- });
606
- const pending = this.pendingIceCandidates[id] || [];
607
- for (const candidate of pending) {
608
- try {
609
- await pc.addIceCandidate(candidate);
610
- } catch (err) {
611
- console.warn(err);
685
+ try {
686
+ if (pc.signalingState !== "stable" && pc.signalingState !== "have-local-offer") {
687
+ console.warn(
688
+ `[Signaling] Cannot accept OFFER in state "${pc.signalingState}"`
689
+ );
690
+ return;
691
+ }
692
+ await pc.setRemoteDescription({
693
+ type: "offer",
694
+ sdp
695
+ });
696
+ const pending = this.pendingIceCandidates[id] || [];
697
+ for (const candidate of pending) {
698
+ try {
699
+ await pc.addIceCandidate(candidate);
700
+ } catch (err) {
701
+ console.warn("[ICE] Failed to add candidate:", err);
702
+ }
612
703
  }
704
+ delete this.pendingIceCandidates[id];
705
+ const answer = await pc.createAnswer();
706
+ await pc.setLocalDescription(answer);
707
+ await this.flushIce(id, pc);
708
+ this.send({
709
+ type: "ANSWER",
710
+ payload: answer.sdp,
711
+ sender: this.myId,
712
+ target: id
713
+ });
714
+ console.debug(`[Answer] Sent to ${id}`);
715
+ } catch (err) {
716
+ console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);
717
+ this.emitError(
718
+ "OFFER_HANDLING_FAILED",
719
+ `Failed to handle offer from ${id}`,
720
+ err,
721
+ true
722
+ );
613
723
  }
614
- delete this.pendingIceCandidates[id];
615
- const answer = await pc.createAnswer();
616
- await pc.setLocalDescription(answer);
617
- await this.flushIce(id, pc);
618
- this.send({
619
- type: "ANSWER",
620
- payload: answer.sdp,
621
- sender: this.myId,
622
- target: id
623
- });
624
724
  }
625
725
  // ---------------- CLEANUP ----------------
626
726
  closePeer(id) {
@@ -635,39 +735,55 @@ var VideoSDKCore = class {
635
735
  this.state.removeParticipant(id);
636
736
  }
637
737
  async startScreenShare() {
638
- if (this.state.presenterId && this.state.presenterId !== this.myId) {
639
- throw new Error("Another user is already sharing their screen.");
640
- }
641
- this.screenStream = await navigator.mediaDevices.getDisplayMedia({
642
- video: true,
643
- audio: true
644
- });
645
- this.isScreenSharing = true;
646
- this.state.updateLocalParticipant({
647
- media: {
648
- isScreenSharing: true,
649
- screenStream: this.screenStream
738
+ try {
739
+ if (this.state.presenterId && this.state.presenterId !== this.myId) {
740
+ throw new Error("Another user is already sharing their screen.");
650
741
  }
651
- });
652
- this.state.setPresenterId(this.myId);
653
- this.screenStream.getVideoTracks()[0].onended = () => {
654
- this.stopScreenShare();
655
- };
656
- Object.entries(this.peers).forEach(([peerId, pc]) => {
657
- this.screenSenders[peerId] = [];
658
- this.screenStream.getTracks().forEach((track) => {
659
- const sender = pc.addTrack(track, this.screenStream);
660
- this.screenSenders[peerId].push(sender);
742
+ if (!navigator.mediaDevices?.getDisplayMedia) {
743
+ throw new Error("Screen sharing not supported on this device");
744
+ }
745
+ this.screenStream = await navigator.mediaDevices.getDisplayMedia({
746
+ video: true
747
+ // audio: true,
661
748
  });
662
- this.createOffer(peerId, true);
663
- });
664
- this.send({
665
- type: "SCREEN_SHARE_START",
666
- sender: this.myId,
667
- room_id: this.roomId,
668
- stream_id: this.screenStream.id.replace(/[{}]/g, "")
669
- });
670
- return this.screenStream;
749
+ this.isScreenSharing = true;
750
+ this.state.updateLocalParticipant({
751
+ media: {
752
+ isScreenSharing: true,
753
+ screenStream: this.screenStream,
754
+ screenTrack: this.screenStream.getVideoTracks()[0]
755
+ }
756
+ });
757
+ this.state.setPresenterId(this.myId);
758
+ this.screenStream.getVideoTracks()[0].onended = () => {
759
+ this.stopScreenShare();
760
+ };
761
+ Object.entries(this.peers).forEach(([peerId, pc]) => {
762
+ this.screenSenders[peerId] = [];
763
+ this.screenStream.getTracks().forEach((track) => {
764
+ const sender = pc.addTrack(track, this.screenStream);
765
+ this.screenSenders[peerId].push(sender);
766
+ });
767
+ this.createOffer(peerId, true);
768
+ });
769
+ this.send({
770
+ type: "SCREEN_SHARE_START",
771
+ sender: this.myId,
772
+ room_id: this.roomId,
773
+ stream_id: this.screenStream.id.replace(/[{}]/g, "")
774
+ });
775
+ return this.screenStream;
776
+ } catch (err) {
777
+ this.emitError(
778
+ "SCREEN_SHARE_FAILED",
779
+ err?.message || "Failed to start screen sharing",
780
+ err,
781
+ true
782
+ );
783
+ this.isScreenSharing = false;
784
+ this.screenStream = null;
785
+ throw err;
786
+ }
671
787
  }
672
788
  stopScreenShare() {
673
789
  if (!this.screenStream) return;
@@ -905,7 +1021,6 @@ var useLocalParticipant = () => {
905
1021
  video.srcObject = stream;
906
1022
  video.autoplay = true;
907
1023
  video.playsInline = true;
908
- video.muted = true;
909
1024
  video.play().catch((err) => {
910
1025
  console.warn(`Autoplay failed for local view:`, err);
911
1026
  });
@@ -955,6 +1070,15 @@ var useRemoteMedia = (participantId) => {
955
1070
  const [participant, setParticipant] = useState4(
956
1071
  () => sdk.state.getParticipant(participantId) || null
957
1072
  );
1073
+ const buildMediaStream = (participant2) => {
1074
+ if (!participant2?.media) return null;
1075
+ const stream = new MediaStream();
1076
+ const videoTrack = participant2.media.stream?.getVideoTracks?.()?.[0];
1077
+ const audioTrack = participant2.media.stream?.getAudioTracks?.()?.[0];
1078
+ if (videoTrack) stream.addTrack(videoTrack);
1079
+ if (audioTrack) stream.addTrack(audioTrack);
1080
+ return stream;
1081
+ };
958
1082
  useEffect5(() => {
959
1083
  const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {
960
1084
  const p = sdk.state.getParticipant(participantId);
@@ -968,13 +1092,14 @@ var useRemoteMedia = (participantId) => {
968
1092
  const stream = participant?.media?.stream;
969
1093
  if (!stream) return;
970
1094
  node.srcObject = stream;
971
- node.autoplay = true;
972
- node.playsInline = true;
973
1095
  node.muted = true;
974
- node.play().catch((e) => console.warn("Video playback failed:", e));
1096
+ node.playsInline = true;
1097
+ node.autoplay = true;
1098
+ node.play().catch((err) => {
1099
+ console.log("can't play video:", err);
1100
+ });
975
1101
  },
976
- // FIX: Added cameraTrack so React knows to re-run this when the video track arrives
977
- [participant?.media?.stream, participant?.media?.cameraTrack]
1102
+ [participant?.media?.stream]
978
1103
  );
979
1104
  const audioRef = useCallback2(
980
1105
  (node) => {
@@ -982,12 +1107,12 @@ var useRemoteMedia = (participantId) => {
982
1107
  const stream = participant?.media?.stream;
983
1108
  if (!stream) return;
984
1109
  node.srcObject = stream;
985
- node.autoplay = true;
986
- node.muted = !participant?.media?.micEnabled;
987
- node.play().catch((e) => console.warn("Audio playback failed:", e));
1110
+ node.muted = false;
1111
+ node.play().catch((err) => {
1112
+ console.log("can't play Audio:", err);
1113
+ });
988
1114
  },
989
- // FIX: Added audioTrack to dependency array
990
- [participant?.media?.stream, participant?.media?.micEnabled]
1115
+ [participant?.media?.stream]
991
1116
  );
992
1117
  return {
993
1118
  videoRef,