@afosecure/meetingsdk 1.3.1 → 1.3.3

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 CHANGED
@@ -130,7 +130,7 @@ declare class VideoSDKCore {
130
130
  private localStream;
131
131
  private screenStream;
132
132
  private isScreenSharing;
133
- private peerTransceivers;
133
+ private screenSenders;
134
134
  private pingInterval;
135
135
  private pendingIceCandidates;
136
136
  private pendingOffers;
@@ -159,32 +159,13 @@ declare class VideoSDKCore {
159
159
  private reset;
160
160
  private handleJoinApproved;
161
161
  private handle;
162
- /**
163
- * Create a peer connection with pre-established transceiver layout:
164
- * - Audio transceiver (sendrecv)
165
- * - Camera video transceiver (sendrecv)
166
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
167
- *
168
- * This fixed layout ensures late joiners get the screen transceiver m-line
169
- * negotiated from the very first offer, even if no one is sharing yet.
170
- */
171
162
  private createPeer;
172
- /**
173
- * Capture the screen transceiver's MID after SDP negotiation completes.
174
- * The MID is assigned during negotiation and is stable for the life of the connection.
175
- */
176
- private captureScreenMid;
177
163
  private createOffer;
178
164
  private shouldInitiate;
179
165
  private handleOffer;
180
166
  private closePeer;
181
- /**
182
- * Start screen sharing using replaceTrack on the pre-established screen transceiver.
183
- * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).
184
- * Just swap the track and update direction if needed.
185
- */
186
167
  startScreenShare(): Promise<MediaStream>;
187
- stopScreenShare(): Promise<void>;
168
+ stopScreenShare(): void;
188
169
  sendChatMessage(payload: ChatInput): void;
189
170
  disconnect(): void;
190
171
  private flushIce;
package/dist/index.d.ts CHANGED
@@ -130,7 +130,7 @@ declare class VideoSDKCore {
130
130
  private localStream;
131
131
  private screenStream;
132
132
  private isScreenSharing;
133
- private peerTransceivers;
133
+ private screenSenders;
134
134
  private pingInterval;
135
135
  private pendingIceCandidates;
136
136
  private pendingOffers;
@@ -159,32 +159,13 @@ declare class VideoSDKCore {
159
159
  private reset;
160
160
  private handleJoinApproved;
161
161
  private handle;
162
- /**
163
- * Create a peer connection with pre-established transceiver layout:
164
- * - Audio transceiver (sendrecv)
165
- * - Camera video transceiver (sendrecv)
166
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
167
- *
168
- * This fixed layout ensures late joiners get the screen transceiver m-line
169
- * negotiated from the very first offer, even if no one is sharing yet.
170
- */
171
162
  private createPeer;
172
- /**
173
- * Capture the screen transceiver's MID after SDP negotiation completes.
174
- * The MID is assigned during negotiation and is stable for the life of the connection.
175
- */
176
- private captureScreenMid;
177
163
  private createOffer;
178
164
  private shouldInitiate;
179
165
  private handleOffer;
180
166
  private closePeer;
181
- /**
182
- * Start screen sharing using replaceTrack on the pre-established screen transceiver.
183
- * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).
184
- * Just swap the track and update direction if needed.
185
- */
186
167
  startScreenShare(): Promise<MediaStream>;
187
- stopScreenShare(): Promise<void>;
168
+ stopScreenShare(): void;
188
169
  sendChatMessage(payload: ChatInput): void;
189
170
  disconnect(): void;
190
171
  private flushIce;
package/dist/index.js CHANGED
@@ -210,8 +210,7 @@ var VideoSDKCore = class {
210
210
  this.localStream = null;
211
211
  this.screenStream = null;
212
212
  this.isScreenSharing = false;
213
- // Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }
214
- this.peerTransceivers = {};
213
+ this.screenSenders = {};
215
214
  this.pingInterval = null;
216
215
  this.pendingIceCandidates = {};
217
216
  this.pendingOffers = {};
@@ -240,7 +239,7 @@ var VideoSDKCore = class {
240
239
  this.joinRejecter = void 0;
241
240
  console.error("[MeetingSDK Error]", err);
242
241
  }
243
- // STREAM
242
+ // ---------------- STREAM ----------------
244
243
  async initLocal(video, name) {
245
244
  this.participantName = name;
246
245
  try {
@@ -277,7 +276,7 @@ var VideoSDKCore = class {
277
276
  throw err;
278
277
  }
279
278
  }
280
- // CONNECT
279
+ // ---------------- CONNECT ----------------
281
280
  async connect(roomId, name) {
282
281
  this.room.id = roomId;
283
282
  this.reset();
@@ -423,11 +422,10 @@ var VideoSDKCore = class {
423
422
  this.pingInterval = null;
424
423
  }
425
424
  }
426
- // RESET
425
+ // ---------------- RESET ----------------
427
426
  reset() {
428
427
  Object.values(this.peers).forEach((pc) => pc.close());
429
428
  this.peers = {};
430
- this.peerTransceivers = {};
431
429
  this.initiators.clear();
432
430
  this.pendingIceCandidates = {};
433
431
  this.state.resetRemoteState();
@@ -482,7 +480,6 @@ var VideoSDKCore = class {
482
480
  type: "answer",
483
481
  sdp: msg.payload
484
482
  });
485
- this.captureScreenMid(msg.sender);
486
483
  await this.flushIce(msg.sender, pc);
487
484
  } catch (err) {
488
485
  console.error("[Signaling] Failed to apply answer:", err);
@@ -519,6 +516,7 @@ var VideoSDKCore = class {
519
516
  if (msg.presenterId) {
520
517
  this.state.setPresenterId(msg.presenterId);
521
518
  this.events.onScreenShareStarted?.(msg.presenterId, null);
519
+ this.state.setPresenterId(msg.presenterId);
522
520
  }
523
521
  for (const p of msg.participants || []) {
524
522
  if (!p?.id || p.id === this.myId) continue;
@@ -601,10 +599,12 @@ var VideoSDKCore = class {
601
599
  });
602
600
  break;
603
601
  }
602
+ // ============ NEW: HANDLE JOIN_APPROVED WITH RECONNECT ============
604
603
  case "JOIN_APPROVED": {
605
604
  await this.handleJoinApproved(msg);
606
605
  break;
607
606
  }
607
+ // ============ END: JOIN_APPROVED ============
608
608
  case "JOIN_REJECTED": {
609
609
  const decision = "rejected";
610
610
  console.log("JOIN_REJECTED - user not allowed to join");
@@ -657,7 +657,8 @@ var VideoSDKCore = class {
657
657
  if (!this.state.presenterId) {
658
658
  this.state.setPresenterId(peerId2);
659
659
  }
660
- this.events.onScreenShareStarted?.(peerId2, null);
660
+ const screenStream = this.state.getParticipant(peerId2)?.media?.screenStream;
661
+ this.events.onScreenShareStarted?.(peerId2, screenStream || null);
661
662
  break;
662
663
  }
663
664
  case "SCREEN_SHARE_STOP": {
@@ -684,17 +685,8 @@ var VideoSDKCore = class {
684
685
  }
685
686
  }
686
687
  }
687
- // PEER
688
- /**
689
- * Create a peer connection with pre-established transceiver layout:
690
- * - Audio transceiver (sendrecv)
691
- * - Camera video transceiver (sendrecv)
692
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
693
- *
694
- * This fixed layout ensures late joiners get the screen transceiver m-line
695
- * negotiated from the very first offer, even if no one is sharing yet.
696
- */
697
- async createPeer(id) {
688
+ // ---------------- PEER ----------------
689
+ createPeer(id) {
698
690
  if (!this.localStream) throw new Error("No local stream");
699
691
  if (!this.iceServers || this.iceServers.length === 0) {
700
692
  throw new Error(
@@ -702,9 +694,7 @@ var VideoSDKCore = class {
702
694
  );
703
695
  }
704
696
  console.log(
705
- "Creating peer connection for",
706
- id,
707
- "with tracks:",
697
+ "Adding tracks",
708
698
  this.localStream.getTracks().map((t) => ({
709
699
  kind: t.kind,
710
700
  enabled: t.enabled,
@@ -714,52 +704,25 @@ var VideoSDKCore = class {
714
704
  const pc = new RTCPeerConnection({
715
705
  iceServers: this.iceServers
716
706
  });
717
- const audioTransceiver = pc.addTransceiver("audio", {
718
- direction: "sendrecv"
719
- });
720
- const audioTrack = this.localStream.getAudioTracks()[0];
721
- if (audioTrack) {
722
- await audioTransceiver.sender.replaceTrack(audioTrack);
723
- }
724
- const cameraTransceiver = pc.addTransceiver("video", {
725
- direction: "sendrecv"
726
- });
727
- const videoTrack = this.localStream.getVideoTracks()[0];
728
- if (videoTrack) {
729
- await cameraTransceiver.sender.replaceTrack(videoTrack);
730
- }
731
- const screenTransceiver = pc.addTransceiver("video", {
732
- direction: this.isScreenSharing ? "sendrecv" : "recvonly"
733
- });
734
- if (this.isScreenSharing && this.screenStream) {
735
- const screenTrack = this.screenStream.getVideoTracks()[0];
736
- if (screenTrack) {
737
- await screenTransceiver.sender.replaceTrack(screenTrack);
738
- }
739
- }
740
- this.peerTransceivers[id] = {
741
- cameraTransceiver,
742
- screenTransceiver,
743
- screenMid: null
744
- // will be populated after negotiation
745
- };
746
707
  pc.ontrack = (event) => {
747
- const transceiver = event.transceiver;
748
- const isScreenTrack = transceiver.mid === this.peerTransceivers[id]?.screenMid;
749
- console.log(
750
- `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`
751
- );
708
+ console.log("ontrack");
709
+ console.log("kind:", event.track.kind);
710
+ console.log("mid:", event.transceiver.mid);
711
+ console.log("streams:", event.streams);
712
+ console.log("stream id:", event.streams[0]?.id);
752
713
  const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
714
+ const participant = this.state.getParticipant(id);
715
+ const isScreenStream = participant?.media?.isScreenSharing && incomingStream.id === participant?.media?.remoteScreenStreamId;
753
716
  if (event.track.muted) {
754
717
  event.track.onunmute = () => {
755
- console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);
718
+ console.log(`${event.track.kind} track unmuted for ${id}`);
756
719
  };
757
720
  }
758
- if (isScreenTrack) {
759
- const videoTrack2 = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0];
721
+ if (isScreenStream) {
722
+ const videoTrack = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0] || participant?.media?.screenTrack;
760
723
  this.state.updateParticipantMedia(id, {
761
724
  screenStream: incomingStream,
762
- screenTrack: videoTrack2,
725
+ screenTrack: videoTrack,
763
726
  isScreenSharing: true
764
727
  });
765
728
  if (!this.state.presenterId) {
@@ -785,41 +748,29 @@ var VideoSDKCore = class {
785
748
  });
786
749
  };
787
750
  pc.oniceconnectionstatechange = () => {
788
- console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);
751
+ console.log(`ICE Connection State: ${pc.iceConnectionState}`);
789
752
  };
790
753
  pc.onconnectionstatechange = () => {
791
- console.log(`[Connection] ${id}: ${pc.connectionState}`);
792
754
  if (pc.connectionState === "failed") {
793
755
  try {
794
756
  pc.restartIce();
795
- } catch (e) {
796
- console.warn("Failed to restart ICE:", e);
757
+ } catch {
797
758
  }
798
759
  }
799
760
  };
800
- return pc;
801
- }
802
- /**
803
- * Capture the screen transceiver's MID after SDP negotiation completes.
804
- * The MID is assigned during negotiation and is stable for the life of the connection.
805
- */
806
- captureScreenMid(peerId) {
807
- const pc = this.peers[peerId];
808
- if (!pc) return;
809
- const transceivers = pc.getTransceivers();
810
- const screenTransceiver = this.peerTransceivers[peerId]?.screenTransceiver;
811
- if (!screenTransceiver) return;
812
- const negotiatedTransceiver = transceivers.find(
813
- (t) => t === screenTransceiver
814
- );
815
- if (negotiatedTransceiver?.mid) {
816
- this.peerTransceivers[peerId].screenMid = negotiatedTransceiver.mid;
817
- console.log(
818
- `[Negotiation] Captured screenMid for ${peerId}: ${negotiatedTransceiver.mid}`
819
- );
761
+ this.localStream.getTracks().forEach((track) => {
762
+ pc.addTrack(track, this.localStream);
763
+ });
764
+ if (this.isScreenSharing && this.screenStream) {
765
+ this.screenSenders[id] = [];
766
+ this.screenStream.getTracks().forEach((track) => {
767
+ const sender = pc.addTrack(track, this.screenStream);
768
+ this.screenSenders[id].push(sender);
769
+ });
820
770
  }
771
+ return pc;
821
772
  }
822
- // OFFER
773
+ // ---------------- OFFER ----------------
823
774
  async createOffer(id, isRenegotiation = false) {
824
775
  if (!isRenegotiation && !this.shouldInitiate(id)) {
825
776
  console.debug(
@@ -837,13 +788,12 @@ var VideoSDKCore = class {
837
788
  this.initiators.add(id);
838
789
  }
839
790
  if (!this.peers[id]) {
840
- this.peers[id] = await this.createPeer(id);
791
+ this.peers[id] = this.createPeer(id);
841
792
  }
842
793
  const pc = this.peers[id];
843
794
  try {
844
795
  const offer = await pc.createOffer();
845
796
  await pc.setLocalDescription(offer);
846
- this.captureScreenMid(id);
847
797
  this.send({
848
798
  type: "OFFER",
849
799
  payload: offer.sdp,
@@ -853,18 +803,12 @@ var VideoSDKCore = class {
853
803
  console.debug(`[Offer] Sent to ${id}`);
854
804
  } catch (err) {
855
805
  console.error(`[Offer] Failed for ${id}:`, err);
856
- this.emitError(
857
- "OFFER_CREATION_FAILED",
858
- `Failed to create offer for ${id}`,
859
- err,
860
- true
861
- );
862
806
  }
863
807
  }
864
808
  shouldInitiate(peerId) {
865
809
  return this.myId < peerId;
866
810
  }
867
- // ANSWER
811
+ // ---------------- ANSWER ----------------
868
812
  async handleOffer(sdp, id) {
869
813
  if (!this.iceServers || this.iceServers.length === 0) {
870
814
  console.warn("[Offer] Waiting for iceServers, queuing offer from", id);
@@ -872,7 +816,7 @@ var VideoSDKCore = class {
872
816
  return;
873
817
  }
874
818
  if (!this.peers[id]) {
875
- this.peers[id] = await this.createPeer(id);
819
+ this.peers[id] = this.createPeer(id);
876
820
  }
877
821
  const pc = this.peers[id];
878
822
  try {
@@ -888,9 +832,8 @@ var VideoSDKCore = class {
888
832
  );
889
833
  pc.close();
890
834
  delete this.peers[id];
891
- delete this.peerTransceivers[id];
892
835
  this.initiators.delete(id);
893
- this.peers[id] = await this.createPeer(id);
836
+ this.peers[id] = this.createPeer(id);
894
837
  }
895
838
  }
896
839
  if (this.peers[id].signalingState !== "stable" && this.peers[id].signalingState !== "have-local-offer") {
@@ -903,7 +846,6 @@ var VideoSDKCore = class {
903
846
  type: "offer",
904
847
  sdp
905
848
  });
906
- this.captureScreenMid(id);
907
849
  const pending = this.pendingIceCandidates[id] || [];
908
850
  for (const candidate of pending) {
909
851
  try {
@@ -933,26 +875,18 @@ var VideoSDKCore = class {
933
875
  );
934
876
  }
935
877
  }
936
- // CLEANUP
878
+ // ---------------- CLEANUP ----------------
937
879
  closePeer(id) {
938
880
  const pc = this.peers[id];
939
881
  if (!pc) return;
940
882
  pc.ontrack = null;
941
883
  pc.onicecandidate = null;
942
884
  pc.onconnectionstatechange = null;
943
- pc.oniceconnectionstatechange = null;
944
885
  pc.close();
945
886
  delete this.peers[id];
946
- delete this.peerTransceivers[id];
947
887
  this.initiators.delete(id);
948
888
  this.state.removeParticipant(id);
949
889
  }
950
- // SCREEN SHARE (TRANSCEIVER-BASED)
951
- /**
952
- * Start screen sharing using replaceTrack on the pre-established screen transceiver.
953
- * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).
954
- * Just swap the track and update direction if needed.
955
- */
956
890
  async startScreenShare() {
957
891
  try {
958
892
  if (this.state.presenterId && this.state.presenterId !== this.myId) {
@@ -963,55 +897,34 @@ var VideoSDKCore = class {
963
897
  }
964
898
  this.screenStream = await navigator.mediaDevices.getDisplayMedia({
965
899
  video: true
900
+ // audio: true,
966
901
  });
967
- const screenTrack = this.screenStream.getVideoTracks()[0];
968
- if (!screenTrack) {
969
- throw new Error("No video track in screen stream");
970
- }
971
902
  this.isScreenSharing = true;
972
903
  this.state.updateLocalParticipant({
973
904
  media: {
974
905
  isScreenSharing: true,
975
906
  screenStream: this.screenStream,
976
- screenTrack
907
+ screenTrack: this.screenStream.getVideoTracks()[0]
977
908
  }
978
909
  });
979
910
  this.state.setPresenterId(this.myId);
980
- screenTrack.onended = () => {
981
- console.log("[Screen Share] User stopped via browser button");
911
+ this.screenStream.getVideoTracks()[0].onended = () => {
982
912
  this.stopScreenShare();
983
913
  };
984
- for (const [peerId, pc] of Object.entries(this.peers)) {
985
- const txInfo = this.peerTransceivers[peerId];
986
- if (!txInfo) {
987
- console.warn(
988
- `[Screen Share] No transceiver info for ${peerId}, skipping`
989
- );
990
- continue;
991
- }
992
- try {
993
- await txInfo.screenTransceiver.sender.replaceTrack(screenTrack);
994
- if (txInfo.screenTransceiver.currentDirection === "recvonly") {
995
- txInfo.screenTransceiver.direction = "sendrecv";
996
- console.log(
997
- `[Screen Share] Flipped ${peerId} screen transceiver to sendrecv`
998
- );
999
- await this.createOffer(peerId, true);
1000
- }
1001
- } catch (err) {
1002
- console.error(
1003
- `[Screen Share] Failed to update transceiver for ${peerId}:`,
1004
- err
1005
- );
1006
- }
1007
- }
914
+ Object.entries(this.peers).forEach(([peerId, pc]) => {
915
+ this.screenSenders[peerId] = [];
916
+ this.screenStream.getTracks().forEach((track) => {
917
+ const sender = pc.addTrack(track, this.screenStream);
918
+ this.screenSenders[peerId].push(sender);
919
+ });
920
+ this.createOffer(peerId, true);
921
+ });
1008
922
  this.send({
1009
923
  type: "SCREEN_SHARE_START",
1010
924
  sender: this.myId,
1011
925
  room_id: this.room.id,
1012
926
  stream_id: this.screenStream.id.replace(/[{}]/g, "")
1013
927
  });
1014
- console.log("[Screen Share] Started successfully");
1015
928
  return this.screenStream;
1016
929
  } catch (err) {
1017
930
  this.emitError(
@@ -1025,29 +938,21 @@ var VideoSDKCore = class {
1025
938
  throw err;
1026
939
  }
1027
940
  }
1028
- async stopScreenShare() {
941
+ stopScreenShare() {
1029
942
  if (!this.screenStream) return;
1030
- console.log("[Screen Share] Stopping...");
1031
943
  this.screenStream.getTracks().forEach((t) => t.stop());
1032
- for (const [peerId, pc] of Object.entries(this.peers)) {
1033
- const txInfo = this.peerTransceivers[peerId];
1034
- if (!txInfo) continue;
1035
- try {
1036
- await txInfo.screenTransceiver.sender.replaceTrack(null);
1037
- if (txInfo.screenTransceiver.currentDirection === "sendrecv") {
1038
- txInfo.screenTransceiver.direction = "recvonly";
1039
- console.log(
1040
- `[Screen Share] Flipped ${peerId} screen transceiver to recvonly`
1041
- );
1042
- await this.createOffer(peerId, true);
944
+ Object.entries(this.peers).forEach(([peerId, pc]) => {
945
+ const senders = this.screenSenders[peerId] || [];
946
+ senders.forEach((sender) => {
947
+ try {
948
+ pc.removeTrack(sender);
949
+ } catch (err) {
950
+ console.warn(err);
1043
951
  }
1044
- } catch (err) {
1045
- console.error(
1046
- `[Screen Share] Failed to clear transceiver for ${peerId}:`,
1047
- err
1048
- );
1049
- }
1050
- }
952
+ });
953
+ delete this.screenSenders[peerId];
954
+ this.createOffer(peerId, true);
955
+ });
1051
956
  this.screenStream = null;
1052
957
  this.isScreenSharing = false;
1053
958
  this.state.updateLocalParticipant({
@@ -1065,9 +970,7 @@ var VideoSDKCore = class {
1065
970
  sender: this.myId,
1066
971
  room_id: this.room.id
1067
972
  });
1068
- console.log("[Screen Share] Stopped");
1069
973
  }
1070
- // CHAT
1071
974
  sendChatMessage(payload) {
1072
975
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
1073
976
  console.warn("WS not connected");
@@ -1100,13 +1003,11 @@ var VideoSDKCore = class {
1100
1003
  client_ts: Date.now()
1101
1004
  });
1102
1005
  }
1103
- // DISCONNECT
1104
1006
  disconnect() {
1105
1007
  this.intentionalDisconnect = true;
1106
1008
  this.stopScreenShare();
1107
1009
  Object.values(this.peers).forEach((pc) => pc.close());
1108
1010
  this.peers = {};
1109
- this.peerTransceivers = {};
1110
1011
  this.initiators.clear();
1111
1012
  this.stopHeartbeat();
1112
1013
  if (this.ws?.readyState === WebSocket.OPEN) {