@afosecure/meetingsdk 1.3.2 → 1.3.4

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
@@ -68,9 +68,7 @@ var MeetingState = class {
68
68
  camEnabled: true,
69
69
  isScreenSharing: false,
70
70
  ...p.media,
71
- // preserve existing media items if they happen to exist
72
71
  ...patch
73
- // apply the incoming stream updates
74
72
  }
75
73
  };
76
74
  const next = new Map(this.participants);
@@ -177,8 +175,7 @@ var VideoSDKCore = class {
177
175
  this.localStream = null;
178
176
  this.screenStream = null;
179
177
  this.isScreenSharing = false;
180
- // Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }
181
- this.peerTransceivers = {};
178
+ this.screenSenders = {};
182
179
  this.pingInterval = null;
183
180
  this.pendingIceCandidates = {};
184
181
  this.pendingOffers = {};
@@ -207,7 +204,7 @@ var VideoSDKCore = class {
207
204
  this.joinRejecter = void 0;
208
205
  console.error("[MeetingSDK Error]", err);
209
206
  }
210
- // STREAM
207
+ // ---------------- STREAM ----------------
211
208
  async initLocal(video, name) {
212
209
  this.participantName = name;
213
210
  try {
@@ -244,7 +241,7 @@ var VideoSDKCore = class {
244
241
  throw err;
245
242
  }
246
243
  }
247
- // CONNECT
244
+ // ---------------- CONNECT ----------------
248
245
  async connect(roomId, name) {
249
246
  this.room.id = roomId;
250
247
  this.reset();
@@ -258,7 +255,8 @@ var VideoSDKCore = class {
258
255
  type: "JOIN",
259
256
  room_id: roomId,
260
257
  user_id: this.myId,
261
- sender_name: name
258
+ sender_name: name,
259
+ camera_stream_id: this.localStream?.id.replace(/[{}]/g, "")
262
260
  });
263
261
  };
264
262
  this.ws.onerror = (err) => {
@@ -390,11 +388,10 @@ var VideoSDKCore = class {
390
388
  this.pingInterval = null;
391
389
  }
392
390
  }
393
- // RESET
391
+ // ---------------- RESET ----------------
394
392
  reset() {
395
393
  Object.values(this.peers).forEach((pc) => pc.close());
396
394
  this.peers = {};
397
- this.peerTransceivers = {};
398
395
  this.initiators.clear();
399
396
  this.pendingIceCandidates = {};
400
397
  this.state.resetRemoteState();
@@ -449,7 +446,6 @@ var VideoSDKCore = class {
449
446
  type: "answer",
450
447
  sdp: msg.payload
451
448
  });
452
- this.captureScreenMid(msg.sender);
453
449
  await this.flushIce(msg.sender, pc);
454
450
  } catch (err) {
455
451
  console.error("[Signaling] Failed to apply answer:", err);
@@ -486,6 +482,7 @@ var VideoSDKCore = class {
486
482
  if (msg.presenterId) {
487
483
  this.state.setPresenterId(msg.presenterId);
488
484
  this.events.onScreenShareStarted?.(msg.presenterId, null);
485
+ this.state.setPresenterId(msg.presenterId);
489
486
  }
490
487
  for (const p of msg.participants || []) {
491
488
  if (!p?.id || p.id === this.myId) continue;
@@ -498,7 +495,8 @@ var VideoSDKCore = class {
498
495
  this.state.setPresenterId(p.id);
499
496
  this.state.updateParticipantMedia(p.id, {
500
497
  isScreenSharing: true,
501
- remoteScreenStreamId: p.remoteScreenStreamId
498
+ remoteScreenStreamId: p.remoteScreenStreamId,
499
+ cameraStreamId: p.cameraStreamId || null
502
500
  });
503
501
  console.log(
504
502
  `[EXISTING_USERS] Pre-seeded screen state for ${p.id}, waiting for ontrack`
@@ -568,10 +566,12 @@ var VideoSDKCore = class {
568
566
  });
569
567
  break;
570
568
  }
569
+ // ============ NEW: HANDLE JOIN_APPROVED WITH RECONNECT ============
571
570
  case "JOIN_APPROVED": {
572
571
  await this.handleJoinApproved(msg);
573
572
  break;
574
573
  }
574
+ // ============ END: JOIN_APPROVED ============
575
575
  case "JOIN_REJECTED": {
576
576
  const decision = "rejected";
577
577
  console.log("JOIN_REJECTED - user not allowed to join");
@@ -619,12 +619,14 @@ var VideoSDKCore = class {
619
619
  const peerId2 = msg.peerId;
620
620
  this.state.updateParticipantMedia(peerId2, {
621
621
  isScreenSharing: true,
622
- remoteScreenStreamId: msg.stream_id
622
+ remoteScreenStreamId: msg.stream_id,
623
+ cameraStreamId: msg.camera_stream_id || null
623
624
  });
624
625
  if (!this.state.presenterId) {
625
626
  this.state.setPresenterId(peerId2);
626
627
  }
627
- this.events.onScreenShareStarted?.(peerId2, null);
628
+ const screenStream = this.state.getParticipant(peerId2)?.media?.screenStream;
629
+ this.events.onScreenShareStarted?.(peerId2, screenStream || null);
628
630
  break;
629
631
  }
630
632
  case "SCREEN_SHARE_STOP": {
@@ -651,7 +653,8 @@ var VideoSDKCore = class {
651
653
  }
652
654
  }
653
655
  }
654
- async createPeer(id) {
656
+ // ---------------- PEER ----------------
657
+ createPeer(id) {
655
658
  if (!this.localStream) throw new Error("No local stream");
656
659
  if (!this.iceServers || this.iceServers.length === 0) {
657
660
  throw new Error(
@@ -659,9 +662,7 @@ var VideoSDKCore = class {
659
662
  );
660
663
  }
661
664
  console.log(
662
- "Creating peer connection for",
663
- id,
664
- "with tracks:",
665
+ "Adding tracks",
665
666
  this.localStream.getTracks().map((t) => ({
666
667
  kind: t.kind,
667
668
  enabled: t.enabled,
@@ -671,58 +672,40 @@ var VideoSDKCore = class {
671
672
  const pc = new RTCPeerConnection({
672
673
  iceServers: this.iceServers
673
674
  });
674
- const audioTransceiver = pc.addTransceiver("audio", {
675
- direction: "sendrecv"
676
- });
677
- const audioTrack = this.localStream.getAudioTracks()[0];
678
- if (audioTrack) {
679
- await audioTransceiver.sender.replaceTrack(audioTrack);
680
- }
681
- const cameraTransceiver = pc.addTransceiver("video", {
682
- direction: "sendrecv"
683
- });
684
- const videoTrack = this.localStream.getVideoTracks()[0];
685
- if (videoTrack) {
686
- await cameraTransceiver.sender.replaceTrack(videoTrack);
687
- }
688
- const screenTransceiver = pc.addTransceiver("video", {
689
- direction: this.isScreenSharing ? "sendrecv" : "recvonly"
690
- });
691
- if (this.isScreenSharing && this.screenStream) {
692
- const screenTrack = this.screenStream.getVideoTracks()[0];
693
- if (screenTrack) {
694
- await screenTransceiver.sender.replaceTrack(screenTrack);
695
- }
696
- }
697
- this.peerTransceivers[id] = {
698
- cameraTransceiver,
699
- screenTransceiver,
700
- screenMid: null
701
- // will be populated after negotiation
702
- };
703
675
  pc.ontrack = (event) => {
704
- const transceiver = event.transceiver;
705
- const isScreenTrack = transceiver === this.peerTransceivers[id]?.screenTransceiver;
676
+ console.log("ontrack");
677
+ console.log("kind:", event.track.kind);
678
+ console.log("mid:", event.transceiver.mid);
679
+ console.log("streams:", event.streams);
680
+ const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
681
+ const streamId = incomingStream?.id;
682
+ const participant = this.state.getParticipant(id);
683
+ const isScreenStream = streamId && (streamId === participant?.media?.remoteScreenStreamId || participant?.media?.isScreenSharing && streamId !== participant?.media?.stream?.id && event.track.kind === "video");
706
684
  console.log(
707
- `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`
685
+ `[ontrack] peerId: ${id}, streamId: ${streamId}, isScreen: ${isScreenStream}`
686
+ );
687
+ console.log(` - cameraStreamId: ${participant?.media?.cameraStreamId}`);
688
+ console.log(
689
+ ` - screenStreamId: ${participant?.media?.remoteScreenStreamId}`
708
690
  );
709
- const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
710
691
  if (event.track.muted) {
711
692
  event.track.onunmute = () => {
712
- console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);
693
+ console.log(`${event.track.kind} track unmuted for ${id}`);
713
694
  };
714
695
  }
715
- if (isScreenTrack) {
716
- const videoTrack2 = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0];
696
+ if (isScreenStream) {
697
+ const videoTrack = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0] || participant?.media?.screenTrack;
717
698
  this.state.updateParticipantMedia(id, {
718
699
  screenStream: incomingStream,
719
- screenTrack: videoTrack2,
700
+ screenTrack: videoTrack,
701
+ remoteScreenStreamId: incomingStream.id,
720
702
  isScreenSharing: true
721
703
  });
722
704
  if (!this.state.presenterId) {
723
705
  this.state.setPresenterId(id);
724
706
  }
725
707
  this.events.onScreenShareStarted?.(id, incomingStream);
708
+ console.log(`Screen share stream detected and stored for ${id}`);
726
709
  } else {
727
710
  this.state.updateParticipantMedia(id, {
728
711
  stream: incomingStream,
@@ -742,41 +725,29 @@ var VideoSDKCore = class {
742
725
  });
743
726
  };
744
727
  pc.oniceconnectionstatechange = () => {
745
- console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);
728
+ console.log(`ICE Connection State: ${pc.iceConnectionState}`);
746
729
  };
747
730
  pc.onconnectionstatechange = () => {
748
- console.log(`[Connection] ${id}: ${pc.connectionState}`);
749
731
  if (pc.connectionState === "failed") {
750
732
  try {
751
733
  pc.restartIce();
752
- } catch (e) {
753
- console.warn("Failed to restart ICE:", e);
734
+ } catch {
754
735
  }
755
736
  }
756
737
  };
757
- return pc;
758
- }
759
- /**
760
- * Capture the screen transceiver's MID after SDP negotiation completes.
761
- * The MID is assigned during negotiation and is stable for the life of the connection.
762
- */
763
- captureScreenMid(peerId) {
764
- const pc = this.peers[peerId];
765
- if (!pc) return;
766
- const transceivers = pc.getTransceivers();
767
- const screenTransceiver = this.peerTransceivers[peerId]?.screenTransceiver;
768
- if (!screenTransceiver) return;
769
- const negotiatedTransceiver = transceivers.find(
770
- (t) => t === screenTransceiver
771
- );
772
- if (negotiatedTransceiver?.mid) {
773
- this.peerTransceivers[peerId].screenMid = negotiatedTransceiver.mid;
774
- console.log(
775
- `[Negotiation] Captured screenMid for ${peerId}: ${negotiatedTransceiver.mid}`
776
- );
738
+ this.localStream.getTracks().forEach((track) => {
739
+ pc.addTrack(track, this.localStream);
740
+ });
741
+ if (this.isScreenSharing && this.screenStream) {
742
+ this.screenSenders[id] = [];
743
+ this.screenStream.getTracks().forEach((track) => {
744
+ const sender = pc.addTrack(track, this.screenStream);
745
+ this.screenSenders[id].push(sender);
746
+ });
777
747
  }
748
+ return pc;
778
749
  }
779
- // OFFER
750
+ // ---------------- OFFER ----------------
780
751
  async createOffer(id, isRenegotiation = false) {
781
752
  if (!isRenegotiation && !this.shouldInitiate(id)) {
782
753
  console.debug(
@@ -794,13 +765,12 @@ var VideoSDKCore = class {
794
765
  this.initiators.add(id);
795
766
  }
796
767
  if (!this.peers[id]) {
797
- this.peers[id] = await this.createPeer(id);
768
+ this.peers[id] = this.createPeer(id);
798
769
  }
799
770
  const pc = this.peers[id];
800
771
  try {
801
772
  const offer = await pc.createOffer();
802
773
  await pc.setLocalDescription(offer);
803
- this.captureScreenMid(id);
804
774
  this.send({
805
775
  type: "OFFER",
806
776
  payload: offer.sdp,
@@ -810,18 +780,12 @@ var VideoSDKCore = class {
810
780
  console.debug(`[Offer] Sent to ${id}`);
811
781
  } catch (err) {
812
782
  console.error(`[Offer] Failed for ${id}:`, err);
813
- this.emitError(
814
- "OFFER_CREATION_FAILED",
815
- `Failed to create offer for ${id}`,
816
- err,
817
- true
818
- );
819
783
  }
820
784
  }
821
785
  shouldInitiate(peerId) {
822
786
  return this.myId < peerId;
823
787
  }
824
- // ANSWER
788
+ // ---------------- ANSWER ----------------
825
789
  async handleOffer(sdp, id) {
826
790
  if (!this.iceServers || this.iceServers.length === 0) {
827
791
  console.warn("[Offer] Waiting for iceServers, queuing offer from", id);
@@ -829,7 +793,7 @@ var VideoSDKCore = class {
829
793
  return;
830
794
  }
831
795
  if (!this.peers[id]) {
832
- this.peers[id] = await this.createPeer(id);
796
+ this.peers[id] = this.createPeer(id);
833
797
  }
834
798
  const pc = this.peers[id];
835
799
  try {
@@ -845,9 +809,8 @@ var VideoSDKCore = class {
845
809
  );
846
810
  pc.close();
847
811
  delete this.peers[id];
848
- delete this.peerTransceivers[id];
849
812
  this.initiators.delete(id);
850
- this.peers[id] = await this.createPeer(id);
813
+ this.peers[id] = this.createPeer(id);
851
814
  }
852
815
  }
853
816
  if (this.peers[id].signalingState !== "stable" && this.peers[id].signalingState !== "have-local-offer") {
@@ -860,7 +823,6 @@ var VideoSDKCore = class {
860
823
  type: "offer",
861
824
  sdp
862
825
  });
863
- this.captureScreenMid(id);
864
826
  const pending = this.pendingIceCandidates[id] || [];
865
827
  for (const candidate of pending) {
866
828
  try {
@@ -890,26 +852,18 @@ var VideoSDKCore = class {
890
852
  );
891
853
  }
892
854
  }
893
- // CLEANUP
855
+ // ---------------- CLEANUP ----------------
894
856
  closePeer(id) {
895
857
  const pc = this.peers[id];
896
858
  if (!pc) return;
897
859
  pc.ontrack = null;
898
860
  pc.onicecandidate = null;
899
861
  pc.onconnectionstatechange = null;
900
- pc.oniceconnectionstatechange = null;
901
862
  pc.close();
902
863
  delete this.peers[id];
903
- delete this.peerTransceivers[id];
904
864
  this.initiators.delete(id);
905
865
  this.state.removeParticipant(id);
906
866
  }
907
- // SCREEN SHARE (TRANSCEIVER-BASED)
908
- /**
909
- * Start screen sharing using replaceTrack on the pre-established screen transceiver.
910
- * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).
911
- * Just swap the track and update direction if needed.
912
- */
913
867
  async startScreenShare() {
914
868
  try {
915
869
  if (this.state.presenterId && this.state.presenterId !== this.myId) {
@@ -920,55 +874,35 @@ var VideoSDKCore = class {
920
874
  }
921
875
  this.screenStream = await navigator.mediaDevices.getDisplayMedia({
922
876
  video: true
877
+ // audio: true,
923
878
  });
924
- const screenTrack = this.screenStream.getVideoTracks()[0];
925
- if (!screenTrack) {
926
- throw new Error("No video track in screen stream");
927
- }
928
879
  this.isScreenSharing = true;
929
880
  this.state.updateLocalParticipant({
930
881
  media: {
931
882
  isScreenSharing: true,
932
883
  screenStream: this.screenStream,
933
- screenTrack
884
+ screenTrack: this.screenStream.getVideoTracks()[0]
934
885
  }
935
886
  });
936
887
  this.state.setPresenterId(this.myId);
937
- screenTrack.onended = () => {
938
- console.log("[Screen Share] User stopped via browser button");
888
+ this.screenStream.getVideoTracks()[0].onended = () => {
939
889
  this.stopScreenShare();
940
890
  };
941
- for (const [peerId, pc] of Object.entries(this.peers)) {
942
- const txInfo = this.peerTransceivers[peerId];
943
- if (!txInfo) {
944
- console.warn(
945
- `[Screen Share] No transceiver info for ${peerId}, skipping`
946
- );
947
- continue;
948
- }
949
- try {
950
- await txInfo.screenTransceiver.sender.replaceTrack(screenTrack);
951
- if (txInfo.screenTransceiver.currentDirection === "recvonly") {
952
- txInfo.screenTransceiver.direction = "sendrecv";
953
- console.log(
954
- `[Screen Share] Flipped ${peerId} screen transceiver to sendrecv`
955
- );
956
- await this.createOffer(peerId, true);
957
- }
958
- } catch (err) {
959
- console.error(
960
- `[Screen Share] Failed to update transceiver for ${peerId}:`,
961
- err
962
- );
963
- }
964
- }
891
+ Object.entries(this.peers).forEach(([peerId, pc]) => {
892
+ this.screenSenders[peerId] = [];
893
+ this.screenStream.getTracks().forEach((track) => {
894
+ const sender = pc.addTrack(track, this.screenStream);
895
+ this.screenSenders[peerId].push(sender);
896
+ });
897
+ this.createOffer(peerId, true);
898
+ });
965
899
  this.send({
966
900
  type: "SCREEN_SHARE_START",
967
901
  sender: this.myId,
968
902
  room_id: this.room.id,
903
+ camera_id: this.localStream?.id.replace(/[{}]/g, ""),
969
904
  stream_id: this.screenStream.id.replace(/[{}]/g, "")
970
905
  });
971
- console.log("[Screen Share] Started successfully");
972
906
  return this.screenStream;
973
907
  } catch (err) {
974
908
  this.emitError(
@@ -982,32 +916,21 @@ var VideoSDKCore = class {
982
916
  throw err;
983
917
  }
984
918
  }
985
- /**
986
- * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.
987
- */
988
- async stopScreenShare() {
919
+ stopScreenShare() {
989
920
  if (!this.screenStream) return;
990
- console.log("[Screen Share] Stopping...");
991
921
  this.screenStream.getTracks().forEach((t) => t.stop());
992
- for (const [peerId, pc] of Object.entries(this.peers)) {
993
- const txInfo = this.peerTransceivers[peerId];
994
- if (!txInfo) continue;
995
- try {
996
- await txInfo.screenTransceiver.sender.replaceTrack(null);
997
- if (txInfo.screenTransceiver.currentDirection === "sendrecv") {
998
- txInfo.screenTransceiver.direction = "recvonly";
999
- console.log(
1000
- `[Screen Share] Flipped ${peerId} screen transceiver to recvonly`
1001
- );
1002
- await this.createOffer(peerId, true);
922
+ Object.entries(this.peers).forEach(([peerId, pc]) => {
923
+ const senders = this.screenSenders[peerId] || [];
924
+ senders.forEach((sender) => {
925
+ try {
926
+ pc.removeTrack(sender);
927
+ } catch (err) {
928
+ console.warn(err);
1003
929
  }
1004
- } catch (err) {
1005
- console.error(
1006
- `[Screen Share] Failed to clear transceiver for ${peerId}:`,
1007
- err
1008
- );
1009
- }
1010
- }
930
+ });
931
+ delete this.screenSenders[peerId];
932
+ this.createOffer(peerId, true);
933
+ });
1011
934
  this.screenStream = null;
1012
935
  this.isScreenSharing = false;
1013
936
  this.state.updateLocalParticipant({
@@ -1025,9 +948,7 @@ var VideoSDKCore = class {
1025
948
  sender: this.myId,
1026
949
  room_id: this.room.id
1027
950
  });
1028
- console.log("[Screen Share] Stopped");
1029
951
  }
1030
- // CHAT
1031
952
  sendChatMessage(payload) {
1032
953
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
1033
954
  console.warn("WS not connected");
@@ -1060,13 +981,11 @@ var VideoSDKCore = class {
1060
981
  client_ts: Date.now()
1061
982
  });
1062
983
  }
1063
- // DISCONNECT
1064
- async disconnect() {
984
+ disconnect() {
1065
985
  this.intentionalDisconnect = true;
1066
- await this.stopScreenShare();
986
+ this.stopScreenShare();
1067
987
  Object.values(this.peers).forEach((pc) => pc.close());
1068
988
  this.peers = {};
1069
- this.peerTransceivers = {};
1070
989
  this.initiators.clear();
1071
990
  this.stopHeartbeat();
1072
991
  if (this.ws?.readyState === WebSocket.OPEN) {