@afosecure/meetingsdk 1.3.0 → 1.3.2
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 +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +158 -61
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +158 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -177,7 +177,8 @@ var VideoSDKCore = class {
|
|
|
177
177
|
this.localStream = null;
|
|
178
178
|
this.screenStream = null;
|
|
179
179
|
this.isScreenSharing = false;
|
|
180
|
-
|
|
180
|
+
// Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }
|
|
181
|
+
this.peerTransceivers = {};
|
|
181
182
|
this.pingInterval = null;
|
|
182
183
|
this.pendingIceCandidates = {};
|
|
183
184
|
this.pendingOffers = {};
|
|
@@ -206,7 +207,7 @@ var VideoSDKCore = class {
|
|
|
206
207
|
this.joinRejecter = void 0;
|
|
207
208
|
console.error("[MeetingSDK Error]", err);
|
|
208
209
|
}
|
|
209
|
-
//
|
|
210
|
+
// STREAM
|
|
210
211
|
async initLocal(video, name) {
|
|
211
212
|
this.participantName = name;
|
|
212
213
|
try {
|
|
@@ -243,7 +244,7 @@ var VideoSDKCore = class {
|
|
|
243
244
|
throw err;
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
|
-
//
|
|
247
|
+
// CONNECT
|
|
247
248
|
async connect(roomId, name) {
|
|
248
249
|
this.room.id = roomId;
|
|
249
250
|
this.reset();
|
|
@@ -389,10 +390,11 @@ var VideoSDKCore = class {
|
|
|
389
390
|
this.pingInterval = null;
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
|
-
//
|
|
393
|
+
// RESET
|
|
393
394
|
reset() {
|
|
394
395
|
Object.values(this.peers).forEach((pc) => pc.close());
|
|
395
396
|
this.peers = {};
|
|
397
|
+
this.peerTransceivers = {};
|
|
396
398
|
this.initiators.clear();
|
|
397
399
|
this.pendingIceCandidates = {};
|
|
398
400
|
this.state.resetRemoteState();
|
|
@@ -447,6 +449,7 @@ var VideoSDKCore = class {
|
|
|
447
449
|
type: "answer",
|
|
448
450
|
sdp: msg.payload
|
|
449
451
|
});
|
|
452
|
+
this.captureScreenMid(msg.sender);
|
|
450
453
|
await this.flushIce(msg.sender, pc);
|
|
451
454
|
} catch (err) {
|
|
452
455
|
console.error("[Signaling] Failed to apply answer:", err);
|
|
@@ -483,7 +486,6 @@ var VideoSDKCore = class {
|
|
|
483
486
|
if (msg.presenterId) {
|
|
484
487
|
this.state.setPresenterId(msg.presenterId);
|
|
485
488
|
this.events.onScreenShareStarted?.(msg.presenterId, null);
|
|
486
|
-
this.state.setPresenterId(msg.presenterId);
|
|
487
489
|
}
|
|
488
490
|
for (const p of msg.participants || []) {
|
|
489
491
|
if (!p?.id || p.id === this.myId) continue;
|
|
@@ -566,12 +568,10 @@ var VideoSDKCore = class {
|
|
|
566
568
|
});
|
|
567
569
|
break;
|
|
568
570
|
}
|
|
569
|
-
// ============ NEW: HANDLE JOIN_APPROVED WITH RECONNECT ============
|
|
570
571
|
case "JOIN_APPROVED": {
|
|
571
572
|
await this.handleJoinApproved(msg);
|
|
572
573
|
break;
|
|
573
574
|
}
|
|
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");
|
|
@@ -624,8 +624,7 @@ var VideoSDKCore = class {
|
|
|
624
624
|
if (!this.state.presenterId) {
|
|
625
625
|
this.state.setPresenterId(peerId2);
|
|
626
626
|
}
|
|
627
|
-
|
|
628
|
-
this.events.onScreenShareStarted?.(peerId2, screenStream || null);
|
|
627
|
+
this.events.onScreenShareStarted?.(peerId2, null);
|
|
629
628
|
break;
|
|
630
629
|
}
|
|
631
630
|
case "SCREEN_SHARE_STOP": {
|
|
@@ -652,8 +651,7 @@ var VideoSDKCore = class {
|
|
|
652
651
|
}
|
|
653
652
|
}
|
|
654
653
|
}
|
|
655
|
-
|
|
656
|
-
createPeer(id) {
|
|
654
|
+
async createPeer(id) {
|
|
657
655
|
if (!this.localStream) throw new Error("No local stream");
|
|
658
656
|
if (!this.iceServers || this.iceServers.length === 0) {
|
|
659
657
|
throw new Error(
|
|
@@ -661,7 +659,9 @@ var VideoSDKCore = class {
|
|
|
661
659
|
);
|
|
662
660
|
}
|
|
663
661
|
console.log(
|
|
664
|
-
"
|
|
662
|
+
"Creating peer connection for",
|
|
663
|
+
id,
|
|
664
|
+
"with tracks:",
|
|
665
665
|
this.localStream.getTracks().map((t) => ({
|
|
666
666
|
kind: t.kind,
|
|
667
667
|
enabled: t.enabled,
|
|
@@ -671,20 +671,52 @@ var VideoSDKCore = class {
|
|
|
671
671
|
const pc = new RTCPeerConnection({
|
|
672
672
|
iceServers: this.iceServers
|
|
673
673
|
});
|
|
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
|
+
};
|
|
674
703
|
pc.ontrack = (event) => {
|
|
704
|
+
const transceiver = event.transceiver;
|
|
705
|
+
const isScreenTrack = transceiver === this.peerTransceivers[id]?.screenTransceiver;
|
|
706
|
+
console.log(
|
|
707
|
+
`[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`
|
|
708
|
+
);
|
|
675
709
|
const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
|
|
676
|
-
const participant = this.state.getParticipant(id);
|
|
677
|
-
const isScreenStream = participant?.media?.isScreenSharing && incomingStream.id === participant?.media?.remoteScreenStreamId;
|
|
678
710
|
if (event.track.muted) {
|
|
679
711
|
event.track.onunmute = () => {
|
|
680
|
-
console.log(
|
|
712
|
+
console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);
|
|
681
713
|
};
|
|
682
714
|
}
|
|
683
|
-
if (
|
|
684
|
-
const
|
|
715
|
+
if (isScreenTrack) {
|
|
716
|
+
const videoTrack2 = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0];
|
|
685
717
|
this.state.updateParticipantMedia(id, {
|
|
686
718
|
screenStream: incomingStream,
|
|
687
|
-
screenTrack:
|
|
719
|
+
screenTrack: videoTrack2,
|
|
688
720
|
isScreenSharing: true
|
|
689
721
|
});
|
|
690
722
|
if (!this.state.presenterId) {
|
|
@@ -710,29 +742,41 @@ var VideoSDKCore = class {
|
|
|
710
742
|
});
|
|
711
743
|
};
|
|
712
744
|
pc.oniceconnectionstatechange = () => {
|
|
713
|
-
console.log(`ICE Connection
|
|
745
|
+
console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);
|
|
714
746
|
};
|
|
715
747
|
pc.onconnectionstatechange = () => {
|
|
748
|
+
console.log(`[Connection] ${id}: ${pc.connectionState}`);
|
|
716
749
|
if (pc.connectionState === "failed") {
|
|
717
750
|
try {
|
|
718
751
|
pc.restartIce();
|
|
719
|
-
} catch {
|
|
752
|
+
} catch (e) {
|
|
753
|
+
console.warn("Failed to restart ICE:", e);
|
|
720
754
|
}
|
|
721
755
|
}
|
|
722
756
|
};
|
|
723
|
-
this.localStream.getTracks().forEach((track) => {
|
|
724
|
-
pc.addTrack(track, this.localStream);
|
|
725
|
-
});
|
|
726
|
-
if (this.isScreenSharing && this.screenStream) {
|
|
727
|
-
this.screenSenders[id] = [];
|
|
728
|
-
this.screenStream.getTracks().forEach((track) => {
|
|
729
|
-
const sender = pc.addTrack(track, this.screenStream);
|
|
730
|
-
this.screenSenders[id].push(sender);
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
757
|
return pc;
|
|
734
758
|
}
|
|
735
|
-
|
|
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
|
+
);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
// OFFER
|
|
736
780
|
async createOffer(id, isRenegotiation = false) {
|
|
737
781
|
if (!isRenegotiation && !this.shouldInitiate(id)) {
|
|
738
782
|
console.debug(
|
|
@@ -750,12 +794,13 @@ var VideoSDKCore = class {
|
|
|
750
794
|
this.initiators.add(id);
|
|
751
795
|
}
|
|
752
796
|
if (!this.peers[id]) {
|
|
753
|
-
this.peers[id] = this.createPeer(id);
|
|
797
|
+
this.peers[id] = await this.createPeer(id);
|
|
754
798
|
}
|
|
755
799
|
const pc = this.peers[id];
|
|
756
800
|
try {
|
|
757
801
|
const offer = await pc.createOffer();
|
|
758
802
|
await pc.setLocalDescription(offer);
|
|
803
|
+
this.captureScreenMid(id);
|
|
759
804
|
this.send({
|
|
760
805
|
type: "OFFER",
|
|
761
806
|
payload: offer.sdp,
|
|
@@ -765,12 +810,18 @@ var VideoSDKCore = class {
|
|
|
765
810
|
console.debug(`[Offer] Sent to ${id}`);
|
|
766
811
|
} catch (err) {
|
|
767
812
|
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
|
+
);
|
|
768
819
|
}
|
|
769
820
|
}
|
|
770
821
|
shouldInitiate(peerId) {
|
|
771
822
|
return this.myId < peerId;
|
|
772
823
|
}
|
|
773
|
-
//
|
|
824
|
+
// ANSWER
|
|
774
825
|
async handleOffer(sdp, id) {
|
|
775
826
|
if (!this.iceServers || this.iceServers.length === 0) {
|
|
776
827
|
console.warn("[Offer] Waiting for iceServers, queuing offer from", id);
|
|
@@ -778,7 +829,7 @@ var VideoSDKCore = class {
|
|
|
778
829
|
return;
|
|
779
830
|
}
|
|
780
831
|
if (!this.peers[id]) {
|
|
781
|
-
this.peers[id] = this.createPeer(id);
|
|
832
|
+
this.peers[id] = await this.createPeer(id);
|
|
782
833
|
}
|
|
783
834
|
const pc = this.peers[id];
|
|
784
835
|
try {
|
|
@@ -794,8 +845,9 @@ var VideoSDKCore = class {
|
|
|
794
845
|
);
|
|
795
846
|
pc.close();
|
|
796
847
|
delete this.peers[id];
|
|
848
|
+
delete this.peerTransceivers[id];
|
|
797
849
|
this.initiators.delete(id);
|
|
798
|
-
this.peers[id] = this.createPeer(id);
|
|
850
|
+
this.peers[id] = await this.createPeer(id);
|
|
799
851
|
}
|
|
800
852
|
}
|
|
801
853
|
if (this.peers[id].signalingState !== "stable" && this.peers[id].signalingState !== "have-local-offer") {
|
|
@@ -808,6 +860,7 @@ var VideoSDKCore = class {
|
|
|
808
860
|
type: "offer",
|
|
809
861
|
sdp
|
|
810
862
|
});
|
|
863
|
+
this.captureScreenMid(id);
|
|
811
864
|
const pending = this.pendingIceCandidates[id] || [];
|
|
812
865
|
for (const candidate of pending) {
|
|
813
866
|
try {
|
|
@@ -837,18 +890,26 @@ var VideoSDKCore = class {
|
|
|
837
890
|
);
|
|
838
891
|
}
|
|
839
892
|
}
|
|
840
|
-
//
|
|
893
|
+
// CLEANUP
|
|
841
894
|
closePeer(id) {
|
|
842
895
|
const pc = this.peers[id];
|
|
843
896
|
if (!pc) return;
|
|
844
897
|
pc.ontrack = null;
|
|
845
898
|
pc.onicecandidate = null;
|
|
846
899
|
pc.onconnectionstatechange = null;
|
|
900
|
+
pc.oniceconnectionstatechange = null;
|
|
847
901
|
pc.close();
|
|
848
902
|
delete this.peers[id];
|
|
903
|
+
delete this.peerTransceivers[id];
|
|
849
904
|
this.initiators.delete(id);
|
|
850
905
|
this.state.removeParticipant(id);
|
|
851
906
|
}
|
|
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
|
+
*/
|
|
852
913
|
async startScreenShare() {
|
|
853
914
|
try {
|
|
854
915
|
if (this.state.presenterId && this.state.presenterId !== this.myId) {
|
|
@@ -859,34 +920,55 @@ var VideoSDKCore = class {
|
|
|
859
920
|
}
|
|
860
921
|
this.screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
861
922
|
video: true
|
|
862
|
-
// audio: true,
|
|
863
923
|
});
|
|
924
|
+
const screenTrack = this.screenStream.getVideoTracks()[0];
|
|
925
|
+
if (!screenTrack) {
|
|
926
|
+
throw new Error("No video track in screen stream");
|
|
927
|
+
}
|
|
864
928
|
this.isScreenSharing = true;
|
|
865
929
|
this.state.updateLocalParticipant({
|
|
866
930
|
media: {
|
|
867
931
|
isScreenSharing: true,
|
|
868
932
|
screenStream: this.screenStream,
|
|
869
|
-
screenTrack
|
|
933
|
+
screenTrack
|
|
870
934
|
}
|
|
871
935
|
});
|
|
872
936
|
this.state.setPresenterId(this.myId);
|
|
873
|
-
|
|
937
|
+
screenTrack.onended = () => {
|
|
938
|
+
console.log("[Screen Share] User stopped via browser button");
|
|
874
939
|
this.stopScreenShare();
|
|
875
940
|
};
|
|
876
|
-
Object.entries(this.peers)
|
|
877
|
-
this.
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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
|
+
}
|
|
884
965
|
this.send({
|
|
885
966
|
type: "SCREEN_SHARE_START",
|
|
886
967
|
sender: this.myId,
|
|
887
968
|
room_id: this.room.id,
|
|
888
969
|
stream_id: this.screenStream.id.replace(/[{}]/g, "")
|
|
889
970
|
});
|
|
971
|
+
console.log("[Screen Share] Started successfully");
|
|
890
972
|
return this.screenStream;
|
|
891
973
|
} catch (err) {
|
|
892
974
|
this.emitError(
|
|
@@ -900,21 +982,32 @@ var VideoSDKCore = class {
|
|
|
900
982
|
throw err;
|
|
901
983
|
}
|
|
902
984
|
}
|
|
903
|
-
|
|
985
|
+
/**
|
|
986
|
+
* Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.
|
|
987
|
+
*/
|
|
988
|
+
async stopScreenShare() {
|
|
904
989
|
if (!this.screenStream) return;
|
|
990
|
+
console.log("[Screen Share] Stopping...");
|
|
905
991
|
this.screenStream.getTracks().forEach((t) => t.stop());
|
|
906
|
-
Object.entries(this.peers)
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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);
|
|
913
1003
|
}
|
|
914
|
-
})
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1004
|
+
} catch (err) {
|
|
1005
|
+
console.error(
|
|
1006
|
+
`[Screen Share] Failed to clear transceiver for ${peerId}:`,
|
|
1007
|
+
err
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
918
1011
|
this.screenStream = null;
|
|
919
1012
|
this.isScreenSharing = false;
|
|
920
1013
|
this.state.updateLocalParticipant({
|
|
@@ -932,7 +1025,9 @@ var VideoSDKCore = class {
|
|
|
932
1025
|
sender: this.myId,
|
|
933
1026
|
room_id: this.room.id
|
|
934
1027
|
});
|
|
1028
|
+
console.log("[Screen Share] Stopped");
|
|
935
1029
|
}
|
|
1030
|
+
// CHAT
|
|
936
1031
|
sendChatMessage(payload) {
|
|
937
1032
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
938
1033
|
console.warn("WS not connected");
|
|
@@ -965,11 +1060,13 @@ var VideoSDKCore = class {
|
|
|
965
1060
|
client_ts: Date.now()
|
|
966
1061
|
});
|
|
967
1062
|
}
|
|
968
|
-
|
|
1063
|
+
// DISCONNECT
|
|
1064
|
+
async disconnect() {
|
|
969
1065
|
this.intentionalDisconnect = true;
|
|
970
|
-
this.stopScreenShare();
|
|
1066
|
+
await this.stopScreenShare();
|
|
971
1067
|
Object.values(this.peers).forEach((pc) => pc.close());
|
|
972
1068
|
this.peers = {};
|
|
1069
|
+
this.peerTransceivers = {};
|
|
973
1070
|
this.initiators.clear();
|
|
974
1071
|
this.stopHeartbeat();
|
|
975
1072
|
if (this.ws?.readyState === WebSocket.OPEN) {
|