@defcomm/meetingsdk 0.3.1 → 0.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
@@ -219,7 +219,7 @@ declare class VideoSDKCore {
219
219
  rejectJoinRequest(requestId: string, reason?: string): Promise<void>;
220
220
  toggleMic(): void;
221
221
  toggleCam(): void;
222
- startScreenShare(): Promise<MediaStream>;
222
+ startScreenShare(): Promise<MediaStream | null>;
223
223
  stopScreenShare(): Promise<void>;
224
224
  startRecording(): Promise<void>;
225
225
  stopRecording(): Promise<void>;
@@ -392,6 +392,7 @@ declare class SFUClient {
392
392
  getResumeToken(): string | null;
393
393
  getLocalStream(): MediaStream | null;
394
394
  getScreenStream(): MediaStream | null;
395
+ getRemoteScreenStream(participantId: string): MediaStream | null;
395
396
  setLocalStream(stream: MediaStream | null): void;
396
397
  private nextRequestId;
397
398
  private request;
@@ -436,7 +437,7 @@ declare class SFUClient {
436
437
  video?: boolean | undefined;
437
438
  }): Promise<MediaStream>;
438
439
  private publishExistingTracks;
439
- shareScreen(): Promise<MediaStream>;
440
+ shareScreen(): Promise<MediaStream | null>;
440
441
  stopScreenShare(): Promise<void>;
441
442
  muteAudio(muted: boolean): void;
442
443
  muteVideo(muted: boolean): void;
package/dist/index.d.ts CHANGED
@@ -219,7 +219,7 @@ declare class VideoSDKCore {
219
219
  rejectJoinRequest(requestId: string, reason?: string): Promise<void>;
220
220
  toggleMic(): void;
221
221
  toggleCam(): void;
222
- startScreenShare(): Promise<MediaStream>;
222
+ startScreenShare(): Promise<MediaStream | null>;
223
223
  stopScreenShare(): Promise<void>;
224
224
  startRecording(): Promise<void>;
225
225
  stopRecording(): Promise<void>;
@@ -392,6 +392,7 @@ declare class SFUClient {
392
392
  getResumeToken(): string | null;
393
393
  getLocalStream(): MediaStream | null;
394
394
  getScreenStream(): MediaStream | null;
395
+ getRemoteScreenStream(participantId: string): MediaStream | null;
395
396
  setLocalStream(stream: MediaStream | null): void;
396
397
  private nextRequestId;
397
398
  private request;
@@ -436,7 +437,7 @@ declare class SFUClient {
436
437
  video?: boolean | undefined;
437
438
  }): Promise<MediaStream>;
438
439
  private publishExistingTracks;
439
- shareScreen(): Promise<MediaStream>;
440
+ shareScreen(): Promise<MediaStream | null>;
440
441
  stopScreenShare(): Promise<void>;
441
442
  muteAudio(muted: boolean): void;
442
443
  muteVideo(muted: boolean): void;
package/dist/index.js CHANGED
@@ -548,6 +548,9 @@ var SFUClient = class {
548
548
  getScreenStream() {
549
549
  return this.screenStream;
550
550
  }
551
+ getRemoteScreenStream(participantId) {
552
+ return this.remoteScreenStreams.get(participantId) || null;
553
+ }
551
554
  setLocalStream(stream) {
552
555
  this.localStream = stream;
553
556
  }
@@ -967,19 +970,7 @@ var SFUClient = class {
967
970
  }
968
971
  case "screen_share_stopped": {
969
972
  const participantId = payload.participant_id;
970
- const screenStream = participantId ? this.remoteScreenStreams.get(participantId) : null;
971
973
  const trackId = payload.track_id;
972
- if (screenStream) {
973
- for (const track of screenStream.getVideoTracks()) {
974
- if (!trackId || this.normalizeRemoteTrackId(track.id) === String(trackId)) {
975
- screenStream.removeTrack(track);
976
- track.stop();
977
- }
978
- }
979
- if (screenStream.getTracks().length === 0 && participantId) {
980
- this.remoteScreenStreams.delete(participantId);
981
- }
982
- }
983
974
  this.onScreenShareStopped(participantId, trackId);
984
975
  break;
985
976
  }
@@ -1362,11 +1353,14 @@ var SFUClient = class {
1362
1353
  sdp: answer.sdp
1363
1354
  }
1364
1355
  });
1365
- const waiters = this.subscriptionWaiters.splice(0);
1366
- waiters.forEach((w) => w.resolve());
1356
+ const waiter = this.subscriptionWaiters.shift();
1357
+ waiter?.resolve();
1367
1358
  } catch (error) {
1368
- const waiters = this.subscriptionWaiters.splice(0);
1369
- waiters.forEach((w) => w.reject(error));
1359
+ const waiter = this.subscriptionWaiters.shift();
1360
+ if (waiter) {
1361
+ waiter.trackIds.forEach((id) => this.subscribedTracks.delete(id));
1362
+ waiter.reject(error);
1363
+ }
1370
1364
  this.onError(
1371
1365
  new Error(`Failed to process subscriber offer: ${String(error)}`)
1372
1366
  );
@@ -1388,11 +1382,20 @@ var SFUClient = class {
1388
1382
  if (!uniqueIds.length || !this.isSocketOpen()) return;
1389
1383
  uniqueIds.forEach((id) => this.subscribedTracks.add(id));
1390
1384
  return new Promise((resolve, reject) => {
1391
- this.subscriptionWaiters.push({ resolve, reject });
1392
- this.send({
1385
+ const sent = this.send({
1393
1386
  type: "subscribe",
1394
1387
  payload: { track_ids: uniqueIds }
1395
1388
  });
1389
+ if (!sent) {
1390
+ uniqueIds.forEach((id) => this.subscribedTracks.delete(id));
1391
+ reject(new Error("Signaling socket is not open"));
1392
+ return;
1393
+ }
1394
+ this.subscriptionWaiters.push({
1395
+ trackIds: uniqueIds,
1396
+ resolve,
1397
+ reject
1398
+ });
1396
1399
  });
1397
1400
  }
1398
1401
  async processSubscriptionQueue() {
@@ -1405,11 +1408,20 @@ var SFUClient = class {
1405
1408
  continue;
1406
1409
  }
1407
1410
  await new Promise((resolve, reject) => {
1408
- this.subscriptionWaiters.push({ resolve, reject });
1409
- this.send({
1411
+ const sent = this.send({
1410
1412
  type: "subscribe",
1411
1413
  payload: { track_ids: [trackId] }
1412
1414
  });
1415
+ if (!sent) {
1416
+ this.subscribedTracks.delete(trackId);
1417
+ reject(new Error("Signaling socket is not open"));
1418
+ return;
1419
+ }
1420
+ this.subscriptionWaiters.push({
1421
+ trackIds: [trackId],
1422
+ resolve,
1423
+ reject
1424
+ });
1413
1425
  });
1414
1426
  }
1415
1427
  } finally {
@@ -1467,9 +1479,16 @@ var SFUClient = class {
1467
1479
  if (!this.screenTransceiver || !this.screenSender) {
1468
1480
  throw new Error("Screen transceiver is not initialized");
1469
1481
  }
1470
- if (this.screenStream) {
1471
- return this.screenStream;
1482
+ const existing = this.screenStream;
1483
+ const existingTrack = this.screenSender.track;
1484
+ if (existing || existingTrack && existingTrack.readyState === "live") {
1485
+ return existing;
1472
1486
  }
1487
+ try {
1488
+ await this.screenSender.replaceTrack(null);
1489
+ } catch {
1490
+ }
1491
+ this.screenTransceiver.direction = "inactive";
1473
1492
  const screenStream = await navigator.mediaDevices.getDisplayMedia({
1474
1493
  video: true,
1475
1494
  audio: false
@@ -1479,29 +1498,33 @@ var SFUClient = class {
1479
1498
  screenStream.getTracks().forEach((track) => track.stop());
1480
1499
  throw new Error("Screen share returned no video track");
1481
1500
  }
1501
+ const generation = ++this.screenShareGeneration;
1482
1502
  this.screenStream = screenStream;
1483
1503
  try {
1484
1504
  await this.screenSender.replaceTrack(screenTrack);
1485
1505
  this.screenTransceiver.direction = "sendonly";
1506
+ this.send({
1507
+ type: "start_screen_share"
1508
+ });
1486
1509
  const offer = await this.publisher.createOffer();
1487
1510
  await this.publisher.setLocalDescription(offer);
1511
+ const localDescription = this.publisher.localDescription;
1512
+ if (!localDescription?.sdp) {
1513
+ throw new Error("Publisher did not produce a valid screen-share SDP");
1514
+ }
1488
1515
  this.send({
1489
1516
  type: "publish_offer",
1490
1517
  payload: {
1491
1518
  revision: ++this.publisherRevision,
1492
- sdp: offer.sdp
1519
+ sdp: localDescription.sdp
1493
1520
  }
1494
1521
  });
1495
- this.send({ type: "start_screen_share" });
1496
1522
  screenTrack.onended = () => {
1523
+ if (generation !== this.screenShareGeneration) {
1524
+ return;
1525
+ }
1497
1526
  void this.stopScreenShare();
1498
1527
  };
1499
- console.info("[publisher] Screen track attached", {
1500
- screenTrackId: screenTrack.id,
1501
- cameraTrackId: this.videoSender?.track?.id,
1502
- screenMid: this.screenTransceiver.mid,
1503
- cameraMid: this.cameraTransceiver?.mid
1504
- });
1505
1528
  return this.screenStream;
1506
1529
  } catch (error) {
1507
1530
  try {
@@ -1509,19 +1532,33 @@ var SFUClient = class {
1509
1532
  } catch {
1510
1533
  }
1511
1534
  this.screenTransceiver.direction = "inactive";
1512
- screenStream.getTracks().forEach((track) => track.stop());
1535
+ screenTrack.onended = null;
1536
+ screenStream.getTracks().forEach((track) => {
1537
+ try {
1538
+ track.stop();
1539
+ } catch {
1540
+ }
1541
+ });
1513
1542
  this.screenStream = null;
1514
1543
  try {
1515
- this.send({ type: "stop_screen_share" });
1544
+ this.send({
1545
+ type: "stop_screen_share"
1546
+ });
1516
1547
  } catch {
1517
1548
  }
1518
1549
  throw error;
1519
1550
  }
1520
1551
  }
1521
1552
  async stopScreenShare() {
1522
- if (!this.screenStream && !this.screenSender?.track) return;
1523
- const screenTrack = this.screenSender?.track || null;
1524
- if (screenTrack) screenTrack.onended = null;
1553
+ const stream = this.screenStream;
1554
+ const senderTrack = this.screenSender?.track || null;
1555
+ if (!stream && !senderTrack) {
1556
+ return;
1557
+ }
1558
+ ++this.screenShareGeneration;
1559
+ if (senderTrack) {
1560
+ senderTrack.onended = null;
1561
+ }
1525
1562
  try {
1526
1563
  await this.screenSender?.replaceTrack(null);
1527
1564
  } catch (error) {
@@ -1530,23 +1567,33 @@ var SFUClient = class {
1530
1567
  if (this.screenTransceiver) {
1531
1568
  this.screenTransceiver.direction = "inactive";
1532
1569
  }
1533
- this.send({ type: "stop_screen_share" });
1534
1570
  try {
1535
- const offer = await this.publisher?.createOffer();
1536
- if (offer && this.publisher) {
1571
+ if (this.publisher) {
1572
+ const offer = await this.publisher.createOffer();
1537
1573
  await this.publisher.setLocalDescription(offer);
1538
- this.send({
1539
- type: "publish_offer",
1540
- payload: {
1541
- revision: ++this.publisherRevision,
1542
- sdp: offer.sdp
1543
- }
1544
- });
1574
+ const localDescription = this.publisher.localDescription;
1575
+ if (localDescription?.sdp) {
1576
+ this.send({
1577
+ type: "publish_offer",
1578
+ payload: {
1579
+ revision: ++this.publisherRevision,
1580
+ sdp: localDescription.sdp
1581
+ }
1582
+ });
1583
+ }
1545
1584
  }
1546
1585
  } catch (error) {
1547
1586
  this.onError(error);
1548
1587
  }
1549
- this.screenStream?.getTracks().forEach((track) => track.stop());
1588
+ this.send({
1589
+ type: "stop_screen_share"
1590
+ });
1591
+ stream?.getTracks().forEach((track) => {
1592
+ try {
1593
+ track.stop();
1594
+ } catch {
1595
+ }
1596
+ });
1550
1597
  this.screenStream = null;
1551
1598
  if (this.currentParticipantId) {
1552
1599
  this.onScreenShareStopped(this.currentParticipantId);
@@ -2310,11 +2357,11 @@ var VideoSDKCore = class {
2310
2357
  // SCREEN SHARE
2311
2358
  onScreenShareStarted: (participantId, _track) => {
2312
2359
  console.log("[SFU] Screen share started", participantId);
2313
- if (participantId === this.myId && !this.screenStream) {
2314
- this.screenStream = this.sfuClient?.getScreenStream() ?? null;
2315
- }
2316
- this.state.setPresenterId(participantId);
2317
2360
  if (participantId === this.myId) {
2361
+ if (!this.screenStream) {
2362
+ this.screenStream = this.sfuClient?.getScreenStream() ?? null;
2363
+ }
2364
+ this.state.setPresenterId(participantId);
2318
2365
  this.state.updateLocalParticipant({
2319
2366
  media: {
2320
2367
  isScreenSharing: true,
@@ -2324,11 +2371,25 @@ var VideoSDKCore = class {
2324
2371
  });
2325
2372
  return;
2326
2373
  }
2374
+ this.state.setPresenterId(participantId);
2375
+ const existingRemoteScreen = this.sfuClient?.getRemoteScreenStream(participantId) ?? this.remoteScreenStreams.get(participantId) ?? null;
2376
+ if (existingRemoteScreen) {
2377
+ this.remoteScreenStreams.set(participantId, existingRemoteScreen);
2378
+ const screenTrack = existingRemoteScreen.getVideoTracks()[0];
2379
+ this.state.updateParticipantMedia(participantId, {
2380
+ isScreenSharing: true,
2381
+ screenStream: existingRemoteScreen,
2382
+ screenTrack
2383
+ });
2384
+ this.events.onScreenShareStarted?.(participantId, existingRemoteScreen);
2385
+ return;
2386
+ }
2327
2387
  const current = this.state.getParticipant(participantId);
2328
2388
  if (current) {
2329
2389
  this.state.updateParticipantMedia(participantId, {
2330
2390
  isScreenSharing: true,
2331
- screenStream: current.media?.screenStream ?? null
2391
+ screenStream: current.media?.screenStream ?? null,
2392
+ screenTrack: current.media?.screenTrack
2332
2393
  });
2333
2394
  }
2334
2395
  },
@@ -2346,13 +2407,8 @@ var VideoSDKCore = class {
2346
2407
  });
2347
2408
  return;
2348
2409
  }
2349
- const screen = this.remoteScreenStreams.get(participantId);
2350
- screen?.getTracks().forEach((track) => track.stop());
2351
- this.remoteScreenStreams.delete(participantId);
2352
2410
  this.state.updateParticipantMedia(participantId, {
2353
- isScreenSharing: false,
2354
- screenStream: null,
2355
- screenTrack: void 0
2411
+ isScreenSharing: false
2356
2412
  });
2357
2413
  this.events.onScreenShareStopped?.(participantId);
2358
2414
  },