@defcomm/meetingsdk 0.2.2 → 0.2.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 +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +239 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +239 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -272,6 +272,7 @@ interface SFUClientOptions {
|
|
|
272
272
|
onScreenShareStarted?: (participantId: string, track?: TrackInfo) => void;
|
|
273
273
|
onScreenShareStopped?: (participantId: string, trackId?: string) => void;
|
|
274
274
|
onTrackStateChanged?: (track: TrackInfo) => void;
|
|
275
|
+
onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
|
|
275
276
|
onRecordingStarted?: (recording: {
|
|
276
277
|
recording_id: string;
|
|
277
278
|
started_at: string;
|
|
@@ -280,6 +281,8 @@ interface SFUClientOptions {
|
|
|
280
281
|
recording_id: string;
|
|
281
282
|
stopped_at: string;
|
|
282
283
|
}) => void;
|
|
284
|
+
/** Authoritative room snapshot received after join/reconnect/resync. */
|
|
285
|
+
onRoomState?: (state: any) => void;
|
|
283
286
|
onError?: (error: any) => void;
|
|
284
287
|
autoSubscribe?: boolean;
|
|
285
288
|
}
|
|
@@ -293,6 +296,9 @@ declare class SFUClient {
|
|
|
293
296
|
private localStream;
|
|
294
297
|
private screenStream;
|
|
295
298
|
private videoSender;
|
|
299
|
+
private audioMuted;
|
|
300
|
+
private videoMuted;
|
|
301
|
+
private activeRecording;
|
|
296
302
|
private audioSender;
|
|
297
303
|
private screenSender;
|
|
298
304
|
private audioTransceiver;
|
|
@@ -333,6 +339,8 @@ declare class SFUClient {
|
|
|
333
339
|
private trackToSource;
|
|
334
340
|
private localTrackIds;
|
|
335
341
|
private subscriberStatsTimer;
|
|
342
|
+
private roomSyncTimer;
|
|
343
|
+
private roomSyncInFlight;
|
|
336
344
|
private onStateChange;
|
|
337
345
|
private onParticipantJoined;
|
|
338
346
|
private onParticipantLeft;
|
|
@@ -350,14 +358,19 @@ declare class SFUClient {
|
|
|
350
358
|
private onScreenShareStarted;
|
|
351
359
|
private onScreenShareStopped;
|
|
352
360
|
private onTrackStateChanged;
|
|
361
|
+
private onParticipantMediaState;
|
|
353
362
|
private onRecordingStarted;
|
|
354
363
|
private onRecordingStopped;
|
|
364
|
+
private onRoomState;
|
|
355
365
|
private onError;
|
|
356
366
|
private autoSubscribe;
|
|
357
367
|
constructor(options?: SFUClientOptions);
|
|
358
368
|
private heartbeatTimer;
|
|
359
369
|
private startHeartbeat;
|
|
360
370
|
private stopHeartbeat;
|
|
371
|
+
private startRoomStateSync;
|
|
372
|
+
private stopRoomStateSync;
|
|
373
|
+
private syncRoomState;
|
|
361
374
|
private createId;
|
|
362
375
|
private resumeStorageKey;
|
|
363
376
|
private loadResumeToken;
|
|
@@ -384,6 +397,8 @@ declare class SFUClient {
|
|
|
384
397
|
displayName?: string;
|
|
385
398
|
avatarUrl?: string;
|
|
386
399
|
participantId?: string;
|
|
400
|
+
audioMuted?: boolean;
|
|
401
|
+
videoMuted?: boolean;
|
|
387
402
|
}): Promise<void>;
|
|
388
403
|
private refreshAccessToken;
|
|
389
404
|
private connect;
|
|
@@ -392,6 +407,7 @@ declare class SFUClient {
|
|
|
392
407
|
private failJoin;
|
|
393
408
|
private resolveJoin;
|
|
394
409
|
private handleServerMessage;
|
|
410
|
+
private applyRoomState;
|
|
395
411
|
private rememberTrack;
|
|
396
412
|
private handleJoined;
|
|
397
413
|
private initializePeerConnections;
|
|
@@ -469,6 +485,7 @@ type MeetingContextValue = {
|
|
|
469
485
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
470
486
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
471
487
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
488
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
472
489
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
473
490
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
474
491
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
|
@@ -527,6 +544,7 @@ declare const useMeeting: (handlers?: {
|
|
|
527
544
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
528
545
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
529
546
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
547
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
530
548
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
531
549
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
532
550
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -272,6 +272,7 @@ interface SFUClientOptions {
|
|
|
272
272
|
onScreenShareStarted?: (participantId: string, track?: TrackInfo) => void;
|
|
273
273
|
onScreenShareStopped?: (participantId: string, trackId?: string) => void;
|
|
274
274
|
onTrackStateChanged?: (track: TrackInfo) => void;
|
|
275
|
+
onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
|
|
275
276
|
onRecordingStarted?: (recording: {
|
|
276
277
|
recording_id: string;
|
|
277
278
|
started_at: string;
|
|
@@ -280,6 +281,8 @@ interface SFUClientOptions {
|
|
|
280
281
|
recording_id: string;
|
|
281
282
|
stopped_at: string;
|
|
282
283
|
}) => void;
|
|
284
|
+
/** Authoritative room snapshot received after join/reconnect/resync. */
|
|
285
|
+
onRoomState?: (state: any) => void;
|
|
283
286
|
onError?: (error: any) => void;
|
|
284
287
|
autoSubscribe?: boolean;
|
|
285
288
|
}
|
|
@@ -293,6 +296,9 @@ declare class SFUClient {
|
|
|
293
296
|
private localStream;
|
|
294
297
|
private screenStream;
|
|
295
298
|
private videoSender;
|
|
299
|
+
private audioMuted;
|
|
300
|
+
private videoMuted;
|
|
301
|
+
private activeRecording;
|
|
296
302
|
private audioSender;
|
|
297
303
|
private screenSender;
|
|
298
304
|
private audioTransceiver;
|
|
@@ -333,6 +339,8 @@ declare class SFUClient {
|
|
|
333
339
|
private trackToSource;
|
|
334
340
|
private localTrackIds;
|
|
335
341
|
private subscriberStatsTimer;
|
|
342
|
+
private roomSyncTimer;
|
|
343
|
+
private roomSyncInFlight;
|
|
336
344
|
private onStateChange;
|
|
337
345
|
private onParticipantJoined;
|
|
338
346
|
private onParticipantLeft;
|
|
@@ -350,14 +358,19 @@ declare class SFUClient {
|
|
|
350
358
|
private onScreenShareStarted;
|
|
351
359
|
private onScreenShareStopped;
|
|
352
360
|
private onTrackStateChanged;
|
|
361
|
+
private onParticipantMediaState;
|
|
353
362
|
private onRecordingStarted;
|
|
354
363
|
private onRecordingStopped;
|
|
364
|
+
private onRoomState;
|
|
355
365
|
private onError;
|
|
356
366
|
private autoSubscribe;
|
|
357
367
|
constructor(options?: SFUClientOptions);
|
|
358
368
|
private heartbeatTimer;
|
|
359
369
|
private startHeartbeat;
|
|
360
370
|
private stopHeartbeat;
|
|
371
|
+
private startRoomStateSync;
|
|
372
|
+
private stopRoomStateSync;
|
|
373
|
+
private syncRoomState;
|
|
361
374
|
private createId;
|
|
362
375
|
private resumeStorageKey;
|
|
363
376
|
private loadResumeToken;
|
|
@@ -384,6 +397,8 @@ declare class SFUClient {
|
|
|
384
397
|
displayName?: string;
|
|
385
398
|
avatarUrl?: string;
|
|
386
399
|
participantId?: string;
|
|
400
|
+
audioMuted?: boolean;
|
|
401
|
+
videoMuted?: boolean;
|
|
387
402
|
}): Promise<void>;
|
|
388
403
|
private refreshAccessToken;
|
|
389
404
|
private connect;
|
|
@@ -392,6 +407,7 @@ declare class SFUClient {
|
|
|
392
407
|
private failJoin;
|
|
393
408
|
private resolveJoin;
|
|
394
409
|
private handleServerMessage;
|
|
410
|
+
private applyRoomState;
|
|
395
411
|
private rememberTrack;
|
|
396
412
|
private handleJoined;
|
|
397
413
|
private initializePeerConnections;
|
|
@@ -469,6 +485,7 @@ type MeetingContextValue = {
|
|
|
469
485
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
470
486
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
471
487
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
488
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
472
489
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
473
490
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
474
491
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
|
@@ -527,6 +544,7 @@ declare const useMeeting: (handlers?: {
|
|
|
527
544
|
onEntryRequested: (cb: (req: any) => void) => () => void;
|
|
528
545
|
onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
|
|
529
546
|
onMeetingLeft: (cb: () => void) => () => void;
|
|
547
|
+
onRoomUpdate: (cb: (data: any) => void) => () => void;
|
|
530
548
|
onTrackStateChanged: (cb: (track: any) => void) => () => void;
|
|
531
549
|
onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
|
|
532
550
|
onRecordingStopped: (cb: (recording: any) => void) => () => void;
|
package/dist/index.js
CHANGED
|
@@ -385,6 +385,9 @@ var SFUClient = class {
|
|
|
385
385
|
this.localStream = null;
|
|
386
386
|
this.screenStream = null;
|
|
387
387
|
this.videoSender = null;
|
|
388
|
+
this.audioMuted = false;
|
|
389
|
+
this.videoMuted = false;
|
|
390
|
+
this.activeRecording = null;
|
|
388
391
|
this.audioSender = null;
|
|
389
392
|
this.screenSender = null;
|
|
390
393
|
this.audioTransceiver = null;
|
|
@@ -405,7 +408,7 @@ var SFUClient = class {
|
|
|
405
408
|
this.joiningWithResume = false;
|
|
406
409
|
this.reconnectTimer = null;
|
|
407
410
|
this.reconnectAttempt = 0;
|
|
408
|
-
this.maxReconnectAttempts =
|
|
411
|
+
this.maxReconnectAttempts = 6;
|
|
409
412
|
this.reconnectBaseMs = 750;
|
|
410
413
|
this.joinWaiter = null;
|
|
411
414
|
this.transportRecoveryInProgress = false;
|
|
@@ -428,6 +431,8 @@ var SFUClient = class {
|
|
|
428
431
|
this.trackToSource = /* @__PURE__ */ new Map();
|
|
429
432
|
this.localTrackIds = /* @__PURE__ */ new Map();
|
|
430
433
|
this.subscriberStatsTimer = null;
|
|
434
|
+
this.roomSyncTimer = null;
|
|
435
|
+
this.roomSyncInFlight = false;
|
|
431
436
|
this.heartbeatTimer = null;
|
|
432
437
|
this.apiBase = options.apiBase || "https://api.example.com/api";
|
|
433
438
|
this.wsBase = options.wsBase || "wss://api.example.com/ws";
|
|
@@ -466,10 +471,14 @@ var SFUClient = class {
|
|
|
466
471
|
});
|
|
467
472
|
this.onTrackStateChanged = options.onTrackStateChanged || (() => {
|
|
468
473
|
});
|
|
474
|
+
this.onParticipantMediaState = options.onParticipantMediaState || (() => {
|
|
475
|
+
});
|
|
469
476
|
this.onRecordingStarted = options.onRecordingStarted || (() => {
|
|
470
477
|
});
|
|
471
478
|
this.onRecordingStopped = options.onRecordingStopped || (() => {
|
|
472
479
|
});
|
|
480
|
+
this.onRoomState = options.onRoomState || (() => {
|
|
481
|
+
});
|
|
473
482
|
this.onError = options.onError || console.error;
|
|
474
483
|
this.autoSubscribe = options.autoSubscribe !== false;
|
|
475
484
|
}
|
|
@@ -491,6 +500,29 @@ var SFUClient = class {
|
|
|
491
500
|
this.heartbeatTimer = null;
|
|
492
501
|
}
|
|
493
502
|
}
|
|
503
|
+
startRoomStateSync() {
|
|
504
|
+
this.stopRoomStateSync();
|
|
505
|
+
this.roomSyncTimer = setInterval(() => {
|
|
506
|
+
if (this.state !== "connected") return;
|
|
507
|
+
void this.syncRoomState();
|
|
508
|
+
}, 5e3);
|
|
509
|
+
}
|
|
510
|
+
stopRoomStateSync() {
|
|
511
|
+
if (this.roomSyncTimer) {
|
|
512
|
+
clearInterval(this.roomSyncTimer);
|
|
513
|
+
this.roomSyncTimer = null;
|
|
514
|
+
}
|
|
515
|
+
this.roomSyncInFlight = false;
|
|
516
|
+
}
|
|
517
|
+
async syncRoomState() {
|
|
518
|
+
if (this.roomSyncInFlight || !this.isSocketOpen()) return;
|
|
519
|
+
this.roomSyncInFlight = true;
|
|
520
|
+
try {
|
|
521
|
+
this.send({ type: "sync_room_state" });
|
|
522
|
+
} finally {
|
|
523
|
+
this.roomSyncInFlight = false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
494
526
|
createId() {
|
|
495
527
|
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `id_${Math.random().toString(36).slice(2)}_${Date.now()}`;
|
|
496
528
|
}
|
|
@@ -588,9 +620,13 @@ var SFUClient = class {
|
|
|
588
620
|
this.resumeToken = null;
|
|
589
621
|
}
|
|
590
622
|
this.room = nextRoom;
|
|
623
|
+
this.audioMuted = Boolean(options.audioMuted);
|
|
624
|
+
this.videoMuted = Boolean(options.videoMuted);
|
|
591
625
|
this.joinOptions = {
|
|
592
626
|
displayName: options.displayName,
|
|
593
|
-
avatarUrl: options.avatarUrl
|
|
627
|
+
avatarUrl: options.avatarUrl,
|
|
628
|
+
audioMuted: this.audioMuted,
|
|
629
|
+
videoMuted: this.videoMuted
|
|
594
630
|
};
|
|
595
631
|
if (options.participantId) this.participantId = options.participantId;
|
|
596
632
|
if (!this.resumeToken) {
|
|
@@ -659,7 +695,9 @@ var SFUClient = class {
|
|
|
659
695
|
room_code: this.room,
|
|
660
696
|
resume_token: this.resumeToken,
|
|
661
697
|
display_name: options.displayName,
|
|
662
|
-
avatar_url: options.avatarUrl
|
|
698
|
+
avatar_url: options.avatarUrl,
|
|
699
|
+
audio_muted: this.audioMuted,
|
|
700
|
+
video_muted: this.videoMuted
|
|
663
701
|
};
|
|
664
702
|
if (this.pendingJoinRequestId) {
|
|
665
703
|
this.send({
|
|
@@ -700,8 +738,20 @@ var SFUClient = class {
|
|
|
700
738
|
scheduleReconnect() {
|
|
701
739
|
if (this.reconnectTimer || this.intentionalClose) return;
|
|
702
740
|
if (this.reconnectAttempt >= this.maxReconnectAttempts) {
|
|
741
|
+
const error = new Error("SFU reconnect attempts exhausted");
|
|
742
|
+
console.error("[SFU] Reconnect attempts exhausted; leaving meeting", error);
|
|
743
|
+
this.intentionalClose = true;
|
|
744
|
+
this.stopHeartbeat();
|
|
745
|
+
this.stopRoomStateSync();
|
|
746
|
+
this.cleanupAll();
|
|
703
747
|
this.setState("disconnected");
|
|
704
|
-
this.
|
|
748
|
+
this.onError({
|
|
749
|
+
code: "RECONNECT_TIMEOUT",
|
|
750
|
+
message: error.message,
|
|
751
|
+
recoverable: false
|
|
752
|
+
});
|
|
753
|
+
this.onDisconnected();
|
|
754
|
+
this.failJoin(error);
|
|
705
755
|
return;
|
|
706
756
|
}
|
|
707
757
|
this.reconnectAttempt += 1;
|
|
@@ -773,7 +823,9 @@ var SFUClient = class {
|
|
|
773
823
|
room_code: this.room,
|
|
774
824
|
resume_token: this.resumeToken,
|
|
775
825
|
display_name: this.joinOptions.displayName,
|
|
776
|
-
avatar_url: this.joinOptions.avatarUrl
|
|
826
|
+
avatar_url: this.joinOptions.avatarUrl,
|
|
827
|
+
audio_muted: this.audioMuted,
|
|
828
|
+
video_muted: this.videoMuted
|
|
777
829
|
}
|
|
778
830
|
});
|
|
779
831
|
}
|
|
@@ -848,22 +900,8 @@ var SFUClient = class {
|
|
|
848
900
|
if (!track?.track_id) break;
|
|
849
901
|
this.rememberTrack(track);
|
|
850
902
|
if (track.participant_id === this.currentParticipantId) {
|
|
851
|
-
if (track.source === "audio")
|
|
852
|
-
|
|
853
|
-
const muted = !(this.localStream?.getAudioTracks()[0]?.enabled ?? true);
|
|
854
|
-
this.send({
|
|
855
|
-
type: "track_state",
|
|
856
|
-
payload: { track_id: track.track_id, muted }
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
if (track.source === "camera") {
|
|
860
|
-
this.localTrackIds.set("camera", track.track_id);
|
|
861
|
-
const muted = !(this.localStream?.getVideoTracks()[0]?.enabled ?? true);
|
|
862
|
-
this.send({
|
|
863
|
-
type: "track_state",
|
|
864
|
-
payload: { track_id: track.track_id, muted }
|
|
865
|
-
});
|
|
866
|
-
}
|
|
903
|
+
if (track.source === "audio") this.localTrackIds.set("audio", track.track_id);
|
|
904
|
+
if (track.source === "camera") this.localTrackIds.set("camera", track.track_id);
|
|
867
905
|
if (track.source === "screen")
|
|
868
906
|
this.localTrackIds.set("screen", track.track_id);
|
|
869
907
|
break;
|
|
@@ -892,6 +930,27 @@ var SFUClient = class {
|
|
|
892
930
|
this.onTrackUnpublished(track);
|
|
893
931
|
break;
|
|
894
932
|
}
|
|
933
|
+
case "participant_media_state": {
|
|
934
|
+
const participantId = payload.participant_id;
|
|
935
|
+
if (!participantId) break;
|
|
936
|
+
const participant = this.participants.get(participantId);
|
|
937
|
+
if (participant) {
|
|
938
|
+
participant.audio_muted = Boolean(payload.audio_muted);
|
|
939
|
+
participant.video_muted = Boolean(payload.video_muted);
|
|
940
|
+
this.participants.set(participantId, participant);
|
|
941
|
+
}
|
|
942
|
+
for (const track of this.trackMetadata.values()) {
|
|
943
|
+
if (track.participant_id !== participantId) continue;
|
|
944
|
+
if (track.source === "audio") track.muted = Boolean(payload.audio_muted);
|
|
945
|
+
if (track.source === "camera") track.muted = Boolean(payload.video_muted);
|
|
946
|
+
}
|
|
947
|
+
this.onParticipantMediaState(
|
|
948
|
+
participantId,
|
|
949
|
+
Boolean(payload.audio_muted),
|
|
950
|
+
Boolean(payload.video_muted)
|
|
951
|
+
);
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
895
954
|
case "track_state_changed": {
|
|
896
955
|
const trackId = payload.track_id || payload.track?.track_id;
|
|
897
956
|
if (!trackId) break;
|
|
@@ -913,14 +972,20 @@ var SFUClient = class {
|
|
|
913
972
|
case "subscription_updated": {
|
|
914
973
|
break;
|
|
915
974
|
}
|
|
975
|
+
case "room_state": {
|
|
976
|
+
await this.applyRoomState(payload);
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
916
979
|
case "recording_started": {
|
|
917
|
-
this.
|
|
980
|
+
this.activeRecording = {
|
|
918
981
|
recording_id: payload.recording_id,
|
|
919
982
|
started_at: payload.started_at
|
|
920
|
-
}
|
|
983
|
+
};
|
|
984
|
+
this.onRecordingStarted(this.activeRecording);
|
|
921
985
|
break;
|
|
922
986
|
}
|
|
923
987
|
case "recording_stopped": {
|
|
988
|
+
this.activeRecording = null;
|
|
924
989
|
this.onRecordingStopped({
|
|
925
990
|
recording_id: payload.recording_id,
|
|
926
991
|
stopped_at: payload.stopped_at
|
|
@@ -978,6 +1043,67 @@ var SFUClient = class {
|
|
|
978
1043
|
console.debug("[SFU] Ignoring unknown server message", message);
|
|
979
1044
|
}
|
|
980
1045
|
}
|
|
1046
|
+
async applyRoomState(payload) {
|
|
1047
|
+
const authoritative = /* @__PURE__ */ new Map();
|
|
1048
|
+
for (const participant of payload?.participants || []) {
|
|
1049
|
+
if (participant?.participant_id) authoritative.set(participant.participant_id, participant);
|
|
1050
|
+
}
|
|
1051
|
+
for (const id of [...this.participants.keys()]) {
|
|
1052
|
+
if (!authoritative.has(id) && id !== this.currentParticipantId) {
|
|
1053
|
+
this.participants.delete(id);
|
|
1054
|
+
this.removeRemoteParticipant(id);
|
|
1055
|
+
this.onParticipantLeft(id);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
for (const [id, participant] of authoritative) {
|
|
1059
|
+
if (id === this.currentParticipantId) continue;
|
|
1060
|
+
const previous = this.participants.get(id);
|
|
1061
|
+
this.participants.set(id, {
|
|
1062
|
+
...previous,
|
|
1063
|
+
...participant,
|
|
1064
|
+
audio_muted: Boolean(participant.audio_muted),
|
|
1065
|
+
video_muted: Boolean(participant.video_muted)
|
|
1066
|
+
});
|
|
1067
|
+
if (!previous) this.onParticipantJoined(participant);
|
|
1068
|
+
if (participant.presence) this.onParticipantPresence(id, String(participant.presence));
|
|
1069
|
+
}
|
|
1070
|
+
const nextTracks = /* @__PURE__ */ new Map();
|
|
1071
|
+
for (const track of payload?.tracks || []) {
|
|
1072
|
+
if (!track?.track_id) continue;
|
|
1073
|
+
nextTracks.set(track.track_id, track);
|
|
1074
|
+
this.rememberTrack(track);
|
|
1075
|
+
}
|
|
1076
|
+
for (const id of [...this.trackMetadata.keys()]) {
|
|
1077
|
+
if (!nextTracks.has(id)) {
|
|
1078
|
+
this.trackMetadata.delete(id);
|
|
1079
|
+
this.subscribedTracks.delete(id);
|
|
1080
|
+
this.trackToParticipant.delete(id);
|
|
1081
|
+
this.trackToSource.delete(id);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (payload?.recording) {
|
|
1085
|
+
const current = {
|
|
1086
|
+
recording_id: payload.recording.recording_id,
|
|
1087
|
+
started_at: payload.recording.started_at
|
|
1088
|
+
};
|
|
1089
|
+
if (!this.activeRecording || this.activeRecording.recording_id !== current.recording_id) {
|
|
1090
|
+
this.activeRecording = current;
|
|
1091
|
+
this.onRecordingStarted(current);
|
|
1092
|
+
}
|
|
1093
|
+
} else if (this.activeRecording) {
|
|
1094
|
+
const previous = this.activeRecording;
|
|
1095
|
+
this.activeRecording = null;
|
|
1096
|
+
this.onRecordingStopped({
|
|
1097
|
+
recording_id: previous.recording_id,
|
|
1098
|
+
stopped_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
if (this.autoSubscribe) {
|
|
1102
|
+
const missing = [...nextTracks.values()].filter((t) => t.participant_id !== this.currentParticipantId && !this.subscribedTracks.has(t.track_id)).map((t) => t.track_id);
|
|
1103
|
+
if (missing.length) await this.subscribeMultiple(missing);
|
|
1104
|
+
}
|
|
1105
|
+
this.onRoomState(payload);
|
|
1106
|
+
}
|
|
981
1107
|
rememberTrack(track) {
|
|
982
1108
|
this.trackMetadata.set(track.track_id, track);
|
|
983
1109
|
this.trackToParticipant.set(track.track_id, track.participant_id);
|
|
@@ -1007,8 +1133,23 @@ var SFUClient = class {
|
|
|
1007
1133
|
for (const track of payload.tracks || []) {
|
|
1008
1134
|
if (track?.track_id) this.rememberTrack(track);
|
|
1009
1135
|
}
|
|
1136
|
+
if (payload.recording) {
|
|
1137
|
+
this.activeRecording = {
|
|
1138
|
+
recording_id: payload.recording.recording_id,
|
|
1139
|
+
started_at: payload.recording.started_at
|
|
1140
|
+
};
|
|
1141
|
+
this.onRecordingStarted(this.activeRecording);
|
|
1142
|
+
} else if (this.activeRecording) {
|
|
1143
|
+
const previous = this.activeRecording;
|
|
1144
|
+
this.activeRecording = null;
|
|
1145
|
+
this.onRecordingStopped({
|
|
1146
|
+
recording_id: previous.recording_id,
|
|
1147
|
+
stopped_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1010
1150
|
await this.initializePeerConnections();
|
|
1011
1151
|
this.setState("connected");
|
|
1152
|
+
this.startRoomStateSync();
|
|
1012
1153
|
this.onConnected(payload, resumed);
|
|
1013
1154
|
for (const participant of this.participants.values()) {
|
|
1014
1155
|
if (participant.participant_id !== this.currentParticipantId) {
|
|
@@ -1563,6 +1704,7 @@ var SFUClient = class {
|
|
|
1563
1704
|
}
|
|
1564
1705
|
async leave() {
|
|
1565
1706
|
this.stopHeartbeat();
|
|
1707
|
+
this.stopRoomStateSync();
|
|
1566
1708
|
this.intentionalClose = true;
|
|
1567
1709
|
this.setState("leaving");
|
|
1568
1710
|
if (this.reconnectTimer) {
|
|
@@ -1930,9 +2072,13 @@ var VideoSDKCore = class {
|
|
|
1930
2072
|
);
|
|
1931
2073
|
return;
|
|
1932
2074
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2075
|
+
if (state === "disconnected" || state === "idle") {
|
|
2076
|
+
this.sfuClient = null;
|
|
2077
|
+
} else {
|
|
2078
|
+
throw new Error(
|
|
2079
|
+
`SFU client already exists in state "${state}". Disconnect explicitly before creating another connection.`
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
1936
2082
|
}
|
|
1937
2083
|
const client = new SFUClient({
|
|
1938
2084
|
apiBase: this.apiBase,
|
|
@@ -1950,6 +2096,48 @@ var VideoSDKCore = class {
|
|
|
1950
2096
|
name: this.participantName
|
|
1951
2097
|
});
|
|
1952
2098
|
},
|
|
2099
|
+
onRoomState: (roomState) => {
|
|
2100
|
+
for (const participant of roomState?.participants || []) {
|
|
2101
|
+
const pid = participant?.participant_id;
|
|
2102
|
+
if (!pid || pid === this.myId) continue;
|
|
2103
|
+
const current = this.state.getParticipant(pid);
|
|
2104
|
+
if (!current) continue;
|
|
2105
|
+
this.state.updateParticipantMedia(pid, {
|
|
2106
|
+
stream: current.media?.stream ?? null,
|
|
2107
|
+
micEnabled: !Boolean(participant.audio_muted),
|
|
2108
|
+
camEnabled: !Boolean(participant.video_muted),
|
|
2109
|
+
isScreenSharing: Boolean(participant.screen_sharing)
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
const presenters = /* @__PURE__ */ new Set();
|
|
2113
|
+
for (const participant of roomState?.participants || []) {
|
|
2114
|
+
const pid = participant?.participant_id;
|
|
2115
|
+
if (!pid || pid === this.myId) continue;
|
|
2116
|
+
const current = this.state.getParticipant(pid);
|
|
2117
|
+
if (!current) continue;
|
|
2118
|
+
if (participant.screen_sharing) {
|
|
2119
|
+
presenters.add(pid);
|
|
2120
|
+
this.state.updateParticipantMedia(pid, {
|
|
2121
|
+
isScreenSharing: true,
|
|
2122
|
+
screenStream: current.media?.screenStream ?? null
|
|
2123
|
+
});
|
|
2124
|
+
} else if (current.media?.isScreenSharing) {
|
|
2125
|
+
const screen = this.remoteScreenStreams.get(pid);
|
|
2126
|
+
screen?.getTracks().forEach((track) => track.stop());
|
|
2127
|
+
this.remoteScreenStreams.delete(pid);
|
|
2128
|
+
this.state.updateParticipantMedia(pid, {
|
|
2129
|
+
isScreenSharing: false,
|
|
2130
|
+
screenStream: null,
|
|
2131
|
+
screenTrack: void 0
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
const presenter = [...presenters][0] ?? null;
|
|
2136
|
+
if (this.state.presenterId !== presenter) {
|
|
2137
|
+
this.state.setPresenterId(presenter);
|
|
2138
|
+
}
|
|
2139
|
+
this.events.onRoomUpdate?.(roomState);
|
|
2140
|
+
},
|
|
1953
2141
|
onDisconnected: () => {
|
|
1954
2142
|
console.log("[SFU] Signaling WebSocket disconnected; reconnecting");
|
|
1955
2143
|
},
|
|
@@ -2021,8 +2209,8 @@ var VideoSDKCore = class {
|
|
|
2021
2209
|
name: pname,
|
|
2022
2210
|
connectionState: "connected",
|
|
2023
2211
|
media: {
|
|
2024
|
-
micEnabled:
|
|
2025
|
-
camEnabled:
|
|
2212
|
+
micEnabled: !Boolean(participant.audio_muted),
|
|
2213
|
+
camEnabled: !Boolean(participant.video_muted),
|
|
2026
2214
|
isScreenSharing: false,
|
|
2027
2215
|
stream: null
|
|
2028
2216
|
}
|
|
@@ -2095,6 +2283,18 @@ var VideoSDKCore = class {
|
|
|
2095
2283
|
camEnabled: track.kind === "video" ? false : current.media?.camEnabled ?? true
|
|
2096
2284
|
});
|
|
2097
2285
|
},
|
|
2286
|
+
onParticipantMediaState: (participantId, audioMuted, videoMuted) => {
|
|
2287
|
+
if (participantId === this.myId) return;
|
|
2288
|
+
const current = this.state.getParticipant(participantId);
|
|
2289
|
+
if (!current) return;
|
|
2290
|
+
this.state.updateParticipantMedia(participantId, {
|
|
2291
|
+
stream: current.media?.stream ?? null,
|
|
2292
|
+
micEnabled: !audioMuted,
|
|
2293
|
+
camEnabled: !videoMuted
|
|
2294
|
+
});
|
|
2295
|
+
this.events.onMuteStateChanged?.(participantId, "audio", audioMuted);
|
|
2296
|
+
this.events.onMuteStateChanged?.(participantId, "video", videoMuted);
|
|
2297
|
+
},
|
|
2098
2298
|
onTrackStateChanged: (track) => {
|
|
2099
2299
|
const pid = track.participant_id;
|
|
2100
2300
|
if (!pid || pid === this.myId) {
|
|
@@ -2195,7 +2395,9 @@ var VideoSDKCore = class {
|
|
|
2195
2395
|
client.setLocalStream(this.localStream);
|
|
2196
2396
|
await client.getToken(roomId, {
|
|
2197
2397
|
displayName: name,
|
|
2198
|
-
participantId: this.myId
|
|
2398
|
+
participantId: this.myId,
|
|
2399
|
+
audioMuted: this.state.localParticipant?.media?.micEnabled,
|
|
2400
|
+
videoMuted: this.state.localParticipant?.media?.camEnabled
|
|
2199
2401
|
});
|
|
2200
2402
|
}
|
|
2201
2403
|
// SFU STATE
|
|
@@ -2509,6 +2711,7 @@ var MeetingProvider = ({
|
|
|
2509
2711
|
/* @__PURE__ */ new Set()
|
|
2510
2712
|
);
|
|
2511
2713
|
const meetingLeftListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2714
|
+
const roomUpdateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2512
2715
|
const trackStateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2513
2716
|
const recordingStartedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
2514
2717
|
const recordingStoppedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
@@ -2533,6 +2736,9 @@ var MeetingProvider = ({
|
|
|
2533
2736
|
onMeetingLeft: () => {
|
|
2534
2737
|
meetingLeftListeners.current.forEach((fn) => fn());
|
|
2535
2738
|
},
|
|
2739
|
+
onRoomUpdate: (data) => {
|
|
2740
|
+
roomUpdateListeners.current.forEach((fn) => fn(data));
|
|
2741
|
+
},
|
|
2536
2742
|
onTrackStateChanged: (track) => {
|
|
2537
2743
|
trackStateListeners.current.forEach((fn) => fn(track));
|
|
2538
2744
|
},
|
|
@@ -2631,6 +2837,10 @@ var MeetingProvider = ({
|
|
|
2631
2837
|
meetingLeftListeners.current.delete(cb);
|
|
2632
2838
|
};
|
|
2633
2839
|
},
|
|
2840
|
+
onRoomUpdate: (cb) => {
|
|
2841
|
+
roomUpdateListeners.current.add(cb);
|
|
2842
|
+
return () => roomUpdateListeners.current.delete(cb);
|
|
2843
|
+
},
|
|
2634
2844
|
onTrackStateChanged: (cb) => {
|
|
2635
2845
|
trackStateListeners.current.add(cb);
|
|
2636
2846
|
return () => trackStateListeners.current.delete(cb);
|