@defcomm/meetingsdk 0.1.2 → 0.1.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.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +192 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -89
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -351,6 +351,9 @@ var SFUClient = class {
|
|
|
351
351
|
this.videoSender = null;
|
|
352
352
|
this.audioSender = null;
|
|
353
353
|
this.screenSender = null;
|
|
354
|
+
this.audioTransceiver = null;
|
|
355
|
+
this.cameraTransceiver = null;
|
|
356
|
+
this.screenTransceiver = null;
|
|
354
357
|
this.iceServers = [];
|
|
355
358
|
this.requestId = 0;
|
|
356
359
|
this.publisherRevision = 0;
|
|
@@ -382,6 +385,7 @@ var SFUClient = class {
|
|
|
382
385
|
this.trackMetadata = /* @__PURE__ */ new Map();
|
|
383
386
|
this.participants = /* @__PURE__ */ new Map();
|
|
384
387
|
this.remoteStreams = /* @__PURE__ */ new Map();
|
|
388
|
+
this.remoteScreenStreams = /* @__PURE__ */ new Map();
|
|
385
389
|
this.trackToParticipant = /* @__PURE__ */ new Map();
|
|
386
390
|
this.trackToSource = /* @__PURE__ */ new Map();
|
|
387
391
|
this.localTrackIds = /* @__PURE__ */ new Map();
|
|
@@ -864,16 +868,18 @@ var SFUClient = class {
|
|
|
864
868
|
}
|
|
865
869
|
case "screen_share_stopped": {
|
|
866
870
|
const participantId = payload.participant_id;
|
|
867
|
-
const
|
|
871
|
+
const screenStream = participantId ? this.remoteScreenStreams.get(participantId) : null;
|
|
868
872
|
const trackId = payload.track_id;
|
|
869
|
-
if (
|
|
870
|
-
for (const track of
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
stream.removeTrack(track);
|
|
873
|
+
if (screenStream) {
|
|
874
|
+
for (const track of screenStream.getVideoTracks()) {
|
|
875
|
+
if (!trackId || this.normalizeRemoteTrackId(track.id) === String(trackId)) {
|
|
876
|
+
screenStream.removeTrack(track);
|
|
874
877
|
track.stop();
|
|
875
878
|
}
|
|
876
879
|
}
|
|
880
|
+
if (screenStream.getTracks().length === 0 && participantId) {
|
|
881
|
+
this.remoteScreenStreams.delete(participantId);
|
|
882
|
+
}
|
|
877
883
|
}
|
|
878
884
|
this.onScreenShareStopped(participantId, trackId);
|
|
879
885
|
break;
|
|
@@ -942,6 +948,18 @@ var SFUClient = class {
|
|
|
942
948
|
const config = { iceServers: this.iceServers };
|
|
943
949
|
this.publisher = new RTCPeerConnection(config);
|
|
944
950
|
this.subscriber = new RTCPeerConnection(config);
|
|
951
|
+
this.audioTransceiver = this.publisher.addTransceiver("audio", {
|
|
952
|
+
direction: "sendonly"
|
|
953
|
+
});
|
|
954
|
+
this.cameraTransceiver = this.publisher.addTransceiver("video", {
|
|
955
|
+
direction: "sendonly"
|
|
956
|
+
});
|
|
957
|
+
this.screenTransceiver = this.publisher.addTransceiver("video", {
|
|
958
|
+
direction: "inactive"
|
|
959
|
+
});
|
|
960
|
+
this.audioSender = this.audioTransceiver.sender;
|
|
961
|
+
this.videoSender = this.cameraTransceiver.sender;
|
|
962
|
+
this.screenSender = this.screenTransceiver.sender;
|
|
945
963
|
this.publisher.onicecandidate = (event) => {
|
|
946
964
|
if (event.candidate) this.sendCandidate("publisher", event.candidate);
|
|
947
965
|
};
|
|
@@ -981,25 +999,61 @@ var SFUClient = class {
|
|
|
981
999
|
return;
|
|
982
1000
|
}
|
|
983
1001
|
const participantId = metadata.participant_id;
|
|
1002
|
+
const source = String(
|
|
1003
|
+
metadata.source || this.trackToSource.get(metadata.track_id) || ""
|
|
1004
|
+
).trim().toLowerCase();
|
|
1005
|
+
if (source === "screen" || source === "screen_share") {
|
|
1006
|
+
let screenStream = this.remoteScreenStreams.get(participantId);
|
|
1007
|
+
if (!screenStream) {
|
|
1008
|
+
screenStream = new MediaStream();
|
|
1009
|
+
this.remoteScreenStreams.set(participantId, screenStream);
|
|
1010
|
+
}
|
|
1011
|
+
for (const existing of screenStream.getVideoTracks()) {
|
|
1012
|
+
if (existing.id !== event.track.id) {
|
|
1013
|
+
screenStream.removeTrack(existing);
|
|
1014
|
+
existing.stop();
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (!screenStream.getTracks().some((track) => track.id === event.track.id)) {
|
|
1018
|
+
screenStream.addTrack(event.track);
|
|
1019
|
+
}
|
|
1020
|
+
event.track.onended = () => {
|
|
1021
|
+
try {
|
|
1022
|
+
screenStream?.removeTrack(event.track);
|
|
1023
|
+
} catch {
|
|
1024
|
+
}
|
|
1025
|
+
if (screenStream?.getTracks().length === 0) {
|
|
1026
|
+
this.remoteScreenStreams.delete(participantId);
|
|
1027
|
+
this.onScreenShareStopped(
|
|
1028
|
+
participantId,
|
|
1029
|
+
metadata.track_id
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
this.onScreenShareStarted(participantId, metadata);
|
|
1034
|
+
this.onRemoteTrack(event.track, screenStream, {
|
|
1035
|
+
...metadata,
|
|
1036
|
+
source: "screen"
|
|
1037
|
+
});
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (event.track.kind === "video" && source !== "camera") {
|
|
1041
|
+
this.onError(
|
|
1042
|
+
new Error(
|
|
1043
|
+
`Refusing to route unknown remote video source to camera: ${metadata.track_id}`
|
|
1044
|
+
)
|
|
1045
|
+
);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
984
1048
|
let remoteStream = this.remoteStreams.get(participantId);
|
|
985
1049
|
if (!remoteStream) {
|
|
986
1050
|
remoteStream = new MediaStream();
|
|
987
1051
|
this.remoteStreams.set(participantId, remoteStream);
|
|
988
1052
|
}
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
remoteStream.removeTrack(existing);
|
|
994
|
-
existing.stop();
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
} else {
|
|
998
|
-
for (const existing of remoteStream.getTracks()) {
|
|
999
|
-
if (existing.kind === event.track.kind && existing.id !== event.track.id) {
|
|
1000
|
-
remoteStream.removeTrack(existing);
|
|
1001
|
-
existing.stop();
|
|
1002
|
-
}
|
|
1053
|
+
for (const existing of remoteStream.getTracks()) {
|
|
1054
|
+
if (existing.kind === event.track.kind && existing.id !== event.track.id) {
|
|
1055
|
+
remoteStream.removeTrack(existing);
|
|
1056
|
+
existing.stop();
|
|
1003
1057
|
}
|
|
1004
1058
|
}
|
|
1005
1059
|
if (!remoteStream.getTracks().some((track) => track.id === event.track.id)) {
|
|
@@ -1011,31 +1065,24 @@ var SFUClient = class {
|
|
|
1011
1065
|
} catch {
|
|
1012
1066
|
}
|
|
1013
1067
|
};
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1068
|
+
this.onRemoteTrack(event.track, remoteStream, {
|
|
1069
|
+
...metadata,
|
|
1070
|
+
source: event.track.kind === "audio" ? "audio" : "camera"
|
|
1071
|
+
});
|
|
1018
1072
|
}
|
|
1019
1073
|
normalizeRemoteTrackId(browserTrackId) {
|
|
1020
1074
|
return browserTrackId?.startsWith("track-") ? browserTrackId.slice("track-".length) : browserTrackId;
|
|
1021
1075
|
}
|
|
1022
1076
|
resolveRemoteTrackMetadata(track, stream) {
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
if (stream?.id) {
|
|
1033
|
-
const participant = this.participants.get(stream.id);
|
|
1034
|
-
if (participant) {
|
|
1035
|
-
return [...this.trackMetadata.values()].find(
|
|
1036
|
-
(entry) => entry.participant_id === participant.participant_id && entry.kind === track.kind
|
|
1037
|
-
) || null;
|
|
1038
|
-
}
|
|
1077
|
+
const candidates = [
|
|
1078
|
+
this.normalizeRemoteTrackId(track.id),
|
|
1079
|
+
track.id,
|
|
1080
|
+
stream?.id ? this.normalizeRemoteTrackId(stream.id) : null,
|
|
1081
|
+
stream?.id || null
|
|
1082
|
+
].filter(Boolean);
|
|
1083
|
+
for (const id of candidates) {
|
|
1084
|
+
const metadata = this.trackMetadata.get(id);
|
|
1085
|
+
if (metadata) return metadata;
|
|
1039
1086
|
}
|
|
1040
1087
|
return null;
|
|
1041
1088
|
}
|
|
@@ -1166,20 +1213,19 @@ var SFUClient = class {
|
|
|
1166
1213
|
}
|
|
1167
1214
|
async publishExistingTracks(audio = true, video = true) {
|
|
1168
1215
|
if (!this.publisher || !this.localStream) return;
|
|
1169
|
-
this.
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (!this.publisher.getSenders().length) return;
|
|
1216
|
+
if (!this.audioTransceiver || !this.cameraTransceiver || !this.screenTransceiver) {
|
|
1217
|
+
throw new Error("Publisher transceivers are not initialized");
|
|
1218
|
+
}
|
|
1219
|
+
const audioTrack = this.localStream.getAudioTracks().find((track) => track.readyState === "live");
|
|
1220
|
+
const cameraTrack = this.localStream.getVideoTracks().find((track) => track.readyState === "live");
|
|
1221
|
+
const screenTrack = this.screenStream?.getVideoTracks().find((track) => track.readyState === "live");
|
|
1222
|
+
await this.audioSender?.replaceTrack(audio && audioTrack ? audioTrack : null);
|
|
1223
|
+
this.audioTransceiver.direction = audio && audioTrack ? "sendonly" : "inactive";
|
|
1224
|
+
await this.videoSender?.replaceTrack(video && cameraTrack ? cameraTrack : null);
|
|
1225
|
+
this.cameraTransceiver.direction = video && cameraTrack ? "sendonly" : "inactive";
|
|
1226
|
+
await this.screenSender?.replaceTrack(screenTrack || null);
|
|
1227
|
+
this.screenTransceiver.direction = screenTrack ? "sendonly" : "inactive";
|
|
1228
|
+
if (!audioTrack && !cameraTrack && !screenTrack) return;
|
|
1183
1229
|
const offer = await this.publisher.createOffer();
|
|
1184
1230
|
await this.publisher.setLocalDescription(offer);
|
|
1185
1231
|
this.send({
|
|
@@ -1189,44 +1235,86 @@ var SFUClient = class {
|
|
|
1189
1235
|
sdp: offer.sdp
|
|
1190
1236
|
}
|
|
1191
1237
|
});
|
|
1192
|
-
if (
|
|
1238
|
+
if (screenTrack) {
|
|
1193
1239
|
this.send({ type: "start_screen_share" });
|
|
1194
1240
|
}
|
|
1195
1241
|
}
|
|
1196
1242
|
async shareScreen() {
|
|
1197
|
-
if (!this.publisher)
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
sdp: offer.sdp
|
|
1210
|
-
}
|
|
1243
|
+
if (!this.publisher) {
|
|
1244
|
+
throw new Error("Publisher PeerConnection is not initialized");
|
|
1245
|
+
}
|
|
1246
|
+
if (!this.screenTransceiver || !this.screenSender) {
|
|
1247
|
+
throw new Error("Screen transceiver is not initialized");
|
|
1248
|
+
}
|
|
1249
|
+
if (this.screenStream) {
|
|
1250
|
+
return this.screenStream;
|
|
1251
|
+
}
|
|
1252
|
+
const screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
1253
|
+
video: true,
|
|
1254
|
+
audio: false
|
|
1211
1255
|
});
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1256
|
+
const screenTrack = screenStream.getVideoTracks()[0];
|
|
1257
|
+
if (!screenTrack) {
|
|
1258
|
+
screenStream.getTracks().forEach((track) => track.stop());
|
|
1259
|
+
throw new Error("Screen share returned no video track");
|
|
1260
|
+
}
|
|
1261
|
+
this.screenStream = screenStream;
|
|
1262
|
+
try {
|
|
1263
|
+
if (!this.send({ type: "start_screen_share" })) {
|
|
1264
|
+
throw new Error("Failed to signal start_screen_share");
|
|
1265
|
+
}
|
|
1266
|
+
await this.screenSender.replaceTrack(screenTrack);
|
|
1267
|
+
this.screenTransceiver.direction = "sendonly";
|
|
1268
|
+
const offer = await this.publisher.createOffer();
|
|
1269
|
+
await this.publisher.setLocalDescription(offer);
|
|
1270
|
+
this.send({
|
|
1271
|
+
type: "publish_offer",
|
|
1272
|
+
payload: {
|
|
1273
|
+
revision: ++this.publisherRevision,
|
|
1274
|
+
sdp: offer.sdp
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
screenTrack.onended = () => {
|
|
1278
|
+
void this.stopScreenShare();
|
|
1279
|
+
};
|
|
1280
|
+
console.info("[publisher] Screen track attached", {
|
|
1281
|
+
screenTrackId: screenTrack.id,
|
|
1282
|
+
cameraTrackId: this.videoSender?.track?.id,
|
|
1283
|
+
screenMid: this.screenTransceiver.mid,
|
|
1284
|
+
cameraMid: this.cameraTransceiver?.mid
|
|
1285
|
+
});
|
|
1286
|
+
return this.screenStream;
|
|
1287
|
+
} catch (error) {
|
|
1223
1288
|
try {
|
|
1224
|
-
this.
|
|
1289
|
+
await this.screenSender.replaceTrack(null);
|
|
1225
1290
|
} catch {
|
|
1226
1291
|
}
|
|
1227
|
-
this.
|
|
1292
|
+
this.screenTransceiver.direction = "inactive";
|
|
1293
|
+
screenStream.getTracks().forEach((track) => track.stop());
|
|
1294
|
+
this.screenStream = null;
|
|
1228
1295
|
try {
|
|
1229
|
-
|
|
1296
|
+
this.send({ type: "stop_screen_share" });
|
|
1297
|
+
} catch {
|
|
1298
|
+
}
|
|
1299
|
+
throw error;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
async stopScreenShare() {
|
|
1303
|
+
if (!this.screenStream && !this.screenSender?.track) return;
|
|
1304
|
+
const screenTrack = this.screenSender?.track || null;
|
|
1305
|
+
if (screenTrack) screenTrack.onended = null;
|
|
1306
|
+
try {
|
|
1307
|
+
await this.screenSender?.replaceTrack(null);
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
this.onError(error);
|
|
1310
|
+
}
|
|
1311
|
+
if (this.screenTransceiver) {
|
|
1312
|
+
this.screenTransceiver.direction = "inactive";
|
|
1313
|
+
}
|
|
1314
|
+
this.send({ type: "stop_screen_share" });
|
|
1315
|
+
try {
|
|
1316
|
+
const offer = await this.publisher?.createOffer();
|
|
1317
|
+
if (offer && this.publisher) {
|
|
1230
1318
|
await this.publisher.setLocalDescription(offer);
|
|
1231
1319
|
this.send({
|
|
1232
1320
|
type: "publish_offer",
|
|
@@ -1235,13 +1323,15 @@ var SFUClient = class {
|
|
|
1235
1323
|
sdp: offer.sdp
|
|
1236
1324
|
}
|
|
1237
1325
|
});
|
|
1238
|
-
this.send({ type: "stop_screen_share" });
|
|
1239
|
-
} catch (error) {
|
|
1240
|
-
this.onError(error);
|
|
1241
1326
|
}
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
this.onError(error);
|
|
1242
1329
|
}
|
|
1243
|
-
this.screenStream
|
|
1330
|
+
this.screenStream?.getTracks().forEach((track) => track.stop());
|
|
1244
1331
|
this.screenStream = null;
|
|
1332
|
+
if (this.currentParticipantId) {
|
|
1333
|
+
this.onScreenShareStopped(this.currentParticipantId);
|
|
1334
|
+
}
|
|
1245
1335
|
}
|
|
1246
1336
|
muteAudio(muted) {
|
|
1247
1337
|
this.localStream?.getAudioTracks().forEach((track) => {
|
|
@@ -1327,6 +1417,11 @@ var SFUClient = class {
|
|
|
1327
1417
|
stream.getTracks().forEach((track) => track.stop());
|
|
1328
1418
|
this.remoteStreams.delete(participantId);
|
|
1329
1419
|
}
|
|
1420
|
+
const screenStream = this.remoteScreenStreams.get(participantId);
|
|
1421
|
+
if (screenStream) {
|
|
1422
|
+
screenStream.getTracks().forEach((track) => track.stop());
|
|
1423
|
+
this.remoteScreenStreams.delete(participantId);
|
|
1424
|
+
}
|
|
1330
1425
|
for (const [trackId, metadata] of this.trackMetadata) {
|
|
1331
1426
|
if (metadata.participant_id === participantId) {
|
|
1332
1427
|
this.trackMetadata.delete(trackId);
|
|
@@ -1402,6 +1497,9 @@ var SFUClient = class {
|
|
|
1402
1497
|
this.videoSender = null;
|
|
1403
1498
|
this.audioSender = null;
|
|
1404
1499
|
this.screenSender = null;
|
|
1500
|
+
this.audioTransceiver = null;
|
|
1501
|
+
this.cameraTransceiver = null;
|
|
1502
|
+
this.screenTransceiver = null;
|
|
1405
1503
|
this.pendingIce = { publisher: [], subscriber: [] };
|
|
1406
1504
|
this.offerQueue = [];
|
|
1407
1505
|
this.subscriberNegotiating = false;
|
|
@@ -1424,6 +1522,10 @@ var SFUClient = class {
|
|
|
1424
1522
|
stream.getTracks().forEach((track) => track.stop());
|
|
1425
1523
|
}
|
|
1426
1524
|
this.remoteStreams.clear();
|
|
1525
|
+
for (const stream of this.remoteScreenStreams.values()) {
|
|
1526
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1527
|
+
}
|
|
1528
|
+
this.remoteScreenStreams.clear();
|
|
1427
1529
|
this.trackMetadata.clear();
|
|
1428
1530
|
this.trackToParticipant.clear();
|
|
1429
1531
|
this.trackToSource.clear();
|
|
@@ -1872,11 +1974,12 @@ var VideoSDKCore = class {
|
|
|
1872
1974
|
this.handleRemoteTrack(track, stream, metadata);
|
|
1873
1975
|
},
|
|
1874
1976
|
// SCREEN SHARE
|
|
1875
|
-
onScreenShareStarted: (participantId,
|
|
1977
|
+
onScreenShareStarted: (participantId, _track) => {
|
|
1876
1978
|
if (participantId === this.myId) {
|
|
1877
1979
|
return;
|
|
1878
1980
|
}
|
|
1879
|
-
console.log("[SFU] Screen share started", participantId
|
|
1981
|
+
console.log("[SFU] Screen share started", participantId);
|
|
1982
|
+
this.state.setPresenterId(participantId);
|
|
1880
1983
|
const current = this.state.getParticipant(participantId);
|
|
1881
1984
|
if (current) {
|
|
1882
1985
|
this.state.updateParticipantMedia(participantId, {
|