@afosecure/meetingsdk 1.2.9 → 1.3.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.d.mts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +167 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +167 -59
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
133
|
+
private peerTransceivers;
|
|
134
134
|
private pingInterval;
|
|
135
135
|
private pendingIceCandidates;
|
|
136
136
|
private pendingOffers;
|
|
@@ -159,13 +159,32 @@ 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
|
+
*/
|
|
162
171
|
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;
|
|
163
177
|
private createOffer;
|
|
164
178
|
private shouldInitiate;
|
|
165
179
|
private handleOffer;
|
|
166
180
|
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
|
+
*/
|
|
167
186
|
startScreenShare(): Promise<MediaStream>;
|
|
168
|
-
stopScreenShare(): void
|
|
187
|
+
stopScreenShare(): Promise<void>;
|
|
169
188
|
sendChatMessage(payload: ChatInput): void;
|
|
170
189
|
disconnect(): void;
|
|
171
190
|
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
|
|
133
|
+
private peerTransceivers;
|
|
134
134
|
private pingInterval;
|
|
135
135
|
private pendingIceCandidates;
|
|
136
136
|
private pendingOffers;
|
|
@@ -159,13 +159,32 @@ 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
|
+
*/
|
|
162
171
|
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;
|
|
163
177
|
private createOffer;
|
|
164
178
|
private shouldInitiate;
|
|
165
179
|
private handleOffer;
|
|
166
180
|
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
|
+
*/
|
|
167
186
|
startScreenShare(): Promise<MediaStream>;
|
|
168
|
-
stopScreenShare(): void
|
|
187
|
+
stopScreenShare(): Promise<void>;
|
|
169
188
|
sendChatMessage(payload: ChatInput): void;
|
|
170
189
|
disconnect(): void;
|
|
171
190
|
private flushIce;
|
package/dist/index.js
CHANGED
|
@@ -210,7 +210,8 @@ var VideoSDKCore = class {
|
|
|
210
210
|
this.localStream = null;
|
|
211
211
|
this.screenStream = null;
|
|
212
212
|
this.isScreenSharing = false;
|
|
213
|
-
|
|
213
|
+
// Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }
|
|
214
|
+
this.peerTransceivers = {};
|
|
214
215
|
this.pingInterval = null;
|
|
215
216
|
this.pendingIceCandidates = {};
|
|
216
217
|
this.pendingOffers = {};
|
|
@@ -239,7 +240,7 @@ var VideoSDKCore = class {
|
|
|
239
240
|
this.joinRejecter = void 0;
|
|
240
241
|
console.error("[MeetingSDK Error]", err);
|
|
241
242
|
}
|
|
242
|
-
//
|
|
243
|
+
// STREAM
|
|
243
244
|
async initLocal(video, name) {
|
|
244
245
|
this.participantName = name;
|
|
245
246
|
try {
|
|
@@ -276,7 +277,7 @@ var VideoSDKCore = class {
|
|
|
276
277
|
throw err;
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
|
-
//
|
|
280
|
+
// CONNECT
|
|
280
281
|
async connect(roomId, name) {
|
|
281
282
|
this.room.id = roomId;
|
|
282
283
|
this.reset();
|
|
@@ -422,10 +423,11 @@ var VideoSDKCore = class {
|
|
|
422
423
|
this.pingInterval = null;
|
|
423
424
|
}
|
|
424
425
|
}
|
|
425
|
-
//
|
|
426
|
+
// RESET
|
|
426
427
|
reset() {
|
|
427
428
|
Object.values(this.peers).forEach((pc) => pc.close());
|
|
428
429
|
this.peers = {};
|
|
430
|
+
this.peerTransceivers = {};
|
|
429
431
|
this.initiators.clear();
|
|
430
432
|
this.pendingIceCandidates = {};
|
|
431
433
|
this.state.resetRemoteState();
|
|
@@ -480,6 +482,7 @@ var VideoSDKCore = class {
|
|
|
480
482
|
type: "answer",
|
|
481
483
|
sdp: msg.payload
|
|
482
484
|
});
|
|
485
|
+
this.captureScreenMid(msg.sender);
|
|
483
486
|
await this.flushIce(msg.sender, pc);
|
|
484
487
|
} catch (err) {
|
|
485
488
|
console.error("[Signaling] Failed to apply answer:", err);
|
|
@@ -525,11 +528,14 @@ var VideoSDKCore = class {
|
|
|
525
528
|
console.log(
|
|
526
529
|
`[Existing Users] ${p.name} is sharing screen (stream: ${p.remoteScreenStreamId})`
|
|
527
530
|
);
|
|
528
|
-
this.state.setPresenterId(p.
|
|
531
|
+
this.state.setPresenterId(p.id);
|
|
529
532
|
this.state.updateParticipantMedia(p.id, {
|
|
530
533
|
isScreenSharing: true,
|
|
531
534
|
remoteScreenStreamId: p.remoteScreenStreamId
|
|
532
535
|
});
|
|
536
|
+
console.log(
|
|
537
|
+
`[EXISTING_USERS] Pre-seeded screen state for ${p.id}, waiting for ontrack`
|
|
538
|
+
);
|
|
533
539
|
}
|
|
534
540
|
if (this.shouldInitiate(p.id)) {
|
|
535
541
|
await this.createOffer(p.id);
|
|
@@ -595,12 +601,10 @@ var VideoSDKCore = class {
|
|
|
595
601
|
});
|
|
596
602
|
break;
|
|
597
603
|
}
|
|
598
|
-
// ============ NEW: HANDLE JOIN_APPROVED WITH RECONNECT ============
|
|
599
604
|
case "JOIN_APPROVED": {
|
|
600
605
|
await this.handleJoinApproved(msg);
|
|
601
606
|
break;
|
|
602
607
|
}
|
|
603
|
-
// ============ END: JOIN_APPROVED ============
|
|
604
608
|
case "JOIN_REJECTED": {
|
|
605
609
|
const decision = "rejected";
|
|
606
610
|
console.log("JOIN_REJECTED - user not allowed to join");
|
|
@@ -653,8 +657,7 @@ var VideoSDKCore = class {
|
|
|
653
657
|
if (!this.state.presenterId) {
|
|
654
658
|
this.state.setPresenterId(peerId2);
|
|
655
659
|
}
|
|
656
|
-
|
|
657
|
-
this.events.onScreenShareStarted?.(peerId2, screenStream || null);
|
|
660
|
+
this.events.onScreenShareStarted?.(peerId2, null);
|
|
658
661
|
break;
|
|
659
662
|
}
|
|
660
663
|
case "SCREEN_SHARE_STOP": {
|
|
@@ -681,8 +684,17 @@ var VideoSDKCore = class {
|
|
|
681
684
|
}
|
|
682
685
|
}
|
|
683
686
|
}
|
|
684
|
-
//
|
|
685
|
-
|
|
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) {
|
|
686
698
|
if (!this.localStream) throw new Error("No local stream");
|
|
687
699
|
if (!this.iceServers || this.iceServers.length === 0) {
|
|
688
700
|
throw new Error(
|
|
@@ -690,7 +702,9 @@ var VideoSDKCore = class {
|
|
|
690
702
|
);
|
|
691
703
|
}
|
|
692
704
|
console.log(
|
|
693
|
-
"
|
|
705
|
+
"Creating peer connection for",
|
|
706
|
+
id,
|
|
707
|
+
"with tracks:",
|
|
694
708
|
this.localStream.getTracks().map((t) => ({
|
|
695
709
|
kind: t.kind,
|
|
696
710
|
enabled: t.enabled,
|
|
@@ -700,20 +714,52 @@ var VideoSDKCore = class {
|
|
|
700
714
|
const pc = new RTCPeerConnection({
|
|
701
715
|
iceServers: this.iceServers
|
|
702
716
|
});
|
|
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
|
+
};
|
|
703
746
|
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
|
+
);
|
|
704
752
|
const incomingStream = event.streams?.[0] || new MediaStream([event.track]);
|
|
705
|
-
const participant = this.state.getParticipant(id);
|
|
706
|
-
const isScreenStream = incomingStream.id === participant?.media?.remoteScreenStreamId;
|
|
707
753
|
if (event.track.muted) {
|
|
708
754
|
event.track.onunmute = () => {
|
|
709
|
-
console.log(
|
|
755
|
+
console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);
|
|
710
756
|
};
|
|
711
757
|
}
|
|
712
|
-
if (
|
|
713
|
-
const
|
|
758
|
+
if (isScreenTrack) {
|
|
759
|
+
const videoTrack2 = event.track.kind === "video" ? event.track : incomingStream.getVideoTracks()[0];
|
|
714
760
|
this.state.updateParticipantMedia(id, {
|
|
715
761
|
screenStream: incomingStream,
|
|
716
|
-
screenTrack:
|
|
762
|
+
screenTrack: videoTrack2,
|
|
717
763
|
isScreenSharing: true
|
|
718
764
|
});
|
|
719
765
|
if (!this.state.presenterId) {
|
|
@@ -739,29 +785,41 @@ var VideoSDKCore = class {
|
|
|
739
785
|
});
|
|
740
786
|
};
|
|
741
787
|
pc.oniceconnectionstatechange = () => {
|
|
742
|
-
console.log(`ICE Connection
|
|
788
|
+
console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);
|
|
743
789
|
};
|
|
744
790
|
pc.onconnectionstatechange = () => {
|
|
791
|
+
console.log(`[Connection] ${id}: ${pc.connectionState}`);
|
|
745
792
|
if (pc.connectionState === "failed") {
|
|
746
793
|
try {
|
|
747
794
|
pc.restartIce();
|
|
748
|
-
} catch {
|
|
795
|
+
} catch (e) {
|
|
796
|
+
console.warn("Failed to restart ICE:", e);
|
|
749
797
|
}
|
|
750
798
|
}
|
|
751
799
|
};
|
|
752
|
-
this.localStream.getTracks().forEach((track) => {
|
|
753
|
-
pc.addTrack(track, this.localStream);
|
|
754
|
-
});
|
|
755
|
-
if (this.isScreenSharing && this.screenStream) {
|
|
756
|
-
this.screenSenders[id] = [];
|
|
757
|
-
this.screenStream.getTracks().forEach((track) => {
|
|
758
|
-
const sender = pc.addTrack(track, this.screenStream);
|
|
759
|
-
this.screenSenders[id].push(sender);
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
800
|
return pc;
|
|
763
801
|
}
|
|
764
|
-
|
|
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
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
// OFFER
|
|
765
823
|
async createOffer(id, isRenegotiation = false) {
|
|
766
824
|
if (!isRenegotiation && !this.shouldInitiate(id)) {
|
|
767
825
|
console.debug(
|
|
@@ -779,12 +837,13 @@ var VideoSDKCore = class {
|
|
|
779
837
|
this.initiators.add(id);
|
|
780
838
|
}
|
|
781
839
|
if (!this.peers[id]) {
|
|
782
|
-
this.peers[id] = this.createPeer(id);
|
|
840
|
+
this.peers[id] = await this.createPeer(id);
|
|
783
841
|
}
|
|
784
842
|
const pc = this.peers[id];
|
|
785
843
|
try {
|
|
786
844
|
const offer = await pc.createOffer();
|
|
787
845
|
await pc.setLocalDescription(offer);
|
|
846
|
+
this.captureScreenMid(id);
|
|
788
847
|
this.send({
|
|
789
848
|
type: "OFFER",
|
|
790
849
|
payload: offer.sdp,
|
|
@@ -794,12 +853,18 @@ var VideoSDKCore = class {
|
|
|
794
853
|
console.debug(`[Offer] Sent to ${id}`);
|
|
795
854
|
} catch (err) {
|
|
796
855
|
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
|
+
);
|
|
797
862
|
}
|
|
798
863
|
}
|
|
799
864
|
shouldInitiate(peerId) {
|
|
800
865
|
return this.myId < peerId;
|
|
801
866
|
}
|
|
802
|
-
//
|
|
867
|
+
// ANSWER
|
|
803
868
|
async handleOffer(sdp, id) {
|
|
804
869
|
if (!this.iceServers || this.iceServers.length === 0) {
|
|
805
870
|
console.warn("[Offer] Waiting for iceServers, queuing offer from", id);
|
|
@@ -807,7 +872,7 @@ var VideoSDKCore = class {
|
|
|
807
872
|
return;
|
|
808
873
|
}
|
|
809
874
|
if (!this.peers[id]) {
|
|
810
|
-
this.peers[id] = this.createPeer(id);
|
|
875
|
+
this.peers[id] = await this.createPeer(id);
|
|
811
876
|
}
|
|
812
877
|
const pc = this.peers[id];
|
|
813
878
|
try {
|
|
@@ -823,8 +888,9 @@ var VideoSDKCore = class {
|
|
|
823
888
|
);
|
|
824
889
|
pc.close();
|
|
825
890
|
delete this.peers[id];
|
|
891
|
+
delete this.peerTransceivers[id];
|
|
826
892
|
this.initiators.delete(id);
|
|
827
|
-
this.peers[id] = this.createPeer(id);
|
|
893
|
+
this.peers[id] = await this.createPeer(id);
|
|
828
894
|
}
|
|
829
895
|
}
|
|
830
896
|
if (this.peers[id].signalingState !== "stable" && this.peers[id].signalingState !== "have-local-offer") {
|
|
@@ -837,6 +903,7 @@ var VideoSDKCore = class {
|
|
|
837
903
|
type: "offer",
|
|
838
904
|
sdp
|
|
839
905
|
});
|
|
906
|
+
this.captureScreenMid(id);
|
|
840
907
|
const pending = this.pendingIceCandidates[id] || [];
|
|
841
908
|
for (const candidate of pending) {
|
|
842
909
|
try {
|
|
@@ -866,18 +933,26 @@ var VideoSDKCore = class {
|
|
|
866
933
|
);
|
|
867
934
|
}
|
|
868
935
|
}
|
|
869
|
-
//
|
|
936
|
+
// CLEANUP
|
|
870
937
|
closePeer(id) {
|
|
871
938
|
const pc = this.peers[id];
|
|
872
939
|
if (!pc) return;
|
|
873
940
|
pc.ontrack = null;
|
|
874
941
|
pc.onicecandidate = null;
|
|
875
942
|
pc.onconnectionstatechange = null;
|
|
943
|
+
pc.oniceconnectionstatechange = null;
|
|
876
944
|
pc.close();
|
|
877
945
|
delete this.peers[id];
|
|
946
|
+
delete this.peerTransceivers[id];
|
|
878
947
|
this.initiators.delete(id);
|
|
879
948
|
this.state.removeParticipant(id);
|
|
880
949
|
}
|
|
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
|
+
*/
|
|
881
956
|
async startScreenShare() {
|
|
882
957
|
try {
|
|
883
958
|
if (this.state.presenterId && this.state.presenterId !== this.myId) {
|
|
@@ -888,34 +963,55 @@ var VideoSDKCore = class {
|
|
|
888
963
|
}
|
|
889
964
|
this.screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
890
965
|
video: true
|
|
891
|
-
// audio: true,
|
|
892
966
|
});
|
|
967
|
+
const screenTrack = this.screenStream.getVideoTracks()[0];
|
|
968
|
+
if (!screenTrack) {
|
|
969
|
+
throw new Error("No video track in screen stream");
|
|
970
|
+
}
|
|
893
971
|
this.isScreenSharing = true;
|
|
894
972
|
this.state.updateLocalParticipant({
|
|
895
973
|
media: {
|
|
896
974
|
isScreenSharing: true,
|
|
897
975
|
screenStream: this.screenStream,
|
|
898
|
-
screenTrack
|
|
976
|
+
screenTrack
|
|
899
977
|
}
|
|
900
978
|
});
|
|
901
979
|
this.state.setPresenterId(this.myId);
|
|
902
|
-
|
|
980
|
+
screenTrack.onended = () => {
|
|
981
|
+
console.log("[Screen Share] User stopped via browser button");
|
|
903
982
|
this.stopScreenShare();
|
|
904
983
|
};
|
|
905
|
-
Object.entries(this.peers)
|
|
906
|
-
this.
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
+
}
|
|
913
1008
|
this.send({
|
|
914
1009
|
type: "SCREEN_SHARE_START",
|
|
915
1010
|
sender: this.myId,
|
|
916
1011
|
room_id: this.room.id,
|
|
917
1012
|
stream_id: this.screenStream.id.replace(/[{}]/g, "")
|
|
918
1013
|
});
|
|
1014
|
+
console.log("[Screen Share] Started successfully");
|
|
919
1015
|
return this.screenStream;
|
|
920
1016
|
} catch (err) {
|
|
921
1017
|
this.emitError(
|
|
@@ -929,21 +1025,29 @@ var VideoSDKCore = class {
|
|
|
929
1025
|
throw err;
|
|
930
1026
|
}
|
|
931
1027
|
}
|
|
932
|
-
stopScreenShare() {
|
|
1028
|
+
async stopScreenShare() {
|
|
933
1029
|
if (!this.screenStream) return;
|
|
1030
|
+
console.log("[Screen Share] Stopping...");
|
|
934
1031
|
this.screenStream.getTracks().forEach((t) => t.stop());
|
|
935
|
-
Object.entries(this.peers)
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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);
|
|
942
1043
|
}
|
|
943
|
-
})
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1044
|
+
} catch (err) {
|
|
1045
|
+
console.error(
|
|
1046
|
+
`[Screen Share] Failed to clear transceiver for ${peerId}:`,
|
|
1047
|
+
err
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
947
1051
|
this.screenStream = null;
|
|
948
1052
|
this.isScreenSharing = false;
|
|
949
1053
|
this.state.updateLocalParticipant({
|
|
@@ -961,7 +1065,9 @@ var VideoSDKCore = class {
|
|
|
961
1065
|
sender: this.myId,
|
|
962
1066
|
room_id: this.room.id
|
|
963
1067
|
});
|
|
1068
|
+
console.log("[Screen Share] Stopped");
|
|
964
1069
|
}
|
|
1070
|
+
// CHAT
|
|
965
1071
|
sendChatMessage(payload) {
|
|
966
1072
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
967
1073
|
console.warn("WS not connected");
|
|
@@ -994,11 +1100,13 @@ var VideoSDKCore = class {
|
|
|
994
1100
|
client_ts: Date.now()
|
|
995
1101
|
});
|
|
996
1102
|
}
|
|
1103
|
+
// DISCONNECT
|
|
997
1104
|
disconnect() {
|
|
998
1105
|
this.intentionalDisconnect = true;
|
|
999
1106
|
this.stopScreenShare();
|
|
1000
1107
|
Object.values(this.peers).forEach((pc) => pc.close());
|
|
1001
1108
|
this.peers = {};
|
|
1109
|
+
this.peerTransceivers = {};
|
|
1002
1110
|
this.initiators.clear();
|
|
1003
1111
|
this.stopHeartbeat();
|
|
1004
1112
|
if (this.ws?.readyState === WebSocket.OPEN) {
|