@defcomm/meetingsdk 0.1.2 → 0.1.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 +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +189 -87
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +189 -87
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -285,6 +285,9 @@ declare class SFUClient {
|
|
|
285
285
|
private videoSender;
|
|
286
286
|
private audioSender;
|
|
287
287
|
private screenSender;
|
|
288
|
+
private audioTransceiver;
|
|
289
|
+
private cameraTransceiver;
|
|
290
|
+
private screenTransceiver;
|
|
288
291
|
private iceServers;
|
|
289
292
|
private requestId;
|
|
290
293
|
private publisherRevision;
|
|
@@ -313,6 +316,7 @@ declare class SFUClient {
|
|
|
313
316
|
private trackMetadata;
|
|
314
317
|
private participants;
|
|
315
318
|
private remoteStreams;
|
|
319
|
+
private remoteScreenStreams;
|
|
316
320
|
private trackToParticipant;
|
|
317
321
|
private trackToSource;
|
|
318
322
|
private localTrackIds;
|
package/dist/index.d.ts
CHANGED
|
@@ -285,6 +285,9 @@ declare class SFUClient {
|
|
|
285
285
|
private videoSender;
|
|
286
286
|
private audioSender;
|
|
287
287
|
private screenSender;
|
|
288
|
+
private audioTransceiver;
|
|
289
|
+
private cameraTransceiver;
|
|
290
|
+
private screenTransceiver;
|
|
288
291
|
private iceServers;
|
|
289
292
|
private requestId;
|
|
290
293
|
private publisherRevision;
|
|
@@ -313,6 +316,7 @@ declare class SFUClient {
|
|
|
313
316
|
private trackMetadata;
|
|
314
317
|
private participants;
|
|
315
318
|
private remoteStreams;
|
|
319
|
+
private remoteScreenStreams;
|
|
316
320
|
private trackToParticipant;
|
|
317
321
|
private trackToSource;
|
|
318
322
|
private localTrackIds;
|
package/dist/index.js
CHANGED
|
@@ -387,6 +387,9 @@ var SFUClient = class {
|
|
|
387
387
|
this.videoSender = null;
|
|
388
388
|
this.audioSender = null;
|
|
389
389
|
this.screenSender = null;
|
|
390
|
+
this.audioTransceiver = null;
|
|
391
|
+
this.cameraTransceiver = null;
|
|
392
|
+
this.screenTransceiver = null;
|
|
390
393
|
this.iceServers = [];
|
|
391
394
|
this.requestId = 0;
|
|
392
395
|
this.publisherRevision = 0;
|
|
@@ -418,6 +421,7 @@ var SFUClient = class {
|
|
|
418
421
|
this.trackMetadata = /* @__PURE__ */ new Map();
|
|
419
422
|
this.participants = /* @__PURE__ */ new Map();
|
|
420
423
|
this.remoteStreams = /* @__PURE__ */ new Map();
|
|
424
|
+
this.remoteScreenStreams = /* @__PURE__ */ new Map();
|
|
421
425
|
this.trackToParticipant = /* @__PURE__ */ new Map();
|
|
422
426
|
this.trackToSource = /* @__PURE__ */ new Map();
|
|
423
427
|
this.localTrackIds = /* @__PURE__ */ new Map();
|
|
@@ -900,16 +904,18 @@ var SFUClient = class {
|
|
|
900
904
|
}
|
|
901
905
|
case "screen_share_stopped": {
|
|
902
906
|
const participantId = payload.participant_id;
|
|
903
|
-
const
|
|
907
|
+
const screenStream = participantId ? this.remoteScreenStreams.get(participantId) : null;
|
|
904
908
|
const trackId = payload.track_id;
|
|
905
|
-
if (
|
|
906
|
-
for (const track of
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
stream.removeTrack(track);
|
|
909
|
+
if (screenStream) {
|
|
910
|
+
for (const track of screenStream.getVideoTracks()) {
|
|
911
|
+
if (!trackId || this.normalizeRemoteTrackId(track.id) === String(trackId)) {
|
|
912
|
+
screenStream.removeTrack(track);
|
|
910
913
|
track.stop();
|
|
911
914
|
}
|
|
912
915
|
}
|
|
916
|
+
if (screenStream.getTracks().length === 0 && participantId) {
|
|
917
|
+
this.remoteScreenStreams.delete(participantId);
|
|
918
|
+
}
|
|
913
919
|
}
|
|
914
920
|
this.onScreenShareStopped(participantId, trackId);
|
|
915
921
|
break;
|
|
@@ -978,6 +984,18 @@ var SFUClient = class {
|
|
|
978
984
|
const config = { iceServers: this.iceServers };
|
|
979
985
|
this.publisher = new RTCPeerConnection(config);
|
|
980
986
|
this.subscriber = new RTCPeerConnection(config);
|
|
987
|
+
this.audioTransceiver = this.publisher.addTransceiver("audio", {
|
|
988
|
+
direction: "sendonly"
|
|
989
|
+
});
|
|
990
|
+
this.cameraTransceiver = this.publisher.addTransceiver("video", {
|
|
991
|
+
direction: "sendonly"
|
|
992
|
+
});
|
|
993
|
+
this.screenTransceiver = this.publisher.addTransceiver("video", {
|
|
994
|
+
direction: "inactive"
|
|
995
|
+
});
|
|
996
|
+
this.audioSender = this.audioTransceiver.sender;
|
|
997
|
+
this.videoSender = this.cameraTransceiver.sender;
|
|
998
|
+
this.screenSender = this.screenTransceiver.sender;
|
|
981
999
|
this.publisher.onicecandidate = (event) => {
|
|
982
1000
|
if (event.candidate) this.sendCandidate("publisher", event.candidate);
|
|
983
1001
|
};
|
|
@@ -1017,25 +1035,61 @@ var SFUClient = class {
|
|
|
1017
1035
|
return;
|
|
1018
1036
|
}
|
|
1019
1037
|
const participantId = metadata.participant_id;
|
|
1038
|
+
const source = String(
|
|
1039
|
+
metadata.source || this.trackToSource.get(metadata.track_id) || ""
|
|
1040
|
+
).trim().toLowerCase();
|
|
1041
|
+
if (source === "screen" || source === "screen_share") {
|
|
1042
|
+
let screenStream = this.remoteScreenStreams.get(participantId);
|
|
1043
|
+
if (!screenStream) {
|
|
1044
|
+
screenStream = new MediaStream();
|
|
1045
|
+
this.remoteScreenStreams.set(participantId, screenStream);
|
|
1046
|
+
}
|
|
1047
|
+
for (const existing of screenStream.getVideoTracks()) {
|
|
1048
|
+
if (existing.id !== event.track.id) {
|
|
1049
|
+
screenStream.removeTrack(existing);
|
|
1050
|
+
existing.stop();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
if (!screenStream.getTracks().some((track) => track.id === event.track.id)) {
|
|
1054
|
+
screenStream.addTrack(event.track);
|
|
1055
|
+
}
|
|
1056
|
+
event.track.onended = () => {
|
|
1057
|
+
try {
|
|
1058
|
+
screenStream?.removeTrack(event.track);
|
|
1059
|
+
} catch {
|
|
1060
|
+
}
|
|
1061
|
+
if (screenStream?.getTracks().length === 0) {
|
|
1062
|
+
this.remoteScreenStreams.delete(participantId);
|
|
1063
|
+
this.onScreenShareStopped(
|
|
1064
|
+
participantId,
|
|
1065
|
+
metadata.track_id
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
this.onScreenShareStarted(participantId, metadata);
|
|
1070
|
+
this.onRemoteTrack(event.track, screenStream, {
|
|
1071
|
+
...metadata,
|
|
1072
|
+
source: "screen"
|
|
1073
|
+
});
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (event.track.kind === "video" && source !== "camera") {
|
|
1077
|
+
this.onError(
|
|
1078
|
+
new Error(
|
|
1079
|
+
`Refusing to route unknown remote video source to camera: ${metadata.track_id}`
|
|
1080
|
+
)
|
|
1081
|
+
);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1020
1084
|
let remoteStream = this.remoteStreams.get(participantId);
|
|
1021
1085
|
if (!remoteStream) {
|
|
1022
1086
|
remoteStream = new MediaStream();
|
|
1023
1087
|
this.remoteStreams.set(participantId, remoteStream);
|
|
1024
1088
|
}
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
remoteStream.removeTrack(existing);
|
|
1030
|
-
existing.stop();
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
} else {
|
|
1034
|
-
for (const existing of remoteStream.getTracks()) {
|
|
1035
|
-
if (existing.kind === event.track.kind && existing.id !== event.track.id) {
|
|
1036
|
-
remoteStream.removeTrack(existing);
|
|
1037
|
-
existing.stop();
|
|
1038
|
-
}
|
|
1089
|
+
for (const existing of remoteStream.getTracks()) {
|
|
1090
|
+
if (existing.kind === event.track.kind && existing.id !== event.track.id) {
|
|
1091
|
+
remoteStream.removeTrack(existing);
|
|
1092
|
+
existing.stop();
|
|
1039
1093
|
}
|
|
1040
1094
|
}
|
|
1041
1095
|
if (!remoteStream.getTracks().some((track) => track.id === event.track.id)) {
|
|
@@ -1047,31 +1101,24 @@ var SFUClient = class {
|
|
|
1047
1101
|
} catch {
|
|
1048
1102
|
}
|
|
1049
1103
|
};
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1104
|
+
this.onRemoteTrack(event.track, remoteStream, {
|
|
1105
|
+
...metadata,
|
|
1106
|
+
source: event.track.kind === "audio" ? "audio" : "camera"
|
|
1107
|
+
});
|
|
1054
1108
|
}
|
|
1055
1109
|
normalizeRemoteTrackId(browserTrackId) {
|
|
1056
1110
|
return browserTrackId?.startsWith("track-") ? browserTrackId.slice("track-".length) : browserTrackId;
|
|
1057
1111
|
}
|
|
1058
1112
|
resolveRemoteTrackMetadata(track, stream) {
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (stream?.id) {
|
|
1069
|
-
const participant = this.participants.get(stream.id);
|
|
1070
|
-
if (participant) {
|
|
1071
|
-
return [...this.trackMetadata.values()].find(
|
|
1072
|
-
(entry) => entry.participant_id === participant.participant_id && entry.kind === track.kind
|
|
1073
|
-
) || null;
|
|
1074
|
-
}
|
|
1113
|
+
const candidates = [
|
|
1114
|
+
this.normalizeRemoteTrackId(track.id),
|
|
1115
|
+
track.id,
|
|
1116
|
+
stream?.id ? this.normalizeRemoteTrackId(stream.id) : null,
|
|
1117
|
+
stream?.id || null
|
|
1118
|
+
].filter(Boolean);
|
|
1119
|
+
for (const id of candidates) {
|
|
1120
|
+
const metadata = this.trackMetadata.get(id);
|
|
1121
|
+
if (metadata) return metadata;
|
|
1075
1122
|
}
|
|
1076
1123
|
return null;
|
|
1077
1124
|
}
|
|
@@ -1202,20 +1249,19 @@ var SFUClient = class {
|
|
|
1202
1249
|
}
|
|
1203
1250
|
async publishExistingTracks(audio = true, video = true) {
|
|
1204
1251
|
if (!this.publisher || !this.localStream) return;
|
|
1205
|
-
this.
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
if (!this.publisher.getSenders().length) return;
|
|
1252
|
+
if (!this.audioTransceiver || !this.cameraTransceiver || !this.screenTransceiver) {
|
|
1253
|
+
throw new Error("Publisher transceivers are not initialized");
|
|
1254
|
+
}
|
|
1255
|
+
const audioTrack = this.localStream.getAudioTracks().find((track) => track.readyState === "live");
|
|
1256
|
+
const cameraTrack = this.localStream.getVideoTracks().find((track) => track.readyState === "live");
|
|
1257
|
+
const screenTrack = this.screenStream?.getVideoTracks().find((track) => track.readyState === "live");
|
|
1258
|
+
await this.audioSender?.replaceTrack(audio && audioTrack ? audioTrack : null);
|
|
1259
|
+
this.audioTransceiver.direction = audio && audioTrack ? "sendonly" : "inactive";
|
|
1260
|
+
await this.videoSender?.replaceTrack(video && cameraTrack ? cameraTrack : null);
|
|
1261
|
+
this.cameraTransceiver.direction = video && cameraTrack ? "sendonly" : "inactive";
|
|
1262
|
+
await this.screenSender?.replaceTrack(screenTrack || null);
|
|
1263
|
+
this.screenTransceiver.direction = screenTrack ? "sendonly" : "inactive";
|
|
1264
|
+
if (!audioTrack && !cameraTrack && !screenTrack) return;
|
|
1219
1265
|
const offer = await this.publisher.createOffer();
|
|
1220
1266
|
await this.publisher.setLocalDescription(offer);
|
|
1221
1267
|
this.send({
|
|
@@ -1225,44 +1271,86 @@ var SFUClient = class {
|
|
|
1225
1271
|
sdp: offer.sdp
|
|
1226
1272
|
}
|
|
1227
1273
|
});
|
|
1228
|
-
if (
|
|
1274
|
+
if (screenTrack) {
|
|
1229
1275
|
this.send({ type: "start_screen_share" });
|
|
1230
1276
|
}
|
|
1231
1277
|
}
|
|
1232
1278
|
async shareScreen() {
|
|
1233
|
-
if (!this.publisher)
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
sdp: offer.sdp
|
|
1246
|
-
}
|
|
1279
|
+
if (!this.publisher) {
|
|
1280
|
+
throw new Error("Publisher PeerConnection is not initialized");
|
|
1281
|
+
}
|
|
1282
|
+
if (!this.screenTransceiver || !this.screenSender) {
|
|
1283
|
+
throw new Error("Screen transceiver is not initialized");
|
|
1284
|
+
}
|
|
1285
|
+
if (this.screenStream) {
|
|
1286
|
+
return this.screenStream;
|
|
1287
|
+
}
|
|
1288
|
+
const screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
1289
|
+
video: true,
|
|
1290
|
+
audio: false
|
|
1247
1291
|
});
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1292
|
+
const screenTrack = screenStream.getVideoTracks()[0];
|
|
1293
|
+
if (!screenTrack) {
|
|
1294
|
+
screenStream.getTracks().forEach((track) => track.stop());
|
|
1295
|
+
throw new Error("Screen share returned no video track");
|
|
1296
|
+
}
|
|
1297
|
+
this.screenStream = screenStream;
|
|
1298
|
+
try {
|
|
1299
|
+
if (!this.send({ type: "start_screen_share" })) {
|
|
1300
|
+
throw new Error("Failed to signal start_screen_share");
|
|
1301
|
+
}
|
|
1302
|
+
await this.screenSender.replaceTrack(screenTrack);
|
|
1303
|
+
this.screenTransceiver.direction = "sendonly";
|
|
1304
|
+
const offer = await this.publisher.createOffer();
|
|
1305
|
+
await this.publisher.setLocalDescription(offer);
|
|
1306
|
+
this.send({
|
|
1307
|
+
type: "publish_offer",
|
|
1308
|
+
payload: {
|
|
1309
|
+
revision: ++this.publisherRevision,
|
|
1310
|
+
sdp: offer.sdp
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
screenTrack.onended = () => {
|
|
1314
|
+
void this.stopScreenShare();
|
|
1315
|
+
};
|
|
1316
|
+
console.info("[publisher] Screen track attached", {
|
|
1317
|
+
screenTrackId: screenTrack.id,
|
|
1318
|
+
cameraTrackId: this.videoSender?.track?.id,
|
|
1319
|
+
screenMid: this.screenTransceiver.mid,
|
|
1320
|
+
cameraMid: this.cameraTransceiver?.mid
|
|
1321
|
+
});
|
|
1322
|
+
return this.screenStream;
|
|
1323
|
+
} catch (error) {
|
|
1259
1324
|
try {
|
|
1260
|
-
this.
|
|
1325
|
+
await this.screenSender.replaceTrack(null);
|
|
1261
1326
|
} catch {
|
|
1262
1327
|
}
|
|
1263
|
-
this.
|
|
1328
|
+
this.screenTransceiver.direction = "inactive";
|
|
1329
|
+
screenStream.getTracks().forEach((track) => track.stop());
|
|
1330
|
+
this.screenStream = null;
|
|
1264
1331
|
try {
|
|
1265
|
-
|
|
1332
|
+
this.send({ type: "stop_screen_share" });
|
|
1333
|
+
} catch {
|
|
1334
|
+
}
|
|
1335
|
+
throw error;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
async stopScreenShare() {
|
|
1339
|
+
if (!this.screenStream && !this.screenSender?.track) return;
|
|
1340
|
+
const screenTrack = this.screenSender?.track || null;
|
|
1341
|
+
if (screenTrack) screenTrack.onended = null;
|
|
1342
|
+
try {
|
|
1343
|
+
await this.screenSender?.replaceTrack(null);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
this.onError(error);
|
|
1346
|
+
}
|
|
1347
|
+
if (this.screenTransceiver) {
|
|
1348
|
+
this.screenTransceiver.direction = "inactive";
|
|
1349
|
+
}
|
|
1350
|
+
this.send({ type: "stop_screen_share" });
|
|
1351
|
+
try {
|
|
1352
|
+
const offer = await this.publisher?.createOffer();
|
|
1353
|
+
if (offer && this.publisher) {
|
|
1266
1354
|
await this.publisher.setLocalDescription(offer);
|
|
1267
1355
|
this.send({
|
|
1268
1356
|
type: "publish_offer",
|
|
@@ -1271,13 +1359,15 @@ var SFUClient = class {
|
|
|
1271
1359
|
sdp: offer.sdp
|
|
1272
1360
|
}
|
|
1273
1361
|
});
|
|
1274
|
-
this.send({ type: "stop_screen_share" });
|
|
1275
|
-
} catch (error) {
|
|
1276
|
-
this.onError(error);
|
|
1277
1362
|
}
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
this.onError(error);
|
|
1278
1365
|
}
|
|
1279
|
-
this.screenStream
|
|
1366
|
+
this.screenStream?.getTracks().forEach((track) => track.stop());
|
|
1280
1367
|
this.screenStream = null;
|
|
1368
|
+
if (this.currentParticipantId) {
|
|
1369
|
+
this.onScreenShareStopped(this.currentParticipantId);
|
|
1370
|
+
}
|
|
1281
1371
|
}
|
|
1282
1372
|
muteAudio(muted) {
|
|
1283
1373
|
this.localStream?.getAudioTracks().forEach((track) => {
|
|
@@ -1363,6 +1453,11 @@ var SFUClient = class {
|
|
|
1363
1453
|
stream.getTracks().forEach((track) => track.stop());
|
|
1364
1454
|
this.remoteStreams.delete(participantId);
|
|
1365
1455
|
}
|
|
1456
|
+
const screenStream = this.remoteScreenStreams.get(participantId);
|
|
1457
|
+
if (screenStream) {
|
|
1458
|
+
screenStream.getTracks().forEach((track) => track.stop());
|
|
1459
|
+
this.remoteScreenStreams.delete(participantId);
|
|
1460
|
+
}
|
|
1366
1461
|
for (const [trackId, metadata] of this.trackMetadata) {
|
|
1367
1462
|
if (metadata.participant_id === participantId) {
|
|
1368
1463
|
this.trackMetadata.delete(trackId);
|
|
@@ -1438,6 +1533,9 @@ var SFUClient = class {
|
|
|
1438
1533
|
this.videoSender = null;
|
|
1439
1534
|
this.audioSender = null;
|
|
1440
1535
|
this.screenSender = null;
|
|
1536
|
+
this.audioTransceiver = null;
|
|
1537
|
+
this.cameraTransceiver = null;
|
|
1538
|
+
this.screenTransceiver = null;
|
|
1441
1539
|
this.pendingIce = { publisher: [], subscriber: [] };
|
|
1442
1540
|
this.offerQueue = [];
|
|
1443
1541
|
this.subscriberNegotiating = false;
|
|
@@ -1460,6 +1558,10 @@ var SFUClient = class {
|
|
|
1460
1558
|
stream.getTracks().forEach((track) => track.stop());
|
|
1461
1559
|
}
|
|
1462
1560
|
this.remoteStreams.clear();
|
|
1561
|
+
for (const stream of this.remoteScreenStreams.values()) {
|
|
1562
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1563
|
+
}
|
|
1564
|
+
this.remoteScreenStreams.clear();
|
|
1463
1565
|
this.trackMetadata.clear();
|
|
1464
1566
|
this.trackToParticipant.clear();
|
|
1465
1567
|
this.trackToSource.clear();
|